aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog49
-rw-r--r--gcc/combine.c6
-rw-r--r--gcc/config/ia64/ia64.c3
-rw-r--r--gcc/emit-rtl.c213
-rw-r--r--gcc/final.c11
-rw-r--r--gcc/regclass.c5
-rw-r--r--gcc/reload.c2
-rw-r--r--gcc/rtl.def10
-rw-r--r--gcc/rtl.h10
-rw-r--r--gcc/var-tracking.c141
10 files changed, 240 insertions, 210 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 94ff58e..b8ae7de 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -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
diff --git a/gcc/combine.c b/gcc/combine.c
index 4e7086f..b3d064d 100644
--- a/gcc/combine.c
+++ b/gcc/combine.c
@@ -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 ();
diff --git a/gcc/config/ia64/ia64.c b/gcc/config/ia64/ia64.c
index a75e68e..f2d00cf 100644
--- a/gcc/config/ia64/ia64.c
+++ b/gcc/config/ia64/ia64.c
@@ -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;
diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
index f9b1347..ae8970c 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -837,6 +837,22 @@ gen_rtvec_v (int n, rtx *argp)
return rt_val;
}
+/* 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);
+}
+
/* 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)
diff --git a/gcc/final.c b/gcc/final.c
index 654f847..72edbc0 100644
--- a/gcc/final.c
+++ b/gcc/final.c
@@ -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);
}
}
diff --git a/gcc/regclass.c b/gcc/regclass.c
index d292519..a4734f2 100644
--- a/gcc/regclass.c
+++ b/gcc/regclass.c
@@ -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 ... */
diff --git a/gcc/reload.c b/gcc/reload.c
index 64c0119..8f84546 100644
--- a/gcc/reload.c
+++ b/gcc/reload.c
@@ -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
diff --git a/gcc/rtl.def b/gcc/rtl.def
index 9dee200..fa2238c 100644
--- a/gcc/rtl.def
+++ b/gcc/rtl.def
@@ -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
diff --git a/gcc/rtl.h b/gcc/rtl.h
index ef3a97e..f4aa35a 100644
--- 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);
diff --git a/gcc/var-tracking.c b/gcc/var-tracking.c
index 46752fa..eb7e3b9 100644
--- a/gcc/var-tracking.c
+++ b/gcc/var-tracking.c
@@ -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);
+ }
}
}