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.