]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
tree-optimization/121220 - improve sinking of stores
authorRichard Biener <rguenther@suse.de>
Wed, 23 Jul 2025 07:40:24 +0000 (09:40 +0200)
committerRichard Biener <rguenth@gcc.gnu.org>
Wed, 23 Jul 2025 10:46:22 +0000 (12:46 +0200)
We currently do only very restricted store sinking into paths
that have no loads or stores and end in a virtual PHI.  The
following extends this to sink towards a single virtual
definition in addition to the case of a PHI, handling skipping
of unrelated virtual uses.  We later have to prune cases
that would require virtual PHI insertion and the patch below
basically restricts this to sinking to noreturn paths for now.

PR tree-optimization/121220
* tree-ssa-sink.cc (statement_sink_location): For stores
handle sinking to paths ending in a store.  Skip loads
that do not use the store.

* gcc.dg/tree-ssa/ssa-sink-23.c: New testcase.

gcc/testsuite/gcc.dg/tree-ssa/ssa-sink-23.c [new file with mode: 0644]
gcc/tree-ssa-sink.cc

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-sink-23.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-sink-23.c
new file mode 100644 (file)
index 0000000..f632dc8
--- /dev/null
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-sink1-details" } */
+
+struct S {
+    int* x;
+    int* y;
+};
+
+void __attribute__((noreturn)) bar(const struct S* s);
+
+void foo(int a, int b) {
+    struct S s;
+    s.x = &a;
+    s.y = &b;
+    if (a < b) {
+        bar(&s);
+    }
+}
+
+/* { dg-final { scan-tree-dump "Sinking.*s.y" "sink1" } } */
+/* { dg-final { scan-tree-dump "Sinking.*s.x" "sink1" } } */
index 959e0d5c6bea18ff6ef1eea74bb579dda66f000b..2244e89fbb7fa1fce6d3c883d80fa8b66e1cd484 100644 (file)
@@ -356,37 +356,54 @@ statement_sink_location (gimple *stmt, basic_block frombb,
 
   use = NULL;
 
-  /* If stmt is a store the one and only use needs to be the VOP
-     merging PHI node.  */
+  /* If stmt is a store the one and only use needs to be a VUSE on
+     the live path.  */
   if (virtual_operand_p (DEF_FROM_PTR (def_p)))
     {
+      tree lhs = gimple_get_lhs (stmt);
+      ao_ref ref;
+      ao_ref_init (&ref, lhs);
       FOR_EACH_IMM_USE_FAST (use_p, imm_iter, DEF_FROM_PTR (def_p))
        {
          gimple *use_stmt = USE_STMT (use_p);
 
          /* A killing definition is not a use.  */
-         if ((gimple_has_lhs (use_stmt)
-              && operand_equal_p (gimple_get_lhs (stmt),
-                                  gimple_get_lhs (use_stmt), 0))
-             || stmt_kills_ref_p (use_stmt, gimple_get_lhs (stmt)))
+         if (gimple_vdef (use_stmt)
+             && ((gimple_has_lhs (use_stmt)
+                  && operand_equal_p (lhs,
+                                      gimple_get_lhs (use_stmt), 0))
+                 || stmt_kills_ref_p (use_stmt, &ref)))
            {
              /* If use_stmt is or might be a nop assignment then USE_STMT
                 acts as a use as well as definition.  */
              if (stmt != use_stmt
-                 && ref_maybe_used_by_stmt_p (use_stmt,
-                                              gimple_get_lhs (stmt)))
-               return false;
+                 && ref_maybe_used_by_stmt_p (use_stmt, &ref))
+               {
+                 if (use && use != use_stmt)
+                   return false;
+                 use = use_stmt;
+               }
              continue;
            }
 
-         if (gimple_code (use_stmt) != GIMPLE_PHI)
-           return false;
-
-         if (use
-             && use != use_stmt)
-           return false;
+         if (is_a <gphi *> (use_stmt)
+             || ref_maybe_used_by_stmt_p (use_stmt, &ref))
+           {
+             if (use && use != use_stmt)
+               return false;
+             use = use_stmt;
+             continue;
+           }
 
-         use = use_stmt;
+         if (gimple_vdef (use_stmt))
+           {
+             if (stmt_may_clobber_ref_p_1 (use_stmt, &ref, false))
+               return false;
+             /* We do not look past VDEFs, so treat them as sink location.  */
+             if (use && use != use_stmt)
+               return false;
+             use = use_stmt;
+           }
        }
       if (!use)
        return false;
@@ -448,18 +465,26 @@ statement_sink_location (gimple *stmt, basic_block frombb,
          break;
        }
       use = USE_STMT (one_use);
+    }
 
-      if (gimple_code (use) != GIMPLE_PHI)
-       {
-         sinkbb = select_best_block (frombb, gimple_bb (use), stmt);
+  if (gimple_code (use) != GIMPLE_PHI)
+    {
+      sinkbb = select_best_block (frombb, gimple_bb (use), stmt);
 
-         if (sinkbb == frombb)
-           return false;
+      if (sinkbb == frombb)
+       return false;
 
-         *togsi = gsi_after_labels (sinkbb);
+      /* The SSA update for sinking of stores cannot insert PHIs, the
+        sink location has to lead to exit without crossing any CFG
+        merge points to paths not dominated by the sink location.  */
+      if (gimple_vdef (stmt)
+         && (!single_succ_p (sinkbb)
+             || single_succ (sinkbb)->index != EXIT_BLOCK))
+       return false;
 
-         return true;
-       }
+      *togsi = gsi_after_labels (sinkbb);
+
+      return true;
     }
 
   sinkbb = find_bb_for_arg (as_a <gphi *> (use), DEF_FROM_PTR (def_p));