]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
rtlanal.c (struct subreg_info, [...]): New.
authorJoseph Myers <joseph@codesourcery.com>
Wed, 20 Dec 2006 16:25:00 +0000 (16:25 +0000)
committerJoseph Myers <jsm28@gcc.gnu.org>
Wed, 20 Dec 2006 16:25:00 +0000 (16:25 +0000)
* rtlanal.c (struct subreg_info, subreg_get_info, subreg_nregs):
New.
(subreg_regno_offset, subreg_offset_representable_p): Change to
wrappers about subreg_get_info.
(refers_to_regno_p, reg_overlap_mentioned_p): Use subreg_nregs.
* rtl.h (subreg_nregs): Declare.
* doc/tm.texi (HARD_REGNO_NREGS_HAS_PADDING): Update to refer to
subreg_get_info.
* caller-save.c (mark_set_regs, add_stored_regs): Use
subreg_nregs.
* df-scan.c (df_ref_record): Use subreg_nregs.
* flow.c (mark_set_1): Use subreg_nregs.
* postreload.c (move2add_note_store): Use subreg_nregs.
* reload.c (decompose, refers_to_regno_for_reload_p,
reg_overlap_mentioned_for_reload_p): Use subreg_nregs.
* resource.c (update_live_status, mark_referenced_resources,
mark_set_resources): Use subreg_nregs.

From-SVN: r120076

gcc/ChangeLog
gcc/caller-save.c
gcc/df-scan.c
gcc/doc/tm.texi
gcc/flow.c
gcc/postreload.c
gcc/reload.c
gcc/resource.c
gcc/rtl.h
gcc/rtlanal.c

index f991a4d066d09078c13f633d206b3c48df2073ca..4f145939528fb8cdec241fd67e3b4e6fb54c19f2 100644 (file)
@@ -1,3 +1,23 @@
+2006-12-20  Joseph Myers  <joseph@codesourcery.com>
+
+       * rtlanal.c (struct subreg_info, subreg_get_info, subreg_nregs):
+       New.
+       (subreg_regno_offset, subreg_offset_representable_p): Change to
+       wrappers about subreg_get_info.
+       (refers_to_regno_p, reg_overlap_mentioned_p): Use subreg_nregs.
+       * rtl.h (subreg_nregs): Declare.
+       * doc/tm.texi (HARD_REGNO_NREGS_HAS_PADDING): Update to refer to
+       subreg_get_info.
+       * caller-save.c (mark_set_regs, add_stored_regs): Use
+       subreg_nregs.
+       * df-scan.c (df_ref_record): Use subreg_nregs.
+       * flow.c (mark_set_1): Use subreg_nregs.
+       * postreload.c (move2add_note_store): Use subreg_nregs.
+       * reload.c (decompose, refers_to_regno_for_reload_p,
+       reg_overlap_mentioned_for_reload_p): Use subreg_nregs.
+       * resource.c (update_live_status, mark_referenced_resources,
+       mark_set_resources): Use subreg_nregs.
+
 2006-12-20  Zdenek Dvorak <dvorakz@suse.cz>
 
        * loop-unswitch.c (unswitch_loop): Update arguments of
index c81c72c49b684987d17a78cced20c459eee873c6..86d74ee172653c6393dc69835e1e0f66d499d28d 100644 (file)
@@ -508,15 +508,17 @@ mark_set_regs (rtx reg, rtx setter ATTRIBUTE_UNUSED, void *data)
       if (!REG_P (inner) || REGNO (inner) >= FIRST_PSEUDO_REGISTER)
        return;
       regno = subreg_regno (reg);
+      endregno = regno + subreg_nregs (reg);
     }
   else if (REG_P (reg)
           && REGNO (reg) < FIRST_PSEUDO_REGISTER)
-    regno = REGNO (reg);
+    {
+      regno = REGNO (reg);
+      endregno = regno + hard_regno_nregs[regno][mode];
+    }
   else
     return;
 
-  endregno = regno + hard_regno_nregs[regno][mode];
-
   for (i = regno; i < endregno; i++)
     SET_HARD_REG_BIT (*this_insn_sets, i);
 }
@@ -542,13 +544,17 @@ add_stored_regs (rtx reg, rtx setter, void *data)
                                    SUBREG_BYTE (reg),
                                    GET_MODE (reg));
       reg = SUBREG_REG (reg);
+      regno = REGNO (reg) + offset;
+      endregno = regno + subreg_nregs (reg);
     }
+  else
+    {
+      if (!REG_P (reg) || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
+       return;
 
-  if (!REG_P (reg) || REGNO (reg) >= FIRST_PSEUDO_REGISTER)
-    return;
-
-  regno = REGNO (reg) + offset;
-  endregno = regno + hard_regno_nregs[regno][mode];
+      regno = REGNO (reg) + offset;
+      endregno = regno + hard_regno_nregs[regno][mode];
+    }
 
   for (i = regno; i < endregno; i++)
     SET_REGNO_REG_SET ((regset) data, i);
index 46dfb072cce43fb561134e1950cfac56f14166af..fea786c01962687ba58d523fff4257d2bd6da46a 100644 (file)
@@ -1083,15 +1083,14 @@ df_ref_record (struct dataflow *dflow, rtx reg, rtx *loc,
       if (!(dflow->flags & DF_HARD_REGS))
        return;
 
-      /* GET_MODE (reg) is correct here.  We do not want to go into a SUBREG
-         for the mode, because we only want to add references to regs, which
-        are really referenced.  E.g., a (subreg:SI (reg:DI 0) 0) does _not_
-        reference the whole reg 0 in DI mode (which would also include
-        reg 1, at least, if 0 and 1 are SImode registers).  */
-      endregno = hard_regno_nregs[regno][GET_MODE (reg)];
       if (GET_CODE (reg) == SUBREG)
-        regno += subreg_regno_offset (regno, GET_MODE (SUBREG_REG (reg)),
-                                     SUBREG_BYTE (reg), GET_MODE (reg));
+       {
+         regno += subreg_regno_offset (regno, GET_MODE (SUBREG_REG (reg)),
+                                       SUBREG_BYTE (reg), GET_MODE (reg));
+         endregno = subreg_nregs (reg);
+       }
+      else
+       endregno = hard_regno_nregs[regno][GET_MODE (reg)];
       endregno += regno;
 
       /*  If this is a multiword hardreg, we create some extra datastructures that 
index f209593f0c9bc52a5351898b5b388a19987ef6a9..0896ab8db1733b8807e688a08bcc3dc0ac6233b6 100644 (file)
@@ -1991,7 +1991,7 @@ registers but takes up 128 bits in memory, then this would be
 nonzero.
 
 This macros only needs to be defined if there are cases where
-@code{subreg_regno_offset} and @code{subreg_offset_representable_p}
+@code{subreg_get_info}
 would otherwise wrongly determine that a @code{subreg} can be
 represented by an offset to the register number, when in fact such a
 @code{subreg} would contain some of the padding not stored in
index 2b5d3032bf754405b01435bc460d3f4eadd682be..1da935bda31d9b92cc3be11c83806abc536e2116 100644 (file)
@@ -2791,8 +2791,7 @@ mark_set_1 (struct propagate_block_info *pbi, enum rtx_code code, rtx reg, rtx c
              regno_first += subreg_regno_offset (regno_first, inner_mode,
                                                  SUBREG_BYTE (reg),
                                                  outer_mode);
-             regno_last = (regno_first
-                           + hard_regno_nregs[regno_first][outer_mode] - 1);
+             regno_last = regno_first + subreg_nregs (reg) - 1;
 
              /* Since we've just adjusted the register number ranges, make
                 sure REG matches.  Otherwise some_was_live will be clear
index 5f4ae4f5b18249c62d892b177d8e856993021888..d1e58ab944d4ef823ef105847e42d7c119e67976 100644 (file)
@@ -1429,6 +1429,7 @@ static void
 move2add_note_store (rtx dst, rtx set, void *data ATTRIBUTE_UNUSED)
 {
   unsigned int regno = 0;
+  unsigned int nregs = 0;
   unsigned int i;
   enum machine_mode mode = GET_MODE (dst);
 
@@ -1438,6 +1439,7 @@ move2add_note_store (rtx dst, rtx set, void *data ATTRIBUTE_UNUSED)
                                   GET_MODE (SUBREG_REG (dst)),
                                   SUBREG_BYTE (dst),
                                   GET_MODE (dst));
+      nregs = subreg_nregs (dst);
       dst = SUBREG_REG (dst);
     }
 
@@ -1455,9 +1457,11 @@ move2add_note_store (rtx dst, rtx set, void *data ATTRIBUTE_UNUSED)
     return;
 
   regno += REGNO (dst);
+  if (!nregs)
+    nregs = hard_regno_nregs[regno][mode];
 
   if (SCALAR_INT_MODE_P (GET_MODE (dst))
-      && hard_regno_nregs[regno][mode] == 1 && GET_CODE (set) == SET
+      && nregs == 1 && GET_CODE (set) == SET
       && GET_CODE (SET_DEST (set)) != ZERO_EXTRACT
       && GET_CODE (SET_DEST (set)) != STRICT_LOW_PART)
     {
@@ -1557,7 +1561,7 @@ move2add_note_store (rtx dst, rtx set, void *data ATTRIBUTE_UNUSED)
     }
   else
     {
-      unsigned int endregno = regno + hard_regno_nregs[regno][mode];
+      unsigned int endregno = regno + nregs;
 
       for (i = regno; i < endregno; i++)
        /* Reset the information about this register.  */
index 1f1bc23020b4f7bbf58f3aef96306cd980babbc1..50af5d5e36621be1cadd6b4a8fba19b1620539df 100644 (file)
@@ -2414,7 +2414,7 @@ decompose (rtx x)
        return decompose (SUBREG_REG (x));
       else
        /* A hard reg.  */
-       val.end = val.start + hard_regno_nregs[val.start][GET_MODE (x)];
+       val.end = val.start + subreg_nregs (x);
       break;
 
     case SCRATCH:
@@ -6381,7 +6381,7 @@ refers_to_regno_for_reload_p (unsigned int regno, unsigned int endregno,
          unsigned int inner_regno = subreg_regno (x);
          unsigned int inner_endregno
            = inner_regno + (inner_regno < FIRST_PSEUDO_REGISTER
-                            ? hard_regno_nregs[inner_regno][GET_MODE (x)] : 1);
+                            ? subreg_nregs (x) : 1);
 
          return endregno > inner_regno && regno < inner_endregno;
        }
@@ -6479,6 +6479,10 @@ reg_overlap_mentioned_for_reload_p (rtx x, rtx in)
                                      GET_MODE (SUBREG_REG (x)),
                                      SUBREG_BYTE (x),
                                      GET_MODE (x));
+      endregno = regno + (regno < FIRST_PSEUDO_REGISTER
+                         ? subreg_nregs (x) : 1);
+
+      return refers_to_regno_for_reload_p (regno, endregno, in, (rtx*) 0);
     }
   else if (REG_P (x))
     {
@@ -6494,6 +6498,10 @@ reg_overlap_mentioned_for_reload_p (rtx x, rtx in)
          gcc_assert (reg_equiv_constant[regno]);
          return 0;
        }
+
+      endregno = regno + hard_regno_nregs[regno][GET_MODE (x)];
+
+      return refers_to_regno_for_reload_p (regno, endregno, in, (rtx*) 0);
     }
   else if (MEM_P (x))
     return refers_to_mem_for_reload_p (in);
@@ -6520,10 +6528,7 @@ reg_overlap_mentioned_for_reload_p (rtx x, rtx in)
                   || reg_overlap_mentioned_for_reload_p (XEXP (x, 1), in));
     }
 
-  endregno = regno + (regno < FIRST_PSEUDO_REGISTER
-                     ? hard_regno_nregs[regno][GET_MODE (x)] : 1);
-
-  return refers_to_regno_for_reload_p (regno, endregno, in, (rtx*) 0);
+  gcc_unreachable ();
 }
 
 /* Return nonzero if anything in X contains a MEM.  Look also for pseudo
index cd4eb10628ca4fbd7dba8ecf42457a260dc74808..398db5c9c404bfe7af9c6d81d09a1c51da928bc5 100644 (file)
@@ -99,11 +99,17 @@ update_live_status (rtx dest, rtx x, void *data ATTRIBUTE_UNUSED)
     return;
 
   if (GET_CODE (dest) == SUBREG)
-    first_regno = subreg_regno (dest);
-  else
-    first_regno = REGNO (dest);
+    {
+      first_regno = subreg_regno (dest);
+      last_regno = first_regno + subreg_nregs (dest);
 
-  last_regno = first_regno + hard_regno_nregs[first_regno][GET_MODE (dest)];
+    }
+  else
+    {
+      first_regno = REGNO (dest);
+      last_regno
+       = first_regno + hard_regno_nregs[first_regno][GET_MODE (dest)];
+    }
 
   if (GET_CODE (x) == CLOBBER)
     for (i = first_regno; i < last_regno; i++)
@@ -229,8 +235,7 @@ mark_referenced_resources (rtx x, struct resources *res,
       else
        {
          unsigned int regno = subreg_regno (x);
-         unsigned int last_regno
-           = regno + hard_regno_nregs[regno][GET_MODE (x)];
+         unsigned int last_regno = regno + subreg_nregs (x);
 
          gcc_assert (last_regno <= FIRST_PSEUDO_REGISTER);
          for (r = regno; r < last_regno; r++)
@@ -763,8 +768,7 @@ mark_set_resources (rtx x, struct resources *res, int in_dest,
          else
            {
              unsigned int regno = subreg_regno (x);
-             unsigned int last_regno
-               = regno + hard_regno_nregs[regno][GET_MODE (x)];
+             unsigned int last_regno = regno + subreg_nregs (x);
 
              gcc_assert (last_regno <= FIRST_PSEUDO_REGISTER);
              for (r = regno; r < last_regno; r++)
index 54dad700087ab14ec384b2de4daaf26eaf54463a..16de3bbe4b00e4aed435212ae6e7627bbd6c75c1 100644 (file)
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -1041,6 +1041,7 @@ extern unsigned int subreg_regno_offset   (unsigned int, enum machine_mode,
 extern bool subreg_offset_representable_p (unsigned int, enum machine_mode,
                                           unsigned int, enum machine_mode);
 extern unsigned int subreg_regno (rtx);
+extern unsigned int subreg_nregs (rtx);
 extern unsigned HOST_WIDE_INT nonzero_bits (rtx, enum machine_mode);
 extern unsigned int num_sign_bit_copies (rtx, enum machine_mode);
 extern bool constant_pool_constant_p (rtx);
index 4b965f8ca1c2c4429e3f2138ed4076cfd05fa028..c45a020709f5865c3d54c8cf0ab52256fd66d666 100644 (file)
@@ -38,6 +38,18 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "regs.h"
 #include "function.h"
 
+/* Information about a subreg of a hard register.  */
+struct subreg_info
+{
+  /* Offset of first hard register involved in the subreg.  */
+  int offset;
+  /* Number of hard registers involved in the subreg.  */
+  int nregs;
+  /* Whether this subreg can be represented as a hard reg with the new
+     mode.  */
+  bool representable_p;
+};
+
 /* Forward declarations */
 static void set_of_1 (rtx, rtx, void *);
 static bool covers_regno_p (rtx, unsigned int);
@@ -45,6 +57,9 @@ static bool covers_regno_no_parallel_p (rtx, unsigned int);
 static int rtx_referenced_p_1 (rtx *, void *);
 static int computed_jump_p_1 (rtx);
 static void parms_set (rtx, rtx, void *);
+static void subreg_get_info (unsigned int, enum machine_mode,
+                            unsigned int, enum machine_mode,
+                            struct subreg_info *);
 
 static unsigned HOST_WIDE_INT cached_nonzero_bits (rtx, enum machine_mode,
                                                    rtx, enum machine_mode,
@@ -1176,7 +1191,7 @@ refers_to_regno_p (unsigned int regno, unsigned int endregno, rtx x,
          unsigned int inner_regno = subreg_regno (x);
          unsigned int inner_endregno
            = inner_regno + (inner_regno < FIRST_PSEUDO_REGISTER
-                            ? hard_regno_nregs[inner_regno][GET_MODE (x)] : 1);
+                            ? subreg_nregs (x) : 1);
 
          return endregno > inner_regno && regno < inner_endregno;
        }
@@ -1266,13 +1281,15 @@ reg_overlap_mentioned_p (rtx x, rtx in)
       regno = REGNO (SUBREG_REG (x));
       if (regno < FIRST_PSEUDO_REGISTER)
        regno = subreg_regno (x);
+      endregno = regno + (regno < FIRST_PSEUDO_REGISTER
+                         ? subreg_nregs (x) : 1);
       goto do_reg;
 
     case REG:
       regno = REGNO (x);
-    do_reg:
       endregno = regno + (regno < FIRST_PSEUDO_REGISTER
                          ? hard_regno_nregs[regno][GET_MODE (x)] : 1);
+    do_reg:
       return refers_to_regno_p (regno, endregno, in, (rtx*) 0);
 
     case MEM:
@@ -2926,69 +2943,27 @@ subreg_lsb (rtx x)
                       SUBREG_BYTE (x));
 }
 
-/* This function returns the regno offset of a subreg expression.
-   xregno - A regno of an inner hard subreg_reg (or what will become one).
-   xmode  - The mode of xregno.
-   offset - The byte offset.
-   ymode  - The mode of a top level SUBREG (or what may become one).
-   RETURN - The regno offset which would be used.  */
-unsigned int
-subreg_regno_offset (unsigned int xregno, enum machine_mode xmode,
-                    unsigned int offset, enum machine_mode ymode)
-{
-  int nregs_xmode, nregs_ymode;
-  int mode_multiple, nregs_multiple;
-  int y_offset;
-
-  gcc_assert (xregno < FIRST_PSEUDO_REGISTER);
-
-  /* Adjust nregs_xmode to allow for 'holes'.  */
-  if (HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode))
-    nregs_xmode = HARD_REGNO_NREGS_WITH_PADDING (xregno, xmode);
-  else
-    nregs_xmode = hard_regno_nregs[xregno][xmode];
-    
-  nregs_ymode = hard_regno_nregs[xregno][ymode];
-
-  /* If this is a big endian paradoxical subreg, which uses more actual
-     hard registers than the original register, we must return a negative
-     offset so that we find the proper highpart of the register.  */
-  if (offset == 0
-      && nregs_ymode > nregs_xmode
-      && (GET_MODE_SIZE (ymode) > UNITS_PER_WORD
-         ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
-    return nregs_xmode - nregs_ymode;
-
-  if (offset == 0 || nregs_xmode == nregs_ymode)
-    return 0;
-
-  /* Size of ymode must not be greater than the size of xmode.  */
-  mode_multiple = GET_MODE_SIZE (xmode) / GET_MODE_SIZE (ymode);
-  gcc_assert (mode_multiple != 0);
-
-  y_offset = offset / GET_MODE_SIZE (ymode);
-  nregs_multiple =  nregs_xmode / nregs_ymode;
-  return (y_offset / (mode_multiple / nregs_multiple)) * nregs_ymode;
-}
-
-/* This function returns true when the offset is representable via
-   subreg_offset in the given regno.
+/* Fill in information about a subreg of a hard register.
    xregno - A regno of an inner hard subreg_reg (or what will become one).
    xmode  - The mode of xregno.
    offset - The byte offset.
    ymode  - The mode of a top level SUBREG (or what may become one).
-   RETURN - Whether the offset is representable.  */
-bool
-subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
-                              unsigned int offset, enum machine_mode ymode)
+   info   - Pointer to structure to fill in.  */
+static void
+subreg_get_info (unsigned int xregno, enum machine_mode xmode,
+                unsigned int offset, enum machine_mode ymode,
+                struct subreg_info *info)
 {
   int nregs_xmode, nregs_ymode;
   int mode_multiple, nregs_multiple;
-  int y_offset;
+  int offset_adj, y_offset, y_offset_adj;
   int regsize_xmode, regsize_ymode;
+  bool rknown;
 
   gcc_assert (xregno < FIRST_PSEUDO_REGISTER);
 
+  rknown = false;
+
   /* If there are holes in a non-scalar mode in registers, we expect
      that it is made up of its units concatenated together.  */
   if (HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode))
@@ -3021,7 +2996,10 @@ subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
          && (offset / GET_MODE_SIZE (xmode_unit)
              != ((offset + GET_MODE_SIZE (ymode) - 1)
                  / GET_MODE_SIZE (xmode_unit))))
-       return false;
+       {
+         info->representable_p = false;
+         rknown = true;
+       }
     }
   else
     nregs_xmode = hard_regno_nregs[xregno][xmode];
@@ -3029,24 +3007,57 @@ subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
   nregs_ymode = hard_regno_nregs[xregno][ymode];
 
   /* Paradoxical subregs are otherwise valid.  */
-  if (offset == 0
-      && nregs_ymode > nregs_xmode
-      && (GET_MODE_SIZE (ymode) > UNITS_PER_WORD
-         ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN))
-    return true;
+  if (!rknown
+      && offset == 0
+      && GET_MODE_SIZE (ymode) > GET_MODE_SIZE (xmode))
+    {
+      info->representable_p = true;
+      /* If this is a big endian paradoxical subreg, which uses more
+        actual hard registers than the original register, we must
+        return a negative offset so that we find the proper highpart
+        of the register.  */
+      if (GET_MODE_SIZE (ymode) > UNITS_PER_WORD
+         ? WORDS_BIG_ENDIAN : BYTES_BIG_ENDIAN)
+       info->offset = nregs_xmode - nregs_ymode;
+      else
+       info->offset = 0;
+      info->nregs = nregs_ymode;
+      return;
+    }
 
   /* If registers store different numbers of bits in the different
      modes, we cannot generally form this subreg.  */
-  regsize_xmode = GET_MODE_SIZE (xmode) / nregs_xmode;
-  regsize_ymode = GET_MODE_SIZE (ymode) / nregs_ymode;
-  if (regsize_xmode > regsize_ymode && nregs_ymode > 1)
-    return false;
-  if (regsize_ymode > regsize_xmode && nregs_xmode > 1)
-    return false;
+  if (!HARD_REGNO_NREGS_HAS_PADDING (xregno, xmode)
+      && !HARD_REGNO_NREGS_HAS_PADDING (xregno, ymode))
+    {
+      regsize_xmode = GET_MODE_SIZE (xmode) / nregs_xmode;
+      gcc_assert (regsize_xmode * nregs_xmode == GET_MODE_SIZE (xmode));
+      regsize_ymode = GET_MODE_SIZE (ymode) / nregs_ymode;
+      gcc_assert (regsize_ymode * nregs_ymode == GET_MODE_SIZE (ymode));
+      if (!rknown && regsize_xmode > regsize_ymode && nregs_ymode > 1)
+       {
+         info->representable_p = false;
+         info->nregs
+           = (GET_MODE_SIZE (ymode) + regsize_xmode - 1) / regsize_xmode;
+         info->offset = offset / regsize_xmode;
+         return;
+       }
+      if (!rknown && regsize_ymode > regsize_xmode && nregs_xmode > 1)
+       {
+         info->representable_p = false;
+         info->nregs
+           = (GET_MODE_SIZE (ymode) + regsize_xmode - 1) / regsize_xmode;
+         info->offset = offset / regsize_xmode;
+         return;
+       }
+    }
 
   /* Lowpart subregs are otherwise valid.  */
-  if (offset == subreg_lowpart_offset (ymode, xmode))
-    return true;
+  if (!rknown && offset == subreg_lowpart_offset (ymode, xmode))
+    {
+      info->representable_p = true;
+      rknown = true;
+    }
 
   /* This should always pass, otherwise we don't know how to verify
      the constraint.  These conditions may be relaxed but
@@ -3057,22 +3068,61 @@ subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
   /* The XMODE value can be seen as a vector of NREGS_XMODE
      values.  The subreg must represent a lowpart of given field.
      Compute what field it is.  */
-  offset -= subreg_lowpart_offset (ymode,
-                                  mode_for_size (GET_MODE_BITSIZE (xmode)
-                                                 / nregs_xmode,
-                                                 MODE_INT, 0));
+  offset_adj = offset;
+  offset_adj -= subreg_lowpart_offset (ymode,
+                                      mode_for_size (GET_MODE_BITSIZE (xmode)
+                                                     / nregs_xmode,
+                                                     MODE_INT, 0));
 
   /* Size of ymode must not be greater than the size of xmode.  */
   mode_multiple = GET_MODE_SIZE (xmode) / GET_MODE_SIZE (ymode);
   gcc_assert (mode_multiple != 0);
 
   y_offset = offset / GET_MODE_SIZE (ymode);
-  nregs_multiple =  nregs_xmode / nregs_ymode;
+  y_offset_adj = offset_adj / GET_MODE_SIZE (ymode);
+  nregs_multiple = nregs_xmode / nregs_ymode;
 
-  gcc_assert ((offset % GET_MODE_SIZE (ymode)) == 0);
+  gcc_assert ((offset_adj % GET_MODE_SIZE (ymode)) == 0);
   gcc_assert ((mode_multiple % nregs_multiple) == 0);
 
-  return (!(y_offset % (mode_multiple / nregs_multiple)));
+  if (!rknown)
+    {
+      info->representable_p = (!(y_offset_adj % (mode_multiple / nregs_multiple)));
+      rknown = true;
+    }
+  info->offset = (y_offset / (mode_multiple / nregs_multiple)) * nregs_ymode;
+  info->nregs = nregs_ymode;
+}
+
+/* This function returns the regno offset of a subreg expression.
+   xregno - A regno of an inner hard subreg_reg (or what will become one).
+   xmode  - The mode of xregno.
+   offset - The byte offset.
+   ymode  - The mode of a top level SUBREG (or what may become one).
+   RETURN - The regno offset which would be used.  */
+unsigned int
+subreg_regno_offset (unsigned int xregno, enum machine_mode xmode,
+                    unsigned int offset, enum machine_mode ymode)
+{
+  struct subreg_info info;
+  subreg_get_info (xregno, xmode, offset, ymode, &info);
+  return info.offset;
+}
+
+/* This function returns true when the offset is representable via
+   subreg_offset in the given regno.
+   xregno - A regno of an inner hard subreg_reg (or what will become one).
+   xmode  - The mode of xregno.
+   offset - The byte offset.
+   ymode  - The mode of a top level SUBREG (or what may become one).
+   RETURN - Whether the offset is representable.  */
+bool
+subreg_offset_representable_p (unsigned int xregno, enum machine_mode xmode,
+                              unsigned int offset, enum machine_mode ymode)
+{
+  struct subreg_info info;
+  subreg_get_info (xregno, xmode, offset, ymode, &info);
+  return info.representable_p;
 }
 
 /* Return the final regno that a subreg expression refers to.  */
@@ -3090,6 +3140,21 @@ subreg_regno (rtx x)
   return ret;
 
 }
+
+/* Return the number of registers that a subreg expression refers
+   to.  */
+unsigned int
+subreg_nregs (rtx x)
+{
+  struct subreg_info info;
+  rtx subreg = SUBREG_REG (x);
+  int regno = REGNO (subreg);
+
+  subreg_get_info (regno, GET_MODE (subreg), SUBREG_BYTE (x), GET_MODE (x),
+                  &info);
+  return info.nregs;
+}
+
 struct parms_set_data
 {
   int nregs;