[DebugInfo][CorrelatedValuePropagation] Eliminating `and` leaves a wrong debug value
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
## Description
`correlated-propagation` eliminates an `and` after proving that its result only affects program behavior when the unsigned input is less than `8`. Within that range, `x & 7` is equal to `x`, so replacing the masked value with the input preserves the program result.
However, the pass also changes the `#dbg_value` for the source variable `masked` to describe `%x` directly. For inputs outside the inferred range, `%x` does not equal the source-level result of `x & 7` even though `masked` is still a source variable that can be inspected. With `x = 8`, LLDB prints `masked` as `8` after the pass instead of the source-level value `0`.
## Reproducer
`case.c`:
```c
#include
#include
uint32_t and_elide(uint32_t x) {
uint32_t masked = x & 7u;
[[gnu::nodebug]] bool in_range = x < 8u;
[[gnu::nodebug]] uint32_t result = in_range ? masked : 24u;
return result;
}
int main() {
uint32_t result = and_elide(8);
return 0;
}
```
Build pipeline:
```sh
clang -g -O0 -Xclang -disable-O0-optnone -fno-discard-value-names -S -emit-llvm case.c -o case.ll
opt -passes='mem2reg,simplifycfg,instsimplify' -S case.ll -o src.ll
opt -passes=correlated-propagation -S src.ll -o tgt.ll
clang src.ll -o src.out
clang tgt.ll -o tgt.out
```
The complete `src.ll` and `tgt.ll` are available in this [Godbolt link](https://godbolt.org/z/3a3YzM5GW).
`lldb-commands.txt`:
```text
breakpoint set --source-pattern-regexp 'return result;'
run
frame variable masked
quit
```
Run LLDB on the binaries before and after `correlated-propagation`:
```sh
lldb src.out -s lldb-commands.txt
lldb tgt.out -s lldb-commands.txt
```
## Observed Behavior
Before `correlated-propagation`, LLDB reports the result of the bitwise `and`:
```text
== src.out ==
(uint32_t) masked = 0
```
After `correlated-propagation`, LLDB reports the input instead:
```text
== tgt.out ==
(uint32_t) masked = 8
```
The relevant IR before the pass is:
```llvm
%and = and i32 %x, 7, !dbg !20
#dbg_value(i32 %and, !21, !DIExpression(), !19)
%cmp = icmp ult i32 %x, 8, !dbg !22
%and. = select i1 %cmp, i32 %and, i32 24, !dbg !23
```
After the pass, the `and` is removed and `%x` is used both by the select and by the debug record for `masked`:
```llvm
#dbg_value(i32 %x, !20, !DIExpression(), !19)
%cmp = icmp ult i32 %x, 8, !dbg !21
%and. = select i1 %cmp, i32 %x, i32 24, !dbg !22
```
Here, `!20` in the optimized IR is the source variable `masked`:
```llvm
!20 = !DILocalVariable(name: "masked", scope: !9, file: !1, line: 5, type: !12)
```
## Expected Behavior
The optimized debug information should not describe `masked` with `%x` when it differs from the source-level value of `x & 7`. At the breakpoint on `return result;`, LLDB should either report the original value `0` or report `masked` as unavailable if the source value cannot be represented after eliminating the `and`.
## Environment
```text
clang version 24.0.0git
llvm-project revision: d35d0e69980f11c2acbd3670c65fec3cf574224a
LLVM version 24.0.0git
lldb version 24.0.0git
```
Contributor guide
Research direction
Start by reproducing the issue with case.c and the shown opt pipeline, then compare the debug records in src.ll and tgt.ll. Trace correlated-propagation where it replaces the eliminated and and updates #dbg_value for masked. Done when the optimized debug information preserves the source-level value or marks masked unavailable, with the LLDB result verified on the reproducer.
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
- 55/100