https://gcc.gnu.org/g:73849aef07bd2954a80fbf1c8543d70db9beae6a

commit r16-5553-g73849aef07bd2954a80fbf1c8543d70db9beae6a
Author: Andrew Pinski <[email protected]>
Date:   Sun Nov 23 16:42:57 2025 -0800

    forwprop: Add call stmt support to simple dse [PR122633]
    
    This adds the ability to the simple dse to remove the lhs of a
    call. It can also remove a call if it was pure/const in some cases.
    
    On trampv3, I found this happened a few times during forwprop2, 3
    and 4. The one in 4 was a suprise and even more it caused a removal
    of a call which gcc was not able to remove before. This is due to
    the nature of DSE/DCE needs to be done iteratively together but
    we currently don't do that. So it just happens the late forwprop4's
    simple dse is able to remove this call.
    
    I will fix the xfail testcases in a followup, basically there exceptions
    can get a mismatch in the CLOBBER which I didn't except before and I was
    being super cautious when it comes having them match but in reality
    the difference in CLOBBERs don't matter.
    
    Bootstrapped and tested on x86_64-linux-gnu
    
            PR tree-optimization/122633
    gcc/ChangeLog:
    
            * tree-ssa-forwprop.cc (do_simple_agr_dse): Remove
            lhs of dead store for a call (or the whole call stmt).
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/tree-ssa/simple-dse-1.C: New test.
            * g++.dg/tree-ssa/simple-dse-2.C: New test.
            * g++.dg/tree-ssa/simple-dse-3.C: New test.
            * g++.dg/tree-ssa/simple-dse-4.C: New test.
    
    Signed-off-by: Andrew Pinski <[email protected]>

Diff:
---
 gcc/testsuite/g++.dg/tree-ssa/simple-dse-1.C | 28 +++++++++++++++
 gcc/testsuite/g++.dg/tree-ssa/simple-dse-2.C | 28 +++++++++++++++
 gcc/testsuite/g++.dg/tree-ssa/simple-dse-3.C | 28 +++++++++++++++
 gcc/testsuite/g++.dg/tree-ssa/simple-dse-4.C | 30 ++++++++++++++++
 gcc/tree-ssa-forwprop.cc                     | 53 ++++++++++++++++++++++++++--
 5 files changed, 164 insertions(+), 3 deletions(-)

diff --git a/gcc/testsuite/g++.dg/tree-ssa/simple-dse-1.C 
b/gcc/testsuite/g++.dg/tree-ssa/simple-dse-1.C
new file mode 100644
index 000000000000..20db9d49414d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/simple-dse-1.C
@@ -0,0 +1,28 @@
+// { dg-do compile }
+// { dg-options "-O2 -fno-exceptions -fdump-tree-forwprop1-details" }
+// PR tree-optimization/122633
+
+struct s1
+{
+  int f1[4];
+  ~s1(){}
+};
+
+struct s1 func1(int a);
+void func2(int a)
+{
+  struct s1 v1 = func1(a);
+}
+
+__attribute__((pure))
+struct s1 pure1(int a);
+void func3(int a)
+{
+  struct s1 p1 = pure1(a);
+}
+
+// { dg-final { scan-tree-dump "Removing dead call store stmt p1 =" 
"forwprop1" } }
+// { dg-final { scan-tree-dump-not "Removing dead call store stmt v1 =" 
"forwprop1" } }
+// { dg-final { scan-tree-dump-not "Removing lhs of call stmt " "forwprop1" } }
+// { dg-final { scan-tree-dump-times "Removing dead call store stmt" 1 
"forwprop1" } }
+
diff --git a/gcc/testsuite/g++.dg/tree-ssa/simple-dse-2.C 
b/gcc/testsuite/g++.dg/tree-ssa/simple-dse-2.C
new file mode 100644
index 000000000000..a4a5a9ff51e9
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/simple-dse-2.C
@@ -0,0 +1,28 @@
+// { dg-do compile }
+// { dg-options "-O2 -fno-exceptions -fdump-tree-forwprop1-details" }
+// PR tree-optimization/122633
+struct s1
+{
+  int f1[4];
+};
+
+struct s1 func1(int a);
+void func2(int a)
+{
+  struct s1 v1 = func1(a);
+}
+
+__attribute__((pure))
+struct s1 pure1(int a);
+void func3(int a)
+{
+  struct s1 p1 = pure1(a);
+}
+
+// { dg-final { scan-tree-dump-not "Removing lhs of call stmt p1 =" 
"forwprop1" } }
+// { dg-final { scan-tree-dump-not "Removing dead call store stmt v1 =" 
"forwprop1" } }
+// { dg-final { scan-tree-dump "Removing lhs of call stmt v1 =" "forwprop1" } }
+// { dg-final { scan-tree-dump "Removing dead call store stmt p1 =" 
"forwprop1" } }
+// { dg-final { scan-tree-dump-times "Removing dead call store stmt" 1 
"forwprop1" } }
+// { dg-final { scan-tree-dump-times "Removing lhs of call stmt" 1 "forwprop1" 
} }
+
diff --git a/gcc/testsuite/g++.dg/tree-ssa/simple-dse-3.C 
b/gcc/testsuite/g++.dg/tree-ssa/simple-dse-3.C
new file mode 100644
index 000000000000..dc31eff3be3b
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/simple-dse-3.C
@@ -0,0 +1,28 @@
+// { dg-do compile }
+// { dg-options "-O2 -fexceptions -fdump-tree-forwprop1-details" }
+// PR tree-optimization/122633
+struct s1
+{
+  int f1[4];
+  ~s1(){}
+};
+
+struct s1 func1(int a);
+void func2(int a)
+{
+  struct s1 v1 = func1(a);
+}
+
+__attribute__((pure))
+struct s1 pure1(int a);
+void func3(int a)
+{
+  struct s1 p1 = pure1(a);
+}
+
+// { dg-final { scan-tree-dump "Removing dead call store stmt p1 =" 
"forwprop1" { xfail *-*-* } } }
+// { dg-final { scan-tree-dump-not "Removing dead call store stmt v1 =" 
"forwprop1" } }
+// { dg-final { scan-tree-dump-not "Removing lhs of call stmt " "forwprop1" } }
+// { dg-final { scan-tree-dump-times "Removing dead call store stmt" 1 
"forwprop1" { target { ! c++26 }  xfail *-*-* } } }
+// { dg-final { scan-tree-dump-times "Removing dead call store stmt" 2 
"forwprop1" { target c++26  xfail *-*-* } } }
+
diff --git a/gcc/testsuite/g++.dg/tree-ssa/simple-dse-4.C 
b/gcc/testsuite/g++.dg/tree-ssa/simple-dse-4.C
new file mode 100644
index 000000000000..1b9ad0761341
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tree-ssa/simple-dse-4.C
@@ -0,0 +1,30 @@
+// { dg-do compile }
+// { dg-options "-O2 -fexceptions -fdump-tree-forwprop1-details" }
+// PR tree-optimization/122633
+struct s1
+{
+  int f1[4];
+};
+
+struct s1 func1(int a);
+void func2(int a)
+{
+  struct s1 v1 = func1(a);
+}
+
+__attribute__((pure))
+struct s1 pure1(int a);
+void func3(int a)
+{
+  struct s1 p1 = pure1(a);
+}
+
+// { dg-final { scan-tree-dump-not "Removing lhs of call stmt p1 =" 
"forwprop1" } }
+// { dg-final { scan-tree-dump-not "Removing dead call store stmt v1 = func1" 
"forwprop1" } }
+// { dg-final { scan-tree-dump "Removing lhs of call stmt v1 =" "forwprop1" } }
+// { dg-final { scan-tree-dump "Removing dead call store stmt p1 =" 
"forwprop1"  } }
+// v1 has an DEFERRED_INIT  associated with it (due to exceptions)
+// { dg-final { scan-tree-dump-times "Removing dead call store stmt" 1 
"forwprop1" { target { ! c++26 } } } }
+// { dg-final { scan-tree-dump-times "Removing dead call store stmt" 2 
"forwprop1" { target c++26 } } }
+// { dg-final { scan-tree-dump-times "Removing lhs of call stmt" 1 "forwprop1" 
} }
+
diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
index 052d17404914..a744e224d995 100644
--- a/gcc/tree-ssa-forwprop.cc
+++ b/gcc/tree-ssa-forwprop.cc
@@ -1842,10 +1842,57 @@ do_simple_agr_dse (gassign *stmt, bool full_walk)
            return;
        }
       vuse = gimple_vuse (ostmt);
-
+      /* This is a call with an assignment to the clobber decl,
+        remove the lhs or the whole stmt if it was pure/const. */
+      if (is_a <gcall*>(ostmt)
+         && lhs == gimple_call_lhs (ostmt))
+       {
+         /* Don't remove stores/statements that are needed for non-call
+             eh to work.  */
+         if (stmt_unremovable_because_of_non_call_eh_p (cfun, ostmt))
+           return;
+         /* If we delete a stmt that could throw, mark the block
+            in to_purge to cleanup afterwards.  */
+         if (stmt_could_throw_p (cfun, ostmt))
+           bitmap_set_bit (to_purge, obb->index);
+         int flags = gimple_call_flags (ostmt);
+         if ((flags & (ECF_PURE|ECF_CONST|ECF_NOVOPS))
+             && !(flags & (ECF_LOOPING_CONST_OR_PURE)))
+           {
+              gimple_stmt_iterator gsi = gsi_for_stmt (ostmt);
+              if (dump_file && (dump_flags & TDF_DETAILS))
+               {
+                 fprintf (dump_file, "Removing dead call store stmt ");
+                 print_gimple_stmt (dump_file, ostmt, 0);
+                 fprintf (dump_file, "\n");
+               }
+             unlink_stmt_vdef (ostmt);
+             release_defs (ostmt);
+             gsi_remove (&gsi, true);
+             statistics_counter_event (cfun, "delete call dead store", 1);
+             /* Only remove the first store previous statement. */
+             return;
+           }
+         /* Make sure we do not remove a return slot we cannot reconstruct
+            later.  */
+         if (gimple_call_return_slot_opt_p (as_a <gcall *>(ostmt))
+             && (TREE_ADDRESSABLE (TREE_TYPE (gimple_call_fntype (ostmt)))
+                 || !poly_int_tree_p
+                     (TYPE_SIZE (TREE_TYPE (gimple_call_fntype (ostmt))))))
+           return;
+         if (dump_file && (dump_flags & TDF_DETAILS))
+           {
+             fprintf (dump_file, "Removing lhs of call stmt ");
+             print_gimple_stmt (dump_file, ostmt, 0);
+             fprintf (dump_file, "\n");
+           }
+         gimple_call_set_lhs (ostmt, NULL_TREE);
+         update_stmt (ostmt);
+         statistics_counter_event (cfun, "removed lhs call", 1);
+         return;
+       }
       /* This an assignment store to the clobbered decl,
-        then maybe remove it. A call is not handled here as
-        the rhs will not make a difference for SRA. */
+        then maybe remove it. */
       if (is_a <gassign*>(ostmt)
          && gimple_store_p (ostmt)
          && !gimple_clobber_p (ostmt)

Reply via email to