llvm / llvm/llvm-project

[DebugInfo] Clang emits incorrect DWARF expression for unsigned integer comparison

Open
#206,325 3 comments 0 reactions 0 assignees View on GitHub
clang:codegen confirmed debuginfo
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

## Summary
Clang can emit an incorrect DWARF location expression for an optimized-out unsigned integer comparison.

For an expression such as `int x = a < b;` where `a` and `b` are `uint64_t`, Clang emits a DWARF expression that applies `DW_OP_lt` directly to two generic type stack values and only converts the result afterwards. Under [DWARF v5](https://dwarfstd.org/doc/DWARF5.pdf), generic type stack values have an address-sized integral type with unspecified signedness (Section 2.5.1), and relational operations on generic operands are performed as signed comparisons (Section 2.5.1.5). Because Clang does not convert the operands to an unsigned type before `DW_OP_lt`, the comparison defaults to signed and does not preserve the unsigned source-language semantics.

For comparison, GCC emits DWARF for this example that fully conforms to the specification.

## Reproducer
```C
#include

volatile uint64_t sink64;

__attribute__((noinline))
int unsigned_dead_cmp_live_operands(uint64_t a, uint64_t b) {
int x = a < b;
sink64 += a;
sink64 += b;
return 0;
}

int main(void) {
return unsigned_dead_cmp_live_operands(0x8000000000000000ULL, 10ULL);
}
```

Build with:
```bash
clang -O2 -g -gdwarf-5 -fno-pie -no-pie demo.c -o demo
```

The generated DWARF for `x` is:

```
DW_AT_location (
DW_OP_breg5 RDI+0,
DW_OP_breg4 RSI+0,
DW_OP_lt,
DW_OP_convert (...) "DW_ATE_unsigned_1",
DW_OP_convert (...) "DW_ATE_unsigned_32",
DW_OP_stack_value
)
```

The `DW_OP_convert` operations occur after `DW_OP_lt`, so they only convert the `boolean` result. They do not make the two operands unsigned before the comparison.

## Expected Behavior

The DWARF expression should preserve the source-language unsigned comparison. For example, the operands should be converted to an unsigned base type before `DW_OP_lt`, or the producer should otherwise emit an equivalent expression whose comparison is unsigned.
For the call `unsigned_dead_cmp_live_operands(0x8000000000000000ULL, 10ULL)`, the source-language value of `x` is `0`, because `0x8000000000000000ULL > 10ULL`.

## Actual Behavior on GDB/LLDB

A DWARF consumer that follows the generic-type comparison rule evaluates the current expression as a signed comparison:

```
(int64_t)0x8000000000000000 < (int64_t)10
```

which is true, so it reports `x = 1`, whereas the correct source-language value is `x = 0`.

GDB does exactly this: its evaluation is correct per the DWARF specification, but it produces the wrong value because the debug info emitted by Clang is incorrect.

LLDB may appear to print the correct value for this particular unsigned case, but only because of a separate LLDB consumer bug in which generic/register integer comparison is not handled according to the DWARF signed-comparison rule. That issue is tracked separately in [#202878](https://github.com/llvm/llvm-project/issues/202878). A signed-comparison variant demonstrates that LLDB's behavior is not reliable and that the apparent correctness in this unsigned case is accidental.

Contributor guide

Open the contributing guide

Research direction

Start with the demo.c reproducer and run the provided clang -O2 -g -gdwarf-5 command, then inspect the generated DWARF location expression for x. Trace the Clang debug-info emission path for optimized-out comparisons and compare the result with GCC's expression. Done means the emitted expression preserves unsigned comparison semantics and GDB evaluates the call with x = 0.

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
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.