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)
