public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH v1 2/3] dwarf: purge DIEs for unreferenced extern globals.
  2017-07-12 15:42 [PATCH v1 0/3] dwarf: purge DIEs for unreferenced extern globals Franklin “Snaipe” Mathieu
@ 2017-07-12 15:42 ` Franklin “Snaipe” Mathieu
  2017-07-12 15:42 ` [PATCH v1 3/3] " Franklin “Snaipe” Mathieu
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Franklin “Snaipe” Mathieu @ 2017-07-12 15:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: snaipe, Franklin “Snaipe” Mathieu

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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v1 1/3] dwarf: purge DIEs for unreferenced extern globals.
  2017-07-12 15:42 [PATCH v1 0/3] dwarf: purge DIEs for unreferenced extern globals Franklin “Snaipe” Mathieu
  2017-07-12 15:42 ` [PATCH v1 2/3] " Franklin “Snaipe” Mathieu
  2017-07-12 15:42 ` [PATCH v1 3/3] " Franklin “Snaipe” Mathieu
@ 2017-07-12 15:42 ` Franklin “Snaipe” Mathieu
  2017-07-17  9:49 ` [PATCH v1 0/3] " Richard Biener
  3 siblings, 0 replies; 5+ messages in thread
From: Franklin “Snaipe” Mathieu @ 2017-07-12 15:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: snaipe, Franklin “Snaipe” Mathieu

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                              |  41 +++++++++++
 gcc/dwarf2out.c                             | 109 ++++++++++++++++++++--------
 gcc/testsuite/g++.dg/debug/dwarf2/pr81135.C |  25 +++++++
 gcc/testsuite/gcc.dg/debug/dwarf2/pr81135.C |  13 ++++
 5 files changed, 168 insertions(+), 30 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 317d5cd..9d64046 100644
--- a/gcc/c/c-decl.c
+++ b/gcc/c/c-decl.c
@@ -11227,6 +11227,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 877745c..93929e6 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"
@@ -4408,6 +4409,42 @@ 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)
+{
+  int rc;
+  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))
+    {
+      switch (TREE_CODE (decl))
+	{
+	case VAR_DECL:
+	  if (DECL_EXTERNAL (decl))
+	    (*debug_hooks->late_global_decl) (decl);
+	  break;
+	case NAMESPACE_DECL:
+	  if ((rc = purge_unused_extern_globals (decl)))
+	    return rc;
+	  break;
+	default:
+	  break;
+	}
+    }
+
+  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
@@ -4791,6 +4828,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 c277d27..261aeef 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"
@@ -25512,6 +25513,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.  */
 
@@ -25520,29 +25531,73 @@ 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, except if they have a DECL_VALUE_EXPR, in which case
-	     they are relevant for debuggers.  */
-	  varpool_node *node = varpool_node::get (decl);
-	  if ((! node || ! node->definition) && ! DECL_HAS_VALUE_EXPR_P (decl))
-	    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, except if they have a DECL_VALUE_EXPR, in which case
+	 they are relevant for debuggers.  */
+      if ((! node || ! node->definition) && ! DECL_HAS_VALUE_EXPR_P (decl))
+	tree_add_const_value_attribute_for_decl (die, decl);
+      else
+	add_location_or_const_value_attribute (die, decl, false);
     }
 }
 
@@ -25646,6 +25701,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.
@@ -27860,16 +27919,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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v1 3/3] dwarf: purge DIEs for unreferenced extern globals.
  2017-07-12 15:42 [PATCH v1 0/3] dwarf: purge DIEs for unreferenced extern globals Franklin “Snaipe” Mathieu
  2017-07-12 15:42 ` [PATCH v1 2/3] " Franklin “Snaipe” Mathieu
@ 2017-07-12 15:42 ` Franklin “Snaipe” Mathieu
  2017-07-12 15:42 ` [PATCH v1 1/3] " Franklin “Snaipe” Mathieu
  2017-07-17  9:49 ` [PATCH v1 0/3] " Richard Biener
  3 siblings, 0 replies; 5+ messages in thread
From: Franklin “Snaipe” Mathieu @ 2017-07-12 15:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: snaipe, Franklin “Snaipe” Mathieu

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 --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

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH v1 0/3] dwarf: purge DIEs for unreferenced extern globals.
@ 2017-07-12 15:42 Franklin “Snaipe” Mathieu
  2017-07-12 15:42 ` [PATCH v1 2/3] " Franklin “Snaipe” Mathieu
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Franklin “Snaipe” Mathieu @ 2017-07-12 15:42 UTC (permalink / raw)
  To: gcc-patches; +Cc: snaipe, Franklin “Snaipe” Mathieu

From: Franklin “Snaipe” Mathieu <snaipe@diacritic.io>

Hello GCC folks,

This patch series addresses PR 81135 [1].

* patch 1/3 is for trunk (built/tested on trunk@250093).
* patch 2/3 is the gcc7 backport (built/tested on gcc-7-branch@249680).
* patch 3/3 is the gcc6 backport (built/tested on gcc-6-branch@249671).

[1]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81135

Kind Regards,

-- 
Franklin "Snaipe" Mathieu
Arista Networks, Ltd

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH v1 0/3] dwarf: purge DIEs for unreferenced extern globals.
  2017-07-12 15:42 [PATCH v1 0/3] dwarf: purge DIEs for unreferenced extern globals Franklin “Snaipe” Mathieu
                   ` (2 preceding siblings ...)
  2017-07-12 15:42 ` [PATCH v1 1/3] " Franklin “Snaipe” Mathieu
@ 2017-07-17  9:49 ` Richard Biener
  3 siblings, 0 replies; 5+ messages in thread
From: Richard Biener @ 2017-07-17  9:49 UTC (permalink / raw)
  To: Franklin “Snaipe” Mathieu
  Cc: GCC Patches, Franklin “Snaipe” Mathieu

On Wed, Jul 12, 2017 at 5:42 PM, Franklin “Snaipe” Mathieu
<snaipe@arista.com> wrote:
> From: Franklin “Snaipe” Mathieu <snaipe@diacritic.io>
>
> Hello GCC folks,
>
> This patch series addresses PR 81135 [1].
>
> * patch 1/3 is for trunk (built/tested on trunk@250093).
> * patch 2/3 is the gcc7 backport (built/tested on gcc-7-branch@249680).
> * patch 3/3 is the gcc6 backport (built/tested on gcc-6-branch@249671).
>
> [1]: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81135

Doing this from late_global_decl is bogus from a conceptual point.  It should
be instead done from dwarf2out_early_finish where we also prune unused
types from (prune_unused_types).  I'd be more comfortable if you'd integrate
removing unused global declarations into that function given it already handles
uses in attributes and the like.

The other possibility is to address this from the FE side and not call
early_global_decl
on mere declarations but only after parsing (when TREE_USED is
properly set) walk
them again and register them at that point.

Richard.

> Kind Regards,
>
> --
> Franklin "Snaipe" Mathieu
> Arista Networks, Ltd
>

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-07-17  9:49 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-12 15:42 [PATCH v1 0/3] dwarf: purge DIEs for unreferenced extern globals Franklin “Snaipe” Mathieu
2017-07-12 15:42 ` [PATCH v1 2/3] " Franklin “Snaipe” Mathieu
2017-07-12 15:42 ` [PATCH v1 3/3] " Franklin “Snaipe” Mathieu
2017-07-12 15:42 ` [PATCH v1 1/3] " Franklin “Snaipe” Mathieu
2017-07-17  9:49 ` [PATCH v1 0/3] " Richard Biener

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).