Issue 230109.1: Values for optimized out arguments
Author: | Jakub Jelinek |
---|---|
Champion: | Jakub Jelinek |
Date submitted: | 2023-01-09 |
Date revised: | 2023-10-02 |
Date closed: | |
Type: | Enhancement |
Status: | Open |
DWARF Version: | 6 |
Section 2.5.1.7, pg 38
If some function has arguments that are never used in the function
or which are only used in code which compiler can prove are unreachable
and the function isn't accessible from other object files (or compiler creates
a local clone of a function), then some compilers can choose not to pass
that parameter at all. If that happens, DW_OP_entry_value
isn't an option,
because when the argument isn't passed at all, there is no register or memory
location for it.
Consider:
static __attribute__((noinline)) int foo (int x, int y) { int z = x + y; return x; }
int bar (void) { return foo (3, 17) + foo (4, 18) + foo (5, 19); }
where noinline attribute is just used to keep the example sufficiently small
to explain. As y
isn't stored anywhere but to a dead variable, there is no
need to pass it at all, so compiler can effectively emit
static int foo_alt (int x) { return x; }
and call it 3 times.
Still users might need to ask in a debugger the value
of y
or z
variables and it is possible to provide that value.
Unlike DW_OP_entry_value
which can be used in debug info consumers in 2 different
ways, one is to find the caller (if possible) and if it can be found, look up
the register or memory referenced in DW_OP_entry_value
in the
DW_TAG_call_site_parameter
, or (mostly for non-interactive consumers) if we
know in advance we'll need DW_OP_entry_value
, put a breakpoint at the start of
the function and collect there a value, then look up the remembered value
when evaluating DW_OP_entry_value
, for the case of completely optimized away
arguments the latter method is not an option, there is nothing to remember
at function entry. Still, the optimized away arguments can be found
with the former method.
The following proposal introduces a new DWARF expression Special operation
DW_OP_parameter_ref
.
In the above testcase, y
would have DW_AT_location
of
DW_OP_parameter_ref <y die> DW_OP_stack_value
and z
would have DW_AT_location
of
DW_OP_breg5 <0> DW_OP_parameter_ref <y die> DW_OP_plus DW_OP_stack_value
(where DW_OP_breg5 <0>
is just an example from x86_64 how to get value of x
).
Then, in the call sites of the 3 foo calls, one would have next to
DW_TAG_call_site_parameter
DW_AT_location DW_OP_reg5 // again x86_64 example where x is passed
DW_AT_call_value DW_OP_lit{3,4,5}
for the call site parameter of x
also
DW_TAG_call_site_parameter
DW_AT_call_parameter <y die>
DW_AT_call_value DW_OP_lit{17,18,19}
Changes relative to dwarf6-20221116.pdf:
In 2.5.1.7 add:
3.
DW_OP_parameter_ref
The
DW_OP_parameter_ref
operation pushes the value that the described parameter would have if it was actually passed. It has a single operand: In the 32-bit DWARF format, the operand is a 4-byte unsigned value; in the 64-bit DWARF format, it is an 8-byte unsigned value (see Section 7.4 following). The operand is used as the offset of aDW_TAG_formal_parameter
debugging information entry in a.debug_info
section. When evaluating DW_OP_parameter_ref, the consumer can try to virtually unwind using the Call Frame Information (see Section 6.4 on page 174) and findDW_TAG_call_site_parameter
referencing the sameDW_TAG_formal_parameter
debugging information entry through itsDW_AT_call_parameter
attribute.
In 7.7.1 add to Table 7.9:
DW_OP_parameter_ref ‡ 0xab 1 4- or 8-byte offset of DIE
In D.1.3 add to the end:
DW_OP_parameter_ref y DW_OP_stack_value
The values of parameter y found in DW_TAG_call_site_parameter
of the caller with DW_AT_call_parameter referencing also parameter y.
Add after D.15.2:
D.15.3 Example with optimized out parameter
Consider the C source in figure D.NN:
static int foo (int x, int y) { int z = x + y; return x; } int bar (void) { return foo (3, 17) + foo (4, 18) + foo (5, 19); }
Figure D.NN: Example with optimized out parameter: Source
where the producer doesn't inline the foo function, but because the
y
argument is never needed in generated code doesn't pass it at runtime at all. Possible generated code could be:foo.local.clone: return bar: ! Decrease stack pointer to reserve local stack frame %reg3 = %reg3 - 32 [%reg3] = %reg4 ! Save the call preserved register to stack %reg0 = 3 ! Load the 1st argument to foo call foo.local.clone ! 17 is never passed L20: %reg4 = %reg0 %reg0 = 4 ! Load the 1st argument to foo call foo.local.clone ! 18 is never passed L21: %reg4 = %reg4 + %reg0 %reg0 = 5 ! Load the 1st argument to foo call foo.local.clone ! 19 is never passed L22: %reg0 = %reg4 + %reg0 %reg4 = [%reg3] ! Restore original value of call preserved register %reg3 = %reg3 + 32 ! Leave stack frame return
Figure D.MM: Example with optimized out parameter: Code
Debug information for the parameters of foo function and the local variable z would be:
DW_TAG_formal_parameter DW_AT_name "x" DW_AT_type(int) DW_AT_location(DW_OP_reg0) DW_TAG_formal_parameter ! y_die DW_AT_name "y" DW_AT_type(int) DW_AT_location(DW_OP_parameter_ref y_die DW_OP_stack_value) DW_TAG_variable DW_AT_name "z" DW_AT_type(int) DW_AT_location(DW_OP_breg0 0 DW_OP_parameter_ref y_die DW_OP_plus DW_OP_stack_value)
and call site information for the 3 calls:
DW_TAG_call_site DW_AT_call_return_pc(L20) DW_AT_call_origin(reference to subprogram foo) DW_TAG_call_site_parameter DW_AT_location(DW_OP_reg0) DW_AT_call_value(DW_OP_lit3) DW_TAG_call_site_parameter DW_AT_call_parameter(y_die) DW_AT_call_value(DW_OP_lit17) DW_TAG_call_site DW_AT_call_return_pc(L21) DW_AT_call_origin(reference to subprogram foo) DW_TAG_call_site_parameter DW_AT_location(DW_OP_reg0) DW_AT_call_value(DW_OP_lit4) DW_TAG_call_site_parameter DW_AT_call_parameter(y_die) DW_AT_call_value(DW_OP_lit18) DW_TAG_call_site DW_AT_call_return_pc(L22) DW_AT_call_origin(reference to subprogram foo) DW_TAG_call_site_parameter DW_AT_location(DW_OP_reg0) DW_AT_call_value(DW_OP_lit5) DW_TAG_call_site_parameter DW_AT_call_parameter(y_die) DW_AT_call_value(DW_OP_lit19)
2023-10-02: Updated examples.