Issue 230529.2: Outlined Subroutines
Author: | Jakub Jelinek |
---|---|
Champion: | Jakub Jelinek |
Date submitted: | 2023-05-29 |
Date revised: | 2024-03-18 |
Date closed: | |
Type: | Enhancement |
Status: | Open |
DWARF Version: | 6 |
Background
Some subroutines are created artificially from just a part of user code.
It is still useful to use DW_AT_abstract_origin
on such subroutines to
express from which user subroutine they are created, but it is desirable
to tell the debugger about that, so that e.g. for break function it doesn't
actually put a breakpoint at the start of such subroutine. Also, it
can be useful to understand the purpose of the outlining.
One example is function splitting for partial inlining.
int make_me_big (void);
void do_work (void);
static int
split_me (int a)
{
if (a < 10)
{
do_work ();
return 42;
}
else
{
make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big ();
make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big ();
make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big ();
make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big ();
make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big ();
make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big (); make_me_big ();
return a + 1;
}
}
int
test(void)
{
return split_me (0)+split_me(1)+split_me(2)+split_me(36)+split_me(82);
}
The whole function might be too long to be inlined, so some compilers choose to only only inline some inexpensive part of the function and outline the rest into an artificial function.
That will typically result in DW_TAG_subprogram
for the abstract instance
with DW_AT_inline
, then DW_TAG_inlined_subroutine
referencing through
DW_AT_abstract_origin
that abstract instance (many times, for each inlined
copy) and finally the large/expensive part is outlined into an artificial
subprogram (in assembly GCC calls that split_me.part.0
). That one
gets DW_TAG_subprogram
mentioning the DW_AT_abstract_origin
. But,
break split_me
shouldn't stop at the start of this artificial subroutine,
it is not the entry of the function. It can have completely different
arguments/return value from the abstract origin, etc.
Another example are OpenMP/OpenACC regions, e.g.
void bar (void) { int a = 42, b, c = 0;
#pragma omp parallel shared(a) private (b)
{
b = omp_get_thread_num ();
printf ("Hello world from thread %d sees a = %d\n", b, a);
}
}
Here, various compilers outline the OpenMP parallel region into a separate
function with some custom calling convention and invoke it multiple times
from each thread. Similarly here it is desirable to tell the debugger
that the artificial function has been created on behalf of user function bar
,
but it is useful to tell the debugger the purpose of it, that it is OpenMP
parallel region, such that it can use e.g. OMPD library if any to query
various details like how to find the frame of the containing function.
While the a
and b
variables often will have DW_AT_location
inside of such
an artificial function such that print a
or print b
etc. works fine in
the debugger, it would be useful to be able to print there also the variables
which weren't privatized nor shared in the region because they weren't
referenced there. For this purpose e.g. GCC uses DW_AT_static_link
,
which makes it work in the outlined region run by the same thread as
the outer part of the function, but it doesn't work in other threads.
The scope of this proposal is just to determine which DW_TAG_subprogram
DIEs represent just portions of original user functions not including the
entry to those functions and the purpose why it has been created, not
solving all other related problems what the debug information consumer
can and should be doing with that information. So, the presence
of the new attribute can hint to the debug info consumer it shouldn't
put a breakpoint at the start when user requests putting a breakpoint
at the start of the corresponding function, or it can hint it sholuld use
OMPD or some other special code to find out which OpenMP thread created
certain parallel region, but for other tasks there should be separate
new extensions if needed.
Proposed Changes
In Table 2.2 add:
DW_AT_outlined Purpose of outlined subroutine
In Table 7.5 add:
DW_AT_outlined ‡ 0x93 constant
Insert new paragraph before 3.3.9 and renumber 3.3.9 Trampolines to 3.3.10:
3.3.9 Outlined regions
Some compilers split user subroutines into multiple parts and outline some of those into compiler-generated subroutines. This happens e.g. during function splitting for partial inlining, where some cheap part of a function including its entry is inlined into callers, but some more expensive/larger part is kept as a separate subroutine, or for various OpenMP or OpenACC constructs where the body of those constructs should be run by multiple threads or on different devices. Outlined subroutines never contain an entry of the user subroutine.
An outlined subroutine is represented by a debugging information entry with the tag
DW_TAG_subprogram
,DW_AT_abstract_origin
attribute pointing to the abstract instance debugging information entry and aDW_AT_outlined
attribute whose value is an integer constant explaining the purpose for which the outlined subroutine has been created.The set of outlining purpose codes for subroutines is given in Table 3.XX.
Table 3.XX Types of outlined subroutines DW_OL_none DW_OL_partial_inline DW_OL_omp_teams DW_OL_omp_parallel DW_OL_omp_task DW_OL_omp_taskloop DW_OL_omp_target DW_OL_omp_target_teams DW_OL_oacc_parallel DW_OL_oacc_kernels DW_OL_lo_user DW_OL_hi_user
If this attribute is not present, or its value is the constant
DW_OL_none
, then the subroutine is not outlined subroutine and contains the subroutine's entry. If the value of the attribute is the constantDW_OL_partial_inline
, then a part of a subroutine including its entry is represented usingDW_TAG_inlined_subroutine
andDW_TAG_subprogram
withDW_AT_outlined DW_OL_partial_inline
contains the other part of the function.If the value of the attribute is
DW_OL_omp_teams
,DW_OL_omp_parallel
,DW_OL_omp_task
,DW_OL_omp_taskloop
,DW_OL_omp_target
, orDW_OL_omp_target_teams
, then it is an outlined subroutine created for a body of an OpenMP teams (not nested inside of target construct), parallel, task, taskloop, target or teams (nested inside of target construct) construct. If the value of the attribute isDW_OL_oacc_parallel
orDW_OL_oacc_kernels
, then it is an outlined subroutine created for a body of an OpenACC parallel or kernel construct.If the value of the attribute is in the
DW_OL_lo_user
toDW_OL_hi_user
range, it is an outlined subroutine created for vendor specific reasons.
In section 7.1 Vendor Extensibility, add DW_OL,
before DW_OP
in second
paragraph.
Add section before 7.17 Array Ordering, renumber 7.17 and later sections;
7.17 Types of outlined subroutines
The encodings of the constants used in the
DW_AT_outlined
attribute are given in Table 7.XX.Table 7.XX Types of outlined subroutines Outline purpose Value ----------------------------- DW_OL_none 0x00 DW_OL_partial_inline 0x01 DW_OL_omp_teams 0x10 DW_OL_omp_parallel 0x11 DW_OL_omp_task 0x12 DW_OL_omp_taskloop 0x13 DW_OL_omp_target 0x14 DW_OL_omp_target_teams 0x15 DW_OL_oacc_parallel 0x20 DW_OL_oacc_kernels 0x21 DW_OL_lo_user 0x20000 DW_OL_hi_user 0x3ffff
In Appendix A., add DW_AT_outlined
as allowed for DW_TAG_subprogram
.
2023-11-27: Need to revise to clarify what problems this proposal intends to solve and what problems it does not.
2024-03-18: Attempted to clarify the above, plus add DW_OL_omp_target_teams and vendor extensibility.