📄 thumb.c
字号:
/* Output routines for GCC for ARM/Thumb Copyright (C) 1996 Cygnus Software Technologies Ltd The basis of this contribution was generated by Richard Earnshaw, Advanced RISC Machines LtdThis file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, or (at your option)any later version.GNU CC is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING. If not, write tothe Free Software Foundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. */#include <stdio.h>#include <string.h>#include "config.h"#include "rtl.h"#include "hard-reg-set.h"#include "regs.h"#include "output.h"#include "insn-flags.h"#include "insn-attr.h"#include "flags.h"#include "tree.h"#include "expr.h"int current_function_anonymous_args = 0;/* Used to parse -mstructure_size_boundary command line option. */char * structure_size_string = NULL;int arm_structure_size_boundary = 32; /* Used to be 8 *//* Predicates */intreload_memory_operand (op, mode) rtx op; enum machine_mode mode;{ int regno = true_regnum (op); return (! CONSTANT_P (op) && (regno == -1 || (GET_CODE (op) == REG && REGNO (op) >= FIRST_PSEUDO_REGISTER)));}/* Return nonzero if op is suitable for the RHS of a cmp instruction. */intthumb_cmp_operand (op, mode) rtx op; enum machine_mode mode;{ return ((GET_CODE (op) == CONST_INT && (unsigned HOST_WIDE_INT) (INTVAL (op)) < 256) || register_operand (op, mode));}intthumb_shiftable_const (val) HOST_WIDE_INT val;{ unsigned HOST_WIDE_INT x = val; unsigned HOST_WIDE_INT mask = 0xff; int i; for (i = 0; i < 25; i++) if ((val & (mask << i)) == val) return 1; return 0;}intthumb_trivial_epilogue (){ int regno; /* ??? If this function ever returns 1, we get a function without any epilogue at all. It appears that the intent was to cause a "return" insn to be emitted, but that does not happen. */ return 0;#if 0 if (get_frame_size () || current_function_outgoing_args_size || current_function_pretend_args_size) return 0; for (regno = 8; regno < 13; regno++) if (regs_ever_live[regno] && ! call_used_regs[regno]) return 0; return 1;#endif}/* Routines for handling the constant pool *//* This is unashamedly hacked from the version in sh.c, since the problem is extremely similar. *//* Thumb instructions cannot load a large constant into a register, constants have to come from a pc relative load. The reference of a pc relative load instruction must be less than 1k infront of the instruction. This means that we often have to dump a constant inside a function, and generate code to branch around it. It is important to minimize this, since the branches will slow things down and make things bigger. Worst case code looks like: ldr rn, L1 b L2 align L1: .long value L2: .. ldr rn, L3 b L4 align L3: .long value L4: .. We fix this by performing a scan before scheduling, which notices which instructions need to have their operands fetched from the constant table and builds the table. The algorithm is: scan, find an instruction which needs a pcrel move. Look forward, find the last barrier which is within MAX_COUNT bytes of the requirement. If there isn't one, make one. Process all the instructions between the find and the barrier. In the above example, we can tell that L3 is within 1k of L1, so the first move can be shrunk from the 2 insn+constant sequence into just 1 insn, and the constant moved to L3 to make: ldr rn, L1 .. ldr rn, L3 b L4 align L1: .long value L3: .long value L4: Then the second move becomes the target for the shortening process. */ typedef struct{ rtx value; /* Value in table */ HOST_WIDE_INT next_offset; enum machine_mode mode; /* Mode of value */} pool_node;/* The maximum number of constants that can fit into one pool, since the pc relative range is 0...1020 bytes and constants are at least 4 bytes long */#define MAX_POOL_SIZE (1020/4)static pool_node pool_vector[MAX_POOL_SIZE];static int pool_size;static rtx pool_vector_label;/* Add a constant to the pool and return its label. */ static HOST_WIDE_INTadd_constant (x, mode) rtx x; enum machine_mode mode;{ int i; rtx lab; HOST_WIDE_INT offset; if (mode == SImode && GET_CODE (x) == MEM && CONSTANT_P (XEXP (x, 0)) && CONSTANT_POOL_ADDRESS_P (XEXP (x, 0))) x = get_pool_constant (XEXP (x, 0)); /* First see if we've already got it */ for (i = 0; i < pool_size; i++) { if (x->code == pool_vector[i].value->code && mode == pool_vector[i].mode) { if (x->code == CODE_LABEL) { if (XINT (x, 3) != XINT (pool_vector[i].value, 3)) continue; } if (rtx_equal_p (x, pool_vector[i].value)) return pool_vector[i].next_offset - GET_MODE_SIZE (mode); } } /* Need a new one */ pool_vector[pool_size].next_offset = GET_MODE_SIZE (mode); offset = 0; if (pool_size == 0) pool_vector_label = gen_label_rtx (); else pool_vector[pool_size].next_offset += (offset = pool_vector[pool_size - 1].next_offset); pool_vector[pool_size].value = x; pool_vector[pool_size].mode = mode; pool_size++; return offset;} /* Output the literal table */ static void dump_table (scan) rtx scan;{ int i; scan = emit_label_after (gen_label_rtx (), scan); scan = emit_insn_after (gen_align_4 (), scan); scan = emit_label_after (pool_vector_label, scan); for (i = 0; i < pool_size; i++) { pool_node *p = pool_vector + i; switch (GET_MODE_SIZE (p->mode)) { case 4: scan = emit_insn_after (gen_consttable_4 (p->value), scan); break; case 8: scan = emit_insn_after (gen_consttable_8 (p->value), scan); break; default: abort (); break; } } scan = emit_insn_after (gen_consttable_end (), scan); scan = emit_barrier_after (scan); pool_size = 0;}/* Non zero if the src operand needs to be fixed up */staticintfixit (src, mode) rtx src; enum machine_mode mode;{ return ((CONSTANT_P (src) && (GET_CODE (src) != CONST_INT || ! (CONST_OK_FOR_LETTER_P (INTVAL (src), 'I') || CONST_OK_FOR_LETTER_P (INTVAL (src), 'J') || (mode != DImode && CONST_OK_FOR_LETTER_P (INTVAL (src), 'K'))))) || (mode == SImode && GET_CODE (src) == MEM && GET_CODE (XEXP (src, 0)) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (XEXP (src, 0))));}/* Find the last barrier less than MAX_COUNT bytes from FROM, or create one. */#define MAX_COUNT_SI 1000 static rtxfind_barrier (from) rtx from;{ int count = 0; rtx found_barrier = 0; rtx label; while (from && count < MAX_COUNT_SI) { if (GET_CODE (from) == BARRIER) return from; /* Count the length of this insn */ if (GET_CODE (from) == INSN && GET_CODE (PATTERN (from)) == SET && CONSTANT_P (SET_SRC (PATTERN (from))) && CONSTANT_POOL_ADDRESS_P (SET_SRC (PATTERN (from)))) { rtx src = SET_SRC (PATTERN (from)); count += 2; } else count += get_attr_length (from); from = NEXT_INSN (from); } /* We didn't find a barrier in time to dump our stuff, so we'll make one */ label = gen_label_rtx (); if (from) from = PREV_INSN (from); else from = get_last_insn (); /* Walk back to be just before any jump */ while (GET_CODE (from) == JUMP_INSN || GET_CODE (from) == NOTE || GET_CODE (from) == CODE_LABEL) from = PREV_INSN (from); from = emit_jump_insn_after (gen_jump (label), from); JUMP_LABEL (from) = label; found_barrier = emit_barrier_after (from); emit_label_after (label, found_barrier); return found_barrier;}/* Non zero if the insn is a move instruction which needs to be fixed. */ static intbroken_move (insn) rtx insn;{ if (!INSN_DELETED_P (insn) && GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SET) { rtx pat = PATTERN (insn); rtx src = SET_SRC (pat); rtx dst = SET_DEST (pat); enum machine_mode mode = GET_MODE (dst); if (dst == pc_rtx) return 0; return fixit (src, mode); } return 0;}#ifdef DBX_DEBUGGING_INFO/* Recursively search through all of the blocks in a function checking to see if any of the variables created in that function match the RTX called 'orig'. If they do then replace them with the RTX called 'new'. */static voidreplace_symbols_in_block (tree block, rtx orig, rtx new){ for (; block; block = BLOCK_CHAIN (block)) { tree sym; if (! TREE_USED (block)) continue; for (sym = BLOCK_VARS (block); sym; sym = TREE_CHAIN (sym)) { if ( (DECL_NAME (sym) == 0 && TREE_CODE (sym) != TYPE_DECL) || DECL_IGNORED_P (sym) || TREE_CODE (sym) != VAR_DECL || DECL_EXTERNAL (sym) || ! rtx_equal_p (DECL_RTL (sym), orig) ) continue; DECL_RTL (sym) = new; } replace_symbols_in_block (BLOCK_SUBBLOCKS (block), orig, new); }}#endifvoidthumb_reorg (first) rtx first;{ rtx insn; for (insn = first; insn; insn = NEXT_INSN (insn)) { if (broken_move (insn)) { /* This is a broken move instruction, scan ahead looking for a barrier to stick the constant table behind */ rtx scan; rtx barrier = find_barrier (insn); /* Now find all the moves between the points and modify them */ for (scan = insn; scan != barrier; scan = NEXT_INSN (scan)) { if (broken_move (scan)) { /* This is a broken move instruction, add it to the pool */ rtx pat = PATTERN (scan); rtx src = SET_SRC (pat); rtx dst = SET_DEST (pat); enum machine_mode mode = GET_MODE (dst); HOST_WIDE_INT offset; rtx newinsn; rtx newsrc; /* If this is an HImode constant load, convert it into an SImode constant load. Since the register is always 32 bits this is safe. We have to do this, since the load pc-relative instruction only does a 32-bit load. */ if (mode == HImode) { mode = SImode; if (GET_CODE (dst) != REG) abort (); PUT_MODE (dst, SImode); } offset = add_constant (src, mode); newsrc = gen_rtx (MEM, mode, plus_constant (gen_rtx (LABEL_REF, VOIDmode, pool_vector_label), offset)); /* Build a jump insn wrapper around the move instead of an ordinary insn, because we want to have room for the target label rtx in fld[7], which an ordinary insn doesn't have. */ newinsn = emit_jump_insn_after (gen_rtx (SET, VOIDmode, dst, newsrc), scan); JUMP_LABEL (newinsn) = pool_vector_label; /* But it's still an ordinary insn */ PUT_CODE (newinsn, INSN);#ifdef DBX_DEBUGGING_INFO /* If debugging information is going to be emitted then we must make sure that any refences to symbols which are removed by the above code are also removed in the descriptions of the function's variables. Failure to do this means that the debugging information emitted could refer to symbols which are not emited by output_constant_pool() because mark_constant_pool() never sees them as being used. */ if (optimize > 0 /* These are the tests used in output_constant_pool() */ && flag_expensive_optimizations /* to decide if the constant pool will be marked. */ && write_symbols == DBX_DEBUG /* Only necessary if debugging info is being emitted. */ && GET_CODE (src) == MEM /* Only necessary for references to memory ... */ && GET_CODE (XEXP (src, 0)) == SYMBOL_REF) /* ... whose address is given by a symbol. */ { replace_symbols_in_block (DECL_INITIAL (current_function_decl), src, newsrc); }#endif /* Kill old insn */ delete_insn (scan); scan = newinsn; } } dump_table (barrier); } }}/* Routines for generating rtl */voidthumb_expand_movstrqi (operands) rtx *operands;{ rtx out = copy_to_mode_reg (SImode, XEXP (operands[0], 0)); rtx in = copy_to_mode_reg (SImode, XEXP (operands[1], 0)); HOST_WIDE_INT len = INTVAL (operands[2]); HOST_WIDE_INT offset = 0; while (len >= 12) { emit_insn (gen_movmem12b (out, in)); len -= 12; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -