Hi, this reorganization of cgraph_node:clone_of_p() prevents verifier falsely ICEing because it cannot recognize that a former thunk (expanded even before reaching pass pipeline) was cloned by IPA-CP.
It basically traverses the clone_of chain at each step of thunk chain traversal, rather than just after it. This is only done when checking is on, so the extra little overhead should be of little concern. Bootstrapped, LTO-bootstrapped and tested on x86_64, OK for trunk? If so, I will check if we need it in GCC 8 and if so, backport it there too. Thanks, Martin 2019-04-11 Martin Jambor <mjam...@suse.cz> PR ipa/89693 * cgraph.c (clone_of_p): Loop over clone chain for each step in the thunk chain. testsuite/ * g++.dg/ipa/pr89693.C: New test. --- gcc/cgraph.c | 30 ++++++++++------- gcc/testsuite/g++.dg/ipa/pr89693.C | 52 ++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 12 deletions(-) create mode 100644 gcc/testsuite/g++.dg/ipa/pr89693.C diff --git a/gcc/cgraph.c b/gcc/cgraph.c index 49d80ad1e28..b1b0b4c42d5 100644 --- a/gcc/cgraph.c +++ b/gcc/cgraph.c @@ -2977,17 +2977,25 @@ cgraph_node::collect_callers (void) static bool clone_of_p (cgraph_node *node, cgraph_node *node2) { - bool skipped_thunk = false; node = node->ultimate_alias_target (); node2 = node2->ultimate_alias_target (); + if (node2->clone_of == node + || node2->former_clone_of == node->decl) + return true; + + if (!node->thunk.thunk_p && !node->former_thunk_p ()) + { + while (node2 && node->decl != node2->decl) + node2 = node2->clone_of; + return node2 != NULL; + } + /* There are no virtual clones of thunks so check former_clone_of or if we might have skipped thunks because this adjustments are no longer necessary. */ while (node->thunk.thunk_p || node->former_thunk_p ()) { - if (node2->former_clone_of == node->decl) - return true; if (!node->thunk.this_adjusting) return false; /* In case of instrumented expanded thunks, which can have multiple calls @@ -2996,23 +3004,21 @@ clone_of_p (cgraph_node *node, cgraph_node *node2) if (node->callees->next_callee) return true; node = node->callees->callee->ultimate_alias_target (); - skipped_thunk = true; - } - if (skipped_thunk) - { if (!node2->clone.args_to_skip || !bitmap_bit_p (node2->clone.args_to_skip, 0)) return false; if (node2->former_clone_of == node->decl) return true; - else if (!node2->clone_of) - return false; + + cgraph_node *n2 = node2; + while (n2 && node->decl != n2->decl) + n2 = n2->clone_of; + if (n2) + return true; } - while (node2 && node->decl != node2->decl) - node2 = node2->clone_of; - return node2 != NULL; + return false; } /* Verify edge count and frequency. */ diff --git a/gcc/testsuite/g++.dg/ipa/pr89693.C b/gcc/testsuite/g++.dg/ipa/pr89693.C new file mode 100644 index 00000000000..4ac83eeeb3a --- /dev/null +++ b/gcc/testsuite/g++.dg/ipa/pr89693.C @@ -0,0 +1,52 @@ +// Copyright (C) 2005 Free Software Foundation, Inc. +// Contributed by Nathan Sidwell 4 Apr 2005 <nat...@codesourcery.com> +// Re-purposed to check for re-rurgesnce of PR 89693 in 2019. + +// { dg-do compile } +// { dg-options "-O3 -fno-ipa-icf-functions" } + +// Origin: yan...@ca.ibm.com +// nat...@codesourcery.com + +struct A { + virtual void One (); +}; +struct B { + virtual B *Two (); + virtual B &Three (); +}; + +struct C : A, B +{ + virtual C *Two (); + virtual C &Three (); +}; +void A::One () {} +B *B::Two() {return this;} +B &B::Three() {return *this;} +C *C::Two () {return 0;} +C &C::Three () {return *(C *)0;} + +B *Foo (B *b) +{ + return b->Two (); +} + +B &Bar (B *b) +{ + return b->Three (); +} + +int main () +{ + C c; + + /* We should not adjust a null pointer. */ + if (Foo (&c)) + return 1; + /* But we should adjust a (bogus) null reference. */ + if (!&Bar (&c)) + return 2; + + return 0; +} -- 2.21.0