llvm / llvm/llvm-project

[DebugInfo][SimplifyCFG] Dropping a block also drops a #dbg_value update, causing LLDB to show a stale value

Open
#210,660 2 comments 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

`simplifycfg` deletes a basic block that contains a `#dbg_value` update for a source variable. After that transformation, the optimized binary still has valid program behavior, but the debug info for variable `l` becomes stale.

In the source program, `l` is initialized to `-1` and later assigned `0` in `if ((l = 0))`. At the final `return 0;`, LLDB should see `l == 0`. Before `simplifycfg`, LLDB reports `0`; after `simplifycfg`, LLDB reports `-1`.

This also reproduces through `function-simplification`, since that pipeline reuses `simplifycfg`.

## Reproducer

`case.c`:

```c
signed char func_1()
{
short l = -1;

for (int i = 0; i < 1; i++)
;

if ((l = 0))
{
return 1;
}
else
{
for (int i = 0; i < 1; i++)
;
}

return 0;
}

int main()
{
func_1();
return 0;
}
```

Build pipeline:

```sh
clang -g -O0 -Xclang -disable-O0-optnone -emit-llvm -S case.c -o case.ll
opt -passes="mem2reg" -S case.ll -o unopt.ll
opt -passes=simplifycfg -S unopt.ll -o opt.ll

clang unopt.ll -o src.out
clang opt.ll -o tgt.out
```

I put links to the full `unopt.ll` and `opt.ll` here: [Full IR Files](https://godbolt.org/z/fo3xE7j6x).

## Debugging Script

```sh
#!/usr/bin/env bash
set -euo pipefail

cd "$(dirname "$0")"

for exe in src.out tgt.out; do
echo "== $exe =="
lldb --batch \
-o 'breakpoint set --file case.c --line 18' \
-o run \
-o 'frame variable l' \
-- "./$exe"
echo
done
```

## Actual Behavior

Before `simplifycfg`, LLDB sees the expected value:

```text
== src.out ==
Breakpoint 1: where = src.out`func_1 + 83 at case.c:18:5

frame #0: ... src.out`func_1 at case.c:18:5
18 return 0;

(lldb) frame variable l
(short) l = 0
```

After `simplifycfg`, LLDB sees the stale initial value:

```text
== tgt.out ==
Breakpoint 1: where = tgt.out`func_1 + 60 at case.c:19:1

frame #0: ... tgt.out`func_1 at case.c:19:1
18 return 0;
-> 19 }

(lldb) frame variable l
(short) l = -1
```

## Expected Behavior

At the final `return 0;`, LLDB should report `l = 0`, matching the source-level assignment `if ((l = 0))`.

If the assignment value cannot be preserved through the transformation, the variable should be reported as unavailable/optimized out for the affected range. It should not keep showing the stale initializer value `-1`.

## Relevant Debug Info Clue

Before `simplifycfg`, `unopt.ll` contains a debug value update for `l = 0`:

```llvm
#dbg_value(i16 0, !14, !DIExpression(), !16)
```

After `simplifycfg`, that update is gone and only the initial debug value for `l = -1` remains:

```llvm
#dbg_value(i16 -1, !14, !DIExpression(), !16)
```

This suggests that when `simplifycfg` removes the block containing the `l = 0` debug update, it fails to preserve/salvage the source variable value for the remaining reachable code.

## Environment

```text
clang version 23.0.0git
llvm-project revision: 58203500b5bd1628401c1a103eb264b7d78ea3bb

lldb version 23.0.0git
```

## Notes

I noticed a previous issue [#55115](https://github.com/llvm/llvm-project/issues/55115) also reported this bug of `simplifycfg` dropping a debug value update. In that case, the debug value dropping causes the variable unavailable, but in this case, the variable is still available but shows a stale value.

By the way, `simplification` reuses `simplifycfg` and this bug can also be reproduced through those pipelines with my `case.c`.

Contributor guide

Open the contributing guide

Research direction

Reproduce the issue with case.c using the listed clang, opt, and LLDB commands, then compare unopt.ll and opt.ll around the #dbg_value updates for l. Trace simplifycfg's handling when it removes the block containing the l = 0 update. Done means preserving the value so LLDB reports 0, or marking l unavailable instead of retaining -1, with a regression test covering the transformation.

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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.