re PR c++/66243 (enum class value is allowed to be initialized by value from other...
[gcc.git] / gcc / tree-eh.c
index 9220931ffff20a2bfcf071c2f3f5ccc5c295eb02..5d41cb423290ecf0db49c939f8c12f6191f1aa0b 100644 (file)
@@ -1,6 +1,5 @@
 /* Exception handling semantics and decomposition for trees.
-   Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
-   Free Software Foundation, Inc.
+   Copyright (C) 2003-2015 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -21,35 +20,74 @@ along with GCC; see the file COPYING3.  If not see
 #include "config.h"
 #include "system.h"
 #include "coretypes.h"
+#include "hash-table.h"
 #include "tm.h"
+#include "hash-set.h"
+#include "machmode.h"
+#include "vec.h"
+#include "double-int.h"
+#include "input.h"
+#include "alias.h"
+#include "symtab.h"
+#include "wide-int.h"
+#include "inchash.h"
 #include "tree.h"
-#include "flags.h"
+#include "fold-const.h"
+#include "hashtab.h"
+#include "hard-reg-set.h"
 #include "function.h"
+#include "rtl.h"
+#include "flags.h"
+#include "statistics.h"
+#include "real.h"
+#include "fixed-value.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
+#include "expr.h"
 #include "except.h"
-#include "pointer-set.h"
-#include "tree-flow.h"
+#include "predict.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "cfganal.h"
+#include "cfgcleanup.h"
+#include "basic-block.h"
+#include "tree-ssa-alias.h"
+#include "internal-fn.h"
+#include "tree-eh.h"
+#include "gimple-expr.h"
+#include "is-a.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "gimple-ssa.h"
+#include "hash-map.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-into-ssa.h"
+#include "tree-ssa.h"
 #include "tree-inline.h"
 #include "tree-pass.h"
 #include "langhooks.h"
-#include "ggc.h"
 #include "diagnostic-core.h"
-#include "gimple.h"
 #include "target.h"
 #include "cfgloop.h"
+#include "gimple-low.h"
 
 /* In some instances a tree and a gimple need to be stored in a same table,
    i.e. in hash tables. This is a structure to do this. */
 typedef union {tree *tp; tree t; gimple g;} treemple;
 
-/* Nonzero if we are using EH to handle cleanups.  */
-static int using_eh_for_cleanups_p = 0;
-
-void
-using_eh_for_cleanups (void)
-{
-  using_eh_for_cleanups_p = 1;
-}
-
 /* Misc functions used in this file.  */
 
 /* Remember and lookup EH landing pad data for arbitrary statements.
@@ -66,26 +104,15 @@ using_eh_for_cleanups (void)
 
 /* Add statement T in function IFUN to landing pad NUM.  */
 
-void
+static void
 add_stmt_to_eh_lp_fn (struct function *ifun, gimple t, int num)
 {
-  struct throw_stmt_node *n;
-  void **slot;
-
   gcc_assert (num != 0);
 
-  n = ggc_alloc_throw_stmt_node ();
-  n->stmt = t;
-  n->lp_nr = num;
-
   if (!get_eh_throw_stmt_table (ifun))
-    set_eh_throw_stmt_table (ifun, htab_create_ggc (31, struct_ptr_hash,
-                                                   struct_ptr_eq,
-                                                   ggc_free));
+    set_eh_throw_stmt_table (ifun, hash_map<gimple, int>::create_ggc (31));
 
-  slot = htab_find_slot (get_eh_throw_stmt_table (ifun), n, INSERT);
-  gcc_assert (!*slot);
-  *slot = n;
+  gcc_assert (!get_eh_throw_stmt_table (ifun)->put (t, num));
 }
 
 /* Add statement T in the current function (cfun) to EH landing pad NUM.  */
@@ -122,22 +149,14 @@ record_stmt_eh_region (eh_region region, gimple t)
 bool
 remove_stmt_from_eh_lp_fn (struct function *ifun, gimple t)
 {
-  struct throw_stmt_node dummy;
-  void **slot;
-
   if (!get_eh_throw_stmt_table (ifun))
     return false;
 
-  dummy.stmt = t;
-  slot = htab_find_slot (get_eh_throw_stmt_table (ifun), &dummy,
-                        NO_INSERT);
-  if (slot)
-    {
-      htab_clear_slot (get_eh_throw_stmt_table (ifun), slot);
-      return true;
-    }
-  else
+  if (!get_eh_throw_stmt_table (ifun)->get (t))
     return false;
+
+  get_eh_throw_stmt_table (ifun)->remove (t);
+      return true;
 }
 
 
@@ -158,14 +177,11 @@ remove_stmt_from_eh_lp (gimple t)
 int
 lookup_stmt_eh_lp_fn (struct function *ifun, gimple t)
 {
-  struct throw_stmt_node *p, n;
-
   if (ifun->eh->throw_stmt_table == NULL)
     return 0;
 
-  n.stmt = t;
-  p = (struct throw_stmt_node *) htab_find (ifun->eh->throw_stmt_table, &n);
-  return p ? p->lp_nr : 0;
+  int *lp_nr = ifun->eh->throw_stmt_table->get (t);
+  return lp_nr ? *lp_nr : 0;
 }
 
 /* Likewise, but always use the current function.  */
@@ -191,35 +207,59 @@ struct finally_tree_node
      tree) leaves the TRY block, its necessary to record a tree in
      this field.  Thus a treemple is used. */
   treemple child;
-  gimple parent;
+  gtry *parent;
+};
+
+/* Hashtable helpers.  */
+
+struct finally_tree_hasher : typed_free_remove <finally_tree_node>
+{
+  typedef finally_tree_node *value_type;
+  typedef finally_tree_node *compare_type;
+  static inline hashval_t hash (const finally_tree_node *);
+  static inline bool equal (const finally_tree_node *,
+                           const finally_tree_node *);
 };
 
+inline hashval_t
+finally_tree_hasher::hash (const finally_tree_node *v)
+{
+  return (intptr_t)v->child.t >> 4;
+}
+
+inline bool
+finally_tree_hasher::equal (const finally_tree_node *v,
+                           const finally_tree_node *c)
+{
+  return v->child.t == c->child.t;
+}
+
 /* Note that this table is *not* marked GTY.  It is short-lived.  */
-static htab_t finally_tree;
+static hash_table<finally_tree_hasher> *finally_tree;
 
 static void
-record_in_finally_tree (treemple child, gimple parent)
+record_in_finally_tree (treemple child, gtry *parent)
 {
   struct finally_tree_node *n;
-  void **slot;
+  finally_tree_node **slot;
 
   n = XNEW (struct finally_tree_node);
   n->child = child;
   n->parent = parent;
 
-  slot = htab_find_slot (finally_tree, n, INSERT);
+  slot = finally_tree->find_slot (n, INSERT);
   gcc_assert (!*slot);
   *slot = n;
 }
 
 static void
-collect_finally_tree (gimple stmt, gimple region);
+collect_finally_tree (gimple stmt, gtry *region);
 
 /* Go through the gimple sequence.  Works with collect_finally_tree to
    record all GIMPLE_LABEL and GIMPLE_TRY statements. */
 
 static void
-collect_finally_tree_1 (gimple_seq seq, gimple region)
+collect_finally_tree_1 (gimple_seq seq, gtry *region)
 {
   gimple_stmt_iterator gsi;
 
@@ -228,14 +268,14 @@ collect_finally_tree_1 (gimple_seq seq, gimple region)
 }
 
 static void
-collect_finally_tree (gimple stmt, gimple region)
+collect_finally_tree (gimple stmt, gtry *region)
 {
   treemple temp;
 
   switch (gimple_code (stmt))
     {
     case GIMPLE_LABEL:
-      temp.t = gimple_label_label (stmt);
+      temp.t = gimple_label_label (as_a <glabel *> (stmt));
       record_in_finally_tree (temp, region);
       break;
 
@@ -244,7 +284,8 @@ collect_finally_tree (gimple stmt, gimple region)
         {
           temp.g = stmt;
           record_in_finally_tree (temp, region);
-          collect_finally_tree_1 (gimple_try_eval (stmt), stmt);
+          collect_finally_tree_1 (gimple_try_eval (stmt),
+                                 as_a <gtry *> (stmt));
          collect_finally_tree_1 (gimple_try_cleanup (stmt), region);
         }
       else if (gimple_try_kind (stmt) == GIMPLE_TRY_CATCH)
@@ -255,7 +296,9 @@ collect_finally_tree (gimple stmt, gimple region)
       break;
 
     case GIMPLE_CATCH:
-      collect_finally_tree_1 (gimple_catch_handler (stmt), region);
+      collect_finally_tree_1 (gimple_catch_handler (
+                                as_a <gcatch *> (stmt)),
+                             region);
       break;
 
     case GIMPLE_EH_FILTER:
@@ -263,8 +306,11 @@ collect_finally_tree (gimple stmt, gimple region)
       break;
 
     case GIMPLE_EH_ELSE:
-      collect_finally_tree_1 (gimple_eh_else_n_body (stmt), region);
-      collect_finally_tree_1 (gimple_eh_else_e_body (stmt), region);
+      {
+       geh_else *eh_else_stmt = as_a <geh_else *> (stmt);
+       collect_finally_tree_1 (gimple_eh_else_n_body (eh_else_stmt), region);
+       collect_finally_tree_1 (gimple_eh_else_e_body (eh_else_stmt), region);
+      }
       break;
 
     default:
@@ -286,7 +332,7 @@ outside_finally_tree (treemple start, gimple target)
   do
     {
       n.child = start;
-      p = (struct finally_tree_node *) htab_find (finally_tree, &n);
+      p = finally_tree->find (&n);
       if (!p)
        return true;
       start.g = p->parent;
@@ -321,6 +367,7 @@ static bitmap eh_region_may_contain_throw_map;
 struct goto_queue_node
 {
   treemple stmt;
+  location_t location;
   gimple_seq repl_stmt;
   gimple cont_stmt;
   int index;
@@ -355,8 +402,8 @@ struct leh_tf_state
      try_finally_expr is the original GIMPLE_TRY_FINALLY.  We need to retain
      this so that outside_finally_tree can reliably reference the tree used
      in the collect_finally_tree data structures.  */
-  gimple try_finally_expr;
-  gimple top_p;
+  gtry *try_finally_expr;
+  gtry *top_p;
 
   /* While lowering a top_p usually it is expanded into multiple statements,
      thus we need the following field to store them. */
@@ -374,10 +421,10 @@ struct leh_tf_state
   size_t goto_queue_active;
 
   /* Pointer map to help in searching goto_queue when it is large.  */
-  struct pointer_map_t *goto_queue_map;
+  hash_map<gimple, goto_queue_node *> *goto_queue_map;
 
   /* The set of unique labels seen as entries in the goto queue.  */
-  VEC(tree,heap) *dest_array;
+  vec<tree> dest_array;
 
   /* A label to be added at the end of the completed transformed
      sequence.  It will be set if may_fallthru was true *at one time*,
@@ -396,7 +443,7 @@ struct leh_tf_state
   bool may_throw;
 };
 
-static gimple_seq lower_eh_must_not_throw (struct leh_state *, gimple);
+static gimple_seq lower_eh_must_not_throw (struct leh_state *, gtry *);
 
 /* Search for STMT in the goto queue.  Return the replacement,
    or null if the statement isn't in the queue.  */
@@ -409,7 +456,6 @@ static gimple_seq
 find_goto_replacement (struct leh_tf_state *tf, treemple stmt)
 {
   unsigned int i;
-  void **slot;
 
   if (tf->goto_queue_active < LARGE_GOTO_QUEUE)
     {
@@ -424,19 +470,18 @@ find_goto_replacement (struct leh_tf_state *tf, treemple stmt)
 
   if (!tf->goto_queue_map)
     {
-      tf->goto_queue_map = pointer_map_create ();
+      tf->goto_queue_map = new hash_map<gimple, goto_queue_node *>;
       for (i = 0; i < tf->goto_queue_active; i++)
        {
-         slot = pointer_map_insert (tf->goto_queue_map,
-                                     tf->goto_queue[i].stmt.g);
-          gcc_assert (*slot == NULL);
-         *slot = &tf->goto_queue[i];
+         bool existed = tf->goto_queue_map->put (tf->goto_queue[i].stmt.g,
+                                                 &tf->goto_queue[i]);
+         gcc_assert (!existed);
        }
     }
 
-  slot = pointer_map_contains (tf->goto_queue_map, stmt.g);
+  goto_queue_node **slot = tf->goto_queue_map->get (stmt.g);
   if (slot != NULL)
-    return (((struct goto_queue_node *) *slot)->repl_stmt);
+    return ((*slot)->repl_stmt);
 
   return NULL;
 }
@@ -512,14 +557,21 @@ replace_goto_queue_1 (gimple stmt, struct leh_tf_state *tf,
       replace_goto_queue_stmt_list (gimple_try_cleanup_ptr (stmt), tf);
       break;
     case GIMPLE_CATCH:
-      replace_goto_queue_stmt_list (gimple_catch_handler_ptr (stmt), tf);
+      replace_goto_queue_stmt_list (gimple_catch_handler_ptr (
+                                     as_a <gcatch *> (stmt)),
+                                   tf);
       break;
     case GIMPLE_EH_FILTER:
       replace_goto_queue_stmt_list (gimple_eh_filter_failure_ptr (stmt), tf);
       break;
     case GIMPLE_EH_ELSE:
-      replace_goto_queue_stmt_list (gimple_eh_else_n_body_ptr (stmt), tf);
-      replace_goto_queue_stmt_list (gimple_eh_else_e_body_ptr (stmt), tf);
+      {
+       geh_else *eh_else_stmt = as_a <geh_else *> (stmt);
+       replace_goto_queue_stmt_list (gimple_eh_else_n_body_ptr (eh_else_stmt),
+                                     tf);
+       replace_goto_queue_stmt_list (gimple_eh_else_e_body_ptr (eh_else_stmt),
+                                     tf);
+      }
       break;
 
     default:
@@ -560,7 +612,8 @@ static void
 record_in_goto_queue (struct leh_tf_state *tf,
                       treemple new_stmt,
                       int index,
-                      bool is_label)
+                      bool is_label,
+                     location_t location)
 {
   size_t active, size;
   struct goto_queue_node *q;
@@ -583,6 +636,7 @@ record_in_goto_queue (struct leh_tf_state *tf,
   memset (q, 0, sizeof (*q));
   q->stmt = new_stmt;
   q->index = index;
+  q->location = location;
   q->is_label = is_label;
 }
 
@@ -590,7 +644,8 @@ record_in_goto_queue (struct leh_tf_state *tf,
    TF is not null.  */
 
 static void
-record_in_goto_queue_label (struct leh_tf_state *tf, treemple stmt, tree label)
+record_in_goto_queue_label (struct leh_tf_state *tf, treemple stmt, tree label,
+                           location_t location)
 {
   int index;
   treemple temp, new_stmt;
@@ -609,27 +664,27 @@ record_in_goto_queue_label (struct leh_tf_state *tf, treemple stmt, tree label)
   if (!outside_finally_tree (temp, tf->try_finally_expr))
     return;
 
-  if (! tf->dest_array)
+  if (! tf->dest_array.exists ())
     {
-      tf->dest_array = VEC_alloc (tree, heap, 10);
-      VEC_quick_push (tree, tf->dest_array, label);
+      tf->dest_array.create (10);
+      tf->dest_array.quick_push (label);
       index = 0;
     }
   else
     {
-      int n = VEC_length (tree, tf->dest_array);
+      int n = tf->dest_array.length ();
       for (index = 0; index < n; ++index)
-        if (VEC_index (tree, tf->dest_array, index) == label)
+        if (tf->dest_array[index] == label)
           break;
       if (index == n)
-        VEC_safe_push (tree, heap, tf->dest_array, label);
+        tf->dest_array.safe_push (label);
     }
 
   /* In the case of a GOTO we want to record the destination label,
      since with a GIMPLE_COND we have an easy access to the then/else
      labels. */
   new_stmt = stmt;
-  record_in_goto_queue (tf, new_stmt, index, true);
+  record_in_goto_queue (tf, new_stmt, index, true, location);
 }
 
 /* For any GIMPLE_GOTO or GIMPLE_RETURN, decide whether it leaves a try_finally
@@ -648,20 +703,28 @@ maybe_record_in_goto_queue (struct leh_state *state, gimple stmt)
   switch (gimple_code (stmt))
     {
     case GIMPLE_COND:
-      new_stmt.tp = gimple_op_ptr (stmt, 2);
-      record_in_goto_queue_label (tf, new_stmt, gimple_cond_true_label (stmt));
-      new_stmt.tp = gimple_op_ptr (stmt, 3);
-      record_in_goto_queue_label (tf, new_stmt, gimple_cond_false_label (stmt));
+      {
+       gcond *cond_stmt = as_a <gcond *> (stmt);
+       new_stmt.tp = gimple_op_ptr (cond_stmt, 2);
+       record_in_goto_queue_label (tf, new_stmt,
+                                   gimple_cond_true_label (cond_stmt),
+                                   EXPR_LOCATION (*new_stmt.tp));
+       new_stmt.tp = gimple_op_ptr (cond_stmt, 3);
+       record_in_goto_queue_label (tf, new_stmt,
+                                   gimple_cond_false_label (cond_stmt),
+                                   EXPR_LOCATION (*new_stmt.tp));
+      }
       break;
     case GIMPLE_GOTO:
       new_stmt.g = stmt;
-      record_in_goto_queue_label (tf, new_stmt, gimple_goto_dest (stmt));
+      record_in_goto_queue_label (tf, new_stmt, gimple_goto_dest (stmt),
+                                 gimple_location (stmt));
       break;
 
     case GIMPLE_RETURN:
       tf->may_return = true;
       new_stmt.g = stmt;
-      record_in_goto_queue (tf, new_stmt, -1, false);
+      record_in_goto_queue (tf, new_stmt, -1, false, gimple_location (stmt));
       break;
 
     default:
@@ -676,7 +739,8 @@ maybe_record_in_goto_queue (struct leh_state *state, gimple stmt)
    of the labels will leave outer GIMPLE_TRY_FINALLY nodes. Verify this.  */
 
 static void
-verify_norecord_switch_expr (struct leh_state *state, gimple switch_expr)
+verify_norecord_switch_expr (struct leh_state *state,
+                            gswitch *switch_expr)
 {
   struct leh_tf_state *tf = state->tf;
   size_t i, n;
@@ -732,6 +796,7 @@ do_return_redirection (struct goto_queue_node *q, tree finlab, gimple_seq mod)
     gimple_seq_add_seq (&q->repl_stmt, mod);
 
   x = gimple_build_goto (finlab);
+  gimple_set_location (x, q->location);
   gimple_seq_add_stmt (&q->repl_stmt, x);
 }
 
@@ -741,16 +806,17 @@ static void
 do_goto_redirection (struct goto_queue_node *q, tree finlab, gimple_seq mod,
                     struct leh_tf_state *tf)
 {
-  gimple x;
+  ggoto *x;
 
   gcc_assert (q->is_label);
 
-  q->cont_stmt = gimple_build_goto (VEC_index (tree, tf->dest_array, q->index));
+  q->cont_stmt = gimple_build_goto (tf->dest_array[q->index]);
 
   if (mod)
     gimple_seq_add_seq (&q->repl_stmt, mod);
 
   x = gimple_build_goto (finlab);
+  gimple_set_location (x, q->location);
   gimple_seq_add_stmt (&q->repl_stmt, x);
 }
 
@@ -760,7 +826,7 @@ static void
 emit_post_landing_pad (gimple_seq *seq, eh_region region)
 {
   eh_landing_pad lp = region->landing_pads;
-  gimple x;
+  glabel *x;
 
   if (lp == NULL)
     lp = gen_eh_landing_pad (region);
@@ -777,7 +843,7 @@ emit_post_landing_pad (gimple_seq *seq, eh_region region)
 static void
 emit_resx (gimple_seq *seq, eh_region region)
 {
-  gimple x = gimple_build_resx (region->index);
+  gresx *x = gimple_build_resx (region->index);
   gimple_seq_add_stmt (seq, x);
   if (region->outer)
     record_stmt_eh_region (region->outer, x);
@@ -788,7 +854,7 @@ emit_resx (gimple_seq *seq, eh_region region)
 static void
 emit_eh_dispatch (gimple_seq *seq, eh_region region)
 {
-  gimple x = gimple_build_eh_dispatch (region->index);
+  geh_dispatch *x = gimple_build_eh_dispatch (region->index);
   gimple_seq_add_stmt (seq, x);
 }
 
@@ -820,10 +886,10 @@ eh_region_may_contain_throw (eh_region r)
 /* We want to transform
        try { body; } catch { stuff; }
    to
-       normal_seqence:
+       normal_sequence:
          body;
          over:
-       eh_seqence:
+       eh_sequence:
          landing_pad:
          stuff;
          goto over;
@@ -833,7 +899,7 @@ eh_region_may_contain_throw (eh_region r)
    an existing label that should be put at the exit, or NULL.  */
 
 static gimple_seq
-frob_into_branch_around (gimple tp, eh_region region, tree over)
+frob_into_branch_around (gtry *tp, eh_region region, tree over)
 {
   gimple x;
   gimple_seq cleanup, result;
@@ -850,6 +916,7 @@ frob_into_branch_around (gimple tp, eh_region region, tree over)
       if (!over)
        over = create_artificial_label (loc);
       x = gimple_build_goto (over);
+      gimple_set_location (x, loc);
       gimple_seq_add_stmt (&cleanup, x);
     }
   gimple_seq_add_seq (&eh_seq, cleanup);
@@ -866,13 +933,26 @@ frob_into_branch_around (gimple tp, eh_region region, tree over)
    Make sure to record all new labels found.  */
 
 static gimple_seq
-lower_try_finally_dup_block (gimple_seq seq, struct leh_state *outer_state)
+lower_try_finally_dup_block (gimple_seq seq, struct leh_state *outer_state,
+                            location_t loc)
 {
-  gimple region = NULL;
+  gtry *region = NULL;
   gimple_seq new_seq;
+  gimple_stmt_iterator gsi;
 
   new_seq = copy_gimple_seq_and_replace_locals (seq);
 
+  for (gsi = gsi_start (new_seq); !gsi_end_p (gsi); gsi_next (&gsi))
+    {
+      gimple stmt = gsi_stmt (gsi);
+      if (LOCATION_LOCUS (gimple_location (stmt)) == UNKNOWN_LOCATION)
+       {
+         tree block = gimple_block (stmt);
+         gimple_set_location (stmt, loc);
+         gimple_set_block (stmt, block);
+       }
+    }
+
   if (outer_state->tf)
     region = outer_state->tf->try_finally_expr;
   collect_finally_tree_1 (new_seq, region);
@@ -906,14 +986,14 @@ lower_try_finally_fallthru_label (struct leh_tf_state *tf)
 /* A subroutine of lower_try_finally.  If FINALLY consits of a
    GIMPLE_EH_ELSE node, return it.  */
 
-static inline gimple
+static inline geh_else *
 get_eh_else (gimple_seq finally)
 {
   gimple x = gimple_seq_first_stmt (finally);
   if (gimple_code (x) == GIMPLE_EH_ELSE)
     {
       gcc_assert (gimple_seq_singleton_p (finally));
-      return x;
+      return as_a <geh_else *> (x);
     }
   return NULL;
 }
@@ -947,7 +1027,10 @@ honor_protect_cleanup_actions (struct leh_state *outer_state,
   gimple_stmt_iterator gsi;
   bool finally_may_fallthru;
   gimple_seq finally;
-  gimple x, eh_else;
+  gimple x;
+  geh_mnt *eh_mnt;
+  gtry *try_stmt;
+  geh_else *eh_else;
 
   /* First check for nothing to do.  */
   if (lang_hooks.eh_protect_cleanup_actions == NULL)
@@ -967,7 +1050,8 @@ honor_protect_cleanup_actions (struct leh_state *outer_state,
       gimple_try_set_cleanup (tf->top_p, gimple_eh_else_n_body (eh_else));
     }
   else if (this_state)
-    finally = lower_try_finally_dup_block (finally, outer_state);
+    finally = lower_try_finally_dup_block (finally, outer_state,
+       gimple_location (tf->try_finally_expr));
   finally_may_fallthru = gimple_seq_may_fallthru (finally);
 
   /* If this cleanup consists of a TRY_CATCH_EXPR with TRY_CATCH_IS_CLEANUP
@@ -988,10 +1072,10 @@ honor_protect_cleanup_actions (struct leh_state *outer_state,
     }
 
   /* Wrap the block with protect_cleanup_actions as the action.  */
-  x = gimple_build_eh_must_not_throw (protect_cleanup_actions);
-  x = gimple_build_try (finally, gimple_seq_alloc_with_stmt (x),
-                       GIMPLE_TRY_CATCH);
-  finally = lower_eh_must_not_throw (outer_state, x);
+  eh_mnt = gimple_build_eh_must_not_throw (protect_cleanup_actions);
+  try_stmt = gimple_build_try (finally, gimple_seq_alloc_with_stmt (eh_mnt),
+                              GIMPLE_TRY_CATCH);
+  finally = lower_eh_must_not_throw (outer_state, try_stmt);
 
   /* Drop all of this into the exception sequence.  */
   emit_post_landing_pad (&eh_seq, tf->region);
@@ -1014,7 +1098,8 @@ lower_try_finally_nofallthru (struct leh_state *state,
                              struct leh_tf_state *tf)
 {
   tree lab;
-  gimple x, eh_else;
+  gimple x;
+  geh_else *eh_else;
   gimple_seq finally;
   struct goto_queue_node *q, *qe;
 
@@ -1064,6 +1149,7 @@ lower_try_finally_nofallthru (struct leh_state *state,
          emit_post_landing_pad (&eh_seq, tf->region);
 
          x = gimple_build_goto (lab);
+         gimple_set_location (x, gimple_location (tf->try_finally_expr));
          gimple_seq_add_stmt (&eh_seq, x);
        }
     }
@@ -1077,8 +1163,11 @@ static void
 lower_try_finally_onedest (struct leh_state *state, struct leh_tf_state *tf)
 {
   struct goto_queue_node *q, *qe;
+  geh_else *eh_else;
+  glabel *label_stmt;
   gimple x;
   gimple_seq finally;
+  gimple_stmt_iterator gsi;
   tree finally_label;
   location_t loc = gimple_location (tf->try_finally_expr);
 
@@ -1088,17 +1177,28 @@ lower_try_finally_onedest (struct leh_state *state, struct leh_tf_state *tf)
   /* Since there's only one destination, and the destination edge can only
      either be EH or non-EH, that implies that all of our incoming edges
      are of the same type.  Therefore we can lower EH_ELSE immediately.  */
-  x = get_eh_else (finally);
-  if (x)
+  eh_else = get_eh_else (finally);
+  if (eh_else)
     {
       if (tf->may_throw)
-       finally = gimple_eh_else_e_body (x);
+       finally = gimple_eh_else_e_body (eh_else);
       else
-       finally = gimple_eh_else_n_body (x);
+       finally = gimple_eh_else_n_body (eh_else);
     }
 
   lower_eh_constructs_1 (state, &finally);
 
+  for (gsi = gsi_start (finally); !gsi_end_p (gsi); gsi_next (&gsi))
+    {
+      gimple stmt = gsi_stmt (gsi);
+      if (LOCATION_LOCUS (gimple_location (stmt)) == UNKNOWN_LOCATION)
+       {
+         tree block = gimple_block (stmt);
+         gimple_set_location (stmt, gimple_location (tf->try_finally_expr));
+         gimple_set_block (stmt, block);
+       }
+    }
+
   if (tf->may_throw)
     {
       /* Only reachable via the exception edge.  Add the given label to
@@ -1118,8 +1218,8 @@ lower_try_finally_onedest (struct leh_state *state, struct leh_tf_state *tf)
     }
 
   finally_label = create_artificial_label (loc);
-  x = gimple_build_label (finally_label);
-  gimple_seq_add_stmt (&tf->top_p_seq, x);
+  label_stmt = gimple_build_label (finally_label);
+  gimple_seq_add_stmt (&tf->top_p_seq, label_stmt);
 
   gimple_seq_add_seq (&tf->top_p_seq, finally);
 
@@ -1140,7 +1240,7 @@ lower_try_finally_onedest (struct leh_state *state, struct leh_tf_state *tf)
        do_goto_redirection (q, finally_label, NULL, tf);
       replace_goto_queue (tf);
 
-      if (VEC_index (tree, tf->dest_array, 0) == tf->fallthru_label)
+      if (tf->dest_array[0] == tf->fallthru_label)
        {
          /* Reachable by goto to fallthru label only.  Redirect it
             to the new label (already created, sadly), and do not
@@ -1167,7 +1267,8 @@ lower_try_finally_copy (struct leh_state *state, struct leh_tf_state *tf)
   gimple_seq finally;
   gimple_seq new_stmt;
   gimple_seq seq;
-  gimple x, eh_else;
+  gimple x;
+  geh_else *eh_else;
   tree tmp;
   location_t tf_loc = gimple_location (tf->try_finally_expr);
 
@@ -1184,12 +1285,13 @@ lower_try_finally_copy (struct leh_state *state, struct leh_tf_state *tf)
 
   if (tf->may_fallthru)
     {
-      seq = lower_try_finally_dup_block (finally, state);
+      seq = lower_try_finally_dup_block (finally, state, tf_loc);
       lower_eh_constructs_1 (state, &seq);
       gimple_seq_add_seq (&new_stmt, seq);
 
       tmp = lower_try_finally_fallthru_label (tf);
       x = gimple_build_goto (tmp);
+      gimple_set_location (x, tf_loc);
       gimple_seq_add_stmt (&new_stmt, x);
     }
 
@@ -1200,7 +1302,7 @@ lower_try_finally_copy (struct leh_state *state, struct leh_tf_state *tf)
       if (eh_else)
        seq = gimple_eh_else_e_body (eh_else);
       else
-       seq = lower_try_finally_dup_block (finally, state);
+       seq = lower_try_finally_dup_block (finally, state, tf_loc);
       lower_eh_constructs_1 (state, &seq);
 
       emit_post_landing_pad (&eh_seq, tf->region);
@@ -1218,7 +1320,7 @@ lower_try_finally_copy (struct leh_state *state, struct leh_tf_state *tf)
        tree label;
       } *labels;
 
-      return_index = VEC_length (tree, tf->dest_array);
+      return_index = tf->dest_array.length ();
       labels = XCNEWVEC (struct labels_s, return_index + 1);
 
       q = tf->goto_queue;
@@ -1250,7 +1352,7 @@ lower_try_finally_copy (struct leh_state *state, struct leh_tf_state *tf)
          x = gimple_build_label (lab);
           gimple_seq_add_stmt (&new_stmt, x);
 
-         seq = lower_try_finally_dup_block (finally, state);
+         seq = lower_try_finally_dup_block (finally, state, q->location);
          lower_eh_constructs_1 (state, &seq);
           gimple_seq_add_seq (&new_stmt, seq);
 
@@ -1297,13 +1399,14 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
   int return_index, eh_index, fallthru_index;
   int nlabels, ndests, j, last_case_index;
   tree last_case;
-  VEC (tree,heap) *case_label_vec;
+  vec<tree> case_label_vec;
   gimple_seq switch_body = NULL;
-  gimple x, eh_else;
+  gimple x;
+  geh_else *eh_else;
   tree tmp;
   gimple switch_stmt;
   gimple_seq finally;
-  struct pointer_map_t *cont_map = NULL;
+  hash_map<tree, gimple> *cont_map = NULL;
   /* The location of the TRY_FINALLY stmt.  */
   location_t tf_loc = gimple_location (tf->try_finally_expr);
   /* The location of the finally block.  */
@@ -1320,11 +1423,8 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
   x = gimple_seq_last_stmt (finally);
   finally_loc = x ? gimple_location (x) : tf_loc;
 
-  /* Lower the finally block itself.  */
-  lower_eh_constructs_1 (state, &finally);
-
   /* Prepare for switch statement generation.  */
-  nlabels = VEC_length (tree, tf->dest_array);
+  nlabels = tf->dest_array.length ();
   return_index = nlabels;
   eh_index = return_index + tf->may_return;
   fallthru_index = eh_index + (tf->may_throw && !eh_else);
@@ -1333,10 +1433,10 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
   finally_tmp = create_tmp_var (integer_type_node, "finally_tmp");
   finally_label = create_artificial_label (finally_loc);
 
-  /* We use VEC_quick_push on case_label_vec throughout this function,
+  /* We use vec::quick_push on case_label_vec throughout this function,
      since we know the size in advance and allocate precisely as muce
      space as needed.  */
-  case_label_vec = VEC_alloc (tree, heap, ndests);
+  case_label_vec.create (ndests);
   last_case = NULL;
   last_case_index = 0;
 
@@ -1354,7 +1454,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
       tmp = build_int_cst (integer_type_node, fallthru_index);
       last_case = build_case_label (tmp, NULL,
                                    create_artificial_label (tf_loc));
-      VEC_quick_push (tree, case_label_vec, last_case);
+      case_label_vec.quick_push (last_case);
       last_case_index++;
 
       x = gimple_build_label (CASE_LABEL (last_case));
@@ -1362,6 +1462,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
 
       tmp = lower_try_finally_fallthru_label (tf);
       x = gimple_build_goto (tmp);
+      gimple_set_location (x, tf_loc);
       gimple_seq_add_stmt (&switch_body, x);
     }
 
@@ -1390,12 +1491,13 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
       gimple_seq_add_stmt (&eh_seq, x);
 
       x = gimple_build_goto (finally_label);
+      gimple_set_location (x, tf_loc);
       gimple_seq_add_stmt (&eh_seq, x);
 
       tmp = build_int_cst (integer_type_node, eh_index);
       last_case = build_case_label (tmp, NULL,
                                    create_artificial_label (tf_loc));
-      VEC_quick_push (tree, case_label_vec, last_case);
+      case_label_vec.quick_push (last_case);
       last_case_index++;
 
       x = gimple_build_label (CASE_LABEL (last_case));
@@ -1406,6 +1508,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
   x = gimple_build_label (finally_label);
   gimple_seq_add_stmt (&tf->top_p_seq, x);
 
+  lower_eh_constructs_1 (state, &finally);
   gimple_seq_add_seq (&tf->top_p_seq, finally);
 
   /* Redirect each incoming goto edge.  */
@@ -1439,36 +1542,30 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
        }
 
       case_index = j + q->index;
-      if (VEC_length (tree, case_label_vec) <= case_index
-          || !VEC_index (tree, case_label_vec, case_index))
+      if (case_label_vec.length () <= case_index || !case_label_vec[case_index])
         {
           tree case_lab;
-          void **slot;
          tmp = build_int_cst (integer_type_node, switch_id);
           case_lab = build_case_label (tmp, NULL,
                                       create_artificial_label (tf_loc));
           /* We store the cont_stmt in the pointer map, so that we can recover
              it in the loop below.  */
           if (!cont_map)
-            cont_map = pointer_map_create ();
-          slot = pointer_map_insert (cont_map, case_lab);
-          *slot = q->cont_stmt;
-          VEC_quick_push (tree, case_label_vec, case_lab);
+            cont_map = new hash_map<tree, gimple>;
+          cont_map->put (case_lab, q->cont_stmt);
+          case_label_vec.quick_push (case_lab);
         }
     }
   for (j = last_case_index; j < last_case_index + nlabels; j++)
     {
       gimple cont_stmt;
-      void **slot;
 
-      last_case = VEC_index (tree, case_label_vec, j);
+      last_case = case_label_vec[j];
 
       gcc_assert (last_case);
       gcc_assert (cont_map);
 
-      slot = pointer_map_contains (cont_map, last_case);
-      gcc_assert (slot);
-      cont_stmt = *(gimple *) slot;
+      cont_stmt = *cont_map->get (last_case);
 
       x = gimple_build_label (CASE_LABEL (last_case));
       gimple_seq_add_stmt (&switch_body, x);
@@ -1476,19 +1573,21 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
       maybe_record_in_goto_queue (state, cont_stmt);
     }
   if (cont_map)
-    pointer_map_destroy (cont_map);
+    delete cont_map;
 
   replace_goto_queue (tf);
 
   /* Make sure that the last case is the default label, as one is required.
      Then sort the labels, which is also required in GIMPLE.  */
   CASE_LOW (last_case) = NULL;
+  tree tem = case_label_vec.pop ();
+  gcc_assert (tem == last_case);
   sort_case_labels (case_label_vec);
 
   /* Build the switch statement, setting last_case to be the default
      label.  */
-  switch_stmt = gimple_build_switch_vec (finally_tmp, last_case,
-                                         case_label_vec);
+  switch_stmt = gimple_build_switch (finally_tmp, last_case,
+                                    case_label_vec);
   gimple_set_location (switch_stmt, finally_loc);
 
   /* Need to link SWITCH_STMT after running replace_goto_queue
@@ -1515,7 +1614,7 @@ static bool
 decide_copy_try_finally (int ndests, bool may_throw, gimple_seq finally)
 {
   int f_estimate, sw_estimate;
-  gimple eh_else;
+  geh_else *eh_else;
 
   /* If there's an EH_ELSE involved, the exception path is separate
      and really doesn't come into play for this computation.  */
@@ -1581,7 +1680,7 @@ cleanup_is_dead_in (eh_region reg)
    arrange for the FINALLY block to be executed on all exits.  */
 
 static gimple_seq
-lower_try_finally (struct leh_state *state, gimple tp)
+lower_try_finally (struct leh_state *state, gtry *tp)
 {
   struct leh_tf_state this_tf;
   struct leh_state this_state;
@@ -1594,7 +1693,7 @@ lower_try_finally (struct leh_state *state, gimple tp)
   this_tf.try_finally_expr = tp;
   this_tf.top_p = tp;
   this_tf.outer = state;
-  if (using_eh_for_cleanups_p && !cleanup_is_dead_in (state->cur_region))
+  if (using_eh_for_cleanups_p () && !cleanup_is_dead_in (state->cur_region))
     {
       this_tf.region = gen_eh_region_cleanup (state->cur_region);
       this_state.cur_region = this_tf.region;
@@ -1626,7 +1725,7 @@ lower_try_finally (struct leh_state *state, gimple tp)
      how many destinations are reached by the finally block.  Use this to
      determine how we process the finally block itself.  */
 
-  ndests = VEC_length (tree, this_tf.dest_array);
+  ndests = this_tf.dest_array.length ();
   ndests += this_tf.may_fallthru;
   ndests += this_tf.may_return;
   ndests += this_tf.may_throw;
@@ -1661,10 +1760,10 @@ lower_try_finally (struct leh_state *state, gimple tp)
       gimple_seq_add_stmt (&this_tf.top_p_seq, x);
     }
 
-  VEC_free (tree, heap, this_tf.dest_array);
+  this_tf.dest_array.release ();
   free (this_tf.goto_queue);
   if (this_tf.goto_queue_map)
-    pointer_map_destroy (this_tf.goto_queue_map);
+    delete this_tf.goto_queue_map;
 
   /* If there was an old (aka outer) eh_seq, append the current eh_seq.
      If there was no old eh_seq, then the append is trivially already done.  */
@@ -1676,7 +1775,7 @@ lower_try_finally (struct leh_state *state, gimple tp)
        {
          gimple_seq new_eh_seq = eh_seq;
          eh_seq = old_eh_seq;
-         gimple_seq_add_seq(&eh_seq, new_eh_seq);
+         gimple_seq_add_seq (&eh_seq, new_eh_seq);
        }
     }
 
@@ -1688,7 +1787,7 @@ lower_try_finally (struct leh_state *state, gimple tp)
    exception region trees that records all the magic.  */
 
 static gimple_seq
-lower_catch (struct leh_state *state, gimple tp)
+lower_catch (struct leh_state *state, gtry *tp)
 {
   eh_region try_region = NULL;
   struct leh_state this_state = *state;
@@ -1716,6 +1815,12 @@ lower_catch (struct leh_state *state, gimple tp)
   this_state.cur_region = state->cur_region;
   this_state.ehp_region = try_region;
 
+  /* Add eh_seq from lowering EH in the cleanup sequence after the cleanup
+     itself, so that e.g. for coverage purposes the nested cleanups don't
+     appear before the cleanup body.  See PR64634 for details.  */
+  gimple_seq old_eh_seq = eh_seq;
+  eh_seq = NULL;
+
   out_label = NULL;
   cleanup = gimple_try_cleanup (tp);
   for (gsi = gsi_start (cleanup);
@@ -1723,13 +1828,13 @@ lower_catch (struct leh_state *state, gimple tp)
        gsi_next (&gsi))
     {
       eh_catch c;
-      gimple gcatch;
+      gcatch *catch_stmt;
       gimple_seq handler;
 
-      gcatch = gsi_stmt (gsi);
-      c = gen_eh_region_catch (try_region, gimple_catch_types (gcatch));
+      catch_stmt = as_a <gcatch *> (gsi_stmt (gsi));
+      c = gen_eh_region_catch (try_region, gimple_catch_types (catch_stmt));
 
-      handler = gimple_catch_handler (gcatch);
+      handler = gimple_catch_handler (catch_stmt);
       lower_eh_constructs_1 (&this_state, &handler);
 
       c->label = create_artificial_label (UNKNOWN_LOCATION);
@@ -1752,7 +1857,11 @@ lower_catch (struct leh_state *state, gimple tp)
 
   gimple_try_set_cleanup (tp, new_seq);
 
-  return frob_into_branch_around (tp, try_region, out_label);
+  gimple_seq new_eh_seq = eh_seq;
+  eh_seq = old_eh_seq;
+  gimple_seq ret_seq = frob_into_branch_around (tp, try_region, out_label);
+  gimple_seq_add_seq (&eh_seq, new_eh_seq);
+  return ret_seq;
 }
 
 /* A subroutine of lower_eh_constructs_1.  Lower a GIMPLE_TRY with a
@@ -1760,7 +1869,7 @@ lower_catch (struct leh_state *state, gimple tp)
    region trees that record all the magic.  */
 
 static gimple_seq
-lower_eh_filter (struct leh_state *state, gimple tp)
+lower_eh_filter (struct leh_state *state, gtry *tp)
 {
   struct leh_state this_state = *state;
   eh_region this_region = NULL;
@@ -1805,7 +1914,7 @@ lower_eh_filter (struct leh_state *state, gimple tp)
    plus the exception region trees that record all the magic.  */
 
 static gimple_seq
-lower_eh_must_not_throw (struct leh_state *state, gimple tp)
+lower_eh_must_not_throw (struct leh_state *state, gtry *tp)
 {
   struct leh_state this_state = *state;
 
@@ -1816,8 +1925,10 @@ lower_eh_must_not_throw (struct leh_state *state, gimple tp)
 
       this_region = gen_eh_region_must_not_throw (state->cur_region);
       this_region->u.must_not_throw.failure_decl
-       = gimple_eh_must_not_throw_fndecl (inner);
-      this_region->u.must_not_throw.failure_loc = gimple_location (tp);
+       = gimple_eh_must_not_throw_fndecl (
+           as_a <geh_mnt *> (inner));
+      this_region->u.must_not_throw.failure_loc
+       = LOCATION_LOCUS (gimple_location (tp));
 
       /* In order to get mangling applied to this decl, we must mark it
         used now.  Otherwise, pass_ipa_free_lang_data won't think it
@@ -1836,7 +1947,7 @@ lower_eh_must_not_throw (struct leh_state *state, gimple tp)
    except that we only execute the cleanup block for exception edges.  */
 
 static gimple_seq
-lower_cleanup (struct leh_state *state, gimple tp)
+lower_cleanup (struct leh_state *state, gtry *tp)
 {
   struct leh_state this_state = *state;
   eh_region this_region = NULL;
@@ -1962,7 +2073,7 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi)
          && is_gimple_reg_type (TREE_TYPE (gimple_get_lhs (stmt))))
        {
          tree lhs = gimple_get_lhs (stmt);
-         tree tmp = create_tmp_var (TREE_TYPE (lhs), NULL);
+         tree tmp = create_tmp_var (TREE_TYPE (lhs));
          gimple s = gimple_build_assign (lhs, tmp);
          gimple_set_location (s, gimple_location (stmt));
          gimple_set_block (s, gimple_block (stmt));
@@ -1987,40 +2098,43 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi)
       break;
 
     case GIMPLE_SWITCH:
-      verify_norecord_switch_expr (state, stmt);
+      verify_norecord_switch_expr (state, as_a <gswitch *> (stmt));
       break;
 
     case GIMPLE_TRY:
-      if (gimple_try_kind (stmt) == GIMPLE_TRY_FINALLY)
-       replace = lower_try_finally (state, stmt);
-      else
-       {
-         x = gimple_seq_first_stmt (gimple_try_cleanup (stmt));
-         if (!x)
-           {
-             replace = gimple_try_eval (stmt);
-             lower_eh_constructs_1 (state, &replace);
-           }
-         else
-           switch (gimple_code (x))
+      {
+       gtry *try_stmt = as_a <gtry *> (stmt);
+       if (gimple_try_kind (try_stmt) == GIMPLE_TRY_FINALLY)
+         replace = lower_try_finally (state, try_stmt);
+       else
+         {
+           x = gimple_seq_first_stmt (gimple_try_cleanup (try_stmt));
+           if (!x)
              {
+               replace = gimple_try_eval (try_stmt);
+               lower_eh_constructs_1 (state, &replace);
+             }
+           else
+             switch (gimple_code (x))
+               {
                case GIMPLE_CATCH:
-                   replace = lower_catch (state, stmt);
-                   break;
+                 replace = lower_catch (state, try_stmt);
+                 break;
                case GIMPLE_EH_FILTER:
-                   replace = lower_eh_filter (state, stmt);
-                   break;
+                 replace = lower_eh_filter (state, try_stmt);
+                 break;
                case GIMPLE_EH_MUST_NOT_THROW:
-                   replace = lower_eh_must_not_throw (state, stmt);
-                   break;
+                 replace = lower_eh_must_not_throw (state, try_stmt);
+                 break;
                case GIMPLE_EH_ELSE:
-                   /* This code is only valid with GIMPLE_TRY_FINALLY.  */
-                   gcc_unreachable ();
+                 /* This code is only valid with GIMPLE_TRY_FINALLY.  */
+                 gcc_unreachable ();
                default:
-                   replace = lower_cleanup (state, stmt);
-                   break;
-             }
-       }
+                 replace = lower_cleanup (state, try_stmt);
+                 break;
+               }
+         }
+      }
 
       /* Remove the old stmt and insert the transformed sequence
         instead. */
@@ -2053,8 +2167,35 @@ lower_eh_constructs_1 (struct leh_state *state, gimple_seq *pseq)
     lower_eh_constructs_2 (state, &gsi);
 }
 
-static unsigned int
-lower_eh_constructs (void)
+namespace {
+
+const pass_data pass_data_lower_eh =
+{
+  GIMPLE_PASS, /* type */
+  "eh", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_EH, /* tv_id */
+  PROP_gimple_lcf, /* properties_required */
+  PROP_gimple_leh, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_lower_eh : public gimple_opt_pass
+{
+public:
+  pass_lower_eh (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_lower_eh, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual unsigned int execute (function *);
+
+}; // class pass_lower_eh
+
+unsigned int
+pass_lower_eh::execute (function *fun)
 {
   struct leh_state null_state;
   gimple_seq bodyp;
@@ -2063,7 +2204,7 @@ lower_eh_constructs (void)
   if (bodyp == NULL)
     return 0;
 
-  finally_tree = htab_create (31, struct_ptr_hash, struct_ptr_eq, free);
+  finally_tree = new hash_table<finally_tree_hasher> (31);
   eh_region_may_contain_throw_map = BITMAP_ALLOC (NULL);
   memset (&null_state, 0, sizeof (null_state));
 
@@ -2081,13 +2222,14 @@ lower_eh_constructs (void)
      didn't change its value, and we don't have to re-set the function.  */
   gcc_assert (bodyp == gimple_body (current_function_decl));
 
-  htab_delete (finally_tree);
+  delete finally_tree;
+  finally_tree = NULL;
   BITMAP_FREE (eh_region_may_contain_throw_map);
   eh_seq = NULL;
 
   /* If this function needs a language specific EH personality routine
      and the frontend didn't already set one do so now.  */
-  if (function_needs_eh_personality (cfun) == eh_personality_lang
+  if (function_needs_eh_personality (fun) == eh_personality_lang
       && !DECL_FUNCTION_PERSONALITY (current_function_decl))
     DECL_FUNCTION_PERSONALITY (current_function_decl)
       = lang_hooks.eh_personality ();
@@ -2095,31 +2237,20 @@ lower_eh_constructs (void)
   return 0;
 }
 
-struct gimple_opt_pass pass_lower_eh =
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_lower_eh (gcc::context *ctxt)
 {
- {
-  GIMPLE_PASS,
-  "eh",                                        /* name */
-  NULL,                                        /* gate */
-  lower_eh_constructs,                 /* execute */
-  NULL,                                        /* sub */
-  NULL,                                        /* next */
-  0,                                   /* static_pass_number */
-  TV_TREE_EH,                          /* tv_id */
-  PROP_gimple_lcf,                     /* properties_required */
-  PROP_gimple_leh,                     /* properties_provided */
-  0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  0                                    /* todo_flags_finish */
- }
-};
+  return new pass_lower_eh (ctxt);
+}
 \f
 /* Create the multiple edges from an EH_DISPATCH statement to all of
    the possible handlers for its EH region.  Return true if there's
    no fallthru edge; false if there is.  */
 
 bool
-make_eh_dispatch_edges (gimple stmt)
+make_eh_dispatch_edges (geh_dispatch *stmt)
 {
   eh_region r;
   eh_catch c;
@@ -2277,7 +2408,7 @@ redirect_eh_edge (edge edge_in, basic_block new_bb)
    The actual edge update will happen in the caller.  */
 
 void
-redirect_eh_dispatch_edge (gimple stmt, edge e, basic_block new_bb)
+redirect_eh_dispatch_edge (geh_dispatch *stmt, edge e, basic_block new_bb)
 {
   tree new_lab = gimple_block_label (new_bb);
   bool any_changed = false;
@@ -2365,11 +2496,6 @@ operation_could_trap_helper_p (enum tree_code op,
     case UNEQ_EXPR:
       return honor_snans;
 
-    case CONVERT_EXPR:
-    case FIX_TRUNC_EXPR:
-      /* Conversion of floating point might trap.  */
-      return honor_nans;
-
     case NEGATE_EXPR:
     case ABS_EXPR:
     case CONJ_EXPR:
@@ -2427,6 +2553,68 @@ operation_could_trap_p (enum tree_code op, bool fp_operation, bool honor_trapv,
                                        &handled);
 }
 
+
+/* Returns true if it is possible to prove that the index of
+   an array access REF (an ARRAY_REF expression) falls into the
+   array bounds.  */
+
+static bool
+in_array_bounds_p (tree ref)
+{
+  tree idx = TREE_OPERAND (ref, 1);
+  tree min, max;
+
+  if (TREE_CODE (idx) != INTEGER_CST)
+    return false;
+
+  min = array_ref_low_bound (ref);
+  max = array_ref_up_bound (ref);
+  if (!min
+      || !max
+      || TREE_CODE (min) != INTEGER_CST
+      || TREE_CODE (max) != INTEGER_CST)
+    return false;
+
+  if (tree_int_cst_lt (idx, min)
+      || tree_int_cst_lt (max, idx))
+    return false;
+
+  return true;
+}
+
+/* Returns true if it is possible to prove that the range of
+   an array access REF (an ARRAY_RANGE_REF expression) falls
+   into the array bounds.  */
+
+static bool
+range_in_array_bounds_p (tree ref)
+{
+  tree domain_type = TYPE_DOMAIN (TREE_TYPE (ref));
+  tree range_min, range_max, min, max;
+
+  range_min = TYPE_MIN_VALUE (domain_type);
+  range_max = TYPE_MAX_VALUE (domain_type);
+  if (!range_min
+      || !range_max
+      || TREE_CODE (range_min) != INTEGER_CST
+      || TREE_CODE (range_max) != INTEGER_CST)
+    return false;
+
+  min = array_ref_low_bound (ref);
+  max = array_ref_up_bound (ref);
+  if (!min
+      || !max
+      || TREE_CODE (min) != INTEGER_CST
+      || TREE_CODE (max) != INTEGER_CST)
+    return false;
+
+  if (tree_int_cst_lt (range_min, min)
+      || tree_int_cst_lt (max, range_max))
+    return false;
+
+  return true;
+}
+
 /* Return true if EXPR can trap, as in dereferencing an invalid pointer
    location or floating point arithmetic.  C.f. the rtl version, may_trap_p.
    This routine expects only GIMPLE lhs or rhs input.  */
@@ -2462,12 +2650,6 @@ tree_could_trap_p (tree expr)
  restart:
   switch (code)
     {
-    case TARGET_MEM_REF:
-      if (TREE_CODE (TMR_BASE (expr)) == ADDR_EXPR
-         && !TMR_INDEX (expr) && !TMR_INDEX2 (expr))
-       return false;
-      return !TREE_THIS_NOTRAP (expr);
-
     case COMPONENT_REF:
     case REALPART_EXPR:
     case IMAGPART_EXPR:
@@ -2494,10 +2676,36 @@ tree_could_trap_p (tree expr)
        return false;
       return !in_array_bounds_p (expr);
 
+    case TARGET_MEM_REF:
     case MEM_REF:
-      if (TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR)
+      if (TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR
+         && tree_could_trap_p (TREE_OPERAND (TREE_OPERAND (expr, 0), 0)))
+       return true;
+      if (TREE_THIS_NOTRAP (expr))
        return false;
-      /* Fallthru.  */
+      /* We cannot prove that the access is in-bounds when we have
+         variable-index TARGET_MEM_REFs.  */
+      if (code == TARGET_MEM_REF
+         && (TMR_INDEX (expr) || TMR_INDEX2 (expr)))
+       return true;
+      if (TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR)
+       {
+         tree base = TREE_OPERAND (TREE_OPERAND (expr, 0), 0);
+         offset_int off = mem_ref_offset (expr);
+         if (wi::neg_p (off, SIGNED))
+           return true;
+         if (TREE_CODE (base) == STRING_CST)
+           return wi::leu_p (TREE_STRING_LENGTH (base), off);
+         else if (DECL_SIZE_UNIT (base) == NULL_TREE
+                  || TREE_CODE (DECL_SIZE_UNIT (base)) != INTEGER_CST
+                  || wi::leu_p (wi::to_offset (DECL_SIZE_UNIT (base)), off))
+           return true;
+         /* Now we are sure the first byte of the access is inside
+            the object.  */
+         return false;
+       }
+      return true;
+
     case INDIRECT_REF:
       return !TREE_THIS_NOTRAP (expr);
 
@@ -2517,15 +2725,12 @@ tree_could_trap_p (tree expr)
       /* Assume that accesses to weak functions may trap, unless we know
         they are certainly defined in current TU or in some other
         LTO partition.  */
-      if (DECL_WEAK (expr))
+      if (DECL_WEAK (expr) && !DECL_COMDAT (expr) && DECL_EXTERNAL (expr))
        {
-         struct cgraph_node *node;
-         if (!DECL_EXTERNAL (expr))
-           return false;
-         node = cgraph_function_node (cgraph_get_node (expr), NULL);
-         if (node && node->symbol.in_other_partition)
-           return false;
-         return true;
+         cgraph_node *node = cgraph_node::get (expr);
+         if (node)
+           node = node->function_symbol ();
+         return !(node && node->in_other_partition);
        }
       return false;
 
@@ -2533,15 +2738,12 @@ tree_could_trap_p (tree expr)
       /* Assume that accesses to weak vars may trap, unless we know
         they are certainly defined in current TU or in some other
         LTO partition.  */
-      if (DECL_WEAK (expr))
+      if (DECL_WEAK (expr) && !DECL_COMDAT (expr) && DECL_EXTERNAL (expr))
        {
-         struct varpool_node *node;
-         if (!DECL_EXTERNAL (expr))
-           return false;
-         node = varpool_variable_node (varpool_get_node (expr), NULL);
-         if (node && node->symbol.in_other_partition)
-           return false;
-         return true;
+         varpool_node *node = varpool_node::get (expr);
+         if (node)
+           node = node->ultimate_alias_target ();
+         return !(node && node->in_other_partition);
        }
       return false;
 
@@ -2621,7 +2823,7 @@ stmt_could_throw_p (gimple stmt)
       return true;
 
     case GIMPLE_CALL:
-      return !gimple_call_nothrow_p (stmt);
+      return !gimple_call_nothrow_p (as_a <gcall *> (stmt));
 
     case GIMPLE_ASSIGN:
     case GIMPLE_COND:
@@ -2632,7 +2834,7 @@ stmt_could_throw_p (gimple stmt)
     case GIMPLE_ASM:
       if (!cfun->can_throw_non_call_exceptions)
         return false;
-      return gimple_asm_volatile_p (stmt);
+      return gimple_asm_volatile_p (as_a <gasm *> (stmt));
 
     default:
       return false;
@@ -2751,10 +2953,10 @@ maybe_clean_or_replace_eh_stmt (gimple old_stmt, gimple new_stmt)
 bool
 maybe_duplicate_eh_stmt_fn (struct function *new_fun, gimple new_stmt,
                            struct function *old_fun, gimple old_stmt,
-                           struct pointer_map_t *map, int default_lp_nr)
+                           hash_map<void *, void *> *map,
+                           int default_lp_nr)
 {
   int old_lp_nr, new_lp_nr;
-  void **slot;
 
   if (!stmt_could_throw_p (new_stmt))
     return false;
@@ -2770,18 +2972,16 @@ maybe_duplicate_eh_stmt_fn (struct function *new_fun, gimple new_stmt,
     {
       eh_landing_pad old_lp, new_lp;
 
-      old_lp = VEC_index (eh_landing_pad, old_fun->eh->lp_array, old_lp_nr);
-      slot = pointer_map_contains (map, old_lp);
-      new_lp = (eh_landing_pad) *slot;
+      old_lp = (*old_fun->eh->lp_array)[old_lp_nr];
+      new_lp = static_cast<eh_landing_pad> (*map->get (old_lp));
       new_lp_nr = new_lp->index;
     }
   else
     {
       eh_region old_r, new_r;
 
-      old_r = VEC_index (eh_region, old_fun->eh->region_array, -old_lp_nr);
-      slot = pointer_map_contains (map, old_r);
-      new_r = (eh_region) *slot;
+      old_r = (*old_fun->eh->region_array)[-old_lp_nr];
+      new_r = static_cast<eh_region> (*map->get (old_r));
       new_lp_nr = -new_r->index;
     }
 
@@ -2860,7 +3060,7 @@ same_handler_p (gimple_seq oneh, gimple_seq twoh)
    temporary used in the initializer for A.  */
 
 static void
-optimize_double_finally (gimple one, gimple two)
+optimize_double_finally (gtry *one, gtry *two)
 {
   gimple oneh;
   gimple_stmt_iterator gsi;
@@ -2907,13 +3107,12 @@ refactor_eh_r (gimple_seq seq)
        two = NULL;
       else
        two = gsi_stmt (gsi);
-      if (one
-         && two
-         && gimple_code (one) == GIMPLE_TRY
-         && gimple_code (two) == GIMPLE_TRY
-         && gimple_try_kind (one) == GIMPLE_TRY_FINALLY
-         && gimple_try_kind (two) == GIMPLE_TRY_FINALLY)
-       optimize_double_finally (one, two);
+      if (one && two)
+       if (gtry *try_one = dyn_cast <gtry *> (one))
+         if (gtry *try_two = dyn_cast <gtry *> (two))
+           if (gimple_try_kind (try_one) == GIMPLE_TRY_FINALLY
+               && gimple_try_kind (try_two) == GIMPLE_TRY_FINALLY)
+             optimize_double_finally (try_one, try_two);
       if (one)
        switch (gimple_code (one))
          {
@@ -2922,14 +3121,17 @@ refactor_eh_r (gimple_seq seq)
            refactor_eh_r (gimple_try_cleanup (one));
            break;
          case GIMPLE_CATCH:
-           refactor_eh_r (gimple_catch_handler (one));
+           refactor_eh_r (gimple_catch_handler (as_a <gcatch *> (one)));
            break;
          case GIMPLE_EH_FILTER:
            refactor_eh_r (gimple_eh_filter_failure (one));
            break;
          case GIMPLE_EH_ELSE:
-           refactor_eh_r (gimple_eh_else_n_body (one));
-           refactor_eh_r (gimple_eh_else_e_body (one));
+           {
+             geh_else *eh_else_stmt = as_a <geh_else *> (one);
+             refactor_eh_r (gimple_eh_else_n_body (eh_else_stmt));
+             refactor_eh_r (gimple_eh_else_e_body (eh_else_stmt));
+           }
            break;
          default:
            break;
@@ -2941,42 +3143,51 @@ refactor_eh_r (gimple_seq seq)
     }
 }
 
-static unsigned
-refactor_eh (void)
+namespace {
+
+const pass_data pass_data_refactor_eh =
 {
-  refactor_eh_r (gimple_body (current_function_decl));
-  return 0;
-}
+  GIMPLE_PASS, /* type */
+  "ehopt", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_EH, /* tv_id */
+  PROP_gimple_lcf, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
 
-static bool
-gate_refactor_eh (void)
+class pass_refactor_eh : public gimple_opt_pass
 {
-  return flag_exceptions != 0;
-}
+public:
+  pass_refactor_eh (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_refactor_eh, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *) { return flag_exceptions != 0; }
+  virtual unsigned int execute (function *)
+    {
+      refactor_eh_r (gimple_body (current_function_decl));
+      return 0;
+    }
 
-struct gimple_opt_pass pass_refactor_eh =
+}; // class pass_refactor_eh
+
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_refactor_eh (gcc::context *ctxt)
 {
- {
-  GIMPLE_PASS,
-  "ehopt",                             /* name */
-  gate_refactor_eh,                    /* gate */
-  refactor_eh,                         /* execute */
-  NULL,                                        /* sub */
-  NULL,                                        /* next */
-  0,                                   /* static_pass_number */
-  TV_TREE_EH,                          /* tv_id */
-  PROP_gimple_lcf,                     /* properties_required */
-  0,                                   /* properties_provided */
-  0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  0                                    /* todo_flags_finish */
- }
-};
+  return new pass_refactor_eh (ctxt);
+}
 \f
 /* At the end of gimple optimization, we can lower RESX.  */
 
 static bool
-lower_resx (basic_block bb, gimple stmt, struct pointer_map_t *mnt_map)
+lower_resx (basic_block bb, gresx *stmt,
+           hash_map<eh_region, tree> *mnt_map)
 {
   int lp_nr;
   eh_region src_r, dst_r;
@@ -3021,21 +3232,19 @@ lower_resx (basic_block bb, gimple stmt, struct pointer_map_t *mnt_map)
       if (lp_nr < 0)
        {
          basic_block new_bb;
-         void **slot;
          tree lab;
 
          /* We are resuming into a MUST_NOT_CALL region.  Expand a call to
             the failure decl into a new block, if needed.  */
          gcc_assert (dst_r->type == ERT_MUST_NOT_THROW);
 
-         slot = pointer_map_contains (mnt_map, dst_r);
+         tree *slot = mnt_map->get (dst_r);
          if (slot == NULL)
            {
              gimple_stmt_iterator gsi2;
 
              new_bb = create_empty_bb (bb);
-             if (current_loops)
-               add_bb_to_loop (new_bb, bb->loop_father);
+             add_bb_to_loop (new_bb, bb->loop_father);
              lab = gimple_block_label (new_bb);
              gsi2 = gsi_start_bb (new_bb);
 
@@ -3044,12 +3253,11 @@ lower_resx (basic_block bb, gimple stmt, struct pointer_map_t *mnt_map)
              gimple_set_location (x, dst_r->u.must_not_throw.failure_loc);
              gsi_insert_after (&gsi2, x, GSI_CONTINUE_LINKING);
 
-             slot = pointer_map_insert (mnt_map, dst_r);
-             *slot = lab;
+             mnt_map->put (dst_r, lab);
            }
          else
            {
-             lab = (tree) *slot;
+             lab = *slot;
              new_bb = label_to_block (lab);
            }
 
@@ -3107,7 +3315,7 @@ lower_resx (basic_block bb, gimple stmt, struct pointer_map_t *mnt_map)
          fn = builtin_decl_implicit (BUILT_IN_EH_POINTER);
          src_nr = build_int_cst (integer_type_node, src_r->index);
          x = gimple_build_call (fn, 1, src_nr);
-         var = create_tmp_var (ptr_type_node, NULL);
+         var = create_tmp_var (ptr_type_node);
          var = make_ssa_name (var, x);
          gimple_call_set_lhs (x, var);
          gsi_insert_before (&gsi, x, GSI_SAME_STMT);
@@ -3125,28 +3333,54 @@ lower_resx (basic_block bb, gimple stmt, struct pointer_map_t *mnt_map)
   return ret;
 }
 
-static unsigned
-execute_lower_resx (void)
+namespace {
+
+const pass_data pass_data_lower_resx =
+{
+  GIMPLE_PASS, /* type */
+  "resx", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_EH, /* tv_id */
+  PROP_gimple_lcf, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_lower_resx : public gimple_opt_pass
+{
+public:
+  pass_lower_resx (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_lower_resx, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *) { return flag_exceptions != 0; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_lower_resx
+
+unsigned
+pass_lower_resx::execute (function *fun)
 {
   basic_block bb;
-  struct pointer_map_t *mnt_map;
   bool dominance_invalidated = false;
   bool any_rewritten = false;
 
-  mnt_map = pointer_map_create ();
+  hash_map<eh_region, tree> mnt_map;
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, fun)
     {
       gimple last = last_stmt (bb);
       if (last && is_gimple_resx (last))
        {
-         dominance_invalidated |= lower_resx (bb, last, mnt_map);
+         dominance_invalidated |=
+           lower_resx (bb, as_a <gresx *> (last), &mnt_map);
          any_rewritten = true;
        }
     }
 
-  pointer_map_destroy (mnt_map);
-
   if (dominance_invalidated)
     {
       free_dominance_info (CDI_DOMINATORS);
@@ -3156,30 +3390,13 @@ execute_lower_resx (void)
   return any_rewritten ? TODO_update_ssa_only_virtuals : 0;
 }
 
-static bool
-gate_lower_resx (void)
-{
-  return flag_exceptions != 0;
-}
+} // anon namespace
 
-struct gimple_opt_pass pass_lower_resx =
+gimple_opt_pass *
+make_pass_lower_resx (gcc::context *ctxt)
 {
- {
-  GIMPLE_PASS,
-  "resx",                              /* name */
-  gate_lower_resx,                     /* gate */
-  execute_lower_resx,                  /* execute */
-  NULL,                                        /* sub */
-  NULL,                                        /* next */
-  0,                                   /* static_pass_number */
-  TV_TREE_EH,                          /* tv_id */
-  PROP_gimple_lcf,                     /* properties_required */
-  0,                                   /* properties_provided */
-  0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  TODO_verify_flow                     /* todo_flags_finish */
- }
-};
+  return new pass_lower_resx (ctxt);
+}
 
 /* Try to optimize var = {v} {CLOBBER} stmts followed just by
    external throw.  */
@@ -3188,14 +3405,48 @@ static void
 optimize_clobbers (basic_block bb)
 {
   gimple_stmt_iterator gsi = gsi_last_bb (bb);
+  bool any_clobbers = false;
+  bool seen_stack_restore = false;
+  edge_iterator ei;
+  edge e;
+
+  /* Only optimize anything if the bb contains at least one clobber,
+     ends with resx (checked by caller), optionally contains some
+     debug stmts or labels, or at most one __builtin_stack_restore
+     call, and has an incoming EH edge.  */
   for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi))
     {
       gimple stmt = gsi_stmt (gsi);
       if (is_gimple_debug (stmt))
        continue;
-      if (!gimple_clobber_p (stmt)
-         || TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME)
-       return;
+      if (gimple_clobber_p (stmt))
+       {
+         any_clobbers = true;
+         continue;
+       }
+      if (!seen_stack_restore
+         && gimple_call_builtin_p (stmt, BUILT_IN_STACK_RESTORE))
+       {
+         seen_stack_restore = true;
+         continue;
+       }
+      if (gimple_code (stmt) == GIMPLE_LABEL)
+       break;
+      return;
+    }
+  if (!any_clobbers)
+    return;
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    if (e->flags & EDGE_EH)
+      break;
+  if (e == NULL)
+    return;
+  gsi = gsi_last_bb (bb);
+  for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi))
+    {
+      gimple stmt = gsi_stmt (gsi);
+      if (!gimple_clobber_p (stmt))
+       continue;
       unlink_stmt_vdef (stmt);
       gsi_remove (&gsi, true);
       release_defs (stmt);
@@ -3213,6 +3464,7 @@ sink_clobbers (basic_block bb)
   gimple_stmt_iterator gsi, dgsi;
   basic_block succbb;
   bool any_clobbers = false;
+  unsigned todo = 0;
 
   /* Only optimize if BB has a single EH successor and
      all predecessor edges are EH too.  */
@@ -3236,43 +3488,105 @@ sink_clobbers (basic_block bb)
        continue;
       if (gimple_code (stmt) == GIMPLE_LABEL)
        break;
-      if (!gimple_clobber_p (stmt)
-         || TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME)
+      if (!gimple_clobber_p (stmt))
        return 0;
       any_clobbers = true;
     }
   if (!any_clobbers)
     return 0;
 
-  succbb = single_succ (bb);
+  edge succe = single_succ_edge (bb);
+  succbb = succe->dest;
+
+  /* See if there is a virtual PHI node to take an updated virtual
+     operand from.  */
+  gphi *vphi = NULL;
+  tree vuse = NULL_TREE;
+  for (gphi_iterator gpi = gsi_start_phis (succbb);
+       !gsi_end_p (gpi); gsi_next (&gpi))
+    {
+      tree res = gimple_phi_result (gpi.phi ());
+      if (virtual_operand_p (res))
+       {
+         vphi = gpi.phi ();
+         vuse = res;
+         break;
+       }
+    }
+
   dgsi = gsi_after_labels (succbb);
   gsi = gsi_last_bb (bb);
   for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi))
     {
       gimple stmt = gsi_stmt (gsi);
+      tree lhs;
       if (is_gimple_debug (stmt))
        continue;
       if (gimple_code (stmt) == GIMPLE_LABEL)
        break;
-      unlink_stmt_vdef (stmt);
+      lhs = gimple_assign_lhs (stmt);
+      /* Unfortunately we don't have dominance info updated at this
+        point, so checking if
+        dominated_by_p (CDI_DOMINATORS, succbb,
+                        gimple_bb (SSA_NAME_DEF_STMT (TREE_OPERAND (lhs, 0)))
+        would be too costly.  Thus, avoid sinking any clobbers that
+        refer to non-(D) SSA_NAMEs.  */
+      if (TREE_CODE (lhs) == MEM_REF
+         && TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME
+         && !SSA_NAME_IS_DEFAULT_DEF (TREE_OPERAND (lhs, 0)))
+       {
+         unlink_stmt_vdef (stmt);
+         gsi_remove (&gsi, true);
+         release_defs (stmt);
+         continue;
+       }
+
+      /* As we do not change stmt order when sinking across a
+         forwarder edge we can keep virtual operands in place.  */
       gsi_remove (&gsi, false);
-      /* Trigger the operand scanner to cause renaming for virtual
-         operands for this statement.
-        ???  Given the simple structure of this code manually
-        figuring out the reaching definition should not be too hard.  */
-      if (gimple_vuse (stmt))
-       gimple_set_vuse (stmt, NULL_TREE);
-      gsi_insert_before (&dgsi, stmt, GSI_SAME_STMT);
+      gsi_insert_before (&dgsi, stmt, GSI_NEW_STMT);
+
+      /* But adjust virtual operands if we sunk across a PHI node.  */
+      if (vuse)
+       {
+         gimple use_stmt;
+         imm_use_iterator iter;
+         use_operand_p use_p;
+         FOR_EACH_IMM_USE_STMT (use_stmt, iter, vuse)
+           FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+             SET_USE (use_p, gimple_vdef (stmt));
+         if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (vuse))
+           {
+             SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_vdef (stmt)) = 1;
+             SSA_NAME_OCCURS_IN_ABNORMAL_PHI (vuse) = 0;
+           }
+         /* Adjust the incoming virtual operand.  */
+         SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (vphi, succe), gimple_vuse (stmt));
+         SET_USE (gimple_vuse_op (stmt), vuse);
+       }
+      /* If there isn't a single predecessor but no virtual PHI node
+         arrange for virtual operands to be renamed.  */
+      else if (gimple_vuse_op (stmt) != NULL_USE_OPERAND_P
+              && !single_pred_p (succbb))
+       {
+         /* In this case there will be no use of the VDEF of this stmt. 
+            ???  Unless this is a secondary opportunity and we have not
+            removed unreachable blocks yet, so we cannot assert this.  
+            Which also means we will end up renaming too many times.  */
+         SET_USE (gimple_vuse_op (stmt), gimple_vop (cfun));
+         mark_virtual_operands_for_renaming (cfun);
+         todo |= TODO_update_ssa_only_virtuals;
+       }
     }
 
-  return TODO_update_ssa_only_virtuals;
+  return todo;
 }
 
 /* At the end of inlining, we can lower EH_DISPATCH.  Return true when 
    we have found some duplicate labels and removed some edges.  */
 
 static bool
-lower_eh_dispatch (basic_block src, gimple stmt)
+lower_eh_dispatch (basic_block src, geh_dispatch *stmt)
 {
   gimple_stmt_iterator gsi;
   int region_nr;
@@ -3290,12 +3604,12 @@ lower_eh_dispatch (basic_block src, gimple stmt)
     {
     case ERT_TRY:
       {
-       VEC (tree, heap) *labels = NULL;
+       auto_vec<tree> labels;
        tree default_label = NULL;
        eh_catch c;
        edge_iterator ei;
        edge e;
-       struct pointer_set_t *seen_values = pointer_set_create ();
+       hash_set<tree> seen_values;
 
        /* Collect the labels for a switch.  Zero the post_landing_pad
           field becase we'll no longer have anything keeping these labels
@@ -3322,12 +3636,12 @@ lower_eh_dispatch (basic_block src, gimple stmt)
                   attached to the handler anymore, we remove 
                   the corresponding edge and then we delete unreachable 
                   blocks at the end of this pass.  */
-               if (! pointer_set_contains (seen_values, TREE_VALUE (flt_node)))
+               if (! seen_values.contains (TREE_VALUE (flt_node)))
                  {
                    tree t = build_case_label (TREE_VALUE (flt_node),
                                               NULL, lab);
-                   VEC_safe_push (tree, heap, labels, t);
-                   pointer_set_insert (seen_values, TREE_VALUE (flt_node));
+                   labels.safe_push (t);
+                   seen_values.add (TREE_VALUE (flt_node));
                    have_label = true;
                  }
 
@@ -3357,7 +3671,7 @@ lower_eh_dispatch (basic_block src, gimple stmt)
 
        /* Don't generate a switch if there's only a default case.
           This is common in the form of try { A; } catch (...) { B; }.  */
-       if (labels == NULL)
+       if (!labels.exists ())
          {
            e = single_succ_edge (src);
            e->flags |= EDGE_FALLTHRU;
@@ -3367,7 +3681,7 @@ lower_eh_dispatch (basic_block src, gimple stmt)
            fn = builtin_decl_implicit (BUILT_IN_EH_FILTER);
            x = gimple_build_call (fn, 1, build_int_cst (integer_type_node,
                                                         region_nr));
-           filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)), NULL);
+           filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)));
            filter = make_ssa_name (filter, x);
            gimple_call_set_lhs (x, filter);
            gsi_insert_before (&gsi, x, GSI_SAME_STMT);
@@ -3376,12 +3690,9 @@ lower_eh_dispatch (basic_block src, gimple stmt)
            default_label = build_case_label (NULL, NULL, default_label);
            sort_case_labels (labels);
 
-           x = gimple_build_switch_vec (filter, default_label, labels);
+           x = gimple_build_switch (filter, default_label, labels);
            gsi_insert_before (&gsi, x, GSI_SAME_STMT);
-
-           VEC_free (tree, heap, labels);
          }
-       pointer_set_destroy (seen_values);
       }
       break;
 
@@ -3393,7 +3704,7 @@ lower_eh_dispatch (basic_block src, gimple stmt)
        fn = builtin_decl_implicit (BUILT_IN_EH_FILTER);
        x = gimple_build_call (fn, 1, build_int_cst (integer_type_node,
                                                     region_nr));
-       filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)), NULL);
+       filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)));
        filter = make_ssa_name (filter, x);
        gimple_call_set_lhs (x, filter);
        gsi_insert_before (&gsi, x, GSI_SAME_STMT);
@@ -3419,8 +3730,36 @@ lower_eh_dispatch (basic_block src, gimple stmt)
   return redirected;
 }
 
-static unsigned
-execute_lower_eh_dispatch (void)
+namespace {
+
+const pass_data pass_data_lower_eh_dispatch =
+{
+  GIMPLE_PASS, /* type */
+  "ehdisp", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_EH, /* tv_id */
+  PROP_gimple_lcf, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_lower_eh_dispatch : public gimple_opt_pass
+{
+public:
+  pass_lower_eh_dispatch (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_lower_eh_dispatch, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  virtual bool gate (function *fun) { return fun->eh->region_tree != NULL; }
+  virtual unsigned int execute (function *);
+
+}; // class pass_lower_eh_dispatch
+
+unsigned
+pass_lower_eh_dispatch::execute (function *fun)
 {
   basic_block bb;
   int flags = 0;
@@ -3428,14 +3767,15 @@ execute_lower_eh_dispatch (void)
 
   assign_filter_values ();
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, fun)
     {
       gimple last = last_stmt (bb);
       if (last == NULL)
        continue;
       if (gimple_code (last) == GIMPLE_EH_DISPATCH)
        {
-         redirected |= lower_eh_dispatch (bb, last);
+         redirected |= lower_eh_dispatch (bb,
+                                          as_a <geh_dispatch *> (last));
          flags |= TODO_update_ssa_only_virtuals;
        }
       else if (gimple_code (last) == GIMPLE_RESX)
@@ -3452,112 +3792,143 @@ execute_lower_eh_dispatch (void)
   return flags;
 }
 
-static bool
-gate_lower_eh_dispatch (void)
-{
-  return cfun->eh->region_tree != NULL;
-}
+} // anon namespace
 
-struct gimple_opt_pass pass_lower_eh_dispatch =
+gimple_opt_pass *
+make_pass_lower_eh_dispatch (gcc::context *ctxt)
 {
- {
-  GIMPLE_PASS,
-  "ehdisp",                            /* name */
-  gate_lower_eh_dispatch,              /* gate */
-  execute_lower_eh_dispatch,           /* execute */
-  NULL,                                        /* sub */
-  NULL,                                        /* next */
-  0,                                   /* static_pass_number */
-  TV_TREE_EH,                          /* tv_id */
-  PROP_gimple_lcf,                     /* properties_required */
-  0,                                   /* properties_provided */
-  0,                                   /* properties_destroyed */
-  0,                                   /* todo_flags_start */
-  TODO_verify_flow                     /* todo_flags_finish */
- }
-};
+  return new pass_lower_eh_dispatch (ctxt);
+}
 \f
-/* Walk statements, see what regions are really referenced and remove
-   those that are unused.  */
+/* Walk statements, see what regions and, optionally, landing pads
+   are really referenced.
+   
+   Returns in R_REACHABLEP an sbitmap with bits set for reachable regions,
+   and in LP_REACHABLE an sbitmap with bits set for reachable landing pads.
+
+   Passing NULL for LP_REACHABLE is valid, in this case only reachable
+   regions are marked.
+
+   The caller is responsible for freeing the returned sbitmaps.  */
 
 static void
-remove_unreachable_handlers (void)
+mark_reachable_handlers (sbitmap *r_reachablep, sbitmap *lp_reachablep)
 {
   sbitmap r_reachable, lp_reachable;
-  eh_region region;
-  eh_landing_pad lp;
   basic_block bb;
-  int lp_nr, r_nr;
+  bool mark_landing_pads = (lp_reachablep != NULL);
+  gcc_checking_assert (r_reachablep != NULL);
+
+  r_reachable = sbitmap_alloc (cfun->eh->region_array->length ());
+  bitmap_clear (r_reachable);
+  *r_reachablep = r_reachable;
 
-  r_reachable = sbitmap_alloc (VEC_length (eh_region, cfun->eh->region_array));
-  lp_reachable
-    = sbitmap_alloc (VEC_length (eh_landing_pad, cfun->eh->lp_array));
-  sbitmap_zero (r_reachable);
-  sbitmap_zero (lp_reachable);
+  if (mark_landing_pads)
+    {
+      lp_reachable = sbitmap_alloc (cfun->eh->lp_array->length ());
+      bitmap_clear (lp_reachable);
+      *lp_reachablep = lp_reachable;
+    }
+  else
+    lp_reachable = NULL;
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_BB_FN (bb, cfun)
     {
       gimple_stmt_iterator gsi;
 
       for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
        {
          gimple stmt = gsi_stmt (gsi);
-         lp_nr = lookup_stmt_eh_lp (stmt);
-
-         /* Negative LP numbers are MUST_NOT_THROW regions which
-            are not considered BB enders.  */
-         if (lp_nr < 0)
-           SET_BIT (r_reachable, -lp_nr);
 
-         /* Positive LP numbers are real landing pads, are are BB enders.  */
-         else if (lp_nr > 0)
+         if (mark_landing_pads)
            {
-             gcc_assert (gsi_one_before_end_p (gsi));
-             region = get_eh_region_from_lp_number (lp_nr);
-             SET_BIT (r_reachable, region->index);
-             SET_BIT (lp_reachable, lp_nr);
+             int lp_nr = lookup_stmt_eh_lp (stmt);
+
+             /* Negative LP numbers are MUST_NOT_THROW regions which
+                are not considered BB enders.  */
+             if (lp_nr < 0)
+               bitmap_set_bit (r_reachable, -lp_nr);
+
+             /* Positive LP numbers are real landing pads, and BB enders.  */
+             else if (lp_nr > 0)
+               {
+                 gcc_assert (gsi_one_before_end_p (gsi));
+                 eh_region region = get_eh_region_from_lp_number (lp_nr);
+                 bitmap_set_bit (r_reachable, region->index);
+                 bitmap_set_bit (lp_reachable, lp_nr);
+               }
            }
 
          /* Avoid removing regions referenced from RESX/EH_DISPATCH.  */
          switch (gimple_code (stmt))
            {
            case GIMPLE_RESX:
-             SET_BIT (r_reachable, gimple_resx_region (stmt));
+             bitmap_set_bit (r_reachable,
+                             gimple_resx_region (as_a <gresx *> (stmt)));
              break;
            case GIMPLE_EH_DISPATCH:
-             SET_BIT (r_reachable, gimple_eh_dispatch_region (stmt));
+             bitmap_set_bit (r_reachable,
+                             gimple_eh_dispatch_region (
+                                as_a <geh_dispatch *> (stmt)));
+             break;
+           case GIMPLE_CALL:
+             if (gimple_call_builtin_p (stmt, BUILT_IN_EH_COPY_VALUES))
+               for (int i = 0; i < 2; ++i)
+                 {
+                   tree rt = gimple_call_arg (stmt, i);
+                   HOST_WIDE_INT ri = tree_to_shwi (rt);
+
+                   gcc_assert (ri = (int)ri);
+                   bitmap_set_bit (r_reachable, ri);
+                 }
              break;
            default:
              break;
            }
        }
     }
+}
+
+/* Remove unreachable handlers and unreachable landing pads.  */
+
+static void
+remove_unreachable_handlers (void)
+{
+  sbitmap r_reachable, lp_reachable;
+  eh_region region;
+  eh_landing_pad lp;
+  unsigned i;
+
+  mark_reachable_handlers (&r_reachable, &lp_reachable);
 
   if (dump_file)
     {
       fprintf (dump_file, "Before removal of unreachable regions:\n");
       dump_eh_tree (dump_file, cfun);
       fprintf (dump_file, "Reachable regions: ");
-      dump_sbitmap_file (dump_file, r_reachable);
+      dump_bitmap_file (dump_file, r_reachable);
       fprintf (dump_file, "Reachable landing pads: ");
-      dump_sbitmap_file (dump_file, lp_reachable);
+      dump_bitmap_file (dump_file, lp_reachable);
     }
 
-  for (r_nr = 1;
-       VEC_iterate (eh_region, cfun->eh->region_array, r_nr, region); ++r_nr)
-    if (region && !TEST_BIT (r_reachable, r_nr))
-      {
-       if (dump_file)
-         fprintf (dump_file, "Removing unreachable region %d\n", r_nr);
-       remove_eh_handler (region);
-      }
+  if (dump_file)
+    {
+      FOR_EACH_VEC_SAFE_ELT (cfun->eh->region_array, i, region)
+       if (region && !bitmap_bit_p (r_reachable, region->index))
+         fprintf (dump_file,
+                  "Removing unreachable region %d\n",
+                  region->index);
+    }
+
+  remove_unreachable_eh_regions (r_reachable);
 
-  for (lp_nr = 1;
-       VEC_iterate (eh_landing_pad, cfun->eh->lp_array, lp_nr, lp); ++lp_nr)
-    if (lp && !TEST_BIT (lp_reachable, lp_nr))
+  FOR_EACH_VEC_SAFE_ELT (cfun->eh->lp_array, i, lp)
+    if (lp && !bitmap_bit_p (lp_reachable, lp->index))
       {
        if (dump_file)
-         fprintf (dump_file, "Removing unreachable landing pad %d\n", lp_nr);
+         fprintf (dump_file,
+                  "Removing unreachable landing pad %d\n",
+                  lp->index);
        remove_eh_landing_pad (lp);
       }
 
@@ -3583,12 +3954,12 @@ void
 maybe_remove_unreachable_handlers (void)
 {
   eh_landing_pad lp;
-  int i;
+  unsigned i;
 
   if (cfun->eh == NULL)
     return;
-              
-  for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i)
+           
+  FOR_EACH_VEC_SAFE_ELT (cfun->eh->lp_array, i, lp)
     if (lp && lp->post_landing_pad)
       {
        if (label_to_block (lp->post_landing_pad) == NULL)
@@ -3601,45 +3972,38 @@ maybe_remove_unreachable_handlers (void)
 
 /* Remove regions that do not have landing pads.  This assumes
    that remove_unreachable_handlers has already been run, and
-   that we've just manipulated the landing pads since then.  */
+   that we've just manipulated the landing pads since then.
+
+   Preserve regions with landing pads and regions that prevent
+   exceptions from propagating further, even if these regions
+   are not reachable.  */
 
 static void
 remove_unreachable_handlers_no_lp (void)
 {
-  eh_region r;
-  int i;
+  eh_region region;
   sbitmap r_reachable;
-  basic_block bb;
+  unsigned i;
 
-  r_reachable = sbitmap_alloc (VEC_length (eh_region, cfun->eh->region_array));
-  sbitmap_zero (r_reachable);
+  mark_reachable_handlers (&r_reachable, /*lp_reachablep=*/NULL);
 
-  FOR_EACH_BB (bb)
+  FOR_EACH_VEC_SAFE_ELT (cfun->eh->region_array, i, region)
     {
-      gimple stmt = last_stmt (bb);
-      if (stmt)
-       /* Avoid removing regions referenced from RESX/EH_DISPATCH.  */
-       switch (gimple_code (stmt))
-         {
-         case GIMPLE_RESX:
-           SET_BIT (r_reachable, gimple_resx_region (stmt));
-           break;
-         case GIMPLE_EH_DISPATCH:
-           SET_BIT (r_reachable, gimple_eh_dispatch_region (stmt));
-           break;
-         default:
-           break;
-         }
+      if (! region)
+       continue;
+
+      if (region->landing_pads != NULL
+         || region->type == ERT_MUST_NOT_THROW)
+       bitmap_set_bit (r_reachable, region->index);
+
+      if (dump_file
+         && !bitmap_bit_p (r_reachable, region->index))
+       fprintf (dump_file,
+                "Removing unreachable region %d\n",
+                region->index);
     }
 
-  for (i = 1; VEC_iterate (eh_region, cfun->eh->region_array, i, r); ++i)
-    if (r && r->landing_pads == NULL && r->type != ERT_MUST_NOT_THROW
-       && !TEST_BIT (r_reachable, i))
-      {
-       if (dump_file)
-         fprintf (dump_file, "Removing unreachable region %d\n", i);
-       remove_eh_handler (r);
-      }
+  remove_unreachable_eh_regions (r_reachable);
 
   sbitmap_free (r_reachable);
 }
@@ -3667,10 +4031,10 @@ unsplit_eh (eh_landing_pad lp)
   edge e_in, e_out;
 
   /* Quickly check the edge counts on BB for singularity.  */
-  if (EDGE_COUNT (bb->preds) != 1 || EDGE_COUNT (bb->succs) != 1)
+  if (!single_pred_p (bb) || !single_succ_p (bb))
     return false;
-  e_in = EDGE_PRED (bb, 0);
-  e_out = EDGE_SUCC (bb, 0);
+  e_in = single_pred_edge (bb);
+  e_out = single_succ_edge (bb);
 
   /* Input edge must be EH and output edge must be normal.  */
   if ((e_in->flags & EDGE_EH) == 0 || (e_out->flags & EDGE_EH) != 0)
@@ -3687,13 +4051,13 @@ unsplit_eh (eh_landing_pad lp)
      for a different region.  */
   for (gsi = gsi_start_bb (e_out->dest); !gsi_end_p (gsi); gsi_next (&gsi))
     {
-      gimple stmt = gsi_stmt (gsi);
+      glabel *label_stmt = dyn_cast <glabel *> (gsi_stmt (gsi));
       tree lab;
       int lp_nr;
 
-      if (gimple_code (stmt) != GIMPLE_LABEL)
+      if (!label_stmt)
        break;
-      lab = gimple_label_label (stmt);
+      lab = gimple_label_label (label_stmt);
       lp_nr = EH_LANDING_PAD_NR (lab);
       if (lp_nr && get_eh_region_from_lp_number (lp_nr) != lp->region)
        return false;
@@ -3710,9 +4074,10 @@ unsplit_eh (eh_landing_pad lp)
      that doesn't appear to handle virtuals.  Propagate by hand.  */
   if (!gimple_seq_empty_p (phi_nodes (bb)))
     {
-      for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); )
+      for (gphi_iterator gpi = gsi_start_phis (bb); !gsi_end_p (gpi); )
        {
-         gimple use_stmt, phi = gsi_stmt (gsi);
+         gimple use_stmt;
+         gphi *phi = gpi.phi ();
          tree lhs = gimple_phi_result (phi);
          tree rhs = gimple_phi_arg_def (phi, 0);
          use_operand_p use_p;
@@ -3727,7 +4092,7 @@ unsplit_eh (eh_landing_pad lp)
          if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
            SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs) = 1;
 
-         remove_phi_node (&gsi, true);
+         remove_phi_node (&gpi, true);
        }
     }
 
@@ -3758,7 +4123,7 @@ unsplit_all_eh (void)
   eh_landing_pad lp;
   int i;
 
-  for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i)
+  for (i = 1; vec_safe_iterate (cfun->eh->lp_array, i, &lp); ++i)
     if (lp)
       changed |= unsplit_eh (lp);
 
@@ -3776,10 +4141,9 @@ static bool
 cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
                             edge old_bb_out, bool change_region)
 {
-  gimple_stmt_iterator ngsi, ogsi;
+  gphi_iterator ngsi, ogsi;
   edge_iterator ei;
   edge e;
-  bitmap rename_virts;
   bitmap ophi_handled;
 
   /* The destination block must not be a regular successor for any
@@ -3802,13 +4166,12 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
     redirect_edge_var_map_clear (e);
 
   ophi_handled = BITMAP_ALLOC (NULL);
-  rename_virts = BITMAP_ALLOC (NULL);
 
   /* First, iterate through the PHIs on NEW_BB and set up the edge_var_map
      for the edges we're going to move.  */
   for (ngsi = gsi_start_phis (new_bb); !gsi_end_p (ngsi); gsi_next (&ngsi))
     {
-      gimple ophi, nphi = gsi_stmt (ngsi);
+      gphi *ophi, *nphi = ngsi.phi ();
       tree nresult, nop;
 
       nresult = gimple_phi_result (nphi);
@@ -3819,7 +4182,7 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
       ophi = NULL;
       for (ogsi = gsi_start_phis (old_bb); !gsi_end_p (ogsi); gsi_next (&ogsi))
        {
-         ophi = gsi_stmt (ogsi);
+         ophi = ogsi.phi ();
          if (gimple_phi_result (ophi) == nop)
            break;
          ophi = NULL;
@@ -3855,11 +4218,7 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
              redirect_edge_var_map_add (e, nresult, oop, oloc);
            }
        }
-      /* If we didn't find the PHI, but it's a VOP, remember to rename
-        it later, assuming all other tests succeed.  */
-      else if (virtual_operand_p (nresult))
-       bitmap_set_bit (rename_virts, SSA_NAME_VERSION (nresult));
-      /* If we didn't find the PHI, and it's a real variable, we know
+      /* If we didn't find the PHI, if it's a real variable or a VOP, we know
         from the fact that OLD_BB is tree_empty_eh_handler_p that the
         variable is unchanged from input to the block and we can simply
         re-use the input to NEW_BB from the OLD_BB_OUT edge.  */
@@ -3876,30 +4235,12 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
      we don't know what values from the other edges into NEW_BB to use.  */
   for (ogsi = gsi_start_phis (old_bb); !gsi_end_p (ogsi); gsi_next (&ogsi))
     {
-      gimple ophi = gsi_stmt (ogsi);
+      gphi *ophi = ogsi.phi ();
       tree oresult = gimple_phi_result (ophi);
       if (!bitmap_bit_p (ophi_handled, SSA_NAME_VERSION (oresult)))
        goto fail;
     }
 
-  /* At this point we know that the merge will succeed.  Remove the PHI
-     nodes for the virtuals that we want to rename.  */
-  if (!bitmap_empty_p (rename_virts))
-    {
-      for (ngsi = gsi_start_phis (new_bb); !gsi_end_p (ngsi); )
-       {
-         gimple nphi = gsi_stmt (ngsi);
-         tree nresult = gimple_phi_result (nphi);
-         if (bitmap_bit_p (rename_virts, SSA_NAME_VERSION (nresult)))
-           {
-             mark_virtual_phi_result_for_renaming (nphi);
-             remove_phi_node (&ngsi, true);
-           }
-         else
-           gsi_next (&ngsi);
-       }
-    }
-
   /* Finally, move the edges and update the PHIs.  */
   for (ei = ei_start (old_bb->preds); (e = ei_safe_edge (ei)); )
     if (e->flags & EDGE_EH)
@@ -3911,13 +4252,11 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
           we may have created a loop with multiple latches.
           All of this isn't easily fixed thus cancel the affected loop
           and mark the other loop as possibly having multiple latches.  */
-       if (current_loops
-           && e->dest == e->dest->loop_father->header)
+       if (e->dest == e->dest->loop_father->header)
          {
-           e->dest->loop_father->header = NULL;
-           e->dest->loop_father->latch = NULL;
+           mark_loop_for_removal (e->dest->loop_father);
            new_bb->loop_father->latch = NULL;
-           loops_state_set (LOOPS_NEED_FIXUP|LOOPS_MAY_HAVE_MULTIPLE_LATCHES);
+           loops_state_set (LOOPS_MAY_HAVE_MULTIPLE_LATCHES);
          }
        redirect_eh_edge_1 (e, new_bb, change_region);
        redirect_edge_succ (e, new_bb);
@@ -3927,14 +4266,12 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
       ei_next (&ei);
 
   BITMAP_FREE (ophi_handled);
-  BITMAP_FREE (rename_virts);
   return true;
 
  fail:
   FOR_EACH_EDGE (e, ei, old_bb->preds)
     redirect_edge_var_map_clear (e);
   BITMAP_FREE (ophi_handled);
-  BITMAP_FREE (rename_virts);
   return false;
 }
 
@@ -3986,10 +4323,10 @@ cleanup_empty_eh_unsplit (basic_block bb, edge e_out, eh_landing_pad lp)
   lab = NULL;
   for (gsi = gsi_start_bb (e_out->dest); !gsi_end_p (gsi); gsi_next (&gsi))
     {
-      gimple stmt = gsi_stmt (gsi);
+      glabel *stmt = dyn_cast <glabel *> (gsi_stmt (gsi));
       int lp_nr;
 
-      if (gimple_code (stmt) != GIMPLE_LABEL)
+      if (!stmt)
        break;
       lab = gimple_label_label (stmt);
       lp_nr = EH_LANDING_PAD_NR (lab);
@@ -4072,7 +4409,7 @@ cleanup_empty_eh (eh_landing_pad lp)
       e_out = NULL;
       break;
     case 1:
-      e_out = EDGE_SUCC (bb, 0);
+      e_out = single_succ_edge (bb);
       break;
     default:
       return false;
@@ -4096,8 +4433,11 @@ cleanup_empty_eh (eh_landing_pad lp)
   /* If the block is totally empty, look for more unsplitting cases.  */
   if (gsi_end_p (gsi))
     {
-      /* For the degenerate case of an infinite loop bail out.  */
-      if (infinite_empty_loop_p (e_out))
+      /* For the degenerate case of an infinite loop bail out.
+        If bb has no successors and is totally empty, which can happen e.g.
+        because of incorrect noreturn attribute, bail out too.  */
+      if (e_out == NULL
+         || infinite_empty_loop_p (e_out))
        return ret;
 
       return ret | cleanup_empty_eh_unsplit (bb, e_out, lp);
@@ -4208,7 +4548,7 @@ cleanup_all_empty_eh (void)
   eh_landing_pad lp;
   int i;
 
-  for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i)
+  for (i = 1; vec_safe_iterate (cfun->eh->lp_array, i, &lp); ++i)
     if (lp)
       changed |= cleanup_empty_eh (lp);
 
@@ -4220,7 +4560,7 @@ cleanup_all_empty_eh (void)
     2) MUST_NOT_THROW regions that became dead because of 1) are optimized out
     3) Info about regions that are containing instructions, and regions
        reachable via local EH edges is collected
-    4) Eh tree is pruned for regions no longer neccesary.
+    4) Eh tree is pruned for regions no longer necessary.
 
    TODO: Push MUST_NOT_THROW regions to the root of the EH tree.
         Unify those that have the same failure decl and locus.
@@ -4234,11 +4574,12 @@ execute_cleanup_eh_1 (void)
   remove_unreachable_handlers ();
 
   /* Watch out for the region tree vanishing due to all unreachable.  */
-  if (cfun->eh->region_tree && optimize)
+  if (cfun->eh->region_tree)
     {
       bool changed = false;
 
-      changed |= unsplit_all_eh ();
+      if (optimize)
+       changed |= unsplit_all_eh ();
       changed |= cleanup_all_empty_eh ();
 
       if (changed)
@@ -4261,8 +4602,41 @@ execute_cleanup_eh_1 (void)
   return 0;
 }
 
-static unsigned int
-execute_cleanup_eh (void)
+namespace {
+
+const pass_data pass_data_cleanup_eh =
+{
+  GIMPLE_PASS, /* type */
+  "ehcleanup", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_EH, /* tv_id */
+  PROP_gimple_lcf, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_cleanup_eh : public gimple_opt_pass
+{
+public:
+  pass_cleanup_eh (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_cleanup_eh, ctxt)
+  {}
+
+  /* opt_pass methods: */
+  opt_pass * clone () { return new pass_cleanup_eh (m_ctxt); }
+  virtual bool gate (function *fun)
+    {
+      return fun->eh != NULL && fun->eh->region_tree != NULL;
+    }
+
+  virtual unsigned int execute (function *);
+
+}; // class pass_cleanup_eh
+
+unsigned int
+pass_cleanup_eh::execute (function *fun)
 {
   int ret = execute_cleanup_eh_1 ();
 
@@ -4270,35 +4644,19 @@ execute_cleanup_eh (void)
      clear it.  This exposes cross-language inlining opportunities
      and avoids references to a never defined personality routine.  */
   if (DECL_FUNCTION_PERSONALITY (current_function_decl)
-      && function_needs_eh_personality (cfun) != eh_personality_lang)
+      && function_needs_eh_personality (fun) != eh_personality_lang)
     DECL_FUNCTION_PERSONALITY (current_function_decl) = NULL_TREE;
 
   return ret;
 }
 
-static bool
-gate_cleanup_eh (void)
+} // anon namespace
+
+gimple_opt_pass *
+make_pass_cleanup_eh (gcc::context *ctxt)
 {
-  return cfun->eh != NULL && cfun->eh->region_tree != NULL;
+  return new pass_cleanup_eh (ctxt);
 }
-
-struct gimple_opt_pass pass_cleanup_eh = {
-  {
-   GIMPLE_PASS,
-   "ehcleanup",                        /* name */
-   gate_cleanup_eh,            /* gate */
-   execute_cleanup_eh,         /* execute */
-   NULL,                       /* sub */
-   NULL,                       /* next */
-   0,                          /* static_pass_number */
-   TV_TREE_EH,                 /* tv_id */
-   PROP_gimple_lcf,            /* properties_required */
-   0,                          /* properties_provided */
-   0,                          /* properties_destroyed */
-   0,                          /* todo_flags_start */
-   0                           /* todo_flags_finish */
-   }
-};
 \f
 /* Verify that BB containing STMT as the last statement, has precisely the
    edge that make_eh_edges would create.  */
@@ -4365,7 +4723,7 @@ verify_eh_edges (gimple stmt)
 /* Similarly, but handle GIMPLE_EH_DISPATCH specifically.  */
 
 DEBUG_FUNCTION bool
-verify_eh_dispatch_edge (gimple stmt)
+verify_eh_dispatch_edge (geh_dispatch *stmt)
 {
   eh_region r;
   eh_catch c;