From: Kaz Kojima Date: Mon, 10 May 2004 23:25:13 +0000 (+0000) Subject: re PR target/15130 ([3.3/3.4][sh4-linux] miscompilation with -O2) X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=726d4cb79ca4a963491a2faeb74641e6ae716b74;p=gcc.git 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 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 63345cb8bab..c8a73c22fff 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,19 @@ +2004-05-10 Kaz Kojima + + 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 * gcse.c (eliminate_partially_redundant_loads): Instead of returning early, diff --git a/gcc/config/sh/sh-protos.h b/gcc/config/sh/sh-protos.h index 082d7941838..7abdac48922 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 e3d15b9388e..5b44f1be03c 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 a31e8b125c1..d3b4c9466ed 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; }")