On Wed, Oct 21, 2026 at 2:36 PM Yongqiang Tian <[email protected]> wrote:
>
> 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);
> +}
The vectorizer has exactly similar code in
tree-vect-loop-manip.cc:vect_do_peeling
around line 3520:
/* Before doing any peeling make sure to reset debug binds outside of
the loop referring to defs not in LC SSA. */
class loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
for (unsigned i = 0; i < loop->num_nodes; ++i)
{
...
can you split that variant out into a helper function in
tree-ssa-loop-manip.{cc,h}
getting a loop *, basic_block * and unsigned num_nodes argument?
Thanks,
Richard.
> +
> /* 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
>