This patch adds an optional parameter to simple_dce_from_worklist to
replace statements with an assgnement to zero instead of removing them
from the IL.
THis helps cases like early builtin_unreachable removal which depends on
removing dead statements so that eliminated ssa-name uses expose
additional opportunities. The PR demonstrates a case which the
SSA_NAME definition, when removed, can cause other issues when
simplification is being used. Replacing these statements with an
assignment to zero effectively removed any SSA_NAMEs from the RHS if the
statement is dead and allows forward progress.
As there are no uses of these statements, they will be eliminated by
the next DCE pass (or sooner), but the ssa defs will remain for the
duration of this pass at least and resolve the issue.
Bootstrapped on x86_64-pc-linux-gnu with no regressions. Pushed.
Andrew
From e02ce3b8c4574c81a943eb1084b0e0bf6a9955d5 Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <[email protected]>
Date: Tue, 4 Aug 2026 12:13:15 -0400
Subject: [PATCH 2/4] Provide a DCE no-delete option.
Add a flag which replaces statement with assignment to zero instead of
removing them.
PR tree-optimization/126329
gcc/
* tree-ssa-dce.cc (simple_dce_from_worklist): Add no-delete option.
* tree-ssa-dce.h (simple_dce_from_worklist): Add no delete param.
* tree-vrp.cc (remove_unreachable::handle_early): Use no delete option.
gcc/testsuite/
* gcc.dg/pr126329.c: New.
---
gcc/testsuite/gcc.dg/pr126329.c | 28 +++++++++++++++++
gcc/tree-ssa-dce.cc | 54 +++++++++++++++++++++++++++------
gcc/tree-ssa-dce.h | 2 +-
gcc/tree-vrp.cc | 2 +-
4 files changed, 74 insertions(+), 12 deletions(-)
create mode 100644 gcc/testsuite/gcc.dg/pr126329.c
diff --git a/gcc/testsuite/gcc.dg/pr126329.c b/gcc/testsuite/gcc.dg/pr126329.c
new file mode 100644
index 00000000000..50165c574e5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr126329.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+#include <stdint.h>
+int32_t g30;
+void f25(int32_t a1)
+{
+ int a2;
+ _Bool c6, c14;
+ int8_t v8;
+ int64_t v15;
+ int8_t v16;
+ if (a1 == 8098) goto lbl_sw11;
+ a2 = __builtin_ffs(a1);
+lbl_b8:
+ v8 = a2;
+ c6 = v8 >= v16;
+ goto lbl_br26;
+lbl_sw11:
+lbl_br17:
+ goto lbl_b8;
+lbl_br26:
+ g30 = v8;
+ if (c6) __builtin_unreachable();
+ c6 = c14 = a1;
+ a1 = v8;
+ if (v15) goto lbl_br26;
+ if (c14) goto lbl_br17;
+}
diff --git a/gcc/tree-ssa-dce.cc b/gcc/tree-ssa-dce.cc
index 548d5d4bd66..bf818ad2c91 100644
--- a/gcc/tree-ssa-dce.cc
+++ b/gcc/tree-ssa-dce.cc
@@ -2170,10 +2170,16 @@ make_pass_cd_dce (gcc::context *ctxt)
/* A cheap DCE interface. WORKLIST is a list of possibly dead stmts and
is consumed by this function. The function has linear complexity in
the number of dead stmts with a constant factor like the average SSA
- use operands number. */
+ use operands number.
+ If no_delete is true (defaults to false) then rather than deleting the
+ statement, it is replaced with an assignment to 0. This allows the
+ same elimination of statement dependencies, but delays the actual statement
+ removal from the IL until the next time DCE is run and they are detected
+ as dead statements with no uses. */
void
-simple_dce_from_worklist (bitmap worklist, bitmap need_eh_cleanup)
+simple_dce_from_worklist (bitmap worklist, bitmap need_eh_cleanup,
+ bool no_delete)
{
int phiremoved = 0;
int stmtremoved = 0;
@@ -2217,11 +2223,20 @@ simple_dce_from_worklist (bitmap worklist, bitmap need_eh_cleanup)
gimple *t = SSA_NAME_DEF_STMT (def);
if (gimple_has_side_effects (t))
{
- if (gcall *call = dyn_cast <gcall *> (t))
+ gcall *call = dyn_cast <gcall *> (t);
+ if (call)
{
gimple_call_set_lhs (call, NULL_TREE);
update_stmt (call);
- release_ssa_name (def);
+ if (no_delete)
+ {
+ tree zero = build_zero_cst (TREE_TYPE (def));
+ gassign *new_stmt = gimple_build_assign (def, zero);
+ gimple_stmt_iterator gsi = gsi_for_stmt (t);
+ gsi_insert_after (&gsi, new_stmt, GSI_SAME_STMT);
+ }
+ else
+ release_ssa_name (def);
}
continue;
}
@@ -2263,19 +2278,38 @@ simple_dce_from_worklist (bitmap worklist, bitmap need_eh_cleanup)
gimple_stmt_iterator gsi = gsi_for_stmt (t);
if (gimple_code (t) == GIMPLE_PHI)
{
- remove_phi_node (&gsi, true);
+ if (no_delete)
+ {
+ gphi *phi = as_a<gphi *> (t);
+ tree zero = build_zero_cst (TREE_TYPE (def));
+ for (unsigned i = 0; i < gimple_phi_num_args (phi); ++i)
+ SET_PHI_ARG_DEF (phi, i, zero);
+ update_stmt (phi);
+ }
+ else
+ remove_phi_node (&gsi, true);
+
phiremoved++;
}
else
{
unlink_stmt_vdef (t);
- gsi_remove (&gsi, true);
- release_defs (t);
+ if (no_delete)
+ {
+ tree zero = build_zero_cst (TREE_TYPE (def));
+ gassign *new_stmt = gimple_build_assign (def, zero);
+ gsi_replace (&gsi, new_stmt, true);
+ }
+ else
+ {
+ gsi_remove (&gsi, true);
+ release_defs (t);
+ }
stmtremoved++;
}
}
- statistics_counter_event (cfun, "PHIs removed",
+ statistics_counter_event (cfun, no_delete ? "PHIs rewritten" : "PHIs removed",
phiremoved);
- statistics_counter_event (cfun, "Statements removed",
- stmtremoved);
+ statistics_counter_event (cfun, no_delete ? "Statements rewritten"
+ : "Statements removed", stmtremoved);
}
diff --git a/gcc/tree-ssa-dce.h b/gcc/tree-ssa-dce.h
index dd874479fb5..d97e5e7c3b1 100644
--- a/gcc/tree-ssa-dce.h
+++ b/gcc/tree-ssa-dce.h
@@ -18,5 +18,5 @@ along with GCC; see the file COPYING3. If not see
#ifndef TREE_SSA_DCE_H
#define TREE_SSA_DCE_H
-extern void simple_dce_from_worklist (bitmap, bitmap = nullptr);
+extern void simple_dce_from_worklist (bitmap, bitmap = nullptr, bool no_delete = false);
#endif
diff --git a/gcc/tree-vrp.cc b/gcc/tree-vrp.cc
index da65d67b405..6f1e3eb9035 100644
--- a/gcc/tree-vrp.cc
+++ b/gcc/tree-vrp.cc
@@ -248,7 +248,7 @@ remove_unreachable::handle_early (gimple *s, edge e)
{
auto_bitmap dce;
bitmap_set_bit (dce, SSA_NAME_VERSION (ssa));
- simple_dce_from_worklist (dce);
+ simple_dce_from_worklist (dce, nullptr, true);
}
}
--
2.45.0