]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Fix register elimination problem
authorBernd Schmidt <bernds@cygnus.co.uk>
Fri, 22 Oct 1999 22:02:17 +0000 (22:02 +0000)
committerBernd Schmidt <crux@gcc.gnu.org>
Fri, 22 Oct 1999 22:02:17 +0000 (22:02 +0000)
From-SVN: r30134

gcc/ChangeLog
gcc/genoutput.c
gcc/recog.h
gcc/reload1.c

index b21a88323c77c7ec5ba194210ed5eb4a054bcf7d..128dadc416a3e8c30b56cf84590f33a1cb3bb1f4 100644 (file)
@@ -1,3 +1,24 @@
+Fri Oct 22 23:46:50 1999  Bernd Schmidt  <bernds@cygnus.co.uk>
+
+       * genoutput.c (struct operand_data): New elt eliminable.
+       (output_operand_data): Write it.
+       (scan_operands): Set it for MATCH_OPERAND, clear for other matchers.
+       (compare_operands): Take it into account.
+       * recog.h (struct insn_operand_data): New elt eliminable.
+       * reload1.c (check_eliminable_occurrences, elimination_effects): New
+       functions.
+       (old_asm_operands_vec, new_asm_operands_vec): Delete.
+       (eliminate_regs): Move code that detects changes to elimination
+       target regs into new function elimination_effects.
+       Delete one #if 0 block.
+       Abort for USE, CLOBBER, ASM_OPERANDS and SET. 
+       (eliminate_regs_in_insn): Return immediately for USEs, CLOBBERs,
+       ADDR_VECs, ADDR_DIFF_VECs and ASM_INPUTs.
+       Only call eliminate_regs for real operands of the insn, not for parts
+       of its structure or parts matched by things like match_operator.
+       Use elimination_effects and check_eliminable_occurrences.  Use
+       copy_insn to duplicate the pattern when not in the final pass.
+
 Fri Oct 22 09:03:44 1999  Mark Mitchell  <mark@codesourcery.com>
 
        * i386.md: Add missing `y' modifiers to uses of fst, fstp, fld,
index 4cb9c00b6490672057d29485033fbe8e210e6be5..bc0ac4529b31265b0c08519f23bd1667974e59ab 100644 (file)
@@ -65,6 +65,10 @@ Boston, MA 02111-1307, USA.  */
 
      e. `strict_low', is nonzero for operands contained in a STRICT_LOW_PART.
 
+     f. `eliminable', is nonzero for operands that are matched normally by
+     MATCH_OPERAND; it is zero for operands that should not be changed during
+     register elimination such as MATCH_OPERATORs.
+
   The code number of an insn is simply its position in the machine
   description; code numbers are assigned sequentially to entries in
   the description, starting with code number 0.
@@ -128,6 +132,7 @@ struct operand_data
   unsigned char n_alternatives;
   char address_p;
   char strict_low;
+  char eliminable;
   char seen;
 };
 
@@ -287,7 +292,9 @@ output_operand_data ()
 
       printf ("    %smode,\n", GET_MODE_NAME (d->mode));
 
-      printf ("    %d\n", d->strict_low);
+      printf ("    %d,\n", d->strict_low);
+
+      printf ("    %d\n", d->eliminable);
 
       printf("  },\n");
     }
@@ -436,6 +443,7 @@ scan_operands (d, part, this_address_p, this_strict_low)
        d->operand[opno].n_alternatives
          = n_occurrences (',', XSTR (part, 2)) + 1;
       d->operand[opno].address_p = this_address_p;
+      d->operand[opno].eliminable = 1;
       return;
 
     case MATCH_SCRATCH:
@@ -460,6 +468,7 @@ scan_operands (d, part, this_address_p, this_strict_low)
        d->operand[opno].n_alternatives
          = n_occurrences (',', XSTR (part, 1)) + 1;
       d->operand[opno].address_p = 0;
+      d->operand[opno].eliminable = 0;
       return;
 
     case MATCH_OPERATOR:
@@ -482,6 +491,7 @@ scan_operands (d, part, this_address_p, this_strict_low)
       d->operand[opno].predicate = XSTR (part, 1);
       d->operand[opno].constraint = 0;
       d->operand[opno].address_p = 0;
+      d->operand[opno].eliminable = 0;
       for (i = 0; i < XVECLEN (part, 2); i++)
        scan_operands (d, XVECEXP (part, 2, i), 0, 0);
       return;
@@ -553,6 +563,9 @@ compare_operands (d0, d1)
   if (d0->strict_low != d1->strict_low)
     return 0;
 
+  if (d0->eliminable != d1->eliminable)
+    return 0;
+
   return 1;
 }
 
index b84758cf0d6e357ca5fce7e6bb974f4f119f5ed3..37c9c4f002dd0683b7761f5986c6b251a9a4ac37 100644 (file)
@@ -209,6 +209,8 @@ struct insn_operand_data
   enum machine_mode mode;
 
   char strict_low;
+
+  char eliminable;
 };
 
 /* Legal values for insn_data.output_format.  Indicate what type of data
index f4036d091617a11248c0964152cd8c59a437ec06..cad122155640d03650ced96108dbf2cf40d6add8 100644 (file)
@@ -394,6 +394,8 @@ static void maybe_mark_pseudo_spilled       PROTO((int));
 static void delete_dead_insn           PROTO((rtx));
 static void alter_reg                          PROTO((int, int));
 static void set_label_offsets          PROTO((rtx, rtx, int));
+static void check_eliminable_occurrences       PROTO((rtx));
+static void elimination_effects                PROTO((rtx, enum machine_mode));
 static int eliminate_regs_in_insn      PROTO((rtx, int));
 static void update_eliminable_offsets  PROTO((void));
 static void mark_not_eliminable                PROTO((rtx, rtx));
@@ -2633,11 +2635,6 @@ set_label_offsets (x, insn, initial_p)
     }
 }
 \f
-/* Used for communication between the next two function to properly share
-   the vector for an ASM_OPERANDS.  */
-
-static struct rtvec_def *old_asm_operands_vec, *new_asm_operands_vec;
-
 /* Scan X and replace any eliminable registers (such as fp) with a
    replacement (such as sp), plus an offset.
 
@@ -2657,9 +2654,6 @@ static struct rtvec_def *old_asm_operands_vec, *new_asm_operands_vec;
    This means, do not set ref_outside_mem even if the reference
    is outside of MEMs.
 
-   If we see a modification to a register we know about, take the
-   appropriate action (see case SET, below).
-
    REG_EQUIV_MEM and REG_EQUIV_ADDRESS contain address that have had
    replacements done assuming all offsets are at their initial values.  If
    they are not, or if REG_EQUIV_ADDRESS is nonzero for a pseudo we
@@ -2717,14 +2711,7 @@ eliminate_regs (x, mem_mode, insn)
          for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
               ep++)
            if (ep->from_rtx == x && ep->can_eliminate)
-             {
-               if (! mem_mode
-                   /* Refs inside notes don't count for this purpose.  */
-                   && ! (insn != 0 && (GET_CODE (insn) == EXPR_LIST
-                                       || GET_CODE (insn) == INSN_LIST)))
-                 ep->ref_outside_mem = 1;
-               return plus_constant (ep->to_rtx, ep->previous_offset);
-             }
+             return plus_constant (ep->to_rtx, ep->previous_offset);
 
        }
       else if (reg_renumber[regno] < 0 && reg_equiv_constant
@@ -2759,12 +2746,6 @@ eliminate_regs (x, mem_mode, insn)
               ep++)
            if (ep->from_rtx == XEXP (x, 0) && ep->can_eliminate)
              {
-               if (! mem_mode
-                   /* Refs inside notes don't count for this purpose.  */
-                   && ! (insn != 0 && (GET_CODE (insn) == EXPR_LIST
-                                       || GET_CODE (insn) == INSN_LIST)))
-                 ep->ref_outside_mem = 1;
-
                /* The only time we want to replace a PLUS with a REG (this
                   occurs when the constant operand of the PLUS is the negative
                   of the offset) is when we are inside a MEM.  We won't want
@@ -2791,14 +2772,10 @@ eliminate_regs (x, mem_mode, insn)
         outermost PLUS.  We will do this by doing register replacement in
         our operands and seeing if a constant shows up in one of them.
 
-        We assume here this is part of an address (or a "load address" insn)
-        since an eliminable register is not likely to appear in any other
-        context.
-
-        If we have (plus (eliminable) (reg)), we want to produce
-        (plus (plus (replacement) (reg) (const))).  If this was part of a
-        normal add insn, (plus (replacement) (reg)) will be pushed as a
-        reload.  This is the desired action.  */
+        Note that there is no risk of modifying the structure of the insn,
+        since we only get called for its operands, thus we are either
+        modifying the address inside a MEM, or something like an address
+        operand of a load-address insn.  */
 
       {
        rtx new0 = eliminate_regs (XEXP (x, 0), mem_mode, insn);
@@ -2920,23 +2897,6 @@ eliminate_regs (x, mem_mode, insn)
     case POST_INC:
     case PRE_DEC:
     case POST_DEC:
-      for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
-       if (ep->to_rtx == XEXP (x, 0))
-         {
-           int size = GET_MODE_SIZE (mem_mode);
-
-           /* If more bytes than MEM_MODE are pushed, account for them.  */
-#ifdef PUSH_ROUNDING
-           if (ep->to_rtx == stack_pointer_rtx)
-             size = PUSH_ROUNDING (size);
-#endif
-           if (code == PRE_DEC || code == POST_DEC)
-             ep->offset += size;
-           else
-             ep->offset -= size;
-         }
-
-      /* Fall through to generic unary operation case.  */
     case STRICT_LOW_PART:
     case NEG:          case NOT:
     case SIGN_EXTEND:  case ZERO_EXTEND:
@@ -2964,30 +2924,7 @@ eliminate_regs (x, mem_mode, insn)
          && reg_equiv_memory_loc != 0
          && reg_equiv_memory_loc[REGNO (SUBREG_REG (x))] != 0)
        {
-#if 0
-         new = eliminate_regs (reg_equiv_memory_loc[REGNO (SUBREG_REG (x))],
-                               mem_mode, insn);
-
-         /* If we didn't change anything, we must retain the pseudo.  */
-         if (new == reg_equiv_memory_loc[REGNO (SUBREG_REG (x))])
-           new = SUBREG_REG (x);
-         else
-           {
-             /* In this case, we must show that the pseudo is used in this
-                insn so that delete_output_reload will do the right thing.  */
-             if (insn != 0 && GET_CODE (insn) != EXPR_LIST
-                 && GET_CODE (insn) != INSN_LIST)
-               REG_NOTES (emit_insn_before (gen_rtx_USE (VOIDmode,
-                                                         SUBREG_REG (x)),
-                                            insn))
-                 = gen_rtx_EXPR_LIST (REG_EQUAL, new, NULL_RTX);
-
-             /* Ensure NEW isn't shared in case we have to reload it.  */
-             new = copy_rtx (new);
-           }
-#else
          new = SUBREG_REG (x);
-#endif
        }
       else
        new = eliminate_regs (SUBREG_REG (x), mem_mode, insn);
@@ -3031,133 +2968,6 @@ eliminate_regs (x, mem_mode, insn)
 
       return x;
 
-    case USE:
-      /* If using a register that is the source of an eliminate we still
-        think can be performed, note it cannot be performed since we don't
-        know how this register is used.  */
-      for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
-       if (ep->from_rtx == XEXP (x, 0))
-         ep->can_eliminate = 0;
-
-      new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
-      if (new != XEXP (x, 0))
-       return gen_rtx_fmt_e (code, GET_MODE (x), new);
-      return x;
-
-    case CLOBBER:
-      /* If clobbering a register that is the replacement register for an
-        elimination we still think can be performed, note that it cannot
-        be performed.  Otherwise, we need not be concerned about it.  */
-      for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
-       if (ep->to_rtx == XEXP (x, 0))
-         ep->can_eliminate = 0;
-
-      new = eliminate_regs (XEXP (x, 0), mem_mode, insn);
-      if (new != XEXP (x, 0))
-       return gen_rtx_fmt_e (code, GET_MODE (x), new);
-      return x;
-
-    case ASM_OPERANDS:
-      {
-       rtx *temp_vec;
-       /* Properly handle sharing input and constraint vectors.  */
-       if (ASM_OPERANDS_INPUT_VEC (x) != old_asm_operands_vec)
-         {
-           /* When we come to a new vector not seen before,
-              scan all its elements; keep the old vector if none
-              of them changes; otherwise, make a copy.  */
-           old_asm_operands_vec = ASM_OPERANDS_INPUT_VEC (x);
-           temp_vec = (rtx *) alloca (XVECLEN (x, 3) * sizeof (rtx));
-           for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
-             temp_vec[i] = eliminate_regs (ASM_OPERANDS_INPUT (x, i),
-                                           mem_mode, insn);
-
-           for (i = 0; i < ASM_OPERANDS_INPUT_LENGTH (x); i++)
-             if (temp_vec[i] != ASM_OPERANDS_INPUT (x, i))
-               break;
-
-           if (i == ASM_OPERANDS_INPUT_LENGTH (x))
-             new_asm_operands_vec = old_asm_operands_vec;
-           else
-             new_asm_operands_vec
-               = gen_rtvec_v (ASM_OPERANDS_INPUT_LENGTH (x), temp_vec);
-         }
-
-       /* If we had to copy the vector, copy the entire ASM_OPERANDS.  */
-       if (new_asm_operands_vec == old_asm_operands_vec)
-         return x;
-
-       new = gen_rtx_ASM_OPERANDS (VOIDmode, ASM_OPERANDS_TEMPLATE (x),
-                                   ASM_OPERANDS_OUTPUT_CONSTRAINT (x),
-                                   ASM_OPERANDS_OUTPUT_IDX (x),
-                                   new_asm_operands_vec,
-                                   ASM_OPERANDS_INPUT_CONSTRAINT_VEC (x),
-                                   ASM_OPERANDS_SOURCE_FILE (x),
-                                   ASM_OPERANDS_SOURCE_LINE (x));
-       new->volatil = x->volatil;
-       return new;
-      }
-
-    case SET:
-      /* Check for setting a register that we know about.  */
-      if (GET_CODE (SET_DEST (x)) == REG)
-       {
-         /* See if this is setting the replacement register for an
-            elimination.
-
-            If DEST is the hard frame pointer, we do nothing because we
-            assume that all assignments to the frame pointer are for
-            non-local gotos and are being done at a time when they are valid
-            and do not disturb anything else.  Some machines want to
-            eliminate a fake argument pointer (or even a fake frame pointer)
-            with either the real frame or the stack pointer.  Assignments to
-            the hard frame pointer must not prevent this elimination.  */
-
-         for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
-              ep++)
-           if (ep->to_rtx == SET_DEST (x)
-               && SET_DEST (x) != hard_frame_pointer_rtx)
-             {
-               /* If it is being incremented, adjust the offset.  Otherwise,
-                  this elimination can't be done.  */
-               rtx src = SET_SRC (x);
-
-               if (GET_CODE (src) == PLUS
-                   && XEXP (src, 0) == SET_DEST (x)
-                   && GET_CODE (XEXP (src, 1)) == CONST_INT)
-                 ep->offset -= INTVAL (XEXP (src, 1));
-               else
-                 ep->can_eliminate = 0;
-             }
-
-         /* Now check to see we are assigning to a register that can be
-            eliminated.  If so, it must be as part of a PARALLEL, since we
-            will not have been called if this is a single SET.  So indicate
-            that we can no longer eliminate this reg.  */
-         for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
-              ep++)
-           if (ep->from_rtx == SET_DEST (x) && ep->can_eliminate)
-             ep->can_eliminate = 0;
-       }
-
-      /* Now avoid the loop below in this common case.  */
-      {
-       rtx new0 = eliminate_regs (SET_DEST (x), 0, insn);
-       rtx new1 = eliminate_regs (SET_SRC (x), 0, insn);
-
-       /* If SET_DEST changed from a REG to a MEM and INSN is an insn,
-          write a CLOBBER insn.  */
-       if (GET_CODE (SET_DEST (x)) == REG && GET_CODE (new0) == MEM
-           && insn != 0 && GET_CODE (insn) != EXPR_LIST
-           && GET_CODE (insn) != INSN_LIST)
-         emit_insn_after (gen_rtx_CLOBBER (VOIDmode, SET_DEST (x)), insn);
-
-       if (new0 != SET_DEST (x) || new1 != SET_SRC (x))
-         return gen_rtx_SET (VOIDmode, new0, new1);
-      }
-
-      return x;
-
     case MEM:
       /* This is only for the benefit of the debugging backends, which call
         eliminate_regs on DECL_RTL; any ADDRESSOFs in the actual insns are
@@ -3180,6 +2990,12 @@ eliminate_regs (x, mem_mode, insn)
       else
        return x;
 
+    case USE:
+    case CLOBBER:
+    case ASM_OPERANDS:
+    case SET:
+      abort ();
+
     default:
       break;
     }
@@ -3233,6 +3049,230 @@ eliminate_regs (x, mem_mode, insn)
 
   return x;
 }
+
+/* Scan rtx X for modifications of elimination target registers.  Update
+   the table of eliminables to reflect the changed state.  MEM_MODE is
+   the mode of an enclosing MEM rtx, or VOIDmode if not within a MEM.  */
+
+static void
+elimination_effects (x, mem_mode)
+     rtx x;
+     enum machine_mode mem_mode;
+
+{
+  enum rtx_code code = GET_CODE (x);
+  struct elim_table *ep;
+  int regno;
+  int i, j;
+  const char *fmt;
+
+  switch (code)
+    {
+    case CONST_INT:
+    case CONST_DOUBLE:
+    case CONST:
+    case SYMBOL_REF:
+    case CODE_LABEL:
+    case PC:
+    case CC0:
+    case ASM_INPUT:
+    case ADDR_VEC:
+    case ADDR_DIFF_VEC:
+    case RETURN:
+      return;
+
+    case ADDRESSOF:
+      abort ();
+
+    case REG:
+      regno = REGNO (x);
+
+      /* First handle the case where we encounter a bare register that
+        is eliminable.  Replace it with a PLUS.  */
+      if (regno < FIRST_PSEUDO_REGISTER)
+       {
+         for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
+              ep++)
+           if (ep->from_rtx == x && ep->can_eliminate)
+             {
+               if (! mem_mode)
+                 ep->ref_outside_mem = 1;
+               return;
+             }
+
+       }
+      else if (reg_renumber[regno] < 0 && reg_equiv_constant
+              && reg_equiv_constant[regno]
+              && ! CONSTANT_P (reg_equiv_constant[regno]))
+       elimination_effects (reg_equiv_constant[regno], mem_mode);
+      return;
+
+    case PRE_INC:
+    case POST_INC:
+    case PRE_DEC:
+    case POST_DEC:
+      for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+       if (ep->to_rtx == XEXP (x, 0))
+         {
+           int size = GET_MODE_SIZE (mem_mode);
+
+           /* If more bytes than MEM_MODE are pushed, account for them.  */
+#ifdef PUSH_ROUNDING
+           if (ep->to_rtx == stack_pointer_rtx)
+             size = PUSH_ROUNDING (size);
+#endif
+           if (code == PRE_DEC || code == POST_DEC)
+             ep->offset += size;
+           else
+             ep->offset -= size;
+         }
+
+      /* Fall through to generic unary operation case.  */
+    case STRICT_LOW_PART:
+    case NEG:          case NOT:
+    case SIGN_EXTEND:  case ZERO_EXTEND:
+    case TRUNCATE:     case FLOAT_EXTEND: case FLOAT_TRUNCATE:
+    case FLOAT:        case FIX:
+    case UNSIGNED_FIX: case UNSIGNED_FLOAT:
+    case ABS:
+    case SQRT:
+    case FFS:
+      elimination_effects (XEXP (x, 0), mem_mode);
+      return;
+
+    case SUBREG:
+      if (GET_CODE (SUBREG_REG (x)) == REG
+         && (GET_MODE_SIZE (GET_MODE (x))
+             <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))
+         && reg_equiv_memory_loc != 0
+         && reg_equiv_memory_loc[REGNO (SUBREG_REG (x))] != 0)
+       return;
+
+      elimination_effects (SUBREG_REG (x), mem_mode);
+      return;
+
+    case USE:
+      /* If using a register that is the source of an eliminate we still
+        think can be performed, note it cannot be performed since we don't
+        know how this register is used.  */
+      for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+       if (ep->from_rtx == XEXP (x, 0))
+         ep->can_eliminate = 0;
+
+      elimination_effects (XEXP (x, 0), mem_mode);
+      return;
+
+    case CLOBBER:
+      /* If clobbering a register that is the replacement register for an
+        elimination we still think can be performed, note that it cannot
+        be performed.  Otherwise, we need not be concerned about it.  */
+      for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+       if (ep->to_rtx == XEXP (x, 0))
+         ep->can_eliminate = 0;
+
+      elimination_effects (XEXP (x, 0), mem_mode);
+      return;
+
+    case SET:
+      /* Check for setting a register that we know about.  */
+      if (GET_CODE (SET_DEST (x)) == REG)
+       {
+         /* See if this is setting the replacement register for an
+            elimination.
+
+            If DEST is the hard frame pointer, we do nothing because we
+            assume that all assignments to the frame pointer are for
+            non-local gotos and are being done at a time when they are valid
+            and do not disturb anything else.  Some machines want to
+            eliminate a fake argument pointer (or even a fake frame pointer)
+            with either the real frame or the stack pointer.  Assignments to
+            the hard frame pointer must not prevent this elimination.  */
+
+         for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
+              ep++)
+           if (ep->to_rtx == SET_DEST (x)
+               && SET_DEST (x) != hard_frame_pointer_rtx)
+             {
+               /* If it is being incremented, adjust the offset.  Otherwise,
+                  this elimination can't be done.  */
+               rtx src = SET_SRC (x);
+
+               if (GET_CODE (src) == PLUS
+                   && XEXP (src, 0) == SET_DEST (x)
+                   && GET_CODE (XEXP (src, 1)) == CONST_INT)
+                 ep->offset -= INTVAL (XEXP (src, 1));
+               else
+                 ep->can_eliminate = 0;
+             }
+       }
+
+      elimination_effects (SET_DEST (x), 0);
+      elimination_effects (SET_SRC (x), 0);
+      return;
+
+    case MEM:
+      if (GET_CODE (XEXP (x, 0)) == ADDRESSOF)
+       abort ();
+
+      /* Our only special processing is to pass the mode of the MEM to our
+        recursive call.  */
+      elimination_effects (XEXP (x, 0), GET_MODE (x));
+      return;
+
+    default:
+      break;
+    }
+
+  fmt = GET_RTX_FORMAT (code);
+  for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
+    {
+      if (*fmt == 'e')
+       elimination_effects (XEXP (x, i), mem_mode);
+      else if (*fmt == 'E')
+       for (j = 0; j < XVECLEN (x, i); j++)
+         elimination_effects (XVECEXP (x, i, j), mem_mode);
+    }
+}
+
+/* Descend through rtx X and verify that no references to eliminable registers
+   remain.  If any do remain, mark the involved register as not
+   eliminable.  */
+static void
+check_eliminable_occurrences (x)
+     rtx x;
+{
+  const char *fmt;
+  int i;
+  enum rtx_code code;
+
+  if (x == 0)
+    return;
+  
+  code = GET_CODE (x);
+
+  if (code == REG && REGNO (x) < FIRST_PSEUDO_REGISTER)
+    {
+      struct elim_table *ep;
+
+      for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS]; ep++)
+       if (ep->from_rtx == x && ep->can_eliminate)
+         ep->can_eliminate = 0;
+      return;
+    }
+  
+  fmt = GET_RTX_FORMAT (code);
+  for (i = 0; i < GET_RTX_LENGTH (code); i++, fmt++)
+    {
+      if (*fmt == 'e')
+       check_eliminable_occurrences (XEXP (x, i));
+      else if (*fmt == 'E')
+       {
+         int j;
+         for (j = 0; j < XVECLEN (x, i); j++)
+           check_eliminable_occurrences (XVECEXP (x, i, j));
+       }
+    }
+}
 \f
 /* Scan INSN and eliminate all eliminable registers in it.
 
@@ -3252,12 +3292,28 @@ eliminate_regs_in_insn (insn, replace)
      rtx insn;
      int replace;
 {
+  int icode = recog_memoized (insn);
   rtx old_body = PATTERN (insn);
+  int insn_is_asm = asm_noperands (old_body) >= 0;
   rtx old_set = single_set (insn);
   rtx new_body;
   int val = 0;
+  int i, any_changes;
+  rtx substed_operand[MAX_RECOG_OPERANDS];
+  rtx orig_operand[MAX_RECOG_OPERANDS];
   struct elim_table *ep;
 
+  if (! insn_is_asm && icode < 0)
+    {
+      if (GET_CODE (PATTERN (insn)) == USE
+         || GET_CODE (PATTERN (insn)) == CLOBBER
+         || GET_CODE (PATTERN (insn)) == ADDR_VEC
+         || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
+         || GET_CODE (PATTERN (insn)) == ASM_INPUT)
+       return 0;
+      abort ();
+    }
+
   if (! replace)
     push_obstacks (&reload_obstack, &reload_obstack);
 
@@ -3385,35 +3441,97 @@ eliminate_regs_in_insn (insn, replace)
            }
     }
 
-  old_asm_operands_vec = 0;
+  /* Determine the effects of this insn on elimination offsets.  */
+  elimination_effects (old_body, 0);
 
-  /* Replace the body of this insn with a substituted form.  If we changed
-     something, return non-zero.
+  /* Eliminate all eliminable registers occurring in operands that
+     can be handled by reload.  */
+  extract_insn (insn);
+  any_changes = 0;
+  for (i = 0; i < recog_data.n_operands; i++)
+    {
+      orig_operand[i] = recog_data.operand[i];
+      substed_operand[i] = recog_data.operand[i];
 
-     If we are replacing a body that was a (set X (plus Y Z)), try to
+      /* For an asm statement, every operand is eliminable.  */
+      if (insn_is_asm || insn_data[icode].operand[i].eliminable)
+       {
+         /* Check for setting a register that we know about.  */
+         if (recog_data.operand_type[i] != OP_IN
+             && GET_CODE (orig_operand[i]) == REG)
+           {
+             /* If we are assigning to a register that can be eliminated, it
+                must be as part of a PARALLEL, since the code above handles
+                single SETs.  We must indicate that we can no longer
+                eliminate this reg.  */
+             for (ep = reg_eliminate; ep < &reg_eliminate[NUM_ELIMINABLE_REGS];
+                  ep++)
+               if (ep->from_rtx == orig_operand[i] && ep->can_eliminate)
+                 ep->can_eliminate = 0;
+           }
+
+         substed_operand[i] = eliminate_regs (recog_data.operand[i], 0,
+                                              replace ? insn : NULL_RTX);
+         if (substed_operand[i] != orig_operand[i])
+           val = any_changes = 1;
+         /* Terminate the search in check_eliminable_occurrences at
+            this point.  */
+         *recog_data.operand_loc[i] = 0;
+
+       /* If an output operand changed from a REG to a MEM and INSN is an
+          insn, write a CLOBBER insn.  */
+         if (recog_data.operand_type[i] != OP_IN
+             && GET_CODE (orig_operand[i]) == REG
+             && GET_CODE (substed_operand[i]) == MEM
+             && replace)
+           emit_insn_after (gen_rtx_CLOBBER (VOIDmode, orig_operand[i]),
+                            insn);
+       }
+    }
+
+  for (i = 0; i < recog_data.n_dups; i++)
+    *recog_data.dup_loc[i]
+       = *recog_data.operand_loc[(int)recog_data.dup_num[i]];
+
+  /* If any eliminable remain, they aren't eliminable anymore.  */
+  check_eliminable_occurrences (old_body);
+
+  /* Substitute the operands; the new values are in the substed_operand
+     array.  */
+  for (i = 0; i < recog_data.n_operands; i++)
+    *recog_data.operand_loc[i] = substed_operand[i];
+  for (i = 0; i < recog_data.n_dups; i++)
+    *recog_data.dup_loc[i] = substed_operand[(int)recog_data.dup_num[i]];
+
+  /* If we are replacing a body that was a (set X (plus Y Z)), try to
      re-recognize the insn.  We do this in case we had a simple addition
      but now can do this as a load-address.  This saves an insn in this
-     common case.  */
+     common case.
+     If re-recognition fails, the old insn code number will still be used,
+     and some register operands may have changed into PLUS expressions.
+     These will be handled by find_reloads by loading them into a register
+     again.*/
 
-  new_body = eliminate_regs (old_body, 0, replace ? insn : NULL_RTX);
-  if (new_body != old_body)
+  if (val)
     {
       /* If we aren't replacing things permanently and we changed something,
         make another copy to ensure that all the RTL is new.  Otherwise
         things can go wrong if find_reload swaps commutative operands
         and one is inside RTL that has been copied while the other is not.  */
-
-      /* Don't copy an asm_operands because (1) there's no need and (2)
-        copy_rtx can't do it properly when there are multiple outputs.  */
-      if (! replace && asm_noperands (old_body) < 0)
-       new_body = copy_rtx (new_body);
+      new_body = old_body;
+      if (! replace)
+       new_body = copy_insn (old_body);
+      PATTERN (insn) = new_body;
 
       /* If we had a move insn but now we don't, rerecognize it.  This will
         cause spurious re-recognition if the old move had a PARALLEL since
         the new one still will, but we can't call single_set without
         having put NEW_BODY into the insn and the re-recognition won't
         hurt in this rare case.  */
-      if (old_set != 0
+      /* ??? Why this huge if statement - why don't we just rerecognize the
+        thing always?  */
+      if (! insn_is_asm
+         && old_set != 0
          && ((GET_CODE (SET_SRC (old_set)) == REG
               && (GET_CODE (new_body) != SET
                   || GET_CODE (SET_SRC (new_body)) != REG))
@@ -3428,19 +3546,27 @@ eliminate_regs_in_insn (insn, replace)
              /* If this was an add insn before, rerecognize.  */
              || GET_CODE (SET_SRC (old_set)) == PLUS))
        {
-         if (! validate_change (insn, &PATTERN (insn), new_body, 0))
-           /* If recognition fails, store the new body anyway.
-              It's normal to have recognition failures here
-              due to bizarre memory addresses; reloading will fix them.  */
-           PATTERN (insn) = new_body;
+         int new_icode = recog (PATTERN (insn), insn, 0);
+         if (new_icode < 0)
+           INSN_CODE (insn) = icode;
        }
-      else
-       PATTERN (insn) = new_body;
+    }
 
-      val = 1;
+  /* Restore the old body.  If there were any changes to it, we made a copy
+     of it while the changes were still in place, so we'll correctly return
+     a modified insn below.  */
+  if (! replace)
+    {
+      /* Restore the old body.  */
+      for (i = 0; i < recog_data.n_operands; i++)
+       *recog_data.operand_loc[i] = orig_operand[i];
+      for (i = 0; i < recog_data.n_dups; i++)
+       *recog_data.dup_loc[i] = orig_operand[(int)recog_data.dup_num[i]];
     }
 
-  /* Loop through all elimination pairs.  See if any have changed.
+  /* Update all elimination pairs to reflect the status after the current
+     insn.  The changes we make were determined by the earlier call to
+     elimination_effects.
 
      We also detect a cases where register elimination cannot be done,
      namely, if a register would be both changed and referenced outside a MEM