This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: New function: simplify_subreg
- To: Richard Kenner <kenner at vlsi1 dot ultra dot nyu dot edu>
- Subject: Re: New function: simplify_subreg
- From: Jan Hubicka <jh at suse dot cz>
- Date: Tue, 15 May 2001 19:46:35 +0200
- Cc: jh at suse dot cz, gcc-patches at gcc dot gnu dot org
- References: <10105151720.AA21747@vlsi1.ultra.nyu.edu>
> So I was trying to avoid calling simplify_subreg in dest for more cases
> than the simplification was done previously, as simplify_subreg is designed
> to simplify src expressions.
>
> Ok, then I'd say leave it that way, but make what's going on clear from
> comments.
OK, I've updated both coments. Here is updated patch:
Wed May 2 17:12:49 CEST 2001 Jan Hubicka <jh@suse.cz>
* 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.
Index: combine.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/combine.c,v
retrieving revision 1.198
diff -c -3 -p -r1.198 combine.c
*** combine.c 2001/05/11 16:15:49 1.198
--- combine.c 2001/05/15 17:44:03
*************** combine_simplify_rtx (x, op0_mode, last,
*** 3764,3924 ****
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 (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. */
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))
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);
! }
/* Note that we cannot do any narrowing for non-constants since
we might have been counting on using the fact that some bits were
--- 3766,3805 ----
break;
case SUBREG:
! if (op0_mode == VOIDmode)
! op0_mode = GET_MODE (SUBREG_REG (x));
+ /* simplify_subreg may have unwanted effects on subregs in the
+ destination, so do allowed optimizations explicitly.
+ We only need to convert (subreg:A (mem:B X) N) to MEM
+ unless the SUBREG is paradoxical. */
if (GET_CODE (SUBREG_REG (x)) == MEM
&& (GET_MODE_SIZE (mode)
<= GET_MODE_SIZE (GET_MODE (SUBREG_REG (x)))))
{
! rtx temp;
! temp = simplify_subreg (mode, SUBREG_REG (x), op0_mode,
! SUBREG_BYTE (x));
! if (temp)
! return temp;
}
/* If we are in a SET_DEST, these other cases can't apply. */
if (in_dest)
return x;
! /* An simplify_subreg can't use gen_lowpart_for_combine. */
if (CONSTANT_P (SUBREG_REG (x))
! && subreg_lowpart_parts_p (mode, op0_mode, SUBREG_BYTE (x)))
return gen_lowpart_for_combine (mode, 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
Index: emit-rtl.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/emit-rtl.c,v
retrieving revision 1.173
diff -c -3 -p -r1.173 emit-rtl.c
*** emit-rtl.c 2001/04/30 16:07:25 1.173
--- emit-rtl.c 2001/05/02 15:23:41
*************** gen_highpart (mode, x)
*** 1278,1284 ****
--- 1278,1305 ----
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.
If X is not a SUBREG, always return 1 (it is its own low part!). */
*************** subreg_lowpart_p (x)
*** 1295,1310 ****
return 1;
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;
}
--- 1316,1324 ----
return 1;
else if (GET_MODE (SUBREG_REG (x)) == VOIDmode)
return 0;
! return subreg_lowpart_parts_p (GET_MODE (x), GET_MODE (SUBREG_REG (x)),
! SUBREG_BYTE (x));
}
Index: recog.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/recog.c,v
retrieving revision 1.98
diff -c -3 -p -r1.98 recog.c
*** recog.c 2001/05/01 12:11:33 1.98
--- recog.c 2001/05/02 15:23:42
*************** validate_replace_rtx_1 (loc, from, to, o
*** 574,718 ****
/* 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))
{
! 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 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);
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;
! }
break;
case ZERO_EXTRACT:
--- 574,599 ----
/* 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 (rtx_equal_p (SUBREG_REG (x), from))
{
! rtx temp;
! temp = simplify_subreg (GET_MODE (x), to, GET_MODE (SUBREG_REG (x)),
! SUBREG_BYTE (x));
! if (temp)
{
! validate_change (object, loc, temp, 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:
Index: rtl.h
===================================================================
RCS file: /cvs/gcc/egcs/gcc/rtl.h,v
retrieving revision 1.257
diff -c -3 -p -r1.257 rtl.h
*** rtl.h 2001/04/28 19:16:29 1.257
--- rtl.h 2001/05/02 15:23:43
*************** extern rtx constant_subword PARAMS ((rt
*** 1194,1199 ****
--- 1194,1202 ----
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));
*************** extern rtx simplify_gen_relational PARAM
*** 1320,1325 ****
--- 1323,1332 ----
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));
Index: simplify-rtx.c
===================================================================
RCS file: /cvs/gcc/egcs/gcc/simplify-rtx.c,v
retrieving revision 1.47
diff -c -3 -p -r1.47 simplify-rtx.c
*** simplify-rtx.c 2001/05/01 12:11:34 1.47
--- simplify-rtx.c 2001/05/02 15:23:44
*************** simplify_ternary_operation (code, mode,
*** 2179,2184 ****
--- 2179,2378 ----
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