[PATCH] Fix pruning of unused debug types (PR c++/27017)

Jakub Jelinek jakub@redhat.com
Thu Nov 13 15:24:00 GMT 2008


Hi!

As can be seen on the testcase, we are prunning local classes as unused
even when they contain static member functions that were emitted and are
used.  If the static member function is inline and is successfully
inlined (or when -fno-eliminate-unused-debug-types is used), the debug info
is emitted.  If there is successful inlining, then the static member
function will be marked as needed through abstract origin reference
and prune_unused_types_mark marks its die_parent (the local class) with
prune_unused_types_mark (die->die_parent, 0), so the local class is emitted,
but only the needed children are emitted for it.
But otherwise nothing during prune_unused_types_walk (comp_unit_die)
walks into the local class and thus nothing finds out the static member
function was actually used.

The following patch fixes that, by walking children of local classes
anyway, looking for emitted static member functions and calls
prune_unused_types_mark (die, 1); on them.  In addition to this it
fixes a bug where prune_unused_types_walk wouldn't do anything if
die_mark was already 1.  If it was set to 1 by a
prune_unused_types_mark (die, 0) call, then the children were never walked.
The patch unifies the meaning of die_mark, 1 means in all the
prune_unused_types_* functions that attributes were (or are about to be)
walked and that the node is needed.  2 means the same and additionally
that children were (or are about to be) walked.

Bootstrapped/regtested on x86_64-linux.
On libstdc++.so .debug_info section grew a tiny bit (0.87%), libgcj.so's
.debug_info grew just by 0.01%, so IMHO the patch works as intended and
doesn't cause massive amounts of types to be suddenly emitted compared
to earlier.  Ok for trunk?

2008-11-13  Jakub Jelinek  <jakub@redhat.com>

	PR c++/27017
	* dwarf2out.c (prune_unused_types_walk_local_classes): New function.
	(prune_unused_types_walk): Call it for non-perennial local classes.
	Set die_mark to 2 if recursing on children.  If die_mark is 1 on
	entry, just set it to 2 and recurse on children, don't walk attributes
	again.

	* g++.dg/debug/dwarf2/localclass1.C: New test.
	* g++.dg/debug/dwarf2/localclass2.C: New test.

--- gcc/dwarf2out.c.jj	2008-10-23 13:21:39.000000000 +0200
+++ gcc/dwarf2out.c	2008-11-13 14:19:13.000000000 +0100
@@ -16248,6 +16248,37 @@ prune_unused_types_mark (dw_die_ref die,
     }
 }
 
+/* For local classes, look if any static member functions were emitted
+   and if so, mark them.  */
+
+static void
+prune_unused_types_walk_local_classes (dw_die_ref die)
+{
+  dw_die_ref c;
+
+  if (die->die_mark == 2)
+    return;
+
+  switch (die->die_tag)
+    {
+    case DW_TAG_structure_type:
+    case DW_TAG_union_type:
+    case DW_TAG_class_type:
+      break;
+
+    case DW_TAG_subprogram:
+      if (!get_AT_flag (die, DW_AT_declaration)
+	  || die->die_definition != NULL)
+	prune_unused_types_mark (die, 1);
+      return;
+
+    default:
+      return;
+    }
+
+  /* Mark children.  */
+  FOR_EACH_CHILD (die, c, prune_unused_types_walk_local_classes (c));
+}
 
 /* Walk the tree DIE and mark types that we actually use.  */
 
@@ -16256,12 +16287,29 @@ prune_unused_types_walk (dw_die_ref die)
 {
   dw_die_ref c;
 
-  /* Don't do anything if this node is already marked.  */
-  if (die->die_mark)
+  /* Don't do anything if this node is already marked and
+     children have been marked as well.  */
+  if (die->die_mark == 2)
     return;
 
   switch (die->die_tag)
     {
+    case DW_TAG_structure_type:
+    case DW_TAG_union_type:
+    case DW_TAG_class_type:
+      if (die->die_perennial_p)
+	break;
+
+      for (c = die->die_parent; c; c = c->die_parent)
+	if (c->die_tag == DW_TAG_subprogram)
+	  break;
+
+      if (c)
+	prune_unused_types_walk_local_classes (die);
+
+      /* It's a type node --- don't mark it.  */
+      return;
+
     case DW_TAG_const_type:
     case DW_TAG_packed_type:
     case DW_TAG_pointer_type:
@@ -16269,9 +16317,6 @@ prune_unused_types_walk (dw_die_ref die)
     case DW_TAG_volatile_type:
     case DW_TAG_typedef:
     case DW_TAG_array_type:
-    case DW_TAG_structure_type:
-    case DW_TAG_union_type:
-    case DW_TAG_class_type:
     case DW_TAG_interface_type:
     case DW_TAG_friend:
     case DW_TAG_variant_part:
@@ -16293,10 +16338,15 @@ prune_unused_types_walk (dw_die_ref die)
       break;
   }
 
-  die->die_mark = 1;
+  if (die->die_mark == 0)
+    {
+      die->die_mark = 1;
+
+      /* Now, mark any dies referenced from here.  */
+      prune_unused_types_walk_attribs (die);
+    }
 
-  /* Now, mark any dies referenced from here.  */
-  prune_unused_types_walk_attribs (die);
+  die->die_mark = 2;
 
   /* Mark children.  */
   FOR_EACH_CHILD (die, c, prune_unused_types_walk (c));
--- gcc/testsuite/g++.dg/debug/dwarf2/localclass1.C.jj	2008-11-13 14:12:08.000000000 +0100
+++ gcc/testsuite/g++.dg/debug/dwarf2/localclass1.C	2008-11-13 14:14:55.000000000 +0100
@@ -0,0 +1,76 @@
+// PR c++/27017
+// { dg-do compile }
+// { dg-options "-gdwarf-2 -dA -feliminate-unused-debug-types -fno-merge-debug-strings" }
+
+int
+foo (int arg1)
+{
+  struct localstruct1
+  {
+    static inline int staticfn1 (int arg2)
+    {
+      int var2 = arg2 << 2;
+      return arg2 + var2;
+    }
+    static int staticfn2 (int arg3)
+    {
+      int var3 = arg3 << 2;
+      return arg3 + var3;
+    }
+    static inline int staticfn3 (int arg4)
+    {
+      int var4 = arg4 << 2;
+      return arg4 + var4;
+    }
+    static int staticfn4 (int arg5)
+    {
+      int var5 = arg5 << 2;
+      return arg5 + var5;
+    }
+    int method1 (int arg6)
+    {
+      int var6 = arg6 << 2;
+      return arg6 + var6;
+    }
+  };
+  struct localstruct2
+  {
+    static inline int staticfn5 (int arg7)
+    {
+      int var7 = arg7 << 2;
+      return arg7 + var7;
+    }
+    static int staticfn6 (int arg8)
+    {
+      int var8 = arg8 << 2;
+      return arg8 + var8;
+    }
+  };
+  return localstruct1::staticfn1 (arg1) + localstruct1::staticfn2 (arg1);
+}
+
+int
+main ()
+{
+  return foo (1) - 10;
+}
+
+// { dg-final { scan-assembler "main\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "foo\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "staticfn1\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "staticfn2\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "staticfn3\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "staticfn4\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "staticfn5\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "staticfn6\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "method1\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "arg1\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "arg2\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "arg3\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "arg4\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "arg5\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "arg6\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "arg7\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "arg8\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "localstruct1\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "localstruct2\[^\n\r\]*DW_AT_name" } }
--- gcc/testsuite/g++.dg/debug/dwarf2/localclass2.C.jj	2008-11-13 14:12:08.000000000 +0100
+++ gcc/testsuite/g++.dg/debug/dwarf2/localclass2.C	2008-11-13 14:16:28.000000000 +0100
@@ -0,0 +1,76 @@
+// PR c++/27017
+// { dg-do compile }
+// { dg-options "-gdwarf-2 -dA -O2 -feliminate-unused-debug-types -fno-merge-debug-strings" }
+
+int
+foo (int arg1)
+{
+  struct localstruct1
+  {
+    static inline int staticfn1 (int arg2)
+    {
+      int var2 = arg2 << 2;
+      return arg2 + var2;
+    }
+    static int staticfn2 (int arg3)
+    {
+      int var3 = arg3 << 2;
+      return arg3 + var3;
+    }
+    static inline int staticfn3 (int arg4)
+    {
+      int var4 = arg4 << 2;
+      return arg4 + var4;
+    }
+    static int staticfn4 (int arg5)
+    {
+      int var5 = arg5 << 2;
+      return arg5 + var5;
+    }
+    int method1 (int arg6)
+    {
+      int var6 = arg6 << 2;
+      return arg6 + var6;
+    }
+  };
+  struct localstruct2
+  {
+    static inline int staticfn5 (int arg7)
+    {
+      int var7 = arg7 << 2;
+      return arg7 + var7;
+    }
+    static int staticfn6 (int arg8)
+    {
+      int var8 = arg8 << 2;
+      return arg8 + var8;
+    }
+  };
+  return localstruct1::staticfn1 (arg1) + localstruct1::staticfn2 (arg1);
+}
+
+int
+main ()
+{
+  return foo (1) - 10;
+}
+
+// { dg-final { scan-assembler "main\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "foo\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "staticfn1\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "staticfn2\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "staticfn3\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "staticfn4\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "staticfn5\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "staticfn6\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "method1\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "arg1\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "arg2\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "arg3\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "arg4\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "arg5\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "arg6\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "arg7\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "arg8\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler "localstruct1\[^\n\r\]*DW_AT_name" } }
+// { dg-final { scan-assembler-not "localstruct2\[^\n\r\]*DW_AT_name" } }

	Jakub



More information about the Gcc-patches mailing list