Fix register elimination problem

From-SVN: r30134
This commit is contained in:
Bernd Schmidt 1999-10-22 22:02:17 +00:00 committed by Bernd Schmidt
parent b8c3c4f014
commit dfac187e9f
4 changed files with 383 additions and 221 deletions

View 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,

View 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;
}

View 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

View 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)
}
}
/* 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;
}
/* Loop through all elimination pairs. See if any have changed.
/* 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]];
}
/* 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