[DebugInfo][CorrelatedValuePropagation] Narrowing `udiv`/`urem` can leave wrong debug values
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
## Description
`correlated-propagation` narrows `udiv i32` and `urem i32` operations to `i8` after proving that their results only affect program behavior when the unsigned dividend is less than `256`. Values in that range fit in `i8`, so truncating the dividend, calculating the quotient or remainder in `i8`, and zero-extending the result back to `i32` preserves the program result.
However, the `#dbg_value` records for the source variables are changed to describe the narrowed results for every input. For values outside the inferred range, truncating the dividend can change both the quotient and remainder even though the source variables can still be inspected. With `x = 256`, LLDB prints `quotient` as `0` instead of `6`, and `remainder` as `0` instead of `4`.
## Reproducer
`case.c`:
```c
#include
unsigned int udiv_narrow(unsigned int x) {
unsigned int quotient = x / 42u;
bool in_range = x < 256u;
return in_range ? quotient : 24u;
}
unsigned int urem_narrow(unsigned int x) {
unsigned int remainder = x % 42u;
bool in_range = x < 256u;
return in_range ? remainder : 24u;
}
int main(void) {
return udiv_narrow(256) != 24 || urem_narrow(256) != 24;
}
```
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,instcombine' -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
```
Here are the complete [src.ll and tgt.ll](https://godbolt.org/z/7ExsYvKWW).
`lldb-commands.txt`:
```text
breakpoint set --file case.c --line 5
breakpoint set --file case.c --line 12
run
frame variable quotient
continue
frame variable remainder
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
For `udiv`, LLDB reports the source-level quotient before `correlated-propagation`:
```text
== src.out ==
(unsigned int) quotient = 6
```
After `correlated-propagation`, LLDB reports the result of the narrowed computation:
```text
== tgt.out ==
(unsigned int) quotient = 0
```
For `urem`, LLDB reports the source-level remainder before `correlated-propagation`:
```text
== src.out ==
(unsigned int) remainder = 4
```
After `correlated-propagation`, LLDB reports the result of the narrowed computation:
```text
== tgt.out ==
(unsigned int) remainder = 0
```
The relevant IR before the pass contains the original `i32` operations and debug records:
```llvm
%div = udiv i32 %x, 42, !dbg !16
#dbg_value(i32 %div, !17, !DIExpression(), !15)
%rem = urem i32 %x, 42, !dbg !26
#dbg_value(i32 %rem, !27, !DIExpression(), !25)
```
After the pass, each dividend is truncated to `i8`, the operation is performed in `i8`, and the result is zero-extended back to `i32`. The debug records describe these narrowed results directly:
```llvm
%div.lhs.trunc = trunc i32 %x to i8, !dbg !16
%div1 = udiv i8 %div.lhs.trunc, 42, !dbg !16
%div.zext = zext i8 %div1 to i32, !dbg !16
#dbg_value(i32 %div.zext, !17, !DIExpression(), !15)
%rem.lhs.trunc = trunc i32 %x to i8, !dbg !26
%rem1 = urem i8 %rem.lhs.trunc, 42, !dbg !26
%rem.zext = zext i8 %rem1 to i32, !dbg !26
#dbg_value(i32 %rem.zext, !27, !DIExpression(), !25)
```
Here, `!17` and `!27` are the source variables `quotient` and `remainder`:
```llvm
!17 = !DILocalVariable(name: "quotient", scope: !9, file: !1, line: 4, type: !12)
!27 = !DILocalVariable(name: "remainder", scope: !23, file: !1, line: 10, type: !12)
```
## Expected Behavior
The optimized debug information should not describe source variables with narrowed `udiv` or `urem` results when they differ from the source-level `i32` operations. LLDB should either report the original values, `quotient = 6` and `remainder = 4`, or report the variables as unavailable if their source values cannot be represented after narrowing.
## Environment
```text
clang version 24.0.0git
llvm-project revision: f6ea145aa8e89631ae04f72df32580b20256d40c
LLVM version 24.0.0git
lldb version 24.0.0git
```
Contributor guide
Research direction
Start with the case.c reproducer and run the listed opt correlated-propagation pipeline, then compare src.ll and tgt.ll and inspect the #dbg_value records with LLDB. Trace how the pass handles the narrowed udiv and urem results. Done means optimized debug information reports the original quotient and remainder values or marks them unavailable when they cannot be represented.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- compilers, devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100