re PR target/15130 ([3.3/3.4][sh4-linux] miscompilation with -O2)
authorKaz Kojima <kkojima@gcc.gnu.org>
Mon, 10 May 2004 23:25:13 +0000 (23:25 +0000)
committerKaz Kojima <kkojima@gcc.gnu.org>
Mon, 10 May 2004 23:25:13 +0000 (23:25 +0000)
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

gcc/ChangeLog
gcc/config/sh/sh-protos.h
gcc/config/sh/sh.c
gcc/config/sh/sh.md

index 63345cb8babe59bf94ec669e7a35d4787a764fab..c8a73c22fffa7faa460612de0ecbf0d4e30a5b6c 100644 (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,
index 082d7941838a92d59880c75d32c6f1c55434cd20..7abdac48922146b769dd14ca33cd81660b17cfb1 100644 (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);
index e3d15b9388e0daf010ce7719352b2c7237e99447..5b44f1be03cfe184d880db5c720927b9031a0d0d 100644 (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);
index a31e8b125c1b44b5d53c4c202451e6dd5538afb1..d3b4c9466ed3e141e27654069f50b66042350510 100644 (file)
   ""
   "
 {
-  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;
 }")