diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 20 | ||||
-rw-r--r-- | gcc/config/arm/arm.c | 360 |
2 files changed, 232 insertions, 148 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index fe0ad78..fb3aa1b 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,23 @@ +2005-03-01 Nick Clifton <nickc@redhat.com> + + * config/arm/arm.c (thumb_find_work_register): Check all of the + argument registers to see if they are free, and a couple of + special cases where the last argument register but can be proved + to be available during the function's prologue. + (print_multi_reg, arm_compute_save_reg0_reg12_mask, + output_return_instruction, emit_multi_reg_push, thumb_pushpop, + thumb_unexpanded_epilogue): Use unsigned long as the type for the + register bit-mask. + (thumb_compute_save_reg_mask): Likewise. Also use + thumb_find_work_register() to ensure that there is agreement about + which work register is going to be used in the prologue. + (thumb_output_function_prologue): Use unsigned long as the type + for the register bit-mask. Also delay pushing the link register if + other high registers are going to be pushed. + (thumb_compute_save_reg_mask, emit_multi_reg_push, + print_multi-reg, number_of_first_bit_set, thumb_pushpop): Remove + redundant prototypes. + 2005-02-28 John David Anglin <dave.anglin#nrc-cnrc.gc.ca> PR target/19819 diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index f8907be..b307983 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -71,9 +71,7 @@ static int thumb_base_register_rtx_p (rtx, enum machine_mode, int); inline static int thumb_index_register_rtx_p (rtx, int); static int thumb_far_jump_used_p (void); static bool thumb_force_lr_save (void); -static unsigned long thumb_compute_save_reg_mask (void); static int const_ok_for_op (HOST_WIDE_INT, enum rtx_code); -static rtx emit_multi_reg_push (int); static rtx emit_sfm (int, int); #ifndef AOF_ASSEMBLER static bool arm_assemble_integer (rtx, unsigned int, int); @@ -84,13 +82,10 @@ static HOST_WIDE_INT int_log2 (HOST_WIDE_INT); static rtx is_jump_table (rtx); static const char *output_multi_immediate (rtx *, const char *, const char *, int, HOST_WIDE_INT); -static void print_multi_reg (FILE *, const char *, int, int); static const char *shift_op (rtx, HOST_WIDE_INT *); static struct machine_function *arm_init_machine_status (void); -static int number_of_first_bit_set (int); static void replace_symbols_in_block (tree, rtx, rtx); static void thumb_exit (FILE *, int); -static void thumb_pushpop (FILE *, int, int, int *, int); static rtx is_jump_table (rtx); static HOST_WIDE_INT get_jump_table_size (rtx); static Mnode *move_minipool_fix_forward_ref (Mnode *, Mnode *, HOST_WIDE_INT); @@ -3102,24 +3097,57 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg) } -/* Find a spare low register. */ +/* Find a spare low register to use during the prolog of a function. */ static int -thumb_find_work_register (int live_regs_mask) +thumb_find_work_register (unsigned long pushed_regs_mask) { int reg; - /* Use a spare arg register. */ - if (!regs_ever_live[LAST_ARG_REGNUM]) + /* Check the argument registers first as these are call-used. The + register allocation order means that sometimes r3 might be used + but earlier argument registers might not, so check them all. */ + for (reg = LAST_ARG_REGNUM; reg >= 0; reg --) + if (!regs_ever_live[reg]) + return reg; + + /* Before going on to check the call-saved registers we can try a couple + more ways of deducing that r3 is available. The first is when we are + pushing anonymous arguments onto the stack and we have less than 4 + registers worth of fixed arguments(*). In this case r3 will be part of + the variable argument list and so we can be sure that it will be + pushed right at the start of the function. Hence it will be available + for the rest of the prologue. + (*): ie current_function_pretend_args_size is greater than 0. */ + if (cfun->machine->uses_anonymous_args + && current_function_pretend_args_size > 0) return LAST_ARG_REGNUM; - /* Look for a pushed register. This is used before the frame pointer is - setup, so r7 is a candidate. */ - for (reg = LAST_LO_REGNUM; reg >=0; reg--) - if (live_regs_mask & (1 << reg)) + /* The other case is when we have fixed arguments but less than 4 registers + worth. In this case r3 might be used in the body of the function, but + it is not being used to convey an argument into the function. In theory + we could just check current_function_args_size to see how many bytes are + being passed in argument registers, but it seems that it is unreliable. + Sometimes it will have the value 0 when in fact arguments are being + passed. (See testcase execute/20021111-1.c for an example). So we also + check the args_info.nregs field as well. The problem with this field is + that it makes no allowances for arguments that are passed to the + function but which are not used. Hence we could miss an opportunity + when a function has an unused argument in r3. But it is better to be + safe than to be sorry. */ + if (! cfun->machine->uses_anonymous_args + && current_function_args_size >= 0 + && current_function_args_size <= (LAST_ARG_REGNUM * UNITS_PER_WORD) + && cfun->args_info.nregs < 4) + return LAST_ARG_REGNUM; + + /* Otherwise look for a call-saved register that is going to be pushed. */ + for (reg = LAST_LO_REGNUM; reg > LAST_ARG_REGNUM; reg --) + if (pushed_regs_mask & (1 << reg)) return reg; - /* Something went wrong. */ + /* Something went wrong - thumb_compute_save_reg_mask() + should have arranged for a suitable register to be pushed. */ abort (); } @@ -7668,11 +7696,13 @@ fp_const_from_val (REAL_VALUE_TYPE *r) MASK is the ARM register set mask of which only bits 0-15 are important. REG is the base register, either the frame pointer or the stack pointer, INSTR is the possibly suffixed load or store instruction. */ + static void -print_multi_reg (FILE *stream, const char *instr, int reg, int mask) +print_multi_reg (FILE *stream, const char *instr, unsigned reg, + unsigned long mask) { - int i; - int not_first = FALSE; + unsigned i; + bool not_first = FALSE; fputc ('\t', stream); asm_fprintf (stream, instr, reg); @@ -8707,11 +8737,12 @@ output_ascii_pseudo_op (FILE *stream, const unsigned char *p, int len) /* Compute the register save mask for registers 0 through 12 inclusive. This code is used by arm_compute_save_reg_mask. */ + static unsigned long arm_compute_save_reg0_reg12_mask (void) { unsigned long func_type = arm_current_func_type (); - unsigned int save_reg_mask = 0; + unsigned long save_reg_mask = 0; unsigned int reg; if (IS_INTERRUPT (func_type)) @@ -8871,31 +8902,42 @@ static unsigned long thumb_compute_save_reg_mask (void) { unsigned long mask; - int reg; + unsigned reg; mask = 0; for (reg = 0; reg < 12; reg ++) - { - if (regs_ever_live[reg] && !call_used_regs[reg]) - mask |= 1 << reg; - } + if (regs_ever_live[reg] && !call_used_regs[reg]) + mask |= 1 << reg; if (flag_pic && !TARGET_SINGLE_PIC_BASE) mask |= (1 << PIC_OFFSET_TABLE_REGNUM); + if (TARGET_SINGLE_PIC_BASE) mask &= ~(1 << arm_pic_register); + /* See if we might need r11 for calls to _interwork_r11_call_via_rN(). */ if (!frame_pointer_needed && CALLER_INTERWORKING_SLOT_SIZE > 0) mask |= 1 << ARM_HARD_FRAME_POINTER_REGNUM; - /* lr will also be pushed if any lo regs are pushed. */ + /* LR will also be pushed if any lo regs are pushed. */ if (mask & 0xff || thumb_force_lr_save ()) mask |= (1 << LR_REGNUM); - /* Make sure we have a low work register if we need one. */ - if (((mask & 0xff) == 0 && regs_ever_live[LAST_ARG_REGNUM]) + /* Make sure we have a low work register if we need one. + We will need one if we are going to push a high register, + but we are not currently intending to push a low register. */ + if ((mask & 0xff) == 0 && ((mask & 0x0f00) || TARGET_BACKTRACE)) - mask |= 1 << LAST_LO_REGNUM; + { + /* Use thumb_find_work_register to choose which register + we will use. If the register is live then we will + have to push it. Use LAST_LO_REGNUM as our fallback + choice for the register to select. */ + reg = thumb_find_work_register (1 << LAST_LO_REGNUM); + + if (! call_used_regs[reg]) + mask |= 1 << reg; + } return mask; } @@ -8951,7 +8993,7 @@ output_return_instruction (rtx operand, int really_return, int reverse) { char conditional[10]; char instr[100]; - int reg; + unsigned reg; unsigned long live_regs_mask; unsigned long func_type; arm_stack_offsets *offsets; @@ -9030,10 +9072,9 @@ output_return_instruction (rtx operand, int really_return, int reverse) we have to use LDM to load the PC so that the CPSR is also restored. */ for (reg = 0; reg <= LAST_ARM_REGNUM; reg++) - { - if (live_regs_mask == (unsigned int)(1 << reg)) - break; - } + if (live_regs_mask == (1U << reg)) + break; + if (reg <= LAST_ARM_REGNUM && (reg != LR_REGNUM || ! really_return @@ -9064,8 +9105,8 @@ output_return_instruction (rtx operand, int really_return, int reverse) sprintf (instr, "ldm%sib\t%%|sp, {", conditional); else { - /* If we can't use ldmib (SA110 bug), then try to pop r3 - instead. */ + /* If we can't use ldmib (SA110 bug), + then try to pop r3 instead. */ if (stack_adjust) live_regs_mask |= 1 << 3; sprintf (instr, "ldm%sfd\t%%|sp, {", conditional); @@ -9663,7 +9704,7 @@ arm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED, semantics of the operation, we need to annotate the insn for the benefit of DWARF2 frame unwind information. */ static rtx -emit_multi_reg_push (int mask) +emit_multi_reg_push (unsigned long mask) { int num_regs = 0; int num_dwarf_regs; @@ -12352,7 +12393,7 @@ replace_symbols_in_block (tree block, rtx orig, rtx new) the least significant set bit in MASK. */ inline static int -number_of_first_bit_set (int mask) +number_of_first_bit_set (unsigned mask) { int bit; @@ -12364,6 +12405,102 @@ number_of_first_bit_set (int mask) return bit; } +/* Emit code to push or pop registers to or from the stack. F is the + assembly file. MASK is the registers to push or pop. PUSH is + nonzero if we should push, and zero if we should pop. For debugging + output, if pushing, adjust CFA_OFFSET by the amount of space added + to the stack. REAL_REGS should have the same number of bits set as + MASK, and will be used instead (in the same order) to describe which + registers were saved - this is used to mark the save slots when we + push high registers after moving them to low registers. */ +static void +thumb_pushpop (FILE *f, unsigned long mask, int push, int *cfa_offset, + unsigned long real_regs) +{ + int regno; + int lo_mask = mask & 0xFF; + int pushed_words = 0; + + if (mask == 0) + abort (); + + if (lo_mask == 0 && !push && (mask & (1 << PC_REGNUM))) + { + /* Special case. Do not generate a POP PC statement here, do it in + thumb_exit() */ + thumb_exit (f, -1); + return; + } + + fprintf (f, "\t%s\t{", push ? "push" : "pop"); + + /* Look at the low registers first. */ + for (regno = 0; regno <= LAST_LO_REGNUM; regno++, lo_mask >>= 1) + { + if (lo_mask & 1) + { + asm_fprintf (f, "%r", regno); + + if ((lo_mask & ~1) != 0) + fprintf (f, ", "); + + pushed_words++; + } + } + + if (push && (mask & (1 << LR_REGNUM))) + { + /* Catch pushing the LR. */ + if (mask & 0xFF) + fprintf (f, ", "); + + asm_fprintf (f, "%r", LR_REGNUM); + + pushed_words++; + } + else if (!push && (mask & (1 << PC_REGNUM))) + { + /* Catch popping the PC. */ + if (TARGET_INTERWORK || TARGET_BACKTRACE + || current_function_calls_eh_return) + { + /* The PC is never poped directly, instead + it is popped into r3 and then BX is used. */ + fprintf (f, "}\n"); + + thumb_exit (f, -1); + + return; + } + else + { + if (mask & 0xFF) + fprintf (f, ", "); + + asm_fprintf (f, "%r", PC_REGNUM); + } + } + + fprintf (f, "}\n"); + + if (push && pushed_words && dwarf2out_do_frame ()) + { + char *l = dwarf2out_cfi_label (); + int pushed_mask = real_regs; + + *cfa_offset += pushed_words * 4; + dwarf2out_def_cfa (l, SP_REGNUM, *cfa_offset); + + pushed_words = 0; + pushed_mask = real_regs; + for (regno = 0; regno <= 14; regno++, pushed_mask >>= 1) + { + if (pushed_mask & 1) + dwarf2out_reg_save (l, regno, 4 * pushed_words++ - *cfa_offset); + } + } +} + /* 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. */ @@ -12641,97 +12778,6 @@ thumb_exit (FILE *f, int reg_containing_return_addr) asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr); } -/* Emit code to push or pop registers to or from the stack. F is the - assembly file. MASK is the registers to push or pop. PUSH is - nonzero if we should push, and zero if we should pop. For debugging - output, if pushing, adjust CFA_OFFSET by the amount of space added - to the stack. REAL_REGS should have the same number of bits set as - MASK, and will be used instead (in the same order) to describe which - registers were saved - this is used to mark the save slots when we - push high registers after moving them to low registers. */ -static void -thumb_pushpop (FILE *f, int mask, int push, int *cfa_offset, int real_regs) -{ - int regno; - int lo_mask = mask & 0xFF; - int pushed_words = 0; - - if (lo_mask == 0 && !push && (mask & (1 << PC_REGNUM))) - { - /* Special case. Do not generate a POP PC statement here, do it in - thumb_exit() */ - thumb_exit (f, -1); - return; - } - - fprintf (f, "\t%s\t{", push ? "push" : "pop"); - - /* Look at the low registers first. */ - for (regno = 0; regno <= LAST_LO_REGNUM; regno++, lo_mask >>= 1) - { - if (lo_mask & 1) - { - asm_fprintf (f, "%r", regno); - - if ((lo_mask & ~1) != 0) - fprintf (f, ", "); - - pushed_words++; - } - } - - if (push && (mask & (1 << LR_REGNUM))) - { - /* Catch pushing the LR. */ - if (mask & 0xFF) - fprintf (f, ", "); - - asm_fprintf (f, "%r", LR_REGNUM); - - pushed_words++; - } - else if (!push && (mask & (1 << PC_REGNUM))) - { - /* Catch popping the PC. */ - if (TARGET_INTERWORK || TARGET_BACKTRACE - || current_function_calls_eh_return) - { - /* The PC is never poped directly, instead - it is popped into r3 and then BX is used. */ - fprintf (f, "}\n"); - - thumb_exit (f, -1); - - return; - } - else - { - if (mask & 0xFF) - fprintf (f, ", "); - - asm_fprintf (f, "%r", PC_REGNUM); - } - } - - fprintf (f, "}\n"); - - if (push && pushed_words && dwarf2out_do_frame ()) - { - char *l = dwarf2out_cfi_label (); - int pushed_mask = real_regs; - - *cfa_offset += pushed_words * 4; - dwarf2out_def_cfa (l, SP_REGNUM, *cfa_offset); - - pushed_words = 0; - pushed_mask = real_regs; - for (regno = 0; regno <= 14; regno++, pushed_mask >>= 1) - { - if (pushed_mask & 1) - dwarf2out_reg_save (l, regno, 4 * pushed_words++ - *cfa_offset); - } - } -} void thumb_final_prescan_insn (rtx insn) @@ -12851,7 +12897,7 @@ const char * thumb_unexpanded_epilogue (void) { int regno; - int live_regs_mask = 0; + unsigned long live_regs_mask = 0; int high_regs_pushed = 0; int had_to_push_lr; int size; @@ -12890,7 +12936,7 @@ thumb_unexpanded_epilogue (void) if (high_regs_pushed) { - int mask = live_regs_mask & 0xff; + unsigned long mask = live_regs_mask & 0xff; int next_hi_reg; /* The available low registers depend on the size of the value we are @@ -13127,8 +13173,8 @@ thumb_expand_prologue (void) } live_regs_mask = thumb_compute_save_reg_mask (); - /* Load the pic register before setting the frame pointer, so we can use r7 - as a temporary work register. */ + /* Load the pic register before setting the frame pointer, + so we can use r7 as a temporary work register. */ if (flag_pic) arm_load_pic_register (thumb_find_work_register (live_regs_mask)); @@ -13302,9 +13348,9 @@ thumb_expand_epilogue (void) static void thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED) { - int live_regs_mask = 0; - int l_mask; - int high_regs_pushed = 0; + unsigned long live_regs_mask = 0; + unsigned long l_mask; + unsigned high_regs_pushed = 0; int cfa_offset = 0; int regno; @@ -13375,19 +13421,23 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED) if (dwarf2out_do_frame ()) { char *l = dwarf2out_cfi_label (); + cfa_offset = cfa_offset + current_function_pretend_args_size; dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset); } } + /* Get the registers we are going to push. */ live_regs_mask = thumb_compute_save_reg_mask (); - /* Just low regs and lr. */ + /* Extract a mask of the ones we can give to the Thumb's push instruction. */ l_mask = live_regs_mask & 0x40ff; + /* Then count how many other high registers will need to be pushed. */ + high_regs_pushed = bit_count (live_regs_mask & 0x0f00); if (TARGET_BACKTRACE) { - int offset; - int work_register; + unsigned offset; + unsigned work_register; /* We have been asked to create a stack backtrace structure. The code looks like this: @@ -13416,6 +13466,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED) if (dwarf2out_do_frame ()) { char *l = dwarf2out_cfi_label (); + cfa_offset = cfa_offset + 16; dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset); } @@ -13465,15 +13516,18 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED) asm_fprintf (f, "\tmov\t%r, %r\t\t%@ Backtrace structure created\n", ARM_HARD_FRAME_POINTER_REGNUM, work_register); } - else if (l_mask) + /* Optimisation: If we are not pushing any low registers but we are going + to push some high registers then delay our first push. This will just + be a push of LR and we can combine it with the push of the first high + register. */ + else if ((l_mask & 0xff) != 0 + || (high_regs_pushed == 0 && l_mask)) thumb_pushpop (f, l_mask, 1, &cfa_offset, l_mask); - high_regs_pushed = bit_count (live_regs_mask & 0x0f00); - if (high_regs_pushed) { - int pushable_regs = 0; - int next_hi_reg; + unsigned pushable_regs; + unsigned next_hi_reg; for (next_hi_reg = 12; next_hi_reg > LAST_LO_REGNUM; next_hi_reg--) if (live_regs_mask & (1 << next_hi_reg)) @@ -13486,21 +13540,21 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED) while (high_regs_pushed > 0) { - int real_regs_mask = 0; + unsigned long real_regs_mask = 0; - for (regno = LAST_LO_REGNUM; regno >= 0; regno--) + for (regno = LAST_LO_REGNUM; regno >= 0; regno --) { if (pushable_regs & (1 << regno)) { asm_fprintf (f, "\tmov\t%r, %r\n", regno, next_hi_reg); - high_regs_pushed--; + high_regs_pushed --; real_regs_mask |= (1 << next_hi_reg); if (high_regs_pushed) { - for (next_hi_reg--; next_hi_reg > LAST_LO_REGNUM; - next_hi_reg--) + for (next_hi_reg --; next_hi_reg > LAST_LO_REGNUM; + next_hi_reg --) if (live_regs_mask & (1 << next_hi_reg)) break; } @@ -13512,7 +13566,17 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED) } } - thumb_pushpop (f, pushable_regs, 1, &cfa_offset, real_regs_mask); + /* If we had to find a work register and we have not yet + saved the LR then add it to the list of regs to push. */ + if (l_mask == (1 << LR_REGNUM)) + { + thumb_pushpop (f, pushable_regs | (1 << LR_REGNUM), + 1, &cfa_offset, + real_regs_mask | (1 << LR_REGNUM)); + l_mask = 0; + } + else + thumb_pushpop (f, pushable_regs, 1, &cfa_offset, real_regs_mask); } } } |