aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorBernd Schmidt <bernds@cygnus.co.uk>1999-10-22 22:02:17 +0000
committerBernd Schmidt <crux@gcc.gnu.org>1999-10-22 22:02:17 +0000
commitdfac187e9f168cad1dbc73ebe9757589c6bebb58 (patch)
tree05a9402e85f3a88d76359f09b6da2679f852994e /gcc
parentb8c3c4f0146f82c900a995c8e98b83a2147115fd (diff)
downloadgcc-dfac187e9f168cad1dbc73ebe9757589c6bebb58.zip
gcc-dfac187e9f168cad1dbc73ebe9757589c6bebb58.tar.gz
gcc-dfac187e9f168cad1dbc73ebe9757589c6bebb58.tar.bz2
Fix register elimination problem
From-SVN: r30134
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog21
-rw-r--r--gcc/genoutput.c15
-rw-r--r--gcc/recog.h2
-rw-r--r--gcc/reload1.c564
4 files changed, 382 insertions, 220 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index b21a883..128dadc 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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,
diff --git a/gcc/genoutput.c b/gcc/genoutput.c
index 4cb9c00..bc0ac45 100644
--- a/gcc/genoutput.c
+++ b/gcc/genoutput.c
@@ -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;
}
diff --git a/gcc/recog.h b/gcc/recog.h
index b84758c..37c9c4f 100644
--- a/gcc/recog.h
+++ b/gcc/recog.h
@@ -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
diff --git a/gcc/reload1.c b/gcc/reload1.c
index f4036d0..cad1221 100644
--- a/gcc/reload1.c
+++ b/gcc/reload1.c
@@ -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)
}
}
-/* 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));
+ }
+ }
+}
/* 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