* simplify-rtx.c (simplify_subreg): Break out from ...
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 17 May 2001 15:00:35 +0000 (15:00 +0000)
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 17 May 2001 15:00:35 +0000 (15:00 +0000)
* 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

gcc/ChangeLog
gcc/combine.c
gcc/emit-rtl.c
gcc/recog.c
gcc/rtl.h
gcc/simplify-rtx.c

index 15b445d..d74a923 100644 (file)
@@ -1,3 +1,12 @@
+Thu May 17 16:59:41 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.
+
 2001-05-17  Bernd Schmidt  <bernds@redhat.com>
 
        * stmt.c (expand_asm_operands): For inout operands, make sure
index a60f92e..53df2f5 100644 (file)
@@ -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 (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 (op0_mode == VOIDmode)
+       op0_mode = GET_MODE (SUBREG_REG (x));
 
-         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
index 3452a68..b22e346 100644 (file)
@@ -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));
 }
 \f
 
index 86209fe..d858394 100644 (file)
@@ -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;
+             validate_change (object, loc, temp, 1);
+             return;
            }
-        }
-
-      /* 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)
+         /* Avoid creating of invalid SUBREGS.  */
+         if (GET_MODE (from) == VOIDmode)
            {
-             validate_change (object, loc, SUBREG_REG (to), 1);
+             /* 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;
            }
-
-         /* 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:
index a925210..6aa8613 100644 (file)
--- 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));
 
index 0468820..c4dee23 100644 (file)
@@ -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