[dwarf,RFC] Emitting per-function dwarf info

Submitted by Senthil Kumar Selvaraj on April 10, 2015, 6:49 a.m.

Details

Message ID 20150410064936.GA2298@atmel.com
State New
Headers show

Commit Message

Senthil Kumar Selvaraj April 10, 2015, 6:49 a.m.
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

Comments

Senthil Kumar Selvaraj May 20, 2015, 5:01 a.m.
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)
>      {

Patch hide | download patch | download mbox

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