Hi, this patch fixes checking ICE in the attached testcase that happens because we end up devirtualizing into a function that is not withing possible polymorphic call target. The defauled analysis is in the PR and also in comment bellow. The basic problem is that we build type inheritance tree starting from virtual methods assuming that we only need polymorphic types with methods defined in it.
This is not completely the case for virtual inheritance, where the class itself uses a method, but derived classes (not having virtual methods themselves) may use a virtual thunk. This patch completes the hiearchy by looking for virtual tables defined. This is still not 100% sure since we may forget enlisting virtual thunks used from other file, but since in that case the thunks needs to be exported our standard visibility code will handle it. Bootstrapped/regtested x86_64-linux. Honza PR ipa/58585 * ipa-devirt.c (build_type_inheritance_graph): Also add types of vtables into the type inheritance graph. * g++.dg/torture/pr58585.C: New testcase. Index: ipa-devirt.c =================================================================== --- ipa-devirt.c (revision 206516) +++ ipa-devirt.c (working copy) @@ -542,7 +542,7 @@ method_class_type (tree t) void build_type_inheritance_graph (void) { - struct cgraph_node *n; + struct symtab_node *n; FILE *inheritance_dump_file; int flags; @@ -554,10 +554,37 @@ build_type_inheritance_graph (void) /* We reconstruct the graph starting of types of all methods seen in the the unit. */ - FOR_EACH_FUNCTION (n) - if (DECL_VIRTUAL_P (n->decl) + FOR_EACH_SYMBOL (n) + if (is_a <cgraph_node> (n) + && DECL_VIRTUAL_P (n->decl) && symtab_real_symbol_p (n)) get_odr_type (method_class_type (TREE_TYPE (n->decl)), true); + + /* Look also for virtual tables of types that do not define any methods. + + We need it in a case where class B has virtual base of class A + re-defining its virtual method and there is class C with no virtual + methods with B as virtual base. + + Here we output B's virtual method in two variant - for non-virtual + and virtual inheritance. B's virtual table has non-virtual version, + while C's has virtual. + + For this reason we need to know about C in order to include both + variants of B. More correctly, record_target_from_binfo should + add both variants of the method when walking B, but we have no + link in between them. + + We rely on fact that either the method is exported and thus we + assume it is called externally or C is in anonymous namespace and + thus we will see the vtable. */ + + else if (is_a <varpool_node> (n) + && DECL_VIRTUAL_P (n->decl) + && TREE_CODE (DECL_CONTEXT (n->decl)) == RECORD_TYPE + && TYPE_BINFO (DECL_CONTEXT (n->decl)) + && polymorphic_type_binfo_p (TYPE_BINFO (DECL_CONTEXT (n->decl)))) + get_odr_type (DECL_CONTEXT (n->decl), true); if (inheritance_dump_file) { dump_type_inheritance_graph (inheritance_dump_file); Index: testsuite/g++.dg/torture/pr58585.C =================================================================== --- testsuite/g++.dg/torture/pr58585.C (revision 0) +++ testsuite/g++.dg/torture/pr58585.C (working copy) @@ -0,0 +1,20 @@ +// { dg-do compile } +// { dg-options "-fpic" { target fpic } } +struct A +{ + virtual void foo() {} + void bar(); +}; +void A::bar() { foo(); } + +struct B : virtual A +{ + virtual void foo() {} + char c; +}; + +struct C : virtual B +{ + C(); +}; +C::C() { bar(); }