llvm / llvm/llvm-project

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

Open
#219,940 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` eliminates an `and` after proving that its result only affects program behavior when the unsigned input is less than `8`. Within that range, `x & 7` is equal to `x`, so replacing the masked value with the input preserves the program result.

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

## Reproducer

`case.c`:

```c
#include
#include

uint32_t and_elide(uint32_t x) {
uint32_t masked = x & 7u;
[[gnu::nodebug]] bool in_range = x < 8u;
[[gnu::nodebug]] uint32_t result = in_range ? masked : 24u;
return result;
}

int main() {
uint32_t result = and_elide(8);
return 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,instsimplify' -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/3a3YzM5GW).

`lldb-commands.txt`:

```text
breakpoint set --source-pattern-regexp 'return result;'
run
frame variable masked
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 result of the bitwise `and`:

```text
== src.out ==
(uint32_t) masked = 0
```

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

```text
== tgt.out ==
(uint32_t) masked = 8
```

The relevant IR before the pass is:

```llvm
%and = and i32 %x, 7, !dbg !20
#dbg_value(i32 %and, !21, !DIExpression(), !19)
%cmp = icmp ult i32 %x, 8, !dbg !22
%and. = select i1 %cmp, i32 %and, i32 24, !dbg !23
```

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

```llvm
#dbg_value(i32 %x, !20, !DIExpression(), !19)
%cmp = icmp ult i32 %x, 8, !dbg !21
%and. = select i1 %cmp, i32 %x, i32 24, !dbg !22
```

Here, `!20` in the optimized IR is the source variable `masked`:

```llvm
!20 = !DILocalVariable(name: "masked", scope: !9, file: !1, line: 5, type: !12)
```

## Expected Behavior

The optimized debug information should not describe `masked` with `%x` when it differs from the source-level value of `x & 7`. At the breakpoint on `return result;`, LLDB should either report the original value `0` or report `masked` as unavailable if the source value cannot be represented after eliminating the `and`.

## 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 by reproducing the issue with case.c and the shown opt pipeline, then compare the debug records in src.ll and tgt.ll. Trace correlated-propagation where it replaces the eliminated and and updates #dbg_value for masked. Done when the optimized debug information preserves the source-level value or marks masked unavailable, with the LLDB result verified on the reproducer.

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.