llvm / llvm/llvm-project

[DebugInfo][InstSimplify] Salvaged equality compares an 8-bit register value with a 64-bit `DW_OP_constu`

Open
#219,463 2 comments 0 reactions 0 assignees View on GitHub
debuginfo llvm:instcombine wrong-debug
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Description

InstSimplify removes an `icmp eq i8` whose result initializes the source variable `cmpeq`, and salvages the comparison into a `DIExpression`. The optimized program behavior is correct, but the generated DWARF compares operands with different effective widths.

The register operand is masked to its low eight bits, while `DW_OP_constu` produces an address-sized value. On x86-64, the expression therefore compares `0x80` with `0xffffffffffffff80`, causing LLDB to report `cmpeq = 0` instead of the source-level value `1`.

## Reproducer

Godbolt link containing the complete `src.ll` and `tgt.ll`: [Godbolt](https://godbolt.org/z/hrTExYdne).

`case.c`:

```c
typedef signed _BitInt(8) i8;
typedef unsigned _BitInt(1) i1;

volatile i8 barriar;

__attribute__((noinline))
i1 sge_or_max_commute_not_op(i8 x, i8 y) {
i1 cmpeq = x == (i8)0x80;
barriar = x;
return cmpeq | (i1)((i8)~x >= y);
}

int main() {
i1 result = sge_or_max_commute_not_op(0x80, 0);
return 0;
}
```

Build pipeline:

```sh
clang -g -O1 -Xclang -disable-llvm-passes -fno-discard-value-names -S -emit-llvm case.c -o case.ll
opt -passes=mem2reg -S case.ll -o src.ll
opt -passes=instsimplify -S src.ll -o tgt.ll
llc -O0 --dwarf-version=5 -relocation-model=pic -filetype=obj src.ll -o src.o
llc -O0 --dwarf-version=5 -relocation-model=pic -filetype=obj tgt.ll -o tgt.o
clang src.o -o src.out
clang tgt.o -o tgt.out
```

`lldb-commands.txt`:

```text
breakpoint set --file case.c --line 9
run
frame variable cmpeq
quit
```

Run LLDB on the binaries before and after InstSimplify:

```sh
lldb src.out -s lldb-commands.txt
lldb tgt.out -s lldb-commands.txt
```

## DWARF Debug Information

The DIE for `cmpeq` in `tgt.out` can be inspected with:

```sh
llvm-dwarfdump --debug-info --name=cmpeq tgt.out
```

The generated location expression is:

```text
0x00000050: DW_TAG_variable
DW_AT_location (indexed (0x1) loclist = 0x0000001a:
[0x0000000000001136, 0x000000000000113e): DW_OP_breg0 RAX+0, DW_OP_constu 0xff, DW_OP_and, DW_OP_constu 0xffffffffffffff80, DW_OP_eq, DW_OP_convert (0x00000026) "DW_ATE_unsigned_1", DW_OP_convert (0x0000002a) "DW_ATE_unsigned_8", DW_OP_stack_value)
DW_AT_name ("cmpeq")
DW_AT_decl_file ("case.c")
DW_AT_decl_line (8)
DW_AT_type (0x00000094 "i1")
```

`DW_OP_breg0 RAX+0`, `DW_OP_constu 0xff`, and `DW_OP_and` retain only the low eight bits of the register value, producing `0x80` for `x = -128`. In contrast, the following `DW_OP_constu 0xffffffffffffff80` produces a 64-bit generic value on x86-64. Since no conversion makes the operand widths consistent before `DW_OP_eq`, the expression compares `0x80` with `0xffffffffffffff80` and evaluates to false.

## Observed Behavior

Before InstSimplify, `cmpeq` is computed directly from the `i8` comparison result:

```llvm
%cmp = icmp eq i8 %x, -128, !dbg !30
%conv = zext i1 %cmp to i32, !dbg !31
%conv1 = trunc i32 %conv to i1, !dbg !32
%storedv = zext i1 %conv1 to i8, !dbg !33
#dbg_value(i8 %storedv, !28, !DIExpression(), !29)
```

After InstSimplify removes the comparison, it salvages the value with an address-sized constant:

```llvm
#dbg_value(i8 %x, !28, !DIExpression(DW_OP_constu, 18446744073709551488, DW_OP_eq, DW_OP_LLVM_convert, 1, DW_ATE_unsigned, DW_OP_LLVM_convert, 8, DW_ATE_unsigned, DW_OP_stack_value), !29)
```

LLDB reports different values before and after InstSimplify:

```text
== src.out ==
(i1) cmpeq = 1

== tgt.out ==
(i1) cmpeq = 0
```

## Expected Behavior

The salvaged debug expression should preserve the eight-bit signed comparison semantics of `icmp eq i8 %x, -128`. At line 9, LLDB should report the same value before and after InstSimplify:

```text
(i1) cmpeq = 1
```

If the comparison cannot be represented correctly, `cmpeq` should be reported as unavailable rather than with an incorrect value.

## 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

Reproduce the issue with case.c and the shown clang, opt, llc, LLDB, and llvm-dwarfdump commands, comparing src.ll with tgt.ll. Start at the InstSimplify pass and its debug-value salvage path, then inspect the generated DWARF expression. Done means the optimized expression preserves the i8 comparison semantics and LLDB reports cmpeq as 1, or reports it unavailable when that cannot be represented safely.

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.