llvm / llvm/llvm-project

[CodeGen] Debug instructions consume EarlyIfConversion's scan budget

Open
#224,482 0 comments 0 reactions 0 assignees View on GitHub
new issue
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.