re PR target/65697 (__atomic memory barriers not strong enough for __sync builtins)
[gcc.git] / gcc / tree-cfgcleanup.c
index fc2141f48c179ec322bdc6589f65618584957915..07e2d7465b005c21be42f25ac680667fac3b47aa 100644 (file)
@@ -1,6 +1,5 @@
 /* CFG cleanup for trees.
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
-   Free Software Foundation, Inc.
+   Copyright (C) 2001-2015 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -22,24 +21,51 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "tm.h"
+#include "alias.h"
+#include "symtab.h"
 #include "tree.h"
+#include "fold-const.h"
 #include "tm_p.h"
+#include "predict.h"
+#include "hard-reg-set.h"
+#include "function.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "cfganal.h"
+#include "cfgcleanup.h"
 #include "basic-block.h"
-#include "output.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
 #include "flags.h"
-#include "function.h"
-#include "ggc.h"
 #include "langhooks.h"
-#include "tree-flow.h"
-#include "timevar.h"
-#include "tree-dump.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "gimple.h"
+#include "gimplify.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "tree-cfg.h"
+#include "tree-phinodes.h"
+#include "ssa-iterators.h"
+#include "stringpool.h"
+#include "tree-ssanames.h"
+#include "tree-ssa-loop-manip.h"
+#include "rtl.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
+#include "expr.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
 #include "tree-pass.h"
-#include "toplev.h"
 #include "except.h"
 #include "cfgloop.h"
-#include "cfglayout.h"
-#include "hashtab.h"
 #include "tree-ssa-propagate.h"
 #include "tree-scalar-evolution.h"
 
@@ -55,7 +81,7 @@ bitmap cfgcleanup_altered_bbs;
 /* Remove any fallthru edge from EV.  Return true if an edge was removed.  */
 
 static bool
-remove_fallthru_edge (VEC(edge,gc) *ev)
+remove_fallthru_edge (vec<edge, va_gc> *ev)
 {
   edge_iterator ei;
   edge e;
@@ -63,7 +89,10 @@ remove_fallthru_edge (VEC(edge,gc) *ev)
   FOR_EACH_EDGE (e, ei, ev)
     if ((e->flags & EDGE_FALLTHRU) != 0)
       {
-       remove_edge_and_dominated_blocks (e);
+       if (e->flags & EDGE_COMPLEX)
+         e->flags &= ~EDGE_FALLTHRU;
+       else
+         remove_edge_and_dominated_blocks (e);
        return true;
       }
   return false;
@@ -93,43 +122,14 @@ cleanup_control_expr_graph (basic_block bb, gimple_stmt_iterator gsi)
       switch (gimple_code (stmt))
        {
        case GIMPLE_COND:
-         {
-           tree lhs = gimple_cond_lhs (stmt);
-           tree rhs = gimple_cond_rhs (stmt);
-           /* For conditions try harder and lookup single-argument
-              PHI nodes.  Only do so from the same basic-block though
-              as other basic-blocks may be dead already.  */
-           if (TREE_CODE (lhs) == SSA_NAME
-               && !name_registered_for_update_p (lhs))
-             {
-               gimple def_stmt = SSA_NAME_DEF_STMT (lhs);
-               if (gimple_code (def_stmt) == GIMPLE_PHI
-                   && gimple_phi_num_args (def_stmt) == 1
-                   && gimple_bb (def_stmt) == gimple_bb (stmt)
-                   && (TREE_CODE (PHI_ARG_DEF (def_stmt, 0)) != SSA_NAME
-                       || !name_registered_for_update_p (PHI_ARG_DEF (def_stmt,
-                                                                      0))))
-                 lhs = PHI_ARG_DEF (def_stmt, 0);
-             }
-           if (TREE_CODE (rhs) == SSA_NAME
-               && !name_registered_for_update_p (rhs))
-             {
-               gimple def_stmt = SSA_NAME_DEF_STMT (rhs);
-               if (gimple_code (def_stmt) == GIMPLE_PHI
-                   && gimple_phi_num_args (def_stmt) == 1
-                   && gimple_bb (def_stmt) == gimple_bb (stmt)
-                   && (TREE_CODE (PHI_ARG_DEF (def_stmt, 0)) != SSA_NAME
-                       || !name_registered_for_update_p (PHI_ARG_DEF (def_stmt,
-                                                                      0))))
-                 rhs = PHI_ARG_DEF (def_stmt, 0);
-             }
-           val = fold_binary_loc (loc, gimple_cond_code (stmt),
-                                  boolean_type_node, lhs, rhs);
-           break;
-         }
+         val = fold_binary_loc (loc, gimple_cond_code (stmt),
+                                boolean_type_node,
+                                gimple_cond_lhs (stmt),
+                                gimple_cond_rhs (stmt));
+         break;
 
        case GIMPLE_SWITCH:
-         val = gimple_switch_index (stmt);
+         val = gimple_switch_index (as_a <gswitch *> (stmt));
          break;
 
        default:
@@ -178,6 +178,23 @@ cleanup_control_expr_graph (basic_block bb, gimple_stmt_iterator gsi)
   return retval;
 }
 
+/* Cleanup the GF_CALL_CTRL_ALTERING flag according to
+   to updated gimple_call_flags.  */
+
+static void
+cleanup_call_ctrl_altering_flag (gimple bb_end)
+{
+  if (!is_gimple_call (bb_end)
+      || !gimple_call_ctrl_altering_p (bb_end))
+    return;
+
+  int flags = gimple_call_flags (bb_end);
+  if (((flags & (ECF_CONST | ECF_PURE))
+       && !(flags & ECF_LOOPING_CONST_OR_PURE))
+      || (flags & ECF_LEAF))
+    gimple_call_set_ctrl_altering (bb_end, false);
+}
+
 /* Try to remove superfluous control structures in basic block BB.  Returns
    true if anything changes.  */
 
@@ -198,6 +215,9 @@ cleanup_control_flow_bb (basic_block bb)
 
   stmt = gsi_stmt (gsi);
 
+  /* Try to cleanup ctrl altering flag for call which ends bb.  */
+  cleanup_call_ctrl_altering_flag (stmt);
+
   if (gimple_code (stmt) == GIMPLE_COND
       || gimple_code (stmt) == GIMPLE_SWITCH)
     retval |= cleanup_control_expr_graph (bb, gsi);
@@ -257,7 +277,7 @@ cleanup_control_flow_bb (basic_block bb)
    the start of the successor block.
 
    As a precondition, we require that BB be not equal to
-   ENTRY_BLOCK_PTR.  */
+   the entry block.  */
 
 static bool
 tree_forwarder_block_p (basic_block bb, bool phi_wanted)
@@ -270,17 +290,15 @@ tree_forwarder_block_p (basic_block bb, bool phi_wanted)
       /* If PHI_WANTED is false, BB must not have any PHI nodes.
         Otherwise, BB must have PHI nodes.  */
       || gimple_seq_empty_p (phi_nodes (bb)) == phi_wanted
-      /* BB may not be a predecessor of EXIT_BLOCK_PTR.  */
-      || single_succ (bb) == EXIT_BLOCK_PTR
+      /* BB may not be a predecessor of the exit block.  */
+      || single_succ (bb) == EXIT_BLOCK_PTR_FOR_FN (cfun)
       /* Nor should this be an infinite loop.  */
       || single_succ (bb) == bb
       /* BB may not have an abnormal outgoing edge.  */
       || (single_succ_edge (bb)->flags & EDGE_ABNORMAL))
     return false;
 
-#if ENABLE_CHECKING
-  gcc_assert (bb != ENTRY_BLOCK_PTR);
-#endif
+  gcc_checking_assert (bb != ENTRY_BLOCK_PTR_FOR_FN (cfun));
 
   locus = single_succ_edge (bb)->goto_locus;
 
@@ -290,7 +308,7 @@ tree_forwarder_block_p (basic_block bb, bool phi_wanted)
     edge e;
 
     FOR_EACH_EDGE (e, ei, bb->preds)
-      if (e->src == ENTRY_BLOCK_PTR || (e->flags & EDGE_EH))
+      if (e->src == ENTRY_BLOCK_PTR_FOR_FN (cfun) || (e->flags & EDGE_EH))
        return false;
       /* If goto_locus of any of the edges differs, prevent removing
         the forwarder block for -O0.  */
@@ -307,7 +325,7 @@ tree_forwarder_block_p (basic_block bb, bool phi_wanted)
       switch (gimple_code (stmt))
        {
        case GIMPLE_LABEL:
-         if (DECL_NONLOCAL (gimple_label_label (stmt)))
+         if (DECL_NONLOCAL (gimple_label_label (as_a <glabel *> (stmt))))
            return false;
          if (optimize == 0 && gimple_location (stmt) != locus)
            return false;
@@ -326,30 +344,34 @@ tree_forwarder_block_p (basic_block bb, bool phi_wanted)
   if (current_loops)
     {
       basic_block dest;
-      /* Protect loop latches, headers and preheaders.  */
+      /* Protect loop headers.  */
       if (bb->loop_father->header == bb)
        return false;
-      dest = EDGE_SUCC (bb, 0)->dest;
 
+      dest = EDGE_SUCC (bb, 0)->dest;
+      /* Protect loop preheaders and latches if requested.  */
       if (dest->loop_father->header == dest)
-       return false;
+       {
+         if (bb->loop_father == dest->loop_father)
+           {
+             if (loops_state_satisfies_p (LOOPS_HAVE_SIMPLE_LATCHES))
+               return false;
+             /* If bb doesn't have a single predecessor we'd make this
+                loop have multiple latches.  Don't do that if that
+                would in turn require disambiguating them.  */
+             return (single_pred_p (bb)
+                     || loops_state_satisfies_p
+                          (LOOPS_MAY_HAVE_MULTIPLE_LATCHES));
+           }
+         else if (bb->loop_father == loop_outer (dest->loop_father))
+           return !loops_state_satisfies_p (LOOPS_HAVE_PREHEADERS);
+         /* Always preserve other edges into loop headers that are
+            not simple latches or preheaders.  */
+         return false;
+       }
     }
-  return true;
-}
-
-/* Return true if BB has at least one abnormal incoming edge.  */
-
-static inline bool
-has_abnormal_incoming_edge_p (basic_block bb)
-{
-  edge e;
-  edge_iterator ei;
 
-  FOR_EACH_EDGE (e, ei, bb->preds)
-    if (e->flags & EDGE_ABNORMAL)
-      return true;
-
-  return false;
+  return true;
 }
 
 /* If all the PHI nodes in DEST have alternatives for E1 and E2 and
@@ -361,11 +383,11 @@ phi_alternatives_equal (basic_block dest, edge e1, edge e2)
 {
   int n1 = e1->dest_idx;
   int n2 = e2->dest_idx;
-  gimple_stmt_iterator gsi;
+  gphi_iterator gsi;
 
   for (gsi = gsi_start_phis (dest); !gsi_end_p (gsi); gsi_next (&gsi))
     {
-      gimple phi = gsi_stmt (gsi);
+      gphi *phi = gsi.phi ();
       tree val1 = gimple_phi_arg_def (phi, n1);
       tree val2 = gimple_phi_arg_def (phi, n2);
 
@@ -400,11 +422,11 @@ remove_forwarder_block (basic_block bb)
   /* If the destination block consists of a nonlocal label or is a
      EH landing pad, do not merge it.  */
   label = first_stmt (dest);
-  if (label
-      && gimple_code (label) == GIMPLE_LABEL
-      && (DECL_NONLOCAL (gimple_label_label (label))
-         || EH_LANDING_PAD_NR (gimple_label_label (label)) != 0))
-    return false;
+  if (label)
+    if (glabel *label_stmt = dyn_cast <glabel *> (label))
+      if (DECL_NONLOCAL (gimple_label_label (label_stmt))
+         || EH_LANDING_PAD_NR (gimple_label_label (label_stmt)) != 0)
+       return false;
 
   /* If there is an abnormal edge to basic block BB, but not into
      dest, problems might occur during removal of the phi node at out
@@ -417,8 +439,8 @@ remove_forwarder_block (basic_block bb)
 
      So if there is an abnormal edge to BB, proceed only if there is
      no abnormal edge to DEST and there are no phi nodes in DEST.  */
-  if (has_abnormal_incoming_edge_p (bb)
-      && (has_abnormal_incoming_edge_p (dest)
+  if (bb_has_abnormal_pred (bb)
+      && (bb_has_abnormal_pred (dest)
          || !gimple_seq_empty_p (phi_nodes (dest))))
     return false;
 
@@ -438,7 +460,11 @@ remove_forwarder_block (basic_block bb)
        }
     }
 
-  can_move_debug_stmts = single_pred_p (dest);
+  can_move_debug_stmts = MAY_HAVE_DEBUG_STMTS && single_pred_p (dest);
+
+  basic_block pred = NULL;
+  if (single_pred_p (bb))
+    pred = single_pred (bb);
 
   /* Redirect the edges.  */
   for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
@@ -458,13 +484,14 @@ remove_forwarder_block (basic_block bb)
        {
          /* Create arguments for the phi nodes, since the edge was not
             here before.  */
-         for (gsi = gsi_start_phis (dest);
-              !gsi_end_p (gsi);
-              gsi_next (&gsi))
+         for (gphi_iterator psi = gsi_start_phis (dest);
+              !gsi_end_p (psi);
+              gsi_next (&psi))
            {
-             gimple phi = gsi_stmt (gsi);
+             gphi *phi = psi.phi ();
              source_location l = gimple_phi_arg_location_from_edge (phi, succ);
-             add_phi_arg (phi, gimple_phi_arg_def (phi, succ->dest_idx), s, l);
+             tree def = gimple_phi_arg_def (phi, succ->dest_idx);
+             add_phi_arg (phi, unshare_expr (def), s, l);
            }
        }
     }
@@ -481,7 +508,7 @@ remove_forwarder_block (basic_block bb)
       label = gsi_stmt (gsi);
       if (is_gimple_debug (label))
        break;
-      decl = gimple_label_label (label);
+      decl = gimple_label_label (as_a <glabel *> (label));
       if (EH_LANDING_PAD_NR (decl) != 0
          || DECL_NONLOCAL (decl)
          || FORCED_LABEL (decl)
@@ -494,8 +521,7 @@ remove_forwarder_block (basic_block bb)
        gsi_next (&gsi);
     }
 
-  /* Move debug statements if the destination has just a single
-     predecessor.  */
+  /* Move debug statements if the destination has a single predecessor.  */
   if (can_move_debug_stmts)
     {
       gsi_to = gsi_after_labels (dest);
@@ -530,14 +556,20 @@ remove_forwarder_block (basic_block bb)
       set_immediate_dominator (CDI_DOMINATORS, dest, dom);
     }
 
+  /* Adjust latch infomation of BB's parent loop as otherwise
+     the cfg hook has a hard time not to kill the loop.  */
+  if (current_loops && bb->loop_father->latch == bb)
+    bb->loop_father->latch = pred;
+
   /* And kill the forwarder block.  */
   delete_basic_block (bb);
 
   return true;
 }
 
-/* STMT is a call that has been discovered noreturn.  Fixup the CFG
-   and remove LHS.  Return true if something changed.  */
+/* 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)
@@ -550,95 +582,54 @@ fixup_noreturn_call (gimple stmt)
 
   /* First split basic block if stmt is not last.  */
   if (stmt != gsi_stmt (gsi_last_bb (bb)))
-    split_block (bb, stmt);
-
-  changed |= remove_fallthru_edge (bb->succs);
-
-  /* If there is LHS, remove it.  */
-  if (gimple_call_lhs (stmt))
     {
-      tree op = gimple_call_lhs (stmt);
-      gimple_call_set_lhs (stmt, NULL_TREE);
-      /* We need to remove SSA name to avoid checking.
-        All uses are dominated by the noreturn and thus will
-        be removed afterwards.  */
-      if (TREE_CODE (op) == SSA_NAME)
+      if (stmt == gsi_stmt (gsi_last_nondebug_bb (bb)))
        {
-         use_operand_p use_p;
-          imm_use_iterator iter;
-         gimple use_stmt;
-
-          FOR_EACH_IMM_USE_STMT (use_stmt, iter, op)
-           FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
-             SET_USE (use_p, error_mark_node);
+         /* Don't split if there are only debug stmts
+            after stmt, that can result in -fcompare-debug
+            failures.  Remove the debug stmts instead,
+            they should be all unreachable anyway.  */
+         gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+         for (gsi_next (&gsi); !gsi_end_p (gsi); )
+           gsi_remove (&gsi, true);
+       }
+      else
+       {
+         split_block (bb, stmt);
+         changed = true;
        }
-      update_stmt (stmt);
-      changed = true;
     }
-  return changed;
-}
 
+  /* If there is an LHS, remove it, but only if its type has fixed size.
+     The LHS will need to be recreated during RTL expansion and creating
+     temporaries of variable-sized types is not supported.  */
+  tree lhs = gimple_call_lhs (stmt);
+  if (lhs && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (lhs))) == INTEGER_CST)
+    {
+      gimple_call_set_lhs (stmt, NULL_TREE);
 
-/* Split basic blocks on calls in the middle of a basic block that are now
-   known not to return, and remove the unreachable code.  */
+      /* We need to fix up the SSA name to avoid checking errors.  */
+      if (TREE_CODE (lhs) == SSA_NAME)
+       {
+         tree new_var = create_tmp_reg (TREE_TYPE (lhs));
+         SET_SSA_NAME_VAR_OR_IDENTIFIER (lhs, new_var);
+         SSA_NAME_DEF_STMT (lhs) = gimple_build_nop ();
+         set_ssa_default_def (cfun, new_var, lhs);
+       }
 
-static bool
-split_bbs_on_noreturn_calls (void)
-{
-  bool changed = false;
-  gimple stmt;
-  basic_block bb;
+      update_stmt (stmt);
+    }
 
-  /* Detect cases where a mid-block call is now known not to return.  */
-  if (cfun->gimple_df)
-    while (VEC_length (gimple, MODIFIED_NORETURN_CALLS (cfun)))
-      {
-       stmt = VEC_pop (gimple, MODIFIED_NORETURN_CALLS (cfun));
-       bb = gimple_bb (stmt);
-       /* BB might be deleted at this point, so verify first
-          BB is present in the cfg.  */
-       if (bb == NULL
-           || bb->index < NUM_FIXED_BLOCKS
-           || bb->index >= n_basic_blocks
-           || BASIC_BLOCK (bb->index) != bb
-           || !gimple_call_noreturn_p (stmt))
-         continue;
-
-       changed |= fixup_noreturn_call (stmt);
-      }
+  /* 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;
 }
 
-/* If GIMPLE_OMP_RETURN in basic block BB is unreachable, remove it.  */
-
-static bool
-cleanup_omp_return (basic_block bb)
-{
-  gimple stmt = last_stmt (bb);
-  basic_block control_bb;
-
-  if (stmt == NULL
-      || gimple_code (stmt) != GIMPLE_OMP_RETURN
-      || !single_pred_p (bb))
-    return false;
-
-  control_bb = single_pred (bb);
-  stmt = last_stmt (control_bb);
-
-  if (stmt == NULL || gimple_code (stmt) != GIMPLE_OMP_SECTIONS_SWITCH)
-    return false;
-
-  /* The block with the control statement normally has two entry edges -- one
-     from entry, one from continue.  If continue is removed, return is
-     unreachable, so we remove it here as well.  */
-  if (EDGE_COUNT (control_bb->preds) == 2)
-    return false;
-
-  gcc_assert (EDGE_COUNT (control_bb->preds) == 1);
-  remove_edge_and_dominated_blocks (single_pred_edge (bb));
-  return true;
-}
 
 /* Tries to cleanup cfg in basic block BB.  Returns true if anything
    changes.  */
@@ -646,12 +637,7 @@ cleanup_omp_return (basic_block bb)
 static bool
 cleanup_tree_cfg_bb (basic_block bb)
 {
-  bool retval = false;
-
-  if (cleanup_omp_return (bb))
-    return true;
-
-  retval = cleanup_control_flow_bb (bb);
+  bool retval = cleanup_control_flow_bb (bb);
 
   if (tree_forwarder_block_p (bb, false)
       && remove_forwarder_block (bb))
@@ -663,8 +649,18 @@ cleanup_tree_cfg_bb (basic_block bb)
   if (single_succ_p (bb)
       && can_merge_blocks_p (bb, single_succ (bb)))
     {
-      merge_blocks (bb, single_succ (bb));
-      return true;
+      /* If there is a merge opportunity with the predecessor
+         do nothing now but wait until we process the predecessor.
+        This happens when we visit BBs in a non-optimal order and
+        avoids quadratic behavior with adjusting stmts BB pointer.  */
+      if (single_pred_p (bb)
+         && can_merge_blocks_p (single_pred (bb), bb))
+       ;
+      else
+       {
+         merge_blocks (bb, single_succ (bb));
+         return true;
+       }
     }
 
   return retval;
@@ -679,8 +675,6 @@ cleanup_tree_cfg_1 (void)
   basic_block bb;
   unsigned i, n;
 
-  retval |= split_bbs_on_noreturn_calls ();
-
   /* Prepare the worklists of altered blocks.  */
   cfgcleanup_altered_bbs = BITMAP_ALLOC (NULL);
 
@@ -689,12 +683,12 @@ cleanup_tree_cfg_1 (void)
      recording of edge to CASE_LABEL_EXPR.  */
   start_recording_case_labels ();
 
-  /* Start by iterating over all basic blocks.  We cannot use FOR_EACH_BB,
+  /* Start by iterating over all basic blocks.  We cannot use FOR_EACH_BB_FN,
      since the basic blocks may get removed.  */
-  n = last_basic_block;
+  n = last_basic_block_for_fn (cfun);
   for (i = NUM_FIXED_BLOCKS; i < n; i++)
     {
-      bb = BASIC_BLOCK (i);
+      bb = BASIC_BLOCK_FOR_FN (cfun, i);
       if (bb)
        retval |= cleanup_tree_cfg_bb (bb);
     }
@@ -707,15 +701,11 @@ cleanup_tree_cfg_1 (void)
       if (i < NUM_FIXED_BLOCKS)
        continue;
 
-      bb = BASIC_BLOCK (i);
+      bb = BASIC_BLOCK_FOR_FN (cfun, i);
       if (!bb)
        continue;
 
       retval |= cleanup_tree_cfg_bb (bb);
-
-      /* Rerun split_bbs_on_noreturn_calls, in case we have altered any noreturn
-        calls.  */
-      retval |= split_bbs_on_noreturn_calls ();
     }
 
   end_recording_case_labels ();
@@ -774,14 +764,23 @@ cleanup_tree_cfg_noloop (void)
 static void
 repair_loop_structures (void)
 {
-  bitmap changed_bbs = BITMAP_ALLOC (NULL);
-  fix_loop_structure (changed_bbs);
+  bitmap changed_bbs;
+  unsigned n_new_loops;
+
+  calculate_dominance_info (CDI_DOMINATORS);
+
+  timevar_push (TV_REPAIR_LOOPS);
+  changed_bbs = BITMAP_ALLOC (NULL);
+  n_new_loops = fix_loop_structure (changed_bbs);
 
   /* This usually does nothing.  But sometimes parts of cfg that originally
      were inside a loop get out of it due to edge removal (since they
-     become unreachable by back edges from latch).  */
+     become unreachable by back edges from latch).  Also a former
+     irreducible loop can become reducible - in this case force a full
+     rewrite into loop-closed SSA form.  */
   if (loops_state_satisfies_p (LOOP_CLOSED_SSA))
-    rewrite_into_loop_closed_ssa (changed_bbs, TODO_update_ssa);
+    rewrite_into_loop_closed_ssa (n_new_loops ? NULL : changed_bbs,
+                                 TODO_update_ssa);
 
   BITMAP_FREE (changed_bbs);
 
@@ -790,7 +789,7 @@ repair_loop_structures (void)
 #endif
   scev_reset ();
 
-  loops_state_clear (LOOPS_NEED_FIXUP);
+  timevar_pop (TV_REPAIR_LOOPS);
 }
 
 /* Cleanup cfg and repair loop structures.  */
@@ -807,9 +806,10 @@ cleanup_tree_cfg (void)
   return changed;
 }
 
-/* Merge the PHI nodes at BB into those at BB's sole successor.  */
+/* Tries to merge the PHI nodes at BB into those at BB's sole successor.
+   Returns true if successful.  */
 
-static void
+static bool
 remove_forwarder_block_with_phi (basic_block bb)
 {
   edge succ = single_succ_edge (bb);
@@ -821,21 +821,27 @@ remove_forwarder_block_with_phi (basic_block bb)
      However it may happen that the infinite loop is created
      afterwards due to removal of forwarders.  */
   if (dest == bb)
-    return;
+    return false;
 
   /* If the destination block consists of a nonlocal label, do not
      merge it.  */
   label = first_stmt (dest);
-  if (label
-      && gimple_code (label) == GIMPLE_LABEL
-      && DECL_NONLOCAL (gimple_label_label (label)))
-    return;
+  if (label)
+    if (glabel *label_stmt = dyn_cast <glabel *> (label))
+      if (DECL_NONLOCAL (gimple_label_label (label_stmt)))
+       return false;
+
+  /* Record BB's single pred in case we need to update the father
+     loop's latch information later.  */
+  basic_block pred = NULL;
+  if (single_pred_p (bb))
+    pred = single_pred (bb);
 
   /* Redirect each incoming edge to BB to DEST.  */
   while (EDGE_COUNT (bb->preds) > 0)
     {
       edge e = EDGE_PRED (bb, 0), s;
-      gimple_stmt_iterator gsi;
+      gphi_iterator gsi;
 
       s = find_edge (e->src, dest);
       if (s)
@@ -867,22 +873,20 @@ remove_forwarder_block_with_phi (basic_block bb)
           !gsi_end_p (gsi);
           gsi_next (&gsi))
        {
-         gimple phi = gsi_stmt (gsi);
+         gphi *phi = gsi.phi ();
          tree def = gimple_phi_arg_def (phi, succ->dest_idx);
          source_location locus = gimple_phi_arg_location_from_edge (phi, succ);
 
          if (TREE_CODE (def) == SSA_NAME)
            {
-             edge_var_map_vector head;
-             edge_var_map *vm;
-             size_t i;
-
              /* If DEF is one of the results of PHI nodes removed during
                 redirection, replace it with the PHI argument that used
                 to be on E.  */
-             head = redirect_edge_var_map_vector (e);
-             for (i = 0; VEC_iterate (edge_var_map, head, i, vm); ++i)
+             vec<edge_var_map> *head = redirect_edge_var_map_vector (e);
+             size_t length = head ? head->length () : 0;
+             for (size_t i = 0; i < length; i++)
                {
+                 edge_var_map *vm = &(*head)[i];
                  tree old_arg = redirect_edge_var_map_result (vm);
                  tree new_arg = redirect_edge_var_map_def (vm);
 
@@ -915,9 +919,16 @@ remove_forwarder_block_with_phi (basic_block bb)
 
   set_immediate_dominator (CDI_DOMINATORS, dest, dom);
 
+  /* Adjust latch infomation of BB's parent loop as otherwise
+     the cfg hook has a hard time not to kill the loop.  */
+  if (current_loops && bb->loop_father->latch == bb)
+    bb->loop_father->latch = pred;
+
   /* Remove BB since all of BB's incoming edges have been redirected
      to DEST.  */
   delete_basic_block (bb);
+
+  return true;
 }
 
 /* This pass merges PHI nodes if one feeds into another.  For example,
@@ -945,17 +956,45 @@ remove_forwarder_block_with_phi (basic_block bb)
 <L10>:;
 */
 
-static unsigned int
-merge_phi_nodes (void)
+namespace {
+
+const pass_data pass_data_merge_phi =
 {
-  basic_block *worklist = XNEWVEC (basic_block, n_basic_blocks);
+  GIMPLE_PASS, /* type */
+  "mergephi", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_MERGE_PHI, /* tv_id */
+  ( PROP_cfg | PROP_ssa ), /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_merge_phi : public gimple_opt_pass
+{
+public:
+  pass_merge_phi (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_merge_phi, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_merge_phi (m_ctxt); }
+  virtual unsigned int execute (function *);
+
+}; // class pass_merge_phi
+
+unsigned int
+pass_merge_phi::execute (function *fun)
+{
+  basic_block *worklist = XNEWVEC (basic_block, n_basic_blocks_for_fn (fun));
   basic_block *current = worklist;
   basic_block bb;
 
   calculate_dominance_info (CDI_DOMINATORS);
 
   /* Find all PHI nodes that we may be able to merge.  */
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, fun)
     {
       basic_block dest;
 
@@ -970,7 +1009,7 @@ merge_phi_nodes (void)
       if (gimple_seq_empty_p (phi_nodes (dest))
          /* We don't want to deal with a basic block with
             abnormal edges.  */
-         || has_abnormal_incoming_edge_p (bb))
+         || bb_has_abnormal_pred (bb))
        continue;
 
       if (!dominated_by_p (CDI_DOMINATORS, dest, bb))
@@ -982,7 +1021,7 @@ merge_phi_nodes (void)
        }
       else
        {
-         gimple_stmt_iterator gsi;
+         gphi_iterator gsi;
          unsigned int dest_idx = single_succ_edge (bb)->dest_idx;
 
          /* BB dominates DEST.  There may be many users of the PHI
@@ -993,7 +1032,7 @@ merge_phi_nodes (void)
          for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi);
               gsi_next (&gsi))
            {
-             gimple phi = gsi_stmt (gsi);
+             gphi *phi = gsi.phi ();
              tree result = gimple_phi_result (phi);
              use_operand_p imm_use;
              gimple use_stmt;
@@ -1019,38 +1058,116 @@ merge_phi_nodes (void)
     }
 
   /* Now let's drain WORKLIST.  */
+  bool changed = false;
   while (current != worklist)
     {
       bb = *--current;
-      remove_forwarder_block_with_phi (bb);
+      changed |= remove_forwarder_block_with_phi (bb);
     }
-
   free (worklist);
+
+  /* Removing forwarder blocks can cause formerly irreducible loops
+     to become reducible if we merged two entry blocks.  */
+  if (changed
+      && current_loops)
+    loops_state_set (LOOPS_NEED_FIXUP);
+
   return 0;
 }
 
-static bool
-gate_merge_phi (void)
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_merge_phi (gcc::context *ctxt)
+{
+  return new pass_merge_phi (ctxt);
+}
+
+/* Pass: cleanup the CFG just before expanding trees to RTL.
+   This is just a round of label cleanups and case node grouping
+   because after the tree optimizers have run such cleanups may
+   be necessary.  */
+
+static unsigned int
+execute_cleanup_cfg_post_optimizing (void)
 {
-  return 1;
+  unsigned int todo = execute_fixup_cfg ();
+  if (cleanup_tree_cfg ())
+    {
+      todo &= ~TODO_cleanup_cfg;
+      todo |= TODO_update_ssa;
+    }
+  maybe_remove_unreachable_handlers ();
+  cleanup_dead_labels ();
+  group_case_labels ();
+  if ((flag_compare_debug_opt || flag_compare_debug)
+      && flag_dump_final_insns)
+    {
+      FILE *final_output = fopen (flag_dump_final_insns, "a");
+
+      if (!final_output)
+       {
+         error ("could not open final insn dump file %qs: %m",
+                flag_dump_final_insns);
+         flag_dump_final_insns = NULL;
+       }
+      else
+       {
+         int save_unnumbered = flag_dump_unnumbered;
+         int save_noaddr = flag_dump_noaddr;
+
+         flag_dump_noaddr = flag_dump_unnumbered = 1;
+         fprintf (final_output, "\n");
+         dump_enumerated_decls (final_output, dump_flags | TDF_NOUID);
+         flag_dump_noaddr = save_noaddr;
+         flag_dump_unnumbered = save_unnumbered;
+         if (fclose (final_output))
+           {
+             error ("could not close final insn dump file %qs: %m",
+                    flag_dump_final_insns);
+             flag_dump_final_insns = NULL;
+           }
+       }
+    }
+  return todo;
 }
 
-struct gimple_opt_pass pass_merge_phi =
+namespace {
+
+const pass_data pass_data_cleanup_cfg_post_optimizing =
 {
- {
-  GIMPLE_PASS,
-  "mergephi",                  /* name */
-  gate_merge_phi,              /* gate */
-  merge_phi_nodes,             /* execute */
-  NULL,                                /* sub */
-  NULL,                                /* next */
-  0,                           /* static_pass_number */
-  TV_TREE_MERGE_PHI,           /* tv_id */
-  PROP_cfg | PROP_ssa,         /* properties_required */
-  0,                           /* properties_provided */
-  0,                           /* properties_destroyed */
-  0,                           /* todo_flags_start */
-  TODO_dump_func | TODO_ggc_collect    /* todo_flags_finish */
-  | TODO_verify_ssa
- }
+  GIMPLE_PASS, /* type */
+  "optimized", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_CLEANUP_CFG, /* tv_id */
+  PROP_cfg, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  TODO_remove_unused_locals, /* todo_flags_finish */
 };
+
+class pass_cleanup_cfg_post_optimizing : public gimple_opt_pass
+{
+public:
+  pass_cleanup_cfg_post_optimizing (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_cleanup_cfg_post_optimizing, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual unsigned int execute (function *)
+    {
+      return execute_cleanup_cfg_post_optimizing ();
+    }
+
+}; // class pass_cleanup_cfg_post_optimizing
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_cleanup_cfg_post_optimizing (gcc::context *ctxt)
+{
+  return new pass_cleanup_cfg_post_optimizing (ctxt);
+}
+
+