[CodeGen] Debug instructions consume EarlyIfConversion's scan budget
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
With data-dependent early if-conversion enabled, compiling the same C source
with `-g0` and `-g` produces different AArch64 machine code. The `-g` build
contains 12 additional debug-only machine instructions before `early-ifcvt`.
They consume the pass's instruction scan budget and prevent a conversion that
succeeds in the `-g0` build.
This reproduces with an assertions-enabled LLVM 24.0.0git build at revision
`bc79eb547e6ef6a9bceef4968df0e0a150b96a8d` on x86-64 Linux. The reproducer
uses the supported but non-default `-enable-early-ifcvt-data-dependent` option.
It does not override `early-ifcvt-max-region-instrs`.
## Reproducer
Attach `source.c` and `issue-reproducer.sh`, then run:
[issue-reproducer.sh](https://github.com/user-attachments/files/32361323/issue-reproducer.sh)
[source.c](https://github.com/user-attachments/files/32361324/source.c)
```console
$ bash issue-reproducer.sh
g0: pre-DBG=0 post-CSEL=1 post-blocks=3
debug: pre-DBG=12 post-CSEL=0 post-blocks=5
27c5b09863073b8952870877cf1452deb11e94e717c2b5fc7b72bb9d646e5a12 .../g0.text
17015fa741810c1b737827605dea5a891e544ab3523c14260888e56d3362bbc7 .../debug.text
27c5b09863073b8952870877cf1452deb11e94e717c2b5fc7b72bb9d646e5a12 .../projected.text
```
The script also checks that `llvm-diff` accepts the `-g0` IR and the `-g` IR
after `opt --strip-debug`. Both variants pass the relevant machine verifiers.
## Actual behavior
Immediately before `early-ifcvt`, the ordinary Machine IR is the same. The
debug build has 12 additional `DBG_VALUE` instructions. After the pass, the
`-g0` variant contains one `CSEL` and three blocks, while the debug variant
contains no `CSEL` and five blocks. The difference survives to the final
`.text` section. Stripping debug information from the debug IR restores the
exact `-g0` bytes.
## Cause
`EarlyIfConverter::hasCallOrLoopInRange()` charges every raw `MachineInstr`
against `MaxRegionInstrs`. This happens in the endpoint ranges, whole-block
scan, and cached block size. `DBG_VALUE` instructions therefore consume a
budget intended to limit the amount of ordinary code searched.
I have a candidate patch that uses `instructionsWithoutDebug(...,
/*SkipPseudoOp=*/false)` consistently for these ranges and caches the number
of non-debug instructions. Keeping `SkipPseudoOp` false preserves the existing
treatment of pseudo probes.
## Expected behavior
Debug-only instructions should not affect this scan budget. The debug and
non-debug variants should make the same early-if-conversion decision and
produce the same ordinary machine code.
Contributor guide
Research direction
Start by running issue-reproducer.sh with source.c and inspect EarlyIfConverter::hasCallOrLoopInRange in the early-ifcvt pass. Trace the endpoint, whole-block, and cached block-size scans to compare raw MachineInstr counts with non-debug instructions. Done means -g and -g0 make the same conversion decision and produce identical ordinary machine code.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100