https://gcc.gnu.org/g:40df8ff06970e783b424299be509115b574d9f13
commit r17-5579-g40df8ff06970e783b424299be509115b574d9f13 Author: Yongqiang Tian <[email protected]> Date: Thu Oct 22 11:22:36 2026 +1100 tree-optimization: Preserve debug correctness in CRC loop replacement [PR127716] 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. The vectorizer already performs the same reset before peeling a loop, so factor that walk into reset_debug_uses_outside_loop and use the common helper from both transformations. 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 (crc_optimization::optimize_crc_loop): Reset outside-loop debug binds before replacing the loop. * tree-ssa-loop-manip.cc (reset_debug_uses_outside_loop): New function, factored from vect_do_peeling. * tree-ssa-loop-manip.h (reset_debug_uses_outside_loop): Declare. * tree-vect-loop-manip.cc (vect_do_peeling): Use it. gcc/testsuite/ChangeLog: * gcc.dg/pr127716.c: New test. Suggested-by: Richard Biener <[email protected]> Signed-off-by: Yongqiang Tian <[email protected]> Diff: --- gcc/crc-verification.cc | 4 ++++ gcc/gimple-crc-optimization.cc | 5 +++++ gcc/testsuite/gcc.dg/pr127716.c | 30 +++++++++++++++++++++++++++ gcc/tree-ssa-loop-manip.cc | 45 +++++++++++++++++++++++++++++++++++++++++ gcc/tree-ssa-loop-manip.h | 2 ++ gcc/tree-vect-loop-manip.cc | 37 ++------------------------------- 6 files changed, 88 insertions(+), 35 deletions(-) diff --git a/gcc/crc-verification.cc b/gcc/crc-verification.cc index ead4b81365cc..ffc93d0df463 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 81ea0a381190..ec985148e08a 100644 --- a/gcc/gimple-crc-optimization.cc +++ b/gcc/gimple-crc-optimization.cc @@ -31,6 +31,7 @@ along with GCC; see the file COPYING3. If not see #include "tree-cfg.h" #include "cfgloop.h" #include "tree-scalar-evolution.h" +#include "tree-ssa-loop-manip.h" #include "crc-verification.h" #include "internal-fn.h" #include "predict.h" @@ -1274,6 +1275,10 @@ crc_optimization::optimize_crc_loop (class loop *loop, gphi *output_crc) return false; } + basic_block *bbs = get_loop_body (loop); + reset_debug_uses_outside_loop (loop, bbs, loop->num_nodes); + free (bbs); + /* 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 000000000000..c7d3de54c686 --- /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" } } */ diff --git a/gcc/tree-ssa-loop-manip.cc b/gcc/tree-ssa-loop-manip.cc index 7e1a183a1785..f1e381e7d174 100644 --- a/gcc/tree-ssa-loop-manip.cc +++ b/gcc/tree-ssa-loop-manip.cc @@ -47,6 +47,51 @@ along with GCC; see the file COPYING3. If not see so that we can free them all at once. */ static bitmap_obstack loop_renamer_obstack; +/* Reset debug binds outside LOOP that use values defined in the NUM_NODES + basic blocks in BBS. */ + +void +reset_debug_uses_outside_loop (class loop *loop, basic_block *bbs, + unsigned num_nodes) +{ + for (unsigned i = 0; i < num_nodes; ++i) + { + basic_block bb = bbs[i]; + imm_use_iterator ui; + gimple *use_stmt; + for (gphi_iterator gpi = gsi_start_phis (bb); !gsi_end_p (gpi); + gsi_next (&gpi)) + { + FOR_EACH_IMM_USE_STMT (use_stmt, ui, + gimple_phi_result (gpi.phi ())) + if (gimple_debug_bind_p (use_stmt) + && loop != gimple_bb (use_stmt)->loop_father + && !flow_loop_nested_p (loop, + gimple_bb (use_stmt)->loop_father)) + { + gimple_debug_bind_reset_value (use_stmt); + update_stmt (use_stmt); + } + } + for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + ssa_op_iter op_iter; + def_operand_p def_p; + FOR_EACH_SSA_DEF_OPERAND (def_p, gsi_stmt (gsi), op_iter, SSA_OP_DEF) + FOR_EACH_IMM_USE_STMT (use_stmt, ui, DEF_FROM_PTR (def_p)) + if (gimple_debug_bind_p (use_stmt) + && loop != gimple_bb (use_stmt)->loop_father + && !flow_loop_nested_p (loop, + gimple_bb (use_stmt)->loop_father)) + { + gimple_debug_bind_reset_value (use_stmt); + update_stmt (use_stmt); + } + } + } +} + /* Insert IV increment statements STMTS before or after INCR_POS; AFTER selects which. INCR_POS and AFTER can be computed using standard_iv_increment_position. */ diff --git a/gcc/tree-ssa-loop-manip.h b/gcc/tree-ssa-loop-manip.h index 521aac2dbac1..aaa625e6550e 100644 --- a/gcc/tree-ssa-loop-manip.h +++ b/gcc/tree-ssa-loop-manip.h @@ -22,6 +22,8 @@ along with GCC; see the file COPYING3. If not see typedef void (*transform_callback)(class loop *, void *); +extern void reset_debug_uses_outside_loop (class loop *, basic_block *, + unsigned); extern void insert_iv_increment (gimple_stmt_iterator *, bool, gimple_seq); extern void create_iv (tree, tree_code, tree, tree, class loop *, gimple_stmt_iterator *, bool, tree *, tree *, diff --git a/gcc/tree-vect-loop-manip.cc b/gcc/tree-vect-loop-manip.cc index 4dfef69de876..56b2a557cde7 100644 --- a/gcc/tree-vect-loop-manip.cc +++ b/gcc/tree-vect-loop-manip.cc @@ -3513,41 +3513,8 @@ vect_do_peeling (loop_vec_info loop_vinfo, tree niters, tree nitersm1, /* 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) - { - basic_block bb = LOOP_VINFO_BBS (loop_vinfo)[i]; - imm_use_iterator ui; - gimple *use_stmt; - for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); - gsi_next (&gsi)) - { - FOR_EACH_IMM_USE_STMT (use_stmt, ui, gimple_phi_result (gsi.phi ())) - if (gimple_debug_bind_p (use_stmt) - && loop != gimple_bb (use_stmt)->loop_father - && !flow_loop_nested_p (loop, - gimple_bb (use_stmt)->loop_father)) - { - gimple_debug_bind_reset_value (use_stmt); - update_stmt (use_stmt); - } - } - for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); - gsi_next (&gsi)) - { - ssa_op_iter op_iter; - def_operand_p def_p; - FOR_EACH_SSA_DEF_OPERAND (def_p, gsi_stmt (gsi), op_iter, SSA_OP_DEF) - FOR_EACH_IMM_USE_STMT (use_stmt, ui, DEF_FROM_PTR (def_p)) - if (gimple_debug_bind_p (use_stmt) - && loop != gimple_bb (use_stmt)->loop_father - && !flow_loop_nested_p (loop, - gimple_bb (use_stmt)->loop_father)) - { - gimple_debug_bind_reset_value (use_stmt); - update_stmt (use_stmt); - } - } - } + reset_debug_uses_outside_loop (loop, LOOP_VINFO_BBS (loop_vinfo), + loop->num_nodes); prob_vector = profile_probability::guessed_always ().apply_scale (9, 10); estimated_vf = vect_vf_for_cost (loop_vinfo);
