The following patch fixes PR65626 - an ordering issue with removing BBs and fixing up noreturn stmts. The fix is simple - delay BB removal to the CFG cleanup run and only split the BB at fixup_noreturn_stmt time so CFG cleanup can find it in O(1). (similarly EH/abnormal cleanup should only remove edges, not dominated blocks, but ...)
Bootstrapped on x86_64-unknown-linux-gnu, testing in progress. Richard. 2015-03-30 Richard Biener <rguent...@suse.de> PR middle-end/65626 * tree-cfgcleanup.c (fixup_noreturn_call): Only split the block of the noreturn call so it is last and cleanup_control_flow_bb can do the CFG part. * g++.dg/torture/pr65626.C: New testcase. Index: gcc/tree-cfgcleanup.c =================================================================== *** gcc/tree-cfgcleanup.c (revision 221770) --- gcc/tree-cfgcleanup.c (working copy) *************** remove_forwarder_block (basic_block bb) *** 579,591 **** return true; } ! /* STMT is a call that has been discovered noreturn. Fixup the CFG ! and remove LHS. Return true if something changed. */ bool fixup_noreturn_call (gimple stmt) { basic_block bb = gimple_bb (stmt); if (gimple_call_builtin_p (stmt, BUILT_IN_RETURN)) return false; --- 579,593 ---- return true; } ! /* STMT is a call that has been discovered noreturn. Split the ! block to prepare fixing up the CFG and remove LHS. ! Return true if cleanup-cfg needs to run. */ bool fixup_noreturn_call (gimple stmt) { basic_block bb = gimple_bb (stmt); + bool changed = false; if (gimple_call_builtin_p (stmt, BUILT_IN_RETURN)) return false; *************** fixup_noreturn_call (gimple stmt) *** 604,610 **** gsi_remove (&gsi, true); } else ! split_block (bb, stmt); } /* If there is an LHS, remove it. */ --- 606,615 ---- gsi_remove (&gsi, true); } else ! { ! split_block (bb, stmt); ! changed = true; ! } } /* If there is an LHS, remove it. */ *************** fixup_noreturn_call (gimple stmt) *** 626,634 **** } /* Mark the call as altering control flow. */ ! gimple_call_set_ctrl_altering (stmt, true); ! return remove_fallthru_edge (bb->succs); } --- 631,643 ---- } /* Mark the call as altering control flow. */ ! if (!gimple_call_ctrl_altering_p (stmt)) ! { ! gimple_call_set_ctrl_altering (stmt, true); ! changed = true; ! } ! return changed; } Index: gcc/testsuite/g++.dg/torture/pr65626.C =================================================================== *** gcc/testsuite/g++.dg/torture/pr65626.C (revision 0) --- gcc/testsuite/g++.dg/torture/pr65626.C (working copy) *************** *** 0 **** --- 1,19 ---- + // { dg-do compile } + + class A { + virtual unsigned long m_fn1() const; + virtual int &m_fn2(unsigned long) const; + }; + class C : A { + public: + int &m_fn2(unsigned long) const; + unsigned long m_fn1() const; + }; + class B { + void m_fn3(const A &, const int &, const C &, int &) const; + }; + void B::m_fn3(const A &, const int &, const C &, int &) const { + C &a(a); + for (long b = 0; a.m_fn1(); b++) + a.m_fn2(0); + }