010301.3 | A | T. Allen | Representation | Thin inlines |
PURPOSE:
This is best illustrated with an example. Consider this example:
1: extern int func();
2: extern void eat(int x);
3:
4: inline int triple(int x) { return x * 3; }
5: inline int tripleplus(int x) { return triple(x) + 1; }
6:
7: int main() {
8: int i = func();
9: int j = tripleplus(i);
10: eat(j);
11: }
Some possible generated (and abbreviated) PowerPC assembly for this might
look like. I've added the decoded line number information from the
.dwarf_lines table to the far right.
main:
addi
r1,r1,-FRAMESIZE
# line 7
mflr
r13
# line 7
stw
r13,FRAMESIZE+8(r1)
# line 7
bl
func
# line 8
..inline.tripleplus_start:
..inline.triple_start:
slwi
r4,r3,2 # multiply by 3 # line 4
subf
r4,r3,r4 # "
# line 4
..inline.triple_end:
addi
r3,r4,1 # add 1
# line 5
..inline.tripleplus_end:
bl
eat
# line 10
lwz
r13,FRAMESIZE+8(r1)
# line 11
li
r3,0
# line 11
addi
r1,r1,FRAMESIZE
# line 11
mtlr
r13
# line 11
blr
# line 11
..main_end:
The ".." labels are included solely so that they can be referenced by the
DWARF DW_AT_low_pc and DW_AT_high_pc attributes. I'm including them here
because they nicely bracket the concrete inline instances.
Say the debugger stops at the "subf" instruction in the middle of the inline
call to triple() called from the inline call to tripleplus() called from
main(). The debugger will show that the user is stopped at line 4. We want
our debugger to "hide" the fact that we're in an inlined call and present it
as if it were an out-of-line call. Most of the time, the implementation
detail of whether a call is inline or out-of-line isn't something that most
users want to worry about. This is especially true if the inline was
performed at the compiler's discretion.
Now say the user uses the debugger's "up" command which should present the
next containing subroutine activation. Ideally, the debugger should present
a "virtual stack frame" for the containing inlined routine, and should
indicate that the user is stopped at line 5 where the call to triple() from
tripleplus() is made. (Maybe it isn't so obvious why this is desirable in
this tiny example. I'm trying to keep the size of the explanation small.
But imagine there being local variables in tripleplus() to which the user
would want visibility. Then, it's desirable that it be clear that he's in
tripleplus() and that the debugger's visibility code find the variables in
tripleplus()).
How can a debugger present the user as being at line 5 in tripleplus()? In
general it can't. You could devise heuristics that try to find the first
instruction either before or after the inline region that you've gone "up"
past, but that's problematic. In this example, a heuristic that used the
instruction before would get line 8, which is one "virtual frame" too far
"up". Using the instruction after would work in this example, but it's
trivial to construct an example where it wouldn't.
Going "up" another "virtual frame" takes us to the real frame for
main().
Again, trying to use the before or after heuristic will yield the wrong line.
In this case, the wrong lines at least are close by, but there's no guarantee
that would be the case. For instance, imagine that the inline call is
bracketed on both side by other inline calls instead of code directly in
main(). Presenting the lines for those other inline calls would be seriously
misleading.
I realize that this is a quality-of-implementation issue for the debugger.
But it's something that shouldn't be precluded by DWARF.
We concluded that the format of the .debug_lines section was not suitable for
describing line numbers of these "virtual frames", especially given that they
could be arbitrarily deep.
The DWARF debugging information entries for the concrete inline instances,
though, are suitable for this arbitrary depth. And our debugger already was
determining the correct scope in the DWARF debugging information entries, so
that it could do proper visibility lookups on local and up-level variables.
So, we augmented the DW_TAG_inlined_subroutine entries with the following
attributes:
DW_AT_call_file
DW_AT_call_line
DW_AT_call_column
These attributes describe the file, line and column of the call site of the
inlined subroutine.
In the above example, the debugging information entries for main would look
like this (abbreviated to only the relevant information):
DW_TAG_subprogram
DW_AT_name(DW_FORM_string) "main"
DW_AT_low_pc(DW_FORM_addr) main
DW_AT_high_pc(DW_FORM_addr) ..main_end
DW_AT_decl_line(DW_FORM_udata) 7
:
DW_TAG_inlined_subroutine
DW_AT_abstract_origin(DW_FORM_ref_addr) ->
DIE for tripleplus
DW_AT_low_pc(DW_FORM_addr)
..inline.tripleplus_start
DW_AT_high_pc(DW_FORM_addr)
..inline.tripleplus_end
DW_AT_call_line(DW_FORM_udata) 9
DW_TAG_formal_parameter
DW_AT_abstract_origin(DW_FORM_ref_addr) -> Die for tripleplus's x
DW_AT_location(DW_FORM_block) -> a loclist
DW_TAG_inlined_subroutine
DW_AT_abstract_origin(DW_FORM_ref_addr) -> DIE for triple
DW_AT_low_pc(DW_FORM_addr) ..inline.triple_start
DW_AT_high_pc(DW_FORM_addr) ..inline.triple_end
DW_AT_call_line(DW_FORM_udata) 5
DW_TAG_formal_parameter
DW_AT_abstract_origin(DW_FORM_ref_addr) -> DIE for triple's x
DW_AT_location(DW_FORM_block) -> a loclist
DW_TAG_padding
DW_TAG_padding
DW_TAG_padding
WORDING CHANGES:
2.2
[New attributes must be added to the Figure 2:]
| DW_AT_call_column Column position
of inline call
| DW_AT_call_file File containing
inline call
| DW_AT_call_line Line number of
inline call
3.3.8.2 Concrete Inlined Instances
:
An inlined subroutine entry may also contain a DW_AT_entry_pc
attribute,
representing the first executable instruction of the inline expansion
(see
Section 2.17).
| An inlined subroutine entry may also have DW_AT_call_file,
| DW_AT_call_line, DW_AT_call_column attributes, each of whose value is an
| integer constant. These attributes represent the source file, source
| line number, and source column number, respectively, of the first
| character of the statement or expression that caused the inline expansion.
| They should not refer to declaration coordinates of the original
| declaration, but rather to those of the point at which it was inlined.
| The DW_AT_call_file attribute corresponds to a file number from the
| statement information table for the compilation unit containing the
| debugging information entry, just as the DW_AT_decl_file attribute does.
7.5.4
[New attributes must be added to Figure 21:]
| DW_AT_call_column 0x??
| DW_AT_call_file 0x??
| DW_AT_call_line 0x??
Appendix A:
[New attributes should be added for
DW_TAG_inlined_subroutine:]
| DW_AT_call_column
| DW_AT_call_file
| DW_AT_call_line
Adopted.