ipa-cp.c (ipcp_cloning_candidate_p): Use opt_for_fn.
[gcc.git] / gcc / tree-cfg.c
index 721c4f77d4ad23f9e53480756e9dece0a972c503..9dd8961cb8f9421fe1ef7e4710835506c5c0cd55 100644 (file)
@@ -1,5 +1,5 @@
 /* Control flow functions for trees.
-   Copyright (C) 2001-2013 Free Software Foundation, Inc.
+   Copyright (C) 2001-2014 Free Software Foundation, Inc.
    Contributed by Diego Novillo <dnovillo@redhat.com>
 
 This file is part of GCC.
@@ -22,15 +22,52 @@ along with GCC; see the file COPYING3.  If not see
 #include "system.h"
 #include "coretypes.h"
 #include "hash-table.h"
+#include "hash-map.h"
 #include "tm.h"
 #include "tree.h"
+#include "trans-mem.h"
+#include "stor-layout.h"
+#include "print-tree.h"
 #include "tm_p.h"
+#include "predict.h"
+#include "vec.h"
+#include "hashtab.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "hard-reg-set.h"
+#include "input.h"
+#include "function.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "cfganal.h"
 #include "basic-block.h"
 #include "flags.h"
-#include "function.h"
-#include "ggc.h"
 #include "gimple-pretty-print.h"
-#include "tree-flow.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "gimple-fold.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimplify-me.h"
+#include "gimple-walk.h"
+#include "gimple-ssa.h"
+#include "plugin-api.h"
+#include "ipa-ref.h"
+#include "cgraph.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 "tree-ssa-loop-niter.h"
+#include "tree-into-ssa.h"
+#include "expr.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
 #include "tree-dump.h"
 #include "tree-pass.h"
 #include "diagnostic-core.h"
@@ -38,9 +75,13 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "tree-ssa-propagate.h"
 #include "value-prof.h"
-#include "pointer-set.h"
 #include "tree-inline.h"
 #include "target.h"
+#include "tree-ssa-live.h"
+#include "omp-low.h"
+#include "tree-cfgcleanup.h"
+#include "wide-int.h"
+#include "wide-int-print.h"
 
 /* This file contains functions for building the Control Flow Graph (CFG)
    for a function tree.  */
@@ -63,7 +104,7 @@ static const int initial_cfg_capacity = 20;
    more persistent.  The key is getting notification of changes to
    the CFG (particularly edge removal, creation and redirection).  */
 
-static struct pointer_map_t *edge_to_cases;
+static hash_map<edge, tree> *edge_to_cases;
 
 /* If we record edge_to_cases, this bitmap will hold indexes
    of basic blocks that end in a GIMPLE_SWITCH which we touched
@@ -79,9 +120,6 @@ struct cfg_stats_d
 
 static struct cfg_stats_d cfg_stats;
 
-/* Nonzero if we found a computed goto while building basic blocks.  */
-static bool found_computed_goto;
-
 /* Hash table to store last discriminator assigned for each locus.  */
 struct locus_discrim_map
 {
@@ -91,7 +129,7 @@ struct locus_discrim_map
 
 /* Hashtable helpers.  */
 
-struct locus_descrim_hasher : typed_free_remove <locus_discrim_map>
+struct locus_discrim_hasher : typed_free_remove <locus_discrim_map>
 {
   typedef locus_discrim_map value_type;
   typedef locus_discrim_map compare_type;
@@ -103,36 +141,34 @@ struct locus_descrim_hasher : typed_free_remove <locus_discrim_map>
    a hash table entry that maps a location_t to a discriminator.  */
 
 inline hashval_t
-locus_descrim_hasher::hash (const value_type *item)
+locus_discrim_hasher::hash (const value_type *item)
 {
-  return item->locus;
+  return LOCATION_LINE (item->locus);
 }
 
 /* Equality function for the locus-to-discriminator map.  A and B
    point to the two hash table entries to compare.  */
 
 inline bool
-locus_descrim_hasher::equal (const value_type *a, const compare_type *b)
+locus_discrim_hasher::equal (const value_type *a, const compare_type *b)
 {
-  return a->locus == b->locus;
+  return LOCATION_LINE (a->locus) == LOCATION_LINE (b->locus);
 }
 
-static hash_table <locus_descrim_hasher> discriminator_per_locus;
+static hash_table<locus_discrim_hasher> *discriminator_per_locus;
 
 /* Basic blocks and flowgraphs.  */
 static void make_blocks (gimple_seq);
-static void factor_computed_gotos (void);
 
 /* Edges.  */
 static void make_edges (void);
+static void assign_discriminators (void);
 static void make_cond_expr_edges (basic_block);
 static void make_gimple_switch_edges (basic_block);
-static void make_goto_expr_edges (basic_block);
+static bool make_goto_expr_edges (basic_block);
 static void make_gimple_asm_edges (basic_block);
-static void assign_discriminator (location_t, basic_block);
 static edge gimple_redirect_edge_and_branch (edge, basic_block);
 static edge gimple_try_redirect_by_replacing_jump (edge, basic_block);
-static unsigned int split_critical_edges (void);
 
 /* Various helpers.  */
 static inline bool stmt_starts_bb_p (gimple, gimple);
@@ -140,6 +176,7 @@ static int gimple_verify_flow_info (void);
 static void gimple_make_forwarder_block (edge);
 static gimple first_non_label_stmt (basic_block);
 static bool verify_gimple_transaction (gimple);
+static bool call_can_make_abnormal_goto (gimple);
 
 /* Flowgraph optimization and cleanup.  */
 static void gimple_merge_blocks (basic_block, basic_block);
@@ -155,27 +192,25 @@ init_empty_tree_cfg_for_function (struct function *fn)
 {
   /* Initialize the basic block array.  */
   init_flow (fn);
-  profile_status_for_function (fn) = PROFILE_ABSENT;
-  n_basic_blocks_for_function (fn) = NUM_FIXED_BLOCKS;
-  last_basic_block_for_function (fn) = NUM_FIXED_BLOCKS;
-  vec_alloc (basic_block_info_for_function (fn), initial_cfg_capacity);
-  vec_safe_grow_cleared (basic_block_info_for_function (fn),
+  profile_status_for_fn (fn) = PROFILE_ABSENT;
+  n_basic_blocks_for_fn (fn) = NUM_FIXED_BLOCKS;
+  last_basic_block_for_fn (fn) = NUM_FIXED_BLOCKS;
+  vec_alloc (basic_block_info_for_fn (fn), initial_cfg_capacity);
+  vec_safe_grow_cleared (basic_block_info_for_fn (fn),
                         initial_cfg_capacity);
 
   /* Build a mapping of labels to their associated blocks.  */
-  vec_alloc (label_to_block_map_for_function (fn), initial_cfg_capacity);
-  vec_safe_grow_cleared (label_to_block_map_for_function (fn),
+  vec_alloc (label_to_block_map_for_fn (fn), initial_cfg_capacity);
+  vec_safe_grow_cleared (label_to_block_map_for_fn (fn),
                         initial_cfg_capacity);
 
-  SET_BASIC_BLOCK_FOR_FUNCTION (fn, ENTRY_BLOCK,
-                               ENTRY_BLOCK_PTR_FOR_FUNCTION (fn));
-  SET_BASIC_BLOCK_FOR_FUNCTION (fn, EXIT_BLOCK,
-                  EXIT_BLOCK_PTR_FOR_FUNCTION (fn));
+  SET_BASIC_BLOCK_FOR_FN (fn, ENTRY_BLOCK, ENTRY_BLOCK_PTR_FOR_FN (fn));
+  SET_BASIC_BLOCK_FOR_FN (fn, EXIT_BLOCK, EXIT_BLOCK_PTR_FOR_FN (fn));
 
-  ENTRY_BLOCK_PTR_FOR_FUNCTION (fn)->next_bb
-    = EXIT_BLOCK_PTR_FOR_FUNCTION (fn);
-  EXIT_BLOCK_PTR_FOR_FUNCTION (fn)->prev_bb
-    = ENTRY_BLOCK_PTR_FOR_FUNCTION (fn);
+  ENTRY_BLOCK_PTR_FOR_FN (fn)->next_bb
+    = EXIT_BLOCK_PTR_FOR_FN (fn);
+  EXIT_BLOCK_PTR_FOR_FN (fn)->prev_bb
+    = ENTRY_BLOCK_PTR_FOR_FN (fn);
 }
 
 void
@@ -201,24 +236,17 @@ build_gimple_cfg (gimple_seq seq)
 
   init_empty_tree_cfg ();
 
-  found_computed_goto = 0;
   make_blocks (seq);
 
-  /* Computed gotos are hell to deal with, especially if there are
-     lots of them with a large number of destinations.  So we factor
-     them to a common computed goto location before we build the
-     edge list.  After we convert back to normal form, we will un-factor
-     the computed gotos since factoring introduces an unwanted jump.  */
-  if (found_computed_goto)
-    factor_computed_gotos ();
-
   /* Make sure there is always at least one block, even if it's empty.  */
-  if (n_basic_blocks == NUM_FIXED_BLOCKS)
-    create_empty_bb (ENTRY_BLOCK_PTR);
+  if (n_basic_blocks_for_fn (cfun) == NUM_FIXED_BLOCKS)
+    create_empty_bb (ENTRY_BLOCK_PTR_FOR_FN (cfun));
 
   /* Adjust the size of the array.  */
-  if (basic_block_info->length () < (size_t) n_basic_blocks)
-    vec_safe_grow_cleared (basic_block_info, n_basic_blocks);
+  if (basic_block_info_for_fn (cfun)->length ()
+      < (size_t) n_basic_blocks_for_fn (cfun))
+    vec_safe_grow_cleared (basic_block_info_for_fn (cfun),
+                          n_basic_blocks_for_fn (cfun));
 
   /* To speed up statement iterator walks, we first purge dead labels.  */
   cleanup_dead_labels ();
@@ -229,12 +257,111 @@ build_gimple_cfg (gimple_seq seq)
   group_case_labels ();
 
   /* Create the edges of the flowgraph.  */
-  discriminator_per_locus.create (13);
+  discriminator_per_locus = new hash_table<locus_discrim_hasher> (13);
   make_edges ();
+  assign_discriminators ();
   cleanup_dead_labels ();
-  discriminator_per_locus.dispose ();
+  delete discriminator_per_locus;
+  discriminator_per_locus = NULL;
 }
 
+/* Look for ANNOTATE calls with loop annotation kind in BB; if found, remove
+   them and propagate the information to LOOP.  We assume that the annotations
+   come immediately before the condition in BB, if any.  */
+
+static void
+replace_loop_annotate_in_block (basic_block bb, struct loop *loop)
+{
+  gimple_stmt_iterator gsi = gsi_last_bb (bb);
+  gimple stmt = gsi_stmt (gsi);
+
+  if (!(stmt && gimple_code (stmt) == GIMPLE_COND))
+    return;
+
+  for (gsi_prev_nondebug (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi))
+    {
+      stmt = gsi_stmt (gsi);
+      if (gimple_code (stmt) != GIMPLE_CALL)
+       break;
+      if (!gimple_call_internal_p (stmt)
+         || gimple_call_internal_fn (stmt) != IFN_ANNOTATE)
+       break;
+
+      switch ((annot_expr_kind) tree_to_shwi (gimple_call_arg (stmt, 1)))
+       {
+       case annot_expr_ivdep_kind:
+         loop->safelen = INT_MAX;
+         break;
+       case annot_expr_no_vector_kind:
+         loop->dont_vectorize = true;
+         break;
+       case annot_expr_vector_kind:
+         loop->force_vectorize = true;
+         cfun->has_force_vectorize_loops = true;
+         break;
+       default:
+         gcc_unreachable ();
+       }
+
+      stmt = gimple_build_assign (gimple_call_lhs (stmt),
+                                 gimple_call_arg (stmt, 0));
+      gsi_replace (&gsi, stmt, true);
+    }
+}
+
+/* Look for ANNOTATE calls with loop annotation kind; if found, remove
+   them and propagate the information to the loop.  We assume that the
+   annotations come immediately before the condition of the loop.  */
+
+static void
+replace_loop_annotate (void)
+{
+  struct loop *loop;
+  basic_block bb;
+  gimple_stmt_iterator gsi;
+  gimple stmt;
+
+  FOR_EACH_LOOP (loop, 0)
+    {
+      /* First look into the header.  */
+      replace_loop_annotate_in_block (loop->header, loop);
+
+      /* Then look into the latch, if any.  */
+      if (loop->latch)
+       replace_loop_annotate_in_block (loop->latch, loop);
+    }
+
+  /* Remove IFN_ANNOTATE.  Safeguard for the case loop->latch == NULL.  */
+  FOR_EACH_BB_FN (bb, cfun)
+    {
+      for (gsi = gsi_last_bb (bb); !gsi_end_p (gsi); gsi_prev (&gsi))
+       {
+         stmt = gsi_stmt (gsi);
+         if (gimple_code (stmt) != GIMPLE_CALL)
+           continue;
+         if (!gimple_call_internal_p (stmt)
+             || gimple_call_internal_fn (stmt) != IFN_ANNOTATE)
+           continue;
+
+         switch ((annot_expr_kind) tree_to_shwi (gimple_call_arg (stmt, 1)))
+           {
+           case annot_expr_ivdep_kind:
+           case annot_expr_no_vector_kind:
+           case annot_expr_vector_kind:
+             break;
+           default:
+             gcc_unreachable ();
+           }
+
+         warning_at (gimple_location (stmt), 0, "ignoring loop annotation");
+         stmt = gimple_build_assign (gimple_call_lhs (stmt),
+                                     gimple_call_arg (stmt, 0));
+         gsi_replace (&gsi, stmt, true);
+       }
+    }
+}
+
+
 static unsigned int
 execute_build_cfg (void)
 {
@@ -249,113 +376,123 @@ execute_build_cfg (void)
     }
   cleanup_tree_cfg ();
   loop_optimizer_init (AVOID_CFG_MODIFICATIONS);
+  replace_loop_annotate ();
   return 0;
 }
 
-struct gimple_opt_pass pass_build_cfg =
-{
- {
-  GIMPLE_PASS,
-  "cfg",                               /* name */
-  OPTGROUP_NONE,                        /* optinfo_flags */
-  NULL,                                        /* gate */
-  execute_build_cfg,                   /* execute */
-  NULL,                                        /* sub */
-  NULL,                                        /* next */
-  0,                                   /* static_pass_number */
-  TV_TREE_CFG,                         /* tv_id */
-  PROP_gimple_leh,                     /* properties_required */
-  PROP_cfg | PROP_loops,               /* properties_provided */
-  0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  TODO_verify_stmts                    /* todo_flags_finish */
- }
+namespace {
+
+const pass_data pass_data_build_cfg =
+{
+  GIMPLE_PASS, /* type */
+  "cfg", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_CFG, /* tv_id */
+  PROP_gimple_leh, /* properties_required */
+  ( PROP_cfg | PROP_loops ), /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
 };
 
+class pass_build_cfg : public gimple_opt_pass
+{
+public:
+  pass_build_cfg (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_build_cfg, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual unsigned int execute (function *) { return execute_build_cfg (); }
+
+}; // class pass_build_cfg
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_build_cfg (gcc::context *ctxt)
+{
+  return new pass_build_cfg (ctxt);
+}
+
 
 /* Return true if T is a computed goto.  */
 
-static bool
+bool
 computed_goto_p (gimple t)
 {
   return (gimple_code (t) == GIMPLE_GOTO
          && TREE_CODE (gimple_goto_dest (t)) != LABEL_DECL);
 }
 
+/* Returns true for edge E where e->src ends with a GIMPLE_COND and
+   the other edge points to a bb with just __builtin_unreachable ().
+   I.e. return true for C->M edge in:
+   <bb C>:
+   ...
+   if (something)
+     goto <bb N>;
+   else
+     goto <bb M>;
+   <bb N>:
+   __builtin_unreachable ();
+   <bb M>:  */
 
-/* Search the CFG for any computed gotos.  If found, factor them to a
-   common computed goto site.  Also record the location of that site so
-   that we can un-factor the gotos after we have converted back to
-   normal form.  */
-
-static void
-factor_computed_gotos (void)
+bool
+assert_unreachable_fallthru_edge_p (edge e)
 {
-  basic_block bb;
-  tree factored_label_decl = NULL;
-  tree var = NULL;
-  gimple factored_computed_goto_label = NULL;
-  gimple factored_computed_goto = NULL;
-
-  /* We know there are one or more computed gotos in this function.
-     Examine the last statement in each basic block to see if the block
-     ends with a computed goto.  */
-
-  FOR_EACH_BB (bb)
+  basic_block pred_bb = e->src;
+  gimple last = last_stmt (pred_bb);
+  if (last && gimple_code (last) == GIMPLE_COND)
     {
-      gimple_stmt_iterator gsi = gsi_last_bb (bb);
-      gimple last;
-
-      if (gsi_end_p (gsi))
-       continue;
-
-      last = gsi_stmt (gsi);
-
-      /* Ignore the computed goto we create when we factor the original
-        computed gotos.  */
-      if (last == factored_computed_goto)
-       continue;
-
-      /* If the last statement is a computed goto, factor it.  */
-      if (computed_goto_p (last))
+      basic_block other_bb = EDGE_SUCC (pred_bb, 0)->dest;
+      if (other_bb == e->dest)
+       other_bb = EDGE_SUCC (pred_bb, 1)->dest;
+      if (EDGE_COUNT (other_bb->succs) == 0)
        {
-         gimple assignment;
+         gimple_stmt_iterator gsi = gsi_after_labels (other_bb);
+         gimple stmt;
 
-         /* The first time we find a computed goto we need to create
-            the factored goto block and the variable each original
-            computed goto will use for their goto destination.  */
-         if (!factored_computed_goto)
+         if (gsi_end_p (gsi))
+           return false;
+         stmt = gsi_stmt (gsi);
+         while (is_gimple_debug (stmt) || gimple_clobber_p (stmt))
            {
-             basic_block new_bb = create_empty_bb (bb);
-             gimple_stmt_iterator new_gsi = gsi_start_bb (new_bb);
-
-             /* Create the destination of the factored goto.  Each original
-                computed goto will put its desired destination into this
-                variable and jump to the label we create immediately
-                below.  */
-             var = create_tmp_var (ptr_type_node, "gotovar");
-
-             /* Build a label for the new block which will contain the
-                factored computed goto.  */
-             factored_label_decl = create_artificial_label (UNKNOWN_LOCATION);
-             factored_computed_goto_label
-               = gimple_build_label (factored_label_decl);
-             gsi_insert_after (&new_gsi, factored_computed_goto_label,
-                               GSI_NEW_STMT);
-
-             /* Build our new computed goto.  */
-             factored_computed_goto = gimple_build_goto (var);
-             gsi_insert_after (&new_gsi, factored_computed_goto, GSI_NEW_STMT);
+             gsi_next (&gsi);
+             if (gsi_end_p (gsi))
+               return false;
+             stmt = gsi_stmt (gsi);
            }
-
-         /* Copy the original computed goto's destination into VAR.  */
-         assignment = gimple_build_assign (var, gimple_goto_dest (last));
-         gsi_insert_before (&gsi, assignment, GSI_SAME_STMT);
-
-         /* And re-vector the computed goto to the new destination.  */
-         gimple_goto_set_dest (last, factored_label_decl);
+         return gimple_call_builtin_p (stmt, BUILT_IN_UNREACHABLE);
        }
     }
+  return false;
+}
+
+
+/* Initialize GF_CALL_CTRL_ALTERING flag, which indicates the call
+   could alter control flow except via eh. We initialize the flag at
+   CFG build time and only ever clear it later.  */
+
+static void
+gimple_call_initialize_ctrl_altering (gimple stmt)
+{
+  int flags = gimple_call_flags (stmt);
+
+  /* A call alters control flow if it can make an abnormal goto.  */
+  if (call_can_make_abnormal_goto (stmt)
+      /* A call also alters control flow if it does not return.  */
+      || flags & ECF_NORETURN
+      /* TM ending statements have backedges out of the transaction.
+        Return true so we split the basic block containing them.
+        Note that the TM_BUILTIN test is merely an optimization.  */
+      || ((flags & ECF_TM_BUILTIN)
+         && is_tm_ending_fndecl (gimple_call_fndecl (stmt)))
+      /* BUILT_IN_RETURN call is same as return statement.  */
+      || gimple_call_builtin_p (stmt, BUILT_IN_RETURN))
+    gimple_call_set_ctrl_altering (stmt, true);
+  else
+    gimple_call_set_ctrl_altering (stmt, false);
 }
 
 
@@ -368,7 +505,7 @@ make_blocks (gimple_seq seq)
   gimple stmt = NULL;
   bool start_new_block = true;
   bool first_stmt_of_seq = true;
-  basic_block bb = ENTRY_BLOCK_PTR;
+  basic_block bb = ENTRY_BLOCK_PTR_FOR_FN (cfun);
 
   while (!gsi_end_p (i))
     {
@@ -377,6 +514,9 @@ make_blocks (gimple_seq seq)
       prev_stmt = stmt;
       stmt = gsi_stmt (i);
 
+      if (stmt && is_gimple_call (stmt))
+       gimple_call_initialize_ctrl_altering (stmt);
+
       /* If the statement starts a new basic block or if we have determined
         in a previous pass that we need to create a new block for STMT, do
         so now.  */
@@ -392,9 +532,6 @@ make_blocks (gimple_seq seq)
         codes.  */
       gimple_set_bb (stmt, bb);
 
-      if (computed_goto_p (stmt))
-       found_computed_goto = true;
-
       /* If STMT is a basic block terminator, set START_NEW_BLOCK for the
         next iteration.  */
       if (stmt_ends_bb_p (stmt))
@@ -442,7 +579,7 @@ create_bb (void *h, void *e, basic_block after)
      not have to clear the newly allocated basic block here.  */
   bb = alloc_block ();
 
-  bb->index = last_basic_block;
+  bb->index = last_basic_block_for_fn (cfun);
   bb->flags = BB_NEW;
   set_bb_seq (bb, h ? (gimple_seq) h : NULL);
 
@@ -450,17 +587,20 @@ create_bb (void *h, void *e, basic_block after)
   link_block (bb, after);
 
   /* Grow the basic block array if needed.  */
-  if ((size_t) last_basic_block == basic_block_info->length ())
+  if ((size_t) last_basic_block_for_fn (cfun)
+      == basic_block_info_for_fn (cfun)->length ())
     {
-      size_t new_size = last_basic_block + (last_basic_block + 3) / 4;
-      vec_safe_grow_cleared (basic_block_info, new_size);
+      size_t new_size =
+       (last_basic_block_for_fn (cfun)
+        + (last_basic_block_for_fn (cfun) + 3) / 4);
+      vec_safe_grow_cleared (basic_block_info_for_fn (cfun), new_size);
     }
 
   /* Add the newly created block to the array.  */
-  SET_BASIC_BLOCK (last_basic_block, bb);
+  SET_BASIC_BLOCK_FOR_FN (cfun, last_basic_block_for_fn (cfun), bb);
 
-  n_basic_blocks++;
-  last_basic_block++;
+  n_basic_blocks_for_fn (cfun)++;
+  last_basic_block_for_fn (cfun)++;
 
   return bb;
 }
@@ -477,7 +617,7 @@ fold_cond_expr_cond (void)
 {
   basic_block bb;
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       gimple stmt = last_stmt (bb);
 
@@ -509,6 +649,144 @@ fold_cond_expr_cond (void)
     }
 }
 
+/* If basic block BB has an abnormal edge to a basic block
+   containing IFN_ABNORMAL_DISPATCHER internal call, return
+   that the dispatcher's basic block, otherwise return NULL.  */
+
+basic_block
+get_abnormal_succ_dispatcher (basic_block bb)
+{
+  edge e;
+  edge_iterator ei;
+
+  FOR_EACH_EDGE (e, ei, bb->succs)
+    if ((e->flags & (EDGE_ABNORMAL | EDGE_EH)) == EDGE_ABNORMAL)
+      {
+       gimple_stmt_iterator gsi
+         = gsi_start_nondebug_after_labels_bb (e->dest);
+       gimple g = gsi_stmt (gsi);
+       if (g
+           && is_gimple_call (g)
+           && gimple_call_internal_p (g)
+           && gimple_call_internal_fn (g) == IFN_ABNORMAL_DISPATCHER)
+         return e->dest;
+      }
+  return NULL;
+}
+
+/* Helper function for make_edges.  Create a basic block with
+   with ABNORMAL_DISPATCHER internal call in it if needed, and
+   create abnormal edges from BBS to it and from it to FOR_BB
+   if COMPUTED_GOTO is false, otherwise factor the computed gotos.  */
+
+static void
+handle_abnormal_edges (basic_block *dispatcher_bbs,
+                      basic_block for_bb, int *bb_to_omp_idx,
+                      auto_vec<basic_block> *bbs, bool computed_goto)
+{
+  basic_block *dispatcher = dispatcher_bbs + (computed_goto ? 1 : 0);
+  unsigned int idx = 0;
+  basic_block bb;
+  bool inner = false;
+
+  if (bb_to_omp_idx)
+    {
+      dispatcher = dispatcher_bbs + 2 * bb_to_omp_idx[for_bb->index];
+      if (bb_to_omp_idx[for_bb->index] != 0)
+       inner = true;
+    }
+
+  /* If the dispatcher has been created already, then there are basic
+     blocks with abnormal edges to it, so just make a new edge to
+     for_bb.  */
+  if (*dispatcher == NULL)
+    {
+      /* Check if there are any basic blocks that need to have
+        abnormal edges to this dispatcher.  If there are none, return
+        early.  */
+      if (bb_to_omp_idx == NULL)
+       {
+         if (bbs->is_empty ())
+           return;
+       }
+      else
+       {
+         FOR_EACH_VEC_ELT (*bbs, idx, bb)
+           if (bb_to_omp_idx[bb->index] == bb_to_omp_idx[for_bb->index])
+             break;
+         if (bb == NULL)
+           return;
+       }
+
+      /* Create the dispatcher bb.  */
+      *dispatcher = create_basic_block (NULL, NULL, for_bb);
+      if (computed_goto)
+       {
+         /* Factor computed gotos into a common computed goto site.  Also
+            record the location of that site so that we can un-factor the
+            gotos after we have converted back to normal form.  */
+         gimple_stmt_iterator gsi = gsi_start_bb (*dispatcher);
+
+         /* Create the destination of the factored goto.  Each original
+            computed goto will put its desired destination into this
+            variable and jump to the label we create immediately below.  */
+         tree var = create_tmp_var (ptr_type_node, "gotovar");
+
+         /* Build a label for the new block which will contain the
+            factored computed goto.  */
+         tree factored_label_decl
+           = create_artificial_label (UNKNOWN_LOCATION);
+         gimple factored_computed_goto_label
+           = gimple_build_label (factored_label_decl);
+         gsi_insert_after (&gsi, factored_computed_goto_label, GSI_NEW_STMT);
+
+         /* Build our new computed goto.  */
+         gimple factored_computed_goto = gimple_build_goto (var);
+         gsi_insert_after (&gsi, factored_computed_goto, GSI_NEW_STMT);
+
+         FOR_EACH_VEC_ELT (*bbs, idx, bb)
+           {
+             if (bb_to_omp_idx
+                 && bb_to_omp_idx[bb->index] != bb_to_omp_idx[for_bb->index])
+               continue;
+
+             gsi = gsi_last_bb (bb);
+             gimple last = gsi_stmt (gsi);
+
+             gcc_assert (computed_goto_p (last));
+
+             /* Copy the original computed goto's destination into VAR.  */
+             gimple assignment
+               = gimple_build_assign (var, gimple_goto_dest (last));
+             gsi_insert_before (&gsi, assignment, GSI_SAME_STMT);
+
+             edge e = make_edge (bb, *dispatcher, EDGE_FALLTHRU);
+             e->goto_locus = gimple_location (last);
+             gsi_remove (&gsi, true);
+           }
+       }
+      else
+       {
+         tree arg = inner ? boolean_true_node : boolean_false_node;
+         gimple g = gimple_build_call_internal (IFN_ABNORMAL_DISPATCHER,
+                                                1, arg);
+         gimple_stmt_iterator gsi = gsi_after_labels (*dispatcher);
+         gsi_insert_after (&gsi, g, GSI_NEW_STMT);
+
+         /* Create predecessor edges of the dispatcher.  */
+         FOR_EACH_VEC_ELT (*bbs, idx, bb)
+           {
+             if (bb_to_omp_idx
+                 && bb_to_omp_idx[bb->index] != bb_to_omp_idx[for_bb->index])
+               continue;
+             make_edge (bb, *dispatcher, EDGE_ABNORMAL);
+           }
+       }
+    }
+
+  make_edge (*dispatcher, for_bb, EDGE_ABNORMAL);
+}
+
 /* Join all the blocks in the flowgraph.  */
 
 static void
@@ -516,29 +794,42 @@ make_edges (void)
 {
   basic_block bb;
   struct omp_region *cur_region = NULL;
+  auto_vec<basic_block> ab_edge_goto;
+  auto_vec<basic_block> ab_edge_call;
+  int *bb_to_omp_idx = NULL;
+  int cur_omp_region_idx = 0;
 
   /* Create an edge from entry to the first block with executable
      statements in it.  */
-  make_edge (ENTRY_BLOCK_PTR, BASIC_BLOCK (NUM_FIXED_BLOCKS), EDGE_FALLTHRU);
+  make_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun),
+            BASIC_BLOCK_FOR_FN (cfun, NUM_FIXED_BLOCKS),
+            EDGE_FALLTHRU);
 
   /* Traverse the basic block array placing edges.  */
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       gimple last = last_stmt (bb);
       bool fallthru;
 
+      if (bb_to_omp_idx)
+       bb_to_omp_idx[bb->index] = cur_omp_region_idx;
+
       if (last)
        {
          enum gimple_code code = gimple_code (last);
          switch (code)
            {
            case GIMPLE_GOTO:
-             make_goto_expr_edges (bb);
+             if (make_goto_expr_edges (bb))
+               ab_edge_goto.safe_push (bb);
              fallthru = false;
              break;
            case GIMPLE_RETURN:
-             make_edge (bb, EXIT_BLOCK_PTR, 0);
-             fallthru = false;
+             {
+               edge e = make_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
+               e->goto_locus = gimple_location (last);
+               fallthru = false;
+             }
              break;
            case GIMPLE_COND:
              make_cond_expr_edges (bb);
@@ -561,7 +852,7 @@ make_edges (void)
                 make edges from this call site to all the nonlocal goto
                 handlers.  */
              if (stmt_can_make_abnormal_goto (last))
-               make_abnormal_goto_edges (bb, true);
+               ab_edge_call.safe_push (bb);
 
              /* If this statement has reachable exception handlers, then
                 create abnormal edges to them.  */
@@ -569,7 +860,10 @@ make_edges (void)
 
              /* BUILTIN_RETURN is really a return statement.  */
              if (gimple_call_builtin_p (last, BUILT_IN_RETURN))
-               make_edge (bb, EXIT_BLOCK_PTR, 0), fallthru = false;
+               {
+                 make_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
+                 fallthru = false;
+               }
              /* Some calls are known not to return.  */
              else
                fallthru = !(gimple_call_flags (last) & ECF_NORETURN);
@@ -588,88 +882,11 @@ make_edges (void)
              fallthru = true;
              break;
 
-           case GIMPLE_OMP_PARALLEL:
-           case GIMPLE_OMP_TASK:
-           case GIMPLE_OMP_FOR:
-           case GIMPLE_OMP_SINGLE:
-           case GIMPLE_OMP_MASTER:
-           case GIMPLE_OMP_ORDERED:
-           case GIMPLE_OMP_CRITICAL:
-           case GIMPLE_OMP_SECTION:
-             cur_region = new_omp_region (bb, code, cur_region);
-             fallthru = true;
-             break;
-
-           case GIMPLE_OMP_SECTIONS:
-             cur_region = new_omp_region (bb, code, cur_region);
-             fallthru = true;
-             break;
-
-           case GIMPLE_OMP_SECTIONS_SWITCH:
-             fallthru = false;
-             break;
-
-            case GIMPLE_OMP_ATOMIC_LOAD:
-            case GIMPLE_OMP_ATOMIC_STORE:
-               fallthru = true;
-               break;
-
-           case GIMPLE_OMP_RETURN:
-             /* In the case of a GIMPLE_OMP_SECTION, the edge will go
-                somewhere other than the next block.  This will be
-                created later.  */
-             cur_region->exit = bb;
-             fallthru = cur_region->type != GIMPLE_OMP_SECTION;
-             cur_region = cur_region->outer;
-             break;
-
-           case GIMPLE_OMP_CONTINUE:
-             cur_region->cont = bb;
-             switch (cur_region->type)
-               {
-               case GIMPLE_OMP_FOR:
-                 /* Mark all GIMPLE_OMP_FOR and GIMPLE_OMP_CONTINUE
-                    succs edges as abnormal to prevent splitting
-                    them.  */
-                 single_succ_edge (cur_region->entry)->flags |= EDGE_ABNORMAL;
-                 /* Make the loopback edge.  */
-                 make_edge (bb, single_succ (cur_region->entry),
-                            EDGE_ABNORMAL);
-
-                 /* Create an edge from GIMPLE_OMP_FOR to exit, which
-                    corresponds to the case that the body of the loop
-                    is not executed at all.  */
-                 make_edge (cur_region->entry, bb->next_bb, EDGE_ABNORMAL);
-                 make_edge (bb, bb->next_bb, EDGE_FALLTHRU | EDGE_ABNORMAL);
-                 fallthru = false;
-                 break;
-
-               case GIMPLE_OMP_SECTIONS:
-                 /* Wire up the edges into and out of the nested sections.  */
-                 {
-                   basic_block switch_bb = single_succ (cur_region->entry);
-
-                   struct omp_region *i;
-                   for (i = cur_region->inner; i ; i = i->next)
-                     {
-                       gcc_assert (i->type == GIMPLE_OMP_SECTION);
-                       make_edge (switch_bb, i->entry, 0);
-                       make_edge (i->exit, bb, EDGE_FALLTHRU);
-                     }
-
-                   /* Make the loopback edge to the block with
-                      GIMPLE_OMP_SECTIONS_SWITCH.  */
-                   make_edge (bb, switch_bb, 0);
-
-                   /* Make the edge from the switch to exit.  */
-                   make_edge (switch_bb, bb->next_bb, 0);
-                   fallthru = false;
-                 }
-                 break;
-
-               default:
-                 gcc_unreachable ();
-               }
+           CASE_GIMPLE_OMP:
+             fallthru = make_gimple_omp_edges (bb, &cur_region,
+                                               &cur_omp_region_idx);
+             if (cur_region && bb_to_omp_idx == NULL)
+               bb_to_omp_idx = XCNEWVEC (int, n_basic_blocks_for_fn (cfun));
              break;
 
            case GIMPLE_TRANSACTION:
@@ -690,15 +907,81 @@ make_edges (void)
        fallthru = true;
 
       if (fallthru)
-        {
-         make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
-         if (last)
-            assign_discriminator (gimple_location (last), bb->next_bb);
+       make_edge (bb, bb->next_bb, EDGE_FALLTHRU);
+    }
+
+  /* Computed gotos are hell to deal with, especially if there are
+     lots of them with a large number of destinations.  So we factor
+     them to a common computed goto location before we build the
+     edge list.  After we convert back to normal form, we will un-factor
+     the computed gotos since factoring introduces an unwanted jump.
+     For non-local gotos and abnormal edges from calls to calls that return
+     twice or forced labels, factor the abnormal edges too, by having all
+     abnormal edges from the calls go to a common artificial basic block
+     with ABNORMAL_DISPATCHER internal call and abnormal edges from that
+     basic block to all forced labels and calls returning twice.
+     We do this per-OpenMP structured block, because those regions
+     are guaranteed to be single entry single exit by the standard,
+     so it is not allowed to enter or exit such regions abnormally this way,
+     thus all computed gotos, non-local gotos and setjmp/longjmp calls
+     must not transfer control across SESE region boundaries.  */
+  if (!ab_edge_goto.is_empty () || !ab_edge_call.is_empty ())
+    {
+      gimple_stmt_iterator gsi;
+      basic_block dispatcher_bb_array[2] = { NULL, NULL };
+      basic_block *dispatcher_bbs = dispatcher_bb_array;
+      int count = n_basic_blocks_for_fn (cfun);
+
+      if (bb_to_omp_idx)
+       dispatcher_bbs = XCNEWVEC (basic_block, 2 * count);
+
+      FOR_EACH_BB_FN (bb, cfun)
+       {
+         for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+           {
+             gimple label_stmt = gsi_stmt (gsi);
+             tree target;
+
+             if (gimple_code (label_stmt) != GIMPLE_LABEL)
+               break;
+
+             target = gimple_label_label (label_stmt);
+
+             /* Make an edge to every label block that has been marked as a
+                potential target for a computed goto or a non-local goto.  */
+             if (FORCED_LABEL (target))
+               handle_abnormal_edges (dispatcher_bbs, bb, bb_to_omp_idx,
+                                      &ab_edge_goto, true);
+             if (DECL_NONLOCAL (target))
+               {
+                 handle_abnormal_edges (dispatcher_bbs, bb, bb_to_omp_idx,
+                                        &ab_edge_call, false);
+                 break;
+               }
+           }
+
+         if (!gsi_end_p (gsi) && is_gimple_debug (gsi_stmt (gsi)))
+           gsi_next_nondebug (&gsi);
+         if (!gsi_end_p (gsi))
+           {
+             /* Make an edge to every setjmp-like call.  */
+             gimple call_stmt = gsi_stmt (gsi);
+             if (is_gimple_call (call_stmt)
+                 && ((gimple_call_flags (call_stmt) & ECF_RETURNS_TWICE)
+                     || gimple_call_builtin_p (call_stmt,
+                                               BUILT_IN_SETJMP_RECEIVER)))
+               handle_abnormal_edges (dispatcher_bbs, bb, bb_to_omp_idx,
+                                      &ab_edge_call, false);
+           }
        }
+
+      if (bb_to_omp_idx)
+       XDELETE (dispatcher_bbs);
     }
 
-  if (root_omp_region)
-    free_omp_regions ();
+  XDELETE (bb_to_omp_idx);
+
+  free_omp_regions ();
 
   /* Fold COND_EXPR_COND of each COND_EXPR.  */
   fold_cond_expr_cond ();
@@ -717,7 +1000,8 @@ next_discriminator_for_locus (location_t locus)
 
   item.locus = locus;
   item.discriminator = 0;
-  slot = discriminator_per_locus.find_slot_with_hash (&item, locus, INSERT);
+  slot = discriminator_per_locus->find_slot_with_hash (
+      &item, LOCATION_LINE (locus), INSERT);
   gcc_assert (slot);
   if (*slot == HTAB_EMPTY_ENTRY)
     {
@@ -752,22 +1036,37 @@ same_line_p (location_t locus1, location_t locus2)
           && filename_cmp (from.file, to.file) == 0);
 }
 
-/* Assign a unique discriminator value to block BB if it begins at the same
-   LOCUS as its predecessor block.  */
+/* Assign discriminators to each basic block.  */
 
 static void
-assign_discriminator (location_t locus, basic_block bb)
+assign_discriminators (void)
 {
-  gimple first_in_to_bb, last_in_to_bb;
+  basic_block bb;
 
-  if (locus == 0 || bb->discriminator != 0)
-    return;
+  FOR_EACH_BB_FN (bb, cfun)
+    {
+      edge e;
+      edge_iterator ei;
+      gimple last = last_stmt (bb);
+      location_t locus = last ? gimple_location (last) : UNKNOWN_LOCATION;
+
+      if (locus == UNKNOWN_LOCATION)
+       continue;
 
-  first_in_to_bb = first_non_label_stmt (bb);
-  last_in_to_bb = last_stmt (bb);
-  if ((first_in_to_bb && same_line_p (locus, gimple_location (first_in_to_bb)))
-      || (last_in_to_bb && same_line_p (locus, gimple_location (last_in_to_bb))))
-    bb->discriminator = next_discriminator_for_locus (locus);
+      FOR_EACH_EDGE (e, ei, bb->succs)
+       {
+         gimple first = first_non_label_stmt (e->dest);
+         gimple last = last_stmt (e->dest);
+         if ((first && same_line_p (locus, gimple_location (first)))
+             || (last && same_line_p (locus, gimple_location (last))))
+           {
+             if (e->dest->discriminator != 0 && bb->discriminator == 0)
+               bb->discriminator = next_discriminator_for_locus (locus);
+             else
+               e->dest->discriminator = next_discriminator_for_locus (locus);
+           }
+       }
+    }
 }
 
 /* Create the edges for a GIMPLE_COND starting at block BB.  */
@@ -780,13 +1079,10 @@ make_cond_expr_edges (basic_block bb)
   basic_block then_bb, else_bb;
   tree then_label, else_label;
   edge e;
-  location_t entry_locus;
 
   gcc_assert (entry);
   gcc_assert (gimple_code (entry) == GIMPLE_COND);
 
-  entry_locus = gimple_location (entry);
-
   /* Entry basic blocks for each component.  */
   then_label = gimple_cond_true_label (entry);
   else_label = gimple_cond_false_label (entry);
@@ -796,14 +1092,10 @@ make_cond_expr_edges (basic_block bb)
   else_stmt = first_stmt (else_bb);
 
   e = make_edge (bb, then_bb, EDGE_TRUE_VALUE);
-  assign_discriminator (entry_locus, then_bb);
   e->goto_locus = gimple_location (then_stmt);
   e = make_edge (bb, else_bb, EDGE_FALSE_VALUE);
   if (e)
-    {
-      assign_discriminator (entry_locus, else_bb);
-      e->goto_locus = gimple_location (else_stmt);
-    }
+    e->goto_locus = gimple_location (else_stmt);
 
   /* We do not need the labels anymore.  */
   gimple_cond_set_true_label (entry, NULL_TREE);
@@ -818,19 +1110,17 @@ make_cond_expr_edges (basic_block bb)
    SWITCH_EXPRs and structure sharing rules, then free the hash table
    element.  */
 
-static bool
-edge_to_cases_cleanup (const void *key ATTRIBUTE_UNUSED, void **value,
-                      void *data ATTRIBUTE_UNUSED)
+bool
+edge_to_cases_cleanup (edge const &, tree const &value, void *)
 {
   tree t, next;
 
-  for (t = (tree) *value; t; t = next)
+  for (t = value; t; t = next)
     {
       next = CASE_CHAIN (t);
       CASE_CHAIN (t) = NULL;
     }
 
-  *value = NULL;
   return true;
 }
 
@@ -840,7 +1130,7 @@ void
 start_recording_case_labels (void)
 {
   gcc_assert (edge_to_cases == NULL);
-  edge_to_cases = pointer_map_create ();
+  edge_to_cases = new hash_map<edge, tree>;
   touched_switch_bbs = BITMAP_ALLOC (NULL);
 }
 
@@ -859,12 +1149,12 @@ end_recording_case_labels (void)
 {
   bitmap_iterator bi;
   unsigned i;
-  pointer_map_traverse (edge_to_cases, edge_to_cases_cleanup, NULL);
-  pointer_map_destroy (edge_to_cases);
+  edge_to_cases->traverse<void *, edge_to_cases_cleanup> (NULL);
+  delete edge_to_cases;
   edge_to_cases = NULL;
   EXECUTE_IF_SET_IN_BITMAP (touched_switch_bbs, 0, i, bi)
     {
-      basic_block bb = BASIC_BLOCK (i);
+      basic_block bb = BASIC_BLOCK_FOR_FN (cfun, i);
       if (bb)
        {
          gimple stmt = last_stmt (bb);
@@ -883,7 +1173,7 @@ end_recording_case_labels (void)
 static tree
 get_cases_for_edge (edge e, gimple t)
 {
-  void **slot;
+  tree *slot;
   size_t i, n;
 
   /* If we are not recording cases, then we do not have CASE_LABEL_EXPR
@@ -891,9 +1181,9 @@ get_cases_for_edge (edge e, gimple t)
   if (!recording_case_labels_p ())
     return NULL;
 
-  slot = pointer_map_contains (edge_to_cases, e);
+  slot = edge_to_cases->get (e);
   if (slot)
-    return (tree) *slot;
+    return *slot;
 
   /* If we did not find E in the hash table, then this must be the first
      time we have been queried for information about E & T.  Add all the
@@ -909,12 +1199,12 @@ get_cases_for_edge (edge e, gimple t)
 
       /* Add it to the chain of CASE_LABEL_EXPRs referencing E, or create
         a new chain.  */
-      slot = pointer_map_insert (edge_to_cases, this_edge);
-      CASE_CHAIN (elt) = (tree) *slot;
-      *slot = elt;
+      tree &s = edge_to_cases->get_or_insert (this_edge);
+      CASE_CHAIN (elt) = s;
+      s = elt;
     }
 
-  return (tree) *pointer_map_contains (edge_to_cases, e);
+  return *edge_to_cases->get (e);
 }
 
 /* Create the edges for a GIMPLE_SWITCH starting at block BB.  */
@@ -923,11 +1213,8 @@ static void
 make_gimple_switch_edges (basic_block bb)
 {
   gimple entry = last_stmt (bb);
-  location_t entry_locus;
   size_t i, n;
 
-  entry_locus = gimple_location (entry);
-
   n = gimple_switch_num_labels (entry);
 
   for (i = 0; i < n; ++i)
@@ -935,7 +1222,6 @@ make_gimple_switch_edges (basic_block bb)
       tree lab = CASE_LABEL (gimple_switch_label (entry, i));
       basic_block label_bb = label_to_block (lab);
       make_edge (bb, label_bb, 0);
-      assign_discriminator (entry_locus, label_bb);
     }
 }
 
@@ -952,7 +1238,8 @@ label_to_block_fn (struct function *ifun, tree dest)
      and undefined variable warnings quite right.  */
   if (seen_error () && uid < 0)
     {
-      gimple_stmt_iterator gsi = gsi_start_bb (BASIC_BLOCK (NUM_FIXED_BLOCKS));
+      gimple_stmt_iterator gsi =
+       gsi_start_bb (BASIC_BLOCK_FOR_FN (cfun, NUM_FIXED_BLOCKS));
       gimple stmt;
 
       stmt = gimple_build_label (dest);
@@ -964,50 +1251,10 @@ label_to_block_fn (struct function *ifun, tree dest)
   return (*ifun->cfg->x_label_to_block_map)[uid];
 }
 
-/* Create edges for an abnormal goto statement at block BB.  If FOR_CALL
-   is true, the source statement is a CALL_EXPR instead of a GOTO_EXPR.  */
-
-void
-make_abnormal_goto_edges (basic_block bb, bool for_call)
-{
-  basic_block target_bb;
-  gimple_stmt_iterator gsi;
-
-  FOR_EACH_BB (target_bb)
-    {
-      for (gsi = gsi_start_bb (target_bb); !gsi_end_p (gsi); gsi_next (&gsi))
-       {
-         gimple label_stmt = gsi_stmt (gsi);
-         tree target;
-
-         if (gimple_code (label_stmt) != GIMPLE_LABEL)
-           break;
-
-         target = gimple_label_label (label_stmt);
-
-         /* Make an edge to every label block that has been marked as a
-            potential target for a computed goto or a non-local goto.  */
-         if ((FORCED_LABEL (target) && !for_call)
-             || (DECL_NONLOCAL (target) && for_call))
-           {
-             make_edge (bb, target_bb, EDGE_ABNORMAL);
-             break;
-           }
-       }
-      if (!gsi_end_p (gsi))
-       {
-         /* Make an edge to every setjmp-like call.  */
-         gimple call_stmt = gsi_stmt (gsi);
-         if (is_gimple_call (call_stmt)
-             && (gimple_call_flags (call_stmt) & ECF_RETURNS_TWICE))
-           make_edge (bb, target_bb, EDGE_ABNORMAL);
-       }
-    }
-}
-
-/* Create edges for a goto statement at block BB.  */
+/* Create edges for a goto statement at block BB.  Returns true
+   if abnormal edges should be created.  */
 
-static void
+static bool
 make_goto_expr_edges (basic_block bb)
 {
   gimple_stmt_iterator last = gsi_last_bb (bb);
@@ -1020,13 +1267,12 @@ make_goto_expr_edges (basic_block bb)
       basic_block label_bb = label_to_block (dest);
       edge e = make_edge (bb, label_bb, EDGE_FALLTHRU);
       e->goto_locus = gimple_location (goto_t);
-      assign_discriminator (e->goto_locus, label_bb);
       gsi_remove (&last, true);
-      return;
+      return false;
     }
 
   /* A computed GOTO creates abnormal edges.  */
-  make_abnormal_goto_edges (bb, false);
+  return true;
 }
 
 /* Create edges for an asm statement with labels at block BB.  */
@@ -1035,7 +1281,6 @@ static void
 make_gimple_asm_edges (basic_block bb)
 {
   gimple stmt = last_stmt (bb);
-  location_t stmt_loc = gimple_location (stmt);
   int i, n = gimple_asm_nlabels (stmt);
 
   for (i = 0; i < n; ++i)
@@ -1043,7 +1288,6 @@ make_gimple_asm_edges (basic_block bb)
       tree label = TREE_VALUE (gimple_asm_label_op (stmt, i));
       basic_block label_bb = label_to_block (label);
       make_edge (bb, label_bb, 0);
-      assign_discriminator (stmt_loc, label_bb);
     }
 }
 
@@ -1149,11 +1393,11 @@ void
 cleanup_dead_labels (void)
 {
   basic_block bb;
-  label_for_bb = XCNEWVEC (struct label_record, last_basic_block);
+  label_for_bb = XCNEWVEC (struct label_record, last_basic_block_for_fn (cfun));
 
   /* Find a suitable label for each block.  We use the first user-defined
      label if there is one, or otherwise just the first label we see.  */
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       gimple_stmt_iterator i;
 
@@ -1189,7 +1433,7 @@ cleanup_dead_labels (void)
 
   /* Now redirect all jumps/branches to the selected label.
      First do so for each block ending in a control statement.  */
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       gimple stmt = last_stmt (bb);
       tree label, new_label;
@@ -1281,7 +1525,7 @@ cleanup_dead_labels (void)
   /* Finally, purge dead labels.  All user-defined labels and labels that
      can be the target of non-local gotos and labels which have their
      address taken are preserved.  */
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       gimple_stmt_iterator i;
       tree label_for_this_bb = label_for_bb[bb->index].label;
@@ -1363,12 +1607,12 @@ group_case_labels_stmt (gimple stmt)
        {
          tree merge_case = gimple_switch_label (stmt, i);
          basic_block merge_bb = label_to_block (CASE_LABEL (merge_case));
-         double_int bhp1 = tree_to_double_int (base_high) + double_int_one;
+         wide_int bhp1 = wi::add (base_high, 1);
 
          /* Merge the cases if they jump to the same place,
             and their ranges are consecutive.  */
          if (merge_bb == base_bb
-             && tree_to_double_int (CASE_LOW (merge_case)) == bhp1)
+             && wi::eq_p (CASE_LOW (merge_case), bhp1))
            {
              base_high = CASE_HIGH (merge_case) ?
                  CASE_HIGH (merge_case) : CASE_LOW (merge_case);
@@ -1405,7 +1649,7 @@ group_case_labels (void)
 {
   basic_block bb;
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       gimple stmt = last_stmt (bb);
       if (stmt && gimple_code (stmt) == GIMPLE_SWITCH)
@@ -1433,7 +1677,7 @@ gimple_can_merge_blocks_p (basic_block a, basic_block b)
   if (!single_pred_p (b))
     return false;
 
-  if (b == EXIT_BLOCK_PTR)
+  if (b == EXIT_BLOCK_PTR_FOR_FN (cfun))
     return false;
 
   /* If A ends by a statement causing exceptions or something similar, we
@@ -1462,8 +1706,12 @@ gimple_can_merge_blocks_p (basic_block a, basic_block b)
        return false;
     }
 
-  /* Protect the loop latches.  */
-  if (current_loops && b->loop_father->latch == b)
+  /* Protect simple loop latches.  We only want to avoid merging
+     the latch with the loop header in this case.  */
+  if (current_loops
+      && b->loop_father->latch == b
+      && loops_state_satisfies_p (LOOPS_HAVE_SIMPLE_LATCHES)
+      && b->loop_father->header == a)
     return false;
 
   /* It must be possible to eliminate all phi nodes in B.  If ssa form
@@ -1497,49 +1745,6 @@ gimple_can_merge_blocks_p (basic_block a, basic_block b)
   return true;
 }
 
-/* Return true if the var whose chain of uses starts at PTR has no
-   nondebug uses.  */
-bool
-has_zero_uses_1 (const ssa_use_operand_t *head)
-{
-  const ssa_use_operand_t *ptr;
-
-  for (ptr = head->next; ptr != head; ptr = ptr->next)
-    if (!is_gimple_debug (USE_STMT (ptr)))
-      return false;
-
-  return true;
-}
-
-/* Return true if the var whose chain of uses starts at PTR has a
-   single nondebug use.  Set USE_P and STMT to that single nondebug
-   use, if so, or to NULL otherwise.  */
-bool
-single_imm_use_1 (const ssa_use_operand_t *head,
-                 use_operand_p *use_p, gimple *stmt)
-{
-  ssa_use_operand_t *ptr, *single_use = 0;
-
-  for (ptr = head->next; ptr != head; ptr = ptr->next)
-    if (!is_gimple_debug (USE_STMT (ptr)))
-      {
-       if (single_use)
-         {
-           single_use = NULL;
-           break;
-         }
-       single_use = ptr;
-      }
-
-  if (use_p)
-    *use_p = single_use;
-
-  if (stmt)
-    *stmt = single_use ? single_use->loc.stmt : NULL;
-
-  return !!single_use;
-}
-
 /* Replaces all uses of NAME by VAL.  */
 
 void
@@ -1552,6 +1757,11 @@ replace_uses_by (tree name, tree val)
 
   FOR_EACH_IMM_USE_STMT (stmt, imm_iter, name)
     {
+      /* Mark the block if we change the last stmt in it.  */
+      if (cfgcleanup_altered_bbs
+         && stmt_ends_bb_p (stmt))
+       bitmap_set_bit (cfgcleanup_altered_bbs, gimple_bb (stmt)->index);
+
       FOR_EACH_IMM_USE_ON_STMT (use, imm_iter)
         {
          replace_exp (use, val);
@@ -1576,11 +1786,6 @@ replace_uses_by (tree name, tree val)
          gimple orig_stmt = stmt;
          size_t i;
 
-         /* Mark the block if we changed the last stmt in it.  */
-         if (cfgcleanup_altered_bbs
-             && stmt_ends_bb_p (stmt))
-           bitmap_set_bit (cfgcleanup_altered_bbs, gimple_bb (stmt)->index);
-
          /* FIXME.  It shouldn't be required to keep TREE_CONSTANT
             on ADDR_EXPRs up-to-date on GIMPLE.  Propagation will
             only change sth from non-invariant to invariant, and only
@@ -1612,9 +1817,8 @@ replace_uses_by (tree name, tree val)
   if (current_loops)
     {
       struct loop *loop;
-      loop_iterator li;
 
-      FOR_EACH_LOOP (li, loop, 0)
+      FOR_EACH_LOOP (loop, 0)
        {
          substitute_in_loop_info (loop, name, val);
        }
@@ -1740,6 +1944,15 @@ gimple_merge_blocks (basic_block a, basic_block b)
        }
     }
 
+  /* When merging two BBs, if their counts are different, the larger count
+     is selected as the new bb count. This is to handle inconsistent
+     profiles.  */
+  if (a->loop_father == b->loop_father)
+    {
+      a->count = MAX (a->count, b->count);
+      a->frequency = MAX (a->frequency, b->frequency);
+    }
+
   /* Merge the sequences.  */
   last = gsi_last_bb (a);
   gsi_insert_seq_after (&last, bb_seq (b), GSI_NEW_STMT);
@@ -1823,7 +2036,7 @@ remove_bb (basic_block bb)
       fprintf (dump_file, "Removing basic block %d\n", bb->index);
       if (dump_flags & TDF_DETAILS)
        {
-         dump_bb (dump_file, bb, 0, dump_flags);
+         dump_bb (dump_file, bb, 0, TDF_BLOCKS);
          fprintf (dump_file, "\n");
        }
     }
@@ -2051,8 +2264,8 @@ gimple_debug_bb (basic_block bb)
 basic_block
 gimple_debug_bb_n (int n)
 {
-  gimple_debug_bb (BASIC_BLOCK (n));
-  return BASIC_BLOCK (n);
+  gimple_debug_bb (BASIC_BLOCK_FOR_FN (cfun, n));
+  return BASIC_BLOCK_FOR_FN (cfun, n);
 }
 
 
@@ -2080,7 +2293,8 @@ gimple_dump_cfg (FILE *file, int flags)
     {
       dump_function_header (file, current_function_decl, flags);
       fprintf (file, ";; \n%d basic blocks, %d edges, last basic block %d.\n\n",
-              n_basic_blocks, n_edges, last_basic_block);
+              n_basic_blocks_for_fn (cfun), n_edges_for_fn (cfun),
+              last_basic_block_for_fn (cfun));
 
       brief_dump_cfg (file, flags | TDF_COMMENT);
       fprintf (file, "\n");
@@ -2115,13 +2329,13 @@ dump_cfg_stats (FILE *file)
   fprintf (file, fmt_str, "", "  instances  ", "used ");
   fprintf (file, "---------------------------------------------------------\n");
 
-  size = n_basic_blocks * sizeof (struct basic_block_def);
+  size = n_basic_blocks_for_fn (cfun) * sizeof (struct basic_block_def);
   total += size;
-  fprintf (file, fmt_str_1, "Basic blocks", n_basic_blocks,
+  fprintf (file, fmt_str_1, "Basic blocks", n_basic_blocks_for_fn (cfun),
           SCALE (size), LABEL (size));
 
   num_edges = 0;
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     num_edges += EDGE_COUNT (bb->succs);
   size = num_edges * sizeof (struct edge_def);
   total += size;
@@ -2224,28 +2438,10 @@ is_ctrl_altering_stmt (gimple t)
   switch (gimple_code (t))
     {
     case GIMPLE_CALL:
-      {
-       int flags = gimple_call_flags (t);
-
-       /* A call alters control flow if it can make an abnormal goto.  */
-       if (call_can_make_abnormal_goto (t))
-         return true;
-
-       /* A call also alters control flow if it does not return.  */
-       if (flags & ECF_NORETURN)
-         return true;
-
-       /* TM ending statements have backedges out of the transaction.
-          Return true so we split the basic block containing them.
-          Note that the TM_BUILTIN test is merely an optimization.  */
-       if ((flags & ECF_TM_BUILTIN)
-           && is_tm_ending_fndecl (gimple_call_fndecl (t)))
-         return true;
-
-       /* BUILT_IN_RETURN call is same as return statement.  */
-       if (gimple_call_builtin_p (t, BUILT_IN_RETURN))
-         return true;
-      }
+      /* Per stmt call flag indicates whether the call could alter
+        controlflow.  */
+      if (gimple_call_ctrl_altering_p (t))
+       return true;
       break;
 
     case GIMPLE_EH_DISPATCH:
@@ -2343,7 +2539,7 @@ stmt_ends_bb_p (gimple t)
 void
 delete_tree_cfg_annotations (void)
 {
-  vec_free (label_to_block_map);
+  vec_free (label_to_block_map_for_fn (cfun));
 }
 
 
@@ -2427,12 +2623,11 @@ last_and_only_stmt (basic_block bb)
 static void
 reinstall_phi_args (edge new_edge, edge old_edge)
 {
-  edge_var_map_vector *v;
   edge_var_map *vm;
   int i;
   gimple_stmt_iterator phis;
 
-  v = redirect_edge_var_map_vector (old_edge);
+  vec<edge_var_map> *v = redirect_edge_var_map_vector (old_edge);
   if (!v)
     return;
 
@@ -2669,10 +2864,56 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
 
     case REALPART_EXPR:
     case IMAGPART_EXPR:
+    case BIT_FIELD_REF:
+      if (!is_gimple_reg_type (TREE_TYPE (t)))
+       {
+         error ("non-scalar BIT_FIELD_REF, IMAGPART_EXPR or REALPART_EXPR");
+         return t;
+       }
+
+      if (TREE_CODE (t) == BIT_FIELD_REF)
+       {
+         tree t0 = TREE_OPERAND (t, 0);
+         tree t1 = TREE_OPERAND (t, 1);
+         tree t2 = TREE_OPERAND (t, 2);
+         if (!tree_fits_uhwi_p (t1)
+             || !tree_fits_uhwi_p (t2))
+           {
+             error ("invalid position or size operand to BIT_FIELD_REF");
+             return t;
+           }
+         if (INTEGRAL_TYPE_P (TREE_TYPE (t))
+             && (TYPE_PRECISION (TREE_TYPE (t))
+                 != tree_to_uhwi (t1)))
+           {
+             error ("integral result type precision does not match "
+                    "field size of BIT_FIELD_REF");
+             return t;
+           }
+         else if (!INTEGRAL_TYPE_P (TREE_TYPE (t))
+                  && TYPE_MODE (TREE_TYPE (t)) != BLKmode
+                  && (GET_MODE_PRECISION (TYPE_MODE (TREE_TYPE (t)))
+                      != tree_to_uhwi (t1)))
+           {
+             error ("mode precision of non-integral result does not "
+                    "match field size of BIT_FIELD_REF");
+             return t;
+           }
+         if (!AGGREGATE_TYPE_P (TREE_TYPE (t0))
+             && (tree_to_uhwi (t1) + tree_to_uhwi (t2)
+                 > tree_to_uhwi (TYPE_SIZE (TREE_TYPE (t0)))))
+           {
+             error ("position plus size exceeds size of referenced object in "
+                    "BIT_FIELD_REF");
+             return t;
+           }
+       }
+      t = TREE_OPERAND (t, 0);
+
+      /* Fall-through.  */
     case COMPONENT_REF:
     case ARRAY_REF:
     case ARRAY_RANGE_REF:
-    case BIT_FIELD_REF:
     case VIEW_CONVERT_EXPR:
       /* We have a nest of references.  Verify that each of the operands
         that determine where to reference is either a constant or a variable,
@@ -2691,32 +2932,13 @@ verify_expr (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
              if (TREE_OPERAND (t, 3))
                CHECK_OP (3, "invalid array stride");
            }
-         else if (TREE_CODE (t) == BIT_FIELD_REF)
+         else if (TREE_CODE (t) == BIT_FIELD_REF
+                  || TREE_CODE (t) == REALPART_EXPR
+                  || TREE_CODE (t) == IMAGPART_EXPR)
            {
-             if (!host_integerp (TREE_OPERAND (t, 1), 1)
-                 || !host_integerp (TREE_OPERAND (t, 2), 1))
-               {
-                 error ("invalid position or size operand to BIT_FIELD_REF");
-                 return t;
-               }
-             if (INTEGRAL_TYPE_P (TREE_TYPE (t))
-                 && (TYPE_PRECISION (TREE_TYPE (t))
-                     != TREE_INT_CST_LOW (TREE_OPERAND (t, 1))))
-               {
-                 error ("integral result type precision does not match "
-                        "field size of BIT_FIELD_REF");
-                 return t;
-               }
-             else if (!INTEGRAL_TYPE_P (TREE_TYPE (t))
-                      && !AGGREGATE_TYPE_P (TREE_TYPE (t))
-                      && TYPE_MODE (TREE_TYPE (t)) != BLKmode
-                      && (GET_MODE_PRECISION (TYPE_MODE (TREE_TYPE (t)))
-                          != TREE_INT_CST_LOW (TREE_OPERAND (t, 1))))
-               {
-                 error ("mode precision of non-integral result does not "
-                        "match field size of BIT_FIELD_REF");
-                 return t;
-               }
+             error ("non-top-level BIT_FIELD_REF, IMAGPART_EXPR or "
+                    "REALPART_EXPR");
+             return t;
            }
 
          t = TREE_OPERAND (t, 0);
@@ -3328,7 +3550,7 @@ verify_gimple_assign_unary (gimple stmt)
       {
        if ((!INTEGRAL_TYPE_P (rhs1_type) || !SCALAR_FLOAT_TYPE_P (lhs_type))
            && (!VECTOR_INTEGER_TYPE_P (rhs1_type)
-               || !VECTOR_FLOAT_TYPE_P(lhs_type)))
+               || !VECTOR_FLOAT_TYPE_P (lhs_type)))
          {
            error ("invalid types in conversion to floating point");
            debug_generic_expr (lhs_type);
@@ -3343,7 +3565,7 @@ verify_gimple_assign_unary (gimple stmt)
       {
         if ((!INTEGRAL_TYPE_P (lhs_type) || !SCALAR_FLOAT_TYPE_P (rhs1_type))
             && (!VECTOR_INTEGER_TYPE_P (lhs_type)
-                || !VECTOR_FLOAT_TYPE_P(rhs1_type)))
+                || !VECTOR_FLOAT_TYPE_P (rhs1_type)))
          {
            error ("invalid types in conversion to integer");
            debug_generic_expr (lhs_type);
@@ -3353,12 +3575,21 @@ verify_gimple_assign_unary (gimple stmt)
 
         return false;
       }
-
-    case VEC_UNPACK_HI_EXPR:
-    case VEC_UNPACK_LO_EXPR:
     case REDUC_MAX_EXPR:
     case REDUC_MIN_EXPR:
     case REDUC_PLUS_EXPR:
+      if (!VECTOR_TYPE_P (rhs1_type)
+         || !useless_type_conversion_p (lhs_type, TREE_TYPE (rhs1_type)))
+        {
+         error ("reduction should convert from vector to element type");
+         debug_generic_expr (lhs_type);
+         debug_generic_expr (rhs1_type);
+         return true;
+       }
+      return false;
+
+    case VEC_UNPACK_HI_EXPR:
+    case VEC_UNPACK_LO_EXPR:
     case VEC_UNPACK_FLOAT_HI_EXPR:
     case VEC_UNPACK_FLOAT_LO_EXPR:
       /* FIXME.  */
@@ -3368,7 +3599,6 @@ verify_gimple_assign_unary (gimple stmt)
     case ABS_EXPR:
     case BIT_NOT_EXPR:
     case PAREN_EXPR:
-    case NON_LVALUE_EXPR:
     case CONJ_EXPR:
       break;
 
@@ -3441,59 +3671,26 @@ verify_gimple_assign_binary (gimple stmt)
     case LROTATE_EXPR:
     case RROTATE_EXPR:
       {
-       /* Shifts and rotates are ok on integral types, fixed point
-          types and integer vector types.  */
-       if ((!INTEGRAL_TYPE_P (rhs1_type)
-            && !FIXED_POINT_TYPE_P (rhs1_type)
-            && !(TREE_CODE (rhs1_type) == VECTOR_TYPE
-                 && INTEGRAL_TYPE_P (TREE_TYPE (rhs1_type))))
-           || (!INTEGRAL_TYPE_P (rhs2_type)
-               /* Vector shifts of vectors are also ok.  */
-               && !(TREE_CODE (rhs1_type) == VECTOR_TYPE
-                    && INTEGRAL_TYPE_P (TREE_TYPE (rhs1_type))
-                    && TREE_CODE (rhs2_type) == VECTOR_TYPE
-                    && INTEGRAL_TYPE_P (TREE_TYPE (rhs2_type))))
-           || !useless_type_conversion_p (lhs_type, rhs1_type))
-         {
-           error ("type mismatch in shift expression");
-           debug_generic_expr (lhs_type);
-           debug_generic_expr (rhs1_type);
-           debug_generic_expr (rhs2_type);
-           return true;
-         }
-
-       return false;
-      }
-
-    case VEC_LSHIFT_EXPR:
-    case VEC_RSHIFT_EXPR:
-      {
-       if (TREE_CODE (rhs1_type) != VECTOR_TYPE
-           || !(INTEGRAL_TYPE_P (TREE_TYPE (rhs1_type))
-                || POINTER_TYPE_P (TREE_TYPE (rhs1_type))
-                || FIXED_POINT_TYPE_P (TREE_TYPE (rhs1_type))
-                || SCALAR_FLOAT_TYPE_P (TREE_TYPE (rhs1_type)))
+       /* Shifts and rotates are ok on integral types, fixed point
+          types and integer vector types.  */
+       if ((!INTEGRAL_TYPE_P (rhs1_type)
+            && !FIXED_POINT_TYPE_P (rhs1_type)
+            && !(TREE_CODE (rhs1_type) == VECTOR_TYPE
+                 && INTEGRAL_TYPE_P (TREE_TYPE (rhs1_type))))
            || (!INTEGRAL_TYPE_P (rhs2_type)
-               && (TREE_CODE (rhs2_type) != VECTOR_TYPE
-                   || !INTEGRAL_TYPE_P (TREE_TYPE (rhs2_type))))
+               /* Vector shifts of vectors are also ok.  */
+               && !(TREE_CODE (rhs1_type) == VECTOR_TYPE
+                    && INTEGRAL_TYPE_P (TREE_TYPE (rhs1_type))
+                    && TREE_CODE (rhs2_type) == VECTOR_TYPE
+                    && INTEGRAL_TYPE_P (TREE_TYPE (rhs2_type))))
            || !useless_type_conversion_p (lhs_type, rhs1_type))
          {
-           error ("type mismatch in vector shift expression");
+           error ("type mismatch in shift expression");
            debug_generic_expr (lhs_type);
            debug_generic_expr (rhs1_type);
            debug_generic_expr (rhs2_type);
            return true;
          }
-       /* For shifting a vector of non-integral components we
-          only allow shifting by a constant multiple of the element size.  */
-       if (!INTEGRAL_TYPE_P (TREE_TYPE (rhs1_type))
-           && (TREE_CODE (rhs2) != INTEGER_CST
-               || !div_if_zero_remainder (EXACT_DIV_EXPR, rhs2,
-                                          TYPE_SIZE (TREE_TYPE (rhs1_type)))))
-         {
-           error ("non-element sized vector shift of floating point vector");
-           return true;
-         }
 
        return false;
       }
@@ -3539,11 +3736,10 @@ verify_gimple_assign_binary (gimple stmt)
     case PLUS_EXPR:
     case MINUS_EXPR:
       {
-       /* We use regular PLUS_EXPR and MINUS_EXPR for vectors.
-          ???  This just makes the checker happy and may not be what is
-          intended.  */
-       if (TREE_CODE (lhs_type) == VECTOR_TYPE
-           && POINTER_TYPE_P (TREE_TYPE (lhs_type)))
+       tree lhs_etype = lhs_type;
+       tree rhs1_etype = rhs1_type;
+       tree rhs2_etype = rhs2_type;
+       if (TREE_CODE (lhs_type) == VECTOR_TYPE)
          {
            if (TREE_CODE (rhs1_type) != VECTOR_TYPE
                || TREE_CODE (rhs2_type) != VECTOR_TYPE)
@@ -3551,22 +3747,13 @@ verify_gimple_assign_binary (gimple stmt)
                error ("invalid non-vector operands to vector valued plus");
                return true;
              }
-           lhs_type = TREE_TYPE (lhs_type);
-           rhs1_type = TREE_TYPE (rhs1_type);
-           rhs2_type = TREE_TYPE (rhs2_type);
-           /* PLUS_EXPR is commutative, so we might end up canonicalizing
-              the pointer to 2nd place.  */
-           if (POINTER_TYPE_P (rhs2_type))
-             {
-               tree tem = rhs1_type;
-               rhs1_type = rhs2_type;
-               rhs2_type = tem;
-             }
-           goto do_pointer_plus_expr_check;
+           lhs_etype = TREE_TYPE (lhs_type);
+           rhs1_etype = TREE_TYPE (rhs1_type);
+           rhs2_etype = TREE_TYPE (rhs2_type);
          }
-       if (POINTER_TYPE_P (lhs_type)
-           || POINTER_TYPE_P (rhs1_type)
-           || POINTER_TYPE_P (rhs2_type))
+       if (POINTER_TYPE_P (lhs_etype)
+           || POINTER_TYPE_P (rhs1_etype)
+           || POINTER_TYPE_P (rhs2_etype))
          {
            error ("invalid (pointer) operands to plus/minus");
            return true;
@@ -3578,7 +3765,6 @@ verify_gimple_assign_binary (gimple stmt)
 
     case POINTER_PLUS_EXPR:
       {
-do_pointer_plus_expr_check:
        if (!POINTER_TYPE_P (rhs1_type)
            || !useless_type_conversion_p (lhs_type, rhs1_type)
            || !ptrofftype_p (rhs2_type))
@@ -3805,6 +3991,36 @@ verify_gimple_assign_ternary (gimple stmt)
 
       return false;
 
+    case SAD_EXPR:
+      if (!useless_type_conversion_p (rhs1_type, rhs2_type)
+         || !useless_type_conversion_p (lhs_type, rhs3_type)
+         || 2 * GET_MODE_BITSIZE (GET_MODE_INNER
+                                    (TYPE_MODE (TREE_TYPE (rhs1_type))))
+              > GET_MODE_BITSIZE (GET_MODE_INNER
+                                    (TYPE_MODE (TREE_TYPE (lhs_type)))))
+       {
+         error ("type mismatch in sad expression");
+         debug_generic_expr (lhs_type);
+         debug_generic_expr (rhs1_type);
+         debug_generic_expr (rhs2_type);
+         debug_generic_expr (rhs3_type);
+         return true;
+       }
+
+      if (TREE_CODE (rhs1_type) != VECTOR_TYPE
+         || TREE_CODE (rhs2_type) != VECTOR_TYPE
+         || TREE_CODE (rhs3_type) != VECTOR_TYPE)
+       {
+         error ("vector types expected in sad expression");
+         debug_generic_expr (lhs_type);
+         debug_generic_expr (rhs1_type);
+         debug_generic_expr (rhs2_type);
+         debug_generic_expr (rhs3_type);
+         return true;
+       }
+
+      return false;
+
     case DOT_PROD_EXPR:
     case REALIGN_LOAD_EXPR:
       /* FIXME.  */
@@ -3845,7 +4061,9 @@ verify_gimple_assign_single (gimple stmt)
       return true;
     }
 
-  if (handled_component_p (lhs))
+  if (handled_component_p (lhs)
+      || TREE_CODE (lhs) == MEM_REF
+      || TREE_CODE (lhs) == TARGET_MEM_REF)
     res |= verify_types_in_gimple_reference (lhs, true);
 
   /* Special codes we cannot handle via their class.  */
@@ -4001,8 +4219,20 @@ verify_gimple_assign_single (gimple stmt)
                  debug_generic_stmt (rhs1);
                  return true;
                }
+             if (!is_gimple_val (elt_v))
+               {
+                 error ("vector CONSTRUCTOR element is not a GIMPLE value");
+                 debug_generic_stmt (rhs1);
+                 return true;
+               }
            }
        }
+      else if (CONSTRUCTOR_NELTS (rhs1) != 0)
+       {
+         error ("non-vector CONSTRUCTOR with elements");
+         debug_generic_stmt (rhs1);
+         return true;
+       }
       return res;
     case OBJ_TYPE_REF:
     case ASSERT_EXPR:
@@ -4220,10 +4450,17 @@ verify_gimple_label (gimple stmt)
 
   if (TREE_CODE (decl) != LABEL_DECL)
     return true;
+  if (!DECL_NONLOCAL (decl) && !FORCED_LABEL (decl)
+      && DECL_CONTEXT (decl) != current_function_decl)
+    {
+      error ("label's context is not the current function decl");
+      err |= true;
+    }
 
   uid = LABEL_DECL_UID (decl);
   if (cfun->cfg
-      && (uid == -1 || (*label_to_block_map)[uid] != gimple_bb (stmt)))
+      && (uid == -1
+         || (*label_to_block_map_for_fn (cfun))[uid] != gimple_bb (stmt)))
     {
       error ("incorrect entry in label_to_block_map");
       err |= true;
@@ -4462,7 +4699,7 @@ verify_gimple_in_seq (gimple_seq stmts)
 
 /* Return true when the T can be shared.  */
 
-bool
+static bool
 tree_node_can_be_shared (tree t)
 {
   if (IS_TYPE_OR_DECL_P (t)
@@ -4486,7 +4723,7 @@ tree_node_can_be_shared (tree t)
 static tree
 verify_node_sharing_1 (tree *tp, int *walk_subtrees, void *data)
 {
-  struct pointer_set_t *visited = (struct pointer_set_t *) data;
+  hash_set<void *> *visited = (hash_set<void *> *) data;
 
   if (tree_node_can_be_shared (*tp))
     {
@@ -4494,7 +4731,7 @@ verify_node_sharing_1 (tree *tp, int *walk_subtrees, void *data)
       return NULL;
     }
 
-  if (pointer_set_insert (visited, *tp))
+  if (visited->add (*tp))
     return *tp;
 
   return NULL;
@@ -4510,29 +4747,27 @@ verify_node_sharing (tree *tp, int *walk_subtrees, void *data)
 }
 
 static bool eh_error_found;
-static int
-verify_eh_throw_stmt_node (void **slot, void *data)
+bool
+verify_eh_throw_stmt_node (const gimple &stmt, const int &,
+                          hash_set<gimple> *visited)
 {
-  struct throw_stmt_node *node = (struct throw_stmt_node *)*slot;
-  struct pointer_set_t *visited = (struct pointer_set_t *) data;
-
-  if (!pointer_set_contains (visited, node->stmt))
+  if (!visited->contains (stmt))
     {
       error ("dead STMT in EH table");
-      debug_gimple_stmt (node->stmt);
+      debug_gimple_stmt (stmt);
       eh_error_found = true;
     }
-  return 1;
+  return true;
 }
 
 /* Verify if the location LOCs block is in BLOCKS.  */
 
 static bool
-verify_location (pointer_set_t *blocks, location_t loc)
+verify_location (hash_set<tree> *blocks, location_t loc)
 {
   tree block = LOCATION_BLOCK (loc);
   if (block != NULL_TREE
-      && !pointer_set_contains (blocks, block))
+      && !blocks->contains (block))
     {
       error ("location references block not in block tree");
       return true;
@@ -4565,7 +4800,7 @@ verify_expr_no_block (tree *tp, int *walk_subtrees, void *)
 static tree
 verify_expr_location_1 (tree *tp, int *walk_subtrees, void *data)
 {
-  struct pointer_set_t *blocks = (struct pointer_set_t *) data;
+  hash_set<tree> *blocks = (hash_set<tree> *) data;
 
   if (TREE_CODE (*tp) == VAR_DECL
       && DECL_HAS_DEBUG_EXPR_P (*tp))
@@ -4611,12 +4846,12 @@ verify_expr_location (tree *tp, int *walk_subtrees, void *data)
 /* Insert all subblocks of BLOCK into BLOCKS and recurse.  */
 
 static void
-collect_subblocks (pointer_set_t *blocks, tree block)
+collect_subblocks (hash_set<tree> *blocks, tree block)
 {
   tree t;
   for (t = BLOCK_SUBBLOCKS (block); t; t = BLOCK_CHAIN (t))
     {
-      pointer_set_insert (blocks, t);
+      blocks->add (t);
       collect_subblocks (blocks, t);
     }
 }
@@ -4624,22 +4859,21 @@ collect_subblocks (pointer_set_t *blocks, tree block)
 /* Verify the GIMPLE statements in the CFG of FN.  */
 
 DEBUG_FUNCTION void
-verify_gimple_in_cfg (struct function *fn)
+verify_gimple_in_cfg (struct function *fn, bool verify_nothrow)
 {
   basic_block bb;
   bool err = false;
-  struct pointer_set_t *visited, *visited_stmts, *blocks;
 
   timevar_push (TV_TREE_STMT_VERIFY);
-  visited = pointer_set_create ();
-  visited_stmts = pointer_set_create ();
+  hash_set<void *> visited;
+  hash_set<gimple> visited_stmts;
 
   /* Collect all BLOCKs referenced by the BLOCK tree of FN.  */
-  blocks = pointer_set_create ();
+  hash_set<tree> blocks;
   if (DECL_INITIAL (fn->decl))
     {
-      pointer_set_insert (blocks, DECL_INITIAL (fn->decl));
-      collect_subblocks (blocks, DECL_INITIAL (fn->decl));
+      blocks.add (DECL_INITIAL (fn->decl));
+      collect_subblocks (&blocks, DECL_INITIAL (fn->decl));
     }
 
   FOR_EACH_BB_FN (bb, fn)
@@ -4652,7 +4886,7 @@ verify_gimple_in_cfg (struct function *fn)
          bool err2 = false;
          unsigned i;
 
-         pointer_set_insert (visited_stmts, phi);
+         visited_stmts.add (phi);
 
          if (gimple_bb (phi) != bb)
            {
@@ -4673,7 +4907,7 @@ verify_gimple_in_cfg (struct function *fn)
            {
              tree arg = gimple_phi_arg_def (phi, i);
              tree addr = walk_tree (&arg, verify_node_sharing_1,
-                                    visited, NULL);
+                                    &visited, NULL);
              if (addr)
                {
                  error ("incorrect sharing of tree nodes");
@@ -4687,13 +4921,13 @@ verify_gimple_in_cfg (struct function *fn)
                  error ("virtual PHI with argument locations");
                  err2 = true;
                }
-             addr = walk_tree (&arg, verify_expr_location_1, blocks, NULL);
+             addr = walk_tree (&arg, verify_expr_location_1, &blocks, NULL);
              if (addr)
                {
                  debug_generic_expr (addr);
                  err2 = true;
                }
-             err2 |= verify_location (blocks, loc);
+             err2 |= verify_location (&blocks, loc);
            }
 
          if (err2)
@@ -4709,7 +4943,7 @@ verify_gimple_in_cfg (struct function *fn)
          tree addr;
          int lp_nr;
 
-         pointer_set_insert (visited_stmts, stmt);
+         visited_stmts.add (stmt);
 
          if (gimple_bb (stmt) != bb)
            {
@@ -4718,10 +4952,10 @@ verify_gimple_in_cfg (struct function *fn)
            }
 
          err2 |= verify_gimple_stmt (stmt);
-         err2 |= verify_location (blocks, gimple_location (stmt));
+         err2 |= verify_location (&blocks, gimple_location (stmt));
 
          memset (&wi, 0, sizeof (wi));
-         wi.info = (void *) visited;
+         wi.info = (void *) &visited;
          addr = walk_gimple_op (stmt, verify_node_sharing, &wi);
          if (addr)
            {
@@ -4731,7 +4965,7 @@ verify_gimple_in_cfg (struct function *fn)
            }
 
          memset (&wi, 0, sizeof (wi));
-         wi.info = (void *) blocks;
+         wi.info = (void *) &blocks;
          addr = walk_gimple_op (stmt, verify_expr_location, &wi);
          if (addr)
            {
@@ -4760,16 +4994,17 @@ verify_gimple_in_cfg (struct function *fn)
             that they cannot throw, that we update other data structures
             to match.  */
          lp_nr = lookup_stmt_eh_lp (stmt);
-         if (lp_nr != 0)
+         if (lp_nr > 0)
            {
              if (!stmt_could_throw_p (stmt))
                {
-                 error ("statement marked for throw, but doesn%'t");
-                 err2 |= true;
+                 if (verify_nothrow)
+                   {
+                     error ("statement marked for throw, but doesn%'t");
+                     err2 |= true;
+                   }
                }
-             else if (lp_nr > 0
-                      && !gsi_one_before_end_p (gsi)
-                      && stmt_can_throw_internal (stmt))
+             else if (!gsi_one_before_end_p (gsi))
                {
                  error ("statement marked for throw in middle of block");
                  err2 |= true;
@@ -4783,17 +5018,14 @@ verify_gimple_in_cfg (struct function *fn)
     }
 
   eh_error_found = false;
-  if (get_eh_throw_stmt_table (cfun))
-    htab_traverse (get_eh_throw_stmt_table (cfun),
-                  verify_eh_throw_stmt_node,
-                  visited_stmts);
+  hash_map<gimple, int> *eh_table = get_eh_throw_stmt_table (cfun);
+  if (eh_table)
+    eh_table->traverse<hash_set<gimple> *, verify_eh_throw_stmt_node>
+      (&visited_stmts);
 
   if (err || eh_error_found)
     internal_error ("verify_gimple failed");
 
-  pointer_set_destroy (visited);
-  pointer_set_destroy (visited_stmts);
-  pointer_set_destroy (blocks);
   verify_histograms ();
   timevar_pop (TV_TREE_STMT_VERIFY);
 }
@@ -4811,26 +5043,28 @@ gimple_verify_flow_info (void)
   edge e;
   edge_iterator ei;
 
-  if (ENTRY_BLOCK_PTR->il.gimple.seq || ENTRY_BLOCK_PTR->il.gimple.phi_nodes)
+  if (ENTRY_BLOCK_PTR_FOR_FN (cfun)->il.gimple.seq
+      || ENTRY_BLOCK_PTR_FOR_FN (cfun)->il.gimple.phi_nodes)
     {
       error ("ENTRY_BLOCK has IL associated with it");
       err = 1;
     }
 
-  if (EXIT_BLOCK_PTR->il.gimple.seq || EXIT_BLOCK_PTR->il.gimple.phi_nodes)
+  if (EXIT_BLOCK_PTR_FOR_FN (cfun)->il.gimple.seq
+      || EXIT_BLOCK_PTR_FOR_FN (cfun)->il.gimple.phi_nodes)
     {
       error ("EXIT_BLOCK has IL associated with it");
       err = 1;
     }
 
-  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
+  FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (cfun)->preds)
     if (e->flags & EDGE_FALLTHRU)
       {
        error ("fallthru to exit from bb %d", e->src->index);
        err = 1;
       }
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       bool found_ctrl_stmt = false;
 
@@ -5003,7 +5237,7 @@ gimple_verify_flow_info (void)
              error ("wrong outgoing edge flags at end of bb %d", bb->index);
              err = 1;
            }
-         if (single_succ (bb) != EXIT_BLOCK_PTR)
+         if (single_succ (bb) != EXIT_BLOCK_PTR_FOR_FN (cfun))
            {
              error ("return edge does not point to exit in bb %d",
                     bb->index);
@@ -5243,7 +5477,7 @@ gimple_redirect_edge_and_branch (edge e, basic_block dest)
   if (e->flags & EDGE_EH)
     return redirect_eh_edge (e, dest);
 
-  if (e->src != ENTRY_BLOCK_PTR)
+  if (e->src != ENTRY_BLOCK_PTR_FOR_FN (cfun))
     {
       ret = gimple_try_redirect_by_replacing_jump (e, dest);
       if (ret)
@@ -5472,7 +5706,7 @@ gimple_move_block_after (basic_block bb, basic_block after)
 /* Return TRUE if block BB has no executable statements, otherwise return
    FALSE.  */
 
-bool
+static bool
 gimple_empty_block_p (basic_block bb)
 {
   /* BB must have no executable statements.  */
@@ -5526,7 +5760,7 @@ gimple_duplicate_bb (basic_block bb)
   gimple_seq phis = phi_nodes (bb);
   gimple phi, stmt, copy;
 
-  new_bb = create_empty_bb (EXIT_BLOCK_PTR->prev_bb);
+  new_bb = create_empty_bb (EXIT_BLOCK_PTR_FOR_FN (cfun)->prev_bb);
 
   /* Copy the PHI nodes.  We ignore PHI node arguments here because
      the incoming edges have not been setup yet.  */
@@ -5536,6 +5770,7 @@ gimple_duplicate_bb (basic_block bb)
       copy = create_phi_node (NULL_TREE, new_bb);
       create_new_def_for (gimple_phi_result (phi), copy,
                          gimple_phi_result_ptr (copy));
+      gimple_set_uid (copy, gimple_uid (phi));
     }
 
   gsi_tgt = gsi_start_bb (new_bb);
@@ -5728,14 +5963,11 @@ gimple_duplicate_sese_region (edge entry, edge exit,
        return false;
     }
 
-  set_loop_copy (loop, loop);
-
   /* In case the function is used for loop header copying (which is the primary
      use), ensure that EXIT and its copy will be new latch and entry edges.  */
   if (loop->header == entry->dest)
     {
       copying_header = true;
-      set_loop_copy (loop, loop_outer (loop));
 
       if (!dominated_by_p (CDI_DOMINATORS, loop->latch, exit->src))
        return false;
@@ -5746,14 +5978,19 @@ gimple_duplicate_sese_region (edge entry, edge exit,
          return false;
     }
 
+  initialize_original_copy_tables ();
+
+  if (copying_header)
+    set_loop_copy (loop, loop_outer (loop));
+  else
+    set_loop_copy (loop, loop);
+
   if (!region_copy)
     {
       region_copy = XNEWVEC (basic_block, n_region);
       free_region_copy = true;
     }
 
-  initialize_original_copy_tables ();
-
   /* Record blocks outside the region that are dominated by something
      inside.  */
   if (update_dominance)
@@ -6061,22 +6298,20 @@ gather_blocks_in_sese_region (basic_block entry, basic_block exit,
    The duplicates are recorded in VARS_MAP.  */
 
 static void
-replace_by_duplicate_decl (tree *tp, struct pointer_map_t *vars_map,
+replace_by_duplicate_decl (tree *tp, hash_map<tree, tree> *vars_map,
                           tree to_context)
 {
   tree t = *tp, new_t;
   struct function *f = DECL_STRUCT_FUNCTION (to_context);
-  void **loc;
 
   if (DECL_CONTEXT (t) == to_context)
     return;
 
-  loc = pointer_map_contains (vars_map, t);
+  bool existed;
+  tree &loc = vars_map->get_or_insert (t, &existed);
 
-  if (!loc)
+  if (!existed)
     {
-      loc = pointer_map_insert (vars_map, t);
-
       if (SSA_VAR_P (t))
        {
          new_t = copy_var_decl (t, DECL_NAME (t), TREE_TYPE (t));
@@ -6089,10 +6324,10 @@ replace_by_duplicate_decl (tree *tp, struct pointer_map_t *vars_map,
        }
       DECL_CONTEXT (new_t) = to_context;
 
-      *loc = new_t;
+      loc = new_t;
     }
   else
-    new_t = (tree) *loc;
+    new_t = loc;
 
   *tp = new_t;
 }
@@ -6102,15 +6337,14 @@ replace_by_duplicate_decl (tree *tp, struct pointer_map_t *vars_map,
    VARS_MAP maps old ssa names and var_decls to the new ones.  */
 
 static tree
-replace_ssa_name (tree name, struct pointer_map_t *vars_map,
+replace_ssa_name (tree name, hash_map<tree, tree> *vars_map,
                  tree to_context)
 {
-  void **loc;
   tree new_name;
 
   gcc_assert (!virtual_operand_p (name));
 
-  loc = pointer_map_contains (vars_map, name);
+  tree *loc = vars_map->get (name);
 
   if (!loc)
     {
@@ -6128,11 +6362,10 @@ replace_ssa_name (tree name, struct pointer_map_t *vars_map,
        new_name = copy_ssa_name_fn (DECL_STRUCT_FUNCTION (to_context),
                                     name, SSA_NAME_DEF_STMT (name));
 
-      loc = pointer_map_insert (vars_map, name);
-      *loc = new_name;
+      vars_map->put (name, new_name);
     }
   else
-    new_name = (tree) *loc;
+    new_name = *loc;
 
   return new_name;
 }
@@ -6143,9 +6376,9 @@ struct move_stmt_d
   tree new_block;
   tree from_context;
   tree to_context;
-  struct pointer_map_t *vars_map;
+  hash_map<tree, tree> *vars_map;
   htab_t new_label_map;
-  struct pointer_map_t *eh_map;
+  hash_map<void *, void *> *eh_map;
   bool remap_decls_p;
 };
 
@@ -6222,11 +6455,9 @@ static int
 move_stmt_eh_region_nr (int old_nr, struct move_stmt_d *p)
 {
   eh_region old_r, new_r;
-  void **slot;
 
   old_r = get_eh_region_from_number (old_nr);
-  slot = pointer_map_contains (p->eh_map, old_r);
-  new_r = (eh_region) *slot;
+  new_r = static_cast<eh_region> (*p->eh_map->get (old_r));
 
   return new_r->index;
 }
@@ -6238,7 +6469,7 @@ move_stmt_eh_region_tree_nr (tree old_t_nr, struct move_stmt_d *p)
 {
   int old_nr, new_nr;
 
-  old_nr = tree_low_cst (old_t_nr, 0);
+  old_nr = tree_to_shwi (old_t_nr);
   new_nr = move_stmt_eh_region_nr (old_nr, p);
 
   return build_int_cst (integer_type_node, new_nr);
@@ -6481,7 +6712,7 @@ move_block_to_fn (struct function *dest_cfun, basic_block bb,
 
       /* We cannot leave any operands allocated from the operand caches of
         the current function.  */
-      free_stmt_operands (stmt);
+      free_stmt_operands (cfun, stmt);
       push_cfun (dest_cfun);
       update_stmt (stmt);
       pop_cfun ();
@@ -6560,7 +6791,7 @@ new_label_mapper (tree decl, void *data)
    subblocks.  */
 
 static void
-replace_block_vars_by_duplicates (tree block, struct pointer_map_t *vars_map,
+replace_block_vars_by_duplicates (tree block, hash_map<tree, tree> *vars_map,
                                  tree to_context)
 {
   tree *tp, t;
@@ -6634,12 +6865,13 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
   struct function *saved_cfun = cfun;
   int *entry_flag, *exit_flag;
   unsigned *entry_prob, *exit_prob;
-  unsigned i, num_entry_edges, num_exit_edges;
+  unsigned i, num_entry_edges, num_exit_edges, num_nodes;
   edge e;
   edge_iterator ei;
   htab_t new_label_map;
-  struct pointer_map_t *vars_map, *eh_map;
+  hash_map<void *, void *> *eh_map;
   struct loop *loop = entry_bb->loop_father;
+  struct loop *loop0 = get_loop (saved_cfun, 0);
   struct move_stmt_d d;
 
   /* If ENTRY does not strictly dominate EXIT, this cannot be an SESE
@@ -6726,32 +6958,46 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
     }
 
   /* Initialize an empty loop tree.  */
-  struct loops *loops = ggc_alloc_cleared_loops ();
+  struct loops *loops = ggc_cleared_alloc<struct loops> ();
   init_loops_structure (dest_cfun, loops, 1);
   loops->state = LOOPS_MAY_HAVE_MULTIPLE_LATCHES;
   set_loops_for_fn (dest_cfun, loops);
 
   /* Move the outlined loop tree part.  */
+  num_nodes = bbs.length ();
   FOR_EACH_VEC_ELT (bbs, i, bb)
     {
-      if (bb->loop_father->header == bb
-         && loop_outer (bb->loop_father) == loop)
+      if (bb->loop_father->header == bb)
        {
-         struct loop *loop = bb->loop_father;
-         flow_loop_tree_node_remove (bb->loop_father);
-         flow_loop_tree_node_add (get_loop (dest_cfun, 0), loop);
-         fixup_loop_arrays_after_move (saved_cfun, cfun, loop);
+         struct loop *this_loop = bb->loop_father;
+         struct loop *outer = loop_outer (this_loop);
+         if (outer == loop
+             /* If the SESE region contains some bbs ending with
+                a noreturn call, those are considered to belong
+                to the outermost loop in saved_cfun, rather than
+                the entry_bb's loop_father.  */
+             || outer == loop0)
+           {
+             if (outer != loop)
+               num_nodes -= this_loop->num_nodes;
+             flow_loop_tree_node_remove (bb->loop_father);
+             flow_loop_tree_node_add (get_loop (dest_cfun, 0), this_loop);
+             fixup_loop_arrays_after_move (saved_cfun, cfun, this_loop);
+           }
        }
+      else if (bb->loop_father == loop0 && loop0 != loop)
+       num_nodes--;
 
       /* Remove loop exits from the outlined region.  */
       if (loops_for_fn (saved_cfun)->exits)
        FOR_EACH_EDGE (e, ei, bb->succs)
          {
-           void **slot = htab_find_slot_with_hash
-               (loops_for_fn (saved_cfun)->exits, e,
-                htab_hash_pointer (e), NO_INSERT);
+           struct loops *l = loops_for_fn (saved_cfun);
+           loop_exit **slot
+             = l->exits->find_slot_with_hash (e, htab_hash_pointer (e),
+                                              NO_INSERT);
            if (slot)
-             htab_clear_slot (loops_for_fn (saved_cfun)->exits, slot);
+             l->exits->clear_slot (slot);
          }
     }
 
@@ -6761,20 +7007,21 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
 
   /* Setup a mapping to be used by move_block_to_fn.  */
   loop->aux = current_loops->tree_root;
+  loop0->aux = current_loops->tree_root;
 
   pop_cfun ();
 
   /* Move blocks from BBS into DEST_CFUN.  */
   gcc_assert (bbs.length () >= 2);
   after = dest_cfun->cfg->x_entry_block_ptr;
-  vars_map = pointer_map_create ();
+  hash_map<tree, tree> vars_map;
 
   memset (&d, 0, sizeof (d));
   d.orig_block = orig_block;
   d.new_block = DECL_INITIAL (dest_cfun->decl);
   d.from_context = cfun->decl;
   d.to_context = dest_cfun->decl;
-  d.vars_map = vars_map;
+  d.vars_map = &vars_map;
   d.new_label_map = new_label_map;
   d.eh_map = eh_map;
   d.remap_decls_p = true;
@@ -6789,11 +7036,30 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
     }
 
   loop->aux = NULL;
+  loop0->aux = NULL;
   /* Loop sizes are no longer correct, fix them up.  */
-  loop->num_nodes -= bbs.length ();
+  loop->num_nodes -= num_nodes;
   for (struct loop *outer = loop_outer (loop);
        outer; outer = loop_outer (outer))
-    outer->num_nodes -= bbs.length ();
+    outer->num_nodes -= num_nodes;
+  loop0->num_nodes -= bbs.length () - num_nodes;
+
+  if (saved_cfun->has_simduid_loops || saved_cfun->has_force_vectorize_loops)
+    {
+      struct loop *aloop;
+      for (i = 0; vec_safe_iterate (loops->larray, i, &aloop); i++)
+       if (aloop != NULL)
+         {
+           if (aloop->simduid)
+             {
+               replace_by_duplicate_decl (&aloop->simduid, d.vars_map,
+                                          d.to_context);
+               dest_cfun->has_simduid_loops = true;
+             }
+           if (aloop->force_vectorize)
+             dest_cfun->has_force_vectorize_loops = true;
+         }
+    }
 
   /* Rewire BLOCK_SUBBLOCKS of orig_block.  */
   if (orig_block)
@@ -6810,13 +7076,12 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
     }
 
   replace_block_vars_by_duplicates (DECL_INITIAL (dest_cfun->decl),
-                                   vars_map, dest_cfun->decl);
+                                   &vars_map, dest_cfun->decl);
 
   if (new_label_map)
     htab_delete (new_label_map);
   if (eh_map)
-    pointer_map_destroy (eh_map);
-  pointer_map_destroy (vars_map);
+    delete eh_map;
 
   /* Rewire the entry and exit blocks.  The successor to the entry
      block turns into the successor of DEST_FN's ENTRY_BLOCK_PTR in
@@ -6828,9 +7093,9 @@ move_sese_region_to_fn (struct function *dest_cfun, basic_block entry_bb,
      FIXME, this is silly.  The CFG ought to become a parameter to
      these helpers.  */
   push_cfun (dest_cfun);
-  make_edge (ENTRY_BLOCK_PTR, entry_bb, EDGE_FALLTHRU);
+  make_edge (ENTRY_BLOCK_PTR_FOR_FN (cfun), entry_bb, EDGE_FALLTHRU);
   if (exit_bb)
-    make_edge (exit_bb,  EXIT_BLOCK_PTR, 0);
+    make_edge (exit_bb,  EXIT_BLOCK_PTR_FOR_FN (cfun), 0);
   pop_cfun ();
 
   /* Back in the original function, the SESE region has disappeared,
@@ -6953,13 +7218,13 @@ dump_function_to_file (tree fndecl, FILE *file, int flags)
 
   if (fun && fun->decl == fndecl
       && fun->cfg
-      && basic_block_info_for_function (fun))
+      && basic_block_info_for_fn (fun))
     {
       /* If the CFG has been built, emit a CFG-based dump.  */
       if (!ignore_topmost_bind)
        fprintf (file, "{\n");
 
-      if (any_var && n_basic_blocks_for_function (fun))
+      if (any_var && n_basic_blocks_for_fn (fun))
        fprintf (file, "\n");
 
       FOR_EACH_BB_FN (bb, fun)
@@ -7128,13 +7393,13 @@ print_loop (FILE *file, struct loop *loop, int indent, int verbosity)
   if (loop->any_upper_bound)
     {
       fprintf (file, ", upper_bound = ");
-      dump_double_int (file, loop->nb_iterations_upper_bound, true);
+      print_decu (loop->nb_iterations_upper_bound, file);
     }
 
   if (loop->any_estimate)
     {
       fprintf (file, ", estimate = ");
-      dump_double_int (file, loop->nb_iterations_estimate, true);
+      print_decu (loop->nb_iterations_estimate, file);
     }
   fprintf (file, ")\n");
 
@@ -7142,7 +7407,7 @@ print_loop (FILE *file, struct loop *loop, int indent, int verbosity)
   if (verbosity >= 1)
     {
       fprintf (file, "%s{\n", s_indent);
-      FOR_EACH_BB (bb)
+      FOR_EACH_BB_FN (bb, cfun)
        if (bb->loop_father == loop)
          print_loops_bb (file, bb, indent, verbosity);
 
@@ -7174,7 +7439,7 @@ print_loops (FILE *file, int verbosity)
 {
   basic_block bb;
 
-  bb = ENTRY_BLOCK_PTR;
+  bb = ENTRY_BLOCK_PTR_FOR_FN (cfun);
   if (bb && bb->loop_father)
     print_loop_and_siblings (file, bb->loop_father, 0, verbosity);
 }
@@ -7334,16 +7599,17 @@ gimple_flow_call_edges_add (sbitmap blocks)
 {
   int i;
   int blocks_split = 0;
-  int last_bb = last_basic_block;
+  int last_bb = last_basic_block_for_fn (cfun);
   bool check_last_block = false;
 
-  if (n_basic_blocks == NUM_FIXED_BLOCKS)
+  if (n_basic_blocks_for_fn (cfun) == NUM_FIXED_BLOCKS)
     return 0;
 
   if (! blocks)
     check_last_block = true;
   else
-    check_last_block = bitmap_bit_p (blocks, EXIT_BLOCK_PTR->prev_bb->index);
+    check_last_block = bitmap_bit_p (blocks,
+                                    EXIT_BLOCK_PTR_FOR_FN (cfun)->prev_bb->index);
 
   /* In the last basic block, before epilogue generation, there will be
      a fallthru edge to EXIT.  Special care is required if the last insn
@@ -7359,7 +7625,7 @@ gimple_flow_call_edges_add (sbitmap blocks)
      Handle this by adding a dummy instruction in a new last basic block.  */
   if (check_last_block)
     {
-      basic_block bb = EXIT_BLOCK_PTR->prev_bb;
+      basic_block bb = EXIT_BLOCK_PTR_FOR_FN (cfun)->prev_bb;
       gimple_stmt_iterator gsi = gsi_last_nondebug_bb (bb);
       gimple t = NULL;
 
@@ -7370,7 +7636,7 @@ gimple_flow_call_edges_add (sbitmap blocks)
        {
          edge e;
 
-         e = find_edge (bb, EXIT_BLOCK_PTR);
+         e = find_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun));
          if (e)
            {
              gsi_insert_on_edge (e, gimple_build_nop ());
@@ -7384,7 +7650,7 @@ gimple_flow_call_edges_add (sbitmap blocks)
      return or not...  */
   for (i = 0; i < last_bb; i++)
     {
-      basic_block bb = BASIC_BLOCK (i);
+      basic_block bb = BASIC_BLOCK_FOR_FN (cfun, i);
       gimple_stmt_iterator gsi;
       gimple stmt, last_stmt;
 
@@ -7413,7 +7679,7 @@ gimple_flow_call_edges_add (sbitmap blocks)
 #ifdef ENABLE_CHECKING
                  if (stmt == last_stmt)
                    {
-                     e = find_edge (bb, EXIT_BLOCK_PTR);
+                     e = find_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun));
                      gcc_assert (e == NULL);
                    }
 #endif
@@ -7426,7 +7692,7 @@ gimple_flow_call_edges_add (sbitmap blocks)
                      if (e)
                        blocks_split++;
                    }
-                 make_edge (bb, EXIT_BLOCK_PTR, EDGE_FAKE);
+                 make_edge (bb, EXIT_BLOCK_PTR_FOR_FN (cfun), EDGE_FAKE);
                }
              gsi_prev (&gsi);
            }
@@ -7464,7 +7730,7 @@ remove_edge_and_dominated_blocks (edge e)
     }
 
   /* No updating is needed for edges to exit.  */
-  if (e->dest == EXIT_BLOCK_PTR)
+  if (e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun))
     {
       if (cfgcleanup_altered_bbs)
        bitmap_set_bit (cfgcleanup_altered_bbs, e->src->index);
@@ -7504,7 +7770,7 @@ remove_edge_and_dominated_blocks (edge e)
        {
          FOR_EACH_EDGE (f, ei, bb->succs)
            {
-             if (f->dest != EXIT_BLOCK_PTR)
+             if (f->dest != EXIT_BLOCK_PTR_FOR_FN (cfun))
                bitmap_set_bit (df, f->dest->index);
            }
        }
@@ -7513,7 +7779,7 @@ remove_edge_and_dominated_blocks (edge e)
 
       EXECUTE_IF_SET_IN_BITMAP (df, 0, i, bi)
        {
-         bb = BASIC_BLOCK (i);
+         bb = BASIC_BLOCK_FOR_FN (cfun, i);
          bitmap_set_bit (df_idom,
                          get_immediate_dominator (CDI_DOMINATORS, bb)->index);
        }
@@ -7551,7 +7817,7 @@ remove_edge_and_dominated_blocks (edge e)
      the dominance frontier of E.  Therefore, Y belongs to DF_IDOM.  */
   EXECUTE_IF_SET_IN_BITMAP (df_idom, 0, i, bi)
     {
-      bb = BASIC_BLOCK (i);
+      bb = BASIC_BLOCK_FOR_FN (cfun, i);
       for (dbb = first_dom_son (CDI_DOMINATORS, bb);
           dbb;
           dbb = next_dom_son (CDI_DOMINATORS, dbb))
@@ -7604,7 +7870,7 @@ gimple_purge_all_dead_eh_edges (const_bitmap blocks)
 
   EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i, bi)
     {
-      basic_block bb = BASIC_BLOCK (i);
+      basic_block bb = BASIC_BLOCK_FOR_FN (cfun, i);
 
       /* Earlier gimple_purge_dead_eh_edges could have removed
         this basic block already.  */
@@ -7661,7 +7927,7 @@ gimple_purge_all_dead_abnormal_call_edges (const_bitmap blocks)
 
   EXECUTE_IF_SET_IN_BITMAP (blocks, 0, i, bi)
     {
-      basic_block bb = BASIC_BLOCK (i);
+      basic_block bb = BASIC_BLOCK_FOR_FN (cfun, i);
 
       /* Earlier gimple_purge_dead_abnormal_call_edges could have removed
         this basic block already.  */
@@ -7778,11 +8044,11 @@ gimple_account_profile_record (basic_block bb, int after_pass,
     {
       record->size[after_pass]
        += estimate_num_insns (gsi_stmt (i), &eni_size_weights);
-      if (profile_status == PROFILE_READ)
+      if (profile_status_for_fn (cfun) == PROFILE_READ)
        record->time[after_pass]
          += estimate_num_insns (gsi_stmt (i),
                                 &eni_time_weights) * bb->count;
-      else if (profile_status == PROFILE_GUESSED)
+      else if (profile_status_for_fn (cfun) == PROFILE_GUESSED)
        record->time[after_pass]
          += estimate_num_insns (gsi_stmt (i),
                                 &eni_time_weights) * bb->frequency;
@@ -7829,7 +8095,7 @@ struct cfg_hooks gimple_cfg_hooks = {
 
 /* Split all critical edges.  */
 
-static unsigned int
+unsigned int
 split_critical_edges (void)
 {
   basic_block bb;
@@ -7840,7 +8106,7 @@ split_critical_edges (void)
      expensive.  So we want to enable recording of edge to CASE_LABEL_EXPR
      mappings around the calls to split_edge.  */
   start_recording_case_labels ();
-  FOR_ALL_BB (bb)
+  FOR_ALL_BB_FN (bb, cfun)
     {
       FOR_EACH_EDGE (e, ei, bb->succs)
         {
@@ -7855,8 +8121,8 @@ split_critical_edges (void)
             gimple_find_edge_insert_loc.  */
          else if ((!single_pred_p (e->dest)
                    || !gimple_seq_empty_p (phi_nodes (e->dest))
-                   || e->dest == EXIT_BLOCK_PTR)
-                  && e->src != ENTRY_BLOCK_PTR
+                   || e->dest == EXIT_BLOCK_PTR_FOR_FN (cfun))
+                  && e->src != ENTRY_BLOCK_PTR_FOR_FN (cfun)
                   && !(e->flags & EDGE_ABNORMAL))
            {
              gimple_stmt_iterator gsi;
@@ -7875,26 +8141,82 @@ split_critical_edges (void)
   return 0;
 }
 
-struct gimple_opt_pass pass_split_crit_edges =
-{
- {
-  GIMPLE_PASS,
-  "crited",                          /* name */
-  OPTGROUP_NONE,                 /* optinfo_flags */
-  NULL,                          /* gate */
-  split_critical_edges,          /* execute */
-  NULL,                          /* sub */
-  NULL,                          /* next */
-  0,                             /* static_pass_number */
-  TV_TREE_SPLIT_EDGES,           /* tv_id */
-  PROP_cfg,                      /* properties required */
-  PROP_no_crit_edges,            /* properties_provided */
-  0,                             /* properties_destroyed */
-  0,                             /* todo_flags_start */
-  TODO_verify_flow               /* todo_flags_finish */
- }
+namespace {
+
+const pass_data pass_data_split_crit_edges =
+{
+  GIMPLE_PASS, /* type */
+  "crited", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_SPLIT_EDGES, /* tv_id */
+  PROP_cfg, /* properties_required */
+  PROP_no_crit_edges, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
 };
 
+class pass_split_crit_edges : public gimple_opt_pass
+{
+public:
+  pass_split_crit_edges (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_split_crit_edges, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual unsigned int execute (function *) { return split_critical_edges (); }
+
+  opt_pass * clone () { return new pass_split_crit_edges (m_ctxt); }
+}; // class pass_split_crit_edges
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_split_crit_edges (gcc::context *ctxt)
+{
+  return new pass_split_crit_edges (ctxt);
+}
+
+
+/* Insert COND expression which is GIMPLE_COND after STMT
+   in basic block BB with appropriate basic block split
+   and creation of a new conditionally executed basic block.
+   Return created basic block.  */
+basic_block
+insert_cond_bb (basic_block bb, gimple stmt, gimple cond)
+{
+  edge fall = split_block (bb, stmt);
+  gimple_stmt_iterator iter = gsi_last_bb (bb);
+  basic_block new_bb;
+
+  /* Insert cond statement.  */
+  gcc_assert (gimple_code (cond) == GIMPLE_COND);
+  if (gsi_end_p (iter))
+    gsi_insert_before (&iter, cond, GSI_CONTINUE_LINKING);
+  else
+    gsi_insert_after (&iter, cond, GSI_CONTINUE_LINKING);
+
+  /* Create conditionally executed block.  */
+  new_bb = create_empty_bb (bb);
+  make_edge (bb, new_bb, EDGE_TRUE_VALUE);
+  make_single_succ_edge (new_bb, fall->dest, EDGE_FALLTHRU);
+
+  /* Fix edge for split bb.  */
+  fall->flags = EDGE_FALSE_VALUE;
+
+  /* Update dominance info.  */
+  if (dom_info_available_p (CDI_DOMINATORS))
+    {
+      set_immediate_dominator (CDI_DOMINATORS, new_bb, bb);
+      set_immediate_dominator (CDI_DOMINATORS, fall->dest, bb);
+    }
+
+  /* Update loop info.  */
+  if (current_loops)
+    add_bb_to_loop (new_bb, bb->loop_father);
+
+  return new_bb;
+}
 
 /* Build a ternary operation and gimplify it.  Emit code before GSI.
    Return the gimple_val holding the result.  */
@@ -7947,25 +8269,76 @@ gimplify_build1 (gimple_stmt_iterator *gsi, enum tree_code code, tree type,
 
 
 \f
+/* Given a basic block B which ends with a conditional and has
+   precisely two successors, determine which of the edges is taken if
+   the conditional is true and which is taken if the conditional is
+   false.  Set TRUE_EDGE and FALSE_EDGE appropriately.  */
+
+void
+extract_true_false_edges_from_block (basic_block b,
+                                    edge *true_edge,
+                                    edge *false_edge)
+{
+  edge e = EDGE_SUCC (b, 0);
+
+  if (e->flags & EDGE_TRUE_VALUE)
+    {
+      *true_edge = e;
+      *false_edge = EDGE_SUCC (b, 1);
+    }
+  else
+    {
+      *false_edge = e;
+      *true_edge = EDGE_SUCC (b, 1);
+    }
+}
+
 /* Emit return warnings.  */
 
-static unsigned int
-execute_warn_function_return (void)
+namespace {
+
+const pass_data pass_data_warn_function_return =
+{
+  GIMPLE_PASS, /* type */
+  "*warn_function_return", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_cfg, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_warn_function_return : public gimple_opt_pass
+{
+public:
+  pass_warn_function_return (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_warn_function_return, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual unsigned int execute (function *);
+
+}; // class pass_warn_function_return
+
+unsigned int
+pass_warn_function_return::execute (function *fun)
 {
   source_location location;
   gimple last;
   edge e;
   edge_iterator ei;
 
-  if (!targetm.warn_func_return (cfun->decl))
+  if (!targetm.warn_func_return (fun->decl))
     return 0;
 
   /* If we have a path to EXIT, then we do return.  */
-  if (TREE_THIS_VOLATILE (cfun->decl)
-      && EDGE_COUNT (EXIT_BLOCK_PTR->preds) > 0)
+  if (TREE_THIS_VOLATILE (fun->decl)
+      && EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (fun)->preds) > 0)
     {
       location = UNKNOWN_LOCATION;
-      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
+      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (fun)->preds)
        {
          last = last_stmt (e->src);
          if ((gimple_code (last) == GIMPLE_RETURN
@@ -7981,11 +8354,11 @@ execute_warn_function_return (void)
   /* If we see "return;" in some basic block, then we do reach the end
      without returning a value.  */
   else if (warn_return_type
-          && !TREE_NO_WARNING (cfun->decl)
-          && EDGE_COUNT (EXIT_BLOCK_PTR->preds) > 0
-          && !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (cfun->decl))))
+          && !TREE_NO_WARNING (fun->decl)
+          && EDGE_COUNT (EXIT_BLOCK_PTR_FOR_FN (fun)->preds) > 0
+          && !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (fun->decl))))
     {
-      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
+      FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR_FOR_FN (fun)->preds)
        {
          gimple last = last_stmt (e->src);
          if (gimple_code (last) == GIMPLE_RETURN
@@ -7994,9 +8367,9 @@ execute_warn_function_return (void)
            {
              location = gimple_location (last);
              if (location == UNKNOWN_LOCATION)
-                 location = cfun->function_end_locus;
+               location = fun->function_end_locus;
              warning_at (location, OPT_Wreturn_type, "control reaches end of non-void function");
-             TREE_NO_WARNING (cfun->decl) = 1;
+             TREE_NO_WARNING (fun->decl) = 1;
              break;
            }
        }
@@ -8004,89 +8377,14 @@ execute_warn_function_return (void)
   return 0;
 }
 
+} // anon namespace
 
-/* Given a basic block B which ends with a conditional and has
-   precisely two successors, determine which of the edges is taken if
-   the conditional is true and which is taken if the conditional is
-   false.  Set TRUE_EDGE and FALSE_EDGE appropriately.  */
-
-void
-extract_true_false_edges_from_block (basic_block b,
-                                    edge *true_edge,
-                                    edge *false_edge)
-{
-  edge e = EDGE_SUCC (b, 0);
-
-  if (e->flags & EDGE_TRUE_VALUE)
-    {
-      *true_edge = e;
-      *false_edge = EDGE_SUCC (b, 1);
-    }
-  else
-    {
-      *false_edge = e;
-      *true_edge = EDGE_SUCC (b, 1);
-    }
-}
-
-struct gimple_opt_pass pass_warn_function_return =
-{
- {
-  GIMPLE_PASS,
-  "*warn_function_return",             /* name */
-  OPTGROUP_NONE,                        /* optinfo_flags */
-  NULL,                                        /* gate */
-  execute_warn_function_return,                /* execute */
-  NULL,                                        /* sub */
-  NULL,                                        /* next */
-  0,                                   /* static_pass_number */
-  TV_NONE,                             /* tv_id */
-  PROP_cfg,                            /* properties_required */
-  0,                                   /* properties_provided */
-  0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  0                                    /* todo_flags_finish */
- }
-};
-
-/* Emit noreturn warnings.  */
-
-static unsigned int
-execute_warn_function_noreturn (void)
+gimple_opt_pass *
+make_pass_warn_function_return (gcc::context *ctxt)
 {
-  if (!TREE_THIS_VOLATILE (current_function_decl)
-      && EDGE_COUNT (EXIT_BLOCK_PTR->preds) == 0)
-    warn_function_noreturn (current_function_decl);
-  return 0;
+  return new pass_warn_function_return (ctxt);
 }
 
-static bool
-gate_warn_function_noreturn (void)
-{
-  return warn_suggest_attribute_noreturn;
-}
-
-struct gimple_opt_pass pass_warn_function_noreturn =
-{
- {
-  GIMPLE_PASS,
-  "*warn_function_noreturn",           /* name */
-  OPTGROUP_NONE,                        /* optinfo_flags */
-  gate_warn_function_noreturn,         /* gate */
-  execute_warn_function_noreturn,      /* execute */
-  NULL,                                        /* sub */
-  NULL,                                        /* next */
-  0,                                   /* static_pass_number */
-  TV_NONE,                             /* tv_id */
-  PROP_cfg,                            /* properties_required */
-  0,                                   /* properties_provided */
-  0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  0                                    /* todo_flags_finish */
- }
-};
-
-
 /* Walk a gimplified function and warn for functions whose return value is
    ignored and attribute((warn_unused_result)) is set.  This is done before
    inlining, so we don't have to worry about that.  */
@@ -8152,39 +8450,221 @@ do_warn_unused_result (gimple_seq seq)
     }
 }
 
-static unsigned int
-run_warn_unused_result (void)
+namespace {
+
+const pass_data pass_data_warn_unused_result =
 {
-  do_warn_unused_result (gimple_body (current_function_decl));
-  return 0;
+  GIMPLE_PASS, /* type */
+  "*warn_unused_result", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_gimple_any, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_warn_unused_result : public gimple_opt_pass
+{
+public:
+  pass_warn_unused_result (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_warn_unused_result, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *) { return flag_warn_unused_result; }
+  virtual unsigned int execute (function *)
+    {
+      do_warn_unused_result (gimple_body (current_function_decl));
+      return 0;
+    }
+
+}; // class pass_warn_unused_result
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_warn_unused_result (gcc::context *ctxt)
+{
+  return new pass_warn_unused_result (ctxt);
 }
 
-static bool
-gate_warn_unused_result (void)
-{
-  return flag_warn_unused_result;
-}
-
-struct gimple_opt_pass pass_warn_unused_result =
-{
-  {
-    GIMPLE_PASS,
-    "*warn_unused_result",             /* name */
-    OPTGROUP_NONE,                        /* optinfo_flags */
-    gate_warn_unused_result,           /* gate */
-    run_warn_unused_result,            /* execute */
-    NULL,                              /* sub */
-    NULL,                              /* next */
-    0,                                 /* static_pass_number */
-    TV_NONE,                           /* tv_id */
-    PROP_gimple_any,                   /* properties_required */
-    0,                                 /* properties_provided */
-    0,                                 /* properties_destroyed */
-    0,                                 /* todo_flags_start */
-    0,                                 /* todo_flags_finish */
-  }
+/* IPA passes, compilation of earlier functions or inlining
+   might have changed some properties, such as marked functions nothrow,
+   pure, const or noreturn.
+   Remove redundant edges and basic blocks, and create new ones if necessary.
+
+   This pass can't be executed as stand alone pass from pass manager, because
+   in between inlining and this fixup the verify_flow_info would fail.  */
+
+unsigned int
+execute_fixup_cfg (void)
+{
+  basic_block bb;
+  gimple_stmt_iterator gsi;
+  int todo = 0;
+  gcov_type count_scale;
+  edge e;
+  edge_iterator ei;
+
+  count_scale
+      = GCOV_COMPUTE_SCALE (cgraph_node::get (current_function_decl)->count,
+                           ENTRY_BLOCK_PTR_FOR_FN (cfun)->count);
+
+  ENTRY_BLOCK_PTR_FOR_FN (cfun)->count =
+                           cgraph_node::get (current_function_decl)->count;
+  EXIT_BLOCK_PTR_FOR_FN (cfun)->count =
+                           apply_scale (EXIT_BLOCK_PTR_FOR_FN (cfun)->count,
+                                       count_scale);
+
+  FOR_EACH_EDGE (e, ei, ENTRY_BLOCK_PTR_FOR_FN (cfun)->succs)
+    e->count = apply_scale (e->count, count_scale);
+
+  FOR_EACH_BB_FN (bb, cfun)
+    {
+      bb->count = apply_scale (bb->count, count_scale);
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi);)
+       {
+         gimple stmt = gsi_stmt (gsi);
+         tree decl = is_gimple_call (stmt)
+                     ? gimple_call_fndecl (stmt)
+                     : NULL;
+         if (decl)
+           {
+             int flags = gimple_call_flags (stmt);
+             if (flags & (ECF_CONST | ECF_PURE | ECF_LOOPING_CONST_OR_PURE))
+               {
+                 if (gimple_purge_dead_abnormal_call_edges (bb))
+                   todo |= TODO_cleanup_cfg;
+
+                 if (gimple_in_ssa_p (cfun))
+                   {
+                     todo |= TODO_update_ssa | TODO_cleanup_cfg;
+                     update_stmt (stmt);
+                   }
+               }
+
+             if (flags & ECF_NORETURN
+                 && fixup_noreturn_call (stmt))
+               todo |= TODO_cleanup_cfg;
+            }
+
+         /* Remove stores to variables we marked write-only.
+            Keep access when store has side effect, i.e. in case when source
+            is volatile.  */
+         if (gimple_store_p (stmt)
+             && !gimple_has_side_effects (stmt))
+           {
+             tree lhs = get_base_address (gimple_get_lhs (stmt));
+
+             if (TREE_CODE (lhs) == VAR_DECL
+                 && (TREE_STATIC (lhs) || DECL_EXTERNAL (lhs))
+                 && varpool_node::get (lhs)->writeonly)
+               {
+                 unlink_stmt_vdef (stmt);
+                 gsi_remove (&gsi, true);
+                 release_defs (stmt);
+                 todo |= TODO_update_ssa | TODO_cleanup_cfg;
+                 continue;
+               }
+           }
+         /* For calls we can simply remove LHS when it is known
+            to be write-only.  */
+         if (is_gimple_call (stmt)
+             && gimple_get_lhs (stmt))
+           {
+             tree lhs = get_base_address (gimple_get_lhs (stmt));
+
+             if (TREE_CODE (lhs) == VAR_DECL
+                 && (TREE_STATIC (lhs) || DECL_EXTERNAL (lhs))
+                 && varpool_node::get (lhs)->writeonly)
+               {
+                 gimple_call_set_lhs (stmt, NULL);
+                 update_stmt (stmt);
+                 todo |= TODO_update_ssa | TODO_cleanup_cfg;
+               }
+           }
+
+         if (maybe_clean_eh_stmt (stmt)
+             && gimple_purge_dead_eh_edges (bb))
+           todo |= TODO_cleanup_cfg;
+         gsi_next (&gsi);
+       }
+
+      FOR_EACH_EDGE (e, ei, bb->succs)
+        e->count = apply_scale (e->count, count_scale);
+
+      /* If we have a basic block with no successors that does not
+        end with a control statement or a noreturn call end it with
+        a call to __builtin_unreachable.  This situation can occur
+        when inlining a noreturn call that does in fact return.  */
+      if (EDGE_COUNT (bb->succs) == 0)
+       {
+         gimple stmt = last_stmt (bb);
+         if (!stmt
+             || (!is_ctrl_stmt (stmt)
+                 && (!is_gimple_call (stmt)
+                     || (gimple_call_flags (stmt) & ECF_NORETURN) == 0)))
+           {
+             if (stmt && is_gimple_call (stmt))
+               gimple_call_set_ctrl_altering (stmt, false);
+             stmt = gimple_build_call
+                 (builtin_decl_implicit (BUILT_IN_UNREACHABLE), 0);
+             gimple_stmt_iterator gsi = gsi_last_bb (bb);
+             gsi_insert_after (&gsi, stmt, GSI_NEW_STMT);
+           }
+       }
+    }
+  if (count_scale != REG_BR_PROB_BASE)
+    compute_function_frequency ();
+
+  /* Dump a textual representation of the flowgraph.  */
+  if (dump_file)
+    gimple_dump_cfg (dump_file, dump_flags);
+
+  if (current_loops
+      && (todo & TODO_cleanup_cfg))
+    loops_state_set (LOOPS_NEED_FIXUP);
+
+  return todo;
+}
+
+namespace {
+
+const pass_data pass_data_fixup_cfg =
+{
+  GIMPLE_PASS, /* type */
+  "*free_cfg_annotations", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  PROP_cfg, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
 };
 
+class pass_fixup_cfg : public gimple_opt_pass
+{
+public:
+  pass_fixup_cfg (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_fixup_cfg, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_fixup_cfg (m_ctxt); }
+  virtual unsigned int execute (function *) { return execute_fixup_cfg (); }
+
+}; // class pass_fixup_cfg
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_fixup_cfg (gcc::context *ctxt)
+{
+  return new pass_fixup_cfg (ctxt);
+}
 
 /* Garbage collection support for edge_def.  */
 
@@ -8193,6 +8673,13 @@ extern void gt_ggc_mx (gimple&);
 extern void gt_ggc_mx (rtx&);
 extern void gt_ggc_mx (basic_block&);
 
+static void
+gt_ggc_mx (rtx_insn *& x)
+{
+  if (x)
+    gt_ggc_mx_rtx_def ((void *) x);
+}
+
 void
 gt_ggc_mx (edge_def *e)
 {
@@ -8213,6 +8700,13 @@ extern void gt_pch_nx (gimple&);
 extern void gt_pch_nx (rtx&);
 extern void gt_pch_nx (basic_block&);
 
+static void
+gt_pch_nx (rtx_insn *& x)
+{
+  if (x)
+    gt_pch_nx_rtx_def ((void *) x);
+}
+
 void
 gt_pch_nx (edge_def *e)
 {