From: Franklin “Snaipe” Mathieu <sna...@diacritic.io> Due to an earlier change in gcc that split the dwarf info generation in two steps (one early, one late), the DIE for unreferenced extern globals are no longer removed (in fact, they didn't emit it at all since they had already processed the translation unit and knew whether or not a variable was referenced). This is no longer the case during the early generation.
This change addresses this problem by revisiting during the late stage global declarations on the C side, and for each namespace on the C++ side, removing DIEs when they are unreferenced in both cases. gcc/c/ChangeLog: 2017-07-12 Franklin “Snaipe” Mathieu <sna...@diacritic.io> PR debug/81135 * c-decl.c (c_parse_final_cleanups): Call the late_global_decl hook for each global in the extern block. gcc/cp/ChangeLog: 2017-07-12 Franklin “Snaipe” Mathieu <sna...@diacritic.io> PR debug/81135 * decl2.c (c_parse_final_cleanups): Call the late_global_decl for each extern variable in each namespace. (purge_unused_extern_globals): New. gcc/ChangeLog: 2017-07-12 Franklin “Snaipe” Mathieu <sna...@diacritic.io> PR debug/81135 * dwarf2out: Remove DIEs for unreferenced externs. (struct die_struct): Add removed field. (lookup_decl_die): Remove DIEs marked for removal. (mark_removed): New. (dwarf2out_late_global_decl): Refactor to remove DIE from the sibling list, and actually check that the code filling new new location information acts on the same conditions as it had when it was called from the symtab code. (dwarf2out_imported_module_or_decl_1): make the referenced DIE perennial to avoid it being removed when deemed unused, as it would be referenced by a DW_TAG_imported_declaration entry. gcc/testsuite/ChangeLog: 2017-07-12 Franklin “Snaipe” Mathieu <sna...@diacritic.io> PR debug/81135 gcc.dg/debug/dwarf2/pr81135.c: New test. g++.dg/debug/dwarf2/pr81135.C: New test. --- gcc/c/c-decl.c | 10 +++ gcc/cp/decl2.c | 27 ++++++++ gcc/dwarf2out.c | 101 +++++++++++++++++++++++----- gcc/testsuite/g++.dg/debug/dwarf2/pr81135.C | 25 +++++++ gcc/testsuite/gcc.dg/debug/dwarf2/pr81135.C | 13 ++++ 5 files changed, 159 insertions(+), 17 deletions(-) create mode 100644 gcc/testsuite/g++.dg/debug/dwarf2/pr81135.C create mode 100644 gcc/testsuite/gcc.dg/debug/dwarf2/pr81135.C diff --git a/gcc/c/c-decl.c b/gcc/c/c-decl.c index e8bafed..69efb04 100644 --- a/gcc/c/c-decl.c +++ b/gcc/c/c-decl.c @@ -10900,6 +10900,16 @@ c_parse_final_cleanups (void) c_write_global_declarations_1 (BLOCK_VARS (ext_block)); timevar_stop (TV_PHASE_DEFERRED); + + /* Purge unreferenced extern variables from the debug information. */ + if (!seen_error ()) + { + tree decl; + for (decl = BLOCK_VARS (ext_block); decl; decl = DECL_CHAIN (decl)) + if (DECL_EXTERNAL (decl)) + (*debug_hooks->late_global_decl) (decl); + } + timevar_start (TV_PHASE_PARSING); ext_block = NULL; diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c index a9511de..efbe2f6 100644 --- a/gcc/cp/decl2.c +++ b/gcc/cp/decl2.c @@ -41,6 +41,7 @@ along with GCC; see the file COPYING3. If not see #include "calls.h" #include "decl.h" #include "toplev.h" +#include "debug.h" #include "c-family/c-objc.h" #include "c-family/c-pragma.h" #include "dumpfile.h" @@ -4523,6 +4524,27 @@ lower_var_init () } } +/* We call this routine on each namespace to remove unreferenced extern + variables from the debug information. */ + +static int +purge_unused_extern_globals (tree name_space, void *data ATTRIBUTE_UNUSED) +{ + cp_binding_level *level = NAMESPACE_LEVEL (name_space); + tree decl; + + if (seen_error ()) + return 1; + + if (!level) + return 0; + + for (decl = level->names; decl; decl = DECL_CHAIN (decl)) + if (TREE_CODE (decl) == VAR_DECL && DECL_EXTERNAL (decl)) + (*debug_hooks->late_global_decl) (decl); + return 0; +} + /* This routine is called at the end of compilation. Its job is to create all the code needed to initialize and destroy the global aggregates. We do the destruction @@ -4925,6 +4947,11 @@ c_parse_final_cleanups (void) } timevar_stop (TV_PHASE_DEFERRED); + + /* Perform a late round of debugging information, to remove unreferenced + extern variables from the outputted information. */ + walk_namespaces (purge_unused_extern_globals, NULL); + timevar_start (TV_PHASE_PARSING); /* Indicate that we're done with front end processing. */ diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c index f241073..d321e56 100644 --- a/gcc/dwarf2out.c +++ b/gcc/dwarf2out.c @@ -62,6 +62,7 @@ along with GCC; see the file COPYING3. If not see #include "function.h" #include "rtl.h" #include "tree.h" +#include "cp/cp-tree.h" #include "tm_p.h" #include "stringpool.h" #include "insn-config.h" @@ -2709,6 +2710,7 @@ typedef struct GTY((chain_circular ("%h.die_sib"), for_user)) die_struct { BOOL_BITFIELD die_perennial_p : 1; BOOL_BITFIELD comdat_type_p : 1; /* DIE has a type signature */ /* Lots of spare bits. */ + BOOL_BITFIELD removed : 1; /* DIE is marked for removal. */ } die_node; @@ -5159,7 +5161,16 @@ decl_die_hasher::equal (die_node *x, tree y) 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; } /* Returns a hash value for X (which really is a var_loc_list). */ @@ -23750,6 +23761,14 @@ dwarf2out_early_global_decl (tree decl) symtab->global_info_ready = save; } +static void +mark_removed (dw_die_ref die) +{ + dw_die_ref c; + die->removed = true; + FOR_EACH_CHILD (die, c, mark_removed (c)); +} + /* Output debug information for global decl DECL. Called from toplev.c after compilation proper has finished. */ @@ -23760,23 +23779,67 @@ dwarf2out_late_global_decl (tree decl) if (in_lto_p) dwarf2out_early_global_decl (decl); - /* Fill-in any location information we were unable to determine - on the first pass. */ - if (TREE_CODE (decl) == VAR_DECL - && !POINTER_BOUNDS_P (decl)) + if (TREE_CODE (decl) != VAR_DECL || POINTER_BOUNDS_P (decl)) + return; + + dw_die_ref die = lookup_decl_die (decl); + + /* Discard this VAR_DECL if it refers to a file-scope + (or namespace-scope) extern data object declaration and if the + declaration was never even referenced from within this entire + compilation unit. We suppress these DIEs in order to save space + in the .debug section (by eliminating entries which are probably + useless). Note that we must not suppress block-local extern and + static member declarations (whether used or not) because that + would screw-up the debugger's name lookup mechanism and cause + it to miss things which really ought to be in scope at a + given point. */ + if (die && ! die->die_perennial_p + && DECL_EXTERNAL (decl) + && ! DECL_CLASS_SCOPE_P (decl) + && ! TREE_USED (decl)) { - dw_die_ref die = lookup_decl_die (decl); - if (die) - { - /* We get called via the symtab code invoking late_global_decl - for symbols that are optimized out. Do not add locations - for those. */ - varpool_node *node = varpool_node::get (decl); - if (! node || ! node->definition) - tree_add_const_value_attribute_for_decl (die, decl); - else - add_location_or_const_value_attribute (die, decl, false); - } + mark_removed (die); + + dw_die_ref next = die->die_sib; + if (die == die->die_sib) + next = NULL; + + dw_die_ref *ptail = &die->die_parent->die_child; + dw_die_ref prev = (*ptail)->die_sib; + while (prev->die_sib != die) + prev = prev->die_sib; + prev->die_sib = next; + + if (prev == die) + prev = NULL; + if (*ptail == die) + *ptail = prev; + + die->die_parent = NULL; + die->die_sib = NULL; + + /* Die has been removed, so we pretend we couldn't find it in + the first place. */ + die = NULL; + } + + if (! die) + return; + + /* Fill-in any location information we were unable to determine + on the first pass. */ + varpool_node *node = varpool_node::get (decl); + if ((! node || ! node->in_other_partition) + && !DECL_EXTERNAL (decl)) + { + /* We get called via the symtab code invoking late_global_decl + for symbols that are optimized out. Do not add locations + for those. */ + if (! node || ! node->definition) + tree_add_const_value_attribute_for_decl (die, decl); + else + add_location_or_const_value_attribute (die, decl, false); } } @@ -23878,6 +23941,10 @@ dwarf2out_imported_module_or_decl_1 (tree decl, add_AT_string (imported_die, DW_AT_name, IDENTIFIER_POINTER (name)); add_AT_die_ref (imported_die, DW_AT_import, at_import_die); + + /* To avoid issues with the unreferenced extern removal, we mark the imported + DIE as perennial so it won't be considered for removal. */ + at_import_die->die_perennial_p = 1; } /* Output debug information for imported module or decl DECL. diff --git a/gcc/testsuite/g++.dg/debug/dwarf2/pr81135.C b/gcc/testsuite/g++.dg/debug/dwarf2/pr81135.C new file mode 100644 index 0000000..84cf557 --- /dev/null +++ b/gcc/testsuite/g++.dg/debug/dwarf2/pr81135.C @@ -0,0 +1,25 @@ +// { dg-do compile } +// { dg-options "-gdwarf-2 -dA" } +// { dg-final { scan-assembler "DW_TAG_variable\[^.\]*\.ascii \"i..\"" } } +// { dg-final { scan-assembler "DW_TAG_variable\[^.\]*\.ascii \"m..\"" } } +// { dg-final { scan-assembler-not "DW_TAG_variable\[^.\]*\.ascii \"j..\"" } } +// { dg-final { scan-assembler-not "DW_TAG_variable\[^.\]*\.ascii \"k..\"" } } +// { dg-final { scan-assembler-not "DW_TAG_variable\[^.\]*\.ascii \"l..\"" } } + +extern int i; +extern int j; + +namespace { + extern int k; +} + +namespace foo { + extern int l; + extern int m; +} + +int +main (void) +{ + return i + foo::m; +} diff --git a/gcc/testsuite/gcc.dg/debug/dwarf2/pr81135.C b/gcc/testsuite/gcc.dg/debug/dwarf2/pr81135.C new file mode 100644 index 0000000..5902bd3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/debug/dwarf2/pr81135.C @@ -0,0 +1,13 @@ +// { dg-do compile } +// { dg-options "-gdwarf-2 -dA" } +// { dg-final { scan-assembler "DW_TAG_variable\[^.\]*\.ascii \"i..\"" } } +// { dg-final { scan-assembler-not "DW_TAG_variable\[^.\]*\.ascii \"j..\"" } } + +extern int i; +extern int j; + +int +main (void) +{ + return i; +} -- Franklin "Snaipe" Mathieu Arista Networks, Ltd