Fix linux kernel -foptimize-sibling-calls miscompilation
authorJim Wilson <wilson@redhat.com>
Sat, 14 Apr 2001 03:49:46 +0000 (03:49 +0000)
committerJim Wilson <wilson@gcc.gnu.org>
Sat, 14 Apr 2001 03:49:46 +0000 (20:49 -0700)
Fix linux kernel -foptimize-sibling-calls miscompilation
* config/ia64/ia64.c (ia64_expand_epilogue): Emit alloc if sibcall_p.
(first_instruction): New static variable.
(rtx_needs_barrier): Return 1 for alloc.
(init_insn_group_barriers): Set first_instruction.
(rws_sum): Delete duplicate definition.
(group_barrier_needed_p): Return 0 when first_instruction true.
(safe_group_barrier_needed_p): Save and restore first_instruction
around group_barrier_needed_p call.

From-SVN: r41347

gcc/ChangeLog
gcc/config/ia64/ia64.c

index aadb2017b6b692ab9e5c3e543993f730ddd2ee01..9dcb55d7ce7045cb0da3ad31a9ee467b9bb3c7b5 100644 (file)
@@ -1,3 +1,14 @@
+2001-04-13  Jim Wilson  <wilson@redhat.com>
+
+       * config/ia64/ia64.c (ia64_expand_epilogue): Emit alloc if sibcall_p.
+       (first_instruction): New static variable.
+       (rtx_needs_barrier): Return 1 for alloc.
+       (init_insn_group_barriers): Set first_instruction.
+       (rws_sum): Delete duplicate definition.
+       (group_barrier_needed_p): Return 0 when first_instruction true.
+       (safe_group_barrier_needed_p): Save and restore first_instruction
+       around group_barrier_needed_p call.
+
 Fri Apr 13 21:40:28 2001  Loren J. Rittle  <ljrittle@acm.org>
 
        * expr.h (enum libfunc_index): Add LTI_memmove.
index 17f5768c6ec0fecaa90b71cbcc52697ae4e73e40..5b6ffba626deb51111e88028c6167130ef0e3231 100644 (file)
@@ -2399,6 +2399,18 @@ ia64_expand_epilogue (sibcall_p)
  
   if (! sibcall_p)
     emit_jump_insn (gen_return_internal (gen_rtx_REG (DImode, BR_REG (0))));
+  else
+    /* We must emit an alloc to force the input registers to become output
+       registers.  Otherwise, if the callee tries to pass its parameters
+       through to another call without an intervening alloc, then these
+       values get lost.  */
+    /* ??? We don't need to preserve all input registers.  We only need to
+       preserve those input registers used as arguments to the sibling call.
+       It is unclear how to compute that number here.  */
+    emit_insn (gen_alloc (gen_rtx_REG (DImode, GR_REG (2)),
+                         GEN_INT (0), GEN_INT (0),
+                         GEN_INT (current_frame_info.n_input_regs),
+                         GEN_INT (0)));
 }
 
 /* Return 1 if br.ret can do all the work required to return from a
@@ -3818,6 +3830,11 @@ struct reg_write_state rws_sum[NUM_REGS];
    stop bit is emitted.  */
 struct reg_write_state rws_insn[NUM_REGS];
 
+/* Indicates whether this is the first instruction after a stop bit,
+   in which case we don't need another stop bit.  Without this, we hit
+   the abort in ia64_variable_issue when scheduling an alloc.  */
+static int first_instruction;
+
 /* Misc flags needed to compute RAW/WAW dependencies while we are traversing
    RTL for one instruction.  */
 struct reg_flags
@@ -4379,14 +4396,18 @@ rtx_needs_barrier (x, flags, pred)
       switch (XINT (x, 1))
        {
        case 0: /* alloc */
-         /* Alloc must always be the first instruction.  Currently, we
-            only emit it at the function start, so we don't need to worry
-            about emitting a stop bit before it.  */
-         need_barrier = rws_access_regno (AR_PFS_REGNUM, flags, pred);
+         /* Alloc must always be the first instruction of a group.
+            We force this by always returning true.  */
+         /* ??? We might get better scheduling if we explicitly check for
+            input/local/output register dependencies, and modify the
+            scheduler so that alloc is always reordered to the start of
+            the current group.  We could then eliminate all of the
+            first_instruction code.  */
+         rws_access_regno (AR_PFS_REGNUM, flags, pred);
 
          new_flags.is_write = 1;
-         need_barrier |= rws_access_regno (REG_AR_CFM, new_flags, pred);
-         return need_barrier;
+         rws_access_regno (REG_AR_CFM, new_flags, pred);
+         return 1;
 
        case 1: /* blockage */
        case 2: /* insn group barrier */
@@ -4455,11 +4476,9 @@ static void
 init_insn_group_barriers ()
 {
   memset (rws_sum, 0, sizeof (rws_sum));
+  first_instruction = 1;
 }
 
-/* Cumulative info for the current instruction group.  */
-struct reg_write_state rws_sum[NUM_REGS];
-
 /* Given the current state, recorded by previous calls to this function,
    determine whether a group barrier (a stop bit) is necessary before INSN.
    Return nonzero if so.  */
@@ -4537,12 +4556,18 @@ group_barrier_needed_p (insn)
         asm.  */
       if (! need_barrier)
        need_barrier = rws_access_regno (REG_VOLATILE, flags, 0);
-
       break;
 
     default:
       abort ();
     }
+
+  if (first_instruction)
+    {
+      need_barrier = 0;
+      first_instruction = 0;
+    }
+
   return need_barrier;
 }
 
@@ -4553,10 +4578,17 @@ safe_group_barrier_needed_p (insn)
      rtx insn;
 {
   struct reg_write_state rws_saved[NUM_REGS];
+  int saved_first_instruction;
   int t;
+
   memcpy (rws_saved, rws_sum, NUM_REGS * sizeof *rws_saved);
+  saved_first_instruction = first_instruction;
+
   t = group_barrier_needed_p (insn);
+
   memcpy (rws_sum, rws_saved, NUM_REGS * sizeof *rws_saved);
+  first_instruction = saved_first_instruction;
+
   return t;
 }