[PATCH][RFC] Early LTO debug

Richard Biener rguenther@suse.de
Tue Sep 6 11:18:00 GMT 2016


On Fri, 26 Aug 2016, Richard Biener wrote:

> 
> The patch below is my current development state of Early LTO debug lumped
> into one big patch (no changelog, sorry).  It contains previously posted
> 
>   https://gcc.gnu.org/ml/gcc-patches/2015-08/msg01089.html
> 
> which extends dwarf2asm.h and adjusts a target hook which means the
> patch carries the usual tm.texi burden.
> 
> From previous iterations the patch has removed most of the dwarf2out
> refactoring to make it easier to review.  It does get away with
> dwarf2out_abstract_function (we have that by means of early debug
> for all functions) and it moves quite a bunch of stuff from late
> dwarf processing to early.  For actual submission I will try to
> factor out parts of the latter at least.
> 
> 
> So the main idea is to emit the DWARF tree we have at 
> dwarf2out_early_finish into "Early LTO DWARF" sections (using
> .gnu.debuglto_ section prefixes, not .gnu.lto_ because GNU as
> spits out warnings about bogus section flags used for 
> .gnu.lto_.debug_str).  If we produce fat LTO objects the full
> DWARF info will be emit alongside regular assembly again at
> dwarf2out_finish time.  When streaming out LTO bytecode we
> record for each interesting tree (decls at the moment) a reference
> to the "early DIE" generated via a $symbol + offset pair.  There
> is a single symbol emitted for each early debug-info section.
> 
> At WPA time those references are simply carried through.
> 
> At LTRANS time those references are read back in and dwarf2out
> is told about the existence of the early DIE for tree X and
> it creates a shadow DIE for that, simply refering to the early
> DIE via a symbolic reference.  At dwarf2out_finish time we then
> annotate the early DIE by means of the shadow DIE which has
> a DW_AT_abstract_origin refering to the early DIE.  This mostly
> adds location information to DIEs.
> 
> 
> On the driver side things get a little complicated as we need to
> funnel through the "Early LTO DWARF" sections to the final link,
> presenting them as regular debug info sections.  To make a smooth
> transition possible I choose to write a Early LTO DWARF copier
> in the simple-object framework which basically takes all the
> early LTO DWARF sections and writes them into a new object file,
> renaming them on-the-fly.  I implemented this for ELF only sofar
> and the API needs to be changed to reflect the fact that this is
> really only suited for use as LTO early DWARF section copier
> (tried to make it a little more generic but that didn't work
> out very well).  So for each LTO input file you get a LTO early
> debug object which are then partially linked to a single LTO
> early debug object (we could simply omit that step, it doesn't
> save very much due to the fact that GNU ld with partial linking
> doesn't optimize mergeable string sections...).  This object
> is then fed back as LTO link output to the linker.
> 
> Ideally the linker plugin API would be extended to allow claiming
> only a subset of sections and allow telling the linker to rename
> sections that we didn't claim.  That would avoid some I/O and
> disk-space usage.
> 
> 
> The main motivation for this is to increase the quality of
> debug information you get when using LTO and the most promising
> part of testing is
> 
>                 === libstdc++ tests ===
>  
>  Running target unix/-flto/-g/
>  FAIL: 23_containers/deque/cons/clear_allocator.cc execution test
> -FAIL: libstdc++-prettyprinters/48362.cc print t1
> -FAIL: libstdc++-prettyprinters/48362.cc print t2
> -FAIL: libstdc++-prettyprinters/cxx11.cc print efl
> ...
> -FAIL: libstdc++-prettyprinters/whatis.cc whatis unord2_holder
> -FAIL: libstdc++-prettyprinters/whatis.cc whatis ustring_holder
>  
>                 === libstdc++ Summary for unix/-flto/-g/ ===
>  
>  # of expected passes           11423
> -# of unexpected failures       140
> +# of unexpected failures       1
>  # of expected failures         65
> -# of unsupported tests         234
> +# of unsupported tests         243
> 
> which means all libstdc++ pretty printer tests now pass!
> 
> We get a few guality improvements (mind you, unconditionally added
> -flto -g):
> 
> -FAIL: gcc.dg/guality/inline-params.c   -O1  execution test
> +XPASS: gcc.dg/guality/inline-params.c   -O2  execution test
>  XPASS: gcc.dg/guality/inline-params.c   -O2 -flto -fno-use-linker-plugin 
> -flto-partition=none  execution test
> +XPASS: gcc.dg/guality/inline-params.c   -O2 -flto -fuse-linker-plugin 
> -fno-fat-lto-objects  execution test
> +XPASS: gcc.dg/guality/inline-params.c   -O3 -g  execution test
> +XPASS: gcc.dg/guality/inline-params.c   -Os  execution test
> 
> but also the issue that gdb doesn't cope with the way we represent
> VLAs with the new scheme:
> 
> +FAIL: gcc.dg/guality/vla-1.c   -O0  line 17 sizeof (a) == 6
> +FAIL: gcc.dg/guality/vla-1.c   -O0  line 24 sizeof (a) == 17 * sizeof 
> (short)
> +FAIL: gcc.dg/guality/vla-1.c   -O1  line 17 sizeof (a) == 6
> +FAIL: gcc.dg/guality/vla-1.c   -O1  line 24 sizeof (a) == 17 * sizeof 
> (short)
>  FAIL: gcc.dg/guality/vla-1.c   -O2  line 17 sizeof (a) == 6
> +FAIL: gcc.dg/guality/vla-1.c   -O2  line 24 sizeof (a) == 17 * sizeof 
> (short)
> +FAIL: gcc.dg/guality/vla-1.c   -O2 -flto -fno-use-linker-plugin 
> -flto-partition
> =none  line 17 sizeof (a) == 6
> +FAIL: gcc.dg/guality/vla-1.c   -O2 -flto -fno-use-linker-plugin 
> -flto-partition
> =none  line 24 sizeof (a) == 17 * sizeof (short)
>  FAIL: gcc.dg/guality/vla-1.c   -O2 -flto -fuse-linker-plugin 
> -fno-fat-lto-objec
> ts  line 17 sizeof (a) == 6
> +FAIL: gcc.dg/guality/vla-1.c   -O2 -flto -fuse-linker-plugin 
> -fno-fat-lto-objec
> ts  line 24 sizeof (a) == 17 * sizeof (short)
>  FAIL: gcc.dg/guality/vla-1.c   -O3 -g  line 17 sizeof (a) == 6
> +FAIL: gcc.dg/guality/vla-1.c   -O3 -g  line 24 sizeof (a) == 17 * sizeof 
> (short
> )
>  FAIL: gcc.dg/guality/vla-1.c   -Os  line 17 sizeof (a) == 6
> +FAIL: gcc.dg/guality/vla-1.c   -Os  line 24 sizeof (a) == 17 * sizeof 
> (short)
> +FAIL: gcc.dg/guality/vla-2.c   -O0  line 16 sizeof (a) == 5 * sizeof 
> (int)
> +FAIL: gcc.dg/guality/vla-2.c   -O0  line 25 sizeof (a) == 6 * sizeof 
> (int)
> +FAIL: gcc.dg/guality/vla-2.c   -O1  line 16 sizeof (a) == 5 * sizeof 
> (int)
> +FAIL: gcc.dg/guality/vla-2.c   -O2  line 16 sizeof (a) == 5 * sizeof 
> (int)
> +FAIL: gcc.dg/guality/vla-2.c   -O2 -flto -fno-use-linker-plugin 
> -flto-partition=none  line 16 sizeof (a) == 5 * sizeof (int)
> +FAIL: gcc.dg/guality/vla-2.c   -O2 -flto -fuse-linker-plugin 
> -fno-fat-lto-objects  line 16 sizeof (a) == 5 * sizeof (int)
> +FAIL: gcc.dg/guality/vla-2.c   -O3 -g  line 16 sizeof (a) == 5 * sizeof 
> (int)
> +FAIL: gcc.dg/guality/vla-2.c   -Os  line 16 sizeof (a) == 5 * sizeof 
> (int)
> 
> there is now a gdb bug for this and I raised the issue on the gdb
> list last year but both got zero responses sofar (swo#20426).
> 
> In the end I consider VLAs less important than basic C++ debugging
> with pretty printers.  The above testing was using gdb 7.7 but IIRC
> the situation doesn't change with newer versions.
> 
> At quite some cost we can maybe work around the VLA issue but I'd
> like to get feedback from gdb side before going down that route.

I've done some workarounds to preserve old VLA behavior (it already
"works" when inlining as we get copies of decls and their types
there and we re-create those late).  Still runs into 
https://sourceware.org/bugzilla/show_bug.cgi?id=20536 though.

The following is an updated patch, mainly stripped out old unnecessary
cruft and some fixes for an issue that arised when LTOing Firefox.

It also re-structures the simple-object API addition to be just

/* Copy LTO debug sections from SRC_OBJECT to DEST.
   If an error occurs, return the errno value in ERR and an error string.  
*/

extern const char *
simple_object_copy_lto_debug_sections (simple_object_read *src_object,
                                       const char *dest,
                                       int *err);

but doesn't yet fully exploit this in the ELF implementation (which
also saw some fixes).  No non-ELF implementations available yet.

Feedback (and actual experience debugging LTO programs with this
patch) appreciated.

Thanks,
Richard.

Index: gcc/cgraphunit.c
===================================================================
--- gcc/cgraphunit.c	(revision 240004)
+++ gcc/cgraphunit.c	(working copy)
@@ -1193,8 +1193,23 @@ analyze_functions (bool first_time)
 	     at looking at optimized away DECLs, since
 	     late_global_decl will subsequently be called from the
 	     contents of the now pruned symbol table.  */
+#if 0
 	  if (!decl_function_context (node->decl))
-	    (*debug_hooks->late_global_decl) (node->decl);
+	    {
+	      /* ???  If we are about to generate LTO bytecode
+	         we can't have non-early DIEs because those may
+		 refer to decls we'll never output via
+		 loc_list_from_tree -> rtl_for_decl_location
+		 which via make_decl_rtl_for_debug relies on
+		 resolve_addr to remove referenes to not output
+		 symbols.  Exactly what we do _not_ want here btw!
+		 We want the early path which only uses
+		 tree_add_const_value_attribute_for_decl.  But
+		 we should have visited this decl early anyway.
+		 Thus do nothing now (or invent a new hook).  */
+	      (*debug_hooks->late_global_decl) (node->decl);
+	    }
+#endif
 
 	  node->remove ();
 	  continue;
@@ -2561,7 +2576,7 @@ symbol_table::finalize_compilation_unit
 
       /* Clean up anything that needs cleaning up after initial debug
 	 generation.  */
-      (*debug_hooks->early_finish) ();
+      (*debug_hooks->early_finish) (main_input_filename);
     }
 
   /* Finally drive the pass manager.  */
Index: gcc/config/darwin-protos.h
===================================================================
--- gcc/config/darwin-protos.h	(revision 240004)
+++ gcc/config/darwin-protos.h	(working copy)
@@ -91,9 +91,9 @@ extern void darwin_globalize_label (FILE
 extern void darwin_assemble_visibility (tree, int);
 
 extern void darwin_asm_output_dwarf_delta (FILE *, int, const char *,
-					   const char *);
+					   const char *, HOST_WIDE_INT);
 extern void darwin_asm_output_dwarf_offset (FILE *, int, const char *,
-					    section *);
+					    HOST_WIDE_INT, section *);
 
 extern void darwin_asm_declare_object_name (FILE *, const char *, tree);
 extern void darwin_asm_declare_constant_name (FILE *, const char *,
Index: gcc/config/darwin.c
===================================================================
--- gcc/config/darwin.c	(revision 240004)
+++ gcc/config/darwin.c	(working copy)
@@ -2790,7 +2790,8 @@ static int darwin_dwarf_label_counter;
 
 void
 darwin_asm_output_dwarf_delta (FILE *file, int size,
-			       const char *lab1, const char *lab2)
+			       const char *lab1, const char *lab2,
+			       HOST_WIDE_INT offset)
 {
   int islocaldiff = (lab1[0] == '*' && lab1[1] == 'L'
 		     && lab2[0] == '*' && lab2[1] == 'L');
@@ -2804,6 +2805,8 @@ darwin_asm_output_dwarf_delta (FILE *fil
   assemble_name_raw (file, lab1);
   fprintf (file, "-");
   assemble_name_raw (file, lab2);
+  if (offset != 0)
+    fprintf (file, "+" HOST_WIDE_INT_PRINT_DEC, offset);
   if (islocaldiff)
     fprintf (file, "\n\t%s L$set$%d", directive, darwin_dwarf_label_counter++);
 }
@@ -2815,7 +2818,7 @@ darwin_asm_output_dwarf_delta (FILE *fil
 
 void
 darwin_asm_output_dwarf_offset (FILE *file, int size, const char * lab,
-				section *base)
+				HOST_WIDE_INT offset, section *base)
 {
   char sname[64];
   int namelen;
@@ -2826,7 +2829,7 @@ darwin_asm_output_dwarf_offset (FILE *fi
 
   namelen = strchr (base->named.name + 8, ',') - (base->named.name + 8);
   sprintf (sname, "*Lsection%.*s", namelen, base->named.name + 8);
-  darwin_asm_output_dwarf_delta (file, size, lab, sname);
+  darwin_asm_output_dwarf_delta (file, size, lab, sname, offset);
 }
 
 /* Called from the within the TARGET_ASM_FILE_START for each target.  */
Index: gcc/config/darwin.h
===================================================================
--- gcc/config/darwin.h	(revision 240004)
+++ gcc/config/darwin.h	(working copy)
@@ -835,10 +835,10 @@ enum machopic_addr_class {
      ((CODE) == 1 || (GLOBAL) == 0) ? DW_EH_PE_pcrel : DW_EH_PE_absptr)
 
 #define ASM_OUTPUT_DWARF_DELTA(FILE,SIZE,LABEL1,LABEL2)  \
-  darwin_asm_output_dwarf_delta (FILE, SIZE, LABEL1, LABEL2)
+  darwin_asm_output_dwarf_delta (FILE, SIZE, LABEL1, LABEL2, 0)
 
-#define ASM_OUTPUT_DWARF_OFFSET(FILE,SIZE,LABEL,BASE)  \
-  darwin_asm_output_dwarf_offset (FILE, SIZE, LABEL, BASE)
+#define ASM_OUTPUT_DWARF_OFFSET(FILE,SIZE,LABEL,OFFSET,BASE)  \
+  darwin_asm_output_dwarf_offset (FILE, SIZE, LABEL, OFFSET, BASE)
 
 #define ASM_MAYBE_OUTPUT_ENCODED_ADDR_RTX(ASM_OUT_FILE, ENCODING, SIZE, ADDR, DONE)	\
       if (ENCODING == ASM_PREFERRED_EH_DATA_FORMAT (2, 1)) {				\
Index: gcc/config/i386/cygming.h
===================================================================
--- gcc/config/i386/cygming.h	(revision 240004)
+++ gcc/config/i386/cygming.h	(working copy)
@@ -102,13 +102,15 @@ along with GCC; see the file COPYING3.
 /* Use section relative relocations for debugging offsets.  Unlike
    other targets that fake this by putting the section VMA at 0, PE
    won't allow it.  */
-#define ASM_OUTPUT_DWARF_OFFSET(FILE, SIZE, LABEL, SECTION)	\
+#define ASM_OUTPUT_DWARF_OFFSET(FILE, SIZE, LABEL, OFFSET, SECTION) \
   do {								\
     switch (SIZE)						\
       {								\
       case 4:							\
 	fputs ("\t.secrel32\t", FILE);				\
 	assemble_name (FILE, LABEL);				\
+	if (offset != 0)					\
+	  fprintf (FILE, "+" HOST_WIDE_INT_PRINT_DEC, offset)	\
 	break;							\
       case 8:							\
 	/* This is a hack.  There is no 64-bit section relative	\
@@ -118,6 +120,8 @@ along with GCC; see the file COPYING3.
 	   Fake the 64-bit offset by zero-extending it.  */	\
 	fputs ("\t.secrel32\t", FILE);				\
 	assemble_name (FILE, LABEL);				\
+	if (offset != 0)					\
+	  fprintf (FILE, "+" HOST_WIDE_INT_PRINT_DEC, offset)	\
 	fputs ("\n\t.long\t0", FILE);				\
 	break;							\
       default:							\
Index: gcc/config/ia64/ia64.h
===================================================================
--- gcc/config/ia64/ia64.h	(revision 240004)
+++ gcc/config/ia64/ia64.h	(working copy)
@@ -1583,11 +1583,13 @@ do {									\
 /* Use section-relative relocations for debugging offsets.  Unlike other
    targets that fake this by putting the section VMA at 0, IA-64 has
    proper relocations for them.  */
-#define ASM_OUTPUT_DWARF_OFFSET(FILE, SIZE, LABEL, SECTION)	\
+#define ASM_OUTPUT_DWARF_OFFSET(FILE, SIZE, LABEL, OFFSET, SECTION) \
   do {								\
     fputs (integer_asm_op (SIZE, FALSE), FILE);			\
     fputs ("@secrel(", FILE);					\
     assemble_name (FILE, LABEL);				\
+    if (offset != 0)						\
+      fprintf (FILE, "+" HOST_WIDE_INT_PRINT_DEC, OFFSET);	\
     fputc (')', FILE);						\
   } while (0)
 
Index: gcc/dbxout.c
===================================================================
--- gcc/dbxout.c	(revision 240004)
+++ gcc/dbxout.c	(working copy)
@@ -344,7 +344,7 @@ const struct gcc_debug_hooks dbx_debug_h
 {
   dbxout_init,
   dbxout_finish,
-  debug_nothing_void,
+  debug_nothing_charstar,
   debug_nothing_void,
   debug_nothing_int_charstar,
   debug_nothing_int_charstar,
@@ -370,6 +370,8 @@ const struct gcc_debug_hooks dbx_debug_h
   dbxout_late_global_decl,		 /* late_global_decl */
   dbxout_type_decl,			 /* type_decl */
   debug_nothing_tree_tree_tree_bool,	 /* imported_module_or_decl */
+  debug_false_tree_charstarstar_uhwistar, /* die_ref_for_decl */
+  debug_nothing_tree_charstar_uhwi,	 /* register_external_die */
   debug_nothing_tree,		         /* deferred_inline_function */
   debug_nothing_tree,		         /* outlining_inline_function */
   debug_nothing_rtx_code_label,	         /* label */
Index: gcc/debug.c
===================================================================
--- gcc/debug.c	(revision 240004)
+++ gcc/debug.c	(working copy)
@@ -26,7 +26,7 @@ const struct gcc_debug_hooks do_nothing_
 {
   debug_nothing_charstar,
   debug_nothing_charstar,
-  debug_nothing_void,			/* early_finish */
+  debug_nothing_charstar,			/* early_finish */
   debug_nothing_void,
   debug_nothing_int_charstar,
   debug_nothing_int_charstar,
@@ -48,6 +48,8 @@ const struct gcc_debug_hooks do_nothing_
   debug_nothing_tree,	         	 /* late_global_decl */
   debug_nothing_tree_int,		 /* type_decl */
   debug_nothing_tree_tree_tree_bool,	 /* imported_module_or_decl */
+  debug_false_tree_charstarstar_uhwistar, /* die_ref_for_decl */
+  debug_nothing_tree_charstar_uhwi,	 /* register_external_die */
   debug_nothing_tree,		         /* deferred_inline_function */
   debug_nothing_tree,		         /* outlining_inline_function */
   debug_nothing_rtx_code_label,	         /* label */
@@ -138,3 +140,16 @@ debug_nothing_tree_int (tree decl ATTRIB
 			int local ATTRIBUTE_UNUSED)
 {
 }
+
+bool
+debug_false_tree_charstarstar_uhwistar (tree, const char **,
+					unsigned HOST_WIDE_INT *)
+{
+  return false;
+}
+
+void
+debug_nothing_tree_charstar_uhwi (tree, const char *,
+				  unsigned HOST_WIDE_INT)
+{
+}
Index: gcc/debug.h
===================================================================
--- gcc/debug.h	(revision 240004)
+++ gcc/debug.h	(working copy)
@@ -31,7 +31,7 @@ struct gcc_debug_hooks
   void (* finish) (const char *main_filename);
 
   /* Run cleanups necessary after early debug generation.  */
-  void (* early_finish) (void);
+  void (* early_finish) (const char *main_filename);
 
   /* Called from cgraph_optimize before starting to assemble
      functions/variables/toplevel asms.  */
@@ -146,6 +146,14 @@ struct gcc_debug_hooks
   void (* imported_module_or_decl) (tree decl, tree name,
 				    tree context, bool child);
 
+  /* Return true if a DIE for the tree is available and return a symbol
+     and offset that can be used to refer to it externally.  */
+  bool (* die_ref_for_decl) (tree, const char **, unsigned HOST_WIDE_INT *);
+
+  /* Early debug information for the tree is available at symbol plus
+     offset externally.  */
+  void (* register_external_die) (tree, const char *, unsigned HOST_WIDE_INT);
+
   /* DECL is an inline function, whose body is present, but which is
      not being output at this point.  */
   void (* deferred_inline_function) (tree decl);
@@ -205,6 +213,10 @@ extern void debug_nothing_tree_tree_tree
 extern bool debug_true_const_tree (const_tree);
 extern void debug_nothing_rtx_insn (rtx_insn *);
 extern void debug_nothing_rtx_code_label (rtx_code_label *);
+extern bool debug_false_tree_charstarstar_uhwistar (tree, const char **,
+						    unsigned HOST_WIDE_INT *);
+extern void debug_nothing_tree_charstar_uhwi (tree, const char *,
+					      unsigned HOST_WIDE_INT);
 
 /* Hooks for various debug formats.  */
 extern const struct gcc_debug_hooks do_nothing_debug_hooks;
Index: gcc/doc/tm.texi
===================================================================
--- gcc/doc/tm.texi	(revision 240004)
+++ gcc/doc/tm.texi	(working copy)
@@ -9735,10 +9735,11 @@ between the two given labels in system d
 slots on IA64 VMS, using an integer of the given size.
 @end defmac
 
-@defmac ASM_OUTPUT_DWARF_OFFSET (@var{stream}, @var{size}, @var{label}, @var{section})
+@defmac ASM_OUTPUT_DWARF_OFFSET (@var{stream}, @var{size}, @var{label}, @var{offset}, @var{section})
 A C statement to issue assembly directives that create a
-section-relative reference to the given @var{label}, using an integer of the
-given @var{size}.  The label is known to be defined in the given @var{section}.
+section-relative reference to the given @var{label} plus @var{offset}, using
+an integer of the given @var{size}.  The label is known to be defined in the
+given @var{section}.
 @end defmac
 
 @defmac ASM_OUTPUT_DWARF_PCREL (@var{stream}, @var{size}, @var{label})
Index: gcc/doc/tm.texi.in
===================================================================
--- gcc/doc/tm.texi.in	(revision 240004)
+++ gcc/doc/tm.texi.in	(working copy)
@@ -7075,10 +7075,11 @@ between the two given labels in system d
 slots on IA64 VMS, using an integer of the given size.
 @end defmac
 
-@defmac ASM_OUTPUT_DWARF_OFFSET (@var{stream}, @var{size}, @var{label}, @var{section})
+@defmac ASM_OUTPUT_DWARF_OFFSET (@var{stream}, @var{size}, @var{label}, @var{offset}, @var{section})
 A C statement to issue assembly directives that create a
-section-relative reference to the given @var{label}, using an integer of the
-given @var{size}.  The label is known to be defined in the given @var{section}.
+section-relative reference to the given @var{label} plus @var{offset}, using
+an integer of the given @var{size}.  The label is known to be defined in the
+given @var{section}.
 @end defmac
 
 @defmac ASM_OUTPUT_DWARF_PCREL (@var{stream}, @var{size}, @var{label})
Index: gcc/dwarf2asm.c
===================================================================
--- gcc/dwarf2asm.c	(revision 240004)
+++ gcc/dwarf2asm.c	(working copy)
@@ -30,6 +30,9 @@ along with GCC; see the file COPYING3.
 #include "output.h"
 #include "dwarf2asm.h"
 #include "dwarf2.h"
+#include "tm_p.h"
+#include "function.h"
+#include "emit-rtl.h"
 
 #ifndef XCOFF_DEBUGGING_INFO
 #define XCOFF_DEBUGGING_INFO 0
@@ -191,12 +194,39 @@ dw2_asm_output_offset (int size, const c
   va_start (ap, comment);
 
 #ifdef ASM_OUTPUT_DWARF_OFFSET
-  ASM_OUTPUT_DWARF_OFFSET (asm_out_file, size, label, base);
+  ASM_OUTPUT_DWARF_OFFSET (asm_out_file, size, label, 0, base);
 #else
   dw2_assemble_integer (size, gen_rtx_SYMBOL_REF (Pmode, label));
 #endif
 
   if (flag_debug_asm && comment)
+    {
+      fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
+      vfprintf (asm_out_file, comment, ap);
+    }
+  fputc ('\n', asm_out_file);
+
+  va_end (ap);
+}
+
+void
+dw2_asm_output_offset (int size, const char *label, HOST_WIDE_INT offset,
+		       section *base ATTRIBUTE_UNUSED,
+		       const char *comment, ...)
+{
+  va_list ap;
+
+  va_start (ap, comment);
+
+#ifdef ASM_OUTPUT_DWARF_OFFSET
+  ASM_OUTPUT_DWARF_OFFSET (asm_out_file, size, label, offset, base);
+#else
+  dw2_assemble_integer (size, gen_rtx_PLUS (Pmode,
+					    gen_rtx_SYMBOL_REF (Pmode, label),
+					    gen_int_mode (offset, Pmode)));
+#endif
+
+  if (flag_debug_asm && comment)
     {
       fprintf (asm_out_file, "\t%s ", ASM_COMMENT_START);
       vfprintf (asm_out_file, comment, ap);
Index: gcc/dwarf2asm.h
===================================================================
--- gcc/dwarf2asm.h	(revision 240004)
+++ gcc/dwarf2asm.h	(working copy)
@@ -40,6 +40,10 @@ extern void dw2_asm_output_offset (int,
 				   const char *, ...)
      ATTRIBUTE_NULL_PRINTF_4;
 
+extern void dw2_asm_output_offset (int, const char *, HOST_WIDE_INT,
+				   section *, const char *, ...)
+     ATTRIBUTE_NULL_PRINTF_5;
+
 extern void dw2_asm_output_addr (int, const char *, const char *, ...)
      ATTRIBUTE_NULL_PRINTF_3;
 
Index: gcc/dwarf2out.c
===================================================================
--- gcc/dwarf2out.c	(revision 240004)
+++ gcc/dwarf2out.c	(working copy)
@@ -91,6 +91,7 @@ along with GCC; see the file COPYING3.
 #include "tree-dfa.h"
 #include "gdb/gdb-index.h"
 #include "rtl-iter.h"
+#include "lto-section-names.h"
 
 static void dwarf2out_source_line (unsigned int, const char *, int, bool);
 static rtx_insn *last_var_location_insn;
@@ -2480,7 +2481,7 @@ build_cfa_aligned_loc (dw_cfa_location *
 
 static void dwarf2out_init (const char *);
 static void dwarf2out_finish (const char *);
-static void dwarf2out_early_finish (void);
+static void dwarf2out_early_finish (const char *);
 static void dwarf2out_assembly_start (void);
 static void dwarf2out_define (unsigned int, const char *);
 static void dwarf2out_undef (unsigned int, const char *);
@@ -2503,6 +2504,10 @@ static void dwarf2out_begin_function (tr
 static void dwarf2out_end_function (unsigned int);
 static void dwarf2out_register_main_translation_unit (tree unit);
 static void dwarf2out_set_name (tree, tree);
+static void dwarf2out_register_external_die (tree decl, const char *sym,
+					     unsigned HOST_WIDE_INT off);
+static bool dwarf2out_die_ref_for_decl (tree decl, const char **sym,
+					unsigned HOST_WIDE_INT *off);
 
 /* The debug hooks structure.  */
 
@@ -2537,6 +2542,8 @@ const struct gcc_debug_hooks dwarf2_debu
   dwarf2out_late_global_decl,
   dwarf2out_type_decl,		/* type_decl */
   dwarf2out_imported_module_or_decl,
+  dwarf2out_die_ref_for_decl,
+  dwarf2out_register_external_die,
   debug_nothing_tree,		/* deferred_inline_function */
   /* The DWARF 2 backend tries to reduce debugging bloat by not
      emitting the abstract description of inline functions until
@@ -2556,7 +2563,7 @@ const struct gcc_debug_hooks dwarf2_line
 {
   dwarf2out_init,
   debug_nothing_charstar,
-  debug_nothing_void,
+  debug_nothing_charstar,
   debug_nothing_void,
   debug_nothing_int_charstar,
   debug_nothing_int_charstar,
@@ -2578,6 +2585,8 @@ const struct gcc_debug_hooks dwarf2_line
   debug_nothing_tree,		         /* late_global_decl */
   debug_nothing_tree_int,		 /* type_decl */
   debug_nothing_tree_tree_tree_bool,	 /* imported_module_or_decl */
+  debug_false_tree_charstarstar_uhwistar, /* die_ref_for_decl */
+  debug_nothing_tree_charstar_uhwi,	 /* register_external_die */
   debug_nothing_tree,		         /* deferred_inline_function */
   debug_nothing_tree,		         /* outlining_inline_function */
   debug_nothing_rtx_code_label,	         /* label */
@@ -2705,15 +2714,23 @@ typedef struct GTY((chain_circular ("%h.
   /* Die is used and must not be pruned as unused.  */
   BOOL_BITFIELD die_perennial_p : 1;
   BOOL_BITFIELD comdat_type_p : 1; /* DIE has a type signature */
+  /* For an external ref to die_symbol if die_offset contains an extra
+     offset to that symbol.  */
+  BOOL_BITFIELD with_offset : 1;
+  /* Whether this DIE was removed from the DIE tree, for example via
+     prune_unused_types.  We don't consider those present from the
+     DIE lookup routines.  */
+  BOOL_BITFIELD removed : 1;
   /* Lots of spare bits.  */
 }
 die_node;
 
 /* Set to TRUE while dwarf2out_early_global_decl is running.  */
 static bool early_dwarf;
+static bool early_finished;
 struct set_early_dwarf {
   bool saved;
-  set_early_dwarf () : saved(early_dwarf) { early_dwarf = true; }
+  set_early_dwarf () : saved(early_dwarf) { gcc_assert (!early_finished); early_dwarf = true; }
   ~set_early_dwarf () { early_dwarf = saved; }
 };
 
@@ -4887,6 +4904,7 @@ remove_child_with_prev (dw_die_ref child
     prev->die_sib = child->die_sib;
   if (child->die_parent->die_child == child)
     child->die_parent->die_child = prev;
+  child->die_sib = NULL;
 }
 
 /* Replace OLD_CHILD with NEW_CHILD.  PREV must have the property that
@@ -4913,6 +4931,7 @@ replace_child (dw_die_ref old_child, dw_
     }
   if (old_child->die_parent->die_child == old_child)
     old_child->die_parent->die_child = new_child;
+  old_child->die_sib = NULL;
 }
 
 /* Move all children from OLD_PARENT to NEW_PARENT.  */
@@ -4943,9 +4962,9 @@ remove_child_TAG (dw_die_ref die, enum d
 	remove_child_with_prev (c, prev);
 	c->die_parent = NULL;
 	/* Might have removed every child.  */
-	if (c == c->die_sib)
+	if (die->die_child == NULL)
 	  return;
-	c = c->die_sib;
+	c = prev->die_sib;
       }
   } while (c != die->die_child);
 }
@@ -5091,7 +5110,13 @@ new_die (enum dwarf_tag tag_value, dw_di
 static inline dw_die_ref
 lookup_type_die (tree type)
 {
-  return TYPE_SYMTAB_DIE (type);
+  dw_die_ref die = TYPE_SYMTAB_DIE (type);
+  if (die && die->removed)
+    {
+      TYPE_SYMTAB_DIE (type) = NULL;
+      return NULL;
+    }
+  return die;
 }
 
 /* Given a TYPE_DIE representing the type TYPE, if TYPE is an
@@ -5156,7 +5181,192 @@ decl_die_hasher::equal (die_node *x, tre
 static inline dw_die_ref
 lookup_decl_die (tree decl)
 {
-  return decl_die_table->find_with_hash (decl, DECL_UID (decl));
+  dw_die_ref *die = decl_die_table->find_slot_with_hash (decl, DECL_UID (decl),
+							 NO_INSERT);
+  if (!die)
+    return NULL;
+  if ((*die)->removed)
+    {
+      decl_die_table->clear_slot (die);
+      return NULL;
+    }
+  return *die;
+}
+
+
+/* For DECL which might have early dwarf output query a SYMBOL + OFFSET
+   style reference.  Return true if we found one refering to a DIE for
+   DECL, otherwise return false.  */
+
+static bool
+dwarf2out_die_ref_for_decl (tree decl, const char **sym,
+			    unsigned HOST_WIDE_INT *off)
+{
+  dw_die_ref die;
+
+  if (flag_wpa && !decl_die_table)
+    return false;
+
+  if (TREE_CODE (decl) == BLOCK)
+    die = BLOCK_DIE (decl);
+  else
+    die = lookup_decl_die (decl);
+  if (!die)
+    return false;
+
+  /* During WPA stage we currently use DIEs to store the
+     decl <-> label + offset map.  That's quite inefficient but it
+     works for now.  */
+  if (flag_wpa)
+    {
+      dw_die_ref ref = get_AT_ref (die, DW_AT_abstract_origin);
+      if (!ref)
+	{
+	  gcc_assert (die == comp_unit_die ());
+	  return false;
+	}
+      *off = ref->die_offset;
+      *sym = ref->die_id.die_symbol;
+      return true;
+    }
+
+  /* Similar to get_ref_die_offset_label, but using the "correct"
+     label.  */
+  *off = die->die_offset;
+  while (die->die_parent)
+    die = die->die_parent;
+  /* For the containing CU DIE we compute a die_symbol in
+     compute_section_prefix.  */
+  gcc_assert (die->die_tag == DW_TAG_compile_unit
+	      && die->die_id.die_symbol != NULL);
+  *sym = die->die_id.die_symbol;
+  return true;
+}
+
+/* Add a reference of kind ATTR_KIND to a DIE at SYMBOL + OFFSET to DIE.  */
+
+static void
+add_AT_external_die_ref (dw_die_ref die, enum dwarf_attribute attr_kind,
+			 const char *symbol, HOST_WIDE_INT offset)
+{
+  /* Create a fake DIE that contains the reference.  Don't use
+     new_die because we don't want to end up in the limbo list.  */
+  dw_die_ref ref = ggc_cleared_alloc<die_node> ();
+  ref->die_tag = die->die_tag;
+  ref->die_id.die_symbol = IDENTIFIER_POINTER (get_identifier (symbol));
+  ref->die_offset = offset;
+  ref->with_offset = 1;
+  add_AT_die_ref (die, attr_kind, ref);
+}
+
+/* Create a DIE for DECL if required and add a reference to a DIE
+   at SYMBOL + OFFSET which contains attributes dumped early.  */
+
+static void
+dwarf2out_register_external_die (tree decl, const char *sym,
+				 unsigned HOST_WIDE_INT off)
+{
+  if (debug_info_level == DINFO_LEVEL_NONE)
+    return;
+
+  if (flag_wpa && !decl_die_table)
+    decl_die_table = hash_table<decl_die_hasher>::create_ggc (1000);
+
+  dw_die_ref die
+    = TREE_CODE (decl) == BLOCK ? BLOCK_DIE (decl) : lookup_decl_die (decl);
+  gcc_assert (!die);
+  if (!die)
+    {
+      tree ctx;
+      dw_die_ref parent = NULL;
+      /* Need to lookup a DIE for the decls context - the containing
+         function or translation unit.  */
+      if (TREE_CODE (decl) == BLOCK)
+	{
+	  ctx = BLOCK_SUPERCONTEXT (decl);
+	  /* ???  We do not output DIEs for all scopes thus skip as
+	     many DIEs as needed.  */
+	  while (TREE_CODE (ctx) == BLOCK
+		 && !BLOCK_DIE (ctx))
+	    ctx = BLOCK_SUPERCONTEXT (ctx);
+	}
+      else
+	ctx = DECL_CONTEXT (decl);
+      while (ctx && TYPE_P (ctx))
+	ctx = TYPE_CONTEXT (ctx);
+      if (ctx)
+	{
+	  if (TREE_CODE (ctx) == BLOCK)
+	    parent = BLOCK_DIE (ctx);
+	  else if (TREE_CODE (ctx) == TRANSLATION_UNIT_DECL
+		   /* Keep the 1:1 association during WPA.  */
+		   && !flag_wpa)
+	    /* Otherwise all late annotations go to the main CU which
+	       imports the original CUs.  */
+	    parent = comp_unit_die ();
+	  else
+	    parent = lookup_decl_die (ctx);
+	}
+      else
+	/* ???  In some cases the C++ FE (at least) fails to
+	   set DECL_CONTEXT properly.  Simply globalize stuff
+	   in this case.  For example
+	   __dso_handle created via iostream line 74 col 25.  */
+	parent = comp_unit_die ();
+      /* Create a DIE "stub".  */
+      switch (TREE_CODE (decl))
+	{
+	case TRANSLATION_UNIT_DECL:
+	  if (flag_wpa)
+	    {
+	      /* Keep the 1:1 association during WPA.  */
+	      die = new_die (DW_TAG_compile_unit, NULL, decl);
+	      break;
+	    }
+	  {
+	    die = comp_unit_die ();
+	    dw_die_ref import = new_die (DW_TAG_imported_unit, die, NULL_TREE);
+	    add_AT_external_die_ref (import, DW_AT_import, sym, off);
+	    /* We re-target all CU decls to the LTRANS CU DIE, so no need
+	       to create a DIE for the original CUs.  */
+	    return;
+	  }
+	case NAMESPACE_DECL:
+	  /* ???  LANG issue - DW_TAG_module for fortran.  Either look
+	     at the input language (if we have enough DECL_CONTEXT to follow)
+	     or use a bit in tree_decl_with_vis to record the distinction.  */
+	  die = new_die (DW_TAG_namespace, parent, decl);
+	  break;
+	case FUNCTION_DECL:
+	  die = new_die (DW_TAG_subprogram, parent, decl);
+	  break;
+	case VAR_DECL:
+	case RESULT_DECL:
+	  die = new_die (DW_TAG_variable, parent, decl);
+	  break;
+	case PARM_DECL:
+	  die = new_die (DW_TAG_formal_parameter, parent, decl);
+	  break;
+	case CONST_DECL:
+	  die = new_die (DW_TAG_constant, parent, decl);
+	  break;
+	case LABEL_DECL:
+	  die = new_die (DW_TAG_label, parent, decl);
+	  break;
+	case BLOCK:
+	  die = new_die (DW_TAG_lexical_block, parent, decl);
+	  break;
+	default:
+	  gcc_unreachable ();
+	}
+      if (TREE_CODE (decl) == BLOCK)
+	BLOCK_DIE (decl) = die;
+      else
+	equate_decl_number_to_die (decl, die);
+    }
+
+  /* Add a reference to the DIE providing early debug at $sym + off.  */
+  add_AT_external_die_ref (die, DW_AT_abstract_origin, sym, off);
 }
 
 /* Returns a hash value for X (which really is a var_loc_list).  */
@@ -5645,7 +5855,11 @@ print_dw_val (dw_val_node *val, bool rec
 			       die->die_id.die_type_node->signature);
 	    }
 	  else if (die->die_id.die_symbol)
-	    fprintf (outfile, "die -> label: %s", die->die_id.die_symbol);
+	    {
+	      fprintf (outfile, "die -> label: %s", die->die_id.die_symbol);
+	      if (die->with_offset)
+		fprintf (outfile, " + %ld", die->die_offset);
+	    }
 	  else
 	    fprintf (outfile, "die -> %ld", die->die_offset);
 	  fprintf (outfile, " (%p)", (void *) die);
@@ -5838,6 +6052,39 @@ debug_dwarf (void)
   print_die (comp_unit_die (), stderr);
 }
 
+DEBUG_FUNCTION void
+verify_die (dw_die_ref die)
+{
+  gcc_assert (!die->die_mark);
+  if (die->die_parent == NULL
+      && die->die_sib == NULL)
+    return;
+  /* Verify the die_sib list is cyclic.  */
+  dw_die_ref x = die;
+  do
+    {
+      x->die_mark = 1;
+      x = x->die_sib;
+    }
+  while (x && !x->die_mark);
+  gcc_assert (x == die);
+  x = die;
+  do
+    {
+      /* Verify all dies have the same parent.  */
+      gcc_assert (x->die_parent == die->die_parent);
+      if (x->die_child)
+	{
+	  /* Verify the child has the proper parent and recurse.  */
+	  gcc_assert (x->die_child->die_parent == x);
+	  verify_die (x->die_child);
+	}
+      x->die_mark = 0;
+      x = x->die_sib;
+    }
+  while (x && x->die_mark);
+}
+
 /* Sanity checks on DIEs.  */
 
 static void
@@ -6903,7 +7150,7 @@ static unsigned int comdat_symbol_number
    children, and set comdat_symbol_id accordingly.  */
 
 static void
-compute_section_prefix (dw_die_ref unit_die)
+compute_section_prefix_1 (dw_die_ref unit_die, bool comdat_p)
 {
   const char *die_name = get_AT_string (unit_die, DW_AT_name);
   const char *base = die_name ? lbasename (die_name) : "anonymous";
@@ -6922,7 +7169,11 @@ compute_section_prefix (dw_die_ref unit_
   unmark_all_dies (unit_die);
   md5_finish_ctx (&ctx, checksum);
 
-  sprintf (name, "%s.", base);
+  /* ???  Now that we compute this for comp_unit_die () we have a
+     DW_AT_name that might not start with a letter but with anything
+     valid for filenames - clean_symbol_name doesn't fix that up.
+     Thus unconditionally append 'g' for now (can we use isalpha?).  */
+  sprintf (name, "g%s.", base);
   clean_symbol_name (name);
 
   p = name + strlen (name);
@@ -6932,7 +7183,15 @@ compute_section_prefix (dw_die_ref unit_
       p += 2;
     }
 
-  comdat_symbol_id = unit_die->die_id.die_symbol = xstrdup (name);
+  unit_die->die_id.die_symbol = xstrdup (name);
+  unit_die->comdat_type_p = comdat_p;
+}
+
+static void
+compute_section_prefix (dw_die_ref unit_die)
+{
+  compute_section_prefix_1 (unit_die, true);
+  comdat_symbol_id = unit_die->die_id.die_symbol;
   comdat_symbol_number = 0;
 }
 
@@ -9192,7 +9451,11 @@ output_die (dw_die_ref die)
 
   /* If someone in another CU might refer to us, set up a symbol for
      them to point to.  */
-  if (! die->comdat_type_p && die->die_id.die_symbol)
+  if (! die->comdat_type_p && die->die_id.die_symbol
+      /* Don't output the symbol twice.  For LTO we want the label
+         on the section beginning, not on the actual DIE.  */
+      && (!flag_generate_lto
+	  || die->die_tag != DW_TAG_compile_unit))
     output_die_symbol (die);
 
   dw2_asm_output_data_uleb128 (die->die_abbrev, "(DIE (%#lx) %s)",
@@ -9371,8 +9634,20 @@ output_die (dw_die_ref die)
 		    size = DWARF2_ADDR_SIZE;
 		  else
 		    size = DWARF_OFFSET_SIZE;
-		  dw2_asm_output_offset (size, sym, debug_info_section, "%s",
-					 name);
+		  /* ???  We cannot unconditionally output die_offset if
+		     non-zero - at least -feliminate-dwarf2-dups will
+		     create references to those DIEs via symbols.  And we
+		     do not clear its DIE offset after outputting it
+		     (and the label refers to the actual DIEs, not the
+		     DWARF CU unit header which is when using label + offset
+		     would be the correct thing to do).
+		     ???  This is the reason for the with_offset flag.  */
+		  if (AT_ref (a)->with_offset)
+		    dw2_asm_output_offset (size, sym, AT_ref (a)->die_offset,
+					   debug_info_section, "%s", name);
+		  else
+		    dw2_asm_output_offset (size, sym, debug_info_section, "%s",
+					   name);
 		}
 	    }
 	  else
@@ -9558,7 +9833,7 @@ output_comp_unit (dw_die_ref die, int ou
   calc_die_sizes (die);
 
   oldsym = die->die_id.die_symbol;
-  if (oldsym)
+  if (oldsym && die->comdat_type_p)
     {
       tmp = XALLOCAVEC (char, strlen (oldsym) + 24);
 
@@ -9574,6 +9849,33 @@ output_comp_unit (dw_die_ref die, int ou
       info_section_emitted = true;
     }
 
+  /* For LTO cross unit DIE refs we want a symbol on the start of the
+     debuginfo section, not on the CU DIE.
+     ???  We could simply use the symbol as it would be output by output_die
+     and account for the extra offset produced by the CU header which has fixed
+     size.  OTOH it currently only supports linkonce globals which would
+     be less than ideal?.  */
+  if (flag_generate_lto && oldsym)
+    {
+      /* ???  No way to get visibility assembled without a decl.  */
+      tree decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+			      get_identifier (oldsym), char_type_node);
+      TREE_PUBLIC (decl) = true;
+      TREE_STATIC (decl) = true;
+      DECL_ARTIFICIAL (decl) = true;
+      DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN;
+      DECL_VISIBILITY_SPECIFIED (decl) = true;
+      targetm.asm_out.assemble_visibility (decl, VISIBILITY_HIDDEN);
+#ifdef ASM_WEAKEN_LABEL
+      /* We prefer a .weak because that handles duplicates from duplicate
+         archive members in a graceful way.  */
+      ASM_WEAKEN_LABEL (asm_out_file, oldsym);
+#else
+      targetm.asm_out.globalize_label (asm_out_file, oldsym);
+#endif
+      ASM_OUTPUT_LABEL (asm_out_file, oldsym);
+    }
+
   /* Output debugging information.  */
   output_compilation_unit_header ();
   output_die (die);
@@ -17924,12 +18226,18 @@ tree_add_const_value_attribute (dw_die_r
   init = t;
   gcc_assert (!DECL_P (init));
 
-  rtl = rtl_for_decl_init (init, type);
-  if (rtl)
-    return add_const_value_attribute (die, rtl);
+  if (!early_dwarf)
+    {
+      /* For early dwarf we may not create references to possibly optimized
+         out objects as with early LTO resolve_addr will _not_ fix this
+	 up late.  */
+      rtl = rtl_for_decl_init (init, type);
+      if (rtl)
+	return add_const_value_attribute (die, rtl);
+    }
   /* If the host and target are sane, try harder.  */
-  else if (CHAR_BIT == 8 && BITS_PER_UNIT == 8
-	   && initializer_constant_valid_p (init, type))
+  if (CHAR_BIT == 8 && BITS_PER_UNIT == 8
+      && initializer_constant_valid_p (init, type))
     {
       HOST_WIDE_INT size = int_size_in_bytes (TREE_TYPE (init));
       if (size > 0 && (int) size == size)
@@ -18310,6 +18618,31 @@ add_scalar_info (dw_die_ref die, enum dw
       if (decl != NULL_TREE)
 	{
 	  dw_die_ref decl_die = lookup_decl_die (decl);
+	  /* ???  Force a DIE for the decl - this is probably
+	     artificially generated by gimplifying type sizes and thus
+	     early debug wouldn't have created a DIE for it because
+	     the decl is not associated with any block.  */
+	  /* ???  This doesn't work as type sizes are not always gimplified
+	     like for
+	       void fred () { int n = 10; double (*x)[n]; }
+	     and thus we fail to clear DECL_IGNORED_P for those.  But
+	     we don't have debug-info for the above case anyway.  */
+	  /* ???  When we generate early debug for
+	     gfortran.dg/deferred_type_component_2.f90 then we ICE here
+	     as we refer to a later artificial parameter when generating
+	     the DIE for a parameter of a function.  See below.  Thus really
+	     restrict to VAR_DECLs for now (force_decl_die doesn't handle
+	     PARM_DECLs).  Restructuring to create all parm DIEs in a bare
+	     form early sounds like the solution.  */
+	  /* ???  Having VLA bound DIEs created early (via the following
+	     or process_vla_type) causes vla-2.c to FAIL because of
+	     swo#20536.  If we do not enter the DIE ref here we later
+	     can fill in a literal location list instead.  */
+	  if (! decl_die
+	      && ! DECL_IGNORED_P (decl)
+	      && DECL_ARTIFICIAL (decl)
+	      && TREE_CODE (decl) == VAR_DECL)
+	    decl_die = force_decl_die (decl);
 
 	  /* ??? Can this happen, or should the variable have been bound
 	     first?  Probably it can, since I imagine that we try to create
@@ -18736,7 +19069,20 @@ add_abstract_origin_attribute (dw_die_re
     }
 
   if (DECL_P (origin))
-    origin_die = lookup_decl_die (origin);
+    {
+      dw_die_ref c;
+      origin_die = lookup_decl_die (origin);
+      /* "Unwrap" the decls DIE which we put in the imported unit context.
+          ???  If we finish dwarf2out_function_decl refactoring we can
+	  do this in a better way from the start and only lazily emit
+	  the early DIE references.  */
+      if (in_lto_p
+	  && origin_die
+	  && (c = get_AT_ref (origin_die, DW_AT_abstract_origin))
+	  /* ???  Identify this better.  */
+	  && c->with_offset)
+	origin_die = c;
+    }
   else if (TYPE_P (origin))
     origin_die = lookup_type_die (origin);
   else if (TREE_CODE (origin) == BLOCK)
@@ -19291,7 +19637,12 @@ gen_array_type_die (tree type, dw_die_re
       size = int_size_in_bytes (type);
       if (size >= 0)
 	add_AT_unsigned (array_die, DW_AT_byte_size, size);
-      else if (TYPE_DOMAIN (type) != NULL_TREE
+      /* ???  We can't annotate types late, but for LTO we may not
+	 generate a location early either (gfortran.dg/save_5.f90).
+	 The proper way is to handle it like VLAs though it is told
+	 that DW_AT_string_length does not support this.  */
+      else if (! (early_dwarf && flag_generate_lto)
+	       && TYPE_DOMAIN (type) != NULL_TREE
 	       && TYPE_MAX_VALUE (TYPE_DOMAIN (type)) != NULL_TREE)
 	{
 	  tree szdecl = TYPE_MAX_VALUE (TYPE_DOMAIN (type));
@@ -19746,7 +20097,9 @@ gen_formal_parameter_die (tree node, tre
 	 thing.  */
       if (parm_die && parm_die->die_parent != context_die)
 	{
-	  if (!DECL_ABSTRACT_P (node))
+	  /* ???  The DIE parent is the "abstract" copy and the context_die
+	     is the specification "copy".  */
+	  if (!DECL_ABSTRACT_P (node) && !in_lto_p)
 	    {
 	      /* This can happen when creating an inlined instance, in
 		 which case we need to create a new DIE that will get
@@ -20020,7 +20373,6 @@ gen_type_die_for_member (tree type, tree
 /* Forward declare these functions, because they are mutually recursive
   with their set_block_* pairing functions.  */
 static void set_decl_origin_self (tree);
-static void set_decl_abstract_flags (tree, vec<tree> &);
 
 /* Given a pointer to some BLOCK node, if the BLOCK_ABSTRACT_ORIGIN for the
    given BLOCK node is NULL, set the BLOCK_ABSTRACT_ORIGIN for the node so
@@ -20093,151 +20445,45 @@ set_decl_origin_self (tree decl)
     }
 }
 
-/* Given a pointer to some BLOCK node, set the BLOCK_ABSTRACT flag to 1
-   and if it wasn't 1 before, push it to abstract_vec vector.
-   For all local decls and all local sub-blocks (recursively) do it
-   too.  */
-
-static void
-set_block_abstract_flags (tree stmt, vec<tree> &abstract_vec)
-{
-  tree local_decl;
-  tree subblock;
-  unsigned int i;
-
-  if (!BLOCK_ABSTRACT (stmt))
-    {
-      abstract_vec.safe_push (stmt);
-      BLOCK_ABSTRACT (stmt) = 1;
-    }
-
-  for (local_decl = BLOCK_VARS (stmt);
-       local_decl != NULL_TREE;
-       local_decl = DECL_CHAIN (local_decl))
-    if (! DECL_EXTERNAL (local_decl))
-      set_decl_abstract_flags (local_decl, abstract_vec);
-
-  for (i = 0; i < BLOCK_NUM_NONLOCALIZED_VARS (stmt); i++)
-    {
-      local_decl = BLOCK_NONLOCALIZED_VAR (stmt, i);
-      if ((TREE_CODE (local_decl) == VAR_DECL && !TREE_STATIC (local_decl))
-	  || TREE_CODE (local_decl) == PARM_DECL)
-	set_decl_abstract_flags (local_decl, abstract_vec);
-    }
-
-  for (subblock = BLOCK_SUBBLOCKS (stmt);
-       subblock != NULL_TREE;
-       subblock = BLOCK_CHAIN (subblock))
-    set_block_abstract_flags (subblock, abstract_vec);
-}
-
-/* Given a pointer to some ..._DECL node, set DECL_ABSTRACT_P flag on it
-   to 1 and if it wasn't 1 before, push to abstract_vec vector.
-   In the case where the decl is a FUNCTION_DECL also set the abstract
-   flags for all of the parameters, local vars, local
-   blocks and sub-blocks (recursively).  */
-
-static void
-set_decl_abstract_flags (tree decl, vec<tree> &abstract_vec)
-{
-  if (!DECL_ABSTRACT_P (decl))
-    {
-      abstract_vec.safe_push (decl);
-      DECL_ABSTRACT_P (decl) = 1;
-    }
-
-  if (TREE_CODE (decl) == FUNCTION_DECL)
-    {
-      tree arg;
-
-      for (arg = DECL_ARGUMENTS (decl); arg; arg = DECL_CHAIN (arg))
-	if (!DECL_ABSTRACT_P (arg))
-	  {
-	    abstract_vec.safe_push (arg);
-	    DECL_ABSTRACT_P (arg) = 1;
-	  }
-      if (DECL_INITIAL (decl) != NULL_TREE
-	  && DECL_INITIAL (decl) != error_mark_node)
-	set_block_abstract_flags (DECL_INITIAL (decl), abstract_vec);
-    }
-}
-
-/* Generate the DWARF2 info for the "abstract" instance of a function which we
-   may later generate inlined and/or out-of-line instances of.
-
-   FIXME: In the early-dwarf world, this function, and most of the
-          DECL_ABSTRACT code should be obsoleted.  The early DIE _is_
-          the abstract instance.  All we would need to do is annotate
-          the early DIE with the appropriate DW_AT_inline in late
-          dwarf (perhaps in gen_inlined_subroutine_die).
-
-	  However, we can't do this yet, because LTO streaming of DIEs
-	  has not been implemented yet.  */
+/* Mark the early DIE for DECL as the abstract instance.  */
 
 static void
 dwarf2out_abstract_function (tree decl)
 {
   dw_die_ref old_die;
-  tree save_fn;
-  tree context;
-  hash_table<decl_loc_hasher> *old_decl_loc_table;
-  hash_table<dw_loc_list_hasher> *old_cached_dw_loc_list_table;
-  int old_call_site_count, old_tail_call_site_count;
-  struct call_arg_loc_node *old_call_arg_locations;
 
   /* Make sure we have the actual abstract inline, not a clone.  */
   decl = DECL_ORIGIN (decl);
 
+  if (DECL_IGNORED_P (decl))
+    return;
+
   old_die = lookup_decl_die (decl);
-  if (old_die && get_AT (old_die, DW_AT_inline))
+  /* With early debug we always have an old DIE.  */
+  gcc_assert (old_die != NULL);
+  if (get_AT (old_die, DW_AT_inline))
     /* We've already generated the abstract instance.  */
     return;
 
-  /* We can be called while recursively when seeing block defining inlined subroutine
-     DIE.  Be sure to not clobber the outer location table nor use it or we would
-     get locations in abstract instantces.  */
-  old_decl_loc_table = decl_loc_table;
-  decl_loc_table = NULL;
-  old_cached_dw_loc_list_table = cached_dw_loc_list_table;
-  cached_dw_loc_list_table = NULL;
-  old_call_arg_locations = call_arg_locations;
-  call_arg_locations = NULL;
-  old_call_site_count = call_site_count;
-  call_site_count = -1;
-  old_tail_call_site_count = tail_call_site_count;
-  tail_call_site_count = -1;
-
-  /* Be sure we've emitted the in-class declaration DIE (if any) first, so
-     we don't get confused by DECL_ABSTRACT_P.  */
-  if (debug_info_level > DINFO_LEVEL_TERSE)
+  /* Go ahead and put DW_AT_inline on the DIE.  */
+  if (DECL_DECLARED_INLINE_P (decl))
     {
-      context = decl_class_context (decl);
-      if (context)
-	gen_type_die_for_member
-	  (context, decl, decl_function_context (decl) ? NULL : comp_unit_die ());
+      if (cgraph_function_possibly_inlined_p (decl))
+	add_AT_unsigned (old_die, DW_AT_inline, DW_INL_declared_inlined);
+      else
+	add_AT_unsigned (old_die, DW_AT_inline, DW_INL_declared_not_inlined);
+    }
+  else
+    {
+      if (cgraph_function_possibly_inlined_p (decl))
+	add_AT_unsigned (old_die, DW_AT_inline, DW_INL_inlined);
+      else
+	add_AT_unsigned (old_die, DW_AT_inline, DW_INL_not_inlined);
     }
 
-  /* Pretend we've just finished compiling this function.  */
-  save_fn = current_function_decl;
-  current_function_decl = decl;
-
-  auto_vec<tree, 64> abstract_vec;
-  set_decl_abstract_flags (decl, abstract_vec);
-  dwarf2out_decl (decl);
-  unsigned int i;
-  tree t;
-  FOR_EACH_VEC_ELT (abstract_vec, i, t)
-    if (TREE_CODE (t) == BLOCK)
-      BLOCK_ABSTRACT (t) = 0;
-    else
-      DECL_ABSTRACT_P (t) = 0;
-
-  current_function_decl = save_fn;
-  decl_loc_table = old_decl_loc_table;
-  cached_dw_loc_list_table = old_cached_dw_loc_list_table;
-  call_arg_locations = old_call_arg_locations;
-  call_site_count = old_call_site_count;
-  tail_call_site_count = old_tail_call_site_count;
+  if (DECL_DECLARED_INLINE_P (decl)
+      && lookup_attribute ("artificial", DECL_ATTRIBUTES (decl)))
+    add_AT_flag (old_die, DW_AT_artificial, 1);
 }
 
 /* Helper function of premark_used_types() which gets called through
@@ -20460,7 +20706,12 @@ gen_subprogram_die (tree decl, dw_die_re
       if (old_die && old_die->die_parent == NULL)
 	add_child_die (context_die, old_die);
 
-      if (old_die && get_AT_ref (old_die, DW_AT_abstract_origin))
+      dw_die_ref c;
+      if (old_die
+	  && (c = get_AT_ref (old_die, DW_AT_abstract_origin))
+	  /* ???  In LTO all origin DIEs still refer to the early
+	     debug copy.  Detect that.  */
+	  && get_AT (c, DW_AT_inline))
 	{
 	  /* If we have a DW_AT_abstract_origin we have a working
 	     cached version.  */
@@ -20484,6 +20735,9 @@ gen_subprogram_die (tree decl, dw_die_re
     {
       /* A declaration that has been previously dumped needs no
 	 additional information.  */
+      /* ???  In LTO an existing DIE always means an early generated DIE.
+         We should really refactor the entry points to dwarf generation
+	 so we have the separate stages completely separated.  */
       if (declaration)
 	return;
 
@@ -20528,7 +20782,9 @@ gen_subprogram_die (tree decl, dw_die_re
 	   || (old_die->die_parent
 	       && old_die->die_parent->die_tag == DW_TAG_module)
 	   || context_die == NULL)
-	   && (DECL_ARTIFICIAL (decl)
+	  /* ???  This all (and above) should probably be simply
+	     a ! early_dwarf check somehow.  */
+	   && ((DECL_ARTIFICIAL (decl) || in_lto_p)
 	       || (get_AT_file (old_die, DW_AT_decl_file) == file_index
 		   && (get_AT_unsigned (old_die, DW_AT_decl_line)
 		       == (unsigned) s.line))))
@@ -21236,13 +21492,18 @@ gen_variable_die (tree decl, tree origin
   if (com_decl)
     {
       dw_die_ref com_die;
-      dw_loc_list_ref loc;
+      dw_loc_list_ref loc = NULL;
       die_node com_die_arg;
 
       var_die = lookup_decl_die (decl_or_origin);
       if (var_die)
 	{
-	  if (get_AT (var_die, DW_AT_location) == NULL)
+	  /* ???  gfortran.dg/lto/pr46911_0.f shows we generate relocs
+	     to the symbol early while it might be optimized out later.
+	     The testcase also shows that our lame objcopy based
+	     early debug linking fails when relocs to undefined symbols
+	     are involved.  */
+	  if (! early_dwarf && get_AT (var_die, DW_AT_location) == NULL)
 	    {
 	      loc = loc_list_from_tree (com_decl, off ? 1 : 2, NULL);
 	      if (loc)
@@ -21276,7 +21537,9 @@ gen_variable_die (tree decl, tree origin
       com_die_arg.decl_id = DECL_UID (com_decl);
       com_die_arg.die_parent = context_die;
       com_die = common_block_die_table->find (&com_die_arg);
-      loc = loc_list_from_tree (com_decl, 2, NULL);
+      /* ???  See above.  */
+      if (! early_dwarf)
+	loc = loc_list_from_tree (com_decl, 2, NULL);
       if (com_die == NULL)
 	{
 	  const char *cnam
@@ -21367,6 +21630,24 @@ gen_variable_die (tree decl, tree origin
 	  /* If a DIE was dumped early, it still needs location info.
 	     Skip to where we fill the location bits.  */
 	  var_die = old_die;
+
+	  /* ???  In LTRANS we cannot annotate early created variably
+	     modified type DIEs without copying them and adjusting all
+	     references to them.  Thus we dumped them again, also add a
+	     reference to them.  */
+	  tree type = TREE_TYPE (decl_or_origin);
+	  if (in_lto_p
+	      && variably_modified_type_p
+		   (type, decl_function_context (decl_or_origin)))
+	    {
+	      if (decl_by_reference_p (decl_or_origin))
+		add_type_attribute (var_die, TREE_TYPE (type),
+				    TYPE_UNQUALIFIED, false, context_die);
+	      else
+		add_type_attribute (var_die, type, decl_quals (decl_or_origin),
+				    false, context_die);
+	    }
+
 	  goto gen_variable_die_location;
 	}
     }
@@ -21462,7 +21743,26 @@ gen_variable_die (tree decl, tree origin
       && (origin_die == NULL || get_AT (origin_die, DW_AT_location) == NULL))
     {
       if (early_dwarf)
-	add_pubname (decl_or_origin, var_die);
+	{
+	  add_pubname (decl_or_origin, var_die);
+	  /* We use the early dwarf of a function as abstract instance,
+	     so make sure to add DW_AT_const_value attributes to it.  */
+	  /* ???  This can end up adding a 4MB DW_AT_const_value even
+	     if we later output the initializer into .data.  Now such
+	     a DW_AT_const_value is wasteful even for optimized out
+	     decls, so the following is fine but we really have to limit
+	     the size of DW_AT_const_values we output (a DW_AT_constant_value
+	     might be even smaller than the DW_OP_addr we generate to
+	     point to the in-memory location).  */
+	  int sz;
+	  if (DECL_INITIAL (decl_or_origin)
+	      && (sz = int_size_in_bytes
+		         (TREE_TYPE (DECL_INITIAL (decl_or_origin)))) != -1
+	      /* So for now allow DW_AT_const_value of size less or equal to
+	         that what is required for the reference to emitted data.  */
+	      && sz <= (PTR_SIZE + 2))
+	    tree_add_const_value_attribute_for_decl (var_die, decl_or_origin);
+	}
       else
 	add_location_or_const_value_attribute (var_die, decl_or_origin,
 					       decl == NULL);
@@ -21517,7 +21817,7 @@ gen_label_die (tree decl, dw_die_ref con
 
   if (DECL_ABSTRACT_P (decl))
     equate_decl_number_to_die (decl, lbl_die);
-  else
+  else if (! early_dwarf)
     {
       insn = DECL_RTL_IF_SET (decl);
 
@@ -21697,6 +21997,20 @@ gen_lexical_block_die (tree stmt, dw_die
     {
       /* If this is an inlined instance, create a new lexical die for
 	 anything below to attach DW_AT_abstract_origin to.  */
+      /* ???  Between early and late dwarf we get confused here
+	 by set_block_origin_self () called by dwarf2out_function_decl
+	 via gen_decl_die
+
+	   If we're emitting an out-of-line copy of an inline function,
+	   emit info for the abstract instance and set up to refer to it.
+
+	 which ends up calling dwarf2out_abstract_function (late)
+	 and then set_decl_origin_self.  This should have been discovered
+	 early already.  Or rather we do have the abstract instance
+	 output early already, no need to do that again.  So
+	 dwarf2out_abstract_function should take the early output
+	 DIEs and create a duplicate that just refers back to the
+	 "abstract" early instance?  */
       if (old_die)
 	{
 	  stmt_die = new_die (DW_TAG_lexical_block, context_die, stmt);
@@ -21741,12 +22055,6 @@ gen_inlined_subroutine_die (tree stmt, d
   gcc_checking_assert (DECL_ABSTRACT_P (decl)
 		       || cgraph_function_possibly_inlined_p (decl));
 
-  /* Emit info for the abstract instance first, if we haven't yet.  We
-     must emit this even if the block is abstract, otherwise when we
-     emit the block below (or elsewhere), we may end up trying to emit
-     a die whose origin die hasn't been emitted, and crashing.  */
-  dwarf2out_abstract_function (decl);
-
   if (! BLOCK_ABSTRACT (stmt))
     {
       dw_die_ref subr_die
@@ -23236,6 +23544,33 @@ gen_block_die (tree stmt, dw_die_ref con
     decls_for_scope (stmt, context_die);
 }
 
+static tree
+process_vla_type (tree *tp, int *walk_subtrees, void *ctx)
+{
+  /* ???  walk_type_fields doesn't walk TYPE_SIZE and friends and
+     while it walks TYPE_DOMAIN for arrays it doesn't walk
+     TYPE_MIN/MAX_VALUE.  Just special-case the ARRAY_TYPE domain
+     type case here for now.  */
+  if (TREE_CODE (*tp) == INTEGER_TYPE)
+    {
+      if (TYPE_MIN_VALUE (*tp)
+	  && TREE_CODE (TYPE_MIN_VALUE (*tp)) == VAR_DECL
+	  && DECL_ARTIFICIAL (TYPE_MIN_VALUE (*tp))
+	  && !DECL_IGNORED_P (TYPE_MIN_VALUE (*tp)))
+	gen_decl_die (TYPE_MIN_VALUE (*tp), NULL_TREE, NULL, (dw_die_ref) ctx);
+      if (TYPE_MAX_VALUE (*tp)
+	  && TREE_CODE (TYPE_MAX_VALUE (*tp)) == VAR_DECL
+	  && DECL_ARTIFICIAL (TYPE_MAX_VALUE (*tp))
+	  && !DECL_IGNORED_P (TYPE_MAX_VALUE (*tp)))
+	gen_decl_die (TYPE_MAX_VALUE (*tp), NULL_TREE, NULL, (dw_die_ref) ctx);
+    }
+
+  if (!TYPE_P (*tp))
+    *walk_subtrees = 0;
+
+  return NULL_TREE;
+}
+
 /* Process variable DECL (or variable with origin ORIGIN) within
    block STMT and add it to CONTEXT_DIE.  */
 static void
@@ -23248,7 +23583,24 @@ process_scope_var (tree stmt, tree decl,
     die = lookup_decl_die (decl_or_origin);
   else if (TREE_CODE (decl_or_origin) == TYPE_DECL
            && TYPE_DECL_IS_STUB (decl_or_origin))
-    die = lookup_type_die (TREE_TYPE (decl_or_origin));
+    {
+      /* ???  Ada has those in BLOCK scope but with types that have
+	 the a scope of other types (that's already odd).  OTOH for
+	 these decls process_scope_var isn't the correct place to
+	 generate a DIE for the type (we won't generate one for the
+	 decl).  Instead it looks like we may have this code only
+	 to eventually set the context of a limbo type DIE already
+	 created?!  */
+      /* ???  Note the issue is we try to re-create the DIE late
+         if it was optimized as unused early (this BLOCK reference
+	 does not count as "use").  So refactoring dwarf2out_function_decl
+	 more to only include late parts should "fix" this as well
+	 (we'd simply never call gen_decl_die there but only its
+	 annotation part for existing dies).  */
+      die = lookup_type_die (TREE_TYPE (decl_or_origin));
+      if (!die)
+	return;
+    }
   else
     die = NULL;
 
@@ -23261,7 +23613,49 @@ process_scope_var (tree stmt, tree decl,
 					     stmt, context_die);
     }
   else
-    gen_decl_die (decl, origin, NULL, context_die);
+    {
+      if (decl && DECL_P (decl))
+	die = lookup_decl_die (decl);
+
+      if (in_lto_p
+	  && die && die->die_parent != context_die)
+	{
+	  /* ???  For non-LTO operation we do not want to get here via
+	     dwarf2out_abstract_function / set_decl_origin_self which
+	     ends up modifying the tree rep in some odd way instead
+	     of just playing with the DIEs.  */
+	  /* We associate vars with their DECL_CONTEXT first which misses
+	     their BLOCK association.  Move them.  */
+	  gcc_assert (die->die_parent != NULL);
+	  /* ???  Moving is expensive.  Better fix DECL_CONTEXT?  */
+	  /* If we'd be creating these lazily and only generate the
+	     DIE for the external ref at register_external_die time
+	     we could simply insert a die with the proper abstract
+	     origin here...  */
+	  dw_die_ref prev = die->die_parent->die_child;
+	  while (prev->die_sib != die)
+	    prev = prev->die_sib;
+	  remove_child_with_prev (die, prev);
+	  add_child_die (context_die, die);
+	}
+
+      if (decl
+	  && TREE_CODE (decl) == VAR_DECL
+	  && variably_modified_type_p (TREE_TYPE (decl),
+				       cfun ? cfun->decl : NULL_TREE))
+	{
+	  /* We need to add location attributes to decls refered to
+	     from the decls type but we don't have DIEs for the type
+	     itself materialized.  The decls are also not part of the
+	     functions BLOCK tree (because they are artificial).  */
+	  walk_tree (&TREE_TYPE (decl), process_vla_type, context_die, NULL);
+	}
+
+      /* ???  The following gets stray type DIEs created even for decls
+	 that were created early.  */
+
+      gen_decl_die (decl, origin, NULL, context_die);
+    }
 }
 
 /* Generate all of the decls declared within a given scope and (recursively)
@@ -23287,9 +23681,13 @@ decls_for_scope (tree stmt, dw_die_ref c
     {
       for (decl = BLOCK_VARS (stmt); decl != NULL; decl = DECL_CHAIN (decl))
 	process_scope_var (stmt, decl, NULL_TREE, context_die);
-      for (i = 0; i < BLOCK_NUM_NONLOCALIZED_VARS (stmt); i++)
-	process_scope_var (stmt, NULL, BLOCK_NONLOCALIZED_VAR (stmt, i),
-			   context_die);
+      /* BLOCK_NONLOCALIZED_VARs simply generate DIE stubs with abstract
+	 origin - avoid doing this twice as we have no good way to see
+	 if we've done it once already.  */
+      if (! early_dwarf)
+	for (i = 0; i < BLOCK_NUM_NONLOCALIZED_VARS (stmt); i++)
+	  process_scope_var (stmt, NULL, BLOCK_NONLOCALIZED_VAR (stmt, i),
+			     context_die);
     }
 
   /* Even if we're at -g1, we need to process the subblocks in order to get
@@ -23669,6 +24067,9 @@ gen_decl_die (tree decl, tree origin, st
 
       /* If we're emitting an out-of-line copy of an inline function,
 	 emit info for the abstract instance and set up to refer to it.  */
+      /* ???  We have output an abstract instance early already and
+         could just re-use that.  This is how LTO treats all functions
+	 for example.  */
       else if (cgraph_function_possibly_inlined_p (decl)
 	       && ! DECL_ABSTRACT_P (decl)
 	       && ! class_or_namespace_scope_p (context_die)
@@ -23682,7 +24083,9 @@ gen_decl_die (tree decl, tree origin, st
 	}
 
       /* Otherwise we're emitting the primary DIE for this decl.  */
-      else if (debug_info_level > DINFO_LEVEL_TERSE)
+      else if (debug_info_level > DINFO_LEVEL_TERSE
+	       /* Do not generate stray type DIEs in late LTO dumping.  */
+	       && early_dwarf)
 	{
 	  /* Before we describe the FUNCTION_DECL itself, make sure that we
 	     have its containing type.  */
@@ -23749,17 +24152,30 @@ gen_decl_die (tree decl, tree origin, st
       if (debug_info_level <= DINFO_LEVEL_TERSE)
 	break;
 
-      /* Output any DIEs that are needed to specify the type of this data
-	 object.  */
-      if (decl_by_reference_p (decl_or_origin))
-	gen_type_die (TREE_TYPE (TREE_TYPE (decl_or_origin)), context_die);
-      else
-	gen_type_die (TREE_TYPE (decl_or_origin), context_die);
+      /* Avoid generating stray type DIEs during late dwarf dumping.
+         All types have been dumped early.  */
+      if (! (decl ? lookup_decl_die (decl) : NULL)
+	  /* ???  But in LTRANS we cannot annotate early created variably
+	     modified type DIEs without copying them and adjusting all
+	     references to them.  Dump them again as happens for inlining
+	     which copies both the decl and the types.  */
+	  || (in_lto_p
+	      && decl && variably_modified_type_p (TREE_TYPE (decl),
+						   cfun
+						   ? cfun->decl : NULL_TREE)))
+	{
+	  /* Output any DIEs that are needed to specify the type of this data
+	     object.  */
+	  if (decl_by_reference_p (decl_or_origin))
+	    gen_type_die (TREE_TYPE (TREE_TYPE (decl_or_origin)), context_die);
+	  else
+	    gen_type_die (TREE_TYPE (decl_or_origin), context_die);
 
-      /* And its containing type.  */
-      class_origin = decl_class_context (decl_or_origin);
-      if (class_origin != NULL_TREE)
-	gen_type_die_for_member (class_origin, decl_or_origin, context_die);
+	  /* And its containing type.  */
+	  class_origin = decl_class_context (decl_or_origin);
+	  if (class_origin != NULL_TREE)
+	    gen_type_die_for_member (class_origin, decl_or_origin, context_die);
+	}
 
       /* And its containing namespace.  */
       context_die = declare_in_namespace (decl_or_origin, context_die);
@@ -23862,6 +24278,16 @@ dwarf2out_early_global_decl (tree decl)
 	  if (!DECL_STRUCT_FUNCTION (decl))
 	    goto early_decl_exit;
 
+	  /* Emit an abstract origin of a function first.  This happens
+	     with C++ constructor clones for example and makes
+	     dwarf2out_abstract_function happy which requires the early
+	     DIE of the abstract instance to be present.  */
+	  if (DECL_ABSTRACT_ORIGIN (decl))
+	    {
+	      current_function_decl = DECL_ABSTRACT_ORIGIN (decl);
+	      dwarf2out_decl (DECL_ABSTRACT_ORIGIN (decl));
+	    }
+
 	  current_function_decl = decl;
 	}
       dwarf2out_decl (decl);
@@ -25228,7 +25654,8 @@ output_macinfo_op (macinfo_entry *ref)
     case DW_MACRO_GNU_transparent_include:
       dw2_asm_output_data (1, ref->code, "Transparent include");
       ASM_GENERATE_INTERNAL_LABEL (label,
-				   DEBUG_MACRO_SECTION_LABEL, ref->lineno);
+				   DEBUG_MACRO_SECTION_LABEL "part",
+				   ref->lineno);
       dw2_asm_output_offset (DWARF_OFFSET_SIZE, label, NULL, NULL);
       break;
     default:
@@ -25399,10 +25826,23 @@ save_macinfo_strings (void)
     }
 }
 
+/* Return NAME prefixed with PREFIX if non-NULL in a buffer that is
+   valid until the next call to temp_prefixed_name.  */
+
+static const char *
+temp_prefixed_name (const char *prefix, const char *name)
+{
+  static char buffer[1024];
+  if (!prefix)
+    return name;
+  snprintf (buffer, 1024, "%s%s", prefix ? prefix : "", name);
+  return buffer;
+}
+
 /* Output macinfo section(s).  */
 
 static void
-output_macinfo (void)
+output_macinfo (const char *debug_line_label, const char *secprefix)
 {
   unsigned i;
   unsigned long length = vec_safe_length (macinfo_table);
@@ -25427,9 +25867,7 @@ output_macinfo (void)
 	dw2_asm_output_data (1, 3, "Flags: 64-bit, lineptr present");
       else
 	dw2_asm_output_data (1, 2, "Flags: 32-bit, lineptr present");
-      dw2_asm_output_offset (DWARF_OFFSET_SIZE,
-                             (!dwarf_split_debug_info ? debug_line_section_label
-                              : debug_skeleton_line_section_label),
+      dw2_asm_output_offset (DWARF_OFFSET_SIZE, debug_line_label,
                              debug_line_section, NULL);
     }
 
@@ -25502,12 +25940,14 @@ output_macinfo (void)
 	  tree comdat_key = get_identifier (ref->info);
 	  /* Terminate the previous .debug_macinfo section.  */
 	  dw2_asm_output_data (1, 0, "End compilation unit");
-	  targetm.asm_out.named_section (DEBUG_MACRO_SECTION,
+	  targetm.asm_out.named_section (temp_prefixed_name
+					   (secprefix,
+					    DEBUG_MACRO_SECTION),
 					 SECTION_DEBUG
 					 | SECTION_LINKONCE,
 					 comdat_key);
 	  ASM_GENERATE_INTERNAL_LABEL (label,
-				       DEBUG_MACRO_SECTION_LABEL,
+				       DEBUG_MACRO_SECTION_LABEL "part",
 				       ref->lineno);
 	  ASM_OUTPUT_LABEL (asm_out_file, label);
 	  ref->code = 0;
@@ -25530,135 +25970,179 @@ output_macinfo (void)
       }
 }
 
-/* Set up for Dwarf output at the start of compilation.  */
+/* Initialize the various sections and labels for dwarf output and prefix
+   them with PREFIX if non-NULL.  */
 
 static void
-dwarf2out_init (const char *filename ATTRIBUTE_UNUSED)
+init_sections_and_labels (const char *prefix, unsigned int extra_flags)
 {
-  /* This option is currently broken, see (PR53118 and PR46102).  */
-  if (flag_eliminate_dwarf2_dups
-      && strstr (lang_hooks.name, "C++"))
-    {
-      warning (0, "-feliminate-dwarf2-dups is broken for C++, ignoring");
-      flag_eliminate_dwarf2_dups = 0;
-    }
-
-  /* Allocate the file_table.  */
-  file_table = hash_table<dwarf_file_hasher>::create_ggc (50);
-
-#ifndef DWARF2_LINENO_DEBUGGING_INFO
-  /* Allocate the decl_die_table.  */
-  decl_die_table = hash_table<decl_die_hasher>::create_ggc (10);
-
-  /* Allocate the decl_loc_table.  */
-  decl_loc_table = hash_table<decl_loc_hasher>::create_ggc (10);
-
-  /* Allocate the cached_dw_loc_list_table.  */
-  cached_dw_loc_list_table = hash_table<dw_loc_list_hasher>::create_ggc (10);
-
-  /* Allocate the initial hunk of the decl_scope_table.  */
-  vec_alloc (decl_scope_table, 256);
-
-  /* Allocate the initial hunk of the abbrev_die_table.  */
-  abbrev_die_table = ggc_cleared_vec_alloc<dw_die_ref>
-    (ABBREV_DIE_TABLE_INCREMENT);
-  abbrev_die_table_allocated = ABBREV_DIE_TABLE_INCREMENT;
-  /* Zero-th entry is allocated, but unused.  */
-  abbrev_die_table_in_use = 1;
-
-  /* Allocate the dwarf_proc_stack_usage_map.  */
-  dwarf_proc_stack_usage_map = new hash_map<dw_die_ref, int>;
-
-  /* Allocate the pubtypes and pubnames vectors.  */
-  vec_alloc (pubname_table, 32);
-  vec_alloc (pubtype_table, 32);
-
-  vec_alloc (incomplete_types, 64);
-
-  vec_alloc (used_rtx_array, 32);
+  static unsigned generation = 0;
 
   if (!dwarf_split_debug_info)
     {
-      debug_info_section = get_section (DEBUG_INFO_SECTION,
-                                        SECTION_DEBUG, NULL);
-      debug_abbrev_section = get_section (DEBUG_ABBREV_SECTION,
-                                          SECTION_DEBUG, NULL);
-      debug_loc_section = get_section (DEBUG_LOC_SECTION,
-                                       SECTION_DEBUG, NULL);
+      debug_info_section = get_section (temp_prefixed_name
+					  (prefix, DEBUG_INFO_SECTION),
+                                        SECTION_DEBUG | extra_flags, NULL);
+      debug_abbrev_section = get_section (temp_prefixed_name
+					    (prefix, DEBUG_ABBREV_SECTION),
+                                          SECTION_DEBUG | extra_flags, NULL);
+      debug_loc_section = get_section (temp_prefixed_name
+				         (prefix, DEBUG_LOC_SECTION),
+                                       SECTION_DEBUG | extra_flags, NULL);
     }
   else
     {
-      debug_info_section = get_section (DEBUG_DWO_INFO_SECTION,
+      debug_info_section = get_section (temp_prefixed_name
+					  (prefix, DEBUG_DWO_INFO_SECTION),
                                         SECTION_DEBUG | SECTION_EXCLUDE, NULL);
-      debug_abbrev_section = get_section (DEBUG_DWO_ABBREV_SECTION,
+      debug_abbrev_section = get_section (temp_prefixed_name
+					    (prefix, DEBUG_DWO_ABBREV_SECTION),
                                           SECTION_DEBUG | SECTION_EXCLUDE,
                                           NULL);
-      debug_addr_section = get_section (DEBUG_ADDR_SECTION,
+      debug_addr_section = get_section (temp_prefixed_name
+					  (prefix, DEBUG_ADDR_SECTION),
                                         SECTION_DEBUG, NULL);
-      debug_skeleton_info_section = get_section (DEBUG_INFO_SECTION,
+      debug_skeleton_info_section = get_section (temp_prefixed_name
+						   (prefix, DEBUG_INFO_SECTION),
                                                  SECTION_DEBUG, NULL);
-      debug_skeleton_abbrev_section = get_section (DEBUG_ABBREV_SECTION,
+      debug_skeleton_abbrev_section = get_section (temp_prefixed_name
+						     (prefix,
+						      DEBUG_ABBREV_SECTION),
                                                    SECTION_DEBUG, NULL);
       ASM_GENERATE_INTERNAL_LABEL (debug_skeleton_abbrev_section_label,
-                                  DEBUG_SKELETON_ABBREV_SECTION_LABEL, 0);
+                                  DEBUG_SKELETON_ABBREV_SECTION_LABEL,
+				  generation);
 
-      /* Somewhat confusing detail: The skeleton_[abbrev|info] sections stay in
-         the main .o, but the skeleton_line goes into the split off dwo.  */
-      debug_skeleton_line_section
-          = get_section (DEBUG_DWO_LINE_SECTION,
-                         SECTION_DEBUG | SECTION_EXCLUDE, NULL);
-      ASM_GENERATE_INTERNAL_LABEL (debug_skeleton_line_section_label,
-                                   DEBUG_SKELETON_LINE_SECTION_LABEL, 0);
       debug_str_offsets_section = get_section (DEBUG_STR_OFFSETS_SECTION,
                                                SECTION_DEBUG | SECTION_EXCLUDE,
                                                NULL);
       ASM_GENERATE_INTERNAL_LABEL (debug_skeleton_info_section_label,
-                                   DEBUG_SKELETON_INFO_SECTION_LABEL, 0);
+                                   DEBUG_SKELETON_INFO_SECTION_LABEL,
+				   generation);
       debug_loc_section = get_section (DEBUG_DWO_LOC_SECTION,
                                        SECTION_DEBUG | SECTION_EXCLUDE, NULL);
       debug_str_dwo_section = get_section (DEBUG_STR_DWO_SECTION,
                                            DEBUG_STR_DWO_SECTION_FLAGS, NULL);
     }
-  debug_aranges_section = get_section (DEBUG_ARANGES_SECTION,
-				       SECTION_DEBUG, NULL);
-  debug_macinfo_section = get_section (dwarf_strict
-				       ? DEBUG_MACINFO_SECTION
-				       : DEBUG_MACRO_SECTION,
-                                       DEBUG_MACRO_SECTION_FLAGS, NULL);
-  debug_line_section = get_section (DEBUG_LINE_SECTION,
+  if (dwarf_split_debug_info || flag_generate_lto)
+    {
+      /* Somewhat confusing detail: The skeleton_[abbrev|info] sections stay in
+         the main .o, but the skeleton_line goes into the split off dwo.  */
+      debug_skeleton_line_section
+          = get_section (temp_prefixed_name
+			   (prefix, DEBUG_DWO_LINE_SECTION),
+                         SECTION_DEBUG | SECTION_EXCLUDE, NULL);
+      ASM_GENERATE_INTERNAL_LABEL (debug_skeleton_line_section_label,
+                                   DEBUG_SKELETON_LINE_SECTION_LABEL,
+				   generation);
+    }
+
+  debug_aranges_section = get_section (temp_prefixed_name
+				         (prefix, DEBUG_ARANGES_SECTION),
+				       SECTION_DEBUG, NULL);
+  debug_macinfo_section = get_section (temp_prefixed_name
+				         (prefix,
+					  dwarf_strict
+					  ? DEBUG_MACINFO_SECTION
+					  : DEBUG_MACRO_SECTION),
+                                       DEBUG_MACRO_SECTION_FLAGS
+				       | extra_flags, NULL);
+  debug_line_section = get_section (temp_prefixed_name
+				      (prefix, DEBUG_LINE_SECTION),
 				    SECTION_DEBUG, NULL);
-  debug_pubnames_section = get_section (DEBUG_PUBNAMES_SECTION,
+  debug_pubnames_section = get_section (temp_prefixed_name
+					  (prefix, DEBUG_PUBNAMES_SECTION),
 					SECTION_DEBUG, NULL);
-  debug_pubtypes_section = get_section (DEBUG_PUBTYPES_SECTION,
+  debug_pubtypes_section = get_section (temp_prefixed_name
+					  (prefix, DEBUG_PUBTYPES_SECTION),
 					SECTION_DEBUG, NULL);
-  debug_str_section = get_section (DEBUG_STR_SECTION,
-				   DEBUG_STR_SECTION_FLAGS, NULL);
-  debug_ranges_section = get_section (DEBUG_RANGES_SECTION,
+  debug_str_section = get_section (temp_prefixed_name
+				     (prefix, DEBUG_STR_SECTION),
+				   DEBUG_STR_SECTION_FLAGS | extra_flags,
+				   NULL);
+  debug_ranges_section = get_section (temp_prefixed_name
+				        (prefix, DEBUG_RANGES_SECTION),
 				      SECTION_DEBUG, NULL);
-  debug_frame_section = get_section (DEBUG_FRAME_SECTION,
+  debug_frame_section = get_section (temp_prefixed_name
+				       (prefix, DEBUG_FRAME_SECTION),
 				     SECTION_DEBUG, NULL);
 
-  ASM_GENERATE_INTERNAL_LABEL (text_end_label, TEXT_END_LABEL, 0);
   ASM_GENERATE_INTERNAL_LABEL (abbrev_section_label,
-			       DEBUG_ABBREV_SECTION_LABEL, 0);
-  ASM_GENERATE_INTERNAL_LABEL (text_section_label, TEXT_SECTION_LABEL, 0);
-  ASM_GENERATE_INTERNAL_LABEL (cold_text_section_label,
-			       COLD_TEXT_SECTION_LABEL, 0);
-  ASM_GENERATE_INTERNAL_LABEL (cold_end_label, COLD_END_LABEL, 0);
-
+			       DEBUG_ABBREV_SECTION_LABEL, generation);
   ASM_GENERATE_INTERNAL_LABEL (debug_info_section_label,
-			       DEBUG_INFO_SECTION_LABEL, 0);
+			       DEBUG_INFO_SECTION_LABEL, generation);
+  info_section_emitted = false;
   ASM_GENERATE_INTERNAL_LABEL (debug_line_section_label,
-			       DEBUG_LINE_SECTION_LABEL, 0);
+			       DEBUG_LINE_SECTION_LABEL, generation);
   ASM_GENERATE_INTERNAL_LABEL (ranges_section_label,
-			       DEBUG_RANGES_SECTION_LABEL, 0);
+			       DEBUG_RANGES_SECTION_LABEL, generation);
   ASM_GENERATE_INTERNAL_LABEL (debug_addr_section_label,
-                               DEBUG_ADDR_SECTION_LABEL, 0);
+                               DEBUG_ADDR_SECTION_LABEL, generation);
   ASM_GENERATE_INTERNAL_LABEL (macinfo_section_label,
 			       dwarf_strict
 			       ? DEBUG_MACINFO_SECTION_LABEL
-			       : DEBUG_MACRO_SECTION_LABEL, 0);
-  ASM_GENERATE_INTERNAL_LABEL (loc_section_label, DEBUG_LOC_SECTION_LABEL, 0);
+			       : DEBUG_MACRO_SECTION_LABEL, generation);
+  ASM_GENERATE_INTERNAL_LABEL (loc_section_label, DEBUG_LOC_SECTION_LABEL,
+			       generation);
+
+  ++generation;
+}
+
+/* Set up for Dwarf output at the start of compilation.  */
+
+static void
+dwarf2out_init (const char *filename ATTRIBUTE_UNUSED)
+{
+  /* This option is currently broken, see (PR53118 and PR46102).  */
+  if (flag_eliminate_dwarf2_dups
+      && strstr (lang_hooks.name, "C++"))
+    {
+      warning (0, "-feliminate-dwarf2-dups is broken for C++, ignoring");
+      flag_eliminate_dwarf2_dups = 0;
+    }
+
+  /* Allocate the file_table.  */
+  file_table = hash_table<dwarf_file_hasher>::create_ggc (50);
+
+#ifndef DWARF2_LINENO_DEBUGGING_INFO
+  /* Allocate the decl_die_table.  */
+  decl_die_table = hash_table<decl_die_hasher>::create_ggc (10);
+
+  /* Allocate the decl_loc_table.  */
+  decl_loc_table = hash_table<decl_loc_hasher>::create_ggc (10);
+
+  /* Allocate the cached_dw_loc_list_table.  */
+  cached_dw_loc_list_table = hash_table<dw_loc_list_hasher>::create_ggc (10);
+
+  /* Allocate the initial hunk of the decl_scope_table.  */
+  vec_alloc (decl_scope_table, 256);
+
+  /* Allocate the initial hunk of the abbrev_die_table.  */
+  abbrev_die_table = ggc_cleared_vec_alloc<dw_die_ref>
+    (ABBREV_DIE_TABLE_INCREMENT);
+  abbrev_die_table_allocated = ABBREV_DIE_TABLE_INCREMENT;
+  /* Zero-th entry is allocated, but unused.  */
+  abbrev_die_table_in_use = 1;
+
+  /* Allocate the dwarf_proc_stack_usage_map.  */
+  dwarf_proc_stack_usage_map = new hash_map<dw_die_ref, int>;
+
+  /* Allocate the pubtypes and pubnames vectors.  */
+  vec_alloc (pubname_table, 32);
+  vec_alloc (pubtype_table, 32);
+
+  vec_alloc (incomplete_types, 64);
+
+  vec_alloc (used_rtx_array, 32);
+
+  /* ???  Can't we defer this to dwarf2out_finish ()?  */
+  init_sections_and_labels (NULL, 0);
+
+  ASM_GENERATE_INTERNAL_LABEL (text_end_label, TEXT_END_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (text_section_label, TEXT_SECTION_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (cold_text_section_label,
+			       COLD_TEXT_SECTION_LABEL, 0);
+  ASM_GENERATE_INTERNAL_LABEL (cold_end_label, COLD_END_LABEL, 0);
 
   if (debug_info_level >= DINFO_LEVEL_VERBOSE)
     vec_alloc (macinfo_table, 64);
@@ -26175,6 +26659,16 @@ prune_unused_types_update_strings (dw_di
       }
 }
 
+/* Mark DIE and its children as removed.  */
+
+static void
+mark_removed (dw_die_ref die)
+{
+  dw_die_ref c;
+  die->removed = true;
+  FOR_EACH_CHILD (die, c, mark_removed (c));
+}
+
 /* Remove from the tree DIE any dies that aren't marked.  */
 
 static void
@@ -26190,8 +26684,8 @@ prune_unused_types_prune (dw_die_ref die
 
   c = die->die_child;
   do {
-    dw_die_ref prev = c;
-    for (c = c->die_sib; ! c->die_mark; c = c->die_sib)
+    dw_die_ref prev = c, next;
+    for (c = c->die_sib; ! c->die_mark; c = next)
       if (c == die->die_child)
 	{
 	  /* No marked children between 'prev' and the end of the list.  */
@@ -26203,8 +26697,16 @@ prune_unused_types_prune (dw_die_ref die
 	      prev->die_sib = c->die_sib;
 	      die->die_child = prev;
 	    }
+	  c->die_sib = NULL;
+	  mark_removed (c);
 	  return;
 	}
+      else
+	{
+	  next = c->die_sib;
+	  c->die_sib = NULL;
+	  mark_removed (c);
+	}
 
     if (c != prev->die_sib)
       prev->die_sib = c;
@@ -26449,8 +26951,8 @@ move_marked_base_types (void)
 	  remove_child_with_prev (c, prev);
 	  /* As base types got marked, there must be at least
 	     one node other than DW_TAG_base_type.  */
-	  gcc_assert (c != c->die_sib);
-	  c = c->die_sib;
+	  gcc_assert (die->die_child != NULL);
+	  c = prev->die_sib;
 	}
     }
   while (c != die->die_child);
@@ -27782,50 +28284,93 @@ flush_limbo_die_list (void)
     }
 }
 
+/* Reset DIEs so we can output them again.  */
+
+static void
+reset_dies (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  /* Remove stuff we re-generate.  */
+  die->die_mark = 0;
+  die->die_offset = 0;
+  die->die_abbrev = 0;
+  remove_AT (die, DW_AT_sibling);
+
+  FOR_EACH_CHILD (die, c, reset_dies (c));
+}
+
 /* Output stuff that dwarf requires at the end of every file,
    and generate the DWARF-2 debugging info.  */
 
 static void
-dwarf2out_finish (const char *filename)
+dwarf2out_finish (const char *)
 {
   comdat_type_node *ctnode;
   dw_die_ref main_comp_unit_die;
 
+  /* For LTO early is finished now.  */
+  early_finished = true;
+
   /* Flush out any latecomers to the limbo party.  */
   flush_limbo_die_list ();
 
+  if (flag_checking)
+    {
+      verify_die (comp_unit_die ());
+      limbo_die_node *node;
+      for (node = limbo_die_list; node; node = node->next)
+	verify_die (node->die);
+    }
+
   /* We shouldn't have any symbols with delayed asm names for
      DIEs generated after early finish.  */
   gcc_assert (deferred_asm_name == NULL);
 
-  /* PCH might result in DW_AT_producer string being restored from the
-     header compilation, so always fill it with empty string initially
-     and overwrite only here.  */
-  dw_attr_node *producer = get_AT (comp_unit_die (), DW_AT_producer);
-  producer_string = gen_producer_string ();
-  producer->dw_attr_val.v.val_str->refcount--;
-  producer->dw_attr_val.v.val_str = find_AT_string (producer_string);
-
   gen_remaining_tmpl_value_param_die_attribute ();
 
-  /* Add the name for the main input file now.  We delayed this from
-     dwarf2out_init to avoid complications with PCH.
-     For LTO produced units use a fixed artificial name to avoid
-     leaking tempfile names into the dwarf.  */
-  if (!in_lto_p)
-    add_name_attribute (comp_unit_die (), remap_debug_filename (filename));
-  else
-    add_name_attribute (comp_unit_die (), "<artificial>");
-  if (!IS_ABSOLUTE_PATH (filename) || targetm.force_at_comp_dir)
-    add_comp_dir_attribute (comp_unit_die ());
-  else if (get_AT (comp_unit_die (), DW_AT_comp_dir) == NULL)
+  if (flag_generate_lto)
     {
-      bool p = false;
-      file_table->traverse<bool *, file_table_relative_p> (&p);
-      if (p)
-	add_comp_dir_attribute (comp_unit_die ());
+      gcc_assert (flag_fat_lto_objects);
+
+      /* Switch back to regular section names and labels for the fat object
+	 part.
+	 ???  Do this in dwarf2out_finish ()?  */
+      init_sections_and_labels (NULL, 0);
+
+      /* ???  Prune stuff so that dwarf2out_finish runs successfully
+	 for the fat part of the object.  */
+      if (flag_fat_lto_objects)
+	{
+	  reset_dies (comp_unit_die ());
+	  for (limbo_die_node *node = limbo_die_list; node; node = node->next)
+	    reset_dies (node->die);
+
+	  hash_table<comdat_type_hasher> comdat_type_table (100);
+	  for (ctnode = comdat_type_list; ctnode != NULL; ctnode = ctnode->next)
+	    {
+	      comdat_type_node **slot
+		= comdat_type_table.find_slot (ctnode, INSERT);
+
+	      /* Don't reset types twice.  */
+	      if (*slot != HTAB_EMPTY_ENTRY)
+		continue;
+
+	      /* Add a pointer to the line table for the main compilation unit
+		 so that the debugger can make sense of DW_AT_decl_file
+		 attributes.  */
+	      if (debug_info_level >= DINFO_LEVEL_TERSE)
+		reset_dies (ctnode->root_die);
+
+	      *slot = ctnode;
+	    }
+	}
+
+      /* Reset die CU symbol so we don't output it twice.  */
+      comp_unit_die ()->die_id.die_symbol = NULL;
     }
 
+
 #if ENABLE_ASSERT_CHECKING
   {
     dw_die_ref die = comp_unit_die (), c;
@@ -27833,39 +28378,12 @@ dwarf2out_finish (const char *filename)
   }
 #endif
   resolve_addr (comp_unit_die ());
+  /* ???  The following should be part of early dwarf but currently
+     base-types are marked in resolve_addr (taking advantage of
+     doing this only on the dwarf that remains after resolve_addr
+     which eventually prunes some DIEs).  */
   move_marked_base_types ();
 
-  if (flag_eliminate_unused_debug_types)
-    prune_unused_types ();
-
-  /* Generate separate COMDAT sections for type DIEs. */
-  if (use_debug_types)
-    {
-      break_out_comdat_types (comp_unit_die ());
-
-      /* Each new type_unit DIE was added to the limbo die list when created.
-         Since these have all been added to comdat_type_list, clear the
-         limbo die list.  */
-      limbo_die_list = NULL;
-
-      /* For each new comdat type unit, copy declarations for incomplete
-         types to make the new unit self-contained (i.e., no direct
-         references to the main compile unit).  */
-      for (ctnode = comdat_type_list; ctnode != NULL; ctnode = ctnode->next)
-        copy_decls_for_unworthy_types (ctnode->root_die);
-      copy_decls_for_unworthy_types (comp_unit_die ());
-
-      /* In the process of copying declarations from one unit to another,
-         we may have left some declarations behind that are no longer
-         referenced.  Prune them.  */
-      prune_unused_types ();
-    }
-
-  /* Generate separate CUs for each of the include files we've seen.
-     They will go into limbo_die_list.  */
-  if (flag_eliminate_dwarf2_dups)
-    break_out_includes (comp_unit_die ());
-
   /* Traverse the DIE's and add sibling attributes to those DIE's that
      have children.  */
   add_sibling_attributes (comp_unit_die ());
@@ -28102,7 +28620,8 @@ dwarf2out_finish (const char *filename)
     {
       switch_to_section (debug_macinfo_section);
       ASM_OUTPUT_LABEL (asm_out_file, macinfo_section_label);
-      output_macinfo ();
+      output_macinfo (!dwarf_split_debug_info ? debug_line_section_label
+		      : debug_skeleton_line_section_label, NULL);
       dw2_asm_output_data (1, 0, "End compilation unit");
     }
 
@@ -28133,7 +28652,7 @@ dwarf2out_finish (const char *filename)
    has run.  */
 
 static void
-dwarf2out_early_finish (void)
+dwarf2out_early_finish (const char *filename)
 {
   set_early_dwarf s;
 
@@ -28148,6 +28667,32 @@ dwarf2out_early_finish (void)
   gen_scheduled_generic_parms_dies ();
   gen_remaining_tmpl_value_param_die_attribute ();
 
+  /* PCH might result in DW_AT_producer string being restored from the
+     header compilation, so always fill it with empty string initially
+     and overwrite only here.  */
+  dw_attr_node *producer = get_AT (comp_unit_die (), DW_AT_producer);
+  producer_string = gen_producer_string ();
+  producer->dw_attr_val.v.val_str->refcount--;
+  producer->dw_attr_val.v.val_str = find_AT_string (producer_string);
+
+  /* Add the name for the main input file now.  We delayed this from
+     dwarf2out_init to avoid complications with PCH.
+     For LTO produced units use a fixed artificial name to avoid
+     leaking tempfile names into the dwarf.  */
+  if (!in_lto_p)
+    add_name_attribute (comp_unit_die (), remap_debug_filename (filename));
+  else
+    add_name_attribute (comp_unit_die (), "<artificial>");
+  if (!IS_ABSOLUTE_PATH (filename) || targetm.force_at_comp_dir)
+    add_comp_dir_attribute (comp_unit_die ());
+  else if (get_AT (comp_unit_die (), DW_AT_comp_dir) == NULL)
+    {
+      bool p = false;
+      file_table->traverse<bool *, file_table_relative_p> (&p);
+      if (p)
+	add_comp_dir_attribute (comp_unit_die ());
+    }
+
   /* Add DW_AT_linkage_name for all deferred DIEs.  */
   for (limbo_die_node *node = deferred_asm_name; node; node = node->next)
     {
@@ -28163,6 +28708,163 @@ dwarf2out_early_finish (void)
 	}
     }
   deferred_asm_name = NULL;
+
+  if (flag_eliminate_unused_debug_types)
+    /* ???  Doing prune_unused_types early means we need to restrict
+       pruning somewhat otherwise we for example run into late code
+       generating DIEs refering to callee decl DIEs failing or using
+       less efficient symbol references.
+       One way to improve things is to use the early cgraph we have
+       and mark DIEs of all symbols as used.  */
+    prune_unused_types ();
+
+  /* FIXME debug-early: Prune DIEs for unused decls.  */
+
+  /* Generate separate COMDAT sections for type DIEs. */
+  comdat_type_node *ctnode;
+  if (use_debug_types)
+    {
+      break_out_comdat_types (comp_unit_die ());
+
+      /* Each new type_unit DIE was added to the limbo die list when created.
+         Since these have all been added to comdat_type_list, clear the
+         limbo die list.  */
+      limbo_die_list = NULL;
+
+      /* For each new comdat type unit, copy declarations for incomplete
+         types to make the new unit self-contained (i.e., no direct
+         references to the main compile unit).  */
+      for (ctnode = comdat_type_list; ctnode != NULL; ctnode = ctnode->next)
+        copy_decls_for_unworthy_types (ctnode->root_die);
+      copy_decls_for_unworthy_types (comp_unit_die ());
+
+      /* In the process of copying declarations from one unit to another,
+         we may have left some declarations behind that are no longer
+         referenced.  Prune them.  */
+      prune_unused_types ();
+    }
+
+  /* Generate separate CUs for each of the include files we've seen.
+     They will go into limbo_die_list.  */
+  if (flag_eliminate_dwarf2_dups)
+    break_out_includes (comp_unit_die ());
+
+
+  /* Do not generate DWARF assembler now when not producing LTO bytecode.
+     ???  The following code in principle handles split DWARF producing but
+     it has some issues (and it's likely not desired).  */
+  if (!flag_generate_lto)
+    {
+      early_finished = true;
+      return;
+    }
+
+  /* Now as we are going to output for LTO switch sections and labels
+     to the _gnu.lto prefixed variants.  We don't need a random-seed
+     postfix as other LTO sections as linking the LTO debug sections
+     into one in a partial link is fine.  */
+  /* ???  Can't use .gnu.lto_ as prefix here as gas isn't happy about
+     .gnu.lto_ sections with section flags other than SHF_EXCLUDE and
+     complains with
+       Warning: setting incorrect section attributes for .gnu.lto_.debug_str
+     which is because .gnu.lto_ is in bfds special-section list.
+     We are manually marking sections with SHF_EXCLUDE here so they
+     behave as expected with fat objects and a non-LTO link.  */
+  init_sections_and_labels (".gnu.debuglto_"/*section_name_prefix*/,
+			    SECTION_EXCLUDE);
+
+  /* ???  Duplicated from dwarf2out_finish.  */
+
+  /* Traverse the DIE's and add add sibling attributes to those DIE's
+     that have children.  */
+  add_sibling_attributes (comp_unit_die ());
+  for (limbo_die_node *node = limbo_die_list; node; node = node->next)
+    add_sibling_attributes (node->die);
+  for (ctnode = comdat_type_list; ctnode != NULL; ctnode = ctnode->next)
+    add_sibling_attributes (ctnode->root_die);
+
+  if (have_macinfo)
+    add_AT_macptr (comp_unit_die (),
+		   dwarf_strict ? DW_AT_macro_info : DW_AT_GNU_macros,
+		   macinfo_section_label);
+
+  save_macinfo_strings ();
+
+  /* Output all of the compilation units.  We put the main one last so that
+     the offsets are available to output_pubnames.  */
+  for (limbo_die_node *node = limbo_die_list; node; node = node->next)
+    output_comp_unit (node->die, 0);
+
+  hash_table<comdat_type_hasher> comdat_type_table (100);
+  for (ctnode = comdat_type_list; ctnode != NULL; ctnode = ctnode->next)
+    {
+      comdat_type_node **slot = comdat_type_table.find_slot (ctnode, INSERT);
+
+      /* Don't output duplicate types.  */
+      if (*slot != HTAB_EMPTY_ENTRY)
+        continue;
+
+      /* Add a pointer to the line table for the main compilation unit
+         so that the debugger can make sense of DW_AT_decl_file
+         attributes.  */
+      if (debug_info_level >= DINFO_LEVEL_TERSE)
+        add_AT_lineptr (ctnode->root_die, DW_AT_stmt_list,
+                        (!dwarf_split_debug_info
+                         ? debug_line_section_label
+                         : debug_skeleton_line_section_label));
+
+      output_comdat_type_unit (ctnode);
+      *slot = ctnode;
+    }
+
+  /* The AT_pubnames attribute needs to go in all skeleton dies, including
+     both the main_cu and all skeleton TUs.  Making this call unconditional
+     would end up either adding a second copy of the AT_pubnames attribute, or
+     requiring a special case in add_top_level_skeleton_die_attrs.  */
+  if (!dwarf_split_debug_info)
+    add_AT_pubnames (comp_unit_die ());
+
+  /* Stick a unique symbol to the main debuginfo section.  */
+  compute_section_prefix_1 (comp_unit_die (), false);
+
+  /* Output the main compilation unit.  We always need it if only for
+     the CU symbol.  */
+  output_comp_unit (comp_unit_die (), true);
+
+  /* Output the abbreviation table.  */
+  if (abbrev_die_table_in_use != 1)
+    {
+      switch_to_section (debug_abbrev_section);
+      ASM_OUTPUT_LABEL (asm_out_file, abbrev_section_label);
+      output_abbrev_section ();
+    }
+
+  /* Have to end the macro section.  */
+  if (have_macinfo)
+    {
+      switch_to_section (debug_macinfo_section);
+      ASM_OUTPUT_LABEL (asm_out_file, macinfo_section_label);
+      output_macinfo (debug_skeleton_line_section_label,
+		      ".gnu.debuglto_"/*section_name_prefix*/);
+      dw2_asm_output_data (1, 0, "End compilation unit");
+
+      /* Emit a skeleton debug_line section.  */
+      switch_to_section (debug_skeleton_line_section);
+      ASM_OUTPUT_LABEL (asm_out_file, debug_skeleton_line_section_label);
+      output_line_info (true);
+    }
+
+
+  /* If we emitted any indirect strings, output the string table too.  */
+  if (debug_str_hash || skeleton_debug_str_hash)
+    output_indirect_strings ();
+
+  /* Switch back to the text section.  */
+  switch_to_section (text_section);
+
+  debug_str_hash = NULL;  /* Contents are GCed.  */
+
+  early_finished = true;
 }
 
 /* Reset all state within dwarf2out.c so that we can rerun the compiler
Index: gcc/gimplify.c
===================================================================
--- gcc/gimplify.c	(revision 240004)
+++ gcc/gimplify.c	(working copy)
@@ -11589,6 +11589,9 @@ gimplify_type_sizes (tree type, gimple_s
       /* Ensure VLA bounds aren't removed, for -O0 they should be variables
 	 with assigned stack slots, for -O1+ -g they should be tracked
 	 by VTA.  */
+      /* ???  We need to jump through quite some hoops to handle the fact that
+	 these decls do not appear in any BLOCK_VARs during dwarf2out.c.  Maybe
+	 it's better to add them there in the first place.  */
       if (!(TYPE_NAME (type)
 	    && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL
 	    && DECL_IGNORED_P (TYPE_NAME (type)))
Index: gcc/lto/lto.c
===================================================================
--- gcc/lto/lto.c	(revision 240004)
+++ gcc/lto/lto.c	(working copy)
@@ -1627,6 +1627,9 @@ unify_scc (struct data_in *data_in, unsi
 	      free_node (scc->entries[i]);
 	    }
 
+	  /* Drop DIE references.  */
+	  dref_queue.truncate (0);
+
 	  break;
 	}
 
@@ -1702,8 +1705,7 @@ lto_read_decls (struct lto_file_decl_dat
 						     from);
 	  if (len == 1
 	      && (TREE_CODE (first) == IDENTIFIER_NODE
-		  || TREE_CODE (first) == INTEGER_CST
-		  || TREE_CODE (first) == TRANSLATION_UNIT_DECL))
+		  || TREE_CODE (first) == INTEGER_CST))
 	    continue;
 
 	  /* Try to unify the SCC with already existing ones.  */
@@ -1742,16 +1744,6 @@ lto_read_decls (struct lto_file_decl_dat
 	      if (TREE_CODE (t) == INTEGER_CST
 		  && !TREE_OVERFLOW (t))
 		cache_integer_cst (t);
-	      /* Register TYPE_DECLs with the debuginfo machinery.  */
-	      if (!flag_wpa
-		  && TREE_CODE (t) == TYPE_DECL)
-		{
-		  /* Dwarf2out needs location information.
-		     TODO: Moving this out of the streamer loop may noticealy
-		     improve ltrans linemap memory use.  */
-		  data_in->location_cache.apply_location_cache ();
-		  debug_hooks->type_decl (t, !DECL_FILE_SCOPE_P (t));
-		}
 	      if (!flag_ltrans)
 		{
 		  /* Register variables and functions with the
@@ -1767,6 +1759,14 @@ lto_read_decls (struct lto_file_decl_dat
 		    vec_safe_push (tree_with_vars, t);
 		}
 	    }
+
+	  /* Register DECLs with the debuginfo machinery.  */
+	  while (!dref_queue.is_empty ())
+	    {
+	      dref_entry e = dref_queue.pop ();
+	      debug_hooks->register_external_die (e.decl, e.sym, e.off);
+	    }
+
 	  if (seen_type)
 	    num_type_scc_trees += len;
 	}
@@ -1946,7 +1946,12 @@ lto_section_with_id (const char *name, u
   if (strncmp (name, section_name_prefix, strlen (section_name_prefix)))
     return 0;
   s = strrchr (name, '.');
-  return s && sscanf (s, "." HOST_WIDE_INT_PRINT_HEX_PURE, id) == 1;
+  if (!s)
+    return 0;
+  /* If the section is not suffixed with an ID return.  */
+  if ((size_t)(s - name) == strlen (section_name_prefix))
+    return 0;
+  return sscanf (s, "." HOST_WIDE_INT_PRINT_HEX_PURE, id) == 1;
 }
 
 /* Create file_data of each sub file id */
Index: gcc/lto-streamer-in.c
===================================================================
--- gcc/lto-streamer-in.c	(revision 240004)
+++ gcc/lto-streamer-in.c	(working copy)
@@ -41,6 +41,7 @@ along with GCC; see the file COPYING3.
 #include "except.h"
 #include "cgraph.h"
 #include "cfgloop.h"
+#include "debug.h"
 
 
 struct freeing_string_slot_hasher : string_slot_hasher
@@ -1267,6 +1268,10 @@ lto_input_variable_constructor (struct l
 }
 
 
+/* Queue of acummulated decl -> DIE mappings.  Similar to locations those
+   are only applied to prevailing tree nodes during tree merging.  */
+vec<dref_entry> dref_queue;
+
 /* Read the physical representation of a tree node EXPR from
    input block IB using the per-file context in DATA_IN.  */
 
@@ -1287,6 +1292,23 @@ lto_read_tree_1 (struct lto_input_block
       && TREE_CODE (expr) != TRANSLATION_UNIT_DECL)
     DECL_INITIAL (expr) = stream_read_tree (ib, data_in);
 
+  /* Stream references to early generated DIEs.  Keep in sync with the
+     trees handled in dwarf2out_register_external_die.  */
+  if ((DECL_P (expr)
+       && TREE_CODE (expr) != FIELD_DECL
+       && TREE_CODE (expr) != DEBUG_EXPR_DECL
+       && TREE_CODE (expr) != TYPE_DECL)
+      || TREE_CODE (expr) == BLOCK)
+    {
+      const char *str = streamer_read_string (data_in, ib);
+      if (str)
+	{
+	  unsigned HOST_WIDE_INT off = streamer_read_uhwi (ib);
+	  dref_entry e = { expr, str, off };
+	  dref_queue.safe_push (e);
+	}
+    }
+
 #ifdef LTO_STREAMER_DEBUG
   /* Remove the mapping to RESULT's original address set by
      streamer_alloc_tree.  */
@@ -1437,6 +1459,13 @@ lto_input_tree (struct lto_input_block *
     {
       unsigned len, entry_len;
       lto_input_scc (ib, data_in, &len, &entry_len);
+
+      /* Register DECLs with the debuginfo machinery.  */
+      while (!dref_queue.is_empty ())
+	{
+	  dref_entry e = dref_queue.pop ();
+	  debug_hooks->register_external_die (e.decl, e.sym, e.off);
+	}
     }
   return lto_input_tree_1 (ib, data_in, tag, 0);
 }
Index: gcc/lto-streamer-out.c
===================================================================
--- gcc/lto-streamer-out.c	(revision 240004)
+++ gcc/lto-streamer-out.c	(working copy)
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.
 #include "cfgloop.h"
 #include "builtins.h"
 #include "gomp-constants.h"
+#include "debug.h"
 
 
 static void lto_write_tree (struct output_block*, tree, bool);
@@ -406,6 +407,26 @@ lto_write_tree_1 (struct output_block *o
 			 (ob->decl_state->symtab_node_encoder, expr);
       stream_write_tree (ob, initial, ref_p);
     }
+
+  /* Stream references to early generated DIEs.  Keep in sync with the
+     trees handled in dwarf2out_die_ref_for_decl.  */
+  if ((DECL_P (expr)
+       && TREE_CODE (expr) != FIELD_DECL
+       && TREE_CODE (expr) != DEBUG_EXPR_DECL
+       && TREE_CODE (expr) != TYPE_DECL)
+      || TREE_CODE (expr) == BLOCK)
+    {
+      const char *sym;
+      unsigned HOST_WIDE_INT off;
+      if (debug_info_level > DINFO_LEVEL_NONE
+	  && debug_hooks->die_ref_for_decl (expr, &sym, &off))
+	{
+	  streamer_write_string (ob, ob->main_stream, sym, true);
+	  streamer_write_uhwi (ob, off);
+	}
+      else
+	streamer_write_string (ob, ob->main_stream, NULL, true);
+    }
 }
 
 /* Write a physical representation of tree node EXPR to output block
@@ -765,6 +786,7 @@ DFS::DFS_write_tree_body (struct output_
 	 declarations which should be eliminated by decl merging. Be sure none
 	 leaks to this point.  */
       gcc_assert (DECL_ABSTRACT_ORIGIN (expr) != error_mark_node);
+      DFS_follow_tree_edge (DECL_ABSTRACT_ORIGIN (expr));
 
       if ((TREE_CODE (expr) == VAR_DECL
 	   || TREE_CODE (expr) == PARM_DECL)
@@ -881,6 +903,7 @@ DFS::DFS_write_tree_body (struct output_
 	 in dwarf2out.c, but keep the notion of whether the block
 	 is an inlined block by refering to itself for the sake of
 	 tree_nonartificial_location.  */
+#if 1
       if (inlined_function_outer_scope_p (expr))
 	{
 	  tree ultimate_origin = block_ultimate_origin (expr);
@@ -888,6 +911,9 @@ DFS::DFS_write_tree_body (struct output_
 	}
       else if (BLOCK_ABSTRACT_ORIGIN (expr))
 	DFS_follow_tree_edge (expr);
+#else
+      DFS_follow_tree_edge (BLOCK_ABSTRACT_ORIGIN (expr));
+#endif
       /* Do not follow BLOCK_NONLOCALIZED_VARS.  We cannot handle debug
 	 information for early inlined BLOCKs so drop it on the floor instead
 	 of ICEing in dwarf2out.c.  */
Index: gcc/lto-streamer.h
===================================================================
--- gcc/lto-streamer.h	(revision 240004)
+++ gcc/lto-streamer.h	(working copy)
@@ -1225,4 +1225,14 @@ DEFINE_DECL_STREAM_FUNCS (TYPE_DECL, typ
 DEFINE_DECL_STREAM_FUNCS (NAMESPACE_DECL, namespace_decl)
 DEFINE_DECL_STREAM_FUNCS (LABEL_DECL, label_decl)
 
+/* Entry for the delayed registering of decl -> DIE references.  */
+struct dref_entry {
+    tree decl;
+    const char *sym;
+    unsigned HOST_WIDE_INT off;
+};
+
+extern vec<dref_entry> dref_queue;
+
+
 #endif /* GCC_LTO_STREAMER_H  */
Index: gcc/lto-wrapper.c
===================================================================
--- gcc/lto-wrapper.c	(revision 240004)
+++ gcc/lto-wrapper.c	(working copy)
@@ -70,6 +70,7 @@ static char **output_names;
 static char **offload_names;
 static char *offload_objects_file_name;
 static char *makefile;
+static char *debug_obj;
 
 const char tool_name[] = "lto-wrapper";
 
@@ -88,6 +89,8 @@ tool_cleanup (bool)
     maybe_unlink (offload_objects_file_name);
   if (makefile)
     maybe_unlink (makefile);
+  if (debug_obj)
+    maybe_unlink (debug_obj);
   for (i = 0; i < nr; ++i)
     {
       maybe_unlink (input_names[i]);
@@ -940,6 +943,60 @@ find_and_merge_options (int fd, off_t fi
   return true;
 }
 
+int
+debug_objcopy (const char *infile, const char *outfile)
+{
+  const char *errmsg;
+  int err;
+
+  /* ???  Handle foo.o@xxx.  */
+  const char *p;
+  off_t inoff = 0;
+  long loffset;
+  int consumed;
+  if ((p = strrchr (infile, '@'))
+      && p != infile
+      && sscanf (p, "@%li%n", &loffset, &consumed) >= 1
+      && strlen (p) == (unsigned int) consumed)
+    {
+      char *fname = xstrdup (infile);
+      fname[p - infile] = '\0';
+      infile = fname;
+      inoff = (off_t) loffset;
+    }
+  int infd = open (infile, O_RDONLY);
+  if (infd == -1)
+    return 1;
+  simple_object_read *inobj = simple_object_start_read (infd, inoff,
+							"__GNU_LTO",
+							&errmsg, &err);
+  if (!inobj)
+    return 1;
+
+  off_t off, len;
+  if (simple_object_find_section (inobj, ".gnu.debuglto_.debug_info",
+				  &off, &len, &errmsg, &err) != 1)
+    {
+      if (errmsg)
+	fatal_error (0, "%s: %s\n", errmsg, xstrerror (err));
+
+      simple_object_release_read (inobj);
+      close (infd);
+      return 1;
+    }
+
+  errmsg = simple_object_copy_lto_debug_sections (inobj, outfile, &err);
+  if (errmsg)
+    fatal_error (0, "%s: %s\n", errmsg, xstrerror (err));
+
+  simple_object_release_read (inobj);
+  close (infd);
+
+  return 0;
+}
+
+
+
 /* Execute gcc. ARGC is the number of arguments. ARGV contains the arguments. */
 
 static void
@@ -964,8 +1021,10 @@ run_gcc (unsigned argc, char *argv[])
   int new_head_argc;
   bool have_lto = false;
   bool have_offload = false;
-  unsigned lto_argc = 0;
-  char **lto_argv;
+  unsigned lto_argc = 0, ltoobj_argc = 0;
+  char **lto_argv, **ltoobj_argv;
+  bool skip_debug = false;
+  unsigned n_debugobj;
 
   /* Get the driver and options.  */
   collect_gcc = getenv ("COLLECT_GCC");
@@ -984,6 +1043,7 @@ run_gcc (unsigned argc, char *argv[])
   /* Allocate array for input object files with LTO IL,
      and for possible preceding arguments.  */
   lto_argv = XNEWVEC (char *, argc);
+  ltoobj_argv = XNEWVEC (char *, argc);
 
   /* Look at saved options in the IL files.  */
   for (i = 1; i < argc; ++i)
@@ -1026,7 +1086,7 @@ run_gcc (unsigned argc, char *argv[])
 				  collect_gcc))
 	{
 	  have_lto = true;
-	  lto_argv[lto_argc++] = argv[i];
+	  ltoobj_argv[ltoobj_argc++] = argv[i];
 	}
       close (fd);
     }
@@ -1087,6 +1147,17 @@ run_gcc (unsigned argc, char *argv[])
 	}
     }
 
+  /* Output lto-wrapper invocation command.  */
+  if (verbose)
+    {
+      for (i = 0; i < argc; ++i)
+	{
+	  fputs (argv[i], stderr);
+	  fputc (' ', stderr);
+	}
+      fputc ('\n', stderr);
+    }
+
   if (no_partition)
     {
       lto_mode = LTO_MODE_LTO;
@@ -1276,20 +1347,100 @@ cont1:
         obstack_ptr_grow (&argv_obstack, "-fwpa");
     }
 
-  /* Append the input objects and possible preceding arguments.  */
+  /* Append input arguments.  */
   for (i = 0; i < lto_argc; ++i)
     obstack_ptr_grow (&argv_obstack, lto_argv[i]);
+  /* Append the input objects.  */
+  for (i = 0; i < ltoobj_argc; ++i)
+    obstack_ptr_grow (&argv_obstack, ltoobj_argv[i]);
   obstack_ptr_grow (&argv_obstack, NULL);
 
   new_argv = XOBFINISH (&argv_obstack, const char **);
   argv_ptr = &new_argv[new_head_argc];
   fork_execute (new_argv[0], CONST_CAST (char **, new_argv), true);
 
+  /* Handle early generated debug information.  At compile-time
+     we output early DWARF debug info into .gnu.debuglto_ prefixed
+     sections.  LTRANS object DWARF debug info refers to that.
+     So we need to transfer the .gnu.debuglto_ sections to the final
+     link.  Ideally the linker plugin interface would allow us to
+     not claim those sections and instruct the linker to keep
+     them, renaming them in the process.  For now we extract and
+     rename those sections via a simple-object interface to produce
+     regular objects containing only the early debug info.  We
+     then partially link those to a single early debug info object
+     and pass that as additional output back to the linker plugin.  */
+
+  /* Prepare the partial link to gather the compile-time generated
+     debug-info into a single input for the final link.  */
+  debug_obj = make_temp_file ("debugobj");
+  obstack_ptr_grow (&argv_obstack, collect_gcc);
+  for (i = 1; i < decoded_options_count; ++i)
+    {
+      /* Retain linker choice and -B.  */
+      if (decoded_options[i].opt_index == OPT_B
+	  || decoded_options[i].opt_index == OPT_fuse_ld_bfd
+	  || decoded_options[i].opt_index == OPT_fuse_ld_gold)
+	append_linker_options (&argv_obstack, &decoded_options[i-1], 2);
+      /* Retain all target options, this preserves -m32 for example.  */
+      if (cl_options[decoded_options[i].opt_index].flags & CL_TARGET)
+	append_linker_options (&argv_obstack, &decoded_options[i-1], 2);
+      /* Recognize -g0.  */
+      if (decoded_options[i].opt_index == OPT_g
+	  && strcmp (decoded_options[i].arg, "0") == 0)
+	skip_debug = true;
+    }
+  obstack_ptr_grow (&argv_obstack, "-r");
+  obstack_ptr_grow (&argv_obstack, "-nostdlib");
+  obstack_ptr_grow (&argv_obstack, "-o");
+  obstack_ptr_grow (&argv_obstack, debug_obj);
+
+  /* Copy the early generated debug info from the objects to temporary
+     files and append those to the partial link commandline.  */
+  n_debugobj = 0;
+  if (! skip_debug)
+    for (i = 0; i < ltoobj_argc; ++i)
+      {
+	char *tem = make_temp_file ("debugobjtem");
+	if (!debug_objcopy (ltoobj_argv[i], tem))
+	  {
+	    obstack_ptr_grow (&argv_obstack, tem);
+	    n_debugobj++;
+	  }
+      }
+
+  /* Link them all into a single object.  Ideally this would reduce
+     disk space usage mainly due to .debug_str merging but unfortunately
+     GNU ld doesn't perform this with -r.  */
+  if (n_debugobj)
+    {
+      obstack_ptr_grow (&argv_obstack, NULL);
+      const char **debug_link_argv = XOBFINISH (&argv_obstack, const char **);
+      fork_execute (debug_link_argv[0],
+		    CONST_CAST (char **, debug_link_argv), false);
+
+      /* And dispose the temporaries.  */
+      for (i = 0; debug_link_argv[i]; ++i)
+	;
+      for (--i; i > 0; --i)
+	{
+	  if (strcmp (debug_link_argv[i], debug_obj) == 0)
+	    break;
+	  maybe_unlink (debug_link_argv[i]);
+	}
+    }
+  else
+    skip_debug = true;
+
   if (lto_mode == LTO_MODE_LTO)
     {
       printf ("%s\n", flto_out);
+      if (!skip_debug)
+	printf ("%s\n", debug_obj);
       free (flto_out);
       flto_out = NULL;
+      free (debug_obj);
+      debug_obj = NULL;
     }
   else
     {
@@ -1436,6 +1587,10 @@ cont:
 	  for (i = 0; i < nr; ++i)
 	    maybe_unlink (input_names[i]);
 	}
+      if (!skip_debug)
+	printf ("%s\n", debug_obj);
+      free (debug_obj);
+      debug_obj = NULL;
       for (i = 0; i < nr; ++i)
 	{
 	  fputs (output_names[i], stdout);
Index: gcc/sdbout.c
===================================================================
--- gcc/sdbout.c	(revision 240004)
+++ gcc/sdbout.c	(working copy)
@@ -277,7 +277,7 @@ const struct gcc_debug_hooks sdb_debug_h
 {
   sdbout_init,			         /* init */
   sdbout_finish,		         /* finish */
-  debug_nothing_void,			 /* early_finish */
+  debug_nothing_charstar,		 /* early_finish */
   debug_nothing_void,			 /* assembly_start */
   debug_nothing_int_charstar,	         /* define */
   debug_nothing_int_charstar,	         /* undef */
@@ -299,6 +299,8 @@ const struct gcc_debug_hooks sdb_debug_h
   sdbout_late_global_decl,		 /* late_global_decl */
   sdbout_symbol,			 /* type_decl */
   debug_nothing_tree_tree_tree_bool,	 /* imported_module_or_decl */
+  debug_false_tree_charstarstar_uhwistar, /* die_ref_for_decl */
+  debug_nothing_tree_charstar_uhwi,	 /* register_external_die */
   debug_nothing_tree,		         /* deferred_inline_function */
   debug_nothing_tree,		         /* outlining_inline_function */
   sdbout_label,			         /* label */
Index: gcc/tree-streamer-in.c
===================================================================
--- gcc/tree-streamer-in.c	(revision 240004)
+++ gcc/tree-streamer-in.c	(working copy)
@@ -707,10 +707,7 @@ lto_input_ts_decl_common_tree_pointers (
   DECL_SIZE (expr) = stream_read_tree (ib, data_in);
   DECL_SIZE_UNIT (expr) = stream_read_tree (ib, data_in);
   DECL_ATTRIBUTES (expr) = stream_read_tree (ib, data_in);
-
-  /* Do not stream DECL_ABSTRACT_ORIGIN.  We cannot handle debug information
-     for early inlining so drop it on the floor instead of ICEing in
-     dwarf2out.c.  */
+  DECL_ABSTRACT_ORIGIN (expr) = stream_read_tree (ib, data_in);
 
   if ((TREE_CODE (expr) == VAR_DECL
        || TREE_CODE (expr) == PARM_DECL)
Index: gcc/tree-streamer-out.c
===================================================================
--- gcc/tree-streamer-out.c	(revision 240004)
+++ gcc/tree-streamer-out.c	(working copy)
@@ -584,10 +584,7 @@ write_ts_decl_common_tree_pointers (stru
      special handling in LTO, it must be handled by streamer hooks.  */
 
   stream_write_tree (ob, DECL_ATTRIBUTES (expr), ref_p);
-
-  /* Do not stream DECL_ABSTRACT_ORIGIN.  We cannot handle debug information
-     for early inlining so drop it on the floor instead of ICEing in
-     dwarf2out.c.  */
+  stream_write_tree (ob, DECL_ABSTRACT_ORIGIN (expr), ref_p);
 
   if ((TREE_CODE (expr) == VAR_DECL
        || TREE_CODE (expr) == PARM_DECL)
@@ -771,6 +768,7 @@ write_ts_block_tree_pointers (struct out
      For the rest them on the floor instead of ICEing in dwarf2out.c, but
      keep the notion of whether the block is an inlined block by refering
      to itself for the sake of tree_nonartificial_location.  */
+#if 1
   if (inlined_function_outer_scope_p (expr))
     {
       tree ultimate_origin = block_ultimate_origin (expr);
@@ -779,6 +777,9 @@ write_ts_block_tree_pointers (struct out
   else
     stream_write_tree (ob, (BLOCK_ABSTRACT_ORIGIN (expr)
 			    ? expr : NULL_TREE), ref_p);
+#else
+  stream_write_tree (ob, BLOCK_ABSTRACT_ORIGIN (expr), ref_p);
+#endif
   /* Do not stream BLOCK_NONLOCALIZED_VARS.  We cannot handle debug information
      for early inlined BLOCKs so drop it on the floor instead of ICEing in
      dwarf2out.c.  */
Index: include/simple-object.h
===================================================================
--- include/simple-object.h	(revision 240004)
+++ include/simple-object.h	(working copy)
@@ -197,6 +197,14 @@ simple_object_write_to_file (simple_obje
 extern void
 simple_object_release_write (simple_object_write *);
 
+/* Copy LTO debug sections from SRC_OBJECT to DEST.
+   If an error occurs, return the errno value in ERR and an error string.  */
+
+extern const char *
+simple_object_copy_lto_debug_sections (simple_object_read *src_object,
+				       const char *dest,
+				       int *err);
+
 #ifdef __cplusplus
 }
 #endif
Index: libiberty/simple-object-coff.c
===================================================================
--- libiberty/simple-object-coff.c	(revision 240004)
+++ libiberty/simple-object-coff.c	(working copy)
@@ -800,5 +800,6 @@ const struct simple_object_functions sim
   simple_object_coff_release_attributes,
   simple_object_coff_start_write,
   simple_object_coff_write_to_file,
-  simple_object_coff_release_write
+  simple_object_coff_release_write,
+  NULL
 };
Index: libiberty/simple-object-common.h
===================================================================
--- libiberty/simple-object-common.h	(revision 240004)
+++ libiberty/simple-object-common.h	(working copy)
@@ -141,6 +141,12 @@ struct simple_object_functions
 
   /* Release the private data for an simple_object_write.  */
   void (*release_write) (void *);
+
+  /* Copy LTO debug sections.  */
+  const char *(*copy_lto_debug_sections) (simple_object_read *sobj,
+					  simple_object_write *dobj,
+					  int (*pfn) (const char **),
+					  int *err);
 };
 
 /* The known object file formats.  */
Index: libiberty/simple-object-elf.c
===================================================================
--- libiberty/simple-object-elf.c	(revision 240004)
+++ libiberty/simple-object-elf.c	(working copy)
@@ -183,8 +183,55 @@ typedef struct {
 
 /* Values for sh_type field.  */
 
+#define SHT_NULL	0		/* Section header table entry unused */
 #define SHT_PROGBITS	1		/* Program data */
+#define SHT_SYMTAB	2		/* Link editing symbol table */
 #define SHT_STRTAB	3		/* A string table */
+#define SHT_RELA	4		/* Relocation entries with addends */
+#define SHT_REL		9		/* Relocation entries, no addends */
+#define SHT_GROUP	17		/* Section contains a section group */
+
+/* Values for sh_flags field.  */
+
+#define SHF_EXCLUDE	0x80000000	/* Link editor is to exclude this
+					   section from executable and
+					   shared library that it builds
+					   when those objects are not to be
+					   further relocated.  */
+/* Symbol table entry.  */
+
+typedef struct
+{
+  unsigned char st_name[4];                /* Symbol name (string tbl index) */
+  unsigned char st_value[4];               /* Symbol value */
+  unsigned char st_size[4];                /* Symbol size */
+  unsigned char st_info;                /* Symbol type and binding */
+  unsigned char st_other;               /* Symbol visibility */
+  unsigned char st_shndx[2];               /* Section index */
+} Elf32_External_Sym;
+
+typedef struct
+{
+  unsigned char st_name[4];                /* Symbol name (string tbl index) */
+  unsigned char st_info;                /* Symbol type and binding */
+  unsigned char st_other;               /* Symbol visibility */
+  unsigned char st_shndx[2];               /* Section index */
+  unsigned char st_value[8];               /* Symbol value */
+  unsigned char st_size[8];                /* Symbol size */
+} Elf64_External_Sym;
+
+#define ELF_ST_BIND(val)              (((unsigned char) (val)) >> 4)
+#define ELF_ST_TYPE(val)              ((val) & 0xf)
+#define ELF_ST_INFO(bind, type)       (((bind) << 4) + ((type) & 0xf))
+
+#define STT_OBJECT	1	/* Symbol is a data object */
+#define STT_FUNC	2	/* Symbol is a code object */
+#define STT_TLS		6	/* Thread local data object */
+#define STT_GNU_IFUNC	10	/* Symbol is an indirect code object */
+
+#define STV_DEFAULT	0	/* Visibility is specified by binding type */
+
+#define SHN_COMMON	0xFFF2	/* Associated symbol is in common */
 
 /* Functions to fetch and store different ELF types, depending on the
    endianness and size.  */
@@ -348,6 +395,14 @@ struct simple_object_elf_attributes
   unsigned int flags;
 };
 
+/* Private data for an simple_object_write.  */
+
+struct simple_object_elf_write
+{
+  struct simple_object_elf_attributes attrs;
+  unsigned char *shdrs;
+};
+
 /* See if we have an ELF file.  */
 
 static void *
@@ -675,12 +730,13 @@ simple_object_elf_start_write (void *att
 {
   struct simple_object_elf_attributes *attrs =
     (struct simple_object_elf_attributes *) attributes_data;
-  struct simple_object_elf_attributes *ret;
+  struct simple_object_elf_write *ret;
 
   /* We're just going to record the attributes, but we need to make a
      copy because the user may delete them.  */
-  ret = XNEW (struct simple_object_elf_attributes);
-  *ret = *attrs;
+  ret = XNEW (struct simple_object_elf_write);
+  ret->attrs = *attrs;
+  ret->shdrs = NULL;
   return ret;
 }
 
@@ -766,8 +822,11 @@ static int
 simple_object_elf_write_shdr (simple_object_write *sobj, int descriptor,
 			      off_t offset, unsigned int sh_name,
 			      unsigned int sh_type, unsigned int sh_flags,
+			      off_t sh_addr,
 			      unsigned int sh_offset, unsigned int sh_size,
-			      unsigned int sh_link, unsigned int sh_addralign,
+			      unsigned int sh_link, unsigned int sh_info,
+			      unsigned int sh_addralign,
+			      unsigned int sh_entsize,
 			      const char **errmsg, int *err)
 {
   struct simple_object_elf_attributes *attrs =
@@ -788,12 +847,13 @@ simple_object_elf_write_shdr (simple_obj
   ELF_SET_FIELD (fns, cl, Shdr, buf, sh_name, Elf_Word, sh_name);
   ELF_SET_FIELD (fns, cl, Shdr, buf, sh_type, Elf_Word, sh_type);
   ELF_SET_FIELD (fns, cl, Shdr, buf, sh_flags, Elf_Addr, sh_flags);
+  ELF_SET_FIELD (fns, cl, Shdr, buf, sh_addr, Elf_Addr, sh_addr);
   ELF_SET_FIELD (fns, cl, Shdr, buf, sh_offset, Elf_Addr, sh_offset);
   ELF_SET_FIELD (fns, cl, Shdr, buf, sh_size, Elf_Addr, sh_size);
   ELF_SET_FIELD (fns, cl, Shdr, buf, sh_link, Elf_Word, sh_link);
-  /* sh_info left as zero.  */
+  ELF_SET_FIELD (fns, cl, Shdr, buf, sh_info, Elf_Word, sh_info);
   ELF_SET_FIELD (fns, cl, Shdr, buf, sh_addralign, Elf_Addr, sh_addralign);
-  /* sh_entsize left as zero.  */
+  ELF_SET_FIELD (fns, cl, Shdr, buf, sh_entsize, Elf_Word, sh_entsize);
 
   return simple_object_internal_write (descriptor, offset, buf, shdr_size,
 				       errmsg, err);
@@ -811,8 +871,9 @@ static const char *
 simple_object_elf_write_to_file (simple_object_write *sobj, int descriptor,
 				 int *err)
 {
-  struct simple_object_elf_attributes *attrs =
-    (struct simple_object_elf_attributes *) sobj->data;
+  struct simple_object_elf_write *eow =
+    (struct simple_object_elf_write *) sobj->data;
+  struct simple_object_elf_attributes *attrs = &eow->attrs;
   unsigned char cl;
   size_t ehdr_size;
   size_t shdr_size;
@@ -825,6 +886,7 @@ simple_object_elf_write_to_file (simple_
   unsigned int first_sh_link;
   size_t sh_name;
   unsigned char zero;
+  unsigned secnum;
 
   if (!simple_object_elf_write_ehdr (sobj, descriptor, &errmsg, err))
     return errmsg;
@@ -862,21 +924,54 @@ simple_object_elf_write_to_file (simple_
   else
     first_sh_link = shnum - 1;
   if (!simple_object_elf_write_shdr (sobj, descriptor, shdr_offset,
-				     0, 0, 0, 0, first_sh_size, first_sh_link,
-				     0, &errmsg, err))
+				     0, 0, 0, 0, 0, first_sh_size, first_sh_link,
+				     0, 0, 0, &errmsg, err))
     return errmsg;
 
   shdr_offset += shdr_size;
 
   sh_name = 1;
+  secnum = 0;
   for (section = sobj->sections; section != NULL; section = section->next)
     {
       size_t mask;
       size_t new_sh_offset;
       size_t sh_size;
       struct simple_object_write_section_buffer *buffer;
+      unsigned int sh_type = SHT_PROGBITS;
+      unsigned int sh_flags = 0;
+      off_t sh_addr = 0;
+      unsigned int sh_link = 0;
+      unsigned int sh_info = 0;
+      unsigned int sh_addralign = 1U << section->align;
+      unsigned int sh_entsize = 0;
+      if (eow->shdrs)
+	{
+	  sh_type = ELF_FETCH_FIELD (attrs->type_functions, attrs->ei_class, Shdr,
+				     eow->shdrs + secnum * shdr_size,
+				     sh_type, Elf_Word);
+	  sh_flags = ELF_FETCH_FIELD (attrs->type_functions, attrs->ei_class, Shdr,
+				      eow->shdrs + secnum * shdr_size,
+				      sh_flags, Elf_Addr);
+	  sh_addr = ELF_FETCH_FIELD (attrs->type_functions, attrs->ei_class, Shdr,
+				     eow->shdrs + secnum * shdr_size,
+				     sh_addr, Elf_Addr);
+	  sh_link = ELF_FETCH_FIELD (attrs->type_functions, attrs->ei_class, Shdr,
+				     eow->shdrs + secnum * shdr_size,
+				     sh_link, Elf_Word);
+	  sh_info = ELF_FETCH_FIELD (attrs->type_functions, attrs->ei_class, Shdr,
+				     eow->shdrs + secnum * shdr_size,
+				     sh_info, Elf_Word);
+	  sh_addralign = ELF_FETCH_FIELD (attrs->type_functions, attrs->ei_class, Shdr,
+					  eow->shdrs + secnum * shdr_size,
+					  sh_addralign, Elf_Addr);
+	  sh_entsize = ELF_FETCH_FIELD (attrs->type_functions, attrs->ei_class, Shdr,
+					eow->shdrs + secnum * shdr_size,
+					sh_entsize, Elf_Word);
+	  secnum++;
+	}
 
-      mask = (1U << section->align) - 1;
+      mask = sh_addralign - 1;
       new_sh_offset = sh_offset + mask;
       new_sh_offset &= ~ mask;
       while (new_sh_offset > sh_offset)
@@ -906,8 +1001,10 @@ simple_object_elf_write_to_file (simple_
 	}
 
       if (!simple_object_elf_write_shdr (sobj, descriptor, shdr_offset,
-					 sh_name, SHT_PROGBITS, 0, sh_offset,
-					 sh_size, 0, 1U << section->align,
+					 sh_name, sh_type, sh_flags,
+					 sh_addr, sh_offset,
+					 sh_size, sh_link, sh_info,
+					 sh_addralign, sh_entsize,
 					 &errmsg, err))
 	return errmsg;
 
@@ -917,9 +1014,9 @@ simple_object_elf_write_to_file (simple_
     }
 
   if (!simple_object_elf_write_shdr (sobj, descriptor, shdr_offset,
-				     sh_name, SHT_STRTAB, 0, sh_offset,
-				     sh_name + strlen (".shstrtab") + 1, 0,
-				     1, &errmsg, err))
+				     sh_name, SHT_STRTAB, 0, 0, sh_offset,
+				     sh_name + strlen (".shstrtab") + 1, 0, 0,
+				     1, 0, &errmsg, err))
     return errmsg;
 
   /* .shstrtab has a leading zero byte.  */
@@ -954,9 +1051,365 @@ simple_object_elf_write_to_file (simple_
 static void
 simple_object_elf_release_write (void *data)
 {
+  struct simple_object_elf_write *eow = (struct simple_object_elf_write *) data;
+  if (eow->shdrs)
+    XDELETE (eow->shdrs);
   XDELETE (data);
 }
 
+/* Copy all sections in an ELF file.  */
+
+static const char *
+simple_object_elf_copy_lto_debug_sections (simple_object_read *sobj,
+					   simple_object_write *dobj,
+					   int (*pfn) (const char **),
+					   int *err)
+{
+  struct simple_object_elf_read *eor =
+    (struct simple_object_elf_read *) sobj->data;
+  const struct elf_type_functions *type_functions = eor->type_functions;
+  struct simple_object_elf_write *eow =
+    (struct simple_object_elf_write *) dobj->data;
+  unsigned char ei_class = eor->ei_class;
+  size_t shdr_size;
+  unsigned int shnum;
+  unsigned char *shdrs;
+  const char *errmsg;
+  unsigned char *shstrhdr;
+  size_t name_size;
+  off_t shstroff;
+  unsigned char *names;
+  unsigned int i;
+  int *pfnret;
+  const char **pfnname;
+
+  shdr_size = (ei_class == ELFCLASS32
+	       ? sizeof (Elf32_External_Shdr)
+	       : sizeof (Elf64_External_Shdr));
+
+  /* Read the section headers.  We skip section 0, which is not a
+     useful section.  */
+
+  shnum = eor->shnum;
+  shdrs = XNEWVEC (unsigned char, shdr_size * (shnum - 1));
+
+  if (!simple_object_internal_read (sobj->descriptor,
+				    sobj->offset + eor->shoff + shdr_size,
+				    shdrs,
+				    shdr_size * (shnum - 1),
+				    &errmsg, err))
+    {
+      XDELETEVEC (shdrs);
+      return errmsg;
+    }
+
+  /* Read the section names.  */
+
+  shstrhdr = shdrs + (eor->shstrndx - 1) * shdr_size;
+  name_size = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+			       shstrhdr, sh_size, Elf_Addr);
+  shstroff = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+			      shstrhdr, sh_offset, Elf_Addr);
+  names = XNEWVEC (unsigned char, name_size);
+  if (!simple_object_internal_read (sobj->descriptor,
+				    sobj->offset + shstroff,
+				    names, name_size, &errmsg, err))
+    {
+      XDELETEVEC (names);
+      XDELETEVEC (shdrs);
+      return errmsg;
+    }
+
+  eow->shdrs = XNEWVEC (unsigned char, shdr_size * (shnum - 1));
+  pfnret = XNEWVEC (int, shnum);
+  pfnname = XNEWVEC (const char *, shnum);
+
+  /* First perform the callbacks to know which sections to preserve and
+     what name to use for those.  */
+  for (i = 1; i < shnum; ++i)
+    {
+      unsigned char *shdr;
+      unsigned int sh_name;
+      const char *name;
+      int ret;
+
+      shdr = shdrs + (i - 1) * shdr_size;
+      sh_name = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+				 shdr, sh_name, Elf_Word);
+      if (sh_name >= name_size)
+	{
+	  *err = 0;
+	  XDELETEVEC (names);
+	  XDELETEVEC (shdrs);
+	  return "ELF section name out of range";
+	}
+
+      name = (const char *) names + sh_name;
+
+      ret = (*pfn) (&name);
+      pfnret[i - 1] = ret == 1 ? 0 : -1;
+      pfnname[i - 1] = name;
+    }
+
+  /* Mark sections as preserved that are required by to be preserved
+     sections.  */
+  for (i = 1; i < shnum; ++i)
+    {
+      unsigned char *shdr;
+      unsigned int sh_type, sh_info, sh_link;
+      off_t offset;
+      off_t length;
+
+      shdr = shdrs + (i - 1) * shdr_size;
+      sh_type = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+				 shdr, sh_type, Elf_Word);
+      sh_info = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+				 shdr, sh_info, Elf_Word);
+      sh_link = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+				 shdr, sh_link, Elf_Word);
+      if (sh_type == SHT_GROUP)
+	{
+	  /* Mark groups containing copied sections.  */
+	  unsigned entsize = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+					      shdr, sh_entsize, Elf_Addr);
+	  unsigned char *ent, *buf;
+	  int keep = 0;
+	  offset = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+				    shdr, sh_offset, Elf_Addr);
+	  length = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+				    shdr, sh_size, Elf_Addr);
+	  buf = XNEWVEC (unsigned char, length);
+	  if (!simple_object_internal_read (sobj->descriptor,
+					    sobj->offset + offset, buf,
+					    (size_t) length, &errmsg, err))
+	    {
+	      XDELETEVEC (buf);
+	      XDELETEVEC (names);
+	      XDELETEVEC (shdrs);
+	      return errmsg;
+	    }
+	  for (ent = buf + entsize; ent < buf + length; ent += entsize)
+	    {
+	      unsigned sec = type_functions->fetch_Elf_Word (ent);
+	      if (pfnret[sec - 1] == 0)
+		keep = 1;
+	    }
+	  if (keep)
+	    {
+	      pfnret[sh_link - 1] = 0;
+	      pfnret[i - 1] = 0;
+	    }
+	}
+      if (sh_type == SHT_RELA
+	  || sh_type == SHT_REL)
+	{
+	  /* Mark relocation sections and symtab of copied sections.  */
+	  if (pfnret[sh_info - 1] == 0)
+	    {
+	      pfnret[sh_link - 1] = 0;
+	      pfnret[i - 1] = 0;
+	    }
+	}
+      if (sh_type == SHT_SYMTAB)
+	{
+	  /* Mark strings sections of copied symtabs.  */
+	  if (pfnret[i - 1] == 0)
+	    pfnret[sh_link - 1] = 0;
+	}
+    }
+
+  /* Then perform the actual copying.  */
+  for (i = 1; i < shnum; ++i)
+    {
+      unsigned char *shdr;
+      unsigned int sh_name, sh_type;
+      const char *name;
+      off_t offset;
+      off_t length;
+      int ret;
+      const char *errmsg;
+      simple_object_write_section *dest;
+      off_t flags;
+      unsigned char *buf;
+
+      shdr = shdrs + (i - 1) * shdr_size;
+      sh_name = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+				 shdr, sh_name, Elf_Word);
+      if (sh_name >= name_size)
+	{
+	  *err = 0;
+	  XDELETEVEC (names);
+	  XDELETEVEC (shdrs);
+	  return "ELF section name out of range";
+	}
+
+      name = (const char *) names + sh_name;
+      offset = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+				shdr, sh_offset, Elf_Addr);
+      length = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+				shdr, sh_size, Elf_Addr);
+      sh_type = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+				 shdr, sh_type, Elf_Word);
+
+      /* ???  With two passes we could automatically remove related
+	 sections like relocation sections and GROUPs.  */
+      ret = pfnret[i - 1];
+      name = ret == 0 ? pfnname[i - 1] : "";
+
+      dest = simple_object_write_create_section (dobj, name, 0, &errmsg, err);
+      if (dest == NULL)
+	{
+	  XDELETEVEC (names);
+	  XDELETEVEC (shdrs);
+	  return errmsg;
+	}
+
+      /* Record the SHDR of the source.  */
+      memcpy (eow->shdrs + (i - 1) * shdr_size, shdr, shdr_size);
+      shdr = eow->shdrs + (i - 1) * shdr_size;
+
+      /* Copy the data.
+	 ???  This is quite wasteful and ideally would be delayed until
+	 write_to_file ().  Thus it questions the interfacing
+	 which eventually should contain destination creation plus
+	 writing.  */
+      /* Keep empty sections for sections we should discard.  This avoids
+         the need to rewrite section indices in symtab and relocation
+	 sections.  */
+      if (ret == 0)
+	{
+	  buf = XNEWVEC (unsigned char, length);
+	  if (!simple_object_internal_read (sobj->descriptor,
+					    sobj->offset + offset, buf,
+					    (size_t) length, &errmsg, err))
+	    {
+	      XDELETEVEC (buf);
+	      XDELETEVEC (names);
+	      XDELETEVEC (shdrs);
+	      return errmsg;
+	    }
+
+	  /* If we are processing .symtab purge __gnu_lto_v1 and
+	     __gnu_lto_slim symbols from it.  */
+	  if (sh_type == SHT_SYMTAB)
+	    {
+	      unsigned entsize = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+						  shdr, sh_entsize, Elf_Addr);
+	      unsigned strtab = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+						 shdr, sh_link, Elf_Word);
+	      unsigned char *strshdr = shdrs + (strtab - 1) * shdr_size;
+	      off_t stroff = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+					      strshdr, sh_offset, Elf_Addr);
+	      size_t strsz = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+					      strshdr, sh_size, Elf_Addr);
+	      char *strings = XNEWVEC (char, strsz);
+	      unsigned char *ent;
+	      simple_object_internal_read (sobj->descriptor,
+					   sobj->offset + stroff,
+					   (unsigned char *)strings,
+					   strsz, &errmsg, err);
+	      for (ent = buf; ent < buf + length; ent += entsize)
+		{
+		  unsigned st_shndx = ELF_FETCH_FIELD (type_functions, ei_class,
+						       Sym, ent,
+						       st_shndx, Elf_Half);
+		  unsigned char *st_info;
+		  unsigned char *st_other;
+		  if (ei_class == ELFCLASS32)
+		    {
+		      st_info = &((Elf32_External_Sym *)ent)->st_info;
+		      st_other = &((Elf32_External_Sym *)ent)->st_other;
+		    }
+		  else
+		    {
+		      st_info = &((Elf64_External_Sym *)ent)->st_info;
+		      st_other = &((Elf64_External_Sym *)ent)->st_other;
+		    }
+		  /* Eliminate all COMMONs - this includes __gnu_lto_v1
+		     and __gnu_lto_slim which otherwise cause endless
+		     LTO plugin invocation.  */
+		  if (st_shndx == SHN_COMMON)
+		    /* Setting st_name to "" seems to work to purge
+		       COMMON symbols (in addition to setting their
+		       size to zero).  */
+		    ;
+		  /* We also need to remove symbols refering to sections
+		     we'll eventually remove as with fat LTO objects
+		     we otherwise get duplicate symbols at final link
+		     (with GNU ld, gold is fine and ignores symbols in
+		     sections marked as EXCLUDE).  ld/20513  */
+		  else if (st_shndx != 0
+			   && st_shndx < shnum
+			   && pfnret[st_shndx - 1] == -1)
+		    /* Messing with st_shndx doesn't seem to work very
+		       well.  Likewise changing the symbol type to
+		       a section symbol or making it local.
+		       So just keep with making it unnamed.
+		       Also make all regular symbols STT_OBJECT and
+		       have default visibility, otherwise GNU ld warns
+		       about mismatches for the same `' named symbol.  */
+		    {
+		      if (ELF_ST_TYPE (*st_info) == STT_FUNC
+			  || ELF_ST_TYPE (*st_info) == STT_TLS
+			  || ELF_ST_TYPE (*st_info) == STT_GNU_IFUNC)
+			*st_info = ELF_ST_INFO (ELF_ST_BIND (*st_info),
+						STT_OBJECT);
+		      *st_other = STV_DEFAULT;
+		    }
+		  else
+		    continue;
+
+		  /* For all purged symbols make the symbol unnamed.  */
+		  ELF_SET_FIELD (type_functions, ei_class, Sym,
+				 ent, st_name, Elf_Word, 0);
+
+		  /* At least set st_value and st_size to zero to not go
+		     out of bounds.  */
+		  ELF_SET_FIELD (type_functions, ei_class, Sym,
+				 ent, st_value, Elf_Addr, 0);
+		  ELF_SET_FIELD (type_functions, ei_class, Sym,
+				 ent, st_size, Elf_Word, 0);
+		}
+	      XDELETEVEC (strings);
+	    }
+
+	  errmsg = simple_object_write_add_data (dobj, dest, buf, length, 1, err);
+	  XDELETEVEC (buf);
+	  if (errmsg)
+	    {
+	      XDELETEVEC (names);
+	      XDELETEVEC (shdrs);
+	      return errmsg;
+	    }
+	}
+      else
+	{
+	  /* For deleted sections mark the section header table entry as
+	     unused.  That allows the link editor to remove it in a partial
+	     link.  */
+	  ELF_SET_FIELD (type_functions, ei_class, Shdr,
+			 shdr, sh_type, Elf_Addr, SHT_NULL);
+	}
+
+      flags = ELF_FETCH_FIELD (type_functions, ei_class, Shdr,
+			       shdr, sh_flags, Elf_Addr);
+      if (ret == 0)
+	flags &= ~SHF_EXCLUDE;
+      else if (ret == -1)
+	flags |= SHF_EXCLUDE;
+      ELF_SET_FIELD (type_functions, ei_class, Shdr,
+		     shdr, sh_flags, Elf_Addr, flags);
+    }
+
+  XDELETEVEC (names);
+  XDELETEVEC (shdrs);
+  XDELETEVEC (pfnret);
+  XDELETEVEC (pfnname);
+
+  return NULL;
+}
+
+
 /* The ELF functions.  */
 
 const struct simple_object_functions simple_object_elf_functions =
@@ -969,5 +1422,6 @@ const struct simple_object_functions sim
   simple_object_elf_release_attributes,
   simple_object_elf_start_write,
   simple_object_elf_write_to_file,
-  simple_object_elf_release_write
+  simple_object_elf_release_write,
+  simple_object_elf_copy_lto_debug_sections
 };
Index: libiberty/simple-object-mach-o.c
===================================================================
--- libiberty/simple-object-mach-o.c	(revision 240004)
+++ libiberty/simple-object-mach-o.c	(working copy)
@@ -1374,5 +1374,6 @@ const struct simple_object_functions sim
   simple_object_mach_o_release_attributes,
   simple_object_mach_o_start_write,
   simple_object_mach_o_write_to_file,
-  simple_object_mach_o_release_write
+  simple_object_mach_o_release_write,
+  NULL
 };
Index: libiberty/simple-object-xcoff.c
===================================================================
--- libiberty/simple-object-xcoff.c	(revision 240004)
+++ libiberty/simple-object-xcoff.c	(working copy)
@@ -894,5 +894,6 @@ const struct simple_object_functions sim
   simple_object_xcoff_release_attributes,
   simple_object_xcoff_start_write,
   simple_object_xcoff_write_to_file,
-  simple_object_xcoff_release_write
+  simple_object_xcoff_release_write,
+  NULL
 };
Index: libiberty/simple-object.c
===================================================================
--- libiberty/simple-object.c	(revision 240004)
+++ libiberty/simple-object.c	(working copy)
@@ -22,6 +22,7 @@ Boston, MA 02110-1301, USA.  */
 #include "simple-object.h"
 
 #include <errno.h>
+#include <fcntl.h>
 
 #ifdef HAVE_STDLIB_H
 #include <stdlib.h>
@@ -249,6 +250,85 @@ simple_object_find_section (simple_objec
   return 1;
 }
 
+/* Callback to identify and rename LTO debug sections by name.
+   Returns 1 if NAME is a LTO debug section, 0 if not.  */
+
+static int
+handle_lto_debug_sections (const char **name)
+{
+  /* ???  So we can't use .gnu.lto_ prefixed sections as the assembler
+     complains about bogus section flags.  Which means we need to arrange
+     for that to be fixed or .gnu.debuglto_ marked as SHF_EXCLUDE (to make
+     fat lto object tooling work for the fat part).  */
+  /* ???  For now this handles both .gnu.lto_ and .gnu.debuglto_ prefixed
+     sections.  */
+  /* Copy LTO debug sections and rename them to their non-LTO name.  */
+  if (strncmp (*name, ".gnu.debuglto_", sizeof (".gnu.debuglto_") - 1) == 0)
+    {
+      *name = *name + sizeof (".gnu.debuglto_") - 1;
+      return 1;
+    }
+  else if (strncmp (*name, ".gnu.lto_.debug_", sizeof (".gnu.lto_.debug_") -1) == 0)
+    {
+      *name = *name + sizeof (".gnu.lto_") - 1;
+      return 1;
+    }
+  return 0;
+}
+
+/* Copy LTO debug sections.  */
+
+const char *
+simple_object_copy_lto_debug_sections (simple_object_read *sobj,
+				       const char *dest, int *err)
+{
+  const char *errmsg;
+  simple_object_write *dest_sobj;
+  simple_object_attributes *attrs;
+  int outfd;
+
+  if (! sobj->functions->copy_lto_debug_sections)
+    {
+      *err = EINVAL;
+      return "simple_object_copy_lto_debug_sections not implemented";
+    }
+
+  attrs = simple_object_fetch_attributes (sobj, &errmsg, err);
+  if (! attrs)
+    return errmsg;
+  dest_sobj = simple_object_start_write (attrs, "__GNU_LTO", &errmsg, err);
+  simple_object_release_attributes (attrs);
+  if (! dest_sobj)
+    return errmsg;
+
+  errmsg = sobj->functions->copy_lto_debug_sections (sobj, dest_sobj,
+						     handle_lto_debug_sections,
+						     err);
+  if (errmsg)
+    {
+      simple_object_release_write (dest_sobj);
+      return errmsg;
+    }
+
+  outfd = creat (dest, 00777);
+  if (outfd == -1)
+    {
+      *err = errno;
+      simple_object_release_write (dest_sobj);
+      return "open failed";
+    }
+
+  errmsg = simple_object_write_to_file (dest_sobj, outfd, err);
+  close (outfd);
+  if (errmsg)
+    {
+      simple_object_release_write (dest_sobj);
+      return errmsg;
+    }
+
+  return NULL;
+}
+
 /* Fetch attributes.  */
 
 simple_object_attributes *



More information about the Gcc-patches mailing list