re PR target/15130 ([3.3/3.4][sh4-linux] miscompilation with -O2)
PR target/15130 * config/sh/sh-protos.h (sh_expand_epilogue): Change prototype. * config/sh/sh.c (output_stack_adjust): Take the sibcall epilogue into account. Compute the correct number of general registers for the return value. Generate a special push/pop sequence when failing to get a temporary register for non SHmedia epilogue. (sh_expand_epilogue): Add an argument to show whether it's for sibcall or not. Set the 3rd argument of output_stack_adjust to -1 if needed. (sh_need_epilogue): Call sh_expand_epilogue with 0. * config/sh/sh.md (sibcall_epilogue): Call sh_expand_epilogue with 1. (epilogue): Call sh_expand_epilogue with 0. From-SVN: r81683
This commit is contained in:
parent
0c196bf9d8
commit
726d4cb79c
|
@ -1,3 +1,19 @@
|
|||
2004-05-10 Kaz Kojima <kkojima@gcc.gnu.org>
|
||||
|
||||
PR target/15130
|
||||
* config/sh/sh-protos.h (sh_expand_epilogue): Change prototype.
|
||||
* config/sh/sh.c (output_stack_adjust): Take the sibcall epilogue
|
||||
into account. Compute the correct number of general registers
|
||||
for the return value. Generate a special push/pop sequence when
|
||||
failing to get a temporary register for non SHmedia epilogue.
|
||||
(sh_expand_epilogue): Add an argument to show whether it's for
|
||||
sibcall or not. Set the 3rd argument of output_stack_adjust to
|
||||
-1 if needed.
|
||||
(sh_need_epilogue): Call sh_expand_epilogue with 0.
|
||||
* config/sh/sh.md (sibcall_epilogue): Call sh_expand_epilogue
|
||||
with 1.
|
||||
(epilogue): Call sh_expand_epilogue with 0.
|
||||
|
||||
2004-05-10 Andrew Pinski <pinskia@physics.uc.edu>
|
||||
|
||||
* gcse.c (eliminate_partially_redundant_loads): Instead of returning early,
|
||||
|
|
|
@ -109,7 +109,7 @@ extern int sh_handle_pragma (int (*)(void), void (*)(int), const char *);
|
|||
extern struct rtx_def *get_fpscr_rtx (void);
|
||||
extern int sh_media_register_for_return (void);
|
||||
extern void sh_expand_prologue (void);
|
||||
extern void sh_expand_epilogue (void);
|
||||
extern void sh_expand_epilogue (bool);
|
||||
extern int sh_need_epilogue (void);
|
||||
extern void sh_set_return_address (rtx, rtx);
|
||||
extern int initial_elimination_offset (int, int);
|
||||
|
|
|
@ -4630,8 +4630,9 @@ static int extra_push;
|
|||
|
||||
/* Adjust the stack by SIZE bytes. REG holds the rtl of the register to be
|
||||
adjusted. If epilogue_p is zero, this is for a prologue; otherwise, it's
|
||||
for an epilogue. If LIVE_REGS_MASK is nonzero, it points to a HARD_REG_SET
|
||||
of all the registers that are about to be restored, and hence dead. */
|
||||
for an epilogue and a negative value means that it's for a sibcall
|
||||
epilogue. If LIVE_REGS_MASK is nonzero, it points to a HARD_REG_SET of
|
||||
all the registers that are about to be restored, and hence dead. */
|
||||
|
||||
static void
|
||||
output_stack_adjust (int size, rtx reg, int epilogue_p,
|
||||
|
@ -4666,17 +4667,27 @@ output_stack_adjust (int size, rtx reg, int epilogue_p,
|
|||
/* If TEMP is invalid, we could temporarily save a general
|
||||
register to MACL. However, there is currently no need
|
||||
to handle this case, so just abort when we see it. */
|
||||
if (current_function_interrupt
|
||||
if (epilogue_p < 0
|
||||
|| current_function_interrupt
|
||||
|| ! call_used_regs[temp] || fixed_regs[temp])
|
||||
temp = -1;
|
||||
if (temp < 0 && ! current_function_interrupt)
|
||||
if (temp < 0 && ! current_function_interrupt
|
||||
&& (TARGET_SHMEDIA || epilogue_p >= 0))
|
||||
{
|
||||
HARD_REG_SET temps;
|
||||
COPY_HARD_REG_SET (temps, call_used_reg_set);
|
||||
AND_COMPL_HARD_REG_SET (temps, call_fixed_reg_set);
|
||||
if (epilogue_p)
|
||||
if (epilogue_p > 0)
|
||||
{
|
||||
for (i = 0; i < HARD_REGNO_NREGS (FIRST_RET_REG, DImode); i++)
|
||||
int nreg = 0;
|
||||
if (current_function_return_rtx)
|
||||
{
|
||||
enum machine_mode mode;
|
||||
mode = GET_MODE (current_function_return_rtx);
|
||||
if (BASE_RETURN_VALUE_REG (mode) == FIRST_RET_REG)
|
||||
nreg = HARD_REGNO_NREGS (FIRST_RET_REG, mode);
|
||||
}
|
||||
for (i = 0; i < nreg; i++)
|
||||
CLEAR_HARD_REG_BIT (temps, FIRST_RET_REG + i);
|
||||
if (current_function_calls_eh_return)
|
||||
{
|
||||
|
@ -4685,7 +4696,10 @@ output_stack_adjust (int size, rtx reg, int epilogue_p,
|
|||
CLEAR_HARD_REG_BIT (temps, EH_RETURN_DATA_REGNO (i));
|
||||
}
|
||||
}
|
||||
else
|
||||
if (TARGET_SHMEDIA && epilogue_p < 0)
|
||||
for (i = FIRST_TARGET_REG; i <= LAST_TARGET_REG; i++)
|
||||
CLEAR_HARD_REG_BIT (temps, i);
|
||||
if (epilogue_p <= 0)
|
||||
{
|
||||
for (i = FIRST_PARM_REG;
|
||||
i < FIRST_PARM_REG + NPARM_REGS (SImode); i++)
|
||||
|
@ -4698,7 +4712,55 @@ output_stack_adjust (int size, rtx reg, int epilogue_p,
|
|||
if (temp < 0 && live_regs_mask)
|
||||
temp = scavenge_reg (live_regs_mask);
|
||||
if (temp < 0)
|
||||
abort ();
|
||||
{
|
||||
/* If we reached here, the most likely case is the (sibcall)
|
||||
epilogue for non SHmedia. Put a special push/pop sequence
|
||||
for such case as the last resort. This looks lengthy but
|
||||
would not be problem because it seems to be very rare. */
|
||||
if (! TARGET_SHMEDIA && epilogue_p)
|
||||
{
|
||||
rtx adj_reg, tmp_reg, mem;
|
||||
|
||||
/* ??? There is still the slight possibility that r4 or r5
|
||||
have been reserved as fixed registers or assigned as
|
||||
global registers, and they change during an interrupt.
|
||||
There are possible ways to handle this:
|
||||
- If we are adjusting the frame pointer (r14), we can do
|
||||
with a single temp register and an ordinary push / pop
|
||||
on the stack.
|
||||
- Grab any call-used or call-saved registers (i.e. not
|
||||
fixed or globals) for the temps we need. We might
|
||||
also grab r14 if we are adjusting the stack pointer.
|
||||
If we can't find enough available registers, issue
|
||||
a diagnostic and abort - the user must have reserved
|
||||
way too many registers.
|
||||
But since all this is rather unlikely to happen and
|
||||
would require extra testing, we just abort if r4 / r5
|
||||
are not available. */
|
||||
if (fixed_regs[4] || fixed_regs[5]
|
||||
|| global_regs[4] || global_regs[5])
|
||||
abort ();
|
||||
|
||||
adj_reg = gen_rtx_REG (GET_MODE (reg), 4);
|
||||
tmp_reg = gen_rtx_REG (GET_MODE (reg), 5);
|
||||
emit_move_insn (gen_rtx_MEM (Pmode, reg), adj_reg);
|
||||
emit_insn (GEN_MOV (adj_reg, GEN_INT (size)));
|
||||
emit_insn (GEN_ADD3 (adj_reg, adj_reg, reg));
|
||||
mem = gen_rtx_MEM (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
|
||||
emit_move_insn (mem, tmp_reg);
|
||||
emit_move_insn (tmp_reg, gen_rtx_MEM (Pmode, reg));
|
||||
mem = gen_rtx_MEM (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
|
||||
emit_move_insn (mem, tmp_reg);
|
||||
emit_move_insn (reg, adj_reg);
|
||||
mem = gen_rtx_MEM (Pmode, gen_rtx_POST_INC (Pmode, reg));
|
||||
emit_move_insn (adj_reg, mem);
|
||||
mem = gen_rtx_MEM (Pmode, gen_rtx_POST_INC (Pmode, reg));
|
||||
emit_move_insn (tmp_reg, mem);
|
||||
return;
|
||||
}
|
||||
else
|
||||
abort ();
|
||||
}
|
||||
const_reg = gen_rtx_REG (GET_MODE (reg), temp);
|
||||
|
||||
/* If SIZE is negative, subtract the positive value.
|
||||
|
@ -5538,7 +5600,7 @@ sh_expand_prologue (void)
|
|||
}
|
||||
|
||||
void
|
||||
sh_expand_epilogue (void)
|
||||
sh_expand_epilogue (bool sibcall_p)
|
||||
{
|
||||
HARD_REG_SET live_regs_mask;
|
||||
int d, i;
|
||||
|
@ -5547,6 +5609,7 @@ sh_expand_epilogue (void)
|
|||
int save_flags = target_flags;
|
||||
int frame_size, save_size;
|
||||
int fpscr_deferred = 0;
|
||||
int e = sibcall_p ? -1 : 1;
|
||||
|
||||
d = calc_live_regs (&live_regs_mask);
|
||||
|
||||
|
@ -5581,7 +5644,7 @@ sh_expand_epilogue (void)
|
|||
|
||||
if (frame_pointer_needed)
|
||||
{
|
||||
output_stack_adjust (frame_size, frame_pointer_rtx, 1, &live_regs_mask);
|
||||
output_stack_adjust (frame_size, frame_pointer_rtx, e, &live_regs_mask);
|
||||
|
||||
/* We must avoid moving the stack pointer adjustment past code
|
||||
which reads from the local frame, else an interrupt could
|
||||
|
@ -5597,7 +5660,7 @@ sh_expand_epilogue (void)
|
|||
occur after the SP adjustment and clobber data in the local
|
||||
frame. */
|
||||
emit_insn (gen_blockage ());
|
||||
output_stack_adjust (frame_size, stack_pointer_rtx, 1, &live_regs_mask);
|
||||
output_stack_adjust (frame_size, stack_pointer_rtx, e, &live_regs_mask);
|
||||
}
|
||||
|
||||
if (SHMEDIA_REGS_STACK_ADJUST ())
|
||||
|
@ -5770,7 +5833,7 @@ sh_expand_epilogue (void)
|
|||
output_stack_adjust (extra_push + current_function_pretend_args_size
|
||||
+ save_size + d_rounding
|
||||
+ current_function_args_info.stack_regs * 8,
|
||||
stack_pointer_rtx, 1, NULL);
|
||||
stack_pointer_rtx, e, NULL);
|
||||
|
||||
if (current_function_calls_eh_return)
|
||||
emit_insn (GEN_ADD3 (stack_pointer_rtx, stack_pointer_rtx,
|
||||
|
@ -5798,7 +5861,7 @@ sh_need_epilogue (void)
|
|||
rtx epilogue;
|
||||
|
||||
start_sequence ();
|
||||
sh_expand_epilogue ();
|
||||
sh_expand_epilogue (0);
|
||||
epilogue = get_insns ();
|
||||
end_sequence ();
|
||||
sh_need_epilogue_known = (epilogue == NULL ? -1 : 1);
|
||||
|
|
|
@ -6467,7 +6467,7 @@
|
|||
""
|
||||
"
|
||||
{
|
||||
sh_expand_epilogue ();
|
||||
sh_expand_epilogue (1);
|
||||
if (TARGET_SHCOMPACT)
|
||||
{
|
||||
rtx insn, set;
|
||||
|
@ -7348,7 +7348,7 @@ mov.l\\t1f,r0\\n\\
|
|||
""
|
||||
"
|
||||
{
|
||||
sh_expand_epilogue ();
|
||||
sh_expand_epilogue (0);
|
||||
emit_jump_insn (gen_return ());
|
||||
DONE;
|
||||
}")
|
||||
|
|
Loading…
Reference in New Issue