From eea50aa0ab1e891b39116177f7f6eaded47ef176 Mon Sep 17 00:00:00 2001 From: Jan Hubicka Date: Thu, 17 May 2001 17:00:35 +0200 Subject: [PATCH] simplify-rtx.c (simplify_subreg): Break out from ... * simplify-rtx.c (simplify_subreg): Break out from ... * combine.c (combine_splify_rtx) ... here and ... * recog.c (validate_replace_rtx_1): ... here; * rtl.h (subreg_lowpart_parts_p, simplify_subreg): Declare. * emit-rtl.c (subreg_lowpart_parts_p): Break out from ... (subreg_lowpart_p): ... here. From-SVN: r42199 --- gcc/ChangeLog | 9 +++ gcc/combine.c | 162 +++---------------------------------- gcc/emit-rtl.c | 32 +++++--- gcc/recog.c | 151 ++++------------------------------- gcc/rtl.h | 7 ++ gcc/simplify-rtx.c | 194 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 260 insertions(+), 295 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 15b445d205f..d74a923d201 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +Thu May 17 16:59:41 CEST 2001 Jan Hubicka + + * simplify-rtx.c (simplify_subreg): Break out from ... + * combine.c (combine_splify_rtx) ... here and ... + * recog.c (validate_replace_rtx_1): ... here; + * rtl.h (subreg_lowpart_parts_p, simplify_subreg): Declare. + * emit-rtl.c (subreg_lowpart_parts_p): Break out from ... + (subreg_lowpart_p): ... here. + 2001-05-17 Bernd Schmidt * stmt.c (expand_asm_operands): For inout operands, make sure diff --git a/gcc/combine.c b/gcc/combine.c index a60f92e6eec..53df2f58c86 100644 --- a/gcc/combine.c +++ b/gcc/combine.c @@ -3765,161 +3765,21 @@ combine_simplify_rtx (x, op0_mode, last, in_dest) break; case SUBREG: - /* (subreg:A (mem:B X) N) becomes a modified MEM unless the SUBREG - is paradoxical. If we can't do that safely, then it becomes - something nonsensical so that this combination won't take place. */ + if (op0_mode == VOIDmode) + op0_mode = GET_MODE (SUBREG_REG (x)); - if (GET_CODE (SUBREG_REG (x)) == MEM - && (GET_MODE_SIZE (mode) - <= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))))) - { - rtx inner = SUBREG_REG (x); - int offset = SUBREG_BYTE (x); - /* Don't change the mode of the MEM - if that would change the meaning of the address. */ - if (MEM_VOLATILE_P (SUBREG_REG (x)) - || mode_dependent_address_p (XEXP (inner, 0))) - return gen_rtx_CLOBBER (mode, const0_rtx); - - /* Note if the plus_constant doesn't make a valid address - then this combination won't be accepted. */ - x = gen_rtx_MEM (mode, - plus_constant (XEXP (inner, 0), offset)); - MEM_COPY_ATTRIBUTES (x, inner); - return x; - } - - /* If we are in a SET_DEST, these other cases can't apply. */ - if (in_dest) - return x; - - /* Changing mode twice with SUBREG => just change it once, - or not at all if changing back to starting mode. */ - if (GET_CODE (SUBREG_REG (x)) == SUBREG) - { - int final_offset; - enum machine_mode outer_mode, inner_mode; - - /* If the innermost mode is the same as the goal mode, - and the low word is being referenced in both SUBREGs, - return the innermost element. */ - if (mode == GET_MODE (SUBREG_REG (SUBREG_REG (x)))) - { - int inner_word = SUBREG_BYTE (SUBREG_REG (x)); - int outer_word = SUBREG_BYTE (x); - - inner_word = (inner_word / UNITS_PER_WORD) * UNITS_PER_WORD; - outer_word = (outer_word / UNITS_PER_WORD) * UNITS_PER_WORD; - if (inner_word == 0 - && outer_word == 0) - return SUBREG_REG (SUBREG_REG (x)); - } - - outer_mode = GET_MODE (SUBREG_REG (x)); - inner_mode = GET_MODE (SUBREG_REG (SUBREG_REG (x))); - final_offset = SUBREG_BYTE (x) + SUBREG_BYTE (SUBREG_REG(x)); - - if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN) - && GET_MODE_SIZE (outer_mode) > GET_MODE_SIZE (mode) - && GET_MODE_SIZE (outer_mode) > GET_MODE_SIZE (inner_mode)) - { - /* Inner SUBREG is paradoxical, outer is not. On big endian - we have to special case this. */ - if (SUBREG_BYTE (SUBREG_REG (x))) - abort(); /* Can a paradoxical subreg have nonzero offset? */ - if (WORDS_BIG_ENDIAN && BYTES_BIG_ENDIAN) - final_offset = SUBREG_BYTE (x) - GET_MODE_SIZE (outer_mode) - + GET_MODE_SIZE (inner_mode); - else if (WORDS_BIG_ENDIAN) - final_offset = (final_offset % UNITS_PER_WORD) - + ((SUBREG_BYTE (x) - GET_MODE_SIZE (outer_mode) - + GET_MODE_SIZE (inner_mode)) - * UNITS_PER_WORD) / UNITS_PER_WORD; - else - final_offset = ((final_offset * UNITS_PER_WORD) - / UNITS_PER_WORD) - + ((SUBREG_BYTE (x) - GET_MODE_SIZE (outer_mode) - + GET_MODE_SIZE (inner_mode)) - % UNITS_PER_WORD); - } - - /* The SUBREG rules are that the byte offset must be - some multiple of the toplevel SUBREG's mode. */ - final_offset = (final_offset / GET_MODE_SIZE (mode)); - final_offset = (final_offset * GET_MODE_SIZE (mode)); - - SUBST_INT (SUBREG_BYTE (x), final_offset); - SUBST (SUBREG_REG (x), SUBREG_REG (SUBREG_REG (x))); - } - - /* SUBREG of a hard register => just change the register number - and/or mode. If the hard register is not valid in that mode, - suppress this combination. If the hard register is the stack, - frame, or argument pointer, leave this as a SUBREG. */ - - if (GET_CODE (SUBREG_REG (x)) == REG - && REGNO (SUBREG_REG (x)) < FIRST_PSEUDO_REGISTER - && REGNO (SUBREG_REG (x)) != FRAME_POINTER_REGNUM -#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM - && REGNO (SUBREG_REG (x)) != HARD_FRAME_POINTER_REGNUM -#endif -#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM - && REGNO (SUBREG_REG (x)) != ARG_POINTER_REGNUM -#endif - && REGNO (SUBREG_REG (x)) != STACK_POINTER_REGNUM) - { - int final_regno = subreg_hard_regno (x, 0); - - if (HARD_REGNO_MODE_OK (final_regno, mode)) - return gen_rtx_REG (mode, final_regno); - else - return gen_rtx_CLOBBER (mode, const0_rtx); - } - - /* For a constant, try to pick up the part we want. Handle a full - word and low-order part. Only do this if we are narrowing - the constant; if it is being widened, we have no idea what - the extra bits will have been set to. */ - - if (CONSTANT_P (SUBREG_REG (x)) && op0_mode != VOIDmode - && GET_MODE_SIZE (mode) == UNITS_PER_WORD - && GET_MODE_SIZE (op0_mode) > UNITS_PER_WORD - && GET_MODE_CLASS (mode) == MODE_INT) - { - temp = operand_subword (SUBREG_REG (x), - (SUBREG_BYTE (x) / UNITS_PER_WORD), - 0, op0_mode); - if (temp) - return temp; - } - - /* If we want a subreg of a constant, at offset 0, - take the low bits. On a little-endian machine, that's - always valid. On a big-endian machine, it's valid - only if the constant's mode fits in one word. Note that we - cannot use subreg_lowpart_p since SUBREG_REG may be VOIDmode. */ + /* simplify_subreg can't use gen_lowpart_for_combine. */ if (CONSTANT_P (SUBREG_REG (x)) - && ((GET_MODE_SIZE (op0_mode) <= UNITS_PER_WORD - || ! WORDS_BIG_ENDIAN) - ? SUBREG_BYTE (x) == 0 - : (SUBREG_BYTE (x) - == (GET_MODE_SIZE (op0_mode) - GET_MODE_SIZE (mode)))) - && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (op0_mode) - && (! WORDS_BIG_ENDIAN - || GET_MODE_BITSIZE (op0_mode) <= BITS_PER_WORD)) + && subreg_lowpart_parts_p (mode, op0_mode, SUBREG_BYTE (x))) return gen_lowpart_for_combine (mode, SUBREG_REG (x)); - /* A paradoxical SUBREG of a VOIDmode constant is the same constant, - since we are saying that the high bits don't matter. */ - if (CONSTANT_P (SUBREG_REG (x)) && GET_MODE (SUBREG_REG (x)) == VOIDmode - && GET_MODE_SIZE (mode) > GET_MODE_SIZE (op0_mode)) - { - if (GET_MODE_SIZE (GET_MODE (SUBREG_REG (x))) > UNITS_PER_WORD - && (WORDS_BIG_ENDIAN || SUBREG_BYTE (x) != 0)) - return constant_subword (SUBREG_REG (x), - SUBREG_BYTE (x) / UNITS_PER_WORD, mode); - return SUBREG_REG (x); - } + { + rtx temp; + temp = simplify_subreg (mode, SUBREG_REG (x), op0_mode, + SUBREG_BYTE (x)); + if (temp) + return temp; + } /* Note that we cannot do any narrowing for non-constants since we might have been counting on using the fact that some bits were diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index 3452a68f71f..b22e3467380 100644 --- a/gcc/emit-rtl.c +++ b/gcc/emit-rtl.c @@ -1278,6 +1278,27 @@ gen_highpart (mode, x) else abort (); } +/* Return 1 iff (SUBREG:outermode (OP:innermode) byte) + refers to the least significant part of its containing reg. */ + +int +subreg_lowpart_parts_p (outermode, innermode, byte) + enum machine_mode outermode, innermode; + unsigned int byte; +{ + unsigned int offset = 0; + int difference = (GET_MODE_SIZE (innermode) - GET_MODE_SIZE (outermode)); + + if (difference > 0) + { + if (WORDS_BIG_ENDIAN) + offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD; + if (BYTES_BIG_ENDIAN) + offset += difference % UNITS_PER_WORD; + } + + return byte == offset; +} /* Return 1 iff X, assumed to be a SUBREG, refers to the least significant part of its containing reg. @@ -1296,15 +1317,8 @@ subreg_lowpart_p (x) else if (GET_MODE (SUBREG_REG (x)) == VOIDmode) return 0; - if (difference > 0) - { - if (WORDS_BIG_ENDIAN) - offset += (difference / UNITS_PER_WORD) * UNITS_PER_WORD; - if (BYTES_BIG_ENDIAN) - offset += difference % UNITS_PER_WORD; - } - - return SUBREG_BYTE (x) == offset; + return subreg_lowpart_parts_p (GET_MODE (x), GET_MODE (SUBREG_REG (x)), + SUBREG_BYTE (x)); } diff --git a/gcc/recog.c b/gcc/recog.c index 86209fe773a..d858394c4cd 100644 --- a/gcc/recog.c +++ b/gcc/recog.c @@ -579,145 +579,26 @@ validate_replace_rtx_1 (loc, from, to, object) /* In case we are replacing by constant, attempt to simplify it to non-SUBREG expression. We can't do this later, since the information about inner mode may be lost. */ - if (CONSTANT_P (to) && rtx_equal_p (SUBREG_REG (x), from)) + if (rtx_equal_p (SUBREG_REG (x), from)) { - int offset, part; - unsigned HOST_WIDE_INT val; - - /* A paradoxical SUBREG of a VOIDmode constant is the same constant, - since we are saying that the high bits don't matter. */ - if (GET_MODE (to) == VOIDmode - && (GET_MODE_SIZE (GET_MODE (x)) - >= GET_MODE_SIZE (GET_MODE (from)))) + rtx temp; + temp = simplify_subreg (GET_MODE (x), to, GET_MODE (SUBREG_REG (x)), + SUBREG_BYTE (x)); + if (temp) { - rtx new = gen_lowpart_if_possible (GET_MODE (x), to); - if (new) - { - validate_change (object, loc, new, 1); - return; - } - } - - offset = SUBREG_BYTE (x) * BITS_PER_UNIT; - switch (GET_CODE (to)) - { - case CONST_DOUBLE: - if (GET_MODE (to) != VOIDmode) - break; - - part = offset >= HOST_BITS_PER_WIDE_INT; - if ((BITS_PER_WORD > HOST_BITS_PER_WIDE_INT - && BYTES_BIG_ENDIAN) - || (BITS_PER_WORD <= HOST_BITS_PER_WIDE_INT - && WORDS_BIG_ENDIAN)) - part = !part; - val = part ? CONST_DOUBLE_HIGH (to) : CONST_DOUBLE_LOW (to); - offset %= HOST_BITS_PER_WIDE_INT; - - /* FALLTHROUGH */ - case CONST_INT: - if (GET_CODE (to) == CONST_INT) - val = INTVAL (to); - - { - /* Avoid creating bogus SUBREGs */ - enum machine_mode mode = GET_MODE (x); - enum machine_mode inner_mode = GET_MODE (from); - - /* We've already picked the word we want from a double, so - pretend this is actually an integer. */ - if (GET_CODE (to) == CONST_DOUBLE) - inner_mode = SImode; - - if (GET_MODE_CLASS (mode) != MODE_INT) - { - /* Substitute in something that we know won't be - recognized. */ - to = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx); - validate_change (object, loc, to, 1); - return; - } - - if (BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN) - { - if (WORDS_BIG_ENDIAN) - offset = GET_MODE_BITSIZE (inner_mode) - - GET_MODE_BITSIZE (mode) - offset; - if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN - && GET_MODE_SIZE (mode) < UNITS_PER_WORD) - offset = offset + BITS_PER_WORD - GET_MODE_BITSIZE (mode) - - 2 * (offset % BITS_PER_WORD); - } - - if (offset >= HOST_BITS_PER_WIDE_INT) - to = ((HOST_WIDE_INT) val < 0) ? constm1_rtx : const0_rtx; - else - { - val >>= offset; - if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT) - val = trunc_int_for_mode (val, mode); - to = GEN_INT (val); - } - - validate_change (object, loc, to, 1); - return; - } - - default: - break; - } - } - - /* Changing mode twice with SUBREG => just change it once, - or not at all if changing back to starting mode. */ - if (GET_CODE (to) == SUBREG - && rtx_equal_p (SUBREG_REG (x), from)) - { - if (GET_MODE (x) == GET_MODE (SUBREG_REG (to)) - && SUBREG_BYTE (x) == 0 && SUBREG_BYTE (to) == 0) - { - validate_change (object, loc, SUBREG_REG (to), 1); + validate_change (object, loc, temp, 1); return; } - - /* Make sure the 2 byte counts added together are an even unit - of x's mode, and combine them if so. Otherwise we run - into problems with something like: - (subreg:HI (subreg:QI (SI:55) 3) 0) - we end up with an odd offset into a HI which is invalid. */ - - if (SUBREG_BYTE (to) % GET_MODE_SIZE (GET_MODE (x)) == 0) - validate_change (object, loc, - gen_rtx_SUBREG (GET_MODE (x), SUBREG_REG (to), - SUBREG_BYTE(x) + SUBREG_BYTE (to)), - 1); - else - validate_change (object, loc, to, 1); - - return; - } - - /* If we have a SUBREG of a register that we are replacing and we are - replacing it with a MEM, make a new MEM and try replacing the - SUBREG with it. Don't do this if the MEM has a mode-dependent address - or if we would be widening it. */ - - if (GET_CODE (from) == REG - && GET_CODE (to) == MEM - && rtx_equal_p (SUBREG_REG (x), from) - && ! mode_dependent_address_p (XEXP (to, 0)) - && ! MEM_VOLATILE_P (to) - && GET_MODE_SIZE (GET_MODE (x)) <= GET_MODE_SIZE (GET_MODE (to))) - { - int offset = SUBREG_BYTE (x); - enum machine_mode mode = GET_MODE (x); - rtx new; - - new = gen_rtx_MEM (mode, plus_constant (XEXP (to, 0), offset)); - MEM_COPY_ATTRIBUTES (new, to); - validate_change (object, loc, new, 1); - return; - } + /* Avoid creating of invalid SUBREGS. */ + if (GET_MODE (from) == VOIDmode) + { + /* Substitute in something that we know won't be + recognized. */ + to = gen_rtx_CLOBBER (GET_MODE (x), const0_rtx); + validate_change (object, loc, to, 1); + return; + } + } break; case ZERO_EXTRACT: diff --git a/gcc/rtl.h b/gcc/rtl.h index a9252105f5e..6aa86136b6f 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -1198,6 +1198,9 @@ extern rtx constant_subword PARAMS ((rtx, int, extern rtx operand_subword_force PARAMS ((rtx, unsigned int, enum machine_mode)); extern int subreg_lowpart_p PARAMS ((rtx)); +extern int subreg_lowpart_parts_p PARAMS ((enum machine_mode, + enum machine_mode, + unsigned int)); extern rtx make_safe_from PARAMS ((rtx, rtx)); extern rtx convert_memory_address PARAMS ((enum machine_mode, rtx)); extern rtx get_insns PARAMS ((void)); @@ -1324,6 +1327,10 @@ extern rtx simplify_gen_relational PARAMS ((enum rtx_code, enum machine_mode, enum machine_mode, rtx, rtx)); +extern rtx simplify_subreg PARAMS ((enum machine_mode, + rtx, + enum machine_mode, + unsigned int)); extern rtx simplify_replace_rtx PARAMS ((rtx, rtx, rtx)); extern rtx simplify_rtx PARAMS ((rtx)); diff --git a/gcc/simplify-rtx.c b/gcc/simplify-rtx.c index 046882002a5..c4dee23c9a9 100644 --- a/gcc/simplify-rtx.c +++ b/gcc/simplify-rtx.c @@ -2186,6 +2186,200 @@ simplify_ternary_operation (code, mode, op0_mode, op0, op1, op2) return 0; } +/* Simplify SUBREG:OUTERMODE(OP:INNERMODE, BYTE) + Return 0 if no simplifications is possible. */ +rtx +simplify_subreg (outermode, op, innermode, byte) + rtx op; + unsigned int byte; + enum machine_mode outermode, innermode; +{ + /* Little bit of sanity checking. */ + if (innermode == VOIDmode || outermode == VOIDmode + || innermode == BLKmode || outermode == BLKmode) + abort (); + + if (GET_MODE (op) != innermode + && GET_MODE (op) != VOIDmode) + abort (); + + if (byte % GET_MODE_SIZE (outermode) + || byte >= GET_MODE_SIZE (innermode)) + abort (); + + /* Attempt to simplify constant to non-SUBREG expression. */ + if (CONSTANT_P (op)) + { + int offset, part; + unsigned HOST_WIDE_INT val; + + /* ??? This code is partly redundant with code bellow, but can handle + the subregs of floats and similar corner cases. + Later it we should move all simplification code here and rewrite + GEN_LOWPART_IF_POSSIBLE, GEN_HIGHPART, OPERAND_SUBWORD and friends + using SIMPLIFY_SUBREG. */ + if (subreg_lowpart_parts_p (outermode, innermode, byte)) + { + rtx new = gen_lowpart_if_possible (outermode, op); + if (new) + return new; + } + + /* Similar comment as above apply here. */ + if (GET_MODE_SIZE (outermode) == UNITS_PER_WORD + && GET_MODE_SIZE (innermode) > UNITS_PER_WORD + && GET_MODE_CLASS (outermode) == MODE_INT) + { + rtx new = operand_subword (op, + (byte / UNITS_PER_WORD), + 0, innermode); + if (new) + return new; + } + + offset = byte * BITS_PER_UNIT; + switch (GET_CODE (op)) + { + case CONST_DOUBLE: + if (GET_MODE (op) != VOIDmode) + break; + + /* We can't handle this case yet. */ + if (GET_MODE_BITSIZE (outermode) >= HOST_BITS_PER_WIDE_INT) + return NULL; + + part = offset >= HOST_BITS_PER_WIDE_INT; + if ((BITS_PER_WORD > HOST_BITS_PER_WIDE_INT + && BYTES_BIG_ENDIAN) + || (BITS_PER_WORD <= HOST_BITS_PER_WIDE_INT + && WORDS_BIG_ENDIAN)) + part = !part; + val = part ? CONST_DOUBLE_HIGH (op) : CONST_DOUBLE_LOW (op); + offset %= HOST_BITS_PER_WIDE_INT; + + /* We've already picked the word we want from a double, so + pretend this is actually an integer. */ + innermode = mode_for_size (HOST_BITS_PER_WIDE_INT, MODE_INT, 0); + + /* FALLTHROUGH */ + case CONST_INT: + if (GET_CODE (op) == CONST_INT) + val = INTVAL (op); + + /* We don't handle synthetizing of non-integral constants yet. */ + if (GET_MODE_CLASS (outermode) != MODE_INT) + return NULL; + + if (BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN) + { + if (WORDS_BIG_ENDIAN) + offset = (GET_MODE_BITSIZE (innermode) + - GET_MODE_BITSIZE (outermode) - offset); + if (BYTES_BIG_ENDIAN != WORDS_BIG_ENDIAN + && GET_MODE_SIZE (outermode) < UNITS_PER_WORD) + offset = (offset + BITS_PER_WORD - GET_MODE_BITSIZE (outermode) + - 2 * (offset % BITS_PER_WORD)); + } + + if (offset >= HOST_BITS_PER_WIDE_INT) + return ((HOST_WIDE_INT) val < 0) ? constm1_rtx : const0_rtx; + else + { + val >>= offset; + if (GET_MODE_BITSIZE (outermode) < HOST_BITS_PER_WIDE_INT) + val = trunc_int_for_mode (val, outermode); + return GEN_INT (val); + } + default: + break; + } + } + + /* Changing mode twice with SUBREG => just change it once, + or not at all if changing back op starting mode. */ + if (GET_CODE (op) == SUBREG) + { + enum machine_mode innermostmode = GET_MODE (SUBREG_REG (op)); + unsigned int final_offset = byte + SUBREG_BYTE (op); + rtx new; + + if (outermode == innermostmode + && byte == 0 && SUBREG_BYTE (op) == 0) + return SUBREG_REG (op); + + if ((WORDS_BIG_ENDIAN || BYTES_BIG_ENDIAN) + && GET_MODE_SIZE (innermode) > GET_MODE_SIZE (outermode) + && GET_MODE_SIZE (innermode) > GET_MODE_SIZE (innermostmode)) + { + /* Inner SUBREG is paradoxical, outer is not. On big endian + we have to special case this. */ + if (SUBREG_BYTE (op)) + abort(); /* Can a paradoxical subreg have nonzero offset? */ + if (WORDS_BIG_ENDIAN && BYTES_BIG_ENDIAN) + final_offset = (byte - GET_MODE_SIZE (innermode) + + GET_MODE_SIZE (innermostmode)); + else if (WORDS_BIG_ENDIAN) + final_offset = ((final_offset % UNITS_PER_WORD) + + ((byte - GET_MODE_SIZE (innermode) + + GET_MODE_SIZE (innermostmode)) + * UNITS_PER_WORD) / UNITS_PER_WORD); + else + final_offset = (((final_offset * UNITS_PER_WORD) + / UNITS_PER_WORD) + + ((byte - GET_MODE_SIZE (innermode) + + GET_MODE_SIZE (innermostmode)) + % UNITS_PER_WORD)); + } + + /* Recurse for futher possible simplifications. */ + new = simplify_subreg (outermode, op, GET_MODE (op), + final_offset); + if (new) + return new; + return gen_rtx_SUBREG (outermode, op, final_offset); + } + + /* SUBREG of a hard register => just change the register number + and/or mode. If the hard register is not valid in that mode, + suppress this simplification. If the hard register is the stack, + frame, or argument pointer, leave this as a SUBREG. */ + + if (REG_P (op) == REG + && REGNO (op) < FIRST_PSEUDO_REGISTER + && REGNO (op) != FRAME_POINTER_REGNUM +#if HARD_FRAME_POINTER_REGNUM != FRAME_POINTER_REGNUM + && REGNO (op) != HARD_FRAME_POINTER_REGNUM +#endif +#if FRAME_POINTER_REGNUM != ARG_POINTER_REGNUM + && REGNO (op) != ARG_POINTER_REGNUM +#endif + && REGNO (op) != STACK_POINTER_REGNUM) + { + int final_regno = subreg_hard_regno (gen_rtx_SUBREG (outermode, op, byte), + 0); + + if (HARD_REGNO_MODE_OK (final_regno, outermode)) + return gen_rtx_REG (outermode, final_regno); + } + + /* If we have a SUBREG of a register that we are replacing and we are + replacing it with a MEM, make a new MEM and try replacing the + SUBREG with it. Don't do this if the MEM has a mode-dependent address + or if we would be widening it. */ + + if (GET_CODE (op) == MEM + && ! mode_dependent_address_p (XEXP (op, 0)) + && ! MEM_VOLATILE_P (op) + && GET_MODE_SIZE (outermode) <= GET_MODE_SIZE (GET_MODE (op))) + { + rtx new; + + new = gen_rtx_MEM (outermode, plus_constant (XEXP (op, 0), byte)); + MEM_COPY_ATTRIBUTES (new, op); + return new; + } + return NULL_RTX; +} /* Simplify X, an rtx expression. Return the simplified expression or NULL if no simplifications