static HARD_REG_SET **valid_mode_changes;
static obstack valid_mode_changes_obstack;
+/* Restrict the choice of register for SUBREG_REG (SUBREG) based
+ on information about SUBREG.
+
+ If PARTIAL_DEF, SUBREG is a partial definition of a multipart inner
+ register and we want to ensure that the other parts of the inner
+ register are correctly preserved. If !PARTIAL_DEF we need to
+ ensure that SUBREG itself can be formed. */
+
static void
-record_subregs_of_mode (rtx subreg)
+record_subregs_of_mode (rtx subreg, bool partial_def)
{
unsigned int regno;
if (regno < FIRST_PSEUDO_REGISTER)
return;
+ subreg_shape shape (shape_of_subreg (subreg));
+ if (partial_def)
+ {
+ /* The number of independently-accessible SHAPE.outer_mode values
+ in SHAPE.inner_mode is GET_MODE_SIZE (SHAPE.inner_mode) / SIZE.
+ We need to check that the assignment will preserve all the other
+ SIZE-byte chunks in the inner register besides the one that
+ includes SUBREG.
+
+ In practice it is enough to check whether an equivalent
+ SHAPE.inner_mode value in an adjacent SIZE-byte chunk can be formed.
+ If the underlying registers are small enough, both subregs will
+ be valid. If the underlying registers are too large, one of the
+ subregs will be invalid.
+
+ This relies on the fact that we've already been passed
+ SUBREG with PARTIAL_DEF set to false. */
+ unsigned int size = MAX (REGMODE_NATURAL_SIZE (shape.inner_mode),
+ GET_MODE_SIZE (shape.outer_mode));
+ gcc_checking_assert (size < GET_MODE_SIZE (shape.inner_mode));
+ if (shape.offset >= size)
+ shape.offset -= size;
+ else
+ shape.offset += size;
+ }
+
if (valid_mode_changes[regno])
AND_HARD_REG_SET (*valid_mode_changes[regno],
- simplifiable_subregs (shape_of_subreg (subreg)));
+ simplifiable_subregs (shape));
else
{
valid_mode_changes[regno]
= XOBNEW (&valid_mode_changes_obstack, HARD_REG_SET);
COPY_HARD_REG_SET (*valid_mode_changes[regno],
- simplifiable_subregs (shape_of_subreg (subreg)));
+ simplifiable_subregs (shape));
}
}
int i;
if (code == SUBREG)
- record_subregs_of_mode (x);
+ record_subregs_of_mode (x, false);
/* Time for some deep diving. */
for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
FOR_EACH_BB_FN (bb, cfun)
FOR_BB_INSNS (bb, insn)
if (NONDEBUG_INSN_P (insn))
- find_subregs_of_mode (PATTERN (insn));
+ {
+ find_subregs_of_mode (PATTERN (insn));
+ df_ref def;
+ FOR_EACH_INSN_DEF (def, insn)
+ if (DF_REF_FLAGS_IS_SET (def, DF_REF_PARTIAL)
+ && df_read_modify_subreg_p (DF_REF_REG (def)))
+ record_subregs_of_mode (DF_REF_REG (def), true);
+ }
}
const HARD_REG_SET *
--- /dev/null
+/* { dg-do run } */
+/* { dg-require-effective-target avx } */
+/* { dg-require-effective-target int128 } */
+/* { dg-options "-O -fno-forward-propagate -fno-split-wide-types -mavx" } */
+
+typedef unsigned int u32;
+typedef unsigned __int128 u128;
+typedef unsigned __int128 v32u128 __attribute__ ((vector_size (32)));
+
+u128 __attribute__ ((noinline, noclone))
+foo (u32 u32_0, v32u128 v32u128_0)
+{
+ v32u128_0[0] >>= u32_0;
+ v32u128_0 += (v32u128) {u32_0, 0};
+ return u32_0 + v32u128_0[0] + v32u128_0[1];
+}
+
+int
+main()
+{
+ u128 x = foo (1, (v32u128) {1, 4});
+ if (x != 6)
+ __builtin_abort ();
+ return 0;
+}