[DebugInfo][CorrelatedValuePropagation] Replacing `llvm.smin`/`llvm.smax` with `llvm.umin`/`llvm.umax` counterparts can leave wrong debug values
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
## Description
`correlated-propagation` can replace `llvm.smin`/`llvm.smax` with `llvm.umin`/`llvm.umax` after proving that the result only affects program behavior when the operands satisfy the required non-negative range. This preserves the program result, but a `#dbg_value` describing the signed result can remain attached to the unsigned operation result.
This reproducer demonstrates the `llvm.smin` to `llvm.umin` case. For a negative input, signed and unsigned minimum operations produce different values. Consequently, LLDB prints `minimum` as `42` after the pass instead of the source-level value `-1`. The analogous `llvm.smax` to `llvm.umax` transformation has the same debug-info problem when the signed and unsigned maximum results differ outside the range required for program semantics.
## Reproducer
`case.c`:
```c
int smin_to_umin(int x) {
int minimum = __builtin_elementwise_min(x, 42);
return x >= 0 ? minimum : 24;
}
int main(void) { return smin_to_umin(-1) != 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' -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/b94fEsEYx).
`lldb-commands.txt`:
```text
breakpoint set --file case.c --line 3
run
frame variable minimum
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 signed minimum:
```text
== src.out ==
(int) minimum = -1
```
After `correlated-propagation`, LLDB reports the unsigned minimum:
```text
== tgt.out ==
(int) minimum = 42
```
The relevant IR before the pass is:
```llvm
%elt.min = call i32 @llvm.smin.i32(i32 %x, i32 42), !dbg !16
#dbg_value(i32 %elt.min, !17, !DIExpression(), !15)
```
After the pass, `llvm.smin` has become `llvm.umin`, but the debug record still describes `minimum` directly with its result:
```llvm
%0 = call i32 @llvm.umin.i32(i32 %x, i32 42), !dbg !16
#dbg_value(i32 %0, !17, !DIExpression(), !15)
```
Here, `!17` is the source variable `minimum`:
```llvm
!17 = !DILocalVariable(name: "minimum", scope: !9, file: !1, line: 2, type: !12)
```
## Expected Behavior
The optimized debug information should not describe `minimum` with the unsigned minimum result when it differs from the source-level signed minimum. At the breakpoint on line 3, LLDB should either report the original value `-1` or report `minimum` as unavailable if the source value cannot be represented after the transformation. The same requirement applies when `llvm.smax` is replaced with `llvm.umax`.
## 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
Reproduce the issue with case.c using the listed clang, opt, and LLDB commands, then inspect the correlated-propagation implementation and its handling of debug records during llvm.smin/llvm.smax to llvm.umin/llvm.umax replacements. Confirm completion by ensuring LLDB reports the original signed value or marks minimum unavailable for both minimum and maximum cases.
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
- 58/100