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);
+ }

Reply via email to