CRC verification treats every outside-loop use of an in-loop definition as
an observable use. This includes debug binds, so adding debug information
can prevent an otherwise valid CRC loop from being recognized and replaced.
Ignore debug statements when checking for outside-loop uses. Ignoring them
alone is not sufficient, however: after the loop is replaced, an outside-loop
debug bind may still refer to an SSA value whose original final-iteration
value no longer exists. Such a bind can make the debugger display an
incorrect value.
Before replacing the loop, reset outside-loop debug binds that use values
defined inside it. Keep debug binds inside the loop unchanged because the
transformed loop body still executes once and those binds may remain valid.
Add a regression test that verifies both CRC recognition and that the
unrecoverable outside-loop value is marked unavailable.
Bootstrapped and regression tested on x86_64-pc-linux-gnu. The GCC, G++,
and libstdc++ testsuites showed no patch-induced regressions. The new test
and the existing gcc.dg CRC tests pass.
Assisted-by: OpenAI Codex 5.6 Sol, co-reviewed by Claude and Yongqiang Tian.
PR tree-optimization/127716
gcc/ChangeLog:
* crc-verification.cc
(crc_symbolic_execution::is_used_outside_the_loop): Ignore debug
statements when checking for outside-loop uses.
* gimple-crc-optimization.cc
(reset_outside_debug_uses_for_stmt): New function.
(reset_outside_debug_uses): New function.
(crc_optimization::optimize_crc_loop): Reset outside-loop debug binds
before replacing the loop.
gcc/testsuite/ChangeLog:
* gcc.dg/pr127716.c: New test.
Signed-off-by: Yongqiang Tian <[email protected]>
---
gcc/crc-verification.cc | 4 +++
gcc/gimple-crc-optimization.cc | 51 +++++++++++++++++++++++++++++++++
gcc/testsuite/gcc.dg/pr127716.c | 30 +++++++++++++++++++
3 files changed, 85 insertions(+)
create mode 100644 gcc/testsuite/gcc.dg/pr127716.c
diff --git a/gcc/crc-verification.cc b/gcc/crc-verification.cc
index ead4b8136..ffc93d0df 100644
--- a/gcc/crc-verification.cc
+++ b/gcc/crc-verification.cc
@@ -45,6 +45,10 @@ crc_symbolic_execution::is_used_outside_the_loop (tree def)
gimple *use_stmt;
FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, def)
{
+ /* Debug uses must not affect whether the loop is recognized. Stale
+ outside-loop binds are reset if the loop is transformed. */
+ if (is_gimple_debug (use_stmt))
+ continue;
if (!flow_bb_inside_loop_p (m_crc_loop, use_stmt->bb))
{
if (is_a<gphi *> (use_stmt)
diff --git a/gcc/gimple-crc-optimization.cc b/gcc/gimple-crc-optimization.cc
index 81ea0a381..19b105ff0 100644
--- a/gcc/gimple-crc-optimization.cc
+++ b/gcc/gimple-crc-optimization.cc
@@ -1218,6 +1218,55 @@ crc_optimization::get_output_phi ()
return nullptr;
}
+/* Reset debug binds outside LOOP that use values defined by STMT. */
+
+static void
+reset_outside_debug_uses_for_stmt (gimple *stmt, class loop *loop)
+{
+ ssa_op_iter op_iter;
+ def_operand_p def_p;
+ FOR_EACH_PHI_OR_STMT_DEF (def_p, stmt, op_iter, SSA_OP_DEF)
+ {
+ tree def = DEF_FROM_PTR (def_p);
+ if (TREE_CODE (def) != SSA_NAME)
+ continue;
+
+ imm_use_iterator imm_iter;
+ gimple *use_stmt;
+ FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, def)
+ {
+ if (!gimple_debug_bind_p (use_stmt)
+ || flow_bb_inside_loop_p (loop, gimple_bb (use_stmt)))
+ continue;
+ gimple_debug_bind_reset_value (use_stmt);
+ update_stmt (use_stmt);
+ }
+ }
+}
+
+/* Reset debug binds outside LOOP that use values defined inside LOOP. These
+ values can no longer be reconstructed after the loop is replaced. */
+
+static void
+reset_outside_debug_uses (class loop *loop)
+{
+ if (!MAY_HAVE_DEBUG_BIND_STMTS)
+ return;
+
+ basic_block *bbs = get_loop_body (loop);
+ for (unsigned i = 0; i < loop->num_nodes; ++i)
+ {
+ basic_block bb = bbs[i];
+ for (gphi_iterator gpi = gsi_start_phis (bb); !gsi_end_p (gpi);
+ gsi_next (&gpi))
+ reset_outside_debug_uses_for_stmt (gpi.phi (), loop);
+ for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
+ gsi_next (&gsi))
+ reset_outside_debug_uses_for_stmt (gsi_stmt (gsi), loop);
+ }
+ free (bbs);
+}
+
/* Attempts to optimize a CRC calculation loop by replacing it with a call to
an internal function (IFN_CRC or IFN_CRC_REV).
Returns true if replacement succeeded, otherwise false. */
@@ -1274,6 +1323,8 @@ crc_optimization::optimize_crc_loop (class loop *loop,
gphi *output_crc)
return false;
}
+ reset_outside_debug_uses (loop);
+
/* Add IFN call and write the return value in the phi_result. */
gcall *call = gimple_build_call_internal (ifn, 3, m_crc_arg, m_data_arg,
polynomial_arg);
diff --git a/gcc/testsuite/gcc.dg/pr127716.c b/gcc/testsuite/gcc.dg/pr127716.c
new file mode 100644
index 000000000..c7d3de54c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr127716.c
@@ -0,0 +1,30 @@
+/* PR tree-optimization/127716 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -g -fcompare-debug -fdump-tree-crc-details" } */
+
+typedef unsigned short u16;
+
+#define POLY 0x1021
+
+u16
+crc16 (u16 data)
+{
+ int i = 0;
+ u16 sh;
+
+ do
+ {
+ sh = data << 1;
+ if (data & 0x8000)
+ data = sh ^ POLY;
+ else
+ data = sh;
+ }
+ while (++i < 16);
+
+ u16 last __attribute__ ((unused)) = sh;
+ return data;
+}
+
+/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */
+/* { dg-final { scan-tree-dump "# DEBUG last => NULL" "crc" } } */
--
2.34.1