This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [dwarf, RFC] Emitting per-function dwarf info
- From: Senthil Kumar Selvaraj <senthil_kumar dot selvaraj at atmel dot com>
- To: <gcc-patches at gcc dot gnu dot org>
- Cc: <jason at redhat dot com>, <terry dot guo at arm dot com>, <joey dot ye at arm dot com>, <pitchumani dot s at atmel dot com>
- Date: Wed, 20 May 2015 10:31:48 +0530
- Subject: Re: [dwarf, RFC] Emitting per-function dwarf info
- Authentication-results: sourceware.org; auth=none
- References: <20150410064936 dot GA2298 at atmel dot com>
Ping!
Regards
Senthil
On Fri, Apr 10, 2015 at 12:19:36PM +0530, Senthil Kumar Selvaraj wrote:
> Hi,
>
> This (rather big) patch is an attempt to generate per function DWARF
> information for functions that go into their own sections (through
> -ffunction-section or otherwise). This is so that the GNU linker's
> garbage collection mechanism can then remove all DWARF information when it
> removes an unmarked function section.
>
> Most of the code for splitting off the debug information was adapted from
> Jason's comdat-debug branch, with a few changes to move to the C++
> collection types.
>
> We started out with section groups, aiming to generate one section group
> per function containing DWARF info AND code for the function, so that when
> the linker gc's a function, it removes the whole section group.
> But it turned out to be difficult to split all DWARF information - data that
> goes into debug_ranges, for example, is maintained in a flat list, and we
> found it hard to split it per function.
>
> If not all debug information is in the section group, references from the
> "global" DWARF info into the section group results in the section group
> itself not getting garbage collected - effectively breaking gc-sections.
>
> We then figured that the GNU binutils linker removes sections with the
> SEC_DEBUGGING flag that are named with the function's text section as the
> suffix (see https://www.sourceware.org/ml/binutils/2015-03/msg00326.html), so
> we currently generate sections named to match that, and then let the
> linker work its magic.
>
> At a high level, this is what we do
>
> 1. In gen_subprogram_die, we see if the fde should go into a separate CU and
> if yes, record it in a hash_table.
> 2. In dwarf2_out_finish, we check if each subprogram die is in the table, and
> if yes, create a new CU for the die, along with a DW_TAG_imported_unit
> die referring to the main CU. The newly created CU die is also
> recorded in the hash table.
> 3. In output_comp_unit, if the CU is in the hash table, we switch to
> .debug_info<function_section_name> instead of debug_info_section.
> 4. We do the same thing when generating aranges, pubnames and debug_loc.
>
> Bootstrapping x86_64-linux for c and c++ works, but there are quite a
> few regression test failures. We'll analyze and follow-up on those.
>
> The formatting style is inconsistent, we'll fix those as well.
>
> We wanted to put it out before going ahead further. How does this look?
> Are there are any major problems with this approach?
>
> For code like this,
>
> volatile int x;
> void foo() { x--; }
> int main() { return x; }
>
> $ ~/native/install/bin/gcc -ffunction-sections -g3 -Wa,-gdwarf-sections -fdwarf-sections test.c -c
> $ ~/native/install/bin/objdump -h test.o
> <snip>
> 5 .text.foo 00000016 0000000000000000 0000000000000000 00000050 2**0
> CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
> 6 .text.main 0000001a 0000000000000000 0000000000000000 00000066 2**0
> CONTENTS, ALLOC, LOAD, RELOC, READONLY, CODE
> 7 .debug_info.text.main 00000044 0000000000000000 0000000000000000 00000080 2**0
> CONTENTS, RELOC, READONLY, DEBUGGING
> 8 .debug_info.text.foo 00000040 0000000000000000 0000000000000000 000000b1 2**0
> CONTENTS, RELOC, READONLY, DEBUGGING
> 9 .debug_info 0000004d 0000000000000000 0000000000000000 000000e5 2**0
> CONTENTS, RELOC, READONLY, DEBUGGING
> 10 .debug_abbrev 0000007f 0000000000000000 0000000000000000 0000011e 2**0
> CONTENTS, READONLY, DEBUGGING
> 11 .debug_aranges 00000020 0000000000000000 0000000000000000 00000194 2**0
> CONTENTS, RELOC, READONLY, DEBUGGING
> 12 .debug_aranges.text.main 00000030 0000000000000000 0000000000000000 000001b3 2**0
> CONTENTS, RELOC, READONLY, DEBUGGING
> 13 .debug_aranges.text.foo 00000030 0000000000000000 0000000000000000 000001d6 2**0
> CONTENTS, RELOC, READONLY, DEBUGGING
> 14 .debug_ranges 00000070 0000000000000000 0000000000000000 000001f9 2**0
> CONTENTS, RELOC, READONLY, DEBUGGING
>
> Linking and dumping debug info shows that there is no debug info for
> foo (which would have been gc'ed by the linker)
>
> $ ~/native/install/bin/gcc -Wl,--gc-sections test.o -Wl,-Map=test.map
> $ cat test.map
> <snip>
> Discarded input sections
>
> <snip>
> .text.foo 0x0000000000000000 0x16 test.o
> .debug_info.text.foo
> 0x0000000000000000 0x40 test.o
> .debug_aranges.text.foo
> 0x0000000000000000 0x30 test.o
> .note.GNU-stack
> 0x0000000000000000 0x0 test.o
> .debug_line.text.foo
> 0x0000000000000000 0x12 test.o
> </snip>
>
> $ ~/native/install/bin/objdump -Wi a.out
> Contents of the .debug_info section:
>
> Compilation Unit @ offset 0x0:
> Length: 0x40 (32-bit)
> Version: 4
> Abbrev Offset: 0x0
> Pointer Size: 8
> <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
> <c> DW_AT_producer : (indirect string, offset: 0x567): GNU C11 5.0.0 20150331 (experimental) -mtune=generic -march=x86-64 -g3 -ffunction-sections -fdwarf-sections
> <10> DW_AT_language : 12 (ANSI C99)
> <11> DW_AT_ranges : 0x30
> <15> DW_AT_low_pc : 0x0
> <1d> DW_AT_GNU_macros : 0x0
> <1><21>: Abbrev Number: 2 (DW_TAG_imported_unit)
> <22> DW_AT_import : <0x4f> [Abbrev Number: 5]
> <1><26>: Abbrev Number: 3 (DW_TAG_subprogram)
> <27> DW_AT_external : 1
> <27> DW_AT_name : (indirect string, offset: 0x1928): main
> <2b> DW_AT_decl_file : 1
> <2c> DW_AT_decl_line : 3
> <2d> DW_AT_type : <0x71>
> <31> DW_AT_low_pc : 0x400476
> <39> DW_AT_high_pc : 0x1a
> <41> DW_AT_frame_base : 1 byte block: 9c (DW_OP_call_frame_cfa)
> <43> DW_AT_GNU_all_call_sites: 1
> <1><43>: Abbrev Number: 0
> Compilation Unit @ offset 0x44:
> Length: 0x49 (32-bit)
> Version: 4
> Abbrev Offset: 0x0
> Pointer Size: 8
> <0><4f>: Abbrev Number: 5 (DW_TAG_compile_unit)
> <50> DW_AT_producer : (indirect string, offset: 0x567): GNU C11 5.0.0 20150331 (experimental) -mtune=generic -march=x86-64 -g3 -ffunction-sections -fdwarf-sections
> <54> DW_AT_language : 12 (ANSI C99)
> <55> DW_AT_name : (indirect string, offset: 0x40b): test.c
> <59> DW_AT_comp_dir : (indirect string, offset: 0x19): /home/saaadhu
> <5d> DW_AT_ranges : 0x0
> <61> DW_AT_low_pc : 0x0
> <69> DW_AT_stmt_list : 0x0
> <6d> DW_AT_GNU_macros : 0x0
> <1><71>: Abbrev Number: 6 (DW_TAG_base_type)
> <72> DW_AT_byte_size : 4
> <73> DW_AT_encoding : 5 (signed)
> <74> DW_AT_name : int
> <1><78>: Abbrev Number: 7 (DW_TAG_variable)
> <79> DW_AT_name : x
> <7b> DW_AT_decl_file : 1
> <7c> DW_AT_decl_line : 1
> <7d> DW_AT_type : <0x8b>
> <81> DW_AT_external : 1
> <81> DW_AT_location : 9 byte block: 3 54 8 60 0 0 0 0 0 (DW_OP_addr: 600854)
> <1><8b>: Abbrev Number: 8 (DW_TAG_volatile_type)
> <8c> DW_AT_type : <0x71>
> <1><90>: Abbrev Number: 0
>
> Regards
> Senthil
>
> diff --git a/gcc/common.opt b/gcc/common.opt
> index b49ac46..a73d176 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1277,6 +1277,10 @@ ffunction-sections
> Common Report Var(flag_function_sections)
> Place each function into its own section
>
> +fdwarf-sections
> +Common Report Var(flag_dwarf_sections) Init(0)
> +Place dwarf info for named functions into its own section when using DWARF v4 debuginfo
> +
> fgcse
> Common Report Var(flag_gcse) Optimization
> Perform global common subexpression elimination
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index f9781f4..7e44c7c 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -392,6 +392,7 @@ Objective-C and Objective-C++ Dialects}.
> -fearly-inlining -fipa-sra -fexpensive-optimizations -ffat-lto-objects @gol
> -ffast-math -ffinite-math-only -ffloat-store -fexcess-precision=@var{style} @gol
> -fforward-propagate -ffp-contract=@var{style} -ffunction-sections @gol
> +-fdwarf-sections @gol
> -fgcse -fgcse-after-reload -fgcse-las -fgcse-lm -fgraphite-identity @gol
> -fgcse-sm -fhoist-adjacent-loads -fif-conversion @gol
> -fif-conversion2 -findirect-inlining @gol
> @@ -9990,6 +9991,11 @@ You cannot use @command{gprof} on all systems if you
> specify this option, and you may have problems with debugging if
> you specify both this option and @option{-g}.
>
> +@item -fdwarf-sections
> +@opindex fdwarf-sections
> +Place dwarf info for named functions into its own section when using DWARF
> +v4 debuginfo.
> +
> @item -fbranch-target-load-optimize
> @opindex fbranch-target-load-optimize
> Perform branch target register load optimization before prologue / epilogue
> diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
> index 6c8e51f..6b519b8 100644
> --- a/gcc/dwarf2out.c
> +++ b/gcc/dwarf2out.c
> @@ -244,6 +244,35 @@ struct indirect_string_hasher : ggc_hasher<indirect_string_node *>
>
> static GTY (()) hash_table<indirect_string_hasher> *debug_str_hash;
>
> +/* A set of local types referenced from outside their function. */
> +
> +static GTY (()) hash_set<dw_die_ref> *local_type_template_args;
> +
> +static void
> +switch_to_section_for_imported_decl (tree, const char *prefix);
> +
> +struct GTY((for_user)) imported_unit_node {
> + tree decl;
> + dw_die_ref die;
> +};
> +
> +struct imported_unit_hasher : ggc_hasher<imported_unit_node *>
> +{
> + typedef dw_die_ref compare_type;
> +
> + static hashval_t hash (imported_unit_node *x)
> + {
> + return htab_hash_pointer (x->die);
> + }
> + static bool equal (imported_unit_node *x1, dw_die_ref x2)
> + {
> + return x1->die == x2;
> + }
> +};
> +
> +static GTY (()) hash_table<imported_unit_hasher> *imported_unit_hash;
> +static tree lookup_imported_unit_key (dw_die_ref);
> +
> /* With split_debug_info, both the comp_dir and dwo_name go in the
> main object file, rather than the dwo, similar to the force_direct
> parameter elsewhere but with additional complications:
> @@ -290,6 +319,8 @@ static char *stripattributes (const char *);
> static void output_call_frame_info (int);
> static void dwarf2out_note_section_used (void);
>
> +static bool should_fde_move_to_imported_unit (dw_fde_ref);
> +
> /* Personality decl of current unit. Used only when assembler does not support
> personality CFI. */
> static GTY(()) rtx current_unit_personality;
> @@ -3153,6 +3184,7 @@ static int is_symbol_die (dw_die_ref);
> static inline bool is_template_instantiation (dw_die_ref);
> static void assign_symbol_names (dw_die_ref);
> static void break_out_includes (dw_die_ref);
> +static int is_abstract_die (dw_die_ref);
> static int is_declaration_die (dw_die_ref);
> static int should_move_die_to_comdat (dw_die_ref);
> static dw_die_ref clone_as_declaration (dw_die_ref);
> @@ -3165,6 +3197,8 @@ static dw_die_ref generate_skeleton (dw_die_ref);
> static dw_die_ref remove_child_or_replace_with_skeleton (dw_die_ref,
> dw_die_ref,
> dw_die_ref);
> +static bool can_use_low_high_pc ();
> +static void break_out_functions (dw_die_ref);
> static void break_out_comdat_types (dw_die_ref);
> static void copy_decls_for_unworthy_types (dw_die_ref);
>
> @@ -3173,7 +3207,6 @@ static void output_location_lists (dw_die_ref);
> static int constant_size (unsigned HOST_WIDE_INT);
> static unsigned long size_of_die (dw_die_ref);
> static void calc_die_sizes (dw_die_ref);
> -static void calc_base_type_die_sizes (void);
> static void mark_dies (dw_die_ref);
> static void unmark_dies (dw_die_ref);
> static void unmark_all_dies (dw_die_ref);
> @@ -3194,7 +3227,8 @@ static void add_enumerator_pubname (const char *, dw_die_ref);
> static void add_pubname_string (const char *, dw_die_ref);
> static void add_pubtype (tree, dw_die_ref);
> static void output_pubnames (vec<pubname_entry, va_gc> *);
> -static void output_aranges (unsigned long);
> +static inline unsigned get_range_idx (dw_attr_ref);
> +static void output_aranges ();
> static unsigned int add_ranges_num (int);
> static unsigned int add_ranges (const_tree);
> static void add_ranges_by_labels (dw_die_ref, const char *, const char *,
> @@ -3656,11 +3690,10 @@ get_base_type_offset (dw_die_ref ref)
> {
> if (ref->die_offset)
> return ref->die_offset;
> - if (comp_unit_die ()->die_abbrev)
> - {
> - calc_base_type_die_sizes ();
> - gcc_assert (ref->die_offset);
> - }
> + /* If we're calculating DIE offsets, this needs to be set. It's OK if
> + it's unset during optimize_location_lists. */
> + if (next_die_offset)
> + gcc_assert (ref->die_offset);
> return ref->die_offset;
> }
>
> @@ -6752,6 +6785,12 @@ is_symbol_die (dw_die_ref c)
> {
> return (is_type_die (c)
> || is_declaration_die (c)
> + || is_abstract_die (c)
> + /* DW_TAG_GNU_call_site can refer to subprograms. */
> + || c->die_tag == DW_TAG_subprogram
> + /* DW_TAG_imported_unit can refer to *_unit. */
> + || c->die_tag == DW_TAG_compile_unit
> + || c->die_tag == DW_TAG_partial_unit
> || c->die_tag == DW_TAG_namespace
> || c->die_tag == DW_TAG_module);
> }
> @@ -6837,7 +6876,8 @@ assign_symbol_names (dw_die_ref die)
> {
> dw_die_ref c;
>
> - if (is_symbol_die (die) && !die->comdat_type_p)
> + if (is_symbol_die (die) && !die->comdat_type_p &&
> + !die->die_id.die_symbol)
> {
> if (comdat_symbol_id)
> {
> @@ -6999,6 +7039,8 @@ break_out_includes (dw_die_ref die)
> node = node->next)
> {
> int is_dupl;
> + if (lookup_imported_unit_key (node->die) != NULL)
> + continue;
>
> compute_section_prefix (node->die);
> is_dupl = check_duplicate_cu (node->die, &cu_hash_table,
> @@ -7015,6 +7057,100 @@ break_out_includes (dw_die_ref die)
> }
> }
>
> +static void
> +copy_needed_base_types_loc (dw_die_ref unit, dw_loc_descr_ref loc,
> + hash_map<dw_die_ref, dw_die_ref> *map)
> +{
> + for (; loc; loc = loc->dw_loc_next)
> + {
> + dw_die_ref *op, *slot, copy;
> + switch (loc->dw_loc_opc)
> + {
> + case DW_OP_GNU_convert:
> + case DW_OP_GNU_reinterpret:
> + if (loc->dw_loc_oprnd1.val_class == dw_val_class_unsigned_const)
> + continue;
> + /* else fall through */
> + case DW_OP_GNU_const_type:
> + op = &loc->dw_loc_oprnd1.v.val_die_ref.die;
> + break;
> + case DW_OP_GNU_regval_type:
> + case DW_OP_GNU_deref_type:
> + op = &loc->dw_loc_oprnd2.v.val_die_ref.die;
> + break;
> +
> + /* DW_OP_GNU_entry_value has a location expression for its
> + operand, so recurse. */
> + case DW_OP_GNU_entry_value:
> + copy_needed_base_types_loc (unit, loc->dw_loc_oprnd1.v.val_loc, map);
> + continue;
> +
> + default:
> + continue;
> + }
> +
> + gcc_assert ((*op)->die_tag == DW_TAG_base_type);
> +
> + slot = map->get (*op);
> + if (slot)
> + copy = *slot;
> + else
> + {
> + /* Insert the base type at the beginning of unit's child list
> + so that get_base_type_offset works. */
> + copy = ggc_cleared_alloc<die_node> ();
> + copy->die_tag = DW_TAG_base_type;
> + copy->die_sib = unit->die_child->die_sib;
> + unit->die_child->die_sib = copy;
> + add_AT_unsigned (copy, DW_AT_byte_size,
> + get_AT_unsigned (*op, DW_AT_byte_size));
> + add_AT_unsigned (copy, DW_AT_encoding,
> + get_AT_unsigned (*op, DW_AT_encoding));
> + map->put (*op, copy);
> + }
> + *op = copy;
> + }
> +}
> +
> +static void
> +copy_needed_base_types_1 (dw_die_ref unit, dw_die_ref die,
> + hash_map<dw_die_ref, dw_die_ref> *map)
> +{
> + dw_die_ref c;
> + dw_attr_ref a;
> + unsigned ix;
> + FOR_EACH_VEC_SAFE_ELT (die->die_attr, ix, a)
> + {
> + switch (AT_class (a))
> + {
> + case dw_val_class_loc:
> + copy_needed_base_types_loc (unit, AT_loc (a), map);
> + break;
> +
> + case dw_val_class_loc_list:
> + {
> + dw_loc_list_ref p = AT_loc_list (a);
> + for (; p; p = p->dw_loc_next)
> + copy_needed_base_types_loc (unit, p->expr, map);
> + break;
> + }
> +
> + default:
> + break;
> + }
> + }
> +
> + FOR_EACH_CHILD (die, c, copy_needed_base_types_1 (unit, c, map));
> +}
> +
> +static void
> +copy_needed_base_types (dw_die_ref unit)
> +{
> + hash_map<dw_die_ref, dw_die_ref> *map = new hash_map<dw_die_ref, dw_die_ref>;
> + copy_needed_base_types_1 (unit, unit, map);
> + delete map;
> +}
> +
> /* Return non-zero if this DIE is a declaration. */
>
> static int
> @@ -7030,6 +7166,37 @@ is_declaration_die (dw_die_ref die)
> return 0;
> }
>
> +/* Return non-zero if this DIE is part of an abstract inline. That is, if
> + it's an inline function or a variable, label or parameter of an inline
> + function. */
> +
> +static int
> +is_abstract_die (dw_die_ref die)
> +{
> + switch (die->die_tag)
> + {
> + case DW_TAG_subprogram:
> + case DW_TAG_variable:
> + case DW_TAG_label:
> + case DW_TAG_formal_parameter:
> + break;
> +
> + default:
> + return 0;
> + }
> +
> + for (; die->die_tag != DW_TAG_subprogram; die = die->die_parent)
> + {
> + if (die->die_tag == DW_TAG_compile_unit
> + || die->die_tag == DW_TAG_namespace)
> + return 0;
> + }
> +
> + if (get_AT (die, DW_AT_inline))
> + return 1;
> + return 0;
> +}
> +
> /* Return non-zero if this DIE is nested inside a subprogram. */
>
> static int
> @@ -7480,6 +7647,284 @@ remove_child_or_replace_with_skeleton (dw_die_ref unit, dw_die_ref child,
> return skeleton;
> }
>
> +
> +static void
> +record_imported_unit_key (dw_die_ref die, tree decl)
> +{
> + struct imported_unit_node *mp;
> +
> + if (imported_unit_hash == NULL)
> + imported_unit_hash = hash_table<imported_unit_hasher>::create_ggc (10);
> +
> + imported_unit_node **slot = imported_unit_hash->find_slot_with_hash (die,
> + htab_hash_pointer (die), INSERT);
> + if (*slot == NULL)
> + {
> + mp = ggc_cleared_alloc<imported_unit_node> ();
> + mp->die = die;
> + mp->decl = decl;
> + *slot = mp;
> + }
> +}
> +
> +static tree
> +lookup_imported_unit_key (dw_die_ref die)
> +{
> + struct imported_unit_node *mp;
> +
> + if (imported_unit_hash == NULL)
> + return NULL;
> +
> + mp = imported_unit_hash->find_with_hash (die, htab_hash_pointer (die));
> +
> + if (mp)
> + return mp->decl;
> + else
> + return NULL;
> +}
> +
> +static bool
> +can_use_low_high_pc ()
> +{
> + unsigned fde_idx;
> + dw_fde_ref fde;
> + bool any_non_cu_ranges;
> +
> + any_non_cu_ranges = (ranges_table_in_use > 0);
> + if ((any_non_cu_ranges && have_multiple_function_sections)
> + || cold_text_section_used)
> + return false;
> +
> + if (!fde_vec)
> + return false;
> +
> + /* If we don't need AT_ranges for either of those reasons, are there
> + any extra function sections that belong to the main CU? */
> + FOR_EACH_VEC_ELT (*fde_vec, fde_idx, fde)
> + {
> + if (should_fde_move_to_imported_unit (fde))
> + return false;
> + }
> +
> + return true;
> +}
> +
> +static void
> +split_out_local_types (dw_die_ref old, dw_die_ref decl, bool decl_is_new)
> +{
> + dw_die_ref c, nc;
> + dw_die_ref prev = old->die_child;
> +
> + if (prev) do {
> + force_loop:
> + c = prev->die_sib;
> +
> + switch (c->die_tag)
> + {
> + case DW_TAG_formal_parameter:
> + case DW_TAG_template_type_param:
> + case DW_TAG_template_value_param:
> + case DW_TAG_GNU_template_template_param:
> + /* If we're making a new declaration, we need to copy these
> + over. */
> + if (decl_is_new)
> + {
> + nc = new_die (c->die_tag, decl, NULL_TREE);
> + if (c->die_tag == DW_TAG_GNU_template_template_param)
> + add_AT_string (nc, DW_AT_GNU_template_name,
> + get_AT_string (c, DW_AT_GNU_template_name));
> + else
> + add_AT_die_ref (nc, DW_AT_type, get_AT_ref (c, DW_AT_type));
> + if (get_AT_flag (c, DW_AT_artificial))
> + add_AT_flag (nc, DW_AT_artificial, 1);
> + }
> + break;
> +
> + case DW_TAG_enumeration_type:
> + case DW_TAG_class_type:
> + case DW_TAG_structure_type:
> + /* Types that are used from outside the function need to move
> + into the declaration, but we should keep a stub locally for
> + name lookup. */
> + if (local_type_template_args->contains (c))
> + {
> + nc = ggc_cleared_alloc<die_node> ();
> + nc->die_tag = DW_TAG_typedef;
> + add_AT_die_ref (nc, DW_AT_type, c);
> + replace_child (c, nc, prev);
> + add_child_die (decl, c);
> + c = nc;
> + }
> + break;
> +
> + case DW_TAG_lexical_block:
> + /* Recurse into lexical blocks, then leave them in place. */
> + split_out_local_types (c, decl, decl_is_new);
> + break;
> +
> + default:
> + /* Unnamed types that are used from outside the function can
> + just move. */
> + if (is_type_die (c) && local_type_template_args->contains (c))
> + {
> + remove_child_with_prev (c, prev);
> + add_child_die (decl, c);
> + if (old->die_child == NULL)
> + /* We removed the last child from old. */
> + return;
> + else
> + /* We don't want to advance prev. */
> + goto force_loop;
> + }
> + /* Everything else should stay in the COMDAT die. */
> + break;
> + }
> +
> + prev = c;
> + }
> + while (c != old->die_child);
> +}
> +
> +static dw_die_ref
> +split_out_function_skeleton (dw_die_ref old)
> +{
> + dw_die_ref decl;
> + dw_die_ref new_ = NULL;
> +
> + /* Is there already a declaration DIE we can use? */
> + decl = get_AT_ref (old, DW_AT_specification);
> + if (!decl)
> + {
> + /* Nope, need to make one and move attributes over. */
> + dw_attr_ref a;
> + unsigned ix;
> +
> + decl = new_ = ggc_cleared_alloc<die_node> ();
> + new_->die_tag = DW_TAG_subprogram;
> +
> + add_AT_flag (new_, DW_AT_declaration, 1);
> + add_AT_die_ref (old, DW_AT_specification, new_);
> +
> + FOR_EACH_VEC_SAFE_ELT (old->die_attr, ix, a)
> + switch (a->dw_attr)
> + {
> + case DW_AT_object_pointer:
> + case DW_AT_virtuality:
> + case DW_AT_accessibility:
> + case DW_AT_explicit:
> + /* Member function; we should have already had a declaration. */
> + gcc_unreachable ();
> + break;
> +
> + case DW_AT_name:
> + case DW_AT_type:
> + case DW_AT_artificial:
> + case DW_AT_decl_file:
> + case DW_AT_decl_line:
> + case DW_AT_external:
> + case DW_AT_pure:
> + case DW_AT_calling_convention:
> + case DW_AT_prototyped:
> + case DW_AT_elemental:
> + case DW_AT_recursive:
> + /* Move these to the declaration. */
> + add_dwarf_attr (new_, a);
> + old->die_attr->ordered_remove(ix);
> + --ix;
> + break;
> +
> + default:
> + /* Leave anything else in the definition. */
> + break;
> + }
> + }
> +
> + split_out_local_types (old, decl, decl == new_);
> +
> + return new_;
> +}
> +
> +bool
> +gather_local_type_fns_r (
> + const dw_die_ref &slot,
> + hash_set<dw_die_ref> *local_type_fns)
> +{
> + dw_die_ref die = slot;
> + for (die = die->die_parent; die; die = die->die_parent)
> + if (die->die_tag == DW_TAG_subprogram)
> + break;
> + if (die && lookup_imported_unit_key (die))
> + local_type_fns->add (die);
> + return true;
> +}
> +
> +static struct hash_set<dw_die_ref> *
> +gather_local_type_fns (void)
> +{
> + hash_set<dw_die_ref> *local_type_fns;
> + if (local_type_template_args == NULL)
> + return NULL;
> +
> + local_type_fns = new hash_set<dw_die_ref>;
> + local_type_template_args->traverse<hash_set<dw_die_ref>*, gather_local_type_fns_r>(
> + local_type_fns);
> + return local_type_fns;
> +}
> +
> +static void
> +break_out_functions (dw_die_ref die)
> +{
> + dw_die_ref c, prev;
> + bool found = false;
> + limbo_die_node *node;
> + hash_set<dw_die_ref> *local_type_fns = gather_local_type_fns ();
> +
> + prev = die->die_child;
> + if (prev == NULL)
> + return;
> +
> + do
> + {
> + tree key;
> + c = prev->die_sib;
> + if (c->die_tag == DW_TAG_subprogram
> + && (key = lookup_imported_unit_key (c)))
> + {
> + dw_die_ref nc;
> + dw_die_ref unit = gen_compile_unit_die (NULL);
> + dw_die_ref imp = new_die (DW_TAG_imported_unit, unit,
> + NULL_TREE);
> + add_AT_die_ref (imp, DW_AT_import, comp_unit_die ());
> + if (local_type_fns
> + && local_type_fns->contains (c)
> + && (nc = split_out_function_skeleton (c)))
> + replace_child (c, nc, prev);
> + else
> + remove_child_with_prev (c, prev);
> + add_child_die (unit, c);
> +
> + record_imported_unit_key (unit, key);
> + found = true;
> + }
> + else
> + prev = c;
> + }
> + while (die->die_child && (c != die->die_child));
> +
> + if (!found)
> + return;
> +
> + assign_symbol_names (die);
> + for (node = limbo_die_list;
> + node;
> + node = node->next)
> + if (lookup_imported_unit_key (node->die) != NULL)
> + {
> + copy_needed_base_types (node->die);
> + assign_symbol_names (node->die);
> + }
> +}
> +
> /* Traverse the DIE and set up additional .debug_types sections for each
> type worthy of being placed in a COMDAT section. */
>
> @@ -8164,34 +8609,23 @@ calc_die_sizes (dw_die_ref die)
> next_die_offset += 1;
> }
>
> -/* Size just the base type children at the start of the CU.
> - This is needed because build_abbrev needs to size locs
> - and sizing of type based stack ops needs to know die_offset
> - values for the base types. */
> +/* Return the size of the unit starting at DIE, assuming calc_die_sizes has
> + already been run. */
>
> -static void
> -calc_base_type_die_sizes (void)
> +static unsigned long
> +cu_size (dw_die_ref die)
> {
> - unsigned long die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE;
> - unsigned int i;
> - dw_die_ref base_type;
> -#if ENABLE_ASSERT_CHECKING
> - dw_die_ref prev = comp_unit_die ()->die_child;
> -#endif
> -
> - die_offset += size_of_die (comp_unit_die ());
> - for (i = 0; base_types.iterate (i, &base_type); i++)
> + unsigned terminators = 0;
> + /* die_child points to the last child of a DIE, so we can find the last
> + die by chasing down die_child. */
> + while (die->die_child)
> {
> -#if ENABLE_ASSERT_CHECKING
> - gcc_assert (base_type->die_offset == 0
> - && prev->die_sib == base_type
> - && base_type->die_child == NULL
> - && base_type->die_abbrev);
> - prev = base_type;
> -#endif
> - base_type->die_offset = die_offset;
> - die_offset += size_of_die (base_type);
> + ++terminators;
> + die = die->die_child;
> }
> + /* Then add the size of the last die and the null bytes to terminate
> + sibling lists. */
> + return die->die_offset + size_of_die (die) + terminators;
> }
>
> /* Set the marks for a die and its children. We do this so
> @@ -8289,7 +8723,8 @@ size_of_pubnames (vec<pubname_entry, va_gc> *names)
>
> size = DWARF_PUBNAMES_HEADER_SIZE;
> FOR_EACH_VEC_ELT (*names, i, p)
> - if (include_pubname_in_output (names, p))
> + if ((include_pubname_in_output (names, p)) &&
> + (!lookup_imported_unit_key (p->die)))
> size += strlen (p->name) + DWARF_OFFSET_SIZE + 1 + space_for_flags;
>
> size += DWARF_OFFSET_SIZE;
> @@ -8317,7 +8752,8 @@ size_of_aranges (void)
>
> FOR_EACH_VEC_ELT (*fde_vec, fde_idx, fde)
> {
> - if (DECL_IGNORED_P (fde->decl))
> + if (DECL_IGNORED_P (fde->decl) ||
> + should_fde_move_to_imported_unit (fde))
> continue;
> if (!fde->in_std_section)
> size += 2 * DWARF2_ADDR_SIZE;
> @@ -9107,6 +9543,7 @@ output_comp_unit (dw_die_ref die, int output_if_empty)
> {
> const char *secname, *oldsym;
> char *tmp;
> + tree key;
>
> /* Unless we are outputting main CU, we may throw away empty ones. */
> if (!output_if_empty && die->die_child == NULL)
> @@ -9129,21 +9566,37 @@ output_comp_unit (dw_die_ref die, int output_if_empty)
> next_die_offset = DWARF_COMPILE_UNIT_HEADER_SIZE;
> calc_die_sizes (die);
>
> - oldsym = die->die_id.die_symbol;
> - if (oldsym)
> + key = lookup_imported_unit_key (die);
> + if (key)
> {
> - tmp = XALLOCAVEC (char, strlen (oldsym) + 24);
> + const char *decl_name;
> + switch_to_section_for_imported_decl (key,
> + DEBUG_INFO_SECTION);
> + decl_name = targetm.strip_name_encoding (IDENTIFIER_POINTER
> + (DECL_ASSEMBLER_NAME(key)));
> +
> + ASM_OUTPUT_DEBUG_LABEL (asm_out_file, decl_name, 0);
>
> - sprintf (tmp, ".gnu.linkonce.wi.%s", oldsym);
> - secname = tmp;
> - die->die_id.die_symbol = NULL;
> - switch_to_section (get_section (secname, SECTION_DEBUG, NULL));
> + oldsym = die->die_id.die_symbol;
> }
> else
> {
> - switch_to_section (debug_info_section);
> - ASM_OUTPUT_LABEL (asm_out_file, debug_info_section_label);
> - info_section_emitted = true;
> + oldsym = die->die_id.die_symbol;
> + if (oldsym && (imported_unit_hash && imported_unit_hash->elements() == 0))
> + {
> + tmp = XALLOCAVEC (char, strlen (oldsym) + 24);
> +
> + sprintf (tmp, ".gnu.linkonce.wi.%s", oldsym);
> + secname = tmp;
> + die->die_id.die_symbol = NULL;
> + switch_to_section (get_section (secname, SECTION_DEBUG, NULL));
> + }
> + else
> + {
> + switch_to_section (debug_info_section);
> + ASM_OUTPUT_LABEL (asm_out_file, debug_info_section_label);
> + info_section_emitted = true;
> + }
> }
>
> /* Output debugging information. */
> @@ -9516,41 +9969,55 @@ output_pubname (dw_offset die_offset, pubname_entry *entry)
> visible names; or the public types table used to find type definitions. */
>
> static void
> -output_pubnames (vec<pubname_entry, va_gc> *names)
> +output_pubnames_header (unsigned length, const char *label,
> + unsigned long cu_length)
> {
> - unsigned i;
> - unsigned long pubnames_length = size_of_pubnames (names);
> - pubname_ref pub;
> -
> if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
> dw2_asm_output_data (4, 0xffffffff,
> "Initial length escape value indicating 64-bit DWARF extension");
> - dw2_asm_output_data (DWARF_OFFSET_SIZE, pubnames_length, "Pub Info Length");
> + dw2_asm_output_data (DWARF_OFFSET_SIZE, length, "Pub Info Length");
>
> /* Version number for pubnames/pubtypes is independent of dwarf version. */
> dw2_asm_output_data (2, 2, "DWARF Version");
>
> if (dwarf_split_debug_info)
> - dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_skeleton_info_section_label,
> + dw2_asm_output_offset (DWARF_OFFSET_SIZE,
> + debug_skeleton_info_section_label,
> debug_skeleton_info_section,
> "Offset of Compilation Unit Info");
> else
> - dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
> - debug_info_section,
> + dw2_asm_output_offset (DWARF_OFFSET_SIZE, label, debug_info_section,
> "Offset of Compilation Unit Info");
> - dw2_asm_output_data (DWARF_OFFSET_SIZE, next_die_offset,
> + dw2_asm_output_data (DWARF_OFFSET_SIZE, cu_length,
> "Compilation Unit Length");
> +}
> +
> +static void
> +output_pubnames (vec<pubname_entry, va_gc> *names)
> +{
> + unsigned i;
> + unsigned long pubnames_length = size_of_pubnames (names);
> + pubname_ref pub;
> + limbo_die_node *node;
> +
> + if (names == pubname_table)
> + switch_to_section (debug_pubnames_section);
> + else
> + switch_to_section (debug_pubtypes_section);
> +
> + output_pubnames_header (pubnames_length, debug_info_section_label,
> + next_die_offset);
>
> FOR_EACH_VEC_ELT (*names, i, pub)
> {
> + /* Skip imported units, as they have their own CUs. */
> + if (lookup_imported_unit_key (pub->die))
> + continue;
> +
> if (include_pubname_in_output (names, pub))
> {
> dw_offset die_offset = pub->die->die_offset;
>
> - /* We shouldn't see pubnames for DIEs outside of the main CU. */
> - if (names == pubname_table && pub->die->die_tag != DW_TAG_enumerator)
> - gcc_assert (pub->die->die_mark);
> -
> /* If we're putting types in their own .debug_types sections,
> the .debug_pubtypes table will still point to the compile
> unit (not the type unit), so we want to use the offset of
> @@ -9570,39 +10037,76 @@ output_pubnames (vec<pubname_entry, va_gc> *names)
> }
>
> dw2_asm_output_data (DWARF_OFFSET_SIZE, 0, NULL);
> -}
> -
> -/* Output public names and types tables if necessary. */
>
> -static void
> -output_pubtables (void)
> -{
> - if (!want_pubnames () || !info_section_emitted)
> + if (names == pubtype_table)
> return;
>
> - switch_to_section (debug_pubnames_section);
> - output_pubnames (pubname_table);
> - /* ??? Only defined by DWARF3, but emitted by Darwin for DWARF2.
> - It shouldn't hurt to emit it always, since pure DWARF2 consumers
> - simply won't look for the section. */
> - switch_to_section (debug_pubtypes_section);
> - output_pubnames (pubtype_table);
> + /* Now output the pubnames for each CU. */
> + for (node = limbo_die_list; node; node = node->next)
> + {
> + dw_die_ref cu = node->die;
> + dw_die_ref die;
> + tree key = lookup_imported_unit_key (cu);
> + char *label;
> + const char *name, *decl_name;
> + unsigned long size;
> + bool found = false;
> +
> + if (!key)
> + continue;
> +
> + die = cu->die_child->die_sib;
> + if (die->die_tag == DW_TAG_imported_unit)
> + die = die->die_sib;
> + gcc_assert (die->die_tag == DW_TAG_subprogram);
> +
> + /* Find respective pubname entry. */
> + FOR_EACH_VEC_ELT (*names, i, pub)
> + {
> + if (die == pub->die)
> + {
> + found = true;
> + break;
> + }
> + }
> +
> + gcc_assert (found);
> + name = pub->name;
> + size = (DWARF_PUBNAMES_HEADER_SIZE + strlen(name) + DWARF_OFFSET_SIZE
> + + 1 + DWARF_OFFSET_SIZE);
> +
> + decl_name = IDENTIFIER_POINTER (DECL_NAME (key));
> + label = XALLOCAVEC (char, strlen (decl_name) + 10);
> + ASM_GENERATE_INTERNAL_LABEL (label, decl_name, 0);
> +
> + switch_to_section_for_imported_decl (key, DEBUG_PUBNAMES_SECTION);
> +
> + output_pubnames_header (size, label, cu_size (cu));
> + output_pubname (die->die_offset, pub);
> + dw2_asm_output_data (DWARF_OFFSET_SIZE, 0 , NULL);
> + }
> }
>
> +static inline unsigned
> +get_range_idx (dw_attr_ref a)
> +{
> + gcc_assert (AT_class (a) == dw_val_class_range_list);
> + return a->dw_attr_val.v.val_offset / 2 / DWARF2_ADDR_SIZE;
> +}
>
> /* Output the information that goes into the .debug_aranges table.
> Namely, define the beginning and ending address range of the
> text section generated for this compilation unit. */
>
> static void
> -output_aranges (unsigned long aranges_length)
> +output_aranges_header (unsigned long length, const char *label)
> {
> unsigned i;
>
> if (DWARF_INITIAL_LENGTH_SIZE - DWARF_OFFSET_SIZE == 4)
> dw2_asm_output_data (4, 0xffffffff,
> "Initial length escape value indicating 64-bit DWARF extension");
> - dw2_asm_output_data (DWARF_OFFSET_SIZE, aranges_length,
> + dw2_asm_output_data (DWARF_OFFSET_SIZE, length,
> "Length of Address Ranges Info");
> /* Version number for aranges is still 2, even up to DWARF5. */
> dw2_asm_output_data (2, 2, "DWARF Version");
> @@ -9611,7 +10115,7 @@ output_aranges (unsigned long aranges_length)
> debug_skeleton_info_section,
> "Offset of Compilation Unit Info");
> else
> - dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_info_section_label,
> + dw2_asm_output_offset (DWARF_OFFSET_SIZE, label,
> debug_info_section,
> "Offset of Compilation Unit Info");
> dw2_asm_output_data (1, DWARF2_ADDR_SIZE, "Size of Address");
> @@ -9627,6 +10131,28 @@ output_aranges (unsigned long aranges_length)
> for (i = 2; i < (unsigned) DWARF_ARANGES_PAD_SIZE; i += 2)
> dw2_asm_output_data (2, 0, NULL);
> }
> +}
> +
> +static unsigned
> +count_ranges (dw_attr_ref a)
> +{
> + unsigned idx, count;
> +
> + count = 0;
> + for (idx = get_range_idx (a); ranges_table[idx].num != 0; ++idx)
> + ++count;
> +
> + return count;
> +}
> +
> +static void
> +output_aranges ()
> +{
> + unsigned long aranges_length = size_of_aranges ();
> + limbo_die_node *node;
> +
> + switch_to_section (debug_aranges_section);
> + output_aranges_header (aranges_length, debug_info_section_label);
>
> /* It is necessary not to output these entries if the sections were
> not used; if the sections were not used, the length will be 0 and
> @@ -9654,7 +10180,8 @@ output_aranges (unsigned long aranges_length)
>
> FOR_EACH_VEC_ELT (*fde_vec, fde_idx, fde)
> {
> - if (DECL_IGNORED_P (fde->decl))
> + if (DECL_IGNORED_P (fde->decl) ||
> + should_fde_move_to_imported_unit (fde))
> continue;
> if (!fde->in_std_section)
> {
> @@ -9676,6 +10203,72 @@ output_aranges (unsigned long aranges_length)
> /* Output the terminator words. */
> dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
> dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
> +
> + /* Now output the aranges for function CUs. */
> + for (node = limbo_die_list; node; node = node->next)
> + {
> + dw_die_ref cu = node->die;
> + dw_attr_ref a;
> + unsigned num_ranges;
> + tree key;
> + char *label;
> + const char *decl_name;
> +
> + if ((a = get_AT (cu, DW_AT_ranges)))
> + num_ranges = count_ranges (a);
> + else if ((a = get_AT (cu, DW_AT_low_pc)))
> + num_ranges = 1;
> + else
> + continue;
> +
> + key = lookup_imported_unit_key (cu);
> + if (key)
> + {
> + switch_to_section_for_imported_decl (key,
> + DEBUG_ARANGES_SECTION);
> + }
> +
> + aranges_length = (DWARF_ARANGES_HEADER_SIZE
> + + 2 * num_ranges * DWARF2_ADDR_SIZE
> + + 2 * DWARF2_ADDR_SIZE);
> +
> + decl_name = targetm.strip_name_encoding (IDENTIFIER_POINTER
> + (DECL_ASSEMBLER_NAME (key)));
> + label = XALLOCAVEC (char, strlen (decl_name) + 10);
> + ASM_GENERATE_INTERNAL_LABEL (label, decl_name , 0);
> + output_aranges_header (aranges_length, label);
> + if (a->dw_attr == DW_AT_low_pc)
> + {
> + const char *start = AT_lbl (a);
> + const char *end = get_AT_hi_pc (cu);
> + dw2_asm_output_addr (DWARF2_ADDR_SIZE, start, "Address");
> + dw2_asm_output_delta (DWARF2_ADDR_SIZE, end, start, "Length");
> + }
> + else
> + {
> + unsigned idx;
> + for (idx = get_range_idx (a); ; ++idx)
> + {
> + int num = ranges_table[idx].num;
> + if (num == 0)
> + break;
> + else if (num < 0)
> + {
> + int lab_idx = - num - 1;
> + const char *start = ranges_by_label[lab_idx].begin;
> + const char *end = ranges_by_label[lab_idx].end;
> + dw2_asm_output_addr (DWARF2_ADDR_SIZE, start, "Address");
> + dw2_asm_output_delta (DWARF2_ADDR_SIZE, end, start, "Length");
> + }
> + else
> + /* We shouldn't need to handle block ranges here. */
> + gcc_unreachable ();
> + }
> + }
> + /* Output the terminator words. */
> + dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
> + dw2_asm_output_data (DWARF2_ADDR_SIZE, 0, NULL);
> + }
> }
>
> /* Add a new entry to .debug_ranges. Return the offset at which it
> @@ -13808,6 +14401,21 @@ secname_for_decl (const_tree decl)
> return secname;
> }
>
> +static void
> +switch_to_section_for_imported_decl (tree decl, const char *prefix)
> +{
> + const char *section_name;
> + char *tmp;
> + section_name = DECL_SECTION_NAME (decl);
> + if (!section_name)
> + section_name = function_section (decl)->named.name;
> + tmp = XALLOCAVEC (char, strlen (section_name) +
> + strlen (prefix) + 1);
> +
> + sprintf (tmp, "%s%s", prefix, section_name);
> + switch_to_section (get_section (tmp, SECTION_DEBUG, NULL));
> +}
> +
> /* Return true when DECL_BY_REFERENCE is defined and set for DECL. */
>
> static bool
> @@ -18386,6 +18994,25 @@ gen_call_site_die (tree decl, dw_die_ref subr_die,
> return die;
> }
>
> +static bool
> +should_fde_move_to_imported_unit (dw_fde_ref fde)
> +{
> + dw_die_ref die;
> +
> + if (!flag_dwarf_sections)
> + return false;
> +
> + if (fde->in_std_section ||
> + (fde->dw_fde_second_begin && fde->second_in_std_section))
> + return false;
> +
> + if (!(die = lookup_decl_die (fde->decl)))
> + return false;
> +
> + return !get_AT (die, DW_AT_abstract_origin) &&
> + !get_AT (die, DW_AT_specification);
> +}
> +
> /* Generate a DIE to represent a declared function (either file-scope or
> block-local). */
>
> @@ -18732,6 +19359,10 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
> add_AT_loc (subr_die, DW_AT_frame_base, list->expr);
> }
>
> + if (should_fde_move_to_imported_unit (fun->fde))
> + if (context_die == comp_unit_die ())
> + record_imported_unit_key (subr_die, decl);
> +
> /* Compute a displacement from the "steady-state frame pointer" to
> the CFA. The former is what all stack slots and argument slots
> will reference in the rtl; the latter is what we've told the
> @@ -24644,6 +25275,9 @@ dwarf2out_finish (const char *filename)
> if (flag_eliminate_unused_debug_types)
> prune_unused_types ();
>
> + if (flag_dwarf_sections)
> + break_out_functions (comp_unit_die ());
> +
> /* Generate separate COMDAT sections for type DIEs. */
> if (use_debug_types)
> {
> @@ -24699,8 +25333,7 @@ dwarf2out_finish (const char *filename)
>
> /* We can only use the low/high_pc attributes if all of the code was
> in .text. */
> - if (!have_multiple_function_sections
> - || (dwarf_version < 3 && dwarf_strict))
> + if (can_use_low_high_pc () || (dwarf_version < 3 && dwarf_strict))
> {
> /* Don't add if the CU has no associated code. */
> if (text_section_used)
> @@ -24709,9 +25342,9 @@ dwarf2out_finish (const char *filename)
> }
> else
> {
> + bool range_list_added = false;
> unsigned fde_idx;
> dw_fde_ref fde;
> - bool range_list_added = false;
>
> if (text_section_used)
> add_ranges_by_labels (main_comp_unit_die, text_section_label,
> @@ -24720,6 +25353,8 @@ dwarf2out_finish (const char *filename)
> add_ranges_by_labels (main_comp_unit_die, cold_text_section_label,
> cold_end_label, &range_list_added, true);
>
> + if (fde_vec)
> + {
> FOR_EACH_VEC_ELT (*fde_vec, fde_idx, fde)
> {
> if (DECL_IGNORED_P (fde->decl))
> @@ -24733,6 +25368,7 @@ dwarf2out_finish (const char *filename)
> fde->dw_fde_second_end, &range_list_added,
> true);
> }
> + }
>
> if (range_list_added)
> {
> @@ -24749,14 +25385,102 @@ dwarf2out_finish (const char *filename)
> }
> }
>
> + /* Also add ranges to the CUs for broken out functions. */
> + for (node = limbo_die_list; node; node = node->next)
> + {
> + dw_die_ref c = node->die->die_child;
> + bool added = false;
> + dw_attr_ref a;
> +
> + if (c == NULL)
> + continue;
> + c = c->die_sib;
> + if (c->die_tag == DW_TAG_imported_unit)
> + c = c->die_sib;
> + if (c->die_tag != DW_TAG_subprogram)
> + continue;
> +
> + if (c->die_sib && c->die_sib->die_tag == DW_TAG_subprogram)
> + {
> + /* There are multiple functions in this CU, so we need to add
> + ranges for all of them. */
> + dw_die_ref next = c;
> + do
> + {
> + c = next;
> + next = c->die_sib;
> + if (c->die_tag == DW_TAG_subprogram)
> + {
> + if ((a = get_AT (c, DW_AT_ranges)))
> + {
> + /* Copy the ranges from this function. */
> + unsigned idx; int num;
> + for (idx = get_range_idx (a);
> + (num = ranges_table[idx].num) != 0;
> + ++idx)
> + {
> + if (num < 0)
> + {
> + int idx2 = - num - 1;
> + const char *start = ranges_by_label[idx2].begin;
> + const char *end = ranges_by_label[idx2].end;
> + add_ranges_by_labels (node->die, start, end,
> + &added, true);
> + }
> + else
> + /* We shouldn't need to handle block ranges here. */
> + gcc_unreachable ();
> + }
> + }
> + else
> + add_ranges_by_labels (node->die, get_AT_low_pc (c),
> + get_AT_hi_pc (c), &added, true);
> + }
> + }
> + while (c != node->die->die_child);
> + /* Set the base address. */
> + add_AT_addr (node->die, DW_AT_low_pc, const0_rtx, true);
> + add_ranges (NULL);
> + }
> + /* We need to use DW_AT_ranges on the CU if any descendant DIE does
> + so that we can set up a base address; for now, rather than search
> + descendants let's just use it if we used DW_AT_ranges anywhere in
> + this translation unit.
> +
> + FIXME better would be to use low/hi_pc if the function does and
> + make any ranges in descendant dies relative to the low_pc. */
> +
> + else if (ranges_table_in_use > 0)
> + {
> + if ((a = get_AT (c, DW_AT_ranges)))
> + add_AT_range_list (node->die, DW_AT_ranges,
> + a->dw_attr_val.v.val_offset, true);
> + else
> + add_ranges_by_labels (node->die, get_AT_low_pc (c),
> + get_AT_hi_pc (c), &added, true);
> + add_AT_addr (node->die, DW_AT_low_pc, const0_rtx, true);
> + add_ranges (NULL);
> + }
> + else
> + {
> + add_AT_lbl_id (node->die, DW_AT_low_pc, get_AT_low_pc (c));
> + add_AT_lbl_id (node->die, DW_AT_high_pc, get_AT_hi_pc (c));
> + }
> + }
> if (debug_info_level >= DINFO_LEVEL_TERSE)
> add_AT_lineptr (main_comp_unit_die, DW_AT_stmt_list,
> debug_line_section_label);
>
> if (have_macinfo)
> - add_AT_macptr (comp_unit_die (),
> + {
> + add_AT_macptr (comp_unit_die (),
> dwarf_strict ? DW_AT_macro_info : DW_AT_GNU_macros,
> macinfo_section_label);
> + for (node = limbo_die_list; node; node = node->next)
> + add_AT_macptr (node->die,
> + dwarf_strict ? DW_AT_macro_info : DW_AT_GNU_macros,
> + macinfo_section_label);
> + }
>
> if (dwarf_split_debug_info)
> {
> @@ -24777,7 +25501,11 @@ dwarf2out_finish (const char *filename)
> }
>
> if (have_location_lists)
> - optimize_location_lists (comp_unit_die ());
> + {
> + optimize_location_lists (comp_unit_die ());
> + for (node = limbo_die_list; node; node = node->next)
> + optimize_location_lists (node->die);
> + }
>
> save_macinfo_strings ();
>
> @@ -24875,13 +25603,39 @@ dwarf2out_finish (const char *filename)
> /* Output location list section if necessary. */
> if (have_location_lists)
> {
> + tree key;
> /* Output the location lists info. */
> switch_to_section (debug_loc_section);
> ASM_OUTPUT_LABEL (asm_out_file, loc_section_label);
> output_location_lists (comp_unit_die ());
> +
> + for (node = limbo_die_list; node; node = node->next)
> + {
> + if ((key = lookup_imported_unit_key (node->die)))
> + {
> + switch_to_section_for_imported_decl (key,
> + DEBUG_LOC_SECTION);
> + output_location_lists (node->die);
> + }
> + }
> + }
> +
> + /* Output public names table if necessary. */
> + if (!pubname_table->is_empty() && want_pubnames ())
> + {
> + gcc_assert (info_section_emitted);
> + output_pubnames (pubname_table);
> }
>
> - output_pubtables ();
> + /* Output public types table if necessary. */
> + /* ??? Only defined by DWARF3, but emitted by Darwin for DWARF2.
> + It shouldn't hurt to emit it always, since pure DWARF2 consumers
> + simply won't look for the section. */
> + if (!pubtype_table->is_empty() && want_pubnames ())
> + {
> + gcc_assert (info_section_emitted);
> + output_pubnames (pubtype_table);
> + }
>
> /* Output the address range information if a CU (.debug_info section)
> was emitted. We output an empty table even if we had no functions
> @@ -24890,10 +25644,7 @@ dwarf2out_finish (const char *filename)
> generate a table that would have contained data. */
> if (info_section_emitted)
> {
> - unsigned long aranges_length = size_of_aranges ();
> -
> - switch_to_section (debug_aranges_section);
> - output_aranges (aranges_length);
> + output_aranges ();
> }
>
> /* Output ranges section if necessary. */
> @@ -25022,6 +25773,7 @@ dwarf2out_c_finalize (void)
> base_types.release ();
> XDELETEVEC (producer_string);
> producer_string = NULL;
> + imported_unit_hash = NULL;
> }
>
> #include "gt-dwarf2out.h"
> diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-1.c b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-1.c
> new file mode 100644
> index 0000000..b2d0ee6
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-1.c
> @@ -0,0 +1,34 @@
> +/* { dg-do compile } */
> +/* { dg-options "-ffunction-sections -fdwarf-sections -gdwarf-4 -gpubnames" } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_info" 4 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_infomyTextSection" 1 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_info\.text\.bar_func" 1 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_info\.text\.main" 1 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_aranges" 4 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_arangesmyTextSection" 1 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_aranges\.text\.bar_func" 1 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_aranges\.text\.main" 1 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnames" 4 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnamesmyTextSection" 1 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnames\.text\.bar_func" 1 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnames\.text\.main" 1 } } */
> +
> +int gvar1, i1;
> +
> +void foo (int i) __attribute__((section ("myTextSection")));
> +void bar () asm ("bar_func");
> +
> +void foo (int i) {
> + gvar1 += i;
> +}
> +
> +void bar () {
> + gvar1 -= 1;
> + return;
> +}
> +
> +int main () {
> + foo (i1);
> + i1++;
> + return 1;
> +}
> diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-2.c b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-2.c
> new file mode 100644
> index 0000000..ae918b7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/dwarf-sections-2.c
> @@ -0,0 +1,28 @@
> +/* { dg-do compile } */
> +/* { dg-options "-fdwarf-sections -gdwarf-4 -gpubnames" } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_info" 2 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_info.NonStdSection" 1 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_aranges" 2 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_aranges.NonStdSection" 1 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnames" 2 } } */
> +/* { dg-final { scan-assembler-times "\.section\[\t \]\[^\n\]*debug_pubnames.NonStdSection" 1 } } */
> +
> +int gvar1, i1;
> +
> +void foo (int i) __attribute__((section (".NonStdSection")));
> +void bar () asm ("bar_func");
> +
> +void foo (int i) {
> + gvar1 += i;
> +}
> +
> +void bar () {
> + gvar1 -= 1;
> + return;
> +}
> +
> +int main () {
> + bar ();
> + i1++;
> + return 1;
> +}
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index b06eed3..8548912 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -1603,6 +1603,12 @@ process_options (void)
> }
> }
>
> + if (flag_dwarf_sections && (dwarf_version < 4))
> + {
> + warning (0, "-fdwarf-sections not supported for dwarf version lower than 4");
> + flag_dwarf_sections = 0;
> + }
> +
> #ifndef HAVE_prefetch
> if (flag_prefetch_loop_arrays > 0)
> {