llvm / llvm/llvm-project

[DebugInfo][CorrelatedValuePropagation] Narrowing `sdiv`/`srem` can leave wrong debug values

Open
#218,406 1 comment 0 reactions 0 assignees View on GitHub
debuginfo llvm:transforms wrong-debug
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Description

`correlated-propagation` narrows `sdiv i32` and `srem i32` operations to `i8` after proving that their results only affect program behavior when the dividend is strictly between `-43` and `43`. Values in that range fit in `i8`, so truncating the dividend, calculating the quotient or remainder in `i8`, and sign-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 = 252`, LLDB prints `quotient` as `0` instead of `6`, and `remainer` as `-4` instead of `0`.

## Reproducer

`case.c`:

```c
#include

int sdiv_narrow(int x) {
int quotient = x / 42;
bool __attribute__((nodebug)) in_range = (x < 43) && (x > -43);
return in_range ? quotient : 24;
}

int srem_narrow(int x) {
int remainer = x % 42;
bool in_range = (x < 43) && (x > -43);
return in_range ? remainer : 24;
}

int main(void) {
return sdiv_narrow(252) != 24 || srem_narrow(252) != 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/xz88o4c3v).

`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 remainer
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 `sdiv`, LLDB reports the source-level quotient before `correlated-propagation`:

```text
== src.out ==
(int) quotient = 6
```

After `correlated-propagation`, LLDB reports the result of the narrowed computation:

```text
== tgt.out ==
(int) quotient = 0
```

For `srem`, LLDB reports the source-level remainder before `correlated-propagation`:

```text
== src.out ==
(int) remainer = 0
```

After `correlated-propagation`, LLDB reports the result of the narrowed computation:

```text
== tgt.out ==
(int) remainer = -4
```

The relevant IR before the pass contains the original `i32` operations and debug records:

```llvm
%div = sdiv i32 %x, 42, !dbg !16
#dbg_value(i32 %div, !17, !DIExpression(), !15)

%rem = srem i32 %x, 42, !dbg !24
#dbg_value(i32 %rem, !25, !DIExpression(), !23)
```

After the pass, each dividend is truncated to `i8`, the operation is performed in `i8`, and the result is sign-extended back to `i32`. The debug records describe these narrowed results directly:

```llvm
%div.lhs.trunc = trunc i32 %x to i8, !dbg !16
%div1 = sdiv i8 %div.lhs.trunc, 42, !dbg !16
%div.sext = sext i8 %div1 to i32, !dbg !16
#dbg_value(i32 %div.sext, !17, !DIExpression(), !15)

%rem.lhs.trunc = trunc i32 %x to i8, !dbg !24
%rem1 = srem i8 %rem.lhs.trunc, 42, !dbg !24
%rem.sext = sext i8 %rem1 to i32, !dbg !24
#dbg_value(i32 %rem.sext, !25, !DIExpression(), !23)
```

Here, `!17` and `!25` are the source variables `quotient` and `remainer`:

```llvm
!17 = !DILocalVariable(name: "quotient", scope: !9, file: !1, line: 4, type: !12)
!25 = !DILocalVariable(name: "remainer", scope: !21, file: !1, line: 10, type: !12)
```

## Expected Behavior

The optimized debug information should not describe source variables with narrowed `sdiv` or `srem` results when they differ from the source-level `i32` operations. LLDB should either report the original values, `quotient = 6` and `remainer = 0`, 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

Open the contributing guide

Research direction

Start with the case.c reproducer and run the listed opt and LLDB commands to confirm the changed quotient and remainder values. Then inspect the correlated-propagation pass and its handling of the #dbg_value records around the narrowed sdiv and srem operations. Done means optimized debug information preserves the original values or marks them unavailable when narrowing makes them unrepresentable.

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
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.