aboutsummaryrefslogtreecommitdiff
path: root/gcc/config
diff options
context:
space:
mode:
authorKaz Kojima <kkojima@gcc.gnu.org>2004-05-10 23:25:13 +0000
committerKaz Kojima <kkojima@gcc.gnu.org>2004-05-10 23:25:13 +0000
commit726d4cb79ca4a963491a2faeb74641e6ae716b74 (patch)
treeaeb2d5e436b16aa338bd7429b2513cc5e20811c8 /gcc/config
parent0c196bf9d84c1d3b638bb4cd5f9e088b861afa08 (diff)
downloadgcc-726d4cb79ca4a963491a2faeb74641e6ae716b74.zip
gcc-726d4cb79ca4a963491a2faeb74641e6ae716b74.tar.gz
gcc-726d4cb79ca4a963491a2faeb74641e6ae716b74.tar.bz2
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
Diffstat (limited to 'gcc/config')
-rw-r--r--gcc/config/sh/sh-protos.h2
-rw-r--r--gcc/config/sh/sh.c89
-rw-r--r--gcc/config/sh/sh.md4
3 files changed, 79 insertions, 16 deletions
diff --git a/gcc/config/sh/sh-protos.h b/gcc/config/sh/sh-protos.h
index 082d794..7abdac4 100644
--- a/gcc/config/sh/sh-protos.h
+++ b/gcc/config/sh/sh-protos.h
@@ -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);
diff --git a/gcc/config/sh/sh.c b/gcc/config/sh/sh.c
index e3d15b9..5b44f1b 100644
--- a/gcc/config/sh/sh.c
+++ b/gcc/config/sh/sh.c
@@ -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);
diff --git a/gcc/config/sh/sh.md b/gcc/config/sh/sh.md
index a31e8b1..d3b4c94 100644
--- a/gcc/config/sh/sh.md
+++ b/gcc/config/sh/sh.md
@@ -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;
}")