+
+/* 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 < ®_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 < ®_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 < ®_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 < ®_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 < ®_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 < ®_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));
+ }
+ }
+}