In LTO, replacing an instance's origin tree can redirect the tree-to-DIE
lookup to another input's DIE. Keep the instance's saved origin separate
from the declaration's DIE reference.
Add a kind parameter to the external DIE hooks and pass the instance to
add_abstract_origin_attribute. Prefer its saved reference, falling back
to the origin tree's DIE when none is saved.
Copy saved origins during LTO cloning and inlining. Clear the
destination's saved reference when the source has none.
Tested on x86_64-pc-linux-gnu with a C/C++ checking build and
the LTO suites (gcc.dg/lto: 2012, g++.dg/lto: 1849 expected passes),
with no unexpected failures.
gcc/ChangeLog:
PR debug/126348
* debug.h (debug_external_ref_kind): New enum.
(gcc_debug_hooks): Add the kind parameter and document saved references.
(debug_false_tree_charstarstar_uhwistar,
debug_nothing_tree_charstar_uhwi): Add the kind parameter.
(copy_debug_abstract_origin): Declare.
* debug.cc (debug_false_tree_charstarstar_uhwistar,
debug_nothing_tree_charstar_uhwi): Adjust signatures.
(copy_debug_abstract_origin): New function.
* dwarf2out.cc (saved_die_ref, saved_die_ref_traits, saved_die_ref_map,
external_abstract_origin_map): New.
(dwarf2out_die_ref_for_decl, dwarf2out_register_external_die): Take
the kind parameter. Query and register saved origins separately from
declaration references.
(add_abstract_origin_attribute): Take the instance and prefer its saved
origin. Fall back to the origin tree's DIE.
(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 instance tree.
* cgraphclones.cc: Include debug.h.
(cgraph_node::create_virtual_clone): Copy the saved origin.
* tree-inline.cc (remap_block, expand_call_inline,
tree_function_versioning): Likewise.
* lto-streamer-in.cc (lto_input_tree): Specify DEBUG_DIE_REF.
* lto-streamer-out.cc (lto_write_tree_1): Likewise.
gcc/lto/ChangeLog:
PR debug/126348
* lto-common.cc (process_dref, lto_read_decls): Specify DEBUG_DIE_REF.
Signed-off-by: Longjun Luo <[email protected]>
---
gcc/cgraphclones.cc | 2 +
gcc/debug.cc | 21 +++++++-
gcc/debug.h | 32 ++++++++----
gcc/dwarf2out.cc | 106 +++++++++++++++++++++++++++++-----------
gcc/lto-streamer-in.cc | 6 ++-
gcc/lto-streamer-out.cc | 2 +-
gcc/lto/lto-common.cc | 7 +--
gcc/tree-inline.cc | 3 ++
8 files changed, 133 insertions(+), 46 deletions(-)
diff --git a/gcc/cgraphclones.cc b/gcc/cgraphclones.cc
index c5aead0c3b8..b7e42547876 100644
--- a/gcc/cgraphclones.cc
+++ b/gcc/cgraphclones.cc
@@ -78,6 +78,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-eh.h"
#include "tree-cfg.h"
#include "tree-inline.h"
+#include "debug.h"
#include "attribs.h"
#include "dumpfile.h"
#include "gimple-pretty-print.h"
@@ -687,6 +688,7 @@ cgraph_node::create_virtual_clone (const vec<cgraph_edge *>
&redirect_callers,
/* These pointers represent function body and will be populated only when
clone
is materialized. */
gcc_assert (new_decl != old_decl);
+ copy_debug_abstract_origin (new_decl, old_decl);
DECL_STRUCT_FUNCTION (new_decl) = NULL;
DECL_ARGUMENTS (new_decl) = NULL;
DECL_INITIAL (new_decl) = NULL;
diff --git a/gcc/debug.cc b/gcc/debug.cc
index 5e9c7c1a912..aaadc8c97d4 100644
--- a/gcc/debug.cc
+++ b/gcc/debug.cc
@@ -160,14 +160,31 @@ 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)
{
}
+
+/* Give DEST the saved abstract-origin reference of SOURCE, or none
+ if SOURCE has none. */
+
+void
+copy_debug_abstract_origin (tree dest, tree source)
+{
+ if (!in_lto_p)
+ return;
+
+ const char *sym = nullptr;
+ unsigned HOST_WIDE_INT off = 0;
+ debug_hooks->die_ref_for_decl (source, DEBUG_ABSTRACT_ORIGIN_REF, &sym,
&off);
+ debug_hooks->register_external_die
+ (dest, DEBUG_ABSTRACT_ORIGIN_REF, sym, off);
+}
diff --git a/gcc/debug.h b/gcc/debug.h
index e5976f25d5a..4e355b4e7bd 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. */
+
+/* Kinds of references to early DWARF DIEs. */
+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,15 @@ 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 a DIE reference of KIND is available for the tree,
+ and return its symbol and offset for external use. */
+ 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.
+ A null symbol clears a saved abstract-origin reference. */
+ 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 +242,12 @@ 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);
+
+extern void copy_debug_abstract_origin (tree, tree);
/* 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 d4402926f1e..35a5482beb7 100644
--- a/gcc/dwarf2out.cc
+++ b/gcc/dwarf2out.cc
@@ -2886,10 +2886,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. */
@@ -3927,7 +3927,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);
@@ -5978,6 +5978,21 @@ struct GTY(()) sym_off_pair
};
static GTY(()) hash_map<tree, sym_off_pair> *external_die_map;
+/* A distinct type avoids duplicate gengtype walkers for the ordinary
+ map and the weak cache. */
+struct GTY (()) saved_die_ref
+{
+ const char *sym;
+ unsigned HOST_WIDE_INT off;
+};
+
+struct saved_die_ref_traits
+ : simple_cache_map_traits<default_hash_traits<tree>, saved_die_ref> { };
+typedef hash_map<tree, saved_die_ref, saved_die_ref_traits> saved_die_ref_map;
+
+/* Saved references must not keep dead declarations or blocks alive. */
+static GTY ((cache)) saved_die_ref_map *external_abstract_origin_map;
+
/* Returns a hash value for X (which really is a die_struct). */
inline hashval_t
@@ -6037,16 +6052,30 @@ equate_block_to_die (tree block, dw_die_ref die)
#undef BLOCK_DIE
-/* For DECL which might have early dwarf output query a SYMBOL + OFFSET
- style reference. Return true if we found one referring to a DIE for
- DECL, otherwise return false. */
+/* Return the early-DWARF reference of KIND for DECL in SYM and OFF,
+ if available. */
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_DIE_REF)
+ {
+ /* Saved abstract origins are keyed by the instance, not by its
+ possibly merged origin tree. */
+ if (!external_abstract_origin_map)
+ return false;
+ saved_die_ref *ref = external_abstract_origin_map->get (decl);
+ if (!ref)
+ return false;
+ *sym = ref->sym;
+ *off = ref->off;
+ return true;
+ }
+
if (in_lto_p)
{
/* During WPA stage and incremental linking we use a hash-map
@@ -6105,16 +6134,31 @@ add_AT_external_die_ref (dw_die_ref die, enum
dwarf_attribute attr_kind,
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. */
+/* Record the early-DWARF reference of KIND for DECL at SYM + OFF. */
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_DIE_REF)
+ {
+ if (!sym)
+ {
+ if (external_abstract_origin_map)
+ external_abstract_origin_map->remove (decl);
+ return;
+ }
+ sym = IDENTIFIER_POINTER (get_identifier (sym));
+ if (!external_abstract_origin_map)
+ external_abstract_origin_map = saved_die_ref_map::create_ggc (127);
+ external_abstract_origin_map->put (decl, { sym, off });
+ return;
+ }
+
if (!external_die_map)
external_die_map = hash_map<tree, sym_off_pair>::create_ggc (1000);
gcc_checking_assert (!external_die_map->get (decl));
@@ -22372,20 +22416,26 @@ 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;
- /* 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. */
- sym_off_pair *desc;
+ /* In LTO, prefer INSTANCE's saved abstract-origin reference to
+ ORIGIN's DIE. Saved references are recorded for FUNCTION_DECL
+ and BLOCK instances whose origin trees may be replaced by merging.
+ Body-local parameters and automatic variables retain their input
+ origins and use ORIGIN's DIE. Keep this lookup also for instances
+ without a saved reference. Avoid creating a concrete DIE just to
+ refer to its abstract origin. */
+ const char *sym;
+ unsigned HOST_WIDE_INT off;
if (in_lto_p
- && external_die_map
- && (desc = external_die_map->get (origin)))
+ && ((instance
+ && dwarf2out_die_ref_for_decl (instance, DEBUG_ABSTRACT_ORIGIN_REF,
+ &sym, &off))
+ || dwarf2out_die_ref_for_decl (origin, DEBUG_DIE_REF, &sym, &off)))
{
- add_AT_external_die_ref (die, DW_AT_abstract_origin,
- desc->sym, desc->off);
+ add_AT_external_die_ref (die, DW_AT_abstract_origin, sym, off);
return;
}
@@ -23201,7 +23251,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);
@@ -23476,7 +23526,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
@@ -24074,7 +24124,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
@@ -25102,7 +25152,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.
@@ -25275,7 +25325,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);
}
@@ -25499,7 +25549,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;
}
@@ -25534,7 +25584,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 2e612188669..e0367dd7b8b 100644
--- a/gcc/lto-streamer-in.cc
+++ b/gcc/lto-streamer-in.cc
@@ -2017,7 +2017,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);
@@ -2025,7 +2026,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 6e34d4bea90..aa1d75fa7d0 100644
--- a/gcc/lto-streamer-out.cc
+++ b/gcc/lto-streamer-out.cc
@@ -727,7 +727,7 @@ 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))
+ && debug_hooks->die_ref_for_decl (expr, DEBUG_DIE_REF, &sym, &off))
{
streamer_write_string (ob, ob->main_stream, sym, true);
streamer_write_uhwi (ob, off);
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/tree-inline.cc b/gcc/tree-inline.cc
index 7d73d595907..8c5bf5b4810 100644
--- a/gcc/tree-inline.cc
+++ b/gcc/tree-inline.cc
@@ -806,6 +806,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_debug_abstract_origin (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));
@@ -5049,6 +5050,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_debug_abstract_origin (id->block, fn);
BLOCK_SOURCE_LOCATION (id->block) = loc;
prepend_lexical_block (block, id->block);
}
@@ -6376,6 +6378,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_debug_abstract_origin (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.53.0