[DebugInfo][SimplifyCFG] Dropping a block also drops a #dbg_value update, causing LLDB to show a stale value
- 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
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