diff mbox

[v1,3/3] dwarf: purge DIEs for unreferenced extern globals.

Message ID 20170712154226.25508-4-snaipe@arista.com
State New
Headers show

Commit Message

Franklin “Snaipe” Mathieu July 12, 2017, 3:42 p.m. UTC
From: Franklin “Snaipe” Mathieu <snaipe@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  <snaipe@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  <snaipe@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  <snaipe@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  <snaipe@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 mbox

Patch

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;
+}