Early DWARF gives each input TU its own DIE for a mergeable inline
function.  During WPA, global SCC unification maps those function trees
to one prevailing declaration.  In this testcase the function-local
origin trees already have contexts referring to that prevailing function,
so the semantic tree topology is consistent.  However, the local trees
retain their TU-specific early-DIE references while the prevailing
function tree can retain only one.  A concrete function can consequently
refer to a function DIE from one TU while its children refer to DIEs from
another.

Preserve the physical DIE identity on the abstract-origin edge before
unification loses it.  When a TU-local function declaration or block has
no early DIE of its own and its origin is a mergeable function, stream the
origin DIE reference and distinguish it from a self-DIE reference with a
tag packed into the streamed offset.  Keep these references in a separate
map keyed by the concrete owner, forward them through WPA, and use them
during late DWARF output while the semantic origin still matches.
DECL_ABSTRACT_ORIGIN and BLOCK_ABSTRACT_ORIGIN continue to point to ordinary
trees.

LTO transformations can create further abstract-origin edges after input.
When the inliner remaps a block, creates an inlined-function block, or
versions a function, carry forward the source edge's DIE reference.  If the
source is its own origin, carry its self-DIE reference instead.  Extend the
existing external-DIE hooks with a reference kind rather than adding new
debug hook targets.

The tagged offset changes the LTO bytecode, so bump LTO_minor_version.

Add regression coverage for an out-of-line split function and for both
single and nested inlined subroutines.

Bootstrapped and regression-tested on x86_64-pc-linux-gnu with no new
failures.  The tests report 9 passes and 3 provenance-scan failures without
the change, and 12 passes with it.

        PR debug/126348

gcc/ChangeLog:

        * debug.cc (debug_false_tree_charstarstar_uhwistar): Add reference kind.
        (debug_nothing_tree_charstar_uhwi): Likewise.
        * debug.h (debug_external_ref_kind): New enum.
        (gcc_debug_hooks): Add a reference kind to die_ref_for_decl and
        register_external_die.
        (debug_false_tree_charstarstar_uhwistar): Adjust declaration.
        (debug_nothing_tree_charstar_uhwi): Likewise.
        * dwarf2out.cc (external_abstract_origin_ref): New.
        (external_abstract_origin_map): New.
        (abstract_origin_for_instance): New.
        (dwarf2out_die_ref_for_decl): Handle abstract-origin edge references.
        (dwarf2out_register_external_die): Likewise.
        (add_abstract_origin_attribute): Accept the concrete instance and use
        its edge-specific DIE reference when present.
        (gen_entry_point_die, gen_formal_parameter_die, gen_subprogram_die,
        gen_variable_die, gen_label_die, gen_lexical_block_die,
        gen_inlined_subroutine_die): Pass the concrete instance.
        * lto-streamer-in.cc (lto_read_tree_1): Read abstract-origin edge DIE
        references.
        (lto_input_tree): Pass the self-DIE reference kind.
        * lto-streamer-out.cc (lto_write_tree_1): Capture and write
        abstract-origin edge DIE references.
        * lto-streamer.h (LTO_minor_version): Bump.
        * tree-inline.cc (copy_lto_abstract_origin_ref): New.
        (remap_block, expand_call_inline, tree_function_versioning): Use it.

gcc/lto/ChangeLog:

        * lto-common.cc (process_dref, lto_read_decls): Pass the self-DIE
        reference kind.

gcc/testsuite/ChangeLog:

        * g++.dg/lto/pr126348.h: New test.
        * g++.dg/lto/pr126348-2_0.C: New test.
        * g++.dg/lto/pr126348-2_1.C: New test.
        * g++.dg/lto/pr126348-2_2.C: New test.
        * g++.dg/lto/pr126348_0.C: New test.
        * g++.dg/lto/pr126348_1.C: New test.
        * g++.dg/lto/pr126348_2.C: New test.

Signed-off-by: Longjun Luo <[email protected]>
---
I instrumented stream-in to check the tree state you asked about.  For
the second input TU, the relevant part of the trace is:

  FN   origin=.../pr126348_0.C.../349
  PARM origin=.../pr126348_1.C.../362
       context=.../pr126348_0.C.../349 same-context=1
  VAR  origin=.../pr126348_1.C.../371
       context=.../pr126348_0.C.../349 same-context=1

Here same-context compares the local origin's DECL_CONTEXT with the
function origin.  The local PARM_DECL and VAR_DECL origins remain
TU-specific, but their contexts already refer to the same prevailing
FUNCTION_DECL.

This is expected from lto_fixup_prevailing_decls: LTO_SET_PREVAIL only
substitutes public or external VAR_DECLs and FUNCTION_DECLs.  PARM_DECLs,
local VAR_DECLs, and BLOCKs have no symtab prevailing counterparts.  The
DECL_CONTEXT references of the local decls, by contrast, resolve to the
public prevailing function; that is why the trace reports same-context=1.

The capture condition reflects the same asymmetry: the owner is a block or
TU-local function, while the origin is a public or external function.  LTO
may therefore replace the origin with a prevailing tree while leaving the
owner unmerged.

This condition on the abstract-origin edge replaces the split-clone-specific
fallback in v2/v3 and also covers inlined blocks.

The loss occurs in external_die_map, which holds one sym/off pair per tree.
Once global SCC unification maps both function trees to the prevailing
declaration, that tree can retain only the TU0 DIE reference.  The unmerged
local decls retain their TU1 references, so late DWARF output can combine
references from two physical input TUs.

Mapping those local origins to trees under the prevailing function would
require a separate structural correspondence across TUs.  It would achieve
consistency only by discarding the TU1 provenance that the local origins
still carry.  This patch instead preserves the matching TU1 function DIE
reference on the concrete owner's abstract-origin edge before the global
function tree is unified, keeping the parent and its children tied to the
same physical early-DWARF instance.

The fixup in lto_input_ts_block_tree_pointers restores the ultimate-origin
tree invariant, but cannot retain that per-TU DIE identity.  The comment for
that fixup ends by noting that this should be fixed by "moving to DIE
references".  This patch follows that direction only for abstract-origin
edges, while leaving DECL_ABSTRACT_ORIGIN and BLOCK_ABSTRACT_ORIGIN as
ordinary trees.  The fake-decl approach from the earlier prototype has been
dropped.

I also checked all five LTO partitioning modes (none, one, 1to1, balanced,
and max), parallel LTRANS with -flto=32, aggressive GGC settings, and
two-stage incremental LTO links.

 gcc/debug.cc                            |  6 +-
 gcc/debug.h                             | 29 +++++---
 gcc/dwarf2out.cc                        | 99 +++++++++++++++++++++----
 gcc/lto-streamer-in.cc                  | 27 +++++--
 gcc/lto-streamer-out.cc                 | 45 ++++++++++-
 gcc/lto-streamer.h                      |  2 +-
 gcc/lto/lto-common.cc                   |  7 +-
 gcc/testsuite/g++.dg/lto/pr126348-2_0.C | 17 +++++
 gcc/testsuite/g++.dg/lto/pr126348-2_1.C | 21 ++++++
 gcc/testsuite/g++.dg/lto/pr126348-2_2.C | 11 +++
 gcc/testsuite/g++.dg/lto/pr126348.h     | 28 +++++++
 gcc/testsuite/g++.dg/lto/pr126348_0.C   | 27 +++++++
 gcc/testsuite/g++.dg/lto/pr126348_1.C   | 28 +++++++
 gcc/testsuite/g++.dg/lto/pr126348_2.C   | 14 ++++
 gcc/tree-inline.cc                      | 36 +++++++++
 15 files changed, 358 insertions(+), 39 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/lto/pr126348-2_0.C
 create mode 100644 gcc/testsuite/g++.dg/lto/pr126348-2_1.C
 create mode 100644 gcc/testsuite/g++.dg/lto/pr126348-2_2.C
 create mode 100644 gcc/testsuite/g++.dg/lto/pr126348.h
 create mode 100644 gcc/testsuite/g++.dg/lto/pr126348_0.C
 create mode 100644 gcc/testsuite/g++.dg/lto/pr126348_1.C
 create mode 100644 gcc/testsuite/g++.dg/lto/pr126348_2.C

diff --git a/gcc/debug.cc b/gcc/debug.cc
index 5e9c7c1a912..ef712a9f1ae 100644
--- a/gcc/debug.cc
+++ b/gcc/debug.cc
@@ -160,14 +160,16 @@ debug_nothing_tree_int (tree decl ATTRIBUTE_UNUSED,
 }
 
 bool
-debug_false_tree_charstarstar_uhwistar (tree, const char **,
+debug_false_tree_charstarstar_uhwistar (tree, debug_external_ref_kind,
+                                       const char **,
                                        unsigned HOST_WIDE_INT *)
 {
   return false;
 }
 
 void
-debug_nothing_tree_charstar_uhwi (tree, const char *,
+debug_nothing_tree_charstar_uhwi (tree, debug_external_ref_kind,
+                                 const char *,
                                  unsigned HOST_WIDE_INT)
 {
 }
diff --git a/gcc/debug.h b/gcc/debug.h
index e5976f25d5a..893d8e1e57b 100644
--- a/gcc/debug.h
+++ b/gcc/debug.h
@@ -25,6 +25,14 @@
    way to speak debug to the middle-end once we are able to get rid of
    the remaining targets.  If you need alternate output formats instead
    generate them off the DWARF representation.  */
+
+/* The kind of early-DWARF reference exchanged through the LTO streamer.  */
+enum debug_external_ref_kind
+{
+  DEBUG_DIE_REF,
+  DEBUG_ABSTRACT_ORIGIN_REF
+};
+
 struct gcc_debug_hooks
 {
   /* Initialize debug output.  MAIN_FILENAME is the name of the main
@@ -156,13 +164,14 @@ struct gcc_debug_hooks
                                    tree context, bool child,
                                    bool implicit);
 
-  /* 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 *);
+  /* Return true if an external early-DWARF reference of KIND is available
+     for the tree and return its symbol and offset.  */
+  bool (* die_ref_for_decl) (tree, debug_external_ref_kind, 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);
+  /* Record an external early-DWARF reference of KIND for the tree.  */
+  void (* register_external_die) (tree, debug_external_ref_kind,
+                                 const char *, unsigned HOST_WIDE_INT);
 
   /* DECL is an inline function, whose body is present, but which is
      not being output at this point.  */
@@ -232,10 +241,10 @@ extern void debug_nothing_tree_tree_tree_bool_bool (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);
+extern bool debug_false_tree_charstarstar_uhwistar
+  (tree, debug_external_ref_kind, const char **, unsigned HOST_WIDE_INT *);
+extern void debug_nothing_tree_charstar_uhwi
+  (tree, debug_external_ref_kind, const char *, unsigned HOST_WIDE_INT);
 
 /* Hooks for various debug formats.  */
 extern const struct gcc_debug_hooks do_nothing_debug_hooks;
diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
index 0b974d63c80..bac129644cf 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -2883,10 +2883,10 @@ static void dwarf2out_begin_function (tree);
 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);
+static void dwarf2out_register_external_die
+  (tree, debug_external_ref_kind, const char *, unsigned HOST_WIDE_INT);
+static bool dwarf2out_die_ref_for_decl
+  (tree, debug_external_ref_kind, const char **, unsigned HOST_WIDE_INT *);
 
 /* The debug hooks structure.  */
 
@@ -3924,7 +3924,7 @@ static void add_alignment_attribute (dw_die_ref, tree);
 static void add_bit_offset_attribute (dw_die_ref, tree);
 static void add_bit_size_attribute (dw_die_ref, tree);
 static void add_prototyped_attribute (dw_die_ref, tree);
-static void add_abstract_origin_attribute (dw_die_ref, tree);
+static void add_abstract_origin_attribute (dw_die_ref, tree, tree);
 static void add_pure_or_virtual_attribute (dw_die_ref, tree);
 static void add_src_coords_attributes (dw_die_ref, tree);
 static void add_name_and_src_coords_attributes (dw_die_ref, tree, bool = 
false);
@@ -5974,6 +5974,30 @@ struct GTY(()) sym_off_pair
 };
 static GTY(()) hash_map<tree, sym_off_pair> *external_die_map;
 
+struct GTY(()) external_abstract_origin_ref
+{
+  tree origin;
+  const char *sym;
+  unsigned HOST_WIDE_INT off;
+};
+
+/* Unlike EXTERNAL_DIE_MAP, this map describes an abstract-origin edge.  Its
+   key is the concrete decl or block, rather than the referenced origin.  */
+static GTY(()) hash_map<tree, external_abstract_origin_ref>
+  *external_abstract_origin_map;
+
+/* Return the abstract origin associated with INSTANCE.  */
+
+static tree
+abstract_origin_for_instance (tree instance)
+{
+  if (DECL_P (instance))
+    return DECL_ABSTRACT_ORIGIN (instance);
+  if (TREE_CODE (instance) == BLOCK)
+    return BLOCK_ABSTRACT_ORIGIN (instance);
+  return NULL_TREE;
+}
+
 /* Returns a hash value for X (which really is a die_struct).  */
 
 inline hashval_t
@@ -6038,11 +6062,26 @@ equate_block_to_die (tree block, dw_die_ref die)
    DECL, otherwise return false.  */
 
 static bool
-dwarf2out_die_ref_for_decl (tree decl, const char **sym,
+dwarf2out_die_ref_for_decl (tree decl, debug_external_ref_kind kind,
+                           const char **sym,
                            unsigned HOST_WIDE_INT *off)
 {
   dw_die_ref die;
 
+  if (kind == DEBUG_ABSTRACT_ORIGIN_REF)
+    {
+      if (!external_abstract_origin_map)
+       return false;
+      external_abstract_origin_ref *desc
+       = external_abstract_origin_map->get (decl);
+      if (!desc || desc->origin != abstract_origin_for_instance (decl))
+       return false;
+      *sym = desc->sym;
+      *off = desc->off;
+      return true;
+    }
+
+  gcc_checking_assert (kind == DEBUG_DIE_REF);
   if (in_lto_p)
     {
       /* During WPA stage and incremental linking we use a hash-map
@@ -6105,12 +6144,28 @@ add_AT_external_die_ref (dw_die_ref die, enum 
dwarf_attribute attr_kind,
    at SYMBOL + OFFSET which contains attributes dumped early.  */
 
 static void
-dwarf2out_register_external_die (tree decl, const char *sym,
+dwarf2out_register_external_die (tree decl, debug_external_ref_kind kind,
+                                const char *sym,
                                 unsigned HOST_WIDE_INT off)
 {
   if (debug_info_level == DINFO_LEVEL_NONE)
     return;
 
+  if (kind == DEBUG_ABSTRACT_ORIGIN_REF)
+    {
+      if (!external_abstract_origin_map)
+       external_abstract_origin_map
+         = hash_map<tree, external_abstract_origin_ref>::create_ggc (1000);
+      gcc_checking_assert (!external_abstract_origin_map->get (decl));
+      external_abstract_origin_ref p
+       = { abstract_origin_for_instance (decl),
+           IDENTIFIER_POINTER (get_identifier (sym)), off };
+      gcc_checking_assert (p.origin);
+      external_abstract_origin_map->put (decl, p);
+      return;
+    }
+
+  gcc_checking_assert (kind == DEBUG_DIE_REF);
   if (!external_die_map)
     external_die_map = hash_map<tree, sym_off_pair>::create_ggc (1000);
   gcc_checking_assert (!external_die_map->get (decl));
@@ -22368,10 +22423,24 @@ add_prototyped_attribute (dw_die_ref die, tree 
func_type)
    the block mapping.  */
 
 static inline void
-add_abstract_origin_attribute (dw_die_ref die, tree origin)
+add_abstract_origin_attribute (dw_die_ref die, tree origin, tree instance)
 {
   dw_die_ref origin_die = NULL;
 
+  /* A mergeable tree can denote more than one physical early-DWARF
+     instance.  Prefer the reference recorded for this particular origin
+     edge when LTO streaming preserved one.  */
+  const char *sym;
+  unsigned HOST_WIDE_INT off;
+  if (in_lto_p
+      && instance
+      && dwarf2out_die_ref_for_decl (instance,
+                                    DEBUG_ABSTRACT_ORIGIN_REF, &sym, &off))
+    {
+      add_AT_external_die_ref (die, DW_AT_abstract_origin, sym, off);
+      return;
+    }
+
   /* For late LTO debug output we want to refer directly to the abstract
      DIE in the early debug rather to the possibly existing concrete
      instance and avoid creating that just for this purpose.  */
@@ -23198,7 +23267,7 @@ gen_entry_point_die (tree decl, dw_die_ref context_die)
   dw_die_ref decl_die = new_die (DW_TAG_entry_point, context_die, decl);
 
   if (origin != NULL)
-    add_abstract_origin_attribute (decl_die, origin);
+    add_abstract_origin_attribute (decl_die, origin, decl);
   else
     {
       add_name_and_src_coords_attributes (decl_die, decl);
@@ -23473,7 +23542,7 @@ gen_formal_parameter_die (tree node, tree origin, bool 
emit_name_p,
        goto add_location;
 
       if (origin != NULL)
-       add_abstract_origin_attribute (parm_die, origin);
+       add_abstract_origin_attribute (parm_die, origin, node);
       else if (emit_name_p)
        add_name_and_src_coords_attributes (parm_die, node);
       if (origin == NULL
@@ -24071,7 +24140,7 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
       else
        {
          subr_die = new_die (DW_TAG_subprogram, context_die, decl);
-         add_abstract_origin_attribute (subr_die, origin);
+         add_abstract_origin_attribute (subr_die, origin, decl);
          /*  This is where the actual code for a cloned function is.
              Let's emit linkage name attribute for it.  This helps
              debuggers to e.g, set breakpoints into
@@ -25099,7 +25168,7 @@ gen_variable_die (tree decl, tree origin, dw_die_ref 
context_die)
     var_die = new_die (DW_TAG_variable, context_die, decl);
 
   if (origin != NULL)
-    add_abstract_origin_attribute (var_die, origin);
+    add_abstract_origin_attribute (var_die, origin, decl);
 
   /* Loop unrolling can create multiple blocks that refer to the same
      static variable, so we must test for the DW_AT_declaration flag.
@@ -25272,7 +25341,7 @@ gen_label_die (tree decl, dw_die_ref context_die)
       equate_decl_number_to_die (decl, lbl_die);
 
       if (origin != NULL)
-       add_abstract_origin_attribute (lbl_die, origin);
+       add_abstract_origin_attribute (lbl_die, origin, decl);
       else
        add_name_and_src_coords_attributes (lbl_die, decl);
     }
@@ -25496,7 +25565,7 @@ gen_lexical_block_die (tree stmt, dw_die_ref 
context_die)
 
       tree origin = block_ultimate_origin (stmt);
       if (origin != NULL_TREE && (origin != stmt || old_die))
-       add_abstract_origin_attribute (stmt_die, origin);
+       add_abstract_origin_attribute (stmt_die, origin, stmt);
 
       old_die = NULL;
     }
@@ -25531,7 +25600,7 @@ gen_inlined_subroutine_die (tree stmt, dw_die_ref 
context_die)
 
   if (call_arg_locations || debug_inline_points)
     equate_block_to_die (stmt, subr_die);
-  add_abstract_origin_attribute (subr_die, decl);
+  add_abstract_origin_attribute (subr_die, decl, stmt);
   if (TREE_ASM_WRITTEN (stmt))
     add_high_low_attributes (stmt, subr_die);
   add_call_src_coords_attributes (stmt, subr_die);
diff --git a/gcc/lto-streamer-in.cc b/gcc/lto-streamer-in.cc
index 901c3212656..6230a673171 100644
--- a/gcc/lto-streamer-in.cc
+++ b/gcc/lto-streamer-in.cc
@@ -1802,9 +1802,24 @@ lto_read_tree_1 (class lto_input_block *ib, class 
data_in *data_in, tree expr)
       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);
+         unsigned HOST_WIDE_INT off_and_kind = streamer_read_uhwi (ib);
+         debug_external_ref_kind ref_kind
+           = ((off_and_kind & 1)
+              ? DEBUG_ABSTRACT_ORIGIN_REF : DEBUG_DIE_REF);
+         unsigned HOST_WIDE_INT off = off_and_kind / 2;
+         if (ref_kind == DEBUG_ABSTRACT_ORIGIN_REF)
+           {
+             /* Origin-edge references are keyed by a concrete TU-local owner.
+                Register them directly.  DREF_QUEUE may canonicalize its key to
+                a prevailing tree and is only appropriate for self-DIE
+                references.  */
+             debug_hooks->register_external_die (expr, ref_kind, str, off);
+           }
+         else
+           {
+             dref_entry e = { expr, str, off };
+             dref_queue.safe_push (e);
+           }
        }
       /* When there's no early DIE to refer to but dwarf2out set up
         things in a way to expect that fixup.  This tends to happen
@@ -2018,7 +2033,8 @@ lto_input_tree (class lto_input_block *ib, class data_in 
*data_in)
       while (!dref_queue.is_empty ())
        {
          dref_entry e = dref_queue.pop ();
-         debug_hooks->register_external_die (e.decl, e.sym, e.off);
+         debug_hooks->register_external_die (e.decl, DEBUG_DIE_REF,
+                                             e.sym, e.off);
        }
     }
   tree t = lto_input_tree_1 (ib, data_in, tag, 0);
@@ -2026,7 +2042,8 @@ lto_input_tree (class lto_input_block *ib, class data_in 
*data_in)
   if (!dref_queue.is_empty ())
     {
       dref_entry e = dref_queue.pop ();
-      debug_hooks->register_external_die (e.decl, e.sym, e.off);
+      debug_hooks->register_external_die (e.decl, DEBUG_DIE_REF,
+                                         e.sym, e.off);
       gcc_checking_assert (dref_queue.is_empty ());
     }
   return t;
diff --git a/gcc/lto-streamer-out.cc b/gcc/lto-streamer-out.cc
index 7afc2673ea2..f43acc1fcd2 100644
--- a/gcc/lto-streamer-out.cc
+++ b/gcc/lto-streamer-out.cc
@@ -726,11 +726,50 @@ lto_write_tree_1 (struct output_block *ob, tree expr, 
bool ref_p)
     {
       const char *sym;
       unsigned HOST_WIDE_INT off;
-      if (debug_info_level > DINFO_LEVEL_NONE
-         && debug_hooks->die_ref_for_decl (expr, &sym, &off))
+
+      bool have_ref = false;
+      debug_external_ref_kind ref_kind = DEBUG_DIE_REF;
+      if (debug_info_level > DINFO_LEVEL_NONE)
+       have_ref = debug_hooks->die_ref_for_decl (expr, DEBUG_DIE_REF,
+                                                 &sym, &off);
+
+      /* A global function tree can be shared by several input TUs, but an
+        abstract-origin edge can refer to a particular early-DWARF instance.
+        Preserve that edge separately for TU-local function clones and blocks
+        that have no early DIE of their own.  */
+      tree origin = (DECL_P (expr)
+                    ? DECL_ABSTRACT_ORIGIN (expr)
+                    : BLOCK_ABSTRACT_ORIGIN (expr));
+      bool local_owner
+       = (TREE_CODE (expr) == BLOCK
+          || (TREE_CODE (expr) == FUNCTION_DECL
+              && !TREE_PUBLIC (expr) && !DECL_EXTERNAL (expr)));
+      bool have_origin_ref = false;
+      if (!have_ref
+         && debug_info_level > DINFO_LEVEL_NONE)
+       {
+         have_origin_ref = debug_hooks->die_ref_for_decl
+           (expr, DEBUG_ABSTRACT_ORIGIN_REF, &sym, &off);
+         if (!have_origin_ref
+             && !in_lto_p
+             && local_owner
+             && origin && origin != expr
+             && TREE_CODE (origin) == FUNCTION_DECL
+             && (TREE_PUBLIC (origin) || DECL_EXTERNAL (origin)))
+           have_origin_ref = debug_hooks->die_ref_for_decl
+             (origin, DEBUG_DIE_REF, &sym, &off);
+       }
+      if (have_origin_ref)
+       {
+         have_ref = true;
+         ref_kind = DEBUG_ABSTRACT_ORIGIN_REF;
+       }
+
+      if (have_ref)
        {
          streamer_write_string (ob, ob->main_stream, sym, true);
-         streamer_write_uhwi (ob, off);
+         streamer_write_uhwi
+           (ob, off * 2 + (ref_kind == DEBUG_ABSTRACT_ORIGIN_REF));
        }
       else
        streamer_write_string (ob, ob->main_stream, NULL, true);
diff --git a/gcc/lto-streamer.h b/gcc/lto-streamer.h
index d1285b87bc5..457c79d1298 100644
--- a/gcc/lto-streamer.h
+++ b/gcc/lto-streamer.h
@@ -122,7 +122,7 @@ along with GCC; see the file COPYING3.  If not see
      form followed by the data for the string.  */
 
 #define LTO_major_version GCC_major_version
-#define LTO_minor_version 0
+#define LTO_minor_version 1
 
 typedef unsigned char  lto_decl_flags_t;
 
diff --git a/gcc/lto/lto-common.cc b/gcc/lto/lto-common.cc
index 96bd97f1374..440c8ee3885 100644
--- a/gcc/lto/lto-common.cc
+++ b/gcc/lto/lto-common.cc
@@ -1675,8 +1675,8 @@ process_dref (class data_in *data_in, tree t, unsigned 
from)
                  == streamer_tree_cache_get_tree (cache, from));
       const char *sym;
       unsigned HOST_WIDE_INT off;
-      if (!debug_hooks->die_ref_for_decl (t, &sym, &off))
-       debug_hooks->register_external_die (t, e.sym, e.off);
+      if (!debug_hooks->die_ref_for_decl (t, DEBUG_DIE_REF, &sym, &off))
+       debug_hooks->register_external_die (t, DEBUG_DIE_REF, e.sym, e.off);
     }
 }
 
@@ -1972,7 +1972,8 @@ lto_read_decls (struct lto_file_decl_data *decl_data, 
const void *data,
          while (!dref_queue.is_empty ())
            {
              dref_entry e = dref_queue.pop ();
-             debug_hooks->register_external_die (e.decl, e.sym, e.off);
+             debug_hooks->register_external_die (e.decl, DEBUG_DIE_REF,
+                                                 e.sym, e.off);
            }
 
          if (seen_type)
diff --git a/gcc/testsuite/g++.dg/lto/pr126348-2_0.C 
b/gcc/testsuite/g++.dg/lto/pr126348-2_0.C
new file mode 100644
index 00000000000..7dd5a3057b4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr126348-2_0.C
@@ -0,0 +1,17 @@
+/* PR debug/126348 */
+/* Verify that nested inlined subroutines and their direct children retain
+   the same input TU provenance.  */
+/* { dg-lto-do link } */
+/* { dg-skip-if "No DWARF debug support" { hppa*-*-hpux* } } */
+/* { dg-skip-if "AIX DWARF5" { powerpc-ibm-aix* } } */
+/* { dg-lto-options { { -O2 -g -gdwarf-5 -dA -save-temps -flto 
-flto-partition=one -fno-ipa-icf --param=max-inline-insns-auto=100 } } } */
+/* { dg-extra-ld-options "-r -nostdlib -flinker-output=nolto-rel" } */
+/* { dg-final { scan-lto-assembler 
"DW_TAG_inlined_subroutine\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,10}\[^\n\]*DW_TAG_formal_parameter\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_variable\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,3}\[^\n\]*DW_TAG_inlined_subroutine\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,10}\[^\n\]*DW_TAG_formal_parameter\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_variable\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_2_1\[^\n\]*DW_AT_abstract_origin"
 } } */
+
+#include "pr126348.h"
+
+__attribute__ ((noinline)) int
+one (const Buffer &buffer)
+{
+  return buffer.printable_length ();
+}
diff --git a/gcc/testsuite/g++.dg/lto/pr126348-2_1.C 
b/gcc/testsuite/g++.dg/lto/pr126348-2_1.C
new file mode 100644
index 00000000000..5e871bdc800
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr126348-2_1.C
@@ -0,0 +1,21 @@
+#include "pr126348.h"
+
+__attribute__ ((noinline)) int one (const Buffer &);
+
+void
+fail (int, const char *, const Location &)
+{
+  __builtin_trap ();
+}
+
+void
+note (const char *, int)
+{
+  asm volatile ("" ::: "memory");
+}
+
+__attribute__ ((noinline)) int
+two (const Buffer &buffer)
+{
+  return buffer.printable_length ();
+}
diff --git a/gcc/testsuite/g++.dg/lto/pr126348-2_2.C 
b/gcc/testsuite/g++.dg/lto/pr126348-2_2.C
new file mode 100644
index 00000000000..f540eef4802
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr126348-2_2.C
@@ -0,0 +1,11 @@
+#include "pr126348.h"
+
+__attribute__ ((noinline)) int one (const Buffer &);
+__attribute__ ((noinline)) int two (const Buffer &);
+
+int
+main (int argc, char **)
+{
+  Buffer buffer = { argc };
+  return one (buffer) + two (buffer);
+}
diff --git a/gcc/testsuite/g++.dg/lto/pr126348.h 
b/gcc/testsuite/g++.dg/lto/pr126348.h
new file mode 100644
index 00000000000..eac6698064a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr126348.h
@@ -0,0 +1,28 @@
+struct Location
+{
+  const char *file;
+  const char *function;
+  int line;
+};
+
+extern void fail (int, const char *, const Location &)
+  __attribute__ ((noreturn, cold, noipa));
+extern void note (const char *, int) __attribute__ ((cold, noipa));
+
+static const char source_file[] = __BASE_FILE__;
+
+struct Buffer
+{
+  int length;
+
+  int printable_length () const
+  {
+    if (__builtin_expect (length < 1024, 1))
+      return length;
+    const Location location = { source_file, __func__, __LINE__ };
+    note (location.file, location.line);
+    note (location.function, length);
+    note (location.file, length + 1);
+    fail (3, "length < 1024", location);
+  }
+};
diff --git a/gcc/testsuite/g++.dg/lto/pr126348_0.C 
b/gcc/testsuite/g++.dg/lto/pr126348_0.C
new file mode 100644
index 00000000000..99387845ae2
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr126348_0.C
@@ -0,0 +1,27 @@
+/* PR debug/126348 */
+/* Verify that an out-of-line split function and its direct children retain
+   the same input TU provenance, and that the function has an address-bearing
+   late DIE.  */
+/* { dg-lto-do link } */
+/* { dg-skip-if "No DWARF debug support" { hppa*-*-hpux* } } */
+/* { dg-skip-if "AIX DWARF5" { powerpc-ibm-aix* } } */
+/* { dg-lto-options { { -O2 -g -gdwarf-5 -dA -save-temps -flto 
-flto-partition=one -fno-ipa-icf --param=max-inline-insns-auto=100 } } } */
+/* { dg-extra-ld-options "-r -nostdlib -flinker-output=nolto-rel" } */
+/* { dg-final { scan-lto-assembler 
"DW_TAG_subprogram\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_0\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,4}\[^\n\]*DW_AT_(?:low_pc|ranges)(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_formal_parameter\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_0\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,4}\[^\n\]*DW_TAG_variable\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_0\[^\n\]*DW_AT_abstract_origin"
 } } */
+/* { dg-final { scan-lto-assembler 
"DW_TAG_subprogram\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,4}\[^\n\]*DW_AT_(?:low_pc|ranges)(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_formal_parameter\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,4}\[^\n\]*DW_TAG_variable\\)(?:\[^\n\]*\n){1,6}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin"
 } } */
+
+/* { dg-final { scan-lto-assembler 
"DW_TAG_inlined_subroutine\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,10}\[^\n\]*DW_TAG_formal_parameter\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin(?:\[^\n\]*\n){1,8}\[^\n\]*DW_TAG_variable\\)(?:\[^\n\]*\n){1,3}\[^\n\]*pr126348_1\[^\n\]*DW_AT_abstract_origin"
 } } */
+
+#include "pr126348.h"
+
+__attribute__ ((noinline)) int
+one (const Buffer &buffer)
+{
+  return buffer.printable_length ();
+}
+
+__attribute__ ((noinline)) int
+one_extra (const Buffer &buffer)
+{
+  return buffer.printable_length ();
+}
diff --git a/gcc/testsuite/g++.dg/lto/pr126348_1.C 
b/gcc/testsuite/g++.dg/lto/pr126348_1.C
new file mode 100644
index 00000000000..bad1a25aa63
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr126348_1.C
@@ -0,0 +1,28 @@
+#include "pr126348.h"
+
+__attribute__ ((noinline)) int one (const Buffer &);
+__attribute__ ((noinline)) int one_extra (const Buffer &);
+
+void
+fail (int, const char *, const Location &)
+{
+  __builtin_trap ();
+}
+
+void
+note (const char *, int)
+{
+  asm volatile ("" ::: "memory");
+}
+
+__attribute__ ((noinline)) int
+two (const Buffer &buffer)
+{
+  return buffer.printable_length ();
+}
+
+__attribute__ ((noinline)) int
+two_extra (const Buffer &buffer)
+{
+  return buffer.printable_length ();
+}
diff --git a/gcc/testsuite/g++.dg/lto/pr126348_2.C 
b/gcc/testsuite/g++.dg/lto/pr126348_2.C
new file mode 100644
index 00000000000..7a4015f3b49
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lto/pr126348_2.C
@@ -0,0 +1,14 @@
+#include "pr126348.h"
+
+__attribute__ ((noinline)) int one (const Buffer &);
+__attribute__ ((noinline)) int one_extra (const Buffer &);
+__attribute__ ((noinline)) int two (const Buffer &);
+__attribute__ ((noinline)) int two_extra (const Buffer &);
+
+int
+main (int argc, char **)
+{
+  Buffer buffer = { argc };
+  return one (buffer) + one_extra (buffer)
+        + two (buffer) + two_extra (buffer);
+}
diff --git a/gcc/tree-inline.cc b/gcc/tree-inline.cc
index 8162b1f5051..467b69c49be 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -798,6 +798,39 @@ remap_decls (tree decls, vec<tree, va_gc> 
**nonlocalized_list,
   return nreverse (new_decls);
 }
 
+/* Preserve early-DIE provenance when an LTO transformation copies FROM's
+   abstract origin to TO.  */
+
+static void
+copy_lto_abstract_origin_ref (tree to, tree from)
+{
+  if (!in_lto_p)
+    return;
+
+  tree origin;
+  if (DECL_P (from))
+    origin = DECL_ABSTRACT_ORIGIN (from);
+  else
+    {
+      gcc_checking_assert (TREE_CODE (from) == BLOCK);
+      origin = BLOCK_ABSTRACT_ORIGIN (from);
+    }
+
+  const char *sym;
+  unsigned HOST_WIDE_INT off;
+  bool have_ref
+    = debug_hooks->die_ref_for_decl (from, DEBUG_ABSTRACT_ORIGIN_REF,
+                                    &sym, &off);
+  /* Without an abstract origin TO points directly to FROM, so FROM's own
+     early DIE is the origin DIE to preserve.  */
+  if (!have_ref && (!origin || origin == from))
+    have_ref = debug_hooks->die_ref_for_decl (from, DEBUG_DIE_REF,
+                                             &sym, &off);
+  if (have_ref)
+    debug_hooks->register_external_die (to, DEBUG_ABSTRACT_ORIGIN_REF,
+                                       sym, off);
+}
+
 /* Copy the BLOCK to contain remapped versions of the variables
    therein.  And hook the new block into the block-tree.  */
 
@@ -812,6 +845,7 @@ remap_block (tree *block, copy_body_data *id)
   new_block = make_node (BLOCK);
   TREE_USED (new_block) = TREE_USED (old_block);
   BLOCK_ABSTRACT_ORIGIN (new_block) = BLOCK_ORIGIN (old_block);
+  copy_lto_abstract_origin_ref (new_block, old_block);
   BLOCK_SOURCE_LOCATION (new_block) = BLOCK_SOURCE_LOCATION (old_block);
   BLOCK_NONLOCALIZED_VARS (new_block)
     = vec_safe_copy (BLOCK_NONLOCALIZED_VARS (old_block));
@@ -5054,6 +5088,7 @@ expand_call_inline (basic_block bb, gimple *stmt, 
copy_body_data *id,
                (loc, get_discriminator_from_loc (gimple_location (stmt)));
       id->block = make_node (BLOCK);
       BLOCK_ABSTRACT_ORIGIN (id->block) = DECL_ORIGIN (fn);
+      copy_lto_abstract_origin_ref (id->block, fn);
       BLOCK_SOURCE_LOCATION (id->block) = loc;
       prepend_lexical_block (block, id->block);
     }
@@ -6381,6 +6416,7 @@ tree_function_versioning (tree old_decl, tree new_decl,
 
   DECL_ARTIFICIAL (new_decl) = 1;
   DECL_ABSTRACT_ORIGIN (new_decl) = DECL_ORIGIN (old_decl);
+  copy_lto_abstract_origin_ref (new_decl, old_decl);
   if (DECL_ORIGIN (old_decl) == old_decl)
     old_version_node->used_as_abstract_origin = true;
   DECL_FUNCTION_PERSONALITY (new_decl) = DECL_FUNCTION_PERSONALITY (old_decl);
-- 
2.55.0

Reply via email to