From: Kyrylo Tkachov <[email protected]>

split_overlapping_partition_decls gives every partition but one of a
memory-resident VAR_DECL its own artificial decl, so that distinct stack slots
do not end up sharing a MEM_EXPR.  It takes the variable of a partition from
that partition's representative.  set_rtl attaches the base variable of any
name in a partition to that partition's location, so the variable a partition
is given need not be the one of its representative.

Here the partition holding the loop-carried versions of an oversized vector
temporary has an anonymous representative:

  Partition 1  (_2 - 2 14 16 )        _2 has no base variable, while
                                      14 and 16 are g18_lsm.11_14 and _16
  Partition 30 (g18_lsm.11_41 - 41 )

Partition 1 is skipped because SSA_NAME_VAR of _2 is null, so partition 30
believes it is the only partition of g18_lsm.11 and keeps the user decl.  Both
128 byte slots then get MEM_EXPR g18_lsm.11.  The load/store pair-fusion pass
groups accesses by MEM_EXPR base and MEM_OFFSET, so it treated two stores that
are 144 bytes apart as adjacent and fused them:

  before  (set (mem:V4SI [sp+240]) (reg v17))   ; g18_lsm.11+96
          (set (mem:V4SI [sp+384]) (reg v27))   ; g18_lsm.11+112

  after   (set (mem:V2x16QI [sp+240])
               (unspec [(reg v17) (reg v27)] UNSPEC_STP))

[sp+384] is never written.

Work out the variable each partition will be given and key the keep/split
decision on that, reusing the function set_rtl makes the same choice with,
renamed to expand_leader_merge now.
That function keeps the variable it is given first unless
a later one is DECL_IGNORED_P, and expansion hands set_rtl the representative
before the other names, so the representative's variable goes in first.
A partition holding names of two variables would otherwise be keyed on the
wrong one.

Bootstrapped and tested on aarch64-none-linux-gnu.

gcc/ChangeLog:

        PR middle-end/126405
        * cfgexpand.cc (leader_merge): Rename to...
        (expand_leader_merge): ...this.  Make external.
        * cfgexpand.h (expand_leader_merge): Declare.
        * tree-outof-ssa.cc: Include cfgexpand.h.
        (split_overlapping_partition_decls): Determine the variable of a
        partition from all of its names.

gcc/testsuite/ChangeLog:

        PR middle-end/126405
        * gcc.c-torture/execute/pr126405.c: New test.
        * gcc.c-torture/execute/pr126405-2.c: New test.
        * gcc.dg/pr126405-1.c: New test.
        * gcc.dg/pr126405-2.c: New test.
        * gcc.dg/pr126405-3.c: New test.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/cfgexpand.cc                              | 10 ++--
 gcc/cfgexpand.h                               |  1 +
 .../gcc.c-torture/execute/pr126405-2.c        | 54 ++++++++++++++++++
 .../gcc.c-torture/execute/pr126405.c          | 56 +++++++++++++++++++
 gcc/testsuite/gcc.dg/pr126405-1.c             | 29 ++++++++++
 gcc/testsuite/gcc.dg/pr126405-2.c             | 35 ++++++++++++
 gcc/testsuite/gcc.dg/pr126405-3.c             | 26 +++++++++
 gcc/tree-outof-ssa.cc                         | 29 +++++++++-
 8 files changed, 234 insertions(+), 6 deletions(-)
 create mode 100644 gcc/testsuite/gcc.c-torture/execute/pr126405-2.c
 create mode 100644 gcc/testsuite/gcc.c-torture/execute/pr126405.c
 create mode 100644 gcc/testsuite/gcc.dg/pr126405-1.c
 create mode 100644 gcc/testsuite/gcc.dg/pr126405-2.c
 create mode 100644 gcc/testsuite/gcc.dg/pr126405-3.c

diff --git a/gcc/cfgexpand.cc b/gcc/cfgexpand.cc
index 05efcea13ba..28b449c49f5 100644
--- a/gcc/cfgexpand.cc
+++ b/gcc/cfgexpand.cc
@@ -155,10 +155,11 @@ gimple_assign_rhs_to_tree (gimple *stmt)
 /* Choose either CUR or NEXT as the leader DECL for a partition.
    Prefer ignored decls, to simplify debug dumps and reduce ambiguity
    out of the same user variable being in multiple partitions (this is
-   less likely for compiler-introduced temps).  */
+   less likely for compiler-introduced temps).  Also used by out-of-SSA
+   to work out which variable a partition will be given.  */
 
-static tree
-leader_merge (tree cur, tree next)
+tree
+expand_leader_merge (tree cur, tree next)
 {
   if (cur == NULL || cur == next)
     return next;
@@ -251,7 +252,8 @@ set_rtl (tree t, rtx x)
       else
        gcc_unreachable ();
 
-      tree next = skip ? cur : leader_merge (cur, SSAVAR (t) ? SSAVAR (t) : t);
+      tree next
+       = skip ? cur : expand_leader_merge (cur, SSAVAR (t) ? SSAVAR (t) : t);
 
       if (cur != next)
        {
diff --git a/gcc/cfgexpand.h b/gcc/cfgexpand.h
index f3acb886225..feda4e44173 100644
--- a/gcc/cfgexpand.h
+++ b/gcc/cfgexpand.h
@@ -24,6 +24,7 @@ extern tree gimple_assign_rhs_to_tree (gimple *);
 extern HOST_WIDE_INT estimated_stack_frame_size (struct cgraph_node *);
 extern void expand_remove_edge (edge);
 extern void set_parm_rtl (tree, rtx);
+extern tree expand_leader_merge (tree, tree);
 
 
 #endif /* GCC_CFGEXPAND_H */
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr126405-2.c 
b/gcc/testsuite/gcc.c-torture/execute/pr126405-2.c
new file mode 100644
index 00000000000..49ce58539a2
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr126405-2.c
@@ -0,0 +1,54 @@
+/* The same out-of-SSA defect as pr126405.c, with a narrower companion vector.
+   That changes the register pressure around the copy of the oversized vector
+   and so the pair the fusion pass picks, but the cause is the same: two stack
+   slots of one variable sharing a MEM_EXPR.  Self-checking: aborts if the
+   result is wrong.  */
+
+typedef long __attribute__((vector_size (16 * sizeof (long)))) v16di;
+typedef int __attribute__((vector_size (8 * sizeof (int)))) v8si;
+
+long g2, g12;
+v16di g18;
+v8si g3;
+void *g27;
+
+/* The wrong value is read from an uninitialised stack slot, so make sure the
+   stack the callee reuses does not happen to be zero.  */
+__attribute__((noipa)) static void
+dirty_stack (void)
+{
+  volatile char buf[1024];
+  for (unsigned i = 0; i < sizeof (buf); i++)
+    buf[i] = 0xa5;
+}
+
+void
+f31 (void)
+{
+lbl_br1:
+  g18 = ~g18;
+  g3 = ~g3;
+  if (g2)
+    goto lbl_br1;
+lbl_b5:
+  switch (g12)
+    case 4:
+    case 0:
+      goto lbl_sw8;
+  __builtin_abort ();
+lbl_sw8:
+  if (g27)
+    goto lbl_b5;
+  g18 = ~g18;
+}
+
+int
+main (void)
+{
+  dirty_stack ();
+  f31 ();
+  for (int i = 0; i < 16; i++)
+    if (g18[i] != 0)
+      __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr126405.c 
b/gcc/testsuite/gcc.c-torture/execute/pr126405.c
new file mode 100644
index 00000000000..84d54364e95
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr126405.c
@@ -0,0 +1,56 @@
+/* AArch64 wrong code at -O2.  Store motion creates several SSA versions of an
+   oversized vector temporary (V16DI, 128 bytes, no register mode).  The
+   partition holding the loop-carried versions has an anonymous representative,
+   so out-of-SSA left it and the partition of the copy taken for the use after
+   the loop sharing one MEM_EXPR.  The load/store pair-fusion pass then treated
+   two stores 144 bytes apart as adjacent, fused them, and left the tail of one
+   slot uninitialised.  Self-checking: aborts if the result is wrong.  */
+
+typedef long __attribute__((vector_size (16 * sizeof (long)))) v16di;
+typedef int __attribute__((vector_size (16 * sizeof (int)))) v16si;
+
+long g2, g12;
+v16di g18;
+v16si g3;
+void *g27;
+
+/* The wrong value is read from an uninitialised stack slot, so make sure the
+   stack the callee reuses does not happen to be zero.  */
+__attribute__((noipa)) static void
+dirty_stack (void)
+{
+  volatile char buf[1024];
+  for (unsigned i = 0; i < sizeof (buf); i++)
+    buf[i] = 0xa5;
+}
+
+void
+f31 (void)
+{
+lbl_br1:
+  g18 = ~g18;
+  g3 = ~g3;
+  if (g2)
+    goto lbl_br1;
+lbl_b5:
+  switch (g12)
+    case 4:
+    case 0:
+      goto lbl_sw8;
+  __builtin_abort ();
+lbl_sw8:
+  if (g27)
+    goto lbl_b5;
+  g18 = ~g18;
+}
+
+int
+main (void)
+{
+  dirty_stack ();
+  f31 ();
+  for (int i = 0; i < 16; i++)
+    if (g18[i] != 0)
+      __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/pr126405-1.c 
b/gcc/testsuite/gcc.dg/pr126405-1.c
new file mode 100644
index 00000000000..40007b108eb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr126405-1.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fgimple -ffloat-store -ftree-coalesce-vars 
-fdump-rtl-expand" } */
+
+/* The partition holding v_2 has an anonymous representative, _1, so the
+   variable that expansion attaches to its stack slot comes from a name other
+   than the representative.  -ffloat-store puts both partitions of v in memory,
+   and they must not end up sharing a MEM_EXPR.  */
+
+double in1, in2, out1, out2, out3, out4;
+
+void __GIMPLE (ssa, startwith ("expand"))
+f (void)
+{
+  double v;
+  double _1;
+
+__BB(2):
+  _1 = in1;
+  v_2 = _1;
+  v_3 = in2;
+  out1 = v_2;
+  out2 = v_3;
+  out3 = v_2;
+  out4 = v_3;
+  return;
+}
+
+/* { dg-final { scan-rtl-dump {\[[0-9]+ v\+0} "expand" } } */
+/* { dg-final { scan-rtl-dump {\[[0-9]+ D\.[0-9]+\+0} "expand" } } */
diff --git a/gcc/testsuite/gcc.dg/pr126405-2.c 
b/gcc/testsuite/gcc.dg/pr126405-2.c
new file mode 100644
index 00000000000..c320824e4e4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr126405-2.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fgimple -ffloat-store -ftree-coalesce-vars 
-fdump-rtl-expand" } */
+
+/* Three partitions of v, each with an anonymous representative.  One keeps v
+   and the other two need their own decls, so that no two of the three stack
+   slots share a MEM_EXPR.  */
+
+double in1, in2, in3, out1, out2, out3, out4, out5, out6;
+
+void __GIMPLE (ssa, startwith ("expand"))
+f (void)
+{
+  double v;
+  double _1;
+  double _3;
+  double _5;
+
+__BB(2):
+  _1 = in1;
+  v_2 = _1;
+  _3 = in2;
+  v_4 = _3;
+  _5 = in3;
+  v_6 = _5;
+  out1 = v_2;
+  out2 = v_4;
+  out3 = v_6;
+  out4 = v_2;
+  out5 = v_4;
+  out6 = v_6;
+  return;
+}
+
+/* { dg-final { scan-rtl-dump {\[[0-9]+ v\+0} "expand" } } */
+/* { dg-final { scan-rtl-dump {\[[0-9]+ D\.[0-9]+\+0} "expand" } } */
diff --git a/gcc/testsuite/gcc.dg/pr126405-3.c 
b/gcc/testsuite/gcc.dg/pr126405-3.c
new file mode 100644
index 00000000000..b6e1bdc2498
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr126405-3.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -ffloat-store -ftree-coalesce-vars -fdump-rtl-expand" } */
+
+/* Coalescing puts names of u and of v in one partition, and its representative
+   is a name of v rather than the lowest numbered member.  The variable that
+   expansion attaches to that partition is therefore the one of the
+   representative, and keying the split on any other member of the partition
+   leaves it and the second partition of v sharing a MEM_EXPR.  */
+
+double g1, g2, g3, g4, g5;
+
+void
+f (int n)
+{
+  double u = g1;
+  double v = u;
+  for (int i = 0; i < n; i++)
+    v = v + g3;
+  double t = v;
+  v = g2;
+  g4 = t;
+  g5 = v;
+}
+
+/* { dg-final { scan-rtl-dump {\[[0-9]+ v\+0} "expand" } } */
+/* { dg-final { scan-rtl-dump {\[[0-9]+ D\.[0-9]+\+0} "expand" } } */
diff --git a/gcc/tree-outof-ssa.cc b/gcc/tree-outof-ssa.cc
index 17d5e70e71d..309acd4fde2 100644
--- a/gcc/tree-outof-ssa.cc
+++ b/gcc/tree-outof-ssa.cc
@@ -45,6 +45,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-ssa-ter.h"
 #include "tree-ssa-coalesce.h"
 #include "tree-outof-ssa.h"
+#include "cfgexpand.h"
 #include "dojump.h"
 #include "internal-fn.h"
 #include "gimple-fold.h"
@@ -1072,13 +1073,39 @@ split_overlapping_partition_decls (var_map map)
   auto_vec<tree> new_decl;
   new_decl.safe_grow_cleared (n);
   bool any = false;
+  unsigned ver;
+  tree name;
+
+  /* set_rtl attaches the base variable of any name in a partition to that
+     partition's location, not just the one of its representative, so collect
+     what the names of each partition contribute.  A name with no base
+     variable contributes nothing, since set_rtl passes a type rather than a
+     decl for those and leaves the MEM_EXPR it has in place.  */
+  auto_vec<tree> part_var;
+  part_var.safe_grow_cleared (n);
+  FOR_EACH_SSA_NAME (ver, name, cfun)
+    {
+      int p = var_to_partition (map, name);
+      if (p == NO_PARTITION)
+       continue;
+      tree var = SSA_NAME_VAR (name);
+      if (!var)
+       continue;
+      part_var[p] = expand_leader_merge (part_var[p], var);
+    }
 
   for (unsigned i = 0; i < n; i++)
     {
       tree repr = partition_to_var (map, i);
       if (!repr)
        continue;
+      /* Expansion hands set_rtl the representative before the other names,
+        and expand_leader_merge keeps the variable it is given first unless a
+        later one is DECL_IGNORED_P, so merging the two gives the variable
+        this partition ends up with.  */
       tree var = SSA_NAME_VAR (repr);
+      if (part_var[i])
+       var = expand_leader_merge (var, part_var[i]);
       if (!var || !VAR_P (var))
        continue;
       /* Only partitions that will live in memory can end up with a
@@ -1123,8 +1150,6 @@ split_overlapping_partition_decls (var_map map)
   if (!any)
     return;
 
-  unsigned ver;
-  tree name;
   FOR_EACH_SSA_NAME (ver, name, cfun)
     {
       if (SSA_NAME_IS_DEFAULT_DEF (name))
-- 
2.50.1 (Apple Git-155)

Reply via email to