[DebugInfo][CorrelatedValuePropagation] Converting saturating arithmetic to normal arithmetic leaves wrong debug values
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
## Description
`correlated-propagation` converts a saturating arithmetic instruction to a normal binary operation after proving that saturation cannot affect the optimized program result. This is valid for the instruction's remaining program uses, but the `#dbg_value` for a source variable is also redirected to the normal operation.
The saturating and normal operations can still produce different values on paths where the original result does not affect program behavior. In this example, `llvm.usub.sat.i32(x, 10)` is replaced with `sub nuw i32 x, 10`. When `x = 9`, the source-level saturating result is `0`, while the normal unsigned subtraction wraps to `0xffffffff`. LLDB consequently prints the wrapped result for the source variable `sub` after the pass.
This is not specific to `llvm.usub.sat`. CorrelatedValuePropagation handles every `SaturatingInst` through the shared `processSaturatingInst` path, which replaces the saturating instruction and all of its uses with the corresponding normal binary operation. The same debug-info problem therefore applies to all saturating instructions transformed through this path.
## Reproducer
`case.c`:
```c
#include
uint32_t saturating_inst(uint32_t x) {
uint32_t sub = __builtin_elementwise_sub_sat(x, 10u);
[[gnu::nodebug]] uint32_t result = sub;
if (x < 10u)
result = 42u;
return result;
}
int main(void) {
uint32_t result = saturating_inst(9u);
return result != 42u;
}
```
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 -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/9T46G6Pfh).
`lldb-commands.txt`:
```text
breakpoint set --source-pattern-regexp 'if \(x < 10u\)'
run
frame variable --format hex sub
quit
```
## Observed Behavior
Before `correlated-propagation`, LLDB reports the saturated subtraction result. After the pass, it reports the wrapped result of the normal subtraction:
```text
== src.out ==
(uint32_t) sub = 0x00000000
== tgt.out ==
(uint32_t) sub = 0xffffffff
```
The relevant IR before the pass is:
```llvm
%elt.sat = call i32 @llvm.usub.sat.i32(i32 %x, i32 10), !dbg !20
#dbg_value(i32 %elt.sat, !21, !DIExpression(), !19)
%cmp = icmp ult i32 %x, 10, !dbg !23
br i1 %cmp, label %if.then, label %if.end, !dbg !23
```
After the pass, the saturating subtraction is replaced with a normal subtraction, and the debug record for `sub` directly uses the new result:
```llvm
%elt.sat1 = sub nuw i32 %x, 10, !dbg !20
#dbg_value(i32 %elt.sat1, !21, !DIExpression(), !19)
%cmp = icmp ult i32 %x, 10, !dbg !23
br i1 %cmp, label %if.then, label %if.end, !dbg !23
```
Here, `!21` is the source variable `sub`:
```llvm
!21 = !DILocalVariable(name: "sub", scope: !9, file: !1, line: 4, type: !12)
```
## Expected Behavior
The optimized debug information should not describe a source variable with the normal arithmetic result on paths where it differs from the original saturating result. At the breakpoint on `if (x < 10u)`, LLDB should report `sub = 0x00000000` for `x = 9`.
If the saturating result cannot be preserved after the instruction is replaced, `sub` should be reported as unavailable instead of being shown as `0xffffffff`.
This should hold for every `SaturatingInst` converted to normal arithmetic by CorrelatedValuePropagation.
## 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 at CorrelatedValuePropagation's shared processSaturatingInst path and reproduce the issue with case.c using the provided opt pipeline. Compare the debug records in src.ll and tgt.ll. Done means transformed SaturatingInst values no longer produce incorrect source-level values, with the variable preserved or reported unavailable across the affected transformations.
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