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
>

Reply via email to