llvm / llvm/llvm-project

[DebugInfo][CorrelatedValuePropagation] Narrowing `udiv`/`urem` can leave wrong debug values

Open
#218,628 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` 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

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.