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

Franklin “Snaipe” Mathieu snaipe@arista.com
Wed Jul 12 15:42:00 GMT 2017


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                              |  30 ++++++++
 gcc/dwarf2out.c                             | 107 ++++++++++++++++++++--------
 gcc/testsuite/g++.dg/debug/dwarf2/pr81135.C |  25 +++++++
 gcc/testsuite/gcc.dg/debug/dwarf2/pr81135.C |  13 ++++
 5 files changed, 156 insertions(+), 29 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 2acedac..2596474 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -11259,6 +11259,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 fd5622b..ac098e3 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -42,6 +42,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"
@@ -4439,6 +4440,31 @@ 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)
+{
+  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);
+
+  int rc = 0;
+  for (decl = level->namespaces; decl && !rc; decl = DECL_CHAIN (decl))
+    rc = purge_unused_extern_globals (decl);
+  return rc;
+}
+
 /* 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
@@ -4844,6 +4870,10 @@ c_parse_final_cleanups (void)
     }
 
   timevar_stop (TV_PHASE_DEFERRED);
+
+  /* Purge unreferenced extern variables from the debug information.  */
+  purge_unused_extern_globals (global_namespace);
+
   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 f78aaa9..b7e1770 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 "memmodel.h"
 #include "tm_p.h"
 #include "stringpool.h"
@@ -25506,6 +25507,16 @@ dwarf2out_early_global_decl (tree decl)
   symtab->global_info_ready = save;
 }
 
+/* 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));
+}
+
 /* Output debug information for global decl DECL.  Called from
    toplev.c after compilation proper has finished.  */
 
@@ -25514,28 +25525,72 @@ dwarf2out_late_global_decl (tree decl)
 {
   /* Fill-in any location information we were unable to determine
      on the first pass.  */
-  if (VAR_P (decl) && !POINTER_BOUNDS_P (decl))
+  if (! VAR_P (decl) || POINTER_BOUNDS_P (decl))
+    return;
+
+  dw_die_ref die = lookup_decl_die (decl);
+
+  /* We have to generate early debug late for LTO.  */
+  if (! die && in_lto_p)
     {
-      dw_die_ref die = lookup_decl_die (decl);
+      dwarf2out_decl (decl);
+      die = lookup_decl_die (decl);
+    }
 
-      /* We have to generate early debug late for LTO.  */
-      if (! die && in_lto_p)
-	{
-	  dwarf2out_decl (decl);
-	  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))
+    {
+      mark_removed (die);
 
-      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);
-	}
+      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;
+
+  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);
     }
 }
 
@@ -25639,6 +25694,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.
@@ -27889,16 +27948,6 @@ prune_unused_types_update_strings (dw_die_ref die)
       }
 }
 
-/* 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
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



More information about the Gcc-patches mailing list