This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Register elimination problem + fix


There's a problem with register elimination.  When eliminating a hard reg
to a PLUS of another hard reg and a constant, the resulting rtl is changed
to be in canonical form.  This can change the pattern of the insn so that
it is no longer recognized.

One instance of the problem occurs with the arm port, building newlib.
We have an insn that matches the following pattern before reload:
(define_insn "*arith_shiftsi"
  [(set (match_operand:SI 0 "s_register_operand" "=r")
        (match_operator:SI 1 "shiftable_operator"
          [(match_operator:SI 3 "shift_operator"
             [(match_operand:SI 4 "s_register_operand" "r")
              (match_operand:SI 5 "reg_or_int_operand" "rI")])
           (match_operand:SI 2 "s_register_operand" "r")]))]
  ""
  "%i1%?\\t%0, %2, %4%S3")

Operator 1 is a plus, operator 3 is a mult.  The set source looks like this:

  (plus:SI (mult:SI (reg/v:SI 8 r8)
		    (const_int 8 [0x8]))
	   (reg:SI 25 sfp))

Register elimination processes the plus and eliminates in the two suboperands.
The first one is unchanged, the second one gets eliminated to

  (plus:SI (reg:SI 11 fp)
      (const_int -40 [0xffffffd8]))

Then, when forming a sum of the two, we end up with

(plus:SI (plus:SI (mult:SI ...) (reg:SI 11))
         (const_int -40))

which no longer matches the pattern.

There are other examples of this; Richard Henderson recently added new
patterns to alpha.md that match results of register elimination.  Also,
there was a bug report recently for the arm port (involving the
minmax_arithsi pattern) which was a very similar case.

To fix the problem, I changed the way register elimination works.  With
the patch below, only parts of the insn that correspond to a MATCH_OPERAND
are eliminated, anything corresponding to a MATCH_OPERATOR (or a fixed piece
of rtl not found in any operand at all) is left unchanged.  This ensures
that register elimination does not change the structure of the insn in a
way that reload can't handle.  After doing eliminations this way, another
pass is made over the whole insn to ensure no references to eliminable regs
are left in the processed insn; if there are any, the elimination can't be
done.

This can possibly lose an optimization; in some cases it may be desirable to
change the structure of the insn.  If you have an addsi insn and one of the
operands is eliminated, the result may be valid as load-address insn that
does not require reloads (unlike the addsi insn, which will need a reload if
one operand got turned into a PLUS).  I can see basically two ways to get
around this loss of optimization: put load-address patterns before addsi in
the machine description so that it gets matched before register elimination
(in that case, the patch below will still do the canonicalization for the
PLUS expression since it's now an operand), or write peepholes.

Bernd

	* 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.

	
Index: genoutput.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/genoutput.c,v
retrieving revision 1.38
diff -u -p -r1.38 genoutput.c
--- genoutput.c	1999/10/14 03:43:48	1.38
+++ genoutput.c	1999/10/17 15:21:01
@@ -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;
 };
 
@@ -286,8 +291,10 @@ output_operand_data ()
       printf ("    \"%s\",\n", d->constraint ? d->constraint : "");
 
       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, 
 	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, 
 	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, 
       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;
@@ -551,6 +561,9 @@ compare_operands (d0, d1)
     return 0;
 
   if (d0->strict_low != d1->strict_low)
+    return 0;
+
+  if (d0->eliminable != d1->eliminable)
     return 0;
 
   return 1;
Index: recog.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/recog.h,v
retrieving revision 1.25
diff -u -p -r1.25 recog.h
--- recog.h	1999/10/15 01:52:29	1.25
+++ recog.h	1999/10/17 15:21:01
@@ -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: reload1.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/reload1.c,v
retrieving revision 1.171
diff -u -p -r1.171 reload1.c
--- reload1.c	1999/10/17 06:47:27	1.171
+++ reload1.c	1999/10/17 15:21:21
@@ -394,6 +394,8 @@ static void maybe_mark_pseudo_spilled	PR
 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)
     }
 }
 
-/* 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_operand
    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));
+	}
+    }
+}
 
 /* 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);
 
@@ -3384,36 +3440,98 @@ eliminate_regs_in_insn (insn, replace)
 	      break;
 	    }
     }
+
+  /* Determine the effects of this insn on elimination offsets.  */
+  elimination_effects (old_body, 0);
 
-  old_asm_operands_vec = 0;
+  /* 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];
+
+      /* 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);
+	}
+    }
 
-  /* Replace the body of this insn with a substituted form.  If we changed
-     something, return non-zero.
+  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
+  /* 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.  */
+      new_body = old_body;
+      if (! replace)
+	new_body = copy_insn (old_body);
+      PATTERN (insn) = new_body;
 
-      /* 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);
-
       /* 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



Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]