[debug-early] New branch for streaming dwarf early (and status)
diff mbox

Message ID 53FC04BF.9060508@redhat.com
State New
Headers show

Commit Message

Aldy Hernandez Aug. 26, 2014, 3:53 a.m. UTC
Gentlemen!

As discussed behind closed doors previously... I'll be working on 
streaming dwarf early.  This ultimately benefits Andrew's aspirations 
for a gimple type system, removing the front-end tentacles from the rest 
of the compilation, and LTO debugging in the presence of language hooks.

My main/current goal is calling dwarf2out early for the type system, 
with the streaming part for LTO/dwarf as a side-effect (but not a 
short-term goal).  I hope I can work on everything onto LTO, but I make 
no guarantees.  So, let's start with building the dwarf tables earlier 
(near free_lang_data) and see how this party goes.

I have created a new branch in aldyh/debug-early (patch attached) which 
contains my initial work, if only for feedback and so I don't take 
horribly wrong turns as I move along.  I'm hoping to lean on each one of 
you for advice as debugging, and dwarf, is this black box of magic bits 
I know very little about (not unlike the rest of GCC for me ;-)).

A few notes.

I started with some old patches Michael Matz had for calling dwarf2out() 
on decls early (and by consequence types), as well as supporting 
infrastructure to fix the mess calling dwarf2out twice creates.  For 
reference, his initial work ported to mainline is this commit:

	327f62603a222d9e9ca31f56ae8b52993f649d93

Everything else is my fault.  Michael, I'd appreciate you taking a peek 
at how I have adapted your work.  Feedback, is greatly appreciated.

I have made some changes to gen_formal_parameter_die() to survive being 
called twice.  We initially make the DIE through dwarf2out_early_decl(), 
and augment it with location information when we call dwarf2out again 
from rest_of_handle_final().

With the current branch, I'm slowly going through guality failures and 
fixing them.

My current predicament is that dwarf2out_early_decl() is called early 
enough such that dwarf2out_decl() sets the context die to the entire 
compilation unit.  Which means, that local variables end up with global 
scope.  I was thinking of hacking gen_variable_die() such that the 
second time around through gen_variable_die(), we fix the parent_die to 
point to the correct place, but I haven't thought too much about it. 
Perhaps dwarf2out_early_decl() should set things up correctly??  I'm 
open to suggestions.

The usual branch rules apply.  I'm certainly not above receiving charity 
patches from any of you :).

Fire away!

Aldy

Comments

Jason Merrill Aug. 28, 2014, 5:50 p.m. UTC | #1
On 08/25/2014 11:53 PM, Aldy Hernandez wrote:
> My current predicament is that dwarf2out_early_decl() is called early
> enough such that dwarf2out_decl() sets the context die to the entire
> compilation unit.  Which means, that local variables end up with global
> scope.  I was thinking of hacking gen_variable_die() such that the
> second time around through gen_variable_die(), we fix the parent_die to
> point to the correct place, but I haven't thought too much about it.
> Perhaps dwarf2out_early_decl() should set things up correctly??  I'm
> open to suggestions.

We already have a similar situation with nested functions and such; see 
limbo_die_list.

Jason

Patch
diff mbox

diff --git a/gcc/debug.h b/gcc/debug.h
index 3ac7976..28bc210 100644
--- a/gcc/debug.h
+++ b/gcc/debug.h
@@ -187,6 +187,8 @@  extern void dwarf2out_switch_text_section (void);
 const char *remap_debug_filename (const char *);
 void add_debug_prefix_map (const char *);
 
+extern void dwarf2out_early_decl (tree);
+
 /* For -fdump-go-spec.  */
 
 extern const struct gcc_debug_hooks *
diff --git a/gcc/dwarf2out.c b/gcc/dwarf2out.c
index 1a9e50a..1ee5c26 100644
--- a/gcc/dwarf2out.c
+++ b/gcc/dwarf2out.c
@@ -3677,7 +3677,7 @@  decl_ultimate_origin (const_tree decl)
   /* output_inline_function sets DECL_ABSTRACT_ORIGIN for all the
      nodes in the function to point to themselves; ignore that if
      we're trying to output the abstract instance of this function.  */
-  if (DECL_ABSTRACT (decl) && DECL_ABSTRACT_ORIGIN (decl) == decl)
+  if (/*DECL_ABSTRACT (decl) &&*/ DECL_ABSTRACT_ORIGIN (decl) == decl)
     return NULL_TREE;
 
   /* Since the DECL_ABSTRACT_ORIGIN for a DECL is supposed to be the
@@ -4777,6 +4777,7 @@  remove_child_TAG (dw_die_ref die, enum dwarf_tag tag)
     while (c->die_tag == tag)
       {
 	remove_child_with_prev (c, prev);
+	c->die_parent = NULL;
 	/* Might have removed every child.  */
 	if (c == c->die_sib)
 	  return;
@@ -5515,6 +5516,49 @@  debug_dwarf (void)
   print_indent = 0;
   print_die (comp_unit_die (), stderr);
 }
+
+/* Perform some sanity checks on DIEs after they have been generated
+   earlier in the compilation process.  */
+
+static void
+check_die (dw_die_ref die, unsigned level)
+{
+  static unsigned long mark = 1;
+  dw_die_ref c, p;
+  /* Check that all our childs have their parent set to us.  */
+  c = die->die_child;
+  if (c) do {
+      c = c->die_sib;
+      gcc_assert (c->die_parent == die);
+    } while (c != die->die_child);
+
+  /* Check the we are part of our parent's child list.  */
+  mark++;
+  p = die->die_parent;
+  if (p)
+    {
+      c = p->die_child;
+      gcc_assert (c);
+      do {
+	c = c->die_sib;
+	/* Found it.  */
+	if (c == die)
+	  break;
+	/* If we're at start --> not found.  */
+	gcc_assert (c != p->die_child);
+	/* If we've seen this node already the circular list doesn't
+	   even go back to start.  */
+	gcc_assert (c->die_abbrev != mark);
+	c->die_abbrev = mark;
+      } while (1);
+    }
+
+  if (!level)
+    return;
+
+  FOR_EACH_CHILD (die, c, check_die (c, level - 1));
+}
+
 
 /* Start a new compilation unit DIE for an include file.  OLD_UNIT is the CU
    for the enclosing include file, if any.  BINCL_DIE is the DW_TAG_GNU_BINCL
@@ -8657,9 +8701,10 @@  output_die (dw_die_ref die)
   if (! die->comdat_type_p && die->die_id.die_symbol)
     output_die_symbol (die);
 
-  dw2_asm_output_data_uleb128 (die->die_abbrev, "(DIE (%#lx) %s)",
+  dw2_asm_output_data_uleb128 (die->die_abbrev, "(DIE (%#lx) %s (parent DIE=%#lx))",
 			       (unsigned long)die->die_offset,
-			       dwarf_tag_name (die->die_tag));
+			       dwarf_tag_name (die->die_tag),
+			       die->die_parent ? die->die_parent->die_offset : 0);
 
   FOR_EACH_VEC_SAFE_ELT (die->die_attr, ix, a)
     {
@@ -17598,8 +17643,40 @@  gen_formal_parameter_die (tree node, tree origin, bool emit_name_p,
 {
   tree node_or_origin = node ? node : origin;
   tree ultimate_origin;
-  dw_die_ref parm_die
-    = new_die (DW_TAG_formal_parameter, context_die, node);
+  dw_die_ref parm_die = NULL;
+  
+  if (TREE_CODE_CLASS (TREE_CODE (node_or_origin)) == tcc_declaration)
+    {
+      parm_die = lookup_decl_die (node);
+
+      if (parm_die && parm_die->die_parent == NULL)
+	{
+	  /* Check that parm_die already has the right attributes that
+	     we would have added below.  If any attributes are
+	     missing, fall through to add them.
+
+	     ?? Add more checks here.  */
+	  if (! DECL_ABSTRACT (node_or_origin)
+	      && !get_AT (parm_die, DW_AT_location)
+	      && !get_AT (parm_die, DW_AT_const_value))
+	    /* We are missing  location info, and are about to add it.  */
+	    ;
+	  else
+	    {
+	      add_child_die (context_die, parm_die);
+	      return parm_die;
+	    }
+	}
+    }
+
+  bool reusing_die;
+  if (parm_die)
+    reusing_die = true;
+  else
+    {
+      parm_die = new_die (DW_TAG_formal_parameter, context_die, node);
+      reusing_die = false;
+    }
 
   switch (TREE_CODE_CLASS (TREE_CODE (node_or_origin)))
     {
@@ -17607,7 +17684,11 @@  gen_formal_parameter_die (tree node, tree origin, bool emit_name_p,
       ultimate_origin = decl_ultimate_origin (node_or_origin);
       if (node || ultimate_origin)
 	origin = ultimate_origin;
-      if (origin != NULL)
+
+      if (reusing_die)
+	goto add_location;
+
+      if (origin != NULL && node != origin)
 	add_abstract_origin_attribute (parm_die, origin);
       else if (emit_name_p)
 	add_name_and_src_coords_attributes (parm_die, node);
@@ -17631,6 +17712,7 @@  gen_formal_parameter_die (tree node, tree origin, bool emit_name_p,
 
       if (node && node != origin)
         equate_decl_number_to_die (node, parm_die);
+    add_location:
       if (! DECL_ABSTRACT (node_or_origin))
 	add_location_or_const_value_attribute (parm_die, node_or_origin,
 					       node == NULL, DW_AT_location);
@@ -18147,7 +18229,7 @@  gen_subprogram_die (tree decl, dw_die_ref context_die)
       && debug_info_level > DINFO_LEVEL_TERSE)
     old_die = force_decl_die (decl);
 
-  if (origin != NULL)
+  if (origin != NULL && origin != decl)
     {
       gcc_assert (!declaration || local_scope_p (context_die));
 
@@ -18177,7 +18259,13 @@  gen_subprogram_die (tree decl, dw_die_ref context_die)
 	  && !get_AT (old_die, DW_AT_inline))
 	{
 	  /* Detect and ignore this case, where we are trying to output
-	     something we have already output.  */
+	     something we have already output.
+
+	     If we have no location information, this must be a
+	     partially generated DIE from early dwarf generation.
+	     Fall through and generate it.  */
+	  if (get_AT (old_die, DW_AT_low_pc)
+	      || get_AT (old_die, DW_AT_ranges))
 	  return;
 	}
 
@@ -18196,6 +18284,10 @@  gen_subprogram_die (tree decl, dw_die_ref context_die)
 	{
 	  subr_die = old_die;
 
+	  /* ??? Hmmm, early dwarf generation happened earlier, so no
+	     sense in removing the parameters.  Let's keep them and
+	     augment them with location information later.  */
+	  if (0) {
 	  /* Clear out the declaration attribute and the formal parameters.
 	     Do not remove all children, because it is possible that this
 	     declaration die was forced using force_decl_die(). In such
@@ -18204,6 +18296,7 @@  gen_subprogram_die (tree decl, dw_die_ref context_die)
 	  remove_AT (subr_die, DW_AT_declaration);
 	  remove_AT (subr_die, DW_AT_object_pointer);
 	  remove_child_TAG (subr_die, DW_TAG_formal_parameter);
+	  }
 	}
       else
 	{
@@ -18294,9 +18387,12 @@  gen_subprogram_die (tree decl, dw_die_ref context_die)
 
       equate_decl_number_to_die (decl, subr_die);
     }
-  else if (!DECL_EXTERNAL (decl))
+  else if (!DECL_EXTERNAL (decl)
+	   && (!DECL_STRUCT_FUNCTION (decl)
+	       || DECL_STRUCT_FUNCTION (decl)->gimple_df))
     {
       HOST_WIDE_INT cfa_fb_offset;
+
       struct function *fun = DECL_STRUCT_FUNCTION (decl);
 
       if (!old_die || !get_AT (old_die, DW_AT_inline))
@@ -18459,10 +18555,20 @@  gen_subprogram_die (tree decl, dw_die_ref context_die)
 	add_AT_location_description (subr_die, DW_AT_static_link,
 		 loc_list_from_tree (fun->static_chain_decl, 2));
     }
+  else if (!DECL_EXTERNAL (decl))
+    {
+      if (!old_die || !get_AT (old_die, DW_AT_inline))
+	equate_decl_number_to_die (decl, subr_die);
+    }
 
   /* Generate child dies for template paramaters.  */
   if (debug_info_level > DINFO_LEVEL_TERSE)
-    gen_generic_params_dies (decl);
+    {
+      /* XXX */
+      if (!lookup_decl_die (decl))
+	equate_decl_number_to_die (decl, subr_die);
+      gen_generic_params_dies (decl);
+    }
 
   /* Now output descriptions of the arguments for this function. This gets
      (unnecessarily?) complex because of the fact that the DECL_ARGUMENT list
@@ -18566,7 +18672,9 @@  gen_subprogram_die (tree decl, dw_die_ref context_die)
      a BLOCK node representing the function's outermost pair of curly braces,
      and any blocks used for the base and member initializers of a C++
      constructor function.  */
-  if (! declaration && outer_scope && TREE_CODE (outer_scope) != ERROR_MARK)
+  if (! declaration && outer_scope && TREE_CODE (outer_scope) != ERROR_MARK
+      && (!DECL_STRUCT_FUNCTION (decl)
+	  || DECL_STRUCT_FUNCTION (decl)->gimple_df))
     {
       int call_site_note_count = 0;
       int tail_call_site_note_count = 0;
@@ -18886,7 +18994,7 @@  gen_variable_die (tree decl, tree origin, dw_die_ref context_die)
      and if we already emitted a DIE for it, don't emit a second
      DIE for it again. Allow re-declarations of DECLs that are
      inside functions, though.  */
-  if (old_die && declaration && !local_scope_p (context_die))
+  if (old_die && !declaration && !local_scope_p (context_die))
     return;
 
   /* For static data members, the declaration in the class is supposed
@@ -21007,6 +21115,54 @@  dwarf2out_decl (tree decl)
     }
 
   gen_decl_die (decl, NULL, context_die);
+
+  dw_die_ref die = lookup_decl_die (decl);
+  if (die)
+    check_die (die, 0);
+}
+
+/* Early dumping of DECLs before we lose language data.  */
+
+void
+dwarf2out_early_decl (tree decl)
+{
+  /* gen_decl_die() will set DECL_ABSTRACT because
+     cgraph_function_possibly_inlined_p() returns true.  This is in
+     turn will cause DW_AT_inline attributes to be set.
+
+     This happens because at early dwarf generation, there is no
+     cgraph information, causing cgraph_function_possibly_inlined_p()
+     to return true.  Trick cgraph_function_possibly_inlined_p()
+     while we generate dwarf early.  */
+  bool save = symtab->global_info_ready;
+  symtab->global_info_ready = true;
+
+  /* We don't handle TYPE_DECLs.  If required, they'll be reached via
+     other DECLs and they can point to template types or other things
+     that dwarf2out can't handle when done via dwarf2out_decl.  */
+  if (TREE_CODE (decl) != TYPE_DECL
+      && TREE_CODE (decl) != PARM_DECL)
+    {
+      if (TREE_CODE (decl) == FUNCTION_DECL)
+	{
+	  /* A missing cfun means the symbol is unused and was removed
+	     from the callgraph.  */
+	  if (!DECL_STRUCT_FUNCTION (decl))
+	    goto early_decl_exit;
+
+	  push_cfun (DECL_STRUCT_FUNCTION (decl));
+	  current_function_decl = decl;
+	}
+      dwarf2out_decl (decl);
+      if (TREE_CODE (decl) == FUNCTION_DECL)
+	{
+	  pop_cfun ();
+	  current_function_decl = NULL;
+	}
+    }
+ early_decl_exit:
+  symtab->global_info_ready = save;
+  return;
 }
 
 /* Write the debugging output for DECL.  */
diff --git a/gcc/tree.c b/gcc/tree.c
index 33922a0..ce46191 100644
--- a/gcc/tree.c
+++ b/gcc/tree.c
@@ -5030,6 +5030,10 @@  free_lang_data_in_decl (tree decl)
 {
   gcc_assert (DECL_P (decl));
 
+  /* Early dumping of DECLs before we lose language data.  */
+  if (debug_info_level > DINFO_LEVEL_NONE)
+    dwarf2out_early_decl (decl);
+
   /* Give the FE a chance to remove its own data first.  */
   lang_hooks.free_lang_data (decl);
 
@@ -5628,8 +5632,7 @@  free_lang_data (void)
   unsigned i;
 
   /* If we are the LTO frontend we have freed lang-specific data already.  */
-  if (in_lto_p
-      || !flag_generate_lto)
+  if (in_lto_p)
     return 0;
 
   /* Allocate and assign alias sets to the standard integer types