On 01/28/2015 10:51 AM, Jason Merrill wrote:
On 01/28/2015 01:29 PM, Aldy Hernandez wrote:
+  /* It is rather unfortunate that Cilk creates trees this late +
(during gimplification).  However, until this gets fixed, +
specially handle emitting DWARF for this new function and +
immediately clean up the limbo_die_list where the new function's +
DIE will inevitably end up.  */

Why does it go on limbo_die_list at all?

Good point.  They are ending up in limbo because, during late dwarf
generation, dwarf2out_decl is clearing its context_die, as they are
nested functions:

      /* If we're a nested function, initially use a parent of NULL; if
we're
         a plain function, this will be fixed up in decls_for_scope.  If
         we're a method, it will be ignored, since we already have a DIE.  */
      if (decl_function_context (decl)
          /* But if we're in terse mode, we don't care about scope.  */
          && debug_info_level > DINFO_LEVEL_TERSE)
        context_die = NULL;

We could avoid special casing Cilk, and just allow late limbo DIEs of
nested functions, since decls_for_scope will set their ancestry
correctly shortly afterwards (that is, they don't remain in limbo long-- or into LTO). I have verified this to be true with the
 patch.


I noticed dwarf2out's gen_member_die() disallows generation of
clones earlier, by design:

/* Don't include clones in the member list.  */ if
(DECL_ABSTRACT_ORIGIN (member)) continue;

I'd still like to understand why disabling this doesn't work; I don't
 like the special handling of clones in the front end.

The attached patch disables this, and fixes the fallout. Instead of explaining what required tweaking, the `#if 0'ed out code with appropriate comments explains the relevant changes. Do you agree?

Obviously, now we will get more DIEs than before (complete constructors, base constructors, and what have yous). Whereas previously we only generated a DIE for the used ones. We can optimize this later.

I have added some debugging code to help catch late limbo DIE creation as well as a few things to help the process. We can remove them at merge time.

I special cased allowing late limbo type DIES that occur for template interactions. No sense complicating my life with templates until we iron out the clones. I will take a look at templates next.

I also got rid of my decl_abstract change to dwarf2out_abstract_function() (the static variable plus LTO issue) because it's not being triggered anymore with the attached patch.

Would you be so kind as to look at this approach? It has no guality regressions, and the C++ clones seem to have correct DIEs. I can remove the #if 0 code once we're in agreement.

Thanks.
Aldy
        * cp/decl2.c (emit_debug_for_namespace): Add FIXME note for
        templates.
        * dbxout.c (dbx_debug_hooks): Add early_finish field.
        * sdbout.c (sdb_debug_hooks): Same.
        * vmsdbgout.c (vmsdbg_debug_hooks): Same.
        * debug.h (gcc_debug_hooks): Same.
        * dwarf2out.c (dwarf2_debug_hooks): Same.
        (new_die): Inhibit limbo dies unless generating early dwarf.
        Set tmp_created_for field.
        (gen_subprogram_die): Do not create a new DIE if we have an
        old DIE and it has DW_AT_abstract_origin set.
        Do not reset origin.
        Remove declaration assert.
        (gen_member_die): Allow DIE clones to be generated.
        (struct die_struct): Add tmp_created_for field.
        (lookup_filename): Return NULL when no file name.
        (optimize_location_lists): Abstract flushing of limbo list to...
        (dwarf2out_early_finish): ...here. New function.
        (print_die): Output tmp_created_for field.
        * toplev.c (toplev::main): Add temporary debugging aid.
        * tree.c (free_lang_data): Call debug_hooks->early_finish.

diff --git a/gcc/cp/decl2.c b/gcc/cp/decl2.c
index 691688b..70abc99 100644
--- a/gcc/cp/decl2.c
+++ b/gcc/cp/decl2.c
@@ -4346,6 +4346,9 @@ emit_debug_for_namespace (tree name_space, void* data 
ATTRIBUTE_UNUSED)
 
   check_global_declarations (vec, len);
 
+  /* FIXME: What does this do for templates?  I think we don't want to
+     send a template off to early_global_decl, but rather walk through
+     its specializations and emit them.  */
   for (tree t = level->names; t; t = TREE_CHAIN(t))
     debug_hooks->early_global_decl (t);
 
diff --git a/gcc/dbxout.c b/gcc/dbxout.c
index 430a2eb..202ef8a 100644
--- a/gcc/dbxout.c
+++ b/gcc/dbxout.c
@@ -359,6 +359,7 @@ const struct gcc_debug_hooks dbx_debug_hooks =
   dbxout_init,
   dbxout_finish,
   debug_nothing_void,
+  debug_nothing_void,
   debug_nothing_int_charstar,
   debug_nothing_int_charstar,
   dbxout_start_source_file,
@@ -400,6 +401,7 @@ const struct gcc_debug_hooks xcoff_debug_hooks =
   dbxout_init,
   dbxout_finish,
   debug_nothing_void,
+  debug_nothing_void,
   debug_nothing_int_charstar,
   debug_nothing_int_charstar,
   dbxout_start_source_file,
diff --git a/gcc/debug.c b/gcc/debug.c
index 449d3a1..d0e00c0 100644
--- a/gcc/debug.c
+++ b/gcc/debug.c
@@ -27,6 +27,7 @@ const struct gcc_debug_hooks do_nothing_debug_hooks =
 {
   debug_nothing_charstar,
   debug_nothing_charstar,
+  debug_nothing_void,                  /* early_finish */
   debug_nothing_void,
   debug_nothing_int_charstar,
   debug_nothing_int_charstar,
diff --git a/gcc/debug.h b/gcc/debug.h
index f9485bc..a8d3f23 100644
--- a/gcc/debug.h
+++ b/gcc/debug.h
@@ -30,6 +30,9 @@ struct gcc_debug_hooks
   /* Output debug symbols.  */
   void (* finish) (const char *main_filename);
 
+  /* Run cleanups necessary after early debug generation.  */
+  void (* early_finish) (void);
+
   /* Called from cgraph_optimize before starting to assemble
      functions/variables/toplevel asms.  */
   void (* assembly_start) (void);
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 33738d9..7e1c000 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -106,6 +106,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-dfa.h"
 #include "gdb/gdb-index.h"
 #include "rtl-iter.h"
+#include "print-tree.h"
 
 static void dwarf2out_source_line (unsigned int, const char *, int, bool);
 static rtx_insn *last_var_location_insn;
@@ -2424,6 +2425,7 @@ build_cfa_aligned_loc (dw_cfa_location *cfa,
 
 static void dwarf2out_init (const char *);
 static void dwarf2out_finish (const char *);
+static void dwarf2out_early_finish (void);
 static void dwarf2out_assembly_start (void);
 static void dwarf2out_define (unsigned int, const char *);
 static void dwarf2out_undef (unsigned int, const char *);
@@ -2451,6 +2453,7 @@ const struct gcc_debug_hooks dwarf2_debug_hooks =
 {
   dwarf2out_init,
   dwarf2out_finish,
+  dwarf2out_early_finish,
   dwarf2out_assembly_start,
   dwarf2out_define,
   dwarf2out_undef,
@@ -2611,6 +2614,9 @@ typedef struct GTY((chain_circular ("%h.die_sib"), 
for_user)) die_struct {
   int die_mark;
   unsigned int decl_id;
   enum dwarf_tag die_tag;
+  /* No one should depend on this, as it is a temporary debugging aid
+     to indicate the DECL for which this DIE was created for.  */
+  tree tmp_created_for;
   /* Die is used and must not be pruned as unused.  */
   BOOL_BITFIELD die_perennial_p : 1;
   BOOL_BITFIELD comdat_type_p : 1; /* DIE has a type signature */
@@ -4890,6 +4896,7 @@ new_die (enum dwarf_tag tag_value, dw_die_ref parent_die, 
tree t)
   dw_die_ref die = ggc_cleared_alloc<die_node> ();
 
   die->die_tag = tag_value;
+  die->tmp_created_for = t;
 
   if (early_dwarf_dumping)
     die->dumped_early = true;
@@ -4900,6 +4907,30 @@ new_die (enum dwarf_tag tag_value, dw_die_ref 
parent_die, tree t)
     {
       limbo_die_node *limbo_node;
 
+      /* No DIEs created after early dwarf should end up in limbo,
+        because the limbo list should not persist past LTO
+        streaming.  */
+      if (tag_value != DW_TAG_compile_unit
+         && !early_dwarf_dumping
+         /* Allow nested functions to live in limbo because they will
+            only temporarily live there, as decls_for_scope will fix
+            them up.  */
+         && (TREE_CODE (t) != FUNCTION_DECL
+             || !decl_function_context (t))
+         /* FIXME: Allow types for now.  We are getting some internal
+            template types from inlining (building libstdc++).
+            Templates need to be looked at.  */
+         && !TYPE_P (t)
+         /* FIXME: Allow late limbo DIE creation for LTO, especially
+            in the ltrans stage, but once we implement LTO dwarf
+            streaming, we should remove this exception.  */
+         && !in_lto_p)
+       {
+         fprintf (stderr, "symbol ended up in limbo too late:");
+         debug_generic_stmt (t);
+         gcc_unreachable ();
+       }
+
       limbo_node = ggc_cleared_alloc<limbo_die_node> ();
       limbo_node->die = die;
       limbo_node->created_for = t;
@@ -5399,6 +5430,12 @@ print_die (dw_die_ref die, FILE *outfile)
        fprintf (outfile, ": %s", name);
       fputc (')', outfile);
     }
+  if (die->tmp_created_for
+      && DECL_P (die->tmp_created_for)
+      && CODE_CONTAINS_STRUCT
+           (TREE_CODE (die->tmp_created_for), TS_DECL_WITH_VIS))
+    fprintf (outfile, "(mangle: %s)",
+            IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (die->tmp_created_for)));
   fputc ('\n', outfile);
   print_spaces (outfile);
   fprintf (outfile, "  abbrev id: %lu", die->die_abbrev);
@@ -18414,12 +18451,16 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
      from the member list for the class.  If so, DECLARATION takes priority;
      we'll get back to the abstract instance when done with the class.  */
 
+  /* ?? We must not reset `origin', so C++ clones get a proper
+     DW_AT_abstract_origin tagged DIE further on.  */
+#if 0
   /* The class-scope declaration DIE must be the primary DIE.  */
   if (origin && declaration && class_or_namespace_scope_p (context_die))
     {
       origin = NULL;
       gcc_assert (!old_die);
     }
+#endif
 
   /* Now that the C++ front end lazily declares artificial member fns, we
      might need to retrofit the declaration into its class.  */
@@ -18433,21 +18474,34 @@ gen_subprogram_die (tree decl, dw_die_ref context_die)
   /* An inlined instance, tag a new DIE with DW_AT_abstract_origin.  */
   if (origin != NULL)
     {
+      // ?? Make C++ clones work since they're tagged as
+      // declarations but are class scoped instead.
+#if 0
       gcc_assert (!declaration || local_scope_p (context_die));
+#endif
 
       /* Fixup die_parent for the abstract instance of a nested
         inline function.  */
       if (old_die && old_die->die_parent == NULL)
        add_child_die (context_die, old_die);
 
-      subr_die = new_die (DW_TAG_subprogram, context_die, decl);
-      add_abstract_origin_attribute (subr_die, origin);
-      /*  This is where the actual code for a cloned function is.
-         Let's emit linkage name attribute for it.  This helps
-         debuggers to e.g, set breakpoints into
-         constructors/destructors when the user asks "break
-         K::K".  */
-      add_linkage_name (subr_die, decl);
+      if (old_die && get_AT_ref (old_die, DW_AT_abstract_origin))
+       {
+         /* If we have a DW_AT_abstract_origin we have a working
+            cached version.  */
+         subr_die = old_die;
+       }
+      else
+       {
+         subr_die = new_die (DW_TAG_subprogram, context_die, decl);
+         add_abstract_origin_attribute (subr_die, origin);
+         /*  This is where the actual code for a cloned function is.
+             Let's emit linkage name attribute for it.  This helps
+             debuggers to e.g, set breakpoints into
+             constructors/destructors when the user asks "break
+             K::K".  */
+         add_linkage_name (subr_die, decl);
+       }
     }
   /* A cached copy, possibly from early dwarf generation.  Reuse as
      much as possible.  */
@@ -20125,9 +20179,9 @@ gen_member_die (tree type, dw_die_ref context_die)
   /* Now output info about the function members (if any).  */
   for (member = TYPE_METHODS (type); member; member = DECL_CHAIN (member))
     {
-      /* Don't include clones in the member list.  */
-      if (DECL_ABSTRACT_ORIGIN (member))
-       continue;
+      if (DECL_ABSTRACT_ORIGIN (member) && flag_dump_early_debug_stats)
+       fprintf(stderr, "generating dwarf for member clone: %s\n",
+               IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (member)));
 
       child = lookup_decl_die (member);
       if (child)
@@ -21733,6 +21787,9 @@ lookup_filename (const char *file_name)
 {
   struct dwarf_file_data * created;
 
+  if (!file_name)
+    return NULL;
+
   dwarf_file_data **slot
     = file_table->find_slot_with_hash (file_name, htab_hash_string (file_name),
                                       INSERT);
@@ -24726,10 +24783,19 @@ optimize_location_lists (dw_die_ref die)
 static void
 dwarf2out_finish (const char *filename)
 {
-  limbo_die_node *node, *next_node;
   comdat_type_node *ctnode;
   dw_die_ref main_comp_unit_die;
 
+  /* If the limbo list has anything, it should be things that were
+     created after the compilation proper.  Anything from the early
+     dwarf pass, should have parents and should never be in the limbo
+     list this late.  */
+  for (limbo_die_node *node = limbo_die_list; node; node = node->next)
+    gcc_assert (!node->die->dumped_early);
+
+  /* Flush out any latecomers to the limbo party.  */
+  dwarf2out_early_finish();
+
   /* PCH might result in DW_AT_producer string being restored from the
      header compilation, so always fill it with empty string initially
      and overwrite only here.  */
@@ -24754,55 +24820,6 @@ dwarf2out_finish (const char *filename)
        add_comp_dir_attribute (comp_unit_die ());
     }
 
-  /* Traverse the limbo die list, and add parent/child links.  The only
-     dies without parents that should be here are concrete instances of
-     inline functions, and the comp_unit_die.  We can ignore the comp_unit_die.
-     For concrete instances, we can get the parent die from the abstract
-     instance.  */
-  for (node = limbo_die_list; node; node = next_node)
-    {
-      dw_die_ref die = node->die;
-      next_node = node->next;
-
-      if (die->die_parent == NULL)
-       {
-         dw_die_ref origin = get_AT_ref (die, DW_AT_abstract_origin);
-
-         if (origin && origin->die_parent)
-           add_child_die (origin->die_parent, die);
-         else if (is_cu_die (die))
-           ;
-         else if (seen_error ())
-           /* It's OK to be confused by errors in the input.  */
-           add_child_die (comp_unit_die (), die);
-         else
-           {
-             /* In certain situations, the lexical block containing a
-                nested function can be optimized away, which results
-                in the nested function die being orphaned.  Likewise
-                with the return type of that nested function.  Force
-                this to be a child of the containing function.
-
-                It may happen that even the containing function got fully
-                inlined and optimized out.  In that case we are lost and
-                assign the empty child.  This should not be big issue as
-                the function is likely unreachable too.  */
-             gcc_assert (node->created_for);
-
-             if (DECL_P (node->created_for))
-               origin = get_context_die (DECL_CONTEXT (node->created_for));
-             else if (TYPE_P (node->created_for))
-               origin = scope_die_for (node->created_for, comp_unit_die ());
-             else
-               origin = comp_unit_die ();
-
-             add_child_die (origin, die);
-           }
-       }
-    }
-
-  limbo_die_list = NULL;
-
 #if ENABLE_ASSERT_CHECKING
   {
     dw_die_ref die = comp_unit_die (), c;
@@ -24850,6 +24867,7 @@ dwarf2out_finish (const char *filename)
   /* Traverse the DIE's and add add sibling attributes to those DIE's
      that have children.  */
   add_sibling_attributes (comp_unit_die ());
+  limbo_die_node *node;
   for (node = limbo_die_list; node; node = node->next)
     add_sibling_attributes (node->die);
   for (ctnode = comdat_type_list; ctnode != NULL; ctnode = ctnode->next)
@@ -25111,6 +25129,66 @@ dwarf2out_finish (const char *filename)
     output_indirect_strings ();
 }
 
+/* Perform any cleanups needed after the early debug generation pass
+   has run.  */
+
+static void
+dwarf2out_early_finish (void)
+{
+  /* Traverse the limbo die list, and add parent/child links.  The only
+     dies without parents that should be here are concrete instances of
+     inline functions, and the comp_unit_die.  We can ignore the comp_unit_die.
+     For concrete instances, we can get the parent die from the abstract
+     instance.
+
+     The point here is to flush out the limbo list so that it is empty
+     and we don't need to stream it for LTO.  */
+  limbo_die_node *node, *next_node;
+  for (node = limbo_die_list; node; node = next_node)
+    {
+      dw_die_ref die = node->die;
+      next_node = node->next;
+
+      if (die->die_parent == NULL)
+       {
+         dw_die_ref origin = get_AT_ref (die, DW_AT_abstract_origin);
+
+         if (origin && origin->die_parent)
+           add_child_die (origin->die_parent, die);
+         else if (is_cu_die (die))
+           ;
+         else if (seen_error ())
+           /* It's OK to be confused by errors in the input.  */
+           add_child_die (comp_unit_die (), die);
+         else
+           {
+             /* In certain situations, the lexical block containing a
+                nested function can be optimized away, which results
+                in the nested function die being orphaned.  Likewise
+                with the return type of that nested function.  Force
+                this to be a child of the containing function.
+
+                It may happen that even the containing function got fully
+                inlined and optimized out.  In that case we are lost and
+                assign the empty child.  This should not be big issue as
+                the function is likely unreachable too.  */
+             gcc_assert (node->created_for);
+
+             if (DECL_P (node->created_for))
+               origin = get_context_die (DECL_CONTEXT (node->created_for));
+             else if (TYPE_P (node->created_for))
+               origin = scope_die_for (node->created_for, comp_unit_die ());
+             else
+               origin = comp_unit_die ();
+
+             add_child_die (origin, die);
+           }
+       }
+    }
+
+  limbo_die_list = NULL;
+}
+
 /* Reset all state within dwarf2out.c so that we can rerun the compiler
    within the same process.  For use by toplev::finalize.  */
 
diff --git a/gcc/sdbout.c b/gcc/sdbout.c
index d7b2d6b..43b8cf2 100644
--- a/gcc/sdbout.c
+++ b/gcc/sdbout.c
@@ -279,6 +279,7 @@ const struct gcc_debug_hooks sdb_debug_hooks =
 {
   sdbout_init,                          /* init */
   sdbout_finish,                        /* finish */
+  debug_nothing_void,                   /* early_finish */
   debug_nothing_void,                   /* assembly_start */
   debug_nothing_int_charstar,           /* define */
   debug_nothing_int_charstar,           /* undef */
diff --git a/gcc/toplev.c b/gcc/toplev.c
index 42a2cdc..6b58ed2 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -2136,6 +2136,21 @@ toplev::main (int argc, char **argv)
   if (version_flag)
     print_version (stderr, "");
 
+  /* FIXME: Temporary debugging aid to know which LTO phase we are in
+     without having to pass -v to the driver and all its verbosity.  */
+  if (0)
+    {
+      fprintf(stderr, "MAIN: cc1*\n");
+      if (flag_lto)
+       fprintf(stderr, "\tMAIN: flag_lto\n");
+      if (in_lto_p)
+       fprintf(stderr, "\tMAIN: in_lto_p\n");
+      if (flag_wpa)
+       fprintf(stderr, "\tMAIN: flag_wpa\n");
+      if (flag_ltrans)
+       fprintf(stderr, "\tMAIN: flag_ltrans\n");
+    }
+
   if (help_flag)
     print_plugins_help (stderr, "");
 
diff --git a/gcc/tree.c b/gcc/tree.c
index 8743763..80b4287 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -5730,6 +5730,10 @@ free_lang_data (void)
 {
   unsigned i;
 
+  /* Clean up anything that needs cleaning up after initial debug
+     generation.  */
+  (*debug_hooks->early_finish) ();
+
   /* If we are the LTO frontend we have freed lang-specific data already.  */
   if (in_lto_p
       /* FIXME: Eventually we need to remove this so the function
diff --git a/gcc/vmsdbgout.c b/gcc/vmsdbgout.c
index 5cb66bc..6da48eb 100644
--- a/gcc/vmsdbgout.c
+++ b/gcc/vmsdbgout.c
@@ -179,6 +179,7 @@ static void vmsdbgout_abstract_function (tree);
 const struct gcc_debug_hooks vmsdbg_debug_hooks
 = {vmsdbgout_init,
    vmsdbgout_finish,
+   debug_nothing_void,
    vmsdbgout_assembly_start,
    vmsdbgout_define,
    vmsdbgout_undef,

Reply via email to