diff options
author | Bernd Schmidt <bernds@cygnus.co.uk> | 1999-10-22 22:02:17 +0000 |
---|---|---|
committer | Bernd Schmidt <crux@gcc.gnu.org> | 1999-10-22 22:02:17 +0000 |
commit | dfac187e9f168cad1dbc73ebe9757589c6bebb58 (patch) | |
tree | 05a9402e85f3a88d76359f09b6da2679f852994e /gcc | |
parent | b8c3c4f0146f82c900a995c8e98b83a2147115fd (diff) | |
download | gcc-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/ChangeLog | 21 | ||||
-rw-r--r-- | gcc/genoutput.c | 15 | ||||
-rw-r--r-- | gcc/recog.h | 2 | ||||
-rw-r--r-- | gcc/reload1.c | 564 |
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 < ®_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 < ®_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 < ®_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 < ®_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 < ®_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 < ®_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 < ®_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)); + } + } +} /* 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 < ®_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 |