This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: [dwarf, RFC] Emitting per-function dwarf info


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)
>      {


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]