[DebugInfo][InstSimplify] Salvaged equality compares an 8-bit register value with a 64-bit `DW_OP_constu`
- 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
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