In this minimized C example, variables i and l_3, defined within the scope of the inlined function foo, are associated with DWARF symbols having possibly wrong location definition, which causes variables to not be available during debugging (in the example, the variables are used as call arguments to a function defined in an external module). We observe the bug at -O2 and -O3 with the root cause possibly being inlining. Please find below a detailed analysis for -O2 (with -O3 DWARF info looks identical) on x64, including a comparison with past gcc versions where the bug is sometimes not present. Furthermore, once we make both variables appear by disabling some optimizations, the program reveals a possible bug in gdb, which ends up displaying an incorrect value for variable i while lldb shows the correct one. We are filing a separate bug report, linking it in a comment below, as the behavior is quite articulated. $ cat a.c void foo() { int l_3 = 5, i = 0; for (; i < 8; i++) ; test(l_3, i); } int main() { foo(); } $ cat lib.c #include <stdio.h> void test(int l_3, int i) { printf("%d %d", l_3, i); } GCC and GDB version (GCC commit id: 500d3f0a302): $ gcc --version gcc (GCC) 12.0.0 20211227 (experimental) Copyright (C) 2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ gdb --version GNU gdb (GDB) 11.2 Copyright (C) 2022 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. GDB trace: $ gcc -O2 -g a.c -o opt $ gdb -q opt Reading symbols from opt... (gdb) b 6 Breakpoint 1 at 0x400410: /home/stepping/2/reduce/a.c:6. (2 locations) (gdb) r Starting program: /home/stepping/2/reduce/opt Breakpoint 1, main () at a.c:6 6 test(l_3, i); (gdb) info loc No locals. ASM at -O2: 0000000000400410 <main>: 400410: 48 83 ec 08 sub $0x8,%rsp 400414: be 08 00 00 00 mov $0x8,%esi 400419: bf 05 00 00 00 mov $0x5,%edi 40041e: 31 c0 xor %eax,%eax 400420: e8 1b 01 00 00 callq 400540 <test> 400425: 31 c0 xor %eax,%eax 400427: 48 83 c4 08 add $0x8,%rsp 40042b: c3 retq 40042c: 0f 1f 40 00 nopl 0x0(%rax) DWARF info at -O2: 0x00000065: DW_TAG_inlined_subroutine DW_AT_abstract_origin (0x000000ac "foo") DW_AT_entry_pc (0x0000000000400410) DW_AT_unknown_2138 (0x02) DW_AT_ranges (0x0000000c [0x0000000000400410, 0x0000000000400410) [0x0000000000400414, 0x0000000000400425)) DW_AT_call_file ("/home/stepping2/reduce/a.c") DW_AT_call_line (10) DW_AT_call_column (0x05) 0x0000007a: DW_TAG_lexical_block DW_AT_ranges (0x0000000c [0x0000000000400410, 0x0000000000400410) [0x0000000000400414, 0x0000000000400425)) 0x0000007f: DW_TAG_variable DW_AT_abstract_origin (0x000000b9 "l_3") 0x00000084: DW_TAG_variable DW_AT_abstract_origin (0x000000c3 "i") DW_AT_location (0x0000000e: [0x0000000000400410, 0x0000000000400410): DW_OP_lit0, DW_OP_stack_value) DW_AT_unknown_2137 (0x0000000c) 0x00000091: DW_TAG_call_site DW_AT_call_return_pc (0x0000000000400425) DW_AT_call_origin (0x0000002a) 0x0000009e: DW_TAG_call_site_parameter DW_AT_location (DW_OP_reg5 RDI) DW_AT_call_value (DW_OP_lit5) 0x000000a3: DW_TAG_call_site_parameter DW_AT_location (DW_OP_reg4 RSI) DW_AT_call_value (DW_OP_lit8) ... 0x000000da: DW_TAG_subprogram DW_AT_abstract_origin (0x000000ac "foo") DW_AT_low_pc (0x0000000000400520) DW_AT_high_pc (0x0000000000400531) DW_AT_frame_base (DW_OP_call_frame_cfa) DW_AT_call_all_calls (true) 0x000000f1: DW_TAG_variable DW_AT_abstract_origin (0x000000b9 "l_3") 0x000000f6: DW_TAG_variable DW_AT_abstract_origin (0x000000c3 "i") DW_AT_location (0x0000001e: [0x0000000000400520, 0x0000000000400520): DW_OP_lit0, DW_OP_stack_value) DW_AT_unknown_2137 (0x0000001c) 0x00000103: DW_TAG_call_site DW_AT_call_return_pc (0x0000000000400531) DW_AT_call_tail_call (true) DW_AT_call_origin (0x0000002a) 0x00000110: DW_TAG_call_site_parameter DW_AT_location (DW_OP_reg5 RDI) DW_AT_call_value (DW_OP_lit5) 0x00000115: DW_TAG_call_site_parameter DW_AT_location (DW_OP_reg4 RSI) DW_AT_call_value (DW_OP_lit8) From an initial assessment, there may be multiple factors at play behind the unavailability of variables i and l_3: - the empty range in the location definition for variable i; - the missing location definition for variable l_3 together with the missing const value attribute in the variable DIE in the subprogram DIE of function foo; - the empty ranges in both the inlined subroutine location definition and the lexical block location definition nested in the inlined subroutine. Through some testing we found out that the optimizations that make the variables disappear are related to inlining (-fearly-inlining, -finline, …) at both -O2 and -O3 but include also -ftree-dce at -O2 (since foo does not get inlined under -fno-tree-dce). If we disable either inlining or -ftree-dce, variables l_3 and i appear in the current frame. However, a wrong value for i is displayed at the call site (0 instead of 8, see also the gdb bug report). When compiling with -fno-tree-dce lldb reports the correct value, while when disabling inlining it reports i as not available. ASM with -fno-tree-dce at -O2: 0000000000400410 <main>: 400410: 48 83 ec 08 sub $0x8,%rsp 400414: 31 c0 xor %eax,%eax 400416: e8 05 01 00 00 callq 400520 <foo> 40041b: 31 c0 xor %eax,%eax 40041d: 48 83 c4 08 add $0x8,%rsp 400421: c3 retq 400422: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 400429: 00 00 00 40042c: 0f 1f 40 00 nopl 0x0(%rax) ... 0000000000400520 <foo>: 400520: be 08 00 00 00 mov $0x8,%esi 400525: bf 05 00 00 00 mov $0x5,%edi 40052a: 31 c0 xor %eax,%eax 40052c: e9 0f 00 00 00 jmpq 400540 <test> 400531: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 400538: 00 00 00 40053b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) DEBUG info with -fno-tree-dce at -O2: 0x00000070: DW_TAG_subprogram DW_AT_external (true) DW_AT_name ("foo") DW_AT_decl_file ("/home/stepping/2/reduce/a.c") DW_AT_decl_line (1) DW_AT_decl_column (0x06) DW_AT_low_pc (0x0000000000400520) DW_AT_high_pc (0x0000000000400531) DW_AT_frame_base (DW_OP_call_frame_cfa) DW_AT_call_all_calls (true) 0x0000008a: DW_TAG_variable DW_AT_name ("l_3") DW_AT_decl_file ("/home/stepping/2/reduce/a.c") DW_AT_decl_line (3) DW_AT_decl_column (0x09) DW_AT_type (0x00000039 "int") DW_AT_const_value (0x05) 0x00000097: DW_TAG_variable DW_AT_name ("i") DW_AT_decl_file ("/home/stepping/2/reduce/a.c") DW_AT_decl_line (3) DW_AT_decl_column (0x12) DW_AT_type (0x00000039 "int") DW_AT_location (0x0000001e: [0x0000000000400520, 0x0000000000400520): DW_OP_lit0, DW_OP_stack_value [0x0000000000400520, 0x0000000000400520): DW_OP_lit1, DW_OP_stack_value [0x0000000000400520, 0x0000000000400520): DW_OP_lit2, DW_OP_stack_value [0x0000000000400520, 0x0000000000400520): DW_OP_lit3, DW_OP_stack_value [0x0000000000400520, 0x0000000000400520): DW_OP_lit4, DW_OP_stack_value [0x0000000000400520, 0x0000000000400520): DW_OP_lit5, DW_OP_stack_value [0x0000000000400520, 0x0000000000400520): DW_OP_lit6, DW_OP_stack_value [0x0000000000400520, 0x0000000000400520): DW_OP_lit7, DW_OP_stack_value [0x0000000000400520, 0x0000000000400531): DW_OP_lit8, DW_OP_stack_value) DW_AT_GNU_locviews (0x0000000c) We tested the program with older gcc versions. Within each version, the behavior at -O2 and -O3 is consistent: same generated DWARF info and debugging experience w.r.t. visible variables. gcc-6: both variables are visible and with correct values ggc-7: both variables are visible with i's value being incorrect (not only in gdb, also lldb shows 0 instead of 8) gcc-{8-9-10-11}: neither variable is visible (just like in the 500d3f0a302 git version that we tested) We are discussing gcc-7 below as we found some interesting details. ASM from gcc-7 at -O2: 0000000000000560 <main>: 560: 48 83 ec 08 sub $0x8,%rsp 564: be 08 00 00 00 mov $0x8,%esi 569: bf 05 00 00 00 mov $0x5,%edi 56e: 31 c0 xor %eax,%eax 570: e8 3b 01 00 00 callq 6b0 <test> 575: 31 c0 xor %eax,%eax 577: 48 83 c4 08 add $0x8,%rsp 57b: c3 retq 57c: 0f 1f 40 00 nopl 0x0(%rax) DWARF info generated for function foo: DW_TAG_inlined_subroutine DW_AT_abstract_origin (0x0000009e "foo") DW_AT_low_pc (0x0000000000000564) DW_AT_high_pc (0x0000000000000575) DW_AT_call_file ("/home/stepping/2/reduce/a.c") DW_AT_call_line (10) 0x00000061: DW_TAG_lexical_block DW_AT_low_pc (0x0000000000000564) DW_AT_high_pc (0x0000000000000575) 0x00000072: DW_TAG_variable DW_AT_abstract_origin (0x000000e7 "l_3") 0x00000077: DW_TAG_variable DW_AT_abstract_origin (0x000000ed "i") 0x0000007c: DW_TAG_GNU_call_site DW_AT_low_pc (0x0000000000000575) DW_AT_abstract_origin (0x0000010c "test") 0x00000089: DW_TAG_GNU_call_site_parameter DW_AT_location (DW_OP_reg5 RDI) DW_AT_GNU_call_site_value (DW_OP_lit5) 0x0000008e: DW_TAG_GNU_call_site_parameter DW_AT_location (DW_OP_reg4 RSI) DW_AT_GNU_call_site_value (DW_OP_lit8) ... 0x000000cc: DW_TAG_subprogram DW_AT_abstract_origin (0x0000009e "foo") DW_AT_low_pc (0x0000000000000690) DW_AT_high_pc (0x00000000000006a1) DW_AT_frame_base (DW_OP_call_frame_cfa) DW_AT_GNU_all_call_sites (true) DW_AT_sibling (0x0000010c) 0x000000e7: DW_TAG_variable DW_AT_abstract_origin (0x000000aa "l_3") DW_AT_const_value (0x05) 0x000000ed: DW_TAG_variable DW_AT_abstract_origin (0x000000b5 "i") DW_AT_const_value (0x00) 0x000000f3: DW_TAG_GNU_call_site DW_AT_low_pc (0x00000000000006a1) DW_AT_GNU_tail_call (true) DW_AT_abstract_origin (0x0000010c "test") 0x00000100: DW_TAG_GNU_call_site_parameter DW_AT_location (DW_OP_reg5 RDI) DW_AT_GNU_call_site_value (DW_OP_lit5) 0x00000105: DW_TAG_GNU_call_site_parameter DW_AT_location (DW_OP_reg4 RSI) DW_AT_GNU_call_site_value (DW_OP_lit8) Differently from the latest gcc version that we tested, with gcc 7 in the subprogram DIE of the foo function there is the attribute const value defined for variable l_3 that makes the variable's value available. We can also see that there is the attribute const value defined for variable i that makes the variable have a wrong value during debugging.
The gdb bug report can be found at: https://sourceware.org/bugzilla/show_bug.cgi?id=28987
Before cddce1: <bb 2> : # DEBUG BEGIN_STMT # DEBUG l_3 => 5 # DEBUG i => 0 # DEBUG BEGIN_STMT goto <bb 4>; [INV] <bb 3> : # DEBUG BEGIN_STMT i_6 = i_1 + 1; # DEBUG i => i_6 <bb 4> : # i_1 = PHI <0(2), i_6(3)> # DEBUG i => i_1 # DEBUG BEGIN_STMT if (i_1 != 8) goto <bb 3>; [INV] else goto <bb 5>; [INV] <bb 5> : # DEBUG BEGIN_STMT test (5, 8); After: <bb 2> [local count: 1073741824]: # DEBUG BEGIN_STMT # DEBUG l_3 => 5 # DEBUG i => 0 # DEBUG BEGIN_STMT # DEBUG i => NULL # DEBUG BEGIN_STMT # DEBUG BEGIN_STMT test (5, 8); [tail call] Note CCP is able to figure out l_3 => 5 piece. When VRP figures out i => 8 and changes "i_1 <= 7" into "i_1 != 8", it does not add a debug statement for i => 8 after the branch (thinking it does not need one, I don't think it needs one but CDDEC should be fixed such that it change i => NULL to 8 I think ....)
(In reply to Andrew Pinski from comment #2) > Before cddce1: > <bb 2> : > # DEBUG BEGIN_STMT > # DEBUG l_3 => 5 > # DEBUG i => 0 > # DEBUG BEGIN_STMT > goto <bb 4>; [INV] > > <bb 3> : > # DEBUG BEGIN_STMT > i_6 = i_1 + 1; > # DEBUG i => i_6 > > <bb 4> : > # i_1 = PHI <0(2), i_6(3)> > # DEBUG i => i_1 > # DEBUG BEGIN_STMT > if (i_1 != 8) > goto <bb 3>; [INV] > else > goto <bb 5>; [INV] > > <bb 5> : > # DEBUG BEGIN_STMT > test (5, 8); > > After: > <bb 2> [local count: 1073741824]: > # DEBUG BEGIN_STMT > # DEBUG l_3 => 5 > # DEBUG i => 0 > # DEBUG BEGIN_STMT > # DEBUG i => NULL > # DEBUG BEGIN_STMT > # DEBUG BEGIN_STMT > test (5, 8); [tail call] > > > Note CCP is able to figure out l_3 => 5 piece. When VRP figures out i => 8 > and changes "i_1 <= 7" into "i_1 != 8", it does not add a debug statement > for i => 8 after the branch (thinking it does not need one, I don't think it > needs one but CDDEC should be fixed such that it change i => NULL to 8 I > think ....) Passes usually do not try to be too clever in creating debuginfo, in this case inserting a debug-bind with a loop final value. Instead CDDCE does it correctly and resets 'i' from the initial value. <bb 2> : [t.c:3:4] # DEBUG BEGIN_STMT [t.c:3:8] # DEBUG l_3 => 5 [t.c:3:17] # DEBUG i => 0 [t.c:4:4] # DEBUG BEGIN_STMT # DEBUG i => NULL [t.c:4:13] # DEBUG BEGIN_STMT [t.c:6:4] # DEBUG BEGIN_STMT [t.c:6:4] test (5, 8); [t.c:7:1] return; I think that's exactly OK. What's the thing to improve is EVRP which does void foo () { int i; @@ -37,14 +58,14 @@ # i_1 = PHI <[t.c:3:17] 0(2), [t.c:4:19] i_6(3)> # DEBUG i => i_1 [t.c:4:13] # DEBUG BEGIN_STMT - [t.c:4:13] if (i_1 <= 7) + [t.c:4:13] if (i_1 != 8) goto <bb 3>; [INV] else goto <bb 5>; [INV] <bb 5> : [t.c:6:4] # DEBUG BEGIN_STMT - [t.c:6:4] test (5, i_1); + [t.c:6:4] test (5, 8); [t.c:7:1] return; as you say, but when propagating a constant it should make sure to insert a debug stmt at the definition it removes (it doesn't remove any - but in principle it adds i_42 = 8; in bb5 and updates SSA form to use the new name in dominating stmts). This isn't an issue with CCP which doesn't have contextual lattices, so there's always a definition that is later replaced with a debug stmt. The substitute-and-fold machinery could in principle be taught to do this, but it would need to know whether it faces a "contextual" value (constant or SSA name) or if there's an actual definition with the propagated value. Not sure why this should be classified as wrong-debug - the debug info is conservatively correct. It's just lacking ...