rtl.def (SUBREG): Update comments.
authorRichard Sandiford <rsandifo@nildram.co.uk>
Wed, 19 Dec 2007 09:54:24 +0000 (09:54 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Wed, 19 Dec 2007 09:54:24 +0000 (09:54 +0000)
gcc/
* rtl.def (SUBREG): Update comments.
* rtl.h (reg_attrs): Be explicit about the type of offset used.
(set_reg_attrs_from_mem): Rename to...
(set_reg_attrs_from_value): ...this.
(adjust_reg_mode, byte_lowpart_offset): Declare.
* emit-rtl.c (byte_lowpart_offset): New function.
(update_reg_offset): Remove special offset handling for big-endian
targets.
(gen_rtx_REG_offset, gen_reg_rtx_offset): Explicitly say that the
offset parameter is added to REG_OFFSET.
(adjust_reg_mode): New function.
(set_reg_attrs_for_mem): Rename to...
(set_reg_attrs_for_value): ...this and generalize to all values.
If the register is a lowpart of the value, adjust the offset
accordingly.
(set_reg_attrs_for_parm): Update after the above renaming.
(set_reg_attrs_for_decl_rtl): New function, split out from
set_decl_incoming_rtl.  Set the offset of plain REGs to the
offset of the REG's mode from the decl's.  Assert that all
subregs are lowparts and handle their inner registers in the
same way as plain REGs.
(set_decl_rtl, set_incoming_decl_rtl): Use reg_attrs_for_decl_rtl.
(subreg_lowpart_offset): Explicitly say that the returned offset
is a SUBREG_BYTE.
* combine.c (do_SUBST_MODE, try_combine, undo_all): Use adjust_reg_mode
instead of PUT_MODE.
* final.c (alter_subreg): Fix/update argument to gen_rtx_REG_offset.
* config/ia64/ia64.c (ia64_expand_load_address): Likewise.
* regclass.c (reg_scan_mark_refs): Use set_reg_attrs_from_value.
* reload.c (find_reloads_subreg_address): Call set_mem_offset
when offseting a MEM.
* var-tracking.c (offset_valid_for_tracked_p): Delete.
(mode_for_reg_attrs): Replace with...
(track_loc_p): ...this new function.  Return the mode and offset
to the caller, checking that the latter is valid.  If the rtx is
a paradoxical lowpart of the decl, use the decl's mode instead.
Do the same when storing to a register that contains the entire decl.
(var_lowpart): Use byte_lowpart_offset rather than
subreg_lowpart_offset when adjusting the offset attribute.
(count_uses, add_uses, add_stores): Use track_reg_p instead of
REG_EXPR, MEM_EXPR, REG_OFFSET, INT_MEM_OFFSET, track_expr_p,
offset_valid_for_tracked_p and mode_for_reg_attrs.  Generate
lowparts for MEMs as well as REGs.
(vt_add_function_parameters): When obtaining the information from
the decl_rtl, adjust the offset to match incoming.  Use track_loc_p
and var_lowpart.

From-SVN: r131055

gcc/ChangeLog
gcc/combine.c
gcc/config/ia64/ia64.c
gcc/emit-rtl.c
gcc/final.c
gcc/regclass.c
gcc/reload.c
gcc/rtl.def
gcc/rtl.h
gcc/var-tracking.c

index 94ff58e..b8ae7de 100644 (file)
@@ -1,3 +1,52 @@
+2007-12-19  Richard Sandiford  <rsandifo@nildram.co.uk>
+
+       * rtl.def (SUBREG): Update comments.
+       * rtl.h (reg_attrs): Be explicit about the type of offset used.
+       (set_reg_attrs_from_mem): Rename to...
+       (set_reg_attrs_from_value): ...this.
+       (adjust_reg_mode, byte_lowpart_offset): Declare.
+       * emit-rtl.c (byte_lowpart_offset): New function.
+       (update_reg_offset): Remove special offset handling for big-endian
+       targets.
+       (gen_rtx_REG_offset, gen_reg_rtx_offset): Explicitly say that the
+       offset parameter is added to REG_OFFSET.
+       (adjust_reg_mode): New function.
+       (set_reg_attrs_for_mem): Rename to...
+       (set_reg_attrs_for_value): ...this and generalize to all values.
+       If the register is a lowpart of the value, adjust the offset
+       accordingly.
+       (set_reg_attrs_for_parm): Update after the above renaming.
+       (set_reg_attrs_for_decl_rtl): New function, split out from
+       set_decl_incoming_rtl.  Set the offset of plain REGs to the
+       offset of the REG's mode from the decl's.  Assert that all
+       subregs are lowparts and handle their inner registers in the
+       same way as plain REGs.
+       (set_decl_rtl, set_incoming_decl_rtl): Use reg_attrs_for_decl_rtl.
+       (subreg_lowpart_offset): Explicitly say that the returned offset
+       is a SUBREG_BYTE.
+       * combine.c (do_SUBST_MODE, try_combine, undo_all): Use adjust_reg_mode
+       instead of PUT_MODE.
+       * final.c (alter_subreg): Fix/update argument to gen_rtx_REG_offset.
+       * config/ia64/ia64.c (ia64_expand_load_address): Likewise.
+       * regclass.c (reg_scan_mark_refs): Use set_reg_attrs_from_value.
+       * reload.c (find_reloads_subreg_address): Call set_mem_offset
+       when offseting a MEM.
+       * var-tracking.c (offset_valid_for_tracked_p): Delete.
+       (mode_for_reg_attrs): Replace with...
+       (track_loc_p): ...this new function.  Return the mode and offset
+       to the caller, checking that the latter is valid.  If the rtx is
+       a paradoxical lowpart of the decl, use the decl's mode instead.
+       Do the same when storing to a register that contains the entire decl.
+       (var_lowpart): Use byte_lowpart_offset rather than
+       subreg_lowpart_offset when adjusting the offset attribute.
+       (count_uses, add_uses, add_stores): Use track_reg_p instead of
+       REG_EXPR, MEM_EXPR, REG_OFFSET, INT_MEM_OFFSET, track_expr_p,
+       offset_valid_for_tracked_p and mode_for_reg_attrs.  Generate
+       lowparts for MEMs as well as REGs.
+       (vt_add_function_parameters): When obtaining the information from
+       the decl_rtl, adjust the offset to match incoming.  Use track_loc_p
+       and var_lowpart.
+
 2007-12-18  Sebastian Pop  <sebastian.pop@amd.com>
 
        PR tree-optimization/34123
index 4e7086f..b3d064d 100644 (file)
@@ -751,7 +751,7 @@ do_SUBST_MODE (rtx *into, enum machine_mode newval)
   buf->kind = UNDO_MODE;
   buf->where.r = into;
   buf->old_contents.m = oldval;
-  PUT_MODE (*into, newval);
+  adjust_reg_mode (*into, newval);
 
   buf->next = undobuf.undos, undobuf.undos = buf;
 }
@@ -2984,7 +2984,7 @@ try_combine (rtx i3, rtx i2, rtx i1, int *new_direct_jump_p)
                {
                  struct undo *buf;
 
-                 PUT_MODE (regno_reg_rtx[REGNO (i2dest)], old_mode);
+                 adjust_reg_mode (regno_reg_rtx[REGNO (i2dest)], old_mode);
                  buf = undobuf.undos;
                  undobuf.undos = buf->next;
                  buf->next = undobuf.frees;
@@ -3826,7 +3826,7 @@ undo_all (void)
          *undo->where.i = undo->old_contents.i;
          break;
        case UNDO_MODE:
-         PUT_MODE (*undo->where.r, undo->old_contents.m);
+         adjust_reg_mode (*undo->where.r, undo->old_contents.m);
          break;
        default:
          gcc_unreachable ();
index a75e68e..f2d00cf 100644 (file)
@@ -796,7 +796,8 @@ ia64_expand_load_address (rtx dest, rtx src)
      computation below are also more natural to compute as 64-bit quantities.
      If we've been given an SImode destination register, change it.  */
   if (GET_MODE (dest) != Pmode)
-    dest = gen_rtx_REG_offset (dest, Pmode, REGNO (dest), 0);
+    dest = gen_rtx_REG_offset (dest, Pmode, REGNO (dest),
+                              byte_lowpart_offset (Pmode, GET_MODE (dest)));
 
   if (TARGET_NO_PIC)
     return false;
index f9b1347..ae8970c 100644 (file)
@@ -837,6 +837,22 @@ gen_rtvec_v (int n, rtx *argp)
   return rt_val;
 }
 \f
+/* Return the number of bytes between the start of an OUTER_MODE
+   in-memory value and the start of an INNER_MODE in-memory value,
+   given that the former is a lowpart of the latter.  It may be a
+   paradoxical lowpart, in which case the offset will be negative
+   on big-endian targets.  */
+
+int
+byte_lowpart_offset (enum machine_mode outer_mode,
+                    enum machine_mode inner_mode)
+{
+  if (GET_MODE_SIZE (outer_mode) < GET_MODE_SIZE (inner_mode))
+    return subreg_lowpart_offset (outer_mode, inner_mode);
+  else
+    return -subreg_lowpart_offset (inner_mode, outer_mode);
+}
+\f
 /* Generate a REG rtx for a new pseudo register of mode MODE.
    This pseudo is assigned the next sequential register number.  */
 
@@ -891,101 +907,18 @@ gen_reg_rtx (enum machine_mode mode)
   return val;
 }
 
-/* Update NEW with the same attributes as REG, but offsetted by OFFSET.
-   Do the big endian correction if needed.  */
+/* Update NEW with the same attributes as REG, but with OFFSET added
+   to the REG_OFFSET.  */
 
 static void
 update_reg_offset (rtx new, rtx reg, int offset)
 {
-  tree decl;
-  HOST_WIDE_INT var_size;
-
-  /* PR middle-end/14084
-     The problem appears when a variable is stored in a larger register
-     and later it is used in the original mode or some mode in between
-     or some part of variable is accessed.
-
-     On little endian machines there is no problem because
-     the REG_OFFSET of the start of the variable is the same when
-     accessed in any mode (it is 0).
-
-     However, this is not true on big endian machines.
-     The offset of the start of the variable is different when accessed
-     in different modes.
-     When we are taking a part of the REG we have to change the OFFSET
-     from offset WRT size of mode of REG to offset WRT size of variable.
-
-     If we would not do the big endian correction the resulting REG_OFFSET
-     would be larger than the size of the DECL.
-
-     Examples of correction, for BYTES_BIG_ENDIAN WORDS_BIG_ENDIAN machine:
-
-     REG.mode  MODE  DECL size  old offset  new offset  description
-     DI        SI    4          4           0           int32 in SImode
-     DI        SI    1          4           0           char in SImode
-     DI        QI    1          7           0           char in QImode
-     DI        QI    4          5           1           1st element in QImode
-                                                        of char[4]
-     DI        HI    4          6           2           1st element in HImode
-                                                        of int16[2]
-
-     If the size of DECL is equal or greater than the size of REG
-     we can't do this correction because the register holds the
-     whole variable or a part of the variable and thus the REG_OFFSET
-     is already correct.  */
-
-  decl = REG_EXPR (reg);
-  if ((BYTES_BIG_ENDIAN || WORDS_BIG_ENDIAN)
-      && decl != NULL
-      && offset > 0
-      && GET_MODE_SIZE (GET_MODE (reg)) > GET_MODE_SIZE (GET_MODE (new))
-      && ((var_size = int_size_in_bytes (TREE_TYPE (decl))) > 0
-         && var_size < GET_MODE_SIZE (GET_MODE (reg))))
-    {
-      int offset_le;
-
-      /* Convert machine endian to little endian WRT size of mode of REG.  */
-      if (WORDS_BIG_ENDIAN)
-       offset_le = ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
-                    / UNITS_PER_WORD) * UNITS_PER_WORD;
-      else
-       offset_le = (offset / UNITS_PER_WORD) * UNITS_PER_WORD;
-
-      if (BYTES_BIG_ENDIAN)
-       offset_le += ((GET_MODE_SIZE (GET_MODE (reg)) - 1 - offset)
-                     % UNITS_PER_WORD);
-      else
-       offset_le += offset % UNITS_PER_WORD;
-
-      if (offset_le >= var_size)
-       {
-         /* MODE is wider than the variable so the new reg will cover
-            the whole variable so the resulting OFFSET should be 0.  */
-         offset = 0;
-       }
-      else
-       {
-         /* Convert little endian to machine endian WRT size of variable.  */
-         if (WORDS_BIG_ENDIAN)
-           offset = ((var_size - 1 - offset_le)
-                     / UNITS_PER_WORD) * UNITS_PER_WORD;
-         else
-           offset = (offset_le / UNITS_PER_WORD) * UNITS_PER_WORD;
-
-         if (BYTES_BIG_ENDIAN)
-           offset += ((var_size - 1 - offset_le)
-                      % UNITS_PER_WORD);
-         else
-           offset += offset_le % UNITS_PER_WORD;
-       }
-    }
-
   REG_ATTRS (new) = get_reg_attrs (REG_EXPR (reg),
                                   REG_OFFSET (reg) + offset);
 }
 
-/* Generate a register with same attributes as REG, but offsetted by
-   OFFSET.  */
+/* Generate a register with same attributes as REG, but with OFFSET
+   added to the REG_OFFSET.  */
 
 rtx
 gen_rtx_REG_offset (rtx reg, enum machine_mode mode, unsigned int regno,
@@ -998,7 +931,7 @@ gen_rtx_REG_offset (rtx reg, enum machine_mode mode, unsigned int regno,
 }
 
 /* Generate a new pseudo-register with the same attributes as REG, but
-   offsetted by OFFSET.  */
+   with OFFSET added to the REG_OFFSET.  */
 
 rtx
 gen_reg_rtx_offset (rtx reg, enum machine_mode mode, int offset)
@@ -1009,14 +942,30 @@ gen_reg_rtx_offset (rtx reg, enum machine_mode mode, int offset)
   return new;
 }
 
-/* Set REG to the decl that MEM refers to.  */
+/* Adjust REG in-place so that it has mode MODE.  It is assumed that the
+   new register is a (possibly paradoxical) lowpart of the old one.  */
+
+void
+adjust_reg_mode (rtx reg, enum machine_mode mode)
+{
+  update_reg_offset (reg, reg, byte_lowpart_offset (mode, GET_MODE (reg)));
+  PUT_MODE (reg, mode);
+}
+
+/* Copy REG's attributes from X, if X has any attributes.  If REG and X
+   have different modes, REG is a (possibly paradoxical) lowpart of X.  */
 
 void
-set_reg_attrs_from_mem (rtx reg, rtx mem)
+set_reg_attrs_from_value (rtx reg, rtx x)
 {
-  if (MEM_OFFSET (mem) && GET_CODE (MEM_OFFSET (mem)) == CONST_INT)
+  int offset;
+
+  offset = byte_lowpart_offset (GET_MODE (reg), GET_MODE (x));
+  if (MEM_P (x) && MEM_OFFSET (x) && GET_CODE (MEM_OFFSET (x)) == CONST_INT)
     REG_ATTRS (reg)
-      = get_reg_attrs (MEM_EXPR (mem), INTVAL (MEM_OFFSET (mem)));
+      = get_reg_attrs (MEM_EXPR (x), INTVAL (MEM_OFFSET (x)) + offset);
+  if (REG_P (x) && REG_ATTRS (x))
+    update_reg_offset (reg, x, offset);
 }
 
 /* Set the register attributes for registers contained in PARM_RTX.
@@ -1026,7 +975,7 @@ void
 set_reg_attrs_for_parm (rtx parm_rtx, rtx mem)
 {
   if (REG_P (parm_rtx))
-    set_reg_attrs_from_mem (parm_rtx, mem);
+    set_reg_attrs_from_value (parm_rtx, mem);
   else if (GET_CODE (parm_rtx) == PARALLEL)
     {
       /* Check for a NULL entry in the first slot, used to indicate that the
@@ -1043,54 +992,21 @@ set_reg_attrs_for_parm (rtx parm_rtx, rtx mem)
     }
 }
 
-/* Assign the RTX X to declaration T.  */
-void
-set_decl_rtl (tree t, rtx x)
-{
-  DECL_WRTL_CHECK (t)->decl_with_rtl.rtl = x;
+/* Set the REG_ATTRS for registers in value X, given that X represents
+   decl T.  */
 
-  if (!x)
-    return;
-  /* For register, we maintain the reverse information too.  */
-  if (REG_P (x))
-    REG_ATTRS (x) = get_reg_attrs (t, 0);
-  else if (GET_CODE (x) == SUBREG)
-    REG_ATTRS (SUBREG_REG (x))
-      = get_reg_attrs (t, -SUBREG_BYTE (x));
-  if (GET_CODE (x) == CONCAT)
-    {
-      if (REG_P (XEXP (x, 0)))
-        REG_ATTRS (XEXP (x, 0)) = get_reg_attrs (t, 0);
-      if (REG_P (XEXP (x, 1)))
-       REG_ATTRS (XEXP (x, 1))
-         = get_reg_attrs (t, GET_MODE_UNIT_SIZE (GET_MODE (XEXP (x, 0))));
-    }
-  if (GET_CODE (x) == PARALLEL)
+static void
+set_reg_attrs_for_decl_rtl (tree t, rtx x)
+{
+  if (GET_CODE (x) == SUBREG)
     {
-      int i;
-      for (i = 0; i < XVECLEN (x, 0); i++)
-       {
-         rtx y = XVECEXP (x, 0, i);
-         if (REG_P (XEXP (y, 0)))
-           REG_ATTRS (XEXP (y, 0)) = get_reg_attrs (t, INTVAL (XEXP (y, 1)));
-       }
+      gcc_assert (subreg_lowpart_p (x));
+      x = SUBREG_REG (x);
     }
-}
-
-/* Assign the RTX X to parameter declaration T.  */
-void
-set_decl_incoming_rtl (tree t, rtx x)
-{
-  DECL_INCOMING_RTL (t) = x;
-
-  if (!x)
-    return;
-  /* For register, we maintain the reverse information too.  */
   if (REG_P (x))
-    REG_ATTRS (x) = get_reg_attrs (t, 0);
-  else if (GET_CODE (x) == SUBREG)
-    REG_ATTRS (SUBREG_REG (x))
-      = get_reg_attrs (t, -SUBREG_BYTE (x));
+    REG_ATTRS (x)
+      = get_reg_attrs (t, byte_lowpart_offset (GET_MODE (x),
+                                              TYPE_MODE (TREE_TYPE (t))));
   if (GET_CODE (x) == CONCAT)
     {
       if (REG_P (XEXP (x, 0)))
@@ -1119,6 +1035,26 @@ set_decl_incoming_rtl (tree t, rtx x)
     }
 }
 
+/* Assign the RTX X to declaration T.  */
+
+void
+set_decl_rtl (tree t, rtx x)
+{
+  DECL_WRTL_CHECK (t)->decl_with_rtl.rtl = x;
+  if (x)
+    set_reg_attrs_for_decl_rtl (t, x);
+}
+
+/* Assign the RTX X to parameter declaration T.  */
+
+void
+set_decl_incoming_rtl (tree t, rtx x)
+{
+  DECL_INCOMING_RTL (t) = x;
+  if (x)
+    set_reg_attrs_for_decl_rtl (t, x);
+}
+
 /* Identify REG (which may be a CONCAT) as a user register.  */
 
 void
@@ -1304,8 +1240,7 @@ gen_highpart_mode (enum machine_mode outermode, enum machine_mode innermode, rtx
                              subreg_highpart_offset (outermode, innermode));
 }
 
-/* Return offset in bytes to get OUTERMODE low part
-   of the value in mode INNERMODE stored in memory in target format.  */
+/* Return the SUBREG_BYTE for an OUTERMODE lowpart of an INNERMODE value.  */
 
 unsigned int
 subreg_lowpart_offset (enum machine_mode outermode, enum machine_mode innermode)
index 654f847..72edbc0 100644 (file)
@@ -2763,8 +2763,15 @@ alter_subreg (rtx *xp)
       else if (REG_P (y))
        {
          /* Simplify_subreg can't handle some REG cases, but we have to.  */
-         unsigned int regno = subreg_regno (x);
-         *xp = gen_rtx_REG_offset (y, GET_MODE (x), regno, SUBREG_BYTE (x));
+         unsigned int regno;
+         HOST_WIDE_INT offset;
+
+         regno = subreg_regno (x);
+         if (subreg_lowpart_p (x))
+           offset = byte_lowpart_offset (GET_MODE (x), GET_MODE (y));
+         else
+           offset = SUBREG_BYTE (x);
+         *xp = gen_rtx_REG_offset (y, GET_MODE (x), regno, offset);
        }
     }
 
index d292519..a4734f2 100644 (file)
@@ -2435,10 +2435,7 @@ reg_scan_mark_refs (rtx x, rtx insn)
                 || (GET_CODE (src) == SUBREG && subreg_lowpart_p (src)))
            src = XEXP (src, 0);
 
-         if (REG_P (src))
-           REG_ATTRS (dest) = REG_ATTRS (src);
-         if (MEM_P (src))
-           set_reg_attrs_from_mem (dest, src);
+         set_reg_attrs_from_value (dest, src);
        }
 
       /* ... fall through ...  */
index 64c0119..8f84546 100644 (file)
@@ -6025,6 +6025,8 @@ find_reloads_subreg_address (rtx x, int force_replace, int opnum,
 
              XEXP (tem, 0) = plus_constant (XEXP (tem, 0), offset);
              PUT_MODE (tem, GET_MODE (x));
+             if (MEM_OFFSET (tem))
+               set_mem_offset (tem, plus_constant (MEM_OFFSET (tem), offset));
 
              /* If this was a paradoxical subreg that we replaced, the
                 resulting memory must be sufficiently aligned to allow
index 9dee200..fa2238c 100644 (file)
@@ -371,14 +371,8 @@ DEF_RTL_EXPR(REG, "reg", "i00", RTX_OBJ)
    marked as having one operand so it can be turned into a REG.  */
 DEF_RTL_EXPR(SCRATCH, "scratch", "0", RTX_OBJ)
 
-/* One word of a multi-word value.
-   The first operand is the complete value; the second says which word.
-   The WORDS_BIG_ENDIAN flag controls whether word number 0
-   (as numbered in a SUBREG) is the most or least significant word.
-
-   This is also used to refer to a value in a different machine mode.
-   For example, it can be used to refer to a SImode value as if it were
-   Qimode, or vice versa.  Then the word number is always 0.  */
+/* A reference to a part of another value.  The first operand is the
+   complete value and the second is the byte offset of the selected part.   */
 DEF_RTL_EXPR(SUBREG, "subreg", "ei", RTX_EXTRA)
 
 /* This one-argument rtx is used for move instructions
index ef3a97e..f4aa35a 100644 (file)
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -149,7 +149,11 @@ typedef struct mem_attrs GTY(())
 } mem_attrs;
 
 /* Structure used to describe the attributes of a REG in similar way as
-   mem_attrs does for MEM above.  */
+   mem_attrs does for MEM above.  Note that the OFFSET field is calculated
+   in the same way as for mem_attrs, rather than in the same way as a
+   SUBREG_BYTE.  For example, if a big-endian target stores a byte
+   object in the low part of a 4-byte register, the OFFSET field
+   will be -3 rather than 0.  */
 
 typedef struct reg_attrs GTY(())
 {
@@ -1476,9 +1480,10 @@ extern rtx copy_insn_1 (rtx);
 extern rtx copy_insn (rtx);
 extern rtx gen_int_mode (HOST_WIDE_INT, enum machine_mode);
 extern rtx emit_copy_of_insn_after (rtx, rtx);
-extern void set_reg_attrs_from_mem (rtx, rtx);
+extern void set_reg_attrs_from_value (rtx, rtx);
 extern void set_mem_attrs_from_reg (rtx, rtx);
 extern void set_reg_attrs_for_parm (rtx, rtx);
+extern void adjust_reg_mode (rtx, enum machine_mode);
 extern int mem_expr_equal_p (const_tree, const_tree);
 
 /* In rtl.c */
@@ -1522,6 +1527,7 @@ extern unsigned int subreg_lowpart_offset (enum machine_mode,
                                           enum machine_mode);
 extern unsigned int subreg_highpart_offset (enum machine_mode,
                                            enum machine_mode);
+extern int byte_lowpart_offset (enum machine_mode, enum machine_mode);
 extern rtx make_safe_from (rtx, rtx);
 extern rtx convert_memory_address (enum machine_mode, rtx);
 extern rtx get_insns (void);
index 46752fa..eb7e3b9 100644 (file)
@@ -1645,18 +1645,6 @@ track_expr_p (tree expr)
   return 1;
 }
 
-/* Return true if OFFSET is a valid offset for a register or memory
-   access we want to track.  This is used to reject out-of-bounds
-   accesses that can cause assertions to fail later.  Note that we
-   don't reject negative offsets because they can be generated for
-   paradoxical subregs on big-endian architectures.  */
-
-static inline bool
-offset_valid_for_tracked_p (HOST_WIDE_INT offset)
-{
-  return (-MAX_VAR_PARTS < offset) && (offset < MAX_VAR_PARTS);
-}
-
 /* Determine whether a given LOC refers to the same variable part as
    EXPR+OFFSET.  */
 
@@ -1691,28 +1679,65 @@ same_variable_part_p (rtx loc, tree expr, HOST_WIDE_INT offset)
   return (expr == expr2 && offset == offset2);
 }
 
-/* REG is a register we want to track.  If not all of REG contains useful
-   information, return the mode of the lowpart that does contain useful
-   information, otherwise return the mode of REG.
+/* LOC is a REG or MEM that we would like to track if possible.
+   If EXPR is null, we don't know what expression LOC refers to,
+   otherwise it refers to EXPR + OFFSET.  STORE_REG_P is true if
+   LOC is an lvalue register.
 
-   If REG was a paradoxical subreg, its REG_ATTRS will describe the
-   whole subreg, but only the old inner part is really relevant.  */
+   Return true if EXPR is nonnull and if LOC, or some lowpart of it,
+   is something we can track.  When returning true, store the mode of
+   the lowpart we can track in *MODE_OUT (if nonnull) and its offset
+   from EXPR in *OFFSET_OUT (if nonnull).  */
 
-static enum machine_mode
-mode_for_reg_attrs (rtx reg)
+static bool
+track_loc_p (rtx loc, tree expr, HOST_WIDE_INT offset, bool store_reg_p,
+            enum machine_mode *mode_out, HOST_WIDE_INT *offset_out)
 {
   enum machine_mode mode;
 
-  mode = GET_MODE (reg);
-  if (!HARD_REGISTER_NUM_P (ORIGINAL_REGNO (reg)))
+  if (expr == NULL || !track_expr_p (expr))
+    return false;
+
+  /* If REG was a paradoxical subreg, its REG_ATTRS will describe the
+     whole subreg, but only the old inner part is really relevant.  */
+  mode = GET_MODE (loc);
+  if (REG_P (loc) && !HARD_REGISTER_NUM_P (ORIGINAL_REGNO (loc)))
     {
       enum machine_mode pseudo_mode;
 
-      pseudo_mode = PSEUDO_REGNO_MODE (ORIGINAL_REGNO (reg));
+      pseudo_mode = PSEUDO_REGNO_MODE (ORIGINAL_REGNO (loc));
       if (GET_MODE_SIZE (mode) > GET_MODE_SIZE (pseudo_mode))
-       mode = pseudo_mode;
+       {
+         offset += byte_lowpart_offset (pseudo_mode, mode);
+         mode = pseudo_mode;
+       }
+    }
+
+  /* If LOC is a paradoxical lowpart of EXPR, refer to EXPR itself.
+     Do the same if we are storing to a register and EXPR occupies
+     the whole of register LOC; in that case, the whole of EXPR is
+     being changed.  We exclude complex modes from the second case
+     because the real and imaginary parts are represented as separate
+     pseudo registers, even if the whole complex value fits into one
+     hard register.  */
+  if ((GET_MODE_SIZE (mode) > GET_MODE_SIZE (DECL_MODE (expr))
+       || (store_reg_p
+          && !COMPLEX_MODE_P (DECL_MODE (expr))
+          && hard_regno_nregs[REGNO (loc)][DECL_MODE (expr)] == 1))
+      && offset + byte_lowpart_offset (DECL_MODE (expr), mode) == 0)
+    {
+      mode = DECL_MODE (expr);
+      offset = 0;
     }
-  return mode;
+
+  if (offset < 0 || offset >= MAX_VAR_PARTS)
+    return false;
+
+  if (mode_out)
+    *mode_out = mode;
+  if (offset_out)
+    *offset_out = offset;
+  return true;
 }
 
 /* Return the MODE lowpart of LOC, or null if LOC is not something we
@@ -1722,7 +1747,7 @@ mode_for_reg_attrs (rtx reg)
 static rtx
 var_lowpart (enum machine_mode mode, rtx loc)
 {
-  unsigned int offset, regno;
+  unsigned int offset, reg_offset, regno;
 
   if (!REG_P (loc) && !MEM_P (loc))
     return NULL;
@@ -1730,13 +1755,14 @@ var_lowpart (enum machine_mode mode, rtx loc)
   if (GET_MODE (loc) == mode)
     return loc;
 
-  offset = subreg_lowpart_offset (mode, GET_MODE (loc));
+  offset = byte_lowpart_offset (mode, GET_MODE (loc));
 
   if (MEM_P (loc))
     return adjust_address_nv (loc, mode, offset);
 
+  reg_offset = subreg_lowpart_offset (mode, GET_MODE (loc));
   regno = REGNO (loc) + subreg_regno_offset (REGNO (loc), GET_MODE (loc),
-                                            offset, mode);
+                                            reg_offset, mode);
   return gen_rtx_REG_offset (loc, mode, regno, offset);
 }
 
@@ -1754,9 +1780,8 @@ count_uses (rtx *loc, void *insn)
       VTI (bb)->n_mos++;
     }
   else if (MEM_P (*loc)
-          && MEM_EXPR (*loc)
-          && track_expr_p (MEM_EXPR (*loc))
-          && offset_valid_for_tracked_p (INT_MEM_OFFSET (*loc)))
+          && track_loc_p (*loc, MEM_EXPR (*loc), INT_MEM_OFFSET (*loc),
+                          false, NULL, NULL))
     {
       VTI (bb)->n_mos++;
     }
@@ -1787,17 +1812,18 @@ count_stores (rtx loc, const_rtx expr ATTRIBUTE_UNUSED, void *insn)
 static int
 add_uses (rtx *loc, void *insn)
 {
+  enum machine_mode mode;
+
   if (REG_P (*loc))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
-      if (REG_EXPR (*loc)
-         && track_expr_p (REG_EXPR (*loc))
-         && offset_valid_for_tracked_p (REG_OFFSET (*loc)))
+      if (track_loc_p (*loc, REG_EXPR (*loc), REG_OFFSET (*loc),
+                      false, &mode, NULL))
        {
          mo->type = MO_USE;
-         mo->u.loc = var_lowpart (mode_for_reg_attrs (*loc), *loc);
+         mo->u.loc = var_lowpart (mode, *loc);
        }
       else
        {
@@ -1807,15 +1833,14 @@ add_uses (rtx *loc, void *insn)
       mo->insn = (rtx) insn;
     }
   else if (MEM_P (*loc)
-          && MEM_EXPR (*loc)
-          && track_expr_p (MEM_EXPR (*loc))
-          && offset_valid_for_tracked_p (INT_MEM_OFFSET (*loc)))
+          && track_loc_p (*loc, MEM_EXPR (*loc), INT_MEM_OFFSET (*loc),
+                          false, &mode, NULL))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
       mo->type = MO_USE;
-      mo->u.loc = *loc;
+      mo->u.loc = var_lowpart (mode, *loc);
       mo->insn = (rtx) insn;
     }
 
@@ -1837,22 +1862,22 @@ add_uses_1 (rtx *x, void *insn)
 static void
 add_stores (rtx loc, const_rtx expr, void *insn)
 {
+  enum machine_mode mode;
+
   if (REG_P (loc))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
 
       if (GET_CODE (expr) == CLOBBER
-         || !(REG_EXPR (loc)
-              && track_expr_p (REG_EXPR (loc))
-              && offset_valid_for_tracked_p (REG_OFFSET (loc))))
+         || !track_loc_p (loc, REG_EXPR (loc), REG_OFFSET (loc),
+                          true, &mode, NULL))
        {
          mo->type = MO_CLOBBER;
          mo->u.loc = loc;
        }
       else
        {
-         enum machine_mode mode = mode_for_reg_attrs (loc);
          rtx src = NULL;
 
          if (GET_CODE (expr) == SET && SET_DEST (expr) == loc)
@@ -1878,9 +1903,8 @@ add_stores (rtx loc, const_rtx expr, void *insn)
       mo->insn = (rtx) insn;
     }
   else if (MEM_P (loc)
-          && MEM_EXPR (loc)
-          && track_expr_p (MEM_EXPR (loc))
-          && offset_valid_for_tracked_p (INT_MEM_OFFSET (loc)))
+          && track_loc_p (loc, MEM_EXPR (loc), INT_MEM_OFFSET (loc),
+                          false, &mode, NULL))
     {
       basic_block bb = BLOCK_FOR_INSN ((rtx) insn);
       micro_operation *mo = VTI (bb)->mos + VTI (bb)->n_mos++;
@@ -1888,14 +1912,15 @@ add_stores (rtx loc, const_rtx expr, void *insn)
       if (GET_CODE (expr) == CLOBBER)
        {
          mo->type = MO_CLOBBER;
-         mo->u.loc = loc;
+         mo->u.loc = var_lowpart (mode, loc);
        }
       else
        {
          rtx src = NULL;
 
          if (GET_CODE (expr) == SET && SET_DEST (expr) == loc)
-           src = var_lowpart (GET_MODE (loc), SET_SRC (expr));
+           src = var_lowpart (mode, SET_SRC (expr));
+         loc = var_lowpart (mode, loc);
 
          if (src == NULL)
            {
@@ -1904,6 +1929,8 @@ add_stores (rtx loc, const_rtx expr, void *insn)
            }
          else
            {
+             if (SET_SRC (expr) != src)
+               expr = gen_rtx_SET (VOIDmode, loc, src);
              if (same_variable_part_p (SET_SRC (expr),
                                        MEM_EXPR (loc),
                                        INT_MEM_OFFSET (loc)))
@@ -3115,6 +3142,7 @@ vt_add_function_parameters (void)
       rtx decl_rtl = DECL_RTL_IF_SET (parm);
       rtx incoming = DECL_INCOMING_RTL (parm);
       tree decl;
+      enum machine_mode mode;
       HOST_WIDE_INT offset;
       dataflow_set *out;
 
@@ -3131,18 +3159,26 @@ vt_add_function_parameters (void)
        continue;
 
       if (!vt_get_decl_and_offset (incoming, &decl, &offset))
-       if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
-         continue;
+       {
+         if (!vt_get_decl_and_offset (decl_rtl, &decl, &offset))
+           continue;
+         offset += byte_lowpart_offset (GET_MODE (incoming),
+                                        GET_MODE (decl_rtl));
+       }
 
       if (!decl)
        continue;
 
       gcc_assert (parm == decl);
 
+      if (!track_loc_p (incoming, parm, offset, false, &mode, &offset))
+       continue;
+
       out = &VTI (ENTRY_BLOCK_PTR)->out;
 
       if (REG_P (incoming))
        {
+         incoming = var_lowpart (mode, incoming);
          gcc_assert (REGNO (incoming) < FIRST_PSEUDO_REGISTER);
          attrs_list_insert (&out->regs[REGNO (incoming)],
                             parm, offset, incoming);
@@ -3150,8 +3186,11 @@ vt_add_function_parameters (void)
                             NULL);
        }
       else if (MEM_P (incoming))
-       set_variable_part (out, incoming, parm, offset, VAR_INIT_STATUS_INITIALIZED, 
-                          NULL);
+       {
+         incoming = var_lowpart (mode, incoming);
+         set_variable_part (out, incoming, parm, offset,
+                            VAR_INIT_STATUS_INITIALIZED, NULL);
+       }
     }
 }