On Mon, Nov 24, 2025 at 9:42 AM Andrew Pinski <[email protected]> wrote: > > 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 expect before and I was > being super cautious when it comes having them match but in reality > the difference in CLOBBERs don't matter.
OK. Richard. > 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]> > --- > 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(-) > create mode 100644 gcc/testsuite/g++.dg/tree-ssa/simple-dse-1.C > create mode 100644 gcc/testsuite/g++.dg/tree-ssa/simple-dse-2.C > create mode 100644 gcc/testsuite/g++.dg/tree-ssa/simple-dse-3.C > create mode 100644 gcc/testsuite/g++.dg/tree-ssa/simple-dse-4.C > > 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 00000000000..20db9d49414 > --- /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 00000000000..a4a5a9ff51e > --- /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 00000000000..dc31eff3be3 > --- /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 00000000000..1b9ad076134 > --- /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 052d1740491..a744e224d99 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) > -- > 2.43.0 >
