https://gcc.gnu.org/bugzilla/show_bug.cgi?id=127716
Bug ID: 127716
Summary: CRC verification counts debug uses outside the loop,
so -g prevents loop replacement
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: tree-optimization
Assignee: unassigned at gcc dot gnu.org
Reporter: yqtian668 at gmail dot com
Target Milestone: ---
Host: x86_64-pc-linux-gnu
Target: x86_64-pc-linux-gnu
Build: GCC 17.0.0 20261001 (experimental),
a9b19ded78ca2dd8f66b355b05ebbbb149c2d320
Adding debug information prevents GCC's CRC optimization from replacing a
bit-by-bit CRC loop. The same valid C input is transformed into a table-based
CRC implementation with -g0, but the original loop remains with -g.
I reproduced this on x86_64-pc-linux-gnu with GCC 17.0.0 20261001
(experimental), revision
a9b19ded78ca2dd8f66b355b05ebbbb149c2d320. The relevant
gcc/crc-verification.cc file is unchanged in the later tracked revision
d27e275b54ca0eb44ad136bc9f0488b846355e19.
Reproducer:
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;
}
Command:
gcc -O2 -g -Wall -Wextra -Werror -fcompare-debug -S source.c
Actual result:
gcc: error: source.c: '-fcompare-debug' failure (length)
The same failure occurs at -O3. Without -fcompare-debug, the difference can be
seen directly with:
gcc -O2 -g -fdump-tree-crc-details -S source.c -o g.s
gcc -O2 -g0 -fdump-tree-crc-details -S source.c -o g0.s
The -g CRC dump reports:
Defined variable is used outside the loop.
and g.s retains the 16-iteration shift/XOR loop. The -g0 dump reports that the
loop calculates CRC, and g0.s uses the table-based replacement. The resulting
.text sections differ.
Expected result:
Debug information should not affect whether the CRC loop is recognized and
replaced. The -g and -g0 compilations should make the same code-generation
decision.
Cause analysis:
crc_symbolic_execution::is_used_outside_the_loop() in
gcc/crc-verification.cc iterates FOR_EACH_IMM_USE_STMT for each definition in
the candidate loop. It does not skip debug statements before testing whether
the use's basic block is outside the loop. Loop-closed SSA leaves debug uses
alone, so the debug bind for last refers to the in-loop value of sh from the
exit block. CRC verification therefore treats the debug-only use as an
ordinary use outside the loop and rejects the candidate.
As a causal check, skipping is_gimple_debug(use_stmt) in this scan makes
-fcompare-debug pass at -O2 and -O3. With that diagnostic change, the -g and
-g0 .text and .rodata sections are identical, while the unmodified -g0 output
is unchanged. A focused sweep of the 128 existing tests that request CRC dumps
(768 compilations) also produced unchanged dumps and assembly, with 58 CRC
replacements before and after. A full bootstrap and regression test has not
yet been run.