https://gcc.gnu.org/bugzilla/show_bug.cgi?id=128090
Bug ID: 128090
Summary: [ARM] -g -fno-var-tracking changes forced minipool
barrier placement
Product: gcc
Version: 17.0
Status: UNCONFIRMED
Keywords: compare-debug-failure
Severity: normal
Priority: P3
Component: target
Assignee: unassigned at gcc dot gnu.org
Reporter: yqtian668 at gmail dot com
Target Milestone: ---
Host: x86_64-pc-linux-gnu
Target: arm-none-eabi
Build: GCC 17.0.0 20261022 (experimental), revision
1bfdd3ecb3d7109d059212491a934eadf8cb33c2
Created attachment 65924
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=65924&action=edit
Candidate fix and regression test
On `arm-none-eabi`, adding debug information changes where GCC places forced
literal-pool barriers. This reproduces on GCC master
`1bfdd3ecb3d7109d059212491a934eadf8cb33c2`.
Reduced reproducer:
```c
extern volatile double vd;
#define S(k) if (p[i + k] == k) { vd = k##.75; b = b; } l##k: b = b;
#define S10(k) S(k##0) S(k##1) S(k##2) S(k##3) S(k##4) \
S(k##5) S(k##6) S(k##7) S(k##8) S(k##9)
void
f (int *p, int b)
{
for (int i = 0; i < b; i++)
{
S10(1) S10(2) S10(3) S10(4) S10(5) S10(6)
}
}
```
Command:
```text
arm-none-eabi-gcc -O2 -g -fno-var-tracking -fcompare-debug \
-mthumb -march=armv7-a -S source.c
```
Actual result:
```text
arm-none-eabi-gcc: error: source.c: '-fcompare-debug' failure
```
The first ordinary-code difference appears during the `mach` pass, when the
Arm backend places its minipools. After debug-only assembler directives are
removed, four pool-skipping branches move across an `ldr`/`cmp`/`bne`
sequence. One of the differences has this form:
```diff
- b .L150
ldr r3, [r0, #196]
cmp r3, #60
bne .L53
+ b .L150
```
The observed difference is in code layout; I have not found a wrong-code
effect. The problem is that debug information changes an ordinary
code-generation decision and makes `-fcompare-debug` fail.
The apparent cause is `barrier_cost` in `gcc/config/arm/minipool.cc`.
`minipool::create_fix_barrier` prefers an instruction immediately followed by
a label because that is a cheaper place to insert the branch around a literal
pool. `barrier_cost` currently finds the following instruction with
`next_nonnote_insn`, which does not skip debug instructions.
With `-g -fno-var-tracking`, a non-executing statement-frontier debug
instruction can remain between an ordinary instruction and its following
label until machine reorg. It hides the label from `barrier_cost`, raising
that position's cost from 30 to 50. A later position is then selected and the
pool-skipping branch moves.
A candidate fix uses `next_nonnote_nondebug_insn` in `barrier_cost`. The
reduced test fails before this one-line change and passes afterwards. A full
`arm.exp` before/after comparison changed only the new test from failure to
pass. A broader parallel `check-gcc` differential run likewise found no
other changed failure or unresolved outcome after normalizing one generated
temporary pathname. That broader run used a container without
`arm-none-eabi-as` and `arm-none-eabi-ld`, so it had extensive baseline
environment failures; a clean ARM cross regression run with the target
binutils is still pending.
I also considered skipping debug instructions as candidate insertion points
inside `create_fix_barrier`, but found no independent trigger for that change.
A temporary instrumented compiler and approximately 7,700 compilations of the
Arm target tests at `-O2` and `-Os` never selected a debug instruction as the
final insertion point. The attached candidate patch therefore contains only
the root-cause change above and its regression test.
Expected result: non-executing debug instructions should not affect the
minipool barrier cost or the generated ordinary instruction sequence.
I searched Bugzilla for `minipool debug`, `barrier_cost`,
`create_fix_barrier`, and `minipool compare-debug`, and did not find a matching
report. PR127988 and PR127227 concern different minipool problems.
This issue was found by a non-AI automated testing tool. AI assisted with the
analysis and development of the candidate fix.