<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/161555>161555</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[DebugInfo] dereferencing pointer to local variable shows uninitialized data due to DSE
</td>
</tr>
<tr>
<th>Labels</th>
<td>
new issue
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
thejh
</td>
</tr>
</table>
<pre>
cc @jmorse
When Dead Store Elimination (`DSEPass`) eliminates stores into an object, this is tracked in DWARF debuginfo such that the debugger can still show the logical state of the object; but when the debugger looks at a pointer to the object (in particular, when a pointer to a local variable is passed to an inlined subroutine), when the debugger follows this pointer, it will potentially see uninitialized memory.
This might be related to https://github.com/llvm/llvm-project/issues/38112#issuecomment-981001844 , which is also about DSE causing issues with debugging.
Reproducer:
```
jannh@horn:~/test$ cat debug-dse-pointer-test.c
struct s {
int a;
int b;
};
int blah();
int bar(struct s*);
__attribute__((always_inline)) inline int baz(struct s *s) {
blah();
s->a = s->a + 1;
s->b = s->b - 1;
return bar(s);
}
__attribute__((noinline)) int foo(int a, int b) {
struct s s = {.a = a, .b = b};
return baz(&s);
}
int main(void) {
foo(1, 2);
return 0;
}
jannh@horn:~/test$ cat debug-dse-pointer-test2.c
struct s {
int a;
int b;
};
int blah(){return 0;}
int bar(struct s *s){return 0;}
jannh@horn:~/test$ /usr/local/google/home/jannh/git/foreign/llvmp-build-debug/bin/clang-12 -g -O2 -o debug-dse-pointer-test debug-dse-pointer-test.c debug-dse-pointer-test2.c
jannh@horn:~/test$ gdb ./debug-dse-pointer-test
[...]
(gdb) break baz
Breakpoint 1 at 0x401117: file debug-dse-pointer-test.c, line 10.
(gdb) run
[...]
Breakpoint 1, baz (s=0x7fffffffd6f0) at debug-dse-pointer-test.c:10
10 blah();
(gdb) print *s
$1 = {a = 4198720, b = 0}
(gdb) bt
#0 baz (s=0x7fffffffd6f0) at debug-dse-pointer-test.c:10
#1 foo (a=a@entry=1, b=b@entry=2) at debug-dse-pointer-test.c:18
#2 0x0000000000401150 in main () at debug-dse-pointer-test.c:22
(gdb) frame 1
#1 foo (a=a@entry=1, b=b@entry=2) at debug-dse-pointer-test.c:18
18 return baz(&s);
(gdb) print s
$2 = {a = 1, b = 2}
(gdb)
```
**Note that the outer function `foo` can correctly display the struct contents, wrong data only shows up when following the pointer argument in the inlined callee.**
Relevant DWARF:
```
0x00000059: DW_TAG_subprogram
DW_AT_low_pc (0x0000000000401110)
DW_AT_high_pc (0x0000000000401138)
DW_AT_frame_base (DW_OP_reg7 RSP)
DW_AT_call_all_calls (true)
DW_AT_name ("foo")
DW_AT_decl_file ("/usr/local/google/home/jannh/test/debug-dse-pointer-test.c")
DW_AT_decl_line (16)
DW_AT_prototyped (true)
DW_AT_type (0x00000038 "int")
DW_AT_external (true)
[...]
0x0000007a: DW_TAG_variable
DW_AT_location (indexed (0x2) loclist = 0x0000003e:
[0x0000000000401117, 0x0000000000401120): DW_OP_reg6 RBP, DW_OP_piece 0x4, DW_OP_reg3 RBX, DW_OP_piece 0x4
[0x0000000000401120, 0x0000000000401123): DW_OP_piece 0x4, DW_OP_reg3 RBX, DW_OP_piece 0x4
[0x0000000000401123, 0x0000000000401125): DW_OP_breg7 RSP+0, DW_OP_piece 0x4, DW_OP_reg3 RBX, DW_OP_piece 0x4
[0x0000000000401125, 0x0000000000401129): DW_OP_breg7 RSP+0, DW_OP_piece 0x4
[0x0000000000401129, 0x0000000000401138): DW_OP_breg7 RSP+0)
DW_AT_name ("s")
DW_AT_decl_file ("/usr/local/google/home/jannh/test/debug-dse-pointer-test.c")
DW_AT_decl_line (17)
DW_AT_type (0x00000041 "s")
0x00000083: DW_TAG_inlined_subroutine
DW_AT_abstract_origin (0x00000027 "baz")
DW_AT_low_pc (0x0000000000401117)
DW_AT_high_pc (0x0000000000401131)
DW_AT_call_file ("/usr/local/google/home/jannh/test/debug-dse-pointer-test.c")
DW_AT_call_line (18)
DW_AT_call_column (10)
0x00000090: DW_TAG_formal_parameter
DW_AT_location (DW_OP_reg7 RSP)
DW_AT_abstract_origin (0x0000002f "s")
```
Disassembly:
```
$ objdump --disassemble=foo -Mintel ./debug-dse-pointer-test
[...]
0000000000401110 <foo>:
401110: 55 push rbp
401111: 53 push rbx
401112: 50 push rax
401113: 89 f3 mov ebx,esi
401115: 89 fd mov ebp,edi
401117: 31 c0 xor eax,eax
401119: e8 42 00 00 00 call 401160 <blah>
40111e: ff c5 inc ebp
401120: 89 2c 24 mov DWORD PTR [rsp],ebp
401123: ff cb dec ebx
401125: 89 5c 24 04 mov DWORD PTR [rsp+0x4],ebx
401129: 48 89 e7 mov rdi,rsp
40112c: e8 3f 00 00 00 call 401170 <bar>
401131: 48 83 c4 08 add rsp,0x8
401135: 5b pop rbx
401136: 5d pop rbp
401137: c3 ret
```
Tested with:
```
clang version 22.0.0git (https://github.com/llvm/llvm-project a9b8dfe7b5f224e2d442352979cf2e0c1c0b539b)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/google/home/jannh/git/foreign/llvmp-build-debug/bin
Build config: +unoptimized, +assertions
```
When building with ` -mllvm --print-after-all`, you can see that the initial two `store` instructions in `foo` disappear during `DSEPass`:
```
[...]
; *** IR Dump After MemCpyOptPass on foo ***
; Function Attrs: noinline nounwind uwtable
define dso_local i32 @foo(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 !dbg !8 {
entry:
%s = alloca %struct.s, align 4, !DIAssignID !20
#dbg_assign(i1 poison, !15, !DIExpression(), !20, ptr %s, !DIExpression(), !21)
#dbg_value(i32 %a, !13, !DIExpression(), !21)
#dbg_value(i32 %b, !14, !DIExpression(), !21)
call void @llvm.lifetime.start.p0(ptr nonnull %s) #4, !dbg !22
store i32 %a, ptr %s, align 4, !dbg !23, !tbaa !24, !DIAssignID !29
#dbg_assign(i32 %a, !15, !DIExpression(DW_OP_LLVM_fragment, 0, 32), !29, ptr %s, !DIExpression(), !21)
%b2 = getelementptr inbounds nuw i8, ptr %s, i64 4, !dbg !30
store i32 %b, ptr %b2, align 4, !dbg !23, !tbaa !31, !DIAssignID !32
#dbg_assign(i32 %b, !15, !DIExpression(DW_OP_LLVM_fragment, 32, 32), !32, ptr %b2, !DIExpression(), !21)
#dbg_value(ptr %s, !33, !DIExpression(), !39)
%call.i = tail call i32 (...) @blah() #4, !dbg !41
%add.i = add nsw i32 %a, 1, !dbg !42
store i32 %add.i, ptr %s, align 4, !dbg !43, !tbaa !24, !DIAssignID !44
#dbg_assign(i32 %add.i, !15, !DIExpression(DW_OP_LLVM_fragment, 0, 32), !44, ptr %s, !DIExpression(), !21)
%sub.i = add nsw i32 %b, -1, !dbg !45
store i32 %sub.i, ptr %b2, align 4, !dbg !46, !tbaa !31, !DIAssignID !47
#dbg_assign(i32 %sub.i, !15, !DIExpression(DW_OP_LLVM_fragment, 32, 32), !47, ptr %b2, !DIExpression(), !21)
%call3.i = call i32 @bar(ptr noundef nonnull %s) #4, !dbg !48
call void @llvm.lifetime.end.p0(ptr nonnull %s) #4, !dbg !49
ret i32 %call3.i, !dbg !50
}
; *** IR Dump After DSEPass on foo ***
; Function Attrs: noinline nounwind uwtable
define dso_local i32 @foo(i32 noundef %a, i32 noundef %b) local_unnamed_addr #0 !dbg !8 {
entry:
%s = alloca %struct.s, align 4, !DIAssignID !20
#dbg_assign(i1 poison, !15, !DIExpression(), !20, ptr %s, !DIExpression(), !21)
#dbg_value(i32 %a, !13, !DIExpression(), !21)
#dbg_value(i32 %b, !14, !DIExpression(), !21)
call void @llvm.lifetime.start.p0(ptr nonnull %s) #4, !dbg !22
#dbg_assign(i32 %a, !15, !DIExpression(DW_OP_LLVM_fragment, 0, 32), !23, ptr %s, !DIExpression(), !21)
%b2 = getelementptr inbounds nuw i8, ptr %s, i64 4, !dbg !24
#dbg_assign(i32 %b, !15, !DIExpression(DW_OP_LLVM_fragment, 32, 32), !25, ptr %b2, !DIExpression(), !21)
#dbg_value(ptr %s, !26, !DIExpression(), !32)
%call.i = tail call i32 (...) @blah() #4, !dbg !34
%add.i = add nsw i32 %a, 1, !dbg !35
store i32 %add.i, ptr %s, align 4, !dbg !36, !tbaa !37, !DIAssignID !42
#dbg_assign(i32 %add.i, !15, !DIExpression(DW_OP_LLVM_fragment, 0, 32), !42, ptr %s, !DIExpression(), !21)
%sub.i = add nsw i32 %b, -1, !dbg !43
store i32 %sub.i, ptr %b2, align 4, !dbg !44, !tbaa !45, !DIAssignID !46
#dbg_assign(i32 %sub.i, !15, !DIExpression(DW_OP_LLVM_fragment, 32, 32), !46, ptr %b2, !DIExpression(), !21)
%call3.i = call i32 @bar(ptr noundef nonnull %s) #4, !dbg !47
call void @llvm.lifetime.end.p0(ptr nonnull %s) #4, !dbg !48
ret i32 %call3.i, !dbg !49
}
[...]
```
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzsWklv27r2_zTM5sCGREoeFlnYcfJHgXv_LdLi9e0MSqJltjIpkFSGLt5nfzikZMmOMnS4vW9xhQS2KZ6BvzOSErdWlkqIS5KuSbq54I3ba3Pp9uLL_iLTxeNlngNJoi8Hbawg0YpEq897oWAjeAEfnTYCrit5kIo7qRUQuiCzaPPx-gO3lswiQpcg2vvCgkUCC1I5DVyBzr6I3BF6BW4vLUgLzvD8qyhAKth8Xt3eQCGyppRqp8E2-R7cnjtwexHGS2Eg5wqsk1UFdq_v_b1KlzLnFVjHnQC984OtLLaGrHFwj2s44VNp_dUCd8Ch1lI5YcDpASUuTSqouXEybypuUG3P5oSAQ6VR9h03kmeVwEXV3FpRQFizVJVUogDbZEY3TipB6PLI60Slna4qfW8DOK0MnCkd3OOCa-2EcpJX1SNYIaBRUkn8Lb-JAg7ioM3jNNjsE7I4yHLvIBNgRMVd0GjvXG0JWxF6Q-hNKd2-yaa5PhB6U1V33cekNjqY6kZa2whL6A1bxDEllPmBXB8OQrnJchFHUbxIEghLkvkeEeCV1cAz3TjYfLyGnDdWqhICL7iXbt-uWqqy1fhW1EYXTS4Mahet0JnCX7T6wpXakyTaa6MIW_2H0BsnrCM0gZy7wGpSWDFpQZvg3WlOopV1pskdWCDzNYlWgK4InLD-RxZ-kPmm_RKt_HDF9-jcdBmG_Rg6waJjSejqeJdEq-2WO2dk1jix3XrKBa_u-aPdBg_wVl-27hAk828DdkDoyuKMTtNzDQDshLBrDoRtjl_pGuKT21l_O4PJ8aYRrjGqW0Gv93zzrPZKnynuYKe1jwqEEP3Sw9erfFyK9UqQ-XoatPWzp0G17Ig09Fp98yJnI4qhjAOXitDFnZbFQFpQJkbWdABSyzMa8vkh_6E_6EBn3jNfDzXy6jz1pc74Y7NfUJ7Qm8YajFnMQRjPWpeVIPRmrw_4EWh9nBN6s9NGyFK1MV5PskZWxcSvntCbDEG-ySuuyklMYVLC5D2FiX4Gnmej7kU4X1hMWWQwJfRmnBwRTtfT6ZSk3jPooiy872VG8K_ehaLVGr97KogxtUcPSRTH8ZywFexkJZ7PFPQKfFzG0fSEu2nUmeShDCTL-DfwMcU20cN8F65itvOl8KXkxFYx5rY4gvY6D_hei9qgOO8jOJzEXXyF8Eri5WJOI6-NH4ja8OlB8vhRFsFP60soiwGDD7lwwjacJJFQzjwStgmIELbJBoP0DZwXgTMFiB6i44XWSyNsDjAFQADnFVaUnqx8Z_hBQPyXKh4vOhO-kNDOjNlakp5aMu5tSM9seFYSffVZ_b92ou-RdINNya5ReejNZhHmyFnke6ZcGyNyVz1CIW1d8UdP0iagXCtsLawv4karEgruOGiFncYee5KmDg1L6FGwmCN51whxUzbYD6CpcLxrenJeVUJMg7Jdna_EHVcu9HtPa31n_3SJUQuw-bz9tPq_rW2y2ujS8INPvKfX5vN29Wlb6fttnaN1z30ojgKA43R7We6REMZI2aIlDXO9O20zbkXHg9DF5vP2_YetEeUcbj9-OJmPAGzxH7_Ydr4zjXhJIYUu2zEnlPpKR0_4FiKvtj6lhSlvrgQh2z6TZDERjsjxmRFr7ewlrWujnXaPtSgG0AyWGmbhhOPdDmy2AEKpVO5cvHhwwihenfE6Lwa918x58Jqj33R9ec8TEfLx0WspVSEeRBFU8lFf6byS1oVs2mkpgrcCSddPPGyOoXM-Sr3fMS85eMgMbtcfcGoYqaXIBZapfsiIksHt-t9jk8Zlh8z_ZJSdyP5-SWPXmHQ2Kj09kZ71wbGOfmb5b1UqHVVq-X1KPQP3cox3yBPP8R64dBfcIW5t7_FPrr8r0o9hgU79kmpdLA8iGVuT4aoGoblgZ6HZFontYGd81IZn1hmeu602sgylv-ND5yjCl9hXoHuhILyysq4kjBWE-GmC_w3m8XLOzLN4eRGeJNdVc_D4dUVwYJJl1JnkaJSdNgdebWuOhc4J81zefLnsnZvvSHM04u7MT4atTbTaSMutFYesenzaIeBmQWdfiuZQw2RSHKcKwjbY3k3-RCCrt28mzpsFIOwKay67bhM-tE3EEaw0hbqxe-z4srqfEQ9msNFcFchM9tAT0QFR9AIRgOEDMtaTLZawGxN30Hf4IbIHQq-ElT1xekpc9FNrnFoMps77qSyGfEzDB208MfdygpKedtnTigUkFKKo_Tte6KQQRM088H4XxK57BYTfve0gT0dES5V3encENDpZHM2BJt36Np_f327gw6dbTOzG1mh_enVCPcAVhWYjQguRe1CPNKdwpl5ilIzZ4okCdI2VplXjiBwdIJcskKmYP2NdU0hCr5DVUZ38BHa2exn2eYCdmyHqLD5VgEGeQLQAXvjuzmt-FT0seoIBBukYaOjGuvbE_TrZbEBVvELVr5ANnDJnuOt6mkI-CetE4c8an6YQf8oBd8JYTGiUTqNpVEp_5vt9B6TAl9mi2Il5lu4oTQQtkoSylC7ny3xHRZTHeZSlbNnu4D5xUwqHyj8sZttZMmnUV6Xv1aSSqnmYlKrxZ7dGcAzJQlQ4tdZWImLvlHW4nyo20uD4Lz_9iVZrHMPd4E6WQcS6Ubp28iC_iQL7HkLXmG4NVgL7FHX_rMBzxi2iP-jFDejkgDJhMvGb3wnfYSbmVeWfGFzBo27Cub4Y7Gbbs21w9xp5-EcJyEuqsGlFBXC_2W9zsRTUteAGisag_NNHE08LyXAPwdbQblPpCt7dwgbLywoVhT_F4ap-fF875ARatYcIq35by9Zw0227V84ZdCDoDlBB6UbdS1VAc-_avUghdninsHobnh9IRoEkUXvCyqgnKgSWyTQctp6OZe0uhVfbRmFPWWx5URjwhzyExkVW4seiPbdsjzRCNSM0DSe0vEIO_reHdOoPAHglSwVJsHa8ebfyz4zebfAXjXxXTFmRldvwMAn1jaHW0mrV0sTpkfj6oTbCYpi1R1vhRtiz1M544a_OjvtOp5V9xyvcDXqoOohQMvt5XlnHK3k7L59P77Qs0Ijo69NK7oSTBzG1jhs3rSNCF7hepZVqqqpd9xJ16OS0NvMnWBCencFwfUO4To3UUXardxnnfuAZK54B0BvyDM1n7Bg6vz_--Nef253h5UEo_1DP25TRAT7LH7MyGiGcjJXCiUqgAOQiVYYRYEE19yAX58zlLDlHhEVPscwGdBl9O5gsHgWT0dGQOPOl70OS0XMow8iJ1j_o5WfmYK9GDFsO7IKOPpXeNo7LKjh-WOwCkyl6dBL1R9kj_p3EHTNeFC0v7CuUvT9x9_iMbCwskMGbQiN5Y2gkySuh0Un8BeGRJD8cHrbJxpHzDjc5hy59Cp3n8LZISGZvi4Rk_jJ2R5G_IiCS-Q8GROvErMWvd-AkCg_lQpYOlfb1bJ0sXisAQhXfk_6TZfvAtrNUq-3prDTqn5K-0Lq07c8_Tcu4X_7Tt_zyvuWvbinY391S0FcqxC8r-eEQ-y8o-XT2asmnv7bks3CY_p0Vn42UrbdXfPakas3Hqxb9jRWf_q6Kz36u4idn2CXpOHaz31nxZ_8jFX_-qyv-4i0V3_cFbcUfHlx0BxoXxSUrlmzJL8RlPE-XjC3T5fxif7mk0WK2jPNiNhezXZrRNMoZT6N4xnezPOIX8pJGNI2jKI7ndMbm0zktlllGeZxns5zO5ySJxIHLauqXqU154d_ku4xncZqmFxXPRGX9K6WUKnEf3vMjlJJ0c2Eu_XlZ1pQWcZLW2Z6Nk67y76JuRNaU79ROk3QDhTBiJ4xQuVTl8GXLs1ct2zcTTt6C9O8tFI3A6ZuP1xeNqS5__J3HdoF3l_S_AQAA__9lzPzJ">