re PR target/65697 (__atomic memory barriers not strong enough for __sync builtins)
[gcc.git] / gcc / tree-cfgcleanup.c
index aaaacd9fb33b1917c5b676f13a14dc0e16ef075c..07e2d7465b005c21be42f25ac680667fac3b47aa 100644 (file)
@@ -1,6 +1,5 @@
 /* CFG cleanup for trees.
-   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007
-   Free Software Foundation, Inc.
+   Copyright (C) 2001-2015 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -22,28 +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 "rtl.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 "expr.h"
-#include "ggc.h"
 #include "langhooks.h"
-#include "diagnostic.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"
 
@@ -59,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;
@@ -67,49 +89,52 @@ 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;
 }
 
+
 /* Disconnect an unreachable block in the control expression starting
    at block BB.  */
 
 static bool
-cleanup_control_expr_graph (basic_block bb, block_stmt_iterator bsi)
+cleanup_control_expr_graph (basic_block bb, gimple_stmt_iterator gsi)
 {
   edge taken_edge;
   bool retval = false;
-  tree expr = bsi_stmt (bsi), val;
+  gimple stmt = gsi_stmt (gsi);
+  tree val;
 
   if (!single_succ_p (bb))
     {
       edge e;
       edge_iterator ei;
       bool warned;
+      location_t loc;
 
       fold_defer_overflow_warnings ();
-
-      switch (TREE_CODE (expr))
+      loc = gimple_location (stmt);
+      switch (gimple_code (stmt))
        {
-       case COND_EXPR:
-         val = fold (COND_EXPR_COND (expr));
+       case GIMPLE_COND:
+         val = fold_binary_loc (loc, gimple_cond_code (stmt),
+                                boolean_type_node,
+                                gimple_cond_lhs (stmt),
+                                gimple_cond_rhs (stmt));
          break;
 
-       case SWITCH_EXPR:
-         val = fold (SWITCH_COND (expr));
-         if (TREE_CODE (val) != INTEGER_CST)
-           {
-             fold_undefer_and_ignore_overflow_warnings ();
-             return false;
-           }
+       case GIMPLE_SWITCH:
+         val = gimple_switch_index (as_a <gswitch *> (stmt));
          break;
 
        default:
-         gcc_unreachable ();
+         val = NULL_TREE;
        }
-
       taken_edge = find_taken_edge (bb, val);
       if (!taken_edge)
        {
@@ -126,7 +151,7 @@ cleanup_control_expr_graph (basic_block bb, block_stmt_iterator bsi)
              if (!warned)
                {
                  fold_undefer_overflow_warnings
-                   (true, expr, WARN_STRICT_OVERFLOW_CONDITIONAL);
+                   (true, stmt, WARN_STRICT_OVERFLOW_CONDITIONAL);
                  warned = true;
                }
 
@@ -147,42 +172,62 @@ cleanup_control_expr_graph (basic_block bb, block_stmt_iterator bsi)
     taken_edge = single_succ_edge (bb);
 
   bitmap_set_bit (cfgcleanup_altered_bbs, bb->index);
-  bsi_remove (&bsi, true);
+  gsi_remove (&gsi, true);
   taken_edge->flags = EDGE_FALLTHRU;
 
   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.  */
 
 static bool
 cleanup_control_flow_bb (basic_block bb)
 {
-  block_stmt_iterator bsi;
+  gimple_stmt_iterator gsi;
   bool retval = false;
-  tree stmt;
+  gimple stmt;
 
   /* If the last statement of the block could throw and now cannot,
      we need to prune cfg.  */
-  retval |= tree_purge_dead_eh_edges (bb);
+  retval |= gimple_purge_dead_eh_edges (bb);
 
-  bsi = bsi_last (bb);
-  if (bsi_end_p (bsi))
+  gsi = gsi_last_bb (bb);
+  if (gsi_end_p (gsi))
     return retval;
 
-  stmt = bsi_stmt (bsi);
+  stmt = gsi_stmt (gsi);
+
+  /* Try to cleanup ctrl altering flag for call which ends bb.  */
+  cleanup_call_ctrl_altering_flag (stmt);
 
-  if (TREE_CODE (stmt) == COND_EXPR
-      || TREE_CODE (stmt) == SWITCH_EXPR)
-    retval |= cleanup_control_expr_graph (bb, bsi);
-  /* If we had a computed goto which has a compile-time determinable
-     destination, then we can eliminate the goto.  */
-  else if (TREE_CODE (stmt) == GOTO_EXPR
-          && TREE_CODE (GOTO_DESTINATION (stmt)) == ADDR_EXPR
-          && (TREE_CODE (TREE_OPERAND (GOTO_DESTINATION (stmt), 0))
+  if (gimple_code (stmt) == GIMPLE_COND
+      || gimple_code (stmt) == GIMPLE_SWITCH)
+    retval |= cleanup_control_expr_graph (bb, gsi);
+  else if (gimple_code (stmt) == GIMPLE_GOTO
+          && TREE_CODE (gimple_goto_dest (stmt)) == ADDR_EXPR
+          && (TREE_CODE (TREE_OPERAND (gimple_goto_dest (stmt), 0))
               == LABEL_DECL))
     {
+      /* If we had a computed goto which has a compile-time determinable
+        destination, then we can eliminate the goto.  */
       edge e;
       tree label;
       edge_iterator ei;
@@ -191,7 +236,7 @@ cleanup_control_flow_bb (basic_block bb)
       /* First look at all the outgoing edges.  Delete any outgoing
         edges which do not go to the right block.  For the one
         edge which goes to the right block, fix up its flags.  */
-      label = TREE_OPERAND (GOTO_DESTINATION (stmt), 0);
+      label = TREE_OPERAND (gimple_goto_dest (stmt), 0);
       target_block = label_to_block (label);
       for (ei = ei_start (bb->succs); (e = ei_safe_edge (ei)); )
        {
@@ -213,13 +258,15 @@ cleanup_control_flow_bb (basic_block bb)
 
       /* Remove the GOTO_EXPR as it is not needed.  The CFG has all the
         relevant information we need.  */
-      bsi_remove (&bsi, true);
+      gsi_remove (&gsi, true);
       retval = true;
     }
 
   /* Check for indirect calls that have been turned into
      noreturn calls.  */
-  else if (noreturn_call_p (stmt) && remove_fallthru_edge (bb->succs))
+  else if (is_gimple_call (stmt)
+           && gimple_call_noreturn_p (stmt)
+           && remove_fallthru_edge (bb->succs))
     retval = true;
 
   return retval;
@@ -230,100 +277,103 @@ 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)
 {
-  block_stmt_iterator bsi;
-  edge_iterator ei;
-  edge e, succ;
-  basic_block dest;
+  gimple_stmt_iterator gsi;
+  location_t locus;
 
   /* BB must have a single outgoing edge.  */
   if (single_succ_p (bb) != 1
       /* If PHI_WANTED is false, BB must not have any PHI nodes.
         Otherwise, BB must have PHI nodes.  */
-      || (phi_nodes (bb) != NULL_TREE) != phi_wanted
-      /* BB may not be a predecessor of EXIT_BLOCK_PTR.  */
-      || single_succ (bb) == EXIT_BLOCK_PTR
+      || gimple_seq_empty_p (phi_nodes (bb)) == phi_wanted
+      /* 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;
+
+  /* There should not be an edge coming from entry, or an EH edge.  */
+  {
+    edge_iterator ei;
+    edge e;
+
+    FOR_EACH_EDGE (e, ei, bb->preds)
+      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.  */
+      else if (optimize == 0 && e->goto_locus != locus)
+       return false;
+  }
 
   /* Now walk through the statements backward.  We can ignore labels,
      anything else means this is not a forwarder block.  */
-  for (bsi = bsi_last (bb); !bsi_end_p (bsi); bsi_prev (&bsi))
+  for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi); gsi_prev (&gsi))
     {
-      tree stmt = bsi_stmt (bsi);
+      gimple stmt = gsi_stmt (gsi);
 
-      switch (TREE_CODE (stmt))
+      switch (gimple_code (stmt))
        {
-       case LABEL_EXPR:
-         if (DECL_NONLOCAL (LABEL_EXPR_LABEL (stmt)))
+       case GIMPLE_LABEL:
+         if (DECL_NONLOCAL (gimple_label_label (as_a <glabel *> (stmt))))
+           return false;
+         if (optimize == 0 && gimple_location (stmt) != locus)
            return false;
          break;
 
+         /* ??? For now, hope there's a corresponding debug
+            assignment at the destination.  */
+       case GIMPLE_DEBUG:
+         break;
+
        default:
          return false;
        }
     }
 
-  if (find_edge (ENTRY_BLOCK_PTR, bb))
-    return false;
-
   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 we have an EH edge leaving this block, make sure that the
-     destination of this block has only one predecessor.  This ensures
-     that we don't get into the situation where we try to remove two
-     forwarders that go to the same basic block but are handlers for
-     different EH regions.  */
-  succ = single_succ_edge (bb);
-  dest = succ->dest;
-  FOR_EACH_EDGE (e, ei, bb->preds)
-    {
-      if (e->flags & EDGE_EH)
-        {
-         if (!single_pred_p (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;
-}
-
 /* If all the PHI nodes in DEST have alternatives for E1 and E2 and
    those alternatives are equal in each of the PHI nodes, then return
    true, else return false.  */
@@ -333,12 +383,13 @@ phi_alternatives_equal (basic_block dest, edge e1, edge e2)
 {
   int n1 = e1->dest_idx;
   int n2 = e2->dest_idx;
-  tree phi;
+  gphi_iterator gsi;
 
-  for (phi = phi_nodes (dest); phi; phi = PHI_CHAIN (phi))
+  for (gsi = gsi_start_phis (dest); !gsi_end_p (gsi); gsi_next (&gsi))
     {
-      tree val1 = PHI_ARG_DEF (phi, n1);
-      tree val2 = PHI_ARG_DEF (phi, n2);
+      gphi *phi = gsi.phi ();
+      tree val1 = gimple_phi_arg_def (phi, n1);
+      tree val2 = gimple_phi_arg_def (phi, n2);
 
       gcc_assert (val1 != NULL_TREE);
       gcc_assert (val2 != NULL_TREE);
@@ -357,11 +408,10 @@ remove_forwarder_block (basic_block bb)
 {
   edge succ = single_succ_edge (bb), e, s;
   basic_block dest = succ->dest;
-  tree label;
-  tree phi;
+  gimple label;
   edge_iterator ei;
-  block_stmt_iterator bsi, bsi_to;
-  bool seen_abnormal_edge = false;
+  gimple_stmt_iterator gsi, gsi_to;
+  bool can_move_debug_stmts;
 
   /* We check for infinite loops already in tree_forwarder_block_p.
      However it may happen that the infinite loop is created
@@ -369,13 +419,14 @@ remove_forwarder_block (basic_block bb)
   if (dest == bb)
     return false;
 
-  /* If the destination block consists of a nonlocal label, do not merge
-     it.  */
+  /* 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
-      && TREE_CODE (label) == LABEL_EXPR
-      && DECL_NONLOCAL (LABEL_EXPR_LABEL (label)))
-    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
@@ -388,19 +439,15 @@ 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))
-    {
-      seen_abnormal_edge = true;
-
-      if (has_abnormal_incoming_edge_p (dest)
-         || phi_nodes (dest) != NULL_TREE)
-       return false;
-    }
+  if (bb_has_abnormal_pred (bb)
+      && (bb_has_abnormal_pred (dest)
+         || !gimple_seq_empty_p (phi_nodes (dest))))
+    return false;
 
   /* If there are phi nodes in DEST, and some of the blocks that are
      predecessors of BB are also predecessors of DEST, check that the
      phi node arguments match.  */
-  if (phi_nodes (dest))
+  if (!gimple_seq_empty_p (phi_nodes (dest)))
     {
       FOR_EACH_EDGE (e, ei, bb->preds)
        {
@@ -413,6 +460,12 @@ remove_forwarder_block (basic_block bb)
        }
     }
 
+  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)); )
     {
@@ -431,23 +484,54 @@ remove_forwarder_block (basic_block bb)
        {
          /* Create arguments for the phi nodes, since the edge was not
             here before.  */
-         for (phi = phi_nodes (dest); phi; phi = PHI_CHAIN (phi))
-           add_phi_arg (phi, PHI_ARG_DEF (phi, succ->dest_idx), s);
+         for (gphi_iterator psi = gsi_start_phis (dest);
+              !gsi_end_p (psi);
+              gsi_next (&psi))
+           {
+             gphi *phi = psi.phi ();
+             source_location l = gimple_phi_arg_location_from_edge (phi, succ);
+             tree def = gimple_phi_arg_def (phi, succ->dest_idx);
+             add_phi_arg (phi, unshare_expr (def), s, l);
+           }
        }
     }
 
-  if (seen_abnormal_edge)
+  /* Move nonlocal labels and computed goto targets as well as user
+     defined labels and labels with an EH landing pad number to the
+     new block, so that the redirection of the abnormal edges works,
+     jump targets end up in a sane place and debug information for
+     labels is retained.  */
+  gsi_to = gsi_start_bb (dest);
+  for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
     {
-      /* Move the labels to the new block, so that the redirection of
-        the abnormal edges works.  */
+      tree decl;
+      label = gsi_stmt (gsi);
+      if (is_gimple_debug (label))
+       break;
+      decl = gimple_label_label (as_a <glabel *> (label));
+      if (EH_LANDING_PAD_NR (decl) != 0
+         || DECL_NONLOCAL (decl)
+         || FORCED_LABEL (decl)
+         || !DECL_ARTIFICIAL (decl))
+       {
+         gsi_remove (&gsi, false);
+         gsi_insert_before (&gsi_to, label, GSI_SAME_STMT);
+       }
+      else
+       gsi_next (&gsi);
+    }
 
-      bsi_to = bsi_start (dest);
-      for (bsi = bsi_start (bb); !bsi_end_p (bsi); )
+  /* Move debug statements if the destination has a single predecessor.  */
+  if (can_move_debug_stmts)
+    {
+      gsi_to = gsi_after_labels (dest);
+      for (gsi = gsi_after_labels (bb); !gsi_end_p (gsi); )
        {
-         label = bsi_stmt (bsi);
-         gcc_assert (TREE_CODE (label) == LABEL_EXPR);
-         bsi_remove (&bsi, false);
-         bsi_insert_before (&bsi_to, label, BSI_CONTINUE_LINKING);
+         gimple debug = gsi_stmt (gsi);
+         if (!is_gimple_debug (debug))
+           break;
+         gsi_remove (&gsi, false);
+         gsi_insert_before (&gsi_to, debug, GSI_SAME_STMT);
        }
     }
 
@@ -472,90 +556,90 @@ 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;
 }
 
-/* Split basic blocks on calls in the middle of a basic block that are now
-   known not to return, and remove the unreachable code.  */
+/* 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.  */
 
-static bool
-split_bbs_on_noreturn_calls (void)
+bool
+fixup_noreturn_call (gimple stmt)
 {
+  basic_block bb = gimple_bb (stmt);
   bool changed = false;
-  tree stmt;
-  basic_block bb;
 
-  /* Detect cases where a mid-block call is now known not to return.  */
-  if (cfun->gimple_df)
-    while (VEC_length (tree, MODIFIED_NORETURN_CALLS (cfun)))
-      {
-       stmt = VEC_pop (tree, MODIFIED_NORETURN_CALLS (cfun));
-       bb = bb_for_stmt (stmt);
-       if (bb == NULL
-           || last_stmt (bb) == stmt
-           || !noreturn_call_p (stmt))
-         continue;
-
-       changed = true;
-       split_block (bb, stmt);
-       remove_fallthru_edge (bb->succs);
-      }
-
-  return changed;
-}
-
-/* If OMP_RETURN in basic block BB is unreachable, remove it.  */
+  if (gimple_call_builtin_p (stmt, BUILT_IN_RETURN))
+    return false;
 
-static bool
-cleanup_omp_return (basic_block bb)
-{
-  tree stmt = last_stmt (bb);
-  basic_block control_bb;
+  /* First split basic block if stmt is not last.  */
+  if (stmt != gsi_stmt (gsi_last_bb (bb)))
+    {
+      if (stmt == gsi_stmt (gsi_last_nondebug_bb (bb)))
+       {
+         /* 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;
+       }
+    }
 
-  if (stmt == NULL_TREE
-      || TREE_CODE (stmt) != OMP_RETURN
-      || !single_pred_p (bb))
-    return false;
+  /* 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);
 
-  control_bb = single_pred (bb);
-  stmt = last_stmt (control_bb);
+      /* 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);
+       }
 
-  if (TREE_CODE (stmt) != OMP_SECTIONS_SWITCH)
-    return false;
+      update_stmt (stmt);
+    }
 
-  /* 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;
+  /* Mark the call as altering control flow.  */
+  if (!gimple_call_ctrl_altering_p (stmt))
+    {
+      gimple_call_set_ctrl_altering (stmt, true);
+      changed = true;
+    }
 
-  gcc_assert (EDGE_COUNT (control_bb->preds) == 1);
-  remove_edge_and_dominated_blocks (single_pred_edge (bb));
-  return true;
+  return changed;
 }
 
+
 /* Tries to cleanup cfg in basic block BB.  Returns true if anything
    changes.  */
 
 static bool
 cleanup_tree_cfg_bb (basic_block bb)
 {
-  bool retval = false;
-
-  if (cleanup_omp_return (bb))
-    return true;
+  bool retval = cleanup_control_flow_bb (bb);
 
-  retval = cleanup_control_flow_bb (bb);
-  
-  /* Forwarder blocks can carry line number information which is
-     useful when debugging, so we only clean them up when
-     optimizing.  */
-
-  if (optimize > 0
-      && tree_forwarder_block_p (bb, false)
+  if (tree_forwarder_block_p (bb, false)
       && remove_forwarder_block (bb))
     return true;
 
@@ -565,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;
@@ -581,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);
 
@@ -591,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);
     }
@@ -609,17 +701,13 @@ 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 ();
   BITMAP_FREE (cfgcleanup_altered_bbs);
   return retval;
@@ -676,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);
 
@@ -692,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.  */
@@ -709,35 +806,42 @@ 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);
   basic_block dest = succ->dest;
-  tree label;
+  gimple label;
   basic_block dombb, domdest, dom;
 
   /* We check for infinite loops already in tree_forwarder_block_p.
      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
-      && TREE_CODE (label) == LABEL_EXPR
-      && DECL_NONLOCAL (LABEL_EXPR_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;
-      tree phi;
+      gphi_iterator gsi;
 
       s = find_edge (e->src, dest);
       if (s)
@@ -765,34 +869,37 @@ remove_forwarder_block_with_phi (basic_block bb)
 
       /* Add to the PHI nodes at DEST each PHI argument removed at the
         destination of E.  */
-      for (phi = phi_nodes (dest); phi; phi = PHI_CHAIN (phi))
+      for (gsi = gsi_start_phis (dest);
+          !gsi_end_p (gsi);
+          gsi_next (&gsi))
        {
-         tree def = PHI_ARG_DEF (phi, succ->dest_idx);
+         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);
 
                  if (def == old_arg)
                    {
                      def = new_arg;
+                     locus = redirect_edge_var_map_location (vm);
                      break;
                    }
                }
            }
 
-         add_phi_arg (phi, def, s);
+         add_phi_arg (phi, def, s, locus);
        }
 
       redirect_edge_var_map_clear (e);
@@ -812,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,
@@ -842,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 =
+{
+  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
 {
-  basic_block *worklist = XNEWVEC (basic_block, n_basic_blocks);
+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;
 
@@ -864,10 +1006,10 @@ merge_phi_nodes (void)
 
       /* We have to feed into another basic block with PHI
         nodes.  */
-      if (!phi_nodes (dest)
+      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))
@@ -879,7 +1021,7 @@ merge_phi_nodes (void)
        }
       else
        {
-         tree phi;
+         gphi_iterator gsi;
          unsigned int dest_idx = single_succ_edge (bb)->dest_idx;
 
          /* BB dominates DEST.  There may be many users of the PHI
@@ -887,11 +1029,13 @@ merge_phi_nodes (void)
             can handle.  If the result of every PHI in BB is used
             only by a PHI in DEST, then we can trivially merge the
             PHI nodes from BB into DEST.  */
-         for (phi = phi_nodes (bb); phi; phi = PHI_CHAIN (phi))
+         for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi);
+              gsi_next (&gsi))
            {
-             tree result = PHI_RESULT (phi);
+             gphi *phi = gsi.phi ();
+             tree result = gimple_phi_result (phi);
              use_operand_p imm_use;
-             tree use_stmt;
+             gimple use_stmt;
 
              /* If the PHI's result is never used, then we can just
                 ignore it.  */
@@ -900,49 +1044,130 @@ merge_phi_nodes (void)
 
              /* Get the single use of the result of this PHI node.  */
              if (!single_imm_use (result, &imm_use, &use_stmt)
-                 || TREE_CODE (use_stmt) != PHI_NODE
-                 || bb_for_stmt (use_stmt) != dest
-                 || PHI_ARG_DEF (use_stmt, dest_idx) != result)
+                 || gimple_code (use_stmt) != GIMPLE_PHI
+                 || gimple_bb (use_stmt) != dest
+                 || gimple_phi_arg_def (use_stmt, dest_idx) != result)
                break;
            }
 
          /* If the loop above iterated through all the PHI nodes
             in BB, then we can merge the PHIs from BB into DEST.  */
-         if (!phi)
+         if (gsi_end_p (gsi))
            *current++ = bb;
        }
     }
 
   /* 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 1;
+  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)
+{
+  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 tree_opt_pass pass_merge_phi = {
-  "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,
-  0                            /* letter */
+namespace {
+
+const pass_data pass_data_cleanup_cfg_post_optimizing =
+{
+  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);
+}
+
+