diff options
author | YamaArashi <shadow962@live.com> | 2016-04-27 00:00:40 -0700 |
---|---|---|
committer | YamaArashi <shadow962@live.com> | 2016-04-27 00:00:40 -0700 |
commit | 9dc75fe3b4be91d6066c8e870eacec954117cc08 (patch) | |
tree | 0ff095d5dd600f23a98952682edf5226f40f0391 /gcc/config/arm/thumb.c | |
parent | 9e5f6a79618cb7df0b7d3816472b530c3b7d9c1a (diff) |
reorganize files
Diffstat (limited to 'gcc/config/arm/thumb.c')
-rwxr-xr-x | gcc/config/arm/thumb.c | 1527 |
1 files changed, 0 insertions, 1527 deletions
diff --git a/gcc/config/arm/thumb.c b/gcc/config/arm/thumb.c deleted file mode 100755 index 0310a51..0000000 --- a/gcc/config/arm/thumb.c +++ /dev/null @@ -1,1527 +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 "insn-config.h" -#include "flags.h" -#include "tree.h" -#include "expr.h" -#include "toplev.h" -#include "recog.h" - -int current_function_anonymous_args = 0; -static int current_function_has_far_jump = 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 */ - -/* Return nonzero if op is suitable for the RHS of a cmp instruction. */ -int -thumb_cmp_operand(rtx op, enum machine_mode mode) -{ - return ((GET_CODE(op) == CONST_INT - && (HOST_WIDE_UINT) (INTVAL(op)) < 256) - || register_operand(op, mode)); -} - -int -thumb_shiftable_const(HOST_WIDE_INT val) -{ - HOST_WIDE_UINT x = val; - HOST_WIDE_UINT mask = 0xff; - int i; - - for (i = 0; i < 25; i++) - if ((val & (mask << i)) == val) - return 1; - - return 0; -} - -/* 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(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(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(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(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(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 'replacement'. */ - -static void -replace_symbols_in_block(tree block, rtx orig, rtx replacement) -{ - 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) = replacement; - } - - replace_symbols_in_block(BLOCK_SUBBLOCKS(block), orig, replacement); - } -} - -void -thumb_reorg(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(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(rtx operands) -{ - abort(); -} - -static int -arm_naked_function_p(tree func) -{ - tree a; - - if (TREE_CODE(func) != FUNCTION_DECL) - abort(); - - a = lookup_attribute("naked", DECL_MACHINE_ATTRIBUTES(func)); - return a != NULL_TREE; -} - -/* Routines for emitting code */ - -void -final_prescan_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. */ - -static inline int -number_of_first_bit_set(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(FILE *f, int reg_containing_return_addr) -{ - int reg_available_for_popping; - int mode; - int size; - int restore_a4 = FALSE; - - if (reg_containing_return_addr != -1) - { - /* If the return address is in a register, - then just emit the BX instruction and return. */ - asm_fprintf(f, "\tbx\t%s\n", reg_names[reg_containing_return_addr]); - return; - } - - if (!TARGET_THUMB_INTERWORK) - { - /* If we are not supporting interworking, - then just pop the return address straight into the PC. */ - asm_fprintf(f, "\tpop\t{pc}\n" ); - return; - } - - /* 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 - 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) - reg_available_for_popping = ARG_1_REGISTER; - else - reg_available_for_popping = ARG_2_REGISTER; - } - else if (size <= 4) - { - reg_available_for_popping = ARG_2_REGISTER; - } - else if (size <= 8) - { - reg_available_for_popping = ARG_3_REGISTER; - } - else - { - reg_available_for_popping = ARG_4_REGISTER; - - 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]); - } - } - - /* Pop the return address. */ - thumb_pushpop(f, (1 << reg_available_for_popping), FALSE); - - reg_containing_return_addr = reg_available_for_popping; - - /* If necessary restore the a4 register. */ - if (restore_a4) - { - 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(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) - { - /* The PC is never popped directly, instead - it is popped into r0-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() -{ - rtx insn; - - if (current_function_has_far_jump) - return 1; - - 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) - { - current_function_has_far_jump = 1; - return 1; - } - } - - return 0; -} - -static int return_used_this_function = 0; - -void -thumb_function_prologue(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; - - if (arm_naked_function_p(current_function_decl)) - return; - - 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 (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; - - if (arm_naked_function_p(current_function_decl)) - return; - - 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; - - if (arm_naked_function_p(current_function_decl)) - return; - - 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(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; - current_function_has_far_jump = 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 (arm_naked_function_p(current_function_decl) - || 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; - - /* 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 - { - 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 (current_function_pretend_args_size == 0) - { - if (had_to_push_lr) - live_regs_mask |= 1 << PROGRAM_COUNTER; - - /* No argument registers were pushed, 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, 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(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(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 ""; -} - -static char *conds[] = -{ - "eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", "gt", "le" -}; - -static char * -thumb_condition_code(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(FILE *f, rtx x, int code) -{ - if (code) - { - switch (code) - { - case '@': - fputs(ASM_COMMENT_START, 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)], f); - return; - - case 'R': - if (REGNO(x) > 15) - abort(); - fputs(reg_names[REGNO(x) + 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(); -} - -/* 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(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"); - } -} - -/* 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. - */ -int -arm_valid_machine_decl_attribute(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; - - return 0; -} - -/* 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(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)); -} |