diff mbox

Fix handling of inlining and nested functions in dwarf2out.c

Message ID 5734533.5QCkdLUcOB@polaris
State New
Headers show

Commit Message

Eric Botcazou Nov. 7, 2014, 6:41 p.m. UTC
Hi,

the mix of inlining and nested functions is an interesting challenge on the 
debug info side because it generates cycles in the debug info: if a child 
calls its parent and the parent is inlined but not the child, you have the 
(non-abstract) instance of the child nested in the abstract instance of the 
parent and containing a concrete inline instance of the parent which points 
back to the abstract instance, as per the DWARF spec.  This is what GCC has 
been generating for a while, although this caused GDB to crash until very 
recently (only GDB 7.7 and later versions are OK).

Under very special circumstances[*], you can even have a cycle in the internal 
representation of dwarf2out.c leading to an ICE because the DIEs in the cycle 
have no ultimate parent.  This happens if you have, in addition to the above 
setup, a grandparent which is both inlined in one of its callers and output as 
a standalone function: when gen_decl_die is invoked on the grandparent, the 
cgraph_function_possibly_inlined_p predicate is true so set_decl_origin_self 
is invoked on the grandparent; now set_decl_origin_self recurses down the 
entire nest of functions, so the child is also marked as originating from 
itself by the recursion.  Later, when gen_decl_die is invoked on the child,
the cgraph_function_possibly_inlined_p predicate is false for it so 
dwarf2out_abstract_function is not invoked before gen_subprogram_die,
which results in a DIE with DW_AT_abstract_origin pointing to itself and 
without parent for the child.  Moreover, when gen_subprogram_die is invoked on 
the concrete inline instance of the parent, this DIE is retrieved and

      /* 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);

attaches it to the context_die, creating the cycle since we are in the child.

I think that the source of the problem is the discrepancy between the 
cgraph_function_possibly_inlined_p predicate, which doesn't consider the 
inlining status of the child as being related to that of its parents (which is 
explicitly allowed by the DWARF spec) and the set_decl_origin_self recursion, 
which runs down the entire nest.  Hence the attached patchlet, which is 
sufficient to get rid of the cycle and, therefore, of the ICE.

Bootstrapped and regtested on x86_64-suse-linux, OK for the mainline?


2014-11-07  Eric Botcazou  <ebotcazou@adacore.com>

	* dwarf2out.c (set_block_origin_self): Skip nested functions.



[*] There is an important factor coming into play, which is the order in which 
the functions are sent to dwarf2out.c.  That's decided by the cgraph machinery 
and the ICE happens only when the grandparent is sent before the child.  Now, 
while it's very easy to have this situation without inlining, it's very hard 
when the functions start being inlined because the cgraph machinery sends them
after their callers.  We have a large testcase of several big Ada units for 
which the combination of the various IPA transformations (inlining, cloning, 
etc) and the repeated topological sorts on the callgraph lead to the ICE, but 
any attempt at reducing makes it disappear.

Comments

Jason Merrill Nov. 26, 2014, 3:02 p.m. UTC | #1
I ran a quick test to see if the output after this patch matches the 
examples in D.7, and it does.  So the patch is OK.

Jason
diff mbox

Patch

Index: dwarf2out.c
===================================================================
--- dwarf2out.c	(revision 217148)
+++ dwarf2out.c	(working copy)
@@ -17919,8 +17919,11 @@  set_block_origin_self (tree stmt)
 	for (local_decl = BLOCK_VARS (stmt);
 	     local_decl != NULL_TREE;
 	     local_decl = DECL_CHAIN (local_decl))
-	  if (! DECL_EXTERNAL (local_decl))
-	    set_decl_origin_self (local_decl);	/* Potential recursion.  */
+	  /* Do not recurse on nested functions since the inlining status
+	     of parent and child can be different as per the DWARF spec.  */
+	  if (TREE_CODE (local_decl) != FUNCTION_DECL
+	      && !DECL_EXTERNAL (local_decl))
+	    set_decl_origin_self (local_decl);
       }
 
       {