llvm / llvm/llvm-project

[DebugInfo][CorrelatedValuePropagation] Eliminating `srem` leaves a wrong debug value

Open
#218,631 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` eliminates an `srem` after proving that its result only affects program behavior when the dividend is between `-42` and `42`. Within that range, `x % 42` is equal to `x`, so replacing the remainder with `x` preserves the program result.

However, the pass also changes the `#dbg_value` for the source variable `rem` to describe `%x` directly. For inputs outside the inferred range, `%x` does not equal the source remainder even though `rem` is still a source variable that can be inspected. With `x = 42`, LLDB prints `rem` as `42` after the pass instead of the source-level value `0`.

## Reproducer

`case.c`:

```c
#include

int srem_elide(int x) {
int rem = x % 42;
bool in_range = (x < 42) && (x > -42);
return in_range ? rem : 24;
}
int main(void) { return srem_elide(42) != 0; }
```

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/G81Meashq).

`lldb-commands.txt`:

```text
breakpoint set --file case.c --line 5
run
frame variable rem
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 remainder:

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

After `correlated-propagation`, LLDB reports the input value instead:

```text
== tgt.out ==
(int) rem = 42
```

The relevant IR before the pass is:

```llvm
%rem1 = srem i32 %x, 42, !dbg !16
#dbg_value(i32 %rem1, !17, !DIExpression(), !15)
%0 = add i32 %x, 41, !dbg !18
%spec.select = icmp ult i32 %0, 83, !dbg !18
%cond = select i1 %spec.select, i32 %rem1, i32 24, !dbg !21
```

After the pass, the `srem` is removed and `%x` is used both by the select and by the debug record for `rem`:

```llvm
#dbg_value(i32 %x, !16, !DIExpression(), !15)
%0 = add i32 %x, 41, !dbg !17
%spec.select = icmp ult i32 %0, 83, !dbg !17
%cond = select i1 %spec.select, i32 %x, i32 24, !dbg !20
```

Here, `!16` in the optimized IR is the source variable `rem`:

```llvm
!16 = !DILocalVariable(name: "rem", scope: !9, file: !1, line: 4, type: !12)
```

## Expected Behavior

The optimized debug information should not describe `rem` with `%x` when it differs from the source-level remainder. At the breakpoint on line 5, LLDB should either report the original value `0` or report `rem` as unavailable if the source value cannot be represented after eliminating the `srem`.

## 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 by tracing the correlated-propagation pass using the provided case.c reproducer and compare src.ll with tgt.ll. Use the shown opt pipeline and LLDB commands to inspect how the #dbg_value for rem changes when srem is eliminated. Done means the optimized debug information no longer reports rem as x when the values differ, and the reproducer reports either 0 or an unavailable value.

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
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.