diff options
author | YamaArashi <shadow962@live.com> | 2016-02-13 16:22:29 -0800 |
---|---|---|
committer | YamaArashi <shadow962@live.com> | 2016-02-13 16:22:29 -0800 |
commit | 3878c0ffc606b95917af8a9f3fb406a1718b6c2e (patch) | |
tree | 89b919f26b8670b3629914df486859370b39a8ba /gcc/config/arm/thumb_010110a.c | |
parent | 47d5ab74c312567d17cd5df362d73d36f53eb0c2 (diff) |
remove alternate versions of thumb files
Diffstat (limited to 'gcc/config/arm/thumb_010110a.c')
-rwxr-xr-x | gcc/config/arm/thumb_010110a.c | 2124 |
1 files changed, 0 insertions, 2124 deletions
diff --git a/gcc/config/arm/thumb_010110a.c b/gcc/config/arm/thumb_010110a.c deleted file mode 100755 index ef7ebff..0000000 --- a/gcc/config/arm/thumb_010110a.c +++ /dev/null @@ -1,2124 +0,0 @@ -/* 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 Ltd - -This file is part of GNU CC. - -GNU CC is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the 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 of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with GNU CC; see the file COPYING. If not, write to -the 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 */ -int -reload_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. */ -int -thumb_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)); -} - -int -thumb_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; -} - -int -thumb_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_INT -add_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 */ -static -int -fixit (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 rtx -find_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 int -broken_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; -} - -/* 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 void -replace_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); - } -} - -void -thumb_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); - - /* 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. */ - - - /* These are the tests used in - output_constant_pool() to decide if the constant - pool will be marked. Only necessary if debugging - info is being emitted. Only necessary for - references to memory whose address is given by a - symbol. */ - - if (optimize > 0 - && flag_expensive_optimizations - && write_symbols != NO_DEBUG - && GET_CODE (src) == MEM - && GET_CODE (XEXP (src, 0)) == SYMBOL_REF) - replace_symbols_in_block - (DECL_INITIAL (current_function_decl), src, newsrc); - - /* Kill old insn */ - delete_insn (scan); - scan = newinsn; - } - } - dump_table (barrier); - } - } -} - - -/* Routines for generating rtl */ - -void -thumb_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; - } - if (len >= 8) - { - emit_insn (gen_movmem8b (out, in)); - len -= 8; - } - if (len >= 4) - { - rtx reg = gen_reg_rtx (SImode); - emit_insn (gen_movsi (reg, gen_rtx (MEM, SImode, in))); - emit_insn (gen_movsi (gen_rtx (MEM, SImode, out), reg)); - len -= 4; - offset += 4; - } - if (len >= 2) - { - rtx reg = gen_reg_rtx (HImode); - emit_insn (gen_movhi (reg, gen_rtx (MEM, HImode, - plus_constant (in, offset)))); - emit_insn (gen_movhi (gen_rtx (MEM, HImode, plus_constant (out, offset)), - reg)); - len -= 2; - offset += 2; - } - if (len) - { - rtx reg = gen_reg_rtx (QImode); - emit_insn (gen_movqi (reg, gen_rtx (MEM, QImode, - plus_constant (in, offset)))); - emit_insn (gen_movqi (gen_rtx (MEM, QImode, plus_constant (out, offset)), - reg)); - } -} - - -/* Routines for reloading */ - -void -thumb_reload_out_si (operands) - rtx operands; -{ - abort (); -} - -/* CYGNUS LOCAL nickc/thumb-pe */ - -#ifdef THUMB_PE -/* Return non-zero if FUNC is a naked function. */ - -static int -arm_naked_function_p (func) - tree func; -{ - tree a; - - if (TREE_CODE (func) != FUNCTION_DECL) - abort (); - - a = lookup_attribute ("naked", DECL_MACHINE_ATTRIBUTES (func)); - return a != NULL_TREE; -} -#endif -/* END CYGNUS LOCAL nickc/thumb-pe */ - -/* Return non-zero if FUNC must be entered in ARM mode. */ -int -is_called_in_ARM_mode (func) - tree func; -{ - if (TREE_CODE (func) != FUNCTION_DECL) - abort (); - - /* Ignore the problem about functions whoes address is taken. */ - if (TARGET_CALLEE_INTERWORKING && TREE_PUBLIC (func)) - return TRUE; - -/* CYGNUS LOCAL nickc/thumb-pe */ -#ifdef THUMB_PE - return lookup_attribute ("interfacearm", DECL_MACHINE_ATTRIBUTES (func)) != NULL_TREE; -#else - return FALSE; -#endif -/* END CYGNUS LOCAL */ -} - - -/* Routines for emitting code */ - -void -final_prescan_insn(insn) - rtx insn; -{ - extern int *insn_addresses; - - if (flag_print_asm_name) - fprintf (asm_out_file, "%s 0x%04x\n", ASM_COMMENT_START, - insn_addresses[INSN_UID (insn)]); -} - - -static void thumb_pushpop ( FILE *, int, int ); /* Forward declaration. */ - -#ifdef __GNUC__ -inline -#endif -static int -number_of_first_bit_set (mask) - int mask; -{ - int bit; - - for (bit = 0; - (mask & (1 << bit)) == 0; - ++ bit) - continue; - - return bit; -} - -#define ARG_1_REGISTER 0 -#define ARG_2_REGISTER 1 -#define ARG_3_REGISTER 2 -#define ARG_4_REGISTER 3 -#define WORK_REGISTER 7 -#define FRAME_POINTER 11 -#define IP_REGISTER 12 -#define STACK_POINTER STACK_POINTER_REGNUM -#define LINK_REGISTER 14 -#define PROGRAM_COUNTER 15 - -/* Generate code to return from a thumb function. If - 'reg_containing_return_addr' is -1, then the return address is - actually on the stack, at the stack pointer. */ -static void -thumb_exit (f, reg_containing_return_addr) - FILE * f; - int reg_containing_return_addr; -{ - int regs_available_for_popping; - int regs_to_pop; - int pops_needed; - int reg; - int available; - int required; - int mode; - int size; - int restore_a4 = FALSE; - - /* Compute the registers we need to pop. */ - regs_to_pop = 0; - pops_needed = 0; - - if (reg_containing_return_addr == -1) - { - regs_to_pop |= 1 << LINK_REGISTER; - ++ pops_needed; - } - - if (TARGET_BACKTRACE) - { - /* Restore frame pointer and stack pointer. */ - regs_to_pop |= (1 << FRAME_POINTER) | (1 << STACK_POINTER); - pops_needed += 2; - } - - /* If there is nothing to pop then just emit the BX instruction and return.*/ - if (pops_needed == 0) - { - asm_fprintf (f, "\tbx\t%s\n", reg_names [reg_containing_return_addr]); - - return; - } - - /* Otherwise if we are not supporting interworking and we have not created - a backtrace structure and the function was not entered in ARM mode then - just pop the return address straight into the PC. */ - else if ( ! TARGET_THUMB_INTERWORK - && ! TARGET_BACKTRACE - && ! is_called_in_ARM_mode (current_function_decl)) - { - asm_fprintf (f, "\tpop\t{pc}\n" ); - - return; - } - - /* Find out how many of the (return) argument registers we can corrupt. */ - regs_available_for_popping = 0; - -#ifdef RTX_CODE - /* If we can deduce the registers used from the function's return value. - This is more reliable that examining regs_ever_live[] because that - will be set if the register is ever used in the function, not just if - the register is used to hold a return value. */ - - if (current_function_return_rtx != 0) - mode = GET_MODE (current_function_return_rtx); - else -#endif - mode = DECL_MODE (DECL_RESULT (current_function_decl)); - - size = GET_MODE_SIZE (mode); - - if (size == 0) - { - /* In a void function we can use any argument register. - In a function that returns a structure on the stack - we can use the second and third argument registers. */ - if (mode == VOIDmode) - regs_available_for_popping = - (1 << ARG_1_REGISTER) - | (1 << ARG_2_REGISTER) - | (1 << ARG_3_REGISTER); - else - regs_available_for_popping = - (1 << ARG_2_REGISTER) - | (1 << ARG_3_REGISTER); - } - else if (size <= 4) regs_available_for_popping = - (1 << ARG_2_REGISTER) - | (1 << ARG_3_REGISTER); - else if (size <= 8) regs_available_for_popping = - (1 << ARG_3_REGISTER); - - /* Match registers to be popped with registers into which we pop them. */ - for (available = regs_available_for_popping, - required = regs_to_pop; - required != 0 && available != 0; - available &= ~(available & - available), - required &= ~(required & - required)) - -- pops_needed; - - /* If we have any popping registers left over, remove them. */ - if (available > 0) - regs_available_for_popping &= ~ available; - - /* Otherwise if we need another popping register we can use - the fourth argument register. */ - else if (pops_needed) - { - /* If we have not found any free argument registers and - reg a4 contains the return address, we must move it. */ - if (regs_available_for_popping == 0 - && reg_containing_return_addr == ARG_4_REGISTER) - { - asm_fprintf (f, "\tmov\t%s, %s\n", - reg_names [LINK_REGISTER], reg_names [ARG_4_REGISTER]); - reg_containing_return_addr = LINK_REGISTER; - } - else if (size > 12) - { - /* Register a4 is being used to hold part of the return value, - but we have dire need of a free, low register. */ - restore_a4 = TRUE; - - asm_fprintf (f, "\tmov\t%s, %s\n", - reg_names [IP_REGISTER], reg_names [ARG_4_REGISTER]); - } - - if (reg_containing_return_addr != ARG_4_REGISTER) - { - /* The fourth argument register is available. */ - regs_available_for_popping |= 1 << ARG_4_REGISTER; - - -- pops_needed; - } - } - - /* Pop as many registers as we can. */ - thumb_pushpop (f, regs_available_for_popping, FALSE); - - /* Process the registers we popped. */ - if (reg_containing_return_addr == -1) - { - /* The return address was popped into the lowest numbered register. */ - regs_to_pop &= ~ (1 << LINK_REGISTER); - - reg_containing_return_addr = - number_of_first_bit_set (regs_available_for_popping); - - /* Remove this register for the mask of available registers, so that - the return address will not be corrupted by futher pops. */ - regs_available_for_popping &= ~ (1 << reg_containing_return_addr); - } - - /* If we popped other registers then handle them here. */ - if (regs_available_for_popping) - { - int frame_pointer; - - /* Work out which register currently contains the frame pointer. */ - frame_pointer = number_of_first_bit_set (regs_available_for_popping); - - /* Move it into the correct place. */ - asm_fprintf (f, "\tmov\tfp, %s\n", reg_names [frame_pointer]); - - /* (Temporarily) remove it from the mask of popped registers. */ - regs_available_for_popping &= ~ (1 << frame_pointer); - regs_to_pop &= ~ (1 << FRAME_POINTER); - - if (regs_available_for_popping) - { - int stack_pointer; - - /* We popped the stack pointer as well, find the register that - contains it.*/ - stack_pointer = number_of_first_bit_set (regs_available_for_popping); - - /* Move it into the stack register. */ - asm_fprintf (f, "\tmov\tsp, %s\n", reg_names [stack_pointer]); - - /* At this point we have popped all necessary registers, so - do not worry about restoring regs_available_for_popping - to its correct value: - - assert (pops_needed == 0) - assert (regs_available_for_popping == (1 << frame_pointer)) - assert (regs_to_pop == (1 << STACK_POINTER)) */ - } - else - { - /* Since we have just move the popped value into the frame - pointer, the popping register is available for reuse, and - we know that we still have the stack pointer left to pop. */ - regs_available_for_popping |= (1 << frame_pointer); - } - } - - /* If we still have registers left on the stack, but we no longer have - any registers into which we can pop them, then we must move the return - address into the link register and make available the register that - contained it. */ - if (regs_available_for_popping == 0 && pops_needed > 0) - { - regs_available_for_popping |= 1 << reg_containing_return_addr; - - asm_fprintf (f, "\tmov\t%s, %s\n", - reg_names [LINK_REGISTER], - reg_names [reg_containing_return_addr]); - - reg_containing_return_addr = LINK_REGISTER; - } - - /* If we have registers left on the stack then pop some more. - We know that at most we will want to pop FP and SP. */ - if (pops_needed > 0) - { - int popped_into; - int move_to; - - thumb_pushpop (f, regs_available_for_popping, FALSE); - - /* We have popped either FP or SP. - Move whichever one it is into the correct register. */ - popped_into = number_of_first_bit_set (regs_available_for_popping); - move_to = number_of_first_bit_set (regs_to_pop); - - asm_fprintf (f, "\tmov\t%s, %s\n", - reg_names [move_to], reg_names [popped_into]); - - regs_to_pop &= ~ (1 << move_to); - - -- pops_needed; - } - - /* If we still have not popped everything then we must have only - had one register available to us and we are now popping the SP. */ - if (pops_needed > 0) - { - int popped_into; - - thumb_pushpop (f, regs_available_for_popping, FALSE); - - popped_into = number_of_first_bit_set (regs_available_for_popping); - - asm_fprintf (f, "\tmov\tsp, %s\n", reg_names [popped_into]); - - /* - assert (regs_to_pop == (1 << STACK_POINTER)) - assert (pops_needed == 1) - */ - } - - /* If necessary restore the a4 register. */ - if (restore_a4) - { - if (reg_containing_return_addr != LINK_REGISTER) - { - asm_fprintf (f, "\tmov\t%s, %s\n", - reg_names [LINK_REGISTER], reg_names [ARG_4_REGISTER]); - reg_containing_return_addr = LINK_REGISTER; - } - - asm_fprintf (f, "\tmov\t%s, %s\n", - reg_names [ARG_4_REGISTER], reg_names [IP_REGISTER]); - } - - /* Return to caller. */ - asm_fprintf (f, "\tbx\t%s\n", reg_names [reg_containing_return_addr]); -} - -/* Emit code to push or pop registers to or from the stack. */ -static void -thumb_pushpop (f, mask, push) - FILE * f; - int mask; - int push; -{ - int regno; - int lo_mask = mask & 0xFF; - - if (lo_mask == 0 && ! push && (mask & (1 << 15))) - { - /* Special case. Do not generate a POP PC statement here, do it in - thumb_exit() */ - - thumb_exit (f, -1); - return; - } - - asm_fprintf (f, "\t%s\t{", push ? "push" : "pop"); - - /* Look at the low registers first. */ - - for (regno = 0; regno < 8; regno ++, lo_mask >>= 1) - { - if (lo_mask & 1) - { - asm_fprintf (f, reg_names[regno]); - - if ((lo_mask & ~1) != 0) - asm_fprintf (f, ", "); - } - } - - if (push && (mask & (1 << 14))) - { - /* Catch pushing the LR. */ - - if (mask & 0xFF) - asm_fprintf (f, ", "); - - asm_fprintf (f, reg_names[14]); - } - else if (!push && (mask & (1 << 15))) - { - /* Catch popping the PC. */ - - if (TARGET_THUMB_INTERWORK || TARGET_BACKTRACE) - { - /* The PC is never poped directly, instead - it is popped into r3 and then BX is used. */ - - asm_fprintf (f, "}\n"); - - thumb_exit (f, -1); - - return; - } - else - { - if (mask & 0xFF) - asm_fprintf (f, ", "); - - asm_fprintf (f, reg_names[15]); - } - } - - asm_fprintf (f, "}\n"); -} - -/* Returns non-zero if the current function contains a far jump */ - -int -far_jump_used_p (void) -{ - rtx insn; - - for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) - { - if (GET_CODE (insn) == JUMP_INSN - /* Ignore tablejump patterns. */ - && GET_CODE (PATTERN (insn)) != ADDR_VEC - && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC - && get_attr_far_jump (insn) == FAR_JUMP_YES) - return 1; - } - - return 0; -} - -static int return_used_this_function = 0; - -char * -output_return () -{ - int regno; - int live_regs_mask = 0; - - /* CYGNUS LOCAL nickc/thumb-pe */ -#ifdef THUMB_PE - /* If a function is naked, don't use the "return" insn. */ - if (arm_naked_function_p (current_function_decl)) - return ""; -#endif - /* END CYGNUS LOCAL nickc/thumb-pe */ - - return_used_this_function = 1; - - for (regno = 0; regno < 8; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - live_regs_mask |= 1 << regno; - - if (live_regs_mask == 0) - { - if (leaf_function_p () && ! far_jump_used_p()) - { - thumb_exit (asm_out_file, 14); - } - else if ( TARGET_THUMB_INTERWORK - || TARGET_BACKTRACE - || is_called_in_ARM_mode (current_function_decl)) - { - thumb_exit (asm_out_file, -1); - } - else - asm_fprintf (asm_out_file, "\tpop\t{pc}\n"); - } - else - { - asm_fprintf (asm_out_file, "\tpop\t{"); - - for (regno = 0; live_regs_mask; regno ++, live_regs_mask >>= 1) - if (live_regs_mask & 1) - { - asm_fprintf (asm_out_file, reg_names[regno]); - if (live_regs_mask & ~1) - asm_fprintf (asm_out_file, ", "); - } - - if ( TARGET_THUMB_INTERWORK - || TARGET_BACKTRACE - || is_called_in_ARM_mode (current_function_decl)) - { - asm_fprintf (asm_out_file, "}\n"); - thumb_exit (asm_out_file, -1); - } - else - asm_fprintf (asm_out_file, ", pc}\n"); - } - - return ""; -} - -void -thumb_function_prologue (f, frame_size) - FILE *f; - int frame_size; -{ - int amount = frame_size + current_function_outgoing_args_size; - int live_regs_mask = 0; - int high_regs_pushed = 0; - int store_arg_regs = 0; - int regno; - -/* CYGNUS LOCAL nickc/thumb-pe */ -#ifdef THUMB_PE - if (arm_naked_function_p (current_function_decl)) - return; -#endif -/* CYGNUS LOCAL nickc/thumb-pe */ - - if (is_called_in_ARM_mode (current_function_decl)) - { - char * name; - if (GET_CODE (DECL_RTL (current_function_decl)) != MEM) - abort(); - if (GET_CODE (XEXP (DECL_RTL (current_function_decl), 0)) != SYMBOL_REF) - abort(); - name = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0); - - /* Generate code sequence to switch us into Thumb mode. */ - /* The .code 32 directive has already been emitted by - ASM_DECLARE_FUNCITON_NAME */ - asm_fprintf (f, "\torr\tr12, pc, #1\n"); - asm_fprintf (f, "\tbx\tr12\n"); - - /* Generate a label, so that the debugger will notice the - change in instruction sets. This label is also used by - the assembler to bypass the ARM code when this function - is called from a Thumb encoded function elsewhere in the - same file. Hence the definition of STUB_NAME here must - agree with the definition in gas/config/tc-arm.c */ - -#define STUB_NAME ".real_start_of" - - asm_fprintf (f, "\t.code\t16\n"); - asm_fprintf (f, "\t.globl %s%U%s\n", STUB_NAME, name); - asm_fprintf (f, "\t.thumb_func\n"); - asm_fprintf (f, "%s%U%s:\n", STUB_NAME, name); - } - - if (current_function_anonymous_args && current_function_pretend_args_size) - store_arg_regs = 1; - - if (current_function_pretend_args_size) - { - if (store_arg_regs) - { - asm_fprintf (f, "\tpush\t{"); - for (regno = 4 - current_function_pretend_args_size / 4 ; regno < 4; - regno++) - asm_fprintf (f, "%s%s", reg_names[regno], regno == 3 ? "" : ", "); - asm_fprintf (f, "}\n"); - } - else - asm_fprintf (f, "\tsub\t%Rsp, %Rsp, #%d\n", - current_function_pretend_args_size); - } - - for (regno = 0; regno < 8; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - live_regs_mask |= 1 << regno; - - if (live_regs_mask || ! leaf_function_p () || far_jump_used_p()) - live_regs_mask |= 1 << 14; - - if (TARGET_BACKTRACE) - { - char * name; - int offset; - int work_register = 0; - - - /* We have been asked to create a stack backtrace structure. - The code looks like this: - - 0 .align 2 - 0 func: - 0 sub SP, #16 Reserve space for 4 registers. - 2 push {R7} Get a work register. - 4 add R7, SP, #20 Get the stack pointer before the push. - 6 str R7, [SP, #8] Store the stack pointer (before reserving the space). - 8 mov R7, PC Get hold of the start of this code plus 12. - 10 str R7, [SP, #16] Store it. - 12 mov R7, FP Get hold of the current frame pointer. - 14 str R7, [SP, #4] Store it. - 16 mov R7, LR Get hold of the current return address. - 18 str R7, [SP, #12] Store it. - 20 add R7, SP, #16 Point at the start of the backtrace structure. - 22 mov FP, R7 Put this value into the frame pointer. */ - - if ((live_regs_mask & 0xFF) == 0) - { - /* See if the a4 register is free. */ - - if (regs_ever_live[ 3 ] == 0) - work_register = 3; - else /* We must push a register of our own */ - live_regs_mask |= (1 << 7); - } - - if (work_register == 0) - { - /* Select a register from the list that will be pushed to use as our work register. */ - - for (work_register = 8; work_register--;) - if ((1 << work_register) & live_regs_mask) - break; - } - - name = reg_names[ work_register ]; - - asm_fprintf (f, "\tsub\tsp, sp, #16\t@ Create stack backtrace structure\n"); - - if (live_regs_mask) - thumb_pushpop (f, live_regs_mask, 1); - - for (offset = 0, work_register = 1 << 15; work_register; work_register >>= 1) - if (work_register & live_regs_mask) - offset += 4; - - asm_fprintf (f, "\tadd\t%s, sp, #%d\n", - name, offset + 16 + current_function_pretend_args_size); - - asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset + 4); - - /* Make sure that the instruction fetching the PC is in the right place - to calculate "start of backtrace creation code + 12". */ - - if (live_regs_mask) - { - asm_fprintf (f, "\tmov\t%s, pc\n", name); - asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset + 12); - asm_fprintf (f, "\tmov\t%s, fp\n", name); - asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset); - } - else - { - asm_fprintf (f, "\tmov\t%s, fp\n", name); - asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset); - asm_fprintf (f, "\tmov\t%s, pc\n", name); - asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset + 12); - } - - asm_fprintf (f, "\tmov\t%s, lr\n", name); - asm_fprintf (f, "\tstr\t%s, [sp, #%d]\n", name, offset + 8); - asm_fprintf (f, "\tadd\t%s, sp, #%d\n", name, offset + 12); - asm_fprintf (f, "\tmov\tfp, %s\t\t@ Backtrace structure created\n", name); - } - else if (live_regs_mask) - thumb_pushpop (f, live_regs_mask, 1); - - for (regno = 8; regno < 13; regno++) - { - if (regs_ever_live[regno] && ! call_used_regs[regno]) - high_regs_pushed++; - } - - if (high_regs_pushed) - { - int pushable_regs = 0; - int mask = live_regs_mask & 0xff; - int next_hi_reg; - - for (next_hi_reg = 12; next_hi_reg > 7; next_hi_reg--) - { - if (regs_ever_live[next_hi_reg] && ! call_used_regs[next_hi_reg]) - break; - } - - pushable_regs = mask; - - if (pushable_regs == 0) - { - /* desperation time -- this probably will never happen */ - if (regs_ever_live[3] || ! call_used_regs[3]) - asm_fprintf (f, "\tmov\t%s, %s\n", reg_names[12], reg_names[3]); - mask = 1 << 3; - } - - while (high_regs_pushed > 0) - { - for (regno = 7; regno >= 0; regno--) - { - if (mask & (1 << regno)) - { - asm_fprintf (f, "\tmov\t%s, %s\n", reg_names[regno], - reg_names[next_hi_reg]); - high_regs_pushed--; - if (high_regs_pushed) - for (next_hi_reg--; next_hi_reg > 7; next_hi_reg--) - { - if (regs_ever_live[next_hi_reg] - && ! call_used_regs[next_hi_reg]) - break; - } - else - { - mask &= ~ ((1 << regno) - 1); - break; - } - } - } - thumb_pushpop (f, mask, 1); - } - - if (pushable_regs == 0 && (regs_ever_live[3] || ! call_used_regs[3])) - asm_fprintf (f, "\tmov\t%s, %s\n", reg_names[3], reg_names[12]); - } -} - -void -thumb_expand_prologue () -{ - HOST_WIDE_INT amount = (get_frame_size () - + current_function_outgoing_args_size); - int regno; - int live_regs_mask; - - /* CYGNUS LOCAL nickc/thumb-pe */ -#ifdef THUMB_PE - /* Naked functions don't have prologues. */ - if (arm_naked_function_p (current_function_decl)) - return; -#endif - /* END CYGNUS LOCAL nickc/thumb-pe */ - - if (amount) - { - live_regs_mask = 0; - for (regno = 0; regno < 8; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - live_regs_mask |= 1 << regno; - - if (amount < 512) - emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, - GEN_INT (-amount))); - else - { - rtx reg, spare; - - if ((live_regs_mask & 0xff) == 0) /* Very unlikely */ - emit_insn (gen_movsi (spare = gen_rtx (REG, SImode, 12), - reg = gen_rtx (REG, SImode, 4))); - else - { - for (regno = 0; regno < 8; regno++) - if (live_regs_mask & (1 << regno)) - break; - reg = gen_rtx (REG, SImode, regno); - } - - emit_insn (gen_movsi (reg, GEN_INT (-amount))); - emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg)); - if ((live_regs_mask & 0xff) == 0) - emit_insn (gen_movsi (reg, spare)); - } - } - - if (frame_pointer_needed) - { - if (current_function_outgoing_args_size) - { - rtx offset = GEN_INT (current_function_outgoing_args_size); - - if (current_function_outgoing_args_size < 1024) - emit_insn (gen_addsi3 (frame_pointer_rtx, stack_pointer_rtx, - offset)); - else - { - emit_insn (gen_movsi (frame_pointer_rtx, offset)); - emit_insn (gen_addsi3 (frame_pointer_rtx, frame_pointer_rtx, - stack_pointer_rtx)); - } - } - else - emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx)); - } - - /* if (profile_flag || profile_block_flag) */ - emit_insn (gen_blockage ()); -} - -void -thumb_expand_epilogue () -{ - HOST_WIDE_INT amount = (get_frame_size () - + current_function_outgoing_args_size); - int regno; - - /* CYGNUS LOCAL nickc/thumb-pe */ -#ifdef THUMB_PE - /* Naked functions don't have epilogues. */ - if (arm_naked_function_p (current_function_decl)) - return; -#endif - /* END CYGNUS LOCAL nickc/thumb-pe */ - - if (amount) - { - if (amount < 512) - emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, - GEN_INT (amount))); - else - { - rtx reg = gen_rtx (REG, SImode, 3); /* Always free in the epilogue */ - - emit_insn (gen_movsi (reg, GEN_INT (amount))); - emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg)); - } - /* if (profile_flag || profile_block_flag) */ - emit_insn (gen_blockage ()); - } -} - -void -thumb_function_epilogue (f, frame_size) - FILE *f; - int frame_size; -{ - /* ??? Probably not safe to set this here, since it assumes that a - function will be emitted as assembly immediately after we generate - RTL for it. This does not happen for inline functions. */ - return_used_this_function = 0; -#if 0 /* TODO : comment not really needed */ - fprintf (f, "%s THUMB Epilogue\n", ASM_COMMENT_START); -#endif -} - -/* The bits which aren't usefully expanded as rtl. */ -char * -thumb_unexpanded_epilogue () -{ - int regno; - int live_regs_mask = 0; - int high_regs_pushed = 0; - int leaf_function = leaf_function_p (); - int had_to_push_lr; - - if (return_used_this_function) - return ""; - - for (regno = 0; regno < 8; regno++) - if (regs_ever_live[regno] && ! call_used_regs[regno]) - live_regs_mask |= 1 << regno; - - for (regno = 8; regno < 13; regno++) - { - if (regs_ever_live[regno] && ! call_used_regs[regno]) - high_regs_pushed ++; - } - - /* The prolog may have pushed some high registers to use as - work registers. eg the testuite file: - gcc/testsuite/gcc/gcc.c-torture/execute/complex-2.c - compiles to produce: - push {r4, r5, r6, r7, lr} - mov r7, r9 - mov r6, r8 - push {r6, r7} - as part of the prolog. We have to undo that pushing here. */ - - if (high_regs_pushed) - { - int mask = live_regs_mask; - int next_hi_reg; - int size; - int mode; - -#ifdef RTX_CODE - /* If we can deduce the registers used from the function's return value. - This is more reliable that examining regs_ever_live[] because that - will be set if the register is ever used in the function, not just if - the register is used to hold a return value. */ - - if (current_function_return_rtx != 0) - { - mode = GET_MODE (current_function_return_rtx); - } - else -#endif - { - mode = DECL_MODE (DECL_RESULT (current_function_decl)); - } - - size = GET_MODE_SIZE (mode); - - /* Unless we are returning a type of size > 12 register r3 is available. */ - if (size < 13) - mask |= 1 << 3; - - if (mask == 0) - { - /* Oh dear! We have no low registers into which we can pop high registers! */ - - fatal ("No low registers available for popping high registers"); - } - - for (next_hi_reg = 8; next_hi_reg < 13; next_hi_reg++) - if (regs_ever_live[next_hi_reg] && ! call_used_regs[next_hi_reg]) - break; - - while (high_regs_pushed) - { - /* Find low register(s) into which the high register(s) can be popped. */ - for (regno = 0; regno < 8; regno++) - { - if (mask & (1 << regno)) - high_regs_pushed--; - if (high_regs_pushed == 0) - break; - } - - mask &= (2 << regno) - 1; /* A noop if regno == 8 */ - - /* Pop the values into the low register(s). */ - thumb_pushpop (asm_out_file, mask, 0); - - /* Move the value(s) into the high registers. */ - for (regno = 0; regno < 8; regno++) - { - if (mask & (1 << regno)) - { - asm_fprintf (asm_out_file, "\tmov\t%s, %s\n", - reg_names[next_hi_reg], reg_names[regno]); - for (next_hi_reg++; next_hi_reg < 13; next_hi_reg++) - if (regs_ever_live[next_hi_reg] && - ! call_used_regs[next_hi_reg]) - break; - } - } - } - } - - had_to_push_lr = (live_regs_mask || ! leaf_function || far_jump_used_p()); - - if (TARGET_BACKTRACE && ((live_regs_mask & 0xFF) == 0) && regs_ever_live[ ARG_4_REGISTER ] != 0) - { - /* The stack backtrace structure creation code had to - push R7 in order to get a work register, so we pop - it now. */ - - live_regs_mask |= (1 << WORK_REGISTER); - } - - if (current_function_pretend_args_size == 0 || TARGET_BACKTRACE) - { - if (had_to_push_lr - && ! is_called_in_ARM_mode (current_function_decl)) - live_regs_mask |= 1 << PROGRAM_COUNTER; - - /* Either no argument registers were pushed or a backtrace - structure was created which includes an adjusted stack - pointer, so just pop everything. */ - - if (live_regs_mask) - thumb_pushpop (asm_out_file, live_regs_mask, FALSE); - - /* We have either just popped the return address into the - PC or it is was kept in LR for the entire function or - it is still on the stack because we do not want to - return by doing a pop {pc}. */ - - if ((live_regs_mask & (1 << PROGRAM_COUNTER)) == 0) - thumb_exit (asm_out_file, - (had_to_push_lr - && is_called_in_ARM_mode (current_function_decl)) ? - -1 : LINK_REGISTER); - } - else - { - /* Pop everything but the return address. */ - live_regs_mask &= ~ (1 << PROGRAM_COUNTER); - - if (live_regs_mask) - thumb_pushpop (asm_out_file, live_regs_mask, FALSE); - - if (had_to_push_lr) - { - /* Get the return address into a temporary register. */ - thumb_pushpop (asm_out_file, 1 << ARG_4_REGISTER, 0); - } - - /* Remove the argument registers that were pushed onto the stack. */ - asm_fprintf (asm_out_file, "\tadd\t%s, %s, #%d\n", - reg_names [STACK_POINTER], - reg_names [STACK_POINTER], - current_function_pretend_args_size); - - thumb_exit (asm_out_file, had_to_push_lr ? ARG_4_REGISTER : LINK_REGISTER); - } - - return ""; -} - -/* Handle the case of a double word load into a low register from - a computed memory address. The computed address may involve a - register which is overwritten by the load. */ - -char * -thumb_load_double_from_address (operands) - rtx * operands; -{ - rtx addr; - rtx base; - rtx offset; - rtx arg1; - rtx arg2; - - if (GET_CODE (operands[0]) != REG) - fatal ("thumb_load_double_from_address: destination is not a register"); - - if (GET_CODE (operands[1]) != MEM) - fatal ("thumb_load_double_from_address: source is not a computed memory address"); - - /* Get the memory address. */ - - addr = XEXP (operands[1], 0); - - /* Work out how the memory address is computed. */ - - switch (GET_CODE (addr)) - { - case REG: - operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4)); - - if (REGNO (operands[0]) == REGNO (addr)) - { - output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); - output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); - } - else - { - output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); - output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); - } - break; - - case CONST: - /* Compute <address> + 4 for the high order load. */ - - operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4)); - - output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); - output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); - break; - - case PLUS: - arg1 = XEXP (addr, 0); - arg2 = XEXP (addr, 1); - - if (CONSTANT_P (arg1)) - base = arg2, offset = arg1; - else - base = arg1, offset = arg2; - - if (GET_CODE (base) != REG) - fatal ("thumb_load_double_from_address: base is not a register"); - - /* Catch the case of <address> = <reg> + <reg> */ - - if (GET_CODE (offset) == REG) - { - int reg_offset = REGNO (offset); - int reg_base = REGNO (base); - int reg_dest = REGNO (operands[0]); - - /* Add the base and offset registers together into the higher destination register. */ - - fprintf (asm_out_file, "\tadd\t%s, %s, %s\t\t%s created by thumb_load_double_from_address", - reg_names[ reg_dest + 1 ], - reg_names[ reg_base ], - reg_names[ reg_offset ], - ASM_COMMENT_START); - - /* Load the lower destination register from the address in the higher destination register. */ - - fprintf (asm_out_file, "\tldr\t%s, [%s, #0]\t\t%s created by thumb_load_double_from_address", - reg_names[ reg_dest ], - reg_names[ reg_dest + 1], - ASM_COMMENT_START); - - /* Load the higher destination register from its own address plus 4. */ - - fprintf (asm_out_file, "\tldr\t%s, [%s, #4]\t\t%s created by thumb_load_double_from_address", - reg_names[ reg_dest + 1 ], - reg_names[ reg_dest + 1 ], - ASM_COMMENT_START); - } - else - { - /* Compute <address> + 4 for the high order load. */ - - operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4)); - - /* If the computed address is held in the low order register - then load the high order register first, otherwise always - load the low order register first. */ - - if (REGNO (operands[0]) == REGNO (base)) - { - output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); - output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); - } - else - { - output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); - output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); - } - } - break; - - case LABEL_REF: - /* With no registers to worry about we can just load the value directly. */ - operands[2] = gen_rtx (MEM, SImode, plus_constant (XEXP (operands[1], 0), 4)); - - output_asm_insn ("ldr\t%H0, %2\t\t%@ created by thumb_load_double_from_address", operands); - output_asm_insn ("ldr\t%0, %1\t\t%@ created by thumb_load_double_from_address", operands); - break; - - default: - debug_rtx (operands[1]); - fatal ("thumb_load_double_from_address: Unhandled address calculation"); - break; - } - - return ""; -} - -char * -output_move_mem_multiple (n, operands) - int n; - rtx *operands; -{ - rtx tmp; - - switch (n) - { - case 2: - if (REGNO (operands[2]) > REGNO (operands[3])) - { - tmp = operands[2]; - operands[2] = operands[3]; - operands[3] = tmp; - } - output_asm_insn ("ldmia\t%1!, {%2, %3}", operands); - output_asm_insn ("stmia\t%0!, {%2, %3}", operands); - break; - - case 3: - if (REGNO (operands[2]) > REGNO (operands[3])) - { - tmp = operands[2]; - operands[2] = operands[3]; - operands[3] = tmp; - } - if (REGNO (operands[3]) > REGNO (operands[4])) - { - tmp = operands[3]; - operands[3] = operands[4]; - operands[4] = tmp; - } - if (REGNO (operands[2]) > REGNO (operands[3])) - { - tmp = operands[2]; - operands[2] = operands[3]; - operands[3] = tmp; - } - output_asm_insn ("ldmia\t%1!, {%2, %3, %4}", operands); - output_asm_insn ("stmia\t%0!, {%2, %3, %4}", operands); - break; - - default: - abort (); - } - - return ""; -} - - -int -thumb_epilogue_size () -{ - return 42; /* The answer to .... */ -} - -static char *conds[] = -{ - "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", "gt", "le" -}; - -static char * -thumb_condition_code (x, invert) - rtx x; - int invert; -{ - int val; - - switch (GET_CODE (x)) - { - case EQ: val = 0; break; - case NE: val = 1; break; - case GEU: val = 2; break; - case LTU: val = 3; break; - case GTU: val = 8; break; - case LEU: val = 9; break; - case GE: val = 10; break; - case LT: val = 11; break; - case GT: val = 12; break; - case LE: val = 13; break; - default: - abort (); - } - - return conds[val ^ invert]; -} - -void -thumb_print_operand (f, x, code) - FILE *f; - rtx x; - int code; -{ - if (code) - { - switch (code) - { - case '@': - fputs (ASM_COMMENT_START, f); - return; - - case '_': - fputs (user_label_prefix, f); - return; - - case 'D': - if (x) - fputs (thumb_condition_code (x, 1), f); - return; - - case 'd': - if (x) - fputs (thumb_condition_code (x, 0), f); - return; - - /* An explanation of the 'Q', 'R' and 'H' register operands: - - In a pair of registers containing a DI or DF value the 'Q' - operand returns the register number of the register containing - the least signficant part of the value. The 'R' operand returns - the register number of the register containing the most - significant part of the value. - - The 'H' operand returns the higher of the two register numbers. - On a run where WORDS_BIG_ENDIAN is true the 'H' operand is the - same as the 'Q' operand, since the most signficant part of the - value is held in the lower number register. The reverse is true - on systems where WORDS_BIG_ENDIAN is false. - - The purpose of these operands is to distinguish between cases - where the endian-ness of the values is important (for example - when they are added together), and cases where the endian-ness - is irrelevant, but the order of register operations is important. - For example when loading a value from memory into a register - pair, the endian-ness does not matter. Provided that the value - from the lower memory address is put into the lower numbered - register, and the value from the higher address is put into the - higher numbered register, the load will work regardless of whether - the value being loaded is big-wordian or little-wordian. The - order of the two register loads can matter however, if the address - of the memory location is actually held in one of the registers - being overwritten by the load. */ - case 'Q': - if (REGNO (x) > 15) - abort (); - fputs (reg_names[REGNO (x) + (WORDS_BIG_ENDIAN ? 1 : 0)], f); - return; - - case 'R': - if (REGNO (x) > 15) - abort (); - fputs (reg_names[REGNO (x) + (WORDS_BIG_ENDIAN ? 0 : 1)], f); - return; - - case 'H': - if (REGNO (x) > 15) - abort (); - fputs (reg_names[REGNO (x) + 1], f); - return; - - case 'c': - /* We use 'c' operands with symbols for .vtinherit */ - if (GET_CODE (x) == SYMBOL_REF) - output_addr_const(f, x); - return; - - default: - abort (); - } - } - if (GET_CODE (x) == REG) - fputs (reg_names[REGNO (x)], f); - else if (GET_CODE (x) == MEM) - output_address (XEXP (x, 0)); - else if (GET_CODE (x) == CONST_INT) - { - fputc ('#', f); - output_addr_const (f, x); - } - else - abort (); -} - -#ifdef AOF_ASSEMBLER -int arm_text_section_count = 1; - -char * -aof_text_section (in_readonly) - int in_readonly; -{ - static char buf[100]; - if (in_readonly) - return ""; - sprintf (buf, "\tCODE16\n\tAREA |C$$code%d|, CODE, READONLY", - arm_text_section_count++); - return buf; -} - -static int arm_data_section_count = 1; - -char * -aof_data_section () -{ - static char buf[100]; - sprintf (buf, "\tAREA |C$$data%d|, DATA", arm_data_section_count++); - return buf; -} - -/* The AOF thumb assembler is religiously strict about declarations of - imported and exported symbols, so that it is impossible to declare a - function as imported near the begining of the file, and then to export - it later on. It is, however, possible to delay the decision until all - the functions in the file have been compiled. To get around this, we - maintain a list of the imports and exports, and delete from it any that - are subsequently defined. At the end of compilation we spit the - remainder of the list out before the END directive. */ - -struct import -{ - struct import *next; - char *name; -}; - -static struct import *imports_list = NULL; - -void -thumb_aof_add_import (name) - char *name; -{ - struct import *new; - - for (new = imports_list; new; new = new->next) - if (new->name == name) - return; - - new = (struct import *) xmalloc (sizeof (struct import)); - new->next = imports_list; - imports_list = new; - new->name = name; -} - -void -thumb_aof_delete_import (name) - char *name; -{ - struct import **old; - - for (old = &imports_list; *old; old = & (*old)->next) - { - if ((*old)->name == name) - { - *old = (*old)->next; - return; - } - } -} - -void -thumb_aof_dump_imports (f) - FILE *f; -{ - while (imports_list) - { - fprintf (f, "\tIMPORT\t"); - assemble_name (f, imports_list->name); - fputc ('\n', f); - imports_list = imports_list->next; - } -} -#endif - -/* Decide whether a type should be returned in memory (true) - or in a register (false). This is called by the macro - RETURN_IN_MEMORY. */ - -int -thumb_return_in_memory (type) - tree type; -{ - if (! AGGREGATE_TYPE_P (type)) - { - /* All simple types are returned in registers. */ - - return 0; - } - else if (int_size_in_bytes (type) > 4) - { - /* All structures/unions bigger than one word are returned in memory. */ - - return 1; - } - else if (TREE_CODE (type) == RECORD_TYPE) - { - tree field; - - /* For a struct the APCS says that we must return in a register if - every addressable element has an offset of zero. For practical - purposes this means that the structure can have at most one non- - bit-field element and that this element must be the first one in - the structure. */ - - /* Find the first field, ignoring non FIELD_DECL things which will - have been created by C++. */ - for (field = TYPE_FIELDS (type); - field && TREE_CODE (field) != FIELD_DECL; - field = TREE_CHAIN (field)) - continue; - - if (field == NULL) - return 0; /* An empty structure. Allowed by an extension to ANSI C. */ - - /* Now check the remaining fields, if any. */ - for (field = TREE_CHAIN (field); field; field = TREE_CHAIN (field)) - { - if (TREE_CODE (field) != FIELD_DECL) - continue; - - if (! DECL_BIT_FIELD_TYPE (field)) - return 1; - } - - return 0; - } - else if (TREE_CODE (type) == UNION_TYPE) - { - tree field; - - /* Unions can be returned in registers if every element is - integral, or can be returned in an integer register. */ - - for (field = TYPE_FIELDS (type); - field; - field = TREE_CHAIN (field)) - { - if (TREE_CODE (field) != FIELD_DECL) - continue; - - if (RETURN_IN_MEMORY (TREE_TYPE (field))) - return 1; - } - - return 0; - } - /* XXX Not sure what should be done for other aggregates, so put them in - memory. */ - return 1; -} - -void -thumb_override_options () -{ - if (structure_size_string != NULL) - { - int size = strtol (structure_size_string, NULL, 0); - - if (size == 8 || size == 32) - arm_structure_size_boundary = size; - else - warning ("Structure size boundary can only be set to 8 or 32"); - } - - if (flag_pic) - { - warning ("Position independent code not supported. Ignored"); - flag_pic = 0; - } -} - -/* CYGNUS LOCAL nickc/thumb-pe */ - -#ifdef THUMB_PE -/* Return nonzero if ATTR is a valid attribute for DECL. - ATTRIBUTES are any existing attributes and ARGS are the arguments - supplied with ATTR. - - Supported attributes: - - naked: don't output any prologue or epilogue code, the user is assumed - to do the right thing. - - interfacearm: Always assume that this function will be entered in ARM - mode, not Thumb mode, and that the caller wishes to be returned to in - ARM mode. */ -int -arm_valid_machine_decl_attribute (decl, attributes, attr, args) - tree decl; - tree attributes; - tree attr; - tree args; -{ - if (args != NULL_TREE) - return 0; - - if (is_attribute_p ("naked", attr)) - return TREE_CODE (decl) == FUNCTION_DECL; - - if (is_attribute_p ("interfacearm", attr)) - return TREE_CODE (decl) == FUNCTION_DECL; - - return 0; -} -#endif /* THUMB_PE */ -/* END CYGNUS LOCAL nickc/thumb-pe */ - -/* s_register_operand is the same as register_operand, but it doesn't accept - (SUBREG (MEM)...). - - This function exists because at the time it was put in it led to better - code. SUBREG(MEM) always needs a reload in the places where - s_register_operand is used, and this seemed to lead to excessive - reloading. */ - -int -s_register_operand (op, mode) - register rtx op; - enum machine_mode mode; -{ - if (GET_MODE (op) != mode && mode != VOIDmode) - return 0; - - if (GET_CODE (op) == SUBREG) - op = SUBREG_REG (op); - - /* We don't consider registers whose class is NO_REGS - to be a register operand. */ - /* XXX might have to check for lo regs only for thumb ??? */ - return (GET_CODE (op) == REG - && (REGNO (op) >= FIRST_PSEUDO_REGISTER - || REGNO_REG_CLASS (REGNO (op)) != NO_REGS)); -} |