[gcc r16-9385] out-of-SSA: use the spill predicate for partition decls [PR123625, PR121957]

Kyrylo Tkachov via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:7d85832d09f9959a2dea95d956f54f70eabde1f7

commit r16-9385-g7d85832d09f9959a2dea95d956f54f70eabde1f7
Author: Kyrylo Tkachov <[email protected]>
Date:   Tue Jul 7 03:24:44 2026 -0700

    out-of-SSA: use the spill predicate for partition decls [PR123625, PR121957]
    
    Changes in v2:
    - Match the exact expansion spill predicate instead of using BLKmode.
    - Add a non-BLKmode -ffloat-store regression.
    
    Out-of-SSA can leave several partitions sharing one base VAR_DECL when its
    SSA versions have overlapping live ranges.  If those partitions are spilled,
    set_mem_attributes gives every stack slot the same MEM_EXPR at offset zero.
    MEM_EXPR-based disambiguation can then combine accesses from distinct slots.
    
    Split the overlapping partitions after TER, using the same
    use_register_for_decl predicate as expansion rather than approximating the
    memory decision with BLKmode.  Keep the default-definition partition tied to
    the original decl and give the other partitions artificial decls with debug
    expressions referring back to it.
    
    Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-linux.
    
    gcc/ChangeLog:
    
            PR target/123625
            PR target/121957
            * tree-outof-ssa.cc (split_overlapping_partition_decls): New function.
            Use use_register_for_decl to identify memory partitions.
            (remove_ssa_form): Call split_overlapping_partition_decls.
    
    gcc/testsuite/ChangeLog:
    
            PR target/123625
            PR target/121957
            * gcc.c-torture/execute/pr121957.c: New test.
            * gcc.c-torture/execute/pr123625.c: New test.
            * gcc.c-torture/execute/pr123625-2.c: New test.
            * gcc.c-torture/execute/pr123625-3.c: New test.
            * gcc.dg/pr123625-1.c: New test.
    
    Signed-off-by: Kyrylo Tkachov <[email protected]>
    (cherry picked from commit 9aab80ddc5b2fa0eef80008e718067ab45f42c50)

Diff:
---
 gcc/testsuite/gcc.c-torture/execute/pr121957.c   | 27 ++++++++
 gcc/testsuite/gcc.c-torture/execute/pr123625-2.c | 34 ++++++++++
 gcc/testsuite/gcc.c-torture/execute/pr123625-3.c | 32 +++++++++
 gcc/testsuite/gcc.c-torture/execute/pr123625.c   | 48 +++++++++++++
 gcc/testsuite/gcc.dg/pr123625-1.c                | 22 ++++++
 gcc/tree-outof-ssa.cc                            | 86 ++++++++++++++++++++++++
 6 files changed, 249 insertions(+)

diff --git a/gcc/testsuite/gcc.c-torture/execute/pr121957.c b/gcc/testsuite/gcc.c-torture/execute/pr121957.c
new file mode 100644
index 000000000000..98969566c29c
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr121957.c
@@ -0,0 +1,27 @@
+/* AArch64 wrong code at -O2/-O3.  An oversized vector
+   (V16DI, 128 bytes, with no register mode) is expanded into two distinct
+   stack slots that share the same MEM_EXPR.  The load/store pair-fusion pass
+   then discovers "adjacent" store pairs across the two slots via the shared
+   MEM_EXPR and fuses them, redirecting a store to the wrong slot and leaving
+   part of a slot uninitialised.  Self-checking: aborts if the result is
+   wrong.  */
+
+typedef long __attribute__((vector_size (16 * sizeof (long)))) v16di;
+
+int
+main (void)
+{
+  v16di v = {};
+  asm goto ("" : : : : L1);
+L2:
+  asm goto ("" : : : : L1);
+L0:
+  asm goto ("" : : : : L2);
+  v = (v16di){ -1 };
+  asm goto ("" : : : : L0);
+L1:
+  asm goto ("" : : : : L0);
+  if (v[3])
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr123625-2.c b/gcc/testsuite/gcc.c-torture/execute/pr123625-2.c
new file mode 100644
index 000000000000..50ee842519c4
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr123625-2.c
@@ -0,0 +1,34 @@
+/* With -ftree-coalesce-vars, one variable can have SSA names coalesced into
+   partitions of other variables: the PHIs below put the two names of b into
+   the partitions of a and of c.  All three variables are oversized vectors
+   with no register mode, so both partitions are spilled and b legitimately
+   lives in two distinct stack slots (its DECL_RTL becomes the "multiple
+   places" marker).  Out-of-SSA must keep the two slots distinguishable
+   without rejecting this state.  */
+
+typedef long __attribute__((vector_size (16 * sizeof (long)))) v16di;
+
+v16di g0 = { 1 }, g1 = { 5 }, g2 = { 3 }, g3 = { 7 };
+volatile int p, q;
+
+int
+main (void)
+{
+  v16di b = g0;
+  v16di a;
+  if (p)
+    a = b;
+  else
+    a = g1;
+  g1 = a;
+  b = g2;
+  v16di c;
+  if (q)
+    c = b;
+  else
+    c = g3;
+  g3 = c;
+  if (g1[0] != 5 || g1[1] != 0 || g3[0] != 7 || g3[1] != 0)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr123625-3.c b/gcc/testsuite/gcc.c-torture/execute/pr123625-3.c
new file mode 100644
index 000000000000..ccac48f7797e
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr123625-3.c
@@ -0,0 +1,32 @@
+/* Same state as pr123625-2.c (one variable coalesced into two other
+   variables' spilled partitions) reached through asm goto control flow.  */
+
+typedef long __attribute__((vector_size (16 * sizeof (long)))) v16di;
+
+v16di g0, g1, g2, g3;
+
+int
+main (void)
+{
+  v16di b = g0;
+  v16di a;
+  asm goto ("" : : : : L0);
+  a = b;
+  goto L1;
+L0:
+  a = g1;
+L1:
+  g1 = a;
+  b = g2;
+  v16di c;
+  asm goto ("" : : : : L2);
+  c = b;
+  goto L3;
+L2:
+  c = g3;
+L3:
+  g3 = c;
+  if (g1[0] | g1[1] | g3[0] | g3[1])
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/pr123625.c b/gcc/testsuite/gcc.c-torture/execute/pr123625.c
new file mode 100644
index 000000000000..2b3b29b5e143
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/pr123625.c
@@ -0,0 +1,48 @@
+/* AArch64 wrong code at -O3.  An oversized vector
+   (V16DI, 128 bytes, no register mode) is expanded into two distinct
+   stack slots that shared the same MEM_EXPR.  The load/store pair fusion
+   pass then fused stores belonging to different slots, leaving part of a
+   slot uninitialized.  Self-checking: the checksum is target-independent.  */
+
+#include <stdint.h>
+
+#define BS_VEC(type, num) type __attribute__((vector_size(num * sizeof(type))))
+uint64_t BS_CHECKSUM, g_284;
+struct U0
+{
+  int16_t f0;
+  int64_t f2;
+} g_205;
+int16_t g_8, func_2_BS_COND_1;
+uint8_t g_9[2][1];
+volatile struct U0 g_121[1];
+int64_t *g_565 = &g_284;
+
+int
+main (void)
+{
+  BS_VEC (int64_t, 16) BS_VAR_3 = { 1, 8096386231136, 9039249955151 };
+  uint64_t LOCAL_CHECKSUM = 0;
+  switch (func_2_BS_COND_1)
+    {
+    case 2: goto BS_LABEL_0;
+    case 4: goto BS_LABEL_0;
+    }
+  for (g_8 = 0; g_8 <= 0; g_8 += 1)
+    for (g_205.f2 = 0; g_205.f2 <= 1; g_205.f2 += 1)
+      {
+	if (g_9[g_205.f2][0])
+	BS_LABEL_0:
+	  for (;;)
+	    ;
+	for (uint32_t BS_TEMP_371 = 0; BS_TEMP_371 < 16; BS_TEMP_371++)
+	  LOCAL_CHECKSUM ^= BS_VAR_3[BS_TEMP_371] + 9
+	    + (LOCAL_CHECKSUM << 6) + LOCAL_CHECKSUM
+	    >> (*g_565 |= g_121[0].f0);
+	BS_VAR_3 = (BS_VEC (int64_t, 16)){};
+      }
+  BS_CHECKSUM = LOCAL_CHECKSUM;
+  if (BS_CHECKSUM != 0x7237237237237237ULL)
+    __builtin_abort ();
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/pr123625-1.c b/gcc/testsuite/gcc.dg/pr123625-1.c
new file mode 100644
index 000000000000..f88273e95c65
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr123625-1.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fgimple -ffloat-store -ftree-coalesce-vars -fdump-rtl-expand" } */
+
+double in1, in2, out1, out2;
+
+void __GIMPLE (ssa, startwith ("expand"))
+f (void)
+{
+  double v;
+
+__BB(2):
+  v_1 = in1;
+  v_2 = in2;
+  out1 = v_1;
+  out2 = v_2;
+  return;
+}
+
+/* The scalar floating-point SSA names overlap, and -ffloat-store assigns
+   both partitions to memory.  */
+/* { 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 f4bc4878bd6c..918dbbedde36 100644
--- a/gcc/tree-outof-ssa.cc
+++ b/gcc/tree-outof-ssa.cc
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl.h"
 #include "tree.h"
 #include "gimple.h"
+#include "gimple-expr.h"
 #include "cfghooks.h"
 #include "ssa.h"
 #include "tree-ssa.h"
@@ -1049,6 +1050,86 @@ expand_phi_nodes (struct ssaexpand *sa)
 }
 
 
+/* Out-of-SSA can leave several partitions sharing one base VAR_DECL, when
+   that variable's SSA versions are simultaneously live and so cannot all be
+   coalesced.  When such a partition is spilled (it has no register mode,
+   e.g.  an oversized vector that is BLKmode), set_mem_attributes would give
+   every one of those slots that single decl as its MEM_EXPR at offset 0, so
+   the distinct slots appear to be the same object and mislead MEM_EXPR-based
+   disambiguation, and the load/store pair-fusion pass then fuses across the
+   slots and corrupts one.  Give every partition but one of such a decl its
+   own artificial decl so the slots are distinguished at the source.
+   The new decl carries a DECL_DEBUG_EXPR back to the user variable so debug
+   info still attributes the storage to it (cf.  create_access_replacement in
+   tree-sra.cc).  PARM_DECLs and RESULT_DECLs are left alone, as they require
+   a single partition holding the canonical RTL.  */
+
+static void
+split_overlapping_partition_decls (var_map map)
+{
+  unsigned n = num_var_partitions (map);
+  hash_set<tree> seen;
+  auto_vec<tree> new_decl;
+  new_decl.safe_grow_cleared (n);
+  bool any = false;
+
+  for (unsigned i = 0; i < n; i++)
+    {
+      tree repr = partition_to_var (map, i);
+      if (!repr)
+	continue;
+      tree var = SSA_NAME_VAR (repr);
+      if (!var || !VAR_P (var))
+	continue;
+      /* Only partitions that will live in memory can end up with a
+	 misleading shared MEM_EXPR.  Mirror the decision that
+	 expand_one_ssa_partition will make.  */
+      if (use_register_for_decl (repr))
+	continue;
+      /* One partition of VAR keeps the user decl, the rest are split.
+	 A default definition cannot change its variable, so if VAR has a
+	 partitioned default definition, its partition is the one that
+	 keeps the user decl.  Otherwise the first partition seen does.  */
+      tree ddef = ssa_default_def (cfun, var);
+      int keep = ddef ? var_to_partition (map, ddef) : NO_PARTITION;
+      if (keep == NO_PARTITION)
+	{
+	  if (!seen.add (var))
+	    continue;
+	}
+      else if (keep >= 0 && (unsigned) keep == i)
+	continue;
+
+      tree nvar = create_tmp_var_raw (TREE_TYPE (var));
+      DECL_CONTEXT (nvar) = DECL_CONTEXT (var);
+      DECL_SOURCE_LOCATION (nvar) = DECL_SOURCE_LOCATION (var);
+      SET_DECL_ALIGN (nvar, DECL_ALIGN (var));
+      if (!DECL_ARTIFICIAL (var) && DECL_NAME (var))
+	{
+	  SET_DECL_DEBUG_EXPR (nvar, var);
+	  DECL_HAS_DEBUG_EXPR_P (nvar) = 1;
+	}
+      copy_warning (nvar, var);
+      add_local_decl (cfun, nvar);
+      new_decl[i] = nvar;
+      any = true;
+    }
+
+  if (!any)
+    return;
+
+  unsigned ver;
+  tree name;
+  FOR_EACH_SSA_NAME (ver, name, cfun)
+    {
+      if (SSA_NAME_IS_DEFAULT_DEF (name))
+	continue;
+      int p = var_to_partition (map, name);
+      if (p != NO_PARTITION && new_decl[p])
+	SET_SSA_NAME_VAR_OR_IDENTIFIER (name, new_decl[p]);
+    }
+}
+
 /* Remove the ssa-names in the current function and translate them into normal
    compiler variables.  PERFORM_TER is true if Temporary Expression Replacement
    should also be used.  */
@@ -1080,6 +1161,11 @@ remove_ssa_form (bool perform_ter, struct ssaexpand *sa)
 	dump_replaceable_exprs (dump_file, values);
     }
 
+  /* Distinct partitions of one decl must not share a MEM_EXPR once they are
+     spilled to separate stack slots.  Done after TER so reassigning
+     SSA_NAME_VAR does not perturb find_replaceable_exprs.  */
+  split_overlapping_partition_decls (map);
+
   rewrite_trees (map);
 
   sa->map = map;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.