llvm / llvm/llvm-project

[DebugInfo][CorrelatedValuePropagation] Converting signed `icmp` predicates to unsigned predicates can leave wrong debug values

Open
#218,620 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` can convert signed relational `icmp` predicates into the corresponding unsigned predicates after proving that the comparison result only affects program behavior when the operands have the required signs. This preserves the program result, but a `#dbg_value` describing the signed comparison can remain attached to the converted unsigned comparison result.

This issue is not specific to one predicate: it applies to signed `icmp` comparisons using `slt`, `sle`, `sgt`, and `sge` when they are converted to the corresponding unsigned predicates. The reproducer below demonstrates `slt` to `samesign ult`. For a negative input, the source signed comparison evaluates to true, while the optimized debug information no longer describes that source-level value. Consequently, LLDB prints `less` as `0` after the pass instead of `1`.

## Reproducer

`case.c`:

```c
int icmp_signed_to_unsigned(int x) {
int less = x < 10;
return x >= 0 ? less : 0;
}

int main(void) { return icmp_signed_to_unsigned(-1) != 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' -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/45nd9E6rq).

`lldb-commands.txt`:

```text
breakpoint set --file case.c --line 3
run
frame variable less
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 source signed-comparison result:

```text
== src.out ==
(int) less = 1
```

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

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

The relevant IR before the pass is:

```llvm
%cmp = icmp slt i32 %x, 10, !dbg !17
%conv = zext i1 %cmp to i32, !dbg !17
#dbg_value(i32 %conv, !18, !DIExpression(), !16)
```

After the pass, the signed comparison is replaced with `icmp samesign ult`, but the debug record still describes `less` directly with its result:

```llvm
%cmp = icmp samesign ult i32 %x, 10, !dbg !17
%conv = zext i1 %cmp to i32, !dbg !17
#dbg_value(i32 %conv, !18, !DIExpression(), !16)
```

Here, `!18` is the source variable `less`:

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

## Expected Behavior

The optimized debug information should not describe a source variable with a converted unsigned comparison result when it does not preserve the source-level signed comparison. In this testcase, LLDB should either report the original value `1` for `less` at the breakpoint on line 3 or report it as unavailable if the source value cannot be represented after the transformation. The same requirement applies to conversions of the other signed relational `icmp` predicates.

## 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 passes to compare src.ll with tgt.ll, then inspect the correlated-propagation pass and its handling of #dbg_value records. Check the slt-to-samesign ult example and the corresponding sle, sgt, and sge conversions. Done means the transformed IR no longer presents an incorrect source value: LLDB reports the original value or marks it unavailable.

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.