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
>

Reply via email to