llvm / llvm/llvm-project

[DebugInfo][CorrelatedValuePropagation] Rewriting `srem` through `urem` leaves a wrong debug value

Open
#218,622 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` rewrites an `srem` as negation, `urem`, and another negation after proving that the remainder only affects program behavior when the dividend is negative. This preserves the program result, but the `#dbg_value` for the source variable `remainder` is changed to describe the rewritten expression for every input.

For a positive input, the rewritten expression does not equal the source signed remainder. Consequently, LLDB prints `remainder` as `-30` after the pass instead of the source-level value `16`.

## Reproducer

`case.c`:

```c
int srem_to_urem(int x) {
int remainder = x % 42;
return x < 0 ? remainder : 24;
}

int main(void) { return srem_to_urem(100) != 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/W7qWoxKx3).

`lldb-commands.txt`:

```text
breakpoint set --file case.c --line 3
run
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

Before `correlated-propagation`, LLDB reports the signed remainder:

```text
== src.out ==
(int) remainder = 16
```

After `correlated-propagation`, LLDB reports a different value:

```text
== tgt.out ==
(int) remainder = -30
```

The relevant IR before the pass is:

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

After the pass, the signed remainder is rewritten through `urem`, and the debug record describes `remainder` with the final negated result:

```llvm
%x.nonneg = sub i32 0, %x, !dbg !16
%rem1 = urem i32 %x.nonneg, 42, !dbg !16
%rem1.neg = sub i32 0, %rem1, !dbg !16
#dbg_value(i32 %rem1.neg, !17, !DIExpression(), !15)
```

Here, `!17` is the source variable `remainder`:

```llvm
!17 = !DILocalVariable(name: "remainder", scope: !9, file: !1, line: 2, type: !12)
```

## Expected Behavior

The optimized debug information should not describe `remainder` with the rewritten unsigned-remainder expression when it differs from the source-level signed remainder. At the breakpoint on line 3, LLDB should either report the original value `16` or report `remainder` as unavailable if the source value cannot be represented after the transformation.

## 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 clang, opt, and LLDB commands to compare src.ll with tgt.ll. Then trace the correlated-propagation pass where the srem is rewritten and inspect how its #dbg_value is updated. Done means the optimized debug value is either the correct signed remainder or unavailable when it cannot be represented.

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.