Fix for ARM prologue construction failures
Nick Clifton
nickc@redhat.com
Fri Dec 22 10:21:00 GMT 2000
Hi Guys,
I am checking in the patch below to fix a bug in the reorganisation
of ARM function prologue generation that I recently made. This
patch fixes several gcc testsuite failures.
The problem was that if the prologue decided not to save the link
register onto the stack, the second conditional execution pass would
find it and use it to hold scratch values, totally destroying the
return address. The patch adds a USE of the link register to
prevent this, and makes sure that the epilogue code is not fooled by
the fact that the link register has now become set in
regs_ever_live[].
Cheers
Nick
2000-12-22 Nick Clifton <nickc@redhat.com>
* config/arm/arm.h (struct machine_function): Add new field
'lr_save_eliminated'.
* config/arm/arm.c (arm_compute_save_reg_mask): Do not include the
link register if its save has been eliminated.
(output_arm_prologue): Mention if the save of the link register
has been eliminated.
(output_arm_epilogue): Rename 'live_regs_mask' to
'saved_regs_mask'.
Test 'saved_regs_mask' for the link register, not regs_ever_live.
(arm_expand_prologue): If the link register has not been saved set
lr_save_eliminated and emit a USE to prevent later passes from
scavenging it.
Index: config/arm/arm.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/arm/arm.h,v
retrieving revision 1.92
diff -p -r1.92 arm.h
*** arm.h 2000/12/08 19:25:33 1.92
--- arm.h 2000/12/22 18:10:31
*************** typedef struct machine_function
*** 1444,1449 ****
--- 1444,1451 ----
int far_jump_used;
/* Records if ARG_POINTER was ever live. */
int arg_pointer_live;
+ /* Records if the save of LR has been eliminated. */
+ int lr_save_eliminated;
/* Records the type of the current function. */
unsigned long func_type;
}
Index: config/arm/arm.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/config/arm/arm.c,v
retrieving revision 1.127
diff -p -r1.127 arm.c
*** arm.c 2000/12/15 15:10:30 1.127
--- arm.c 2000/12/22 18:10:31
*************** arm_compute_save_reg_mask ()
*** 6906,6911 ****
--- 6906,6914 ----
&& ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)))
save_reg_mask |= 1 << LR_REGNUM;
+ if (cfun->machine->lr_save_eliminated)
+ save_reg_mask &= ~ (1 << LR_REGNUM);
+
return save_reg_mask;
}
*************** output_arm_prologue (f, frame_size)
*** 7204,7209 ****
--- 7207,7215 ----
frame_pointer_needed,
current_function_anonymous_args);
+ if (cfun->machine->lr_save_eliminated)
+ asm_fprintf (f, "\t%@ link register save eliminated.\n");
+
#ifdef AOF_ASSEMBLER
if (flag_pic)
asm_fprintf (f, "\tmov\t%r, %r\n", IP_REGNUM, PIC_OFFSET_TABLE_REGNUM);
*************** arm_output_epilogue (really_return)
*** 7217,7223 ****
int really_return;
{
int reg;
! unsigned long live_regs_mask;
unsigned long func_type;
/* If we need this, then it will always be at least this much. */
int floats_offset = 12;
--- 7223,7229 ----
int really_return;
{
int reg;
! unsigned long saved_regs_mask;
unsigned long func_type;
/* If we need this, then it will always be at least this much. */
int floats_offset = 12;
*************** arm_output_epilogue (really_return)
*** 7255,7265 ****
be doing a return, so we can't tail-call. */
abort ();
! live_regs_mask = arm_compute_save_reg_mask ();
/* Compute how far away the floats will be. */
for (reg = 0; reg <= LAST_ARM_REGNUM; reg ++)
! if (live_regs_mask & (1 << reg))
floats_offset += 4;
if (frame_pointer_needed)
--- 7261,7271 ----
be doing a return, so we can't tail-call. */
abort ();
! saved_regs_mask = arm_compute_save_reg_mask ();
/* Compute how far away the floats will be. */
for (reg = 0; reg <= LAST_ARM_REGNUM; reg ++)
! if (saved_regs_mask & (1 << reg))
floats_offset += 4;
if (frame_pointer_needed)
*************** arm_output_epilogue (really_return)
*** 7309,7335 ****
FP_REGNUM, floats_offset);
}
! /* live_regs_mask should contain the IP, which at the time of stack
frame generation actually contains the old stack pointer. So a
quick way to unwind the stack is just pop the IP register directly
into the stack pointer. */
! if ((live_regs_mask & (1 << IP_REGNUM)) == 0)
abort ();
! live_regs_mask &= ~ (1 << IP_REGNUM);
! live_regs_mask |= (1 << SP_REGNUM);
! /* There are two registers left in live_regs_mask - LR and PC. We
only need to restore the LR register (the return address), but to
save time we can load it directly into the PC, unless we need a
special function exit sequence, or we are not really returning. */
if (really_return && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)
/* Delete the LR from the register mask, so that the LR on
the stack is loaded into the PC in the register mask. */
! live_regs_mask &= ~ (1 << LR_REGNUM);
else
! live_regs_mask &= ~ (1 << PC_REGNUM);
! print_multi_reg (f, "ldmea\t%r", FP_REGNUM, live_regs_mask);
}
else
{
--- 7315,7341 ----
FP_REGNUM, floats_offset);
}
! /* saved_regs_mask should contain the IP, which at the time of stack
frame generation actually contains the old stack pointer. So a
quick way to unwind the stack is just pop the IP register directly
into the stack pointer. */
! if ((saved_regs_mask & (1 << IP_REGNUM)) == 0)
abort ();
! saved_regs_mask &= ~ (1 << IP_REGNUM);
! saved_regs_mask |= (1 << SP_REGNUM);
! /* There are two registers left in saved_regs_mask - LR and PC. We
only need to restore the LR register (the return address), but to
save time we can load it directly into the PC, unless we need a
special function exit sequence, or we are not really returning. */
if (really_return && ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL)
/* Delete the LR from the register mask, so that the LR on
the stack is loaded into the PC in the register mask. */
! saved_regs_mask &= ~ (1 << LR_REGNUM);
else
! saved_regs_mask &= ~ (1 << PC_REGNUM);
! print_multi_reg (f, "ldmea\t%r", FP_REGNUM, saved_regs_mask);
}
else
{
*************** arm_output_epilogue (really_return)
*** 7385,7399 ****
if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL
&& really_return
&& current_function_pretend_args_size == 0
! && regs_ever_live [LR_REGNUM])
{
! live_regs_mask &= ~ (1 << LR_REGNUM);
! live_regs_mask |= (1 << PC_REGNUM);
}
/* Load the registers off the stack. If we only have one register
to load use the LDR instruction - it is faster. */
! if (live_regs_mask == (1 << LR_REGNUM))
{
/* The excpetion handler ignores the LR, so we do
not really need to load it off the stack. */
--- 7391,7405 ----
if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL
&& really_return
&& current_function_pretend_args_size == 0
! && saved_regs_mask & (1 << LR_REGNUM))
{
! saved_regs_mask &= ~ (1 << LR_REGNUM);
! saved_regs_mask |= (1 << PC_REGNUM);
}
/* Load the registers off the stack. If we only have one register
to load use the LDR instruction - it is faster. */
! if (saved_regs_mask == (1 << LR_REGNUM))
{
/* The excpetion handler ignores the LR, so we do
not really need to load it off the stack. */
*************** arm_output_epilogue (really_return)
*** 7402,7409 ****
else
asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
}
! else if (live_regs_mask)
! print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, live_regs_mask);
if (current_function_pretend_args_size)
{
--- 7408,7415 ----
else
asm_fprintf (f, "\tldr\t%r, [%r], #4\n", LR_REGNUM, SP_REGNUM);
}
! else if (saved_regs_mask)
! print_multi_reg (f, "ldmfd\t%r!", SP_REGNUM, saved_regs_mask);
if (current_function_pretend_args_size)
{
*************** arm_output_epilogue (really_return)
*** 7452,7458 ****
here. */
;
else if (current_function_pretend_args_size == 0
! && regs_ever_live [LR_REGNUM])
/* Similarly we may have been able to load LR into the PC
even if we did not create a stack frame. */
;
--- 7458,7464 ----
here. */
;
else if (current_function_pretend_args_size == 0
! && (saved_regs_mask & (1 << LR_REGNUM)))
/* Similarly we may have been able to load LR into the PC
even if we did not create a stack frame. */
;
*************** arm_expand_prologue ()
*** 7879,7884 ****
--- 7885,7898 ----
scheduling in the prolog. */
if (profile_flag || profile_block_flag || TARGET_NO_SCHED_PRO)
emit_insn (gen_blockage ());
+
+ /* 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. */
+ if ((live_regs_mask & (1 << LR_REGNUM)) == 0)
+ {
+ emit_insn (gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, LR_REGNUM)));
+ cfun->machine->lr_save_eliminated = 1;
+ }
}
/* If CODE is 'd', then the X is a condition operand and the instruction
More information about the Gcc-patches
mailing list