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.

Reply via email to