diff options
author | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2001-05-17 15:00:35 +0000 |
---|---|---|
committer | hubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4> | 2001-05-17 15:00:35 +0000 |
commit | 64ab453f74ec150306b85eaf8900eed607f8829e (patch) | |
tree | 83f417361746b3974c92d8656cd3a83d068dd204 /gcc/simplify-rtx.c | |
parent | 226e9ef3afbb438f448d6e42f40aa6b3ef410c49 (diff) | |
download | gcc-64ab453f74ec150306b85eaf8900eed607f8829e.tar.gz |
* 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.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@42199 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/simplify-rtx.c')
-rw-r--r-- | gcc/simplify-rtx.c | 194 |
1 files changed, 194 insertions, 0 deletions
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 |