llvm / llvm/llvm-project

[DebugInfo][CorrelatedValuePropagation] Converting saturating arithmetic to normal arithmetic leaves wrong debug values

Open
#220,562 1 comment 0 reactions 0 assignees View on GitHub
debuginfo llvm:transforms
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

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.