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:
Kaz Kojima 2004-05-10 23:25:13 +00:00
parent 0c196bf9d8
commit 726d4cb79c
4 changed files with 95 additions and 16 deletions

View File

@ -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,

View File

@ -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);

View File

@ -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);

View File

@ -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;
}")