https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114144
Bug ID: 114144 Summary: Variables optimized out by -Og Product: gcc Version: 14.0 Status: UNCONFIRMED Severity: normal Priority: P3 Component: debug Assignee: unassigned at gcc dot gnu.org Reporter: lukas.gra...@tu-darmstadt.de Target Milestone: --- -Og seems to <optimize out> some variable values: On x86-64, function parameters are lost after calling other functions, they were not saved (to the stack). This could be undesired when debugging. Consider the following example: ===== foo.c ======================== int foo (int i) { return i + 1; } ===== caller.c ===================== extern int foo(int); int main(int argc, char **argv) { int i = foo(argc); int v = foo(i); return v; } ==================================== Compile on x86-64: $ gcc -Og -g caller.c foo.c -o caller Debug with breakpoint after calling foo(): $ gdb caller Reading symbols from caller... (gdb) break caller.c:5 Breakpoint 1 at 0x401116: file caller.c, line 5. (gdb) run Starting program: /home/lukas/test/caller Breakpoint 1, main (argc=<optimised out>, argv=<optimised out>) at caller.c:5 5 return v; (gdb) print argc $1 = <optimised out> (gdb) print i $2 = <optimised out> (gdb) print v $3 = 3 ----------------------------------------------- For 32-bit x86 with "-m32 -Og -g" we would get argc and argv because the calling conventions put them on the stack and not a caller-saved variable. However, the variable i would still be <optimised out> at the breakpoint. ----------------------------------------------- EXPECTED RESULT by compiling the same with -O0: $ gcc -O0 -g caller.c foo.c -o caller $ gdb caller (gdb) break caller.c:5 Breakpoint 1 at 0x40112f: file caller.c, line 5. (gdb) run Starting program: /home/lukas/test/caller Breakpoint 1, main (argc=1, argv=0x7fffffffdc98) at caller.c:5 5 return 0; (gdb) print argc $1 = 1 (gdb) print i $2 = 2 (gdb) print v $3 = 3 ----------------------------------------------- This is not a problem of the debugger gdb: By looking at the DWARF debugging info, it turns out that argc has indeed been optimised out: $ objdump -W caller [...] <2><6d>: Abbrev Number: 1 (DW_TAG_formal_parameter) <6e> DW_AT_name : (indirect string, offset: 0x11): argc <72> DW_AT_decl_file : 1 <72> DW_AT_decl_line : 2 <72> DW_AT_decl_column : 14 <73> DW_AT_type : <0x44> <77> DW_AT_location : 0x10 (location list) <7b> DW_AT_GNU_locviews: 0xc [...] Contents of the .debug_loclists section: [...] 00000010 v000000000000000 v000000000000000 views at 0000000c for: 0000000000401106 000000000040110e (DW_OP_reg5 (rdi)) 00000015 v000000000000000 v000000000000000 views at 0000000e for: 000000000040110e 000000000040111b (DW_OP_entry_value: (DW_OP_reg5 (rdi)); DW_OP_stack_value) 0000001d <End of list> [...] Our breakpoint at location 0x401116 is inside the second range of the loclist. However, we cannot compute "DW_OP_entry_value: (DW_OP_reg5 (rdi))" at 0x401116, since it refers to a state at a previous location (the value of rdi at the subprogram entry). Also, from the disasambly you can clearly see that caller-saved registers %edi and %eax are not saved (neither to the stack nor to callee-saved registers) before calling foo: $ objdump -d caller [...] 0000000000401106 <main>: 401106: 48 83 ec 08 sub $0x8,%rsp 40110a: e8 0c 00 00 00 callq 40111b <foo> 40110f: 89 c7 mov %eax,%edi 401111: e8 05 00 00 00 callq 40111b <foo> 401116: 48 83 c4 08 add $0x8,%rsp 40111a: c3 retq [...] And if you don't like breakpoints, you could modify caller.c as follows to automatically break by calling abort: ===== caller2.c ===================== #include <stdlib.h> extern int foo(int); int main(int argc, char **argv) { int i = foo(argc); int v = foo(i); abort(); } ===================================== $ gcc -Og -g caller2.c foo.c -o caller2 $ gdb ./caller2 (gdb) run Program received signal SIGABRT, Aborted. (gdb) bt #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50 #1 0x00007ffff7dcd859 in __GI_abort () at abort.c:79 #2 0x000000000040113b in main (argc=<optimised out>, argv=<optimised out>) at caller2.c:6 Here, too, we have argc=<optimised out>. ---------------------------------------------------------- According to the documentation of -Og: "-Og should be the optimization level of choice for the standard edit-compile-debug cycle, offering a reasonable level of optimization while maintaining fast compilation and a good debugging experience." "It is a better choice than -O0 for producing debuggable code because some compiler passes that collect debug information are disabled at -O0." But for many cases, -O0 currently seems to be the better choice. Because -O0 saves all values to the stack, they will not be <optimized out>. If -Og is working as designed, then the documentation is misleading. In this case, I would suggest to add something like the following to the documentation: "-Og might also make the code less debuggable than -O0, because of some optimizations for speed and size." Observed in PR 38534.