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]>
---
Thanks for the suggestion. This version factors the existing vectorizer
logic into a common helper and reuses it for the CRC transformation.
Changes in v2:
- Factor the vectorizer's existing outside-loop debug-bind reset walk into
reset_debug_uses_outside_loop in tree-ssa-loop-manip.{cc,h}, as suggested by
Richard.
- Reuse the helper from both vect_do_peeling and the CRC transformation.
- Rerun the focused test, bootstrap, and full regression tests.
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(-)
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..ec985148e 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 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" } } */
diff --git a/gcc/tree-ssa-loop-manip.cc b/gcc/tree-ssa-loop-manip.cc
index 7e1a183a1..f1e381e7d 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 521aac2db..aaa625e65 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 5e875d88f..a127fe4d1 100644
--- a/gcc/tree-vect-loop-manip.cc
+++ b/gcc/tree-vect-loop-manip.cc
@@ -3522,41 +3522,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);
--
2.34.1