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 <ebotca...@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.
--
Eric Botcazou
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);
}
{