arm.c (thumb_force_lr_save): Add prototype.

* arm.c (thumb_force_lr_save): Add prototype.
	(thumb_compute_save_reg_mask): New function.
	(thumb_find_work_register): New function.
	(arm_get_frame_offsets): Use thumb_compute_save_reg_mask.
	(thumb_unexpanded_epilogue): Ditto.  Remove redundant code.
	Don't clobber r3 when removing pretend args.
	(thumb_expand_prologue): Use thumb_compute_save_reg_mask.
	(thumb_output_function_prologue): Use new functions.
	(thumb_set_return_address): Use thumb_compute_save_reg_mask.
	* arm.h (THUMB_REG_PUSHED_P): Remove.

From-SVN: r85818
This commit is contained in:
Paul Brook 2004-08-11 20:59:15 +00:00 committed by Paul Brook
parent af87423702
commit 57934c3979
3 changed files with 166 additions and 161 deletions

View File

@ -1,3 +1,16 @@
2004-08-11 Paul Brook <paul@codesourcery.com>
* arm.c (thumb_force_lr_save): Add prototype.
(thumb_compute_save_reg_mask): New function.
(thumb_find_work_register): New function.
(arm_get_frame_offsets): Use thumb_compute_save_reg_mask.
(thumb_unexpanded_epilogue): Ditto. Remove redundant code.
Don't clobber r3 when removing pretend args.
(thumb_expand_prologue): Use thumb_compute_save_reg_mask.
(thumb_output_function_prologue): Use new functions.
(thumb_set_return_address): Use thumb_compute_save_reg_mask.
* arm.h (THUMB_REG_PUSHED_P): Remove.
2004-08-11 James E Wilson <wilson@specifixinc.com>
PR rtl-optimization/16490

View File

@ -70,6 +70,8 @@ static int arm_legitimate_index_p (enum machine_mode, rtx, RTX_CODE, int);
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);
@ -2989,6 +2991,27 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg)
return orig;
}
/* Find a spare low register. */
static int
thumb_find_work_register (int live_regs_mask)
{
int reg;
/* Use a spare arg register. */
if (!regs_ever_live[LAST_ARG_REGNUM])
return LAST_ARG_REGNUM;
/* Look for a pushed register. */
for (reg = 0; reg < LAST_LO_REGNUM; reg++)
if (live_regs_mask & (1 << reg))
return reg;
/* Something went wrong. */
abort ();
}
/* Generate code to load the PIC register. PROLOGUE is true if
called from arm_expand_prologue (in which case we want the
generated insns at the start of the function); false if called
@ -9158,6 +9181,39 @@ arm_compute_save_reg_mask (void)
}
/* Compute a bit mask of which registers need to be
saved on the stack for the current function. */
static unsigned long
thumb_compute_save_reg_mask (void)
{
unsigned long mask;
int reg;
mask = 0;
for (reg = 0; reg < 12; reg ++)
{
if (regs_ever_live[reg] && !call_used_regs[reg])
mask |= 1 << reg;
}
if (flag_pic && !TARGET_SINGLE_PIC_BASE)
mask |= PIC_OFFSET_TABLE_REGNUM;
if (TARGET_SINGLE_PIC_BASE)
mask &= ~(1 << arm_pic_register);
/* 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])
&& ((mask & 0x0f00) || TARGET_BACKTRACE))
mask |= 1 << LAST_LO_REGNUM;
return mask;
}
/* Return the number of bytes required to save VFP registers. */
static int
arm_get_vfp_saved_size (void)
@ -10216,29 +10272,9 @@ arm_get_frame_offsets (void)
}
else /* TARGET_THUMB */
{
int reg;
int count_regs;
saved = 0;
count_regs = 0;
for (reg = 8; reg < 13; reg ++)
if (THUMB_REG_PUSHED_P (reg))
count_regs ++;
if (count_regs)
saved += 4 * count_regs;
count_regs = 0;
for (reg = 0; reg <= LAST_LO_REGNUM; reg ++)
if (THUMB_REG_PUSHED_P (reg))
count_regs ++;
if (count_regs || thumb_force_lr_save ())
saved += 4 * (count_regs + 1);
saved = bit_count (thumb_compute_save_reg_mask ()) * 4;
if (TARGET_BACKTRACE)
{
if ((count_regs & 0xFF) == 0 && (regs_ever_live[3] != 0))
saved += 20;
else
saved += 16;
}
saved += 16;
}
/* Saved registers include the stack frame. */
@ -13049,6 +13085,8 @@ thumb_unexpanded_epilogue (void)
int live_regs_mask = 0;
int high_regs_pushed = 0;
int had_to_push_lr;
int size;
int mode;
if (return_used_this_function)
return "";
@ -13056,13 +13094,20 @@ thumb_unexpanded_epilogue (void)
if (IS_NAKED (arm_current_func_type ()))
return "";
for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
if (THUMB_REG_PUSHED_P (regno))
live_regs_mask |= 1 << regno;
live_regs_mask = thumb_compute_save_reg_mask ();
high_regs_pushed = bit_count (live_regs_mask & 0x0f00);
for (regno = 8; regno < 13; regno++)
if (THUMB_REG_PUSHED_P (regno))
high_regs_pushed++;
/* 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);
/* The prolog may have pushed some high registers to use as
work registers. eg the testsuite file:
@ -13076,27 +13121,15 @@ thumb_unexpanded_epilogue (void)
if (high_regs_pushed)
{
int mask = live_regs_mask;
int mask = live_regs_mask & 0xff;
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)
/* The available low registers depend on the size of the value we are
returning. */
if (size <= 12)
mask |= 1 << 3;
if (size <= 8)
mask |= 1 << 2;
if (mask == 0)
/* Oh dear! We have no low registers into which we can pop
@ -13105,7 +13138,7 @@ thumb_unexpanded_epilogue (void)
("no low registers available for popping high registers");
for (next_hi_reg = 8; next_hi_reg < 13; next_hi_reg++)
if (THUMB_REG_PUSHED_P (next_hi_reg))
if (live_regs_mask & (1 << next_hi_reg))
break;
while (high_regs_pushed)
@ -13134,29 +13167,21 @@ thumb_unexpanded_epilogue (void)
regno);
for (next_hi_reg++; next_hi_reg < 13; next_hi_reg++)
if (THUMB_REG_PUSHED_P (next_hi_reg))
if (live_regs_mask & (1 << next_hi_reg))
break;
}
}
}
live_regs_mask &= ~0x0f00;
}
had_to_push_lr = (live_regs_mask || thumb_force_lr_save ());
if (TARGET_BACKTRACE
&& ((live_regs_mask & 0xFF) == 0)
&& regs_ever_live [LAST_ARG_REGNUM] != 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 << LAST_LO_REGNUM);
}
had_to_push_lr = (live_regs_mask & (1 << LR_REGNUM)) != 0;
live_regs_mask &= 0xff;
if (current_function_pretend_args_size == 0 || TARGET_BACKTRACE)
{
if (had_to_push_lr
&& !is_called_in_ARM_mode (current_function_decl))
/* Pop the return address into the PC. */
if (had_to_push_lr)
live_regs_mask |= 1 << PC_REGNUM;
/* Either no argument registers were pushed or a backtrace
@ -13165,38 +13190,54 @@ thumb_unexpanded_epilogue (void)
if (live_regs_mask)
thumb_pushpop (asm_out_file, live_regs_mask, FALSE, NULL,
live_regs_mask);
/* 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 << PC_REGNUM)) == 0)
thumb_exit (asm_out_file,
(had_to_push_lr
&& is_called_in_ARM_mode (current_function_decl)) ?
-1 : LR_REGNUM);
PC or it is was kept in LR for the entire function. */
if (!had_to_push_lr)
thumb_exit (asm_out_file, LR_REGNUM);
}
else
{
/* Pop everything but the return address. */
live_regs_mask &= ~(1 << PC_REGNUM);
if (live_regs_mask)
thumb_pushpop (asm_out_file, live_regs_mask, FALSE, NULL,
live_regs_mask);
if (had_to_push_lr)
/* Get the return address into a temporary register. */
thumb_pushpop (asm_out_file, 1 << LAST_ARG_REGNUM, 0, NULL,
1 << LAST_ARG_REGNUM);
{
if (size > 12)
{
/* We have no free low regs, so save one. */
asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", IP_REGNUM,
LAST_ARG_REGNUM);
}
/* Get the return address into a temporary register. */
thumb_pushpop (asm_out_file, 1 << LAST_ARG_REGNUM, 0, NULL,
1 << LAST_ARG_REGNUM);
if (size > 12)
{
/* Move the return address to lr. */
asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", LR_REGNUM,
LAST_ARG_REGNUM);
/* Restore the low register. */
asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", LAST_ARG_REGNUM,
IP_REGNUM);
regno = LR_REGNUM;
}
else
regno = LAST_ARG_REGNUM;
}
else
regno = LR_REGNUM;
/* Remove the argument registers that were pushed onto the stack. */
asm_fprintf (asm_out_file, "\tadd\t%r, %r, #%d\n",
SP_REGNUM, SP_REGNUM,
current_function_pretend_args_size);
thumb_exit (asm_out_file,
had_to_push_lr ? LAST_ARG_REGNUM : LR_REGNUM);
thumb_exit (asm_out_file, regno);
}
return "";
@ -13302,6 +13343,7 @@ thumb_expand_prologue (void)
arm_stack_offsets *offsets;
unsigned long func_type;
int regno;
unsigned long live_regs_mask;
func_type = arm_current_func_type ();
@ -13324,6 +13366,7 @@ thumb_expand_prologue (void)
RTX_FRAME_RELATED_P (insn) = 1;
}
live_regs_mask = thumb_compute_save_reg_mask ();
amount = offsets->outgoing_args - offsets->saved_regs;
if (amount)
{
@ -13352,7 +13395,7 @@ thumb_expand_prologue (void)
been pushed at the start of the prologue and so we can corrupt
it now. */
for (regno = LAST_ARG_REGNUM + 1; regno <= LAST_LO_REGNUM; regno++)
if (THUMB_REG_PUSHED_P (regno)
if (live_regs_mask & (1 << regno)
&& !(frame_pointer_needed
&& (regno == THUMB_HARD_FRAME_POINTER_REGNUM)))
break;
@ -13421,14 +13464,8 @@ thumb_expand_prologue (void)
emit_insn (gen_blockage ());
cfun->machine->lr_save_eliminated = !thumb_force_lr_save ();
for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
{
if (THUMB_REG_PUSHED_P (regno))
{
cfun->machine->lr_save_eliminated = 0;
break;
}
}
if (live_regs_mask & 0xff)
cfun->machine->lr_save_eliminated = 0;
/* If the link register is being kept alive, with the return address in it,
then make sure that it does not get reused by the ce2 pass. */
@ -13436,6 +13473,7 @@ thumb_expand_prologue (void)
emit_insn (gen_prologue_use (gen_rtx_REG (SImode, LR_REGNUM)));
}
void
thumb_expand_epilogue (void)
{
@ -13488,6 +13526,7 @@ 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;
int cfa_offset = 0;
int regno;
@ -13564,18 +13603,14 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
}
}
for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
if (THUMB_REG_PUSHED_P (regno))
live_regs_mask |= 1 << regno;
if (live_regs_mask || thumb_force_lr_save ())
live_regs_mask |= 1 << LR_REGNUM;
live_regs_mask = thumb_compute_save_reg_mask ();
/* Just low regs and lr. */
l_mask = live_regs_mask & 0x40ff;
if (TARGET_BACKTRACE)
{
int offset;
int work_register = 0;
int wr;
int work_register;
/* We have been asked to create a stack backtrace structure.
The code looks like this:
@ -13583,7 +13618,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
0 .align 2
0 func:
0 sub SP, #16 Reserve space for 4 registers.
2 push {R7} Get a work register.
2 push {R7} Push low registers.
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.
@ -13595,24 +13630,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
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 [LAST_ARG_REGNUM] == 0)
work_register = LAST_ARG_REGNUM;
else /* We must push a register of our own. */
live_regs_mask |= (1 << LAST_LO_REGNUM);
}
if (work_register == 0)
{
/* Select a register from the list that will be pushed to
use as our work register. */
for (work_register = (LAST_LO_REGNUM + 1); work_register--;)
if ((1 << work_register) & live_regs_mask)
break;
}
work_register = thumb_find_work_register (live_regs_mask);
asm_fprintf
(f, "\tsub\t%r, %r, #16\t%@ Create stack backtrace structure\n",
@ -13625,12 +13643,13 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset);
}
if (live_regs_mask)
thumb_pushpop (f, live_regs_mask, 1, &cfa_offset, live_regs_mask);
for (offset = 0, wr = 1 << 15; wr != 0; wr >>= 1)
if (wr & live_regs_mask)
offset += 4;
if (l_mask)
{
thumb_pushpop (f, l_mask, 1, &cfa_offset, l_mask);
offset = bit_count (l_mask);
}
else
offset = 0;
asm_fprintf (f, "\tadd\t%r, %r, #%d\n", work_register, SP_REGNUM,
offset + 16 + current_function_pretend_args_size);
@ -13640,7 +13659,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
/* 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)
if (l_mask)
{
asm_fprintf (f, "\tmov\t%r, %r\n", work_register, PC_REGNUM);
asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
@ -13669,32 +13688,24 @@ 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 (live_regs_mask)
thumb_pushpop (f, live_regs_mask, 1, &cfa_offset, live_regs_mask);
else if (l_mask)
thumb_pushpop (f, l_mask, 1, &cfa_offset, l_mask);
for (regno = 8; regno < 13; regno++)
if (THUMB_REG_PUSHED_P (regno))
high_regs_pushed++;
high_regs_pushed = bit_count (live_regs_mask & 0x0f00);
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 > LAST_LO_REGNUM; next_hi_reg--)
if (THUMB_REG_PUSHED_P (next_hi_reg))
if (live_regs_mask & (1 << next_hi_reg))
break;
pushable_regs = mask;
pushable_regs = l_mask & 0xff;
if (pushable_regs == 0)
{
/* Desperation time -- this probably will never happen. */
if (THUMB_REG_PUSHED_P (LAST_ARG_REGNUM))
asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, LAST_ARG_REGNUM);
mask = 1 << LAST_ARG_REGNUM;
}
pushable_regs = 1 << thumb_find_work_register (live_regs_mask);
while (high_regs_pushed > 0)
{
@ -13702,7 +13713,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
for (regno = LAST_LO_REGNUM; regno >= 0; regno--)
{
if (mask & (1 << regno))
if (pushable_regs & (1 << regno))
{
asm_fprintf (f, "\tmov\t%r, %r\n", regno, next_hi_reg);
@ -13713,23 +13724,19 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
for (next_hi_reg--; next_hi_reg > LAST_LO_REGNUM;
next_hi_reg--)
if (THUMB_REG_PUSHED_P (next_hi_reg))
if (live_regs_mask & (1 << next_hi_reg))
break;
}
else
{
mask &= ~((1 << regno) - 1);
pushable_regs &= ~((1 << regno) - 1);
break;
}
}
}
thumb_pushpop (f, mask, 1, &cfa_offset, real_regs_mask);
thumb_pushpop (f, pushable_regs, 1, &cfa_offset, real_regs_mask);
}
if (pushable_regs == 0
&& (THUMB_REG_PUSHED_P (LAST_ARG_REGNUM)))
asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM);
}
}
@ -14741,24 +14748,15 @@ void
thumb_set_return_address (rtx source, rtx scratch)
{
arm_stack_offsets *offsets;
bool lr_saved;
HOST_WIDE_INT delta;
int reg;
rtx addr;
unsigned long mask;
emit_insn (gen_rtx_USE (VOIDmode, source));
lr_saved = FALSE;
for (reg = 0; reg <= LAST_LO_REGNUM; reg++)
{
if (THUMB_REG_PUSHED_P (reg))
{
lr_saved = TRUE;
break;
}
}
lr_saved |= thumb_force_lr_save ();
if (lr_saved)
mask = thumb_compute_save_reg_mask ();
if (mask & (1 << LR_REGNUM))
{
offsets = arm_get_frame_offsets ();

View File

@ -1857,12 +1857,6 @@ typedef struct
((TO) == THUMB_HARD_FRAME_POINTER_REGNUM && TARGET_ARM) ? 0 : \
1)
#define THUMB_REG_PUSHED_P(reg) \
(regs_ever_live [reg] \
&& (! call_used_regs [reg] \
|| (flag_pic && (reg) == PIC_OFFSET_TABLE_REGNUM)) \
&& !(TARGET_SINGLE_PIC_BASE && ((reg) == arm_pic_register)))
/* Define the offset between two registers, one to be eliminated, and the
other its replacement, at the start of a routine. */
#define INITIAL_ELIMINATION_OFFSET(FROM, TO, OFFSET) \