Hi! On 2021-07-02T09:15:27+0200, Richard Biener via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > On Thu, Jul 1, 2021 at 5:17 PM Hafiz Abid Qadeer <ab...@codesourcery.com> > wrote: >> >> Currently, if we look at the debug information for offload kernel >> regions, it looks something like this: >> >> void foo (void) >> { >> #pragma acc kernels >> { >> >> } >> } >> >> DW_TAG_compile_unit >> DW_AT_name ("<artificial>") >> >> DW_TAG_subprogram // notional parent function (foo) with no code range >> >> DW_TAG_subprogram // offload function foo._omp_fn.0 >> >> There is an artificial compile unit. It contains a parent subprogram which >> has the offload function as its child. The parent function makes sense in >> host code where it actually exists and does have an address range. But in >> offload code, it does not exist and neither the generated dwarf has an >> address range for this function. >> >> When debugger read the dwarf for offload code, they see a function with no >> address range and discard it alongwith its children which include offload >> function. This results in a poor debug experience of offload code. >> >> This patch tries to solve this problem by making offload kernels children of >> "artifical" compile unit instead of a non existent parent function. This >> not only improves debug experience but also reflects the reality better >> in debug info. >> >> Patch was tested on x86_64 with amdgcn offload. Debug behavior was >> tested with rocgdb. > > The proper fix is to reflect this in the functions declaration which currently > will have a DECL_CONTEXT of the containing function. That could be > done either on the host as well or alternatively at the time we offload > the "child" but not the parent.
Does that mean adding a (very simple) new pass in the offloading compilation pipeline, conditionalizing this 'DECL_CONTEXT' modification under '#ifdef ACCEL_COMPILER'? See 'gcc/omp-offload.c:pass_omp_target_link' for a simple example. Should that be placed at the beginning of the offloading pipeline, thus before 'pass_oacc_device_lower' (see 'gcc/passes.def'), or doesn't matter where, I suppose? Please cross-reference 'gcc/omp-low.c:create_omp_child_function', 'gcc/omp-expand.c:adjust_context_and_scope', and the new pass, assuming these are the relevant pieces here? > Note that the "parent" should be abstract but I don't think dwarf has a > way to express a fully abstract parent of a concrete instance child - or > at least how GCC expresses this causes consumers to "misinterpret" > that. I wonder if adding a DW_AT_declaration to the late DWARF > emitted "parent" would fix things as well here? (I suppose not, Abid?) Grüße Thomas >> gcc/ >> >> * gcc/dwarf2out.c (notional_parents_list): New file variable. >> (gen_subprogram_die): Record offload kernel functions in >> notional_parents_list. >> (fixup_notional_parents): New function. >> (dwarf2out_finish): Call fixup_notional_parents. >> (dwarf2out_c_finalize): Reset notional_parents_list. >> --- >> gcc/dwarf2out.c | 68 +++++++++++++++++++++++++++++++++++++++++++++++-- >> 1 file changed, 66 insertions(+), 2 deletions(-) >> >> diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c >> index 80acf165fee..769bb7fc4a8 100644 >> --- a/gcc/dwarf2out.c >> +++ b/gcc/dwarf2out.c >> @@ -3506,6 +3506,11 @@ static GTY(()) limbo_die_node *limbo_die_list; >> DW_AT_{,MIPS_}linkage_name once their DECL_ASSEMBLER_NAMEs are set. */ >> static GTY(()) limbo_die_node *deferred_asm_name; >> >> +/* A list of DIEs which represent parents of nested offload kernels. These >> + functions exist on the host side but not in the offloed code. But they >> + still show up as parent of the ofload kernels in DWARF. */ >> +static GTY(()) limbo_die_node *notional_parents_list; >> + >> struct dwarf_file_hasher : ggc_ptr_hash<dwarf_file_data> >> { >> typedef const char *compare_type; >> @@ -23652,8 +23657,23 @@ gen_subprogram_die (tree decl, dw_die_ref >> context_die) >> if (fde->dw_fde_begin) >> { >> /* We have already generated the labels. */ >> - add_AT_low_high_pc (subr_die, fde->dw_fde_begin, >> - fde->dw_fde_end, false); >> + add_AT_low_high_pc (subr_die, fde->dw_fde_begin, >> + fde->dw_fde_end, false); >> + >> + /* Offload kernel functions are nested within a parent function >> + that doesn't actually exist in the offload object. GDB >> + will ignore the function and everything nested within it as >> + the function does not have an address range. We mark the >> + parent functions here and will later fix them. */ >> + if (lookup_attribute ("omp target entrypoint", >> + DECL_ATTRIBUTES (decl))) >> + { >> + limbo_die_node *node = ggc_cleared_alloc<limbo_die_node> (); >> + node->die = subr_die->die_parent; >> + node->created_for = decl; >> + node->next = notional_parents_list; >> + notional_parents_list = node; >> + } >> } >> else >> { >> @@ -31881,6 +31901,46 @@ flush_limbo_die_list (void) >> } >> } >> >> +/* Fixup notional parent function (which does not actually exist) so that >> + a function with no address range is not parent of a function *with* >> address >> + ranges. Otherwise debugger see the parent function without code range >> + and discards it along with its children which here include function >> + which have address range. >> + >> + Typically this occurs when we have an offload kernel, where the parent >> + function only exists in the host-side portion of the code. */ >> + >> +static void >> +fixup_notional_parents (void) >> +{ >> + limbo_die_node *node; >> + >> + for (node = notional_parents_list; node; node = node->next) >> + { >> + dw_die_ref notional_parent = node->die; >> + /* The dwarf at this moment looks like this >> + DW_TAG_compile_unit >> + DW_AT_name ("<artificial>") >> + >> + DW_TAG_subprogram // parent function with no code range >> + >> + DW_TAG_subprogram // offload function 1 >> + ... >> + DW_TAG_subprogram // offload function n >> + Our aim is to make offload function children of CU. */ >> + if (notional_parent >> + && notional_parent->die_tag == DW_TAG_subprogram >> + && !(get_AT (notional_parent, DW_AT_low_pc) >> + || get_AT (notional_parent, DW_AT_ranges))) >> + >> + { >> + dw_die_ref cu = notional_parent->die_parent; >> + if (cu && cu->die_tag == DW_TAG_compile_unit) >> + reparent_child (notional_parent->die_child, cu); >> + } >> + } >> +} >> + >> /* Reset DIEs so we can output them again. */ >> >> static void >> @@ -31938,6 +31998,9 @@ dwarf2out_finish (const char *filename) >> /* Flush out any latecomers to the limbo party. */ >> flush_limbo_die_list (); >> >> + /* Sort out notional parents of offloaded kernel. */ >> + fixup_notional_parents (); >> + >> if (inline_entry_data_table) >> gcc_assert (inline_entry_data_table->is_empty ()); >> >> @@ -32994,6 +33057,7 @@ dwarf2out_c_finalize (void) >> single_comp_unit_die = NULL; >> comdat_type_list = NULL; >> limbo_die_list = NULL; >> + notional_parents_list = NULL; >> file_table = NULL; >> decl_die_table = NULL; >> common_block_die_table = NULL; >> -- >> 2.25.1 >> ----------------- Siemens Electronic Design Automation GmbH; Anschrift: Arnulfstraße 201, 80634 München; Gesellschaft mit beschränkter Haftung; Geschäftsführer: Thomas Heurung, Frank Thürauf; Sitz der Gesellschaft: München; Registergericht München, HRB 106955