Allow negative offset for UNSPEC_DTPOFF/UNSPEC_NTPOFF
[gcc.git] / gcc / tree-eh.c
index 440ac0f4082d42804db1c2fd3ed9c59549d5f6ea..ec74d9d4c41d9374e9041c1c4b05a21929c724fe 100644 (file)
@@ -28,16 +28,14 @@ along with GCC; see the file COPYING3.  If not see
 #include "except.h"
 #include "pointer-set.h"
 #include "tree-flow.h"
-#include "tree-dump.h"
 #include "tree-inline.h"
-#include "tree-iterator.h"
 #include "tree-pass.h"
-#include "timevar.h"
 #include "langhooks.h"
 #include "ggc.h"
 #include "diagnostic-core.h"
 #include "gimple.h"
 #include "target.h"
+#include "cfgloop.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. */
@@ -405,7 +403,7 @@ static gimple_seq lower_eh_must_not_throw (struct leh_state *, gimple);
 
 #define LARGE_GOTO_QUEUE 20
 
-static void lower_eh_constructs_1 (struct leh_state *state, gimple_seq seq);
+static void lower_eh_constructs_1 (struct leh_state *state, gimple_seq *seq);
 
 static gimple_seq
 find_goto_replacement (struct leh_tf_state *tf, treemple stmt)
@@ -480,7 +478,7 @@ replace_goto_queue_cond_clause (tree *tp, struct leh_tf_state *tf,
 /* The real work of replace_goto_queue.  Returns with TSI updated to
    point to the next statement.  */
 
-static void replace_goto_queue_stmt_list (gimple_seq, struct leh_tf_state *);
+static void replace_goto_queue_stmt_list (gimple_seq *, struct leh_tf_state *);
 
 static void
 replace_goto_queue_1 (gimple stmt, struct leh_tf_state *tf,
@@ -510,18 +508,18 @@ replace_goto_queue_1 (gimple stmt, struct leh_tf_state *tf,
       break;
 
     case GIMPLE_TRY:
-      replace_goto_queue_stmt_list (gimple_try_eval (stmt), tf);
-      replace_goto_queue_stmt_list (gimple_try_cleanup (stmt), tf);
+      replace_goto_queue_stmt_list (gimple_try_eval_ptr (stmt), tf);
+      replace_goto_queue_stmt_list (gimple_try_cleanup_ptr (stmt), tf);
       break;
     case GIMPLE_CATCH:
-      replace_goto_queue_stmt_list (gimple_catch_handler (stmt), tf);
+      replace_goto_queue_stmt_list (gimple_catch_handler_ptr (stmt), tf);
       break;
     case GIMPLE_EH_FILTER:
-      replace_goto_queue_stmt_list (gimple_eh_filter_failure (stmt), tf);
+      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 (stmt), tf);
-      replace_goto_queue_stmt_list (gimple_eh_else_e_body (stmt), tf);
+      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);
       break;
 
     default:
@@ -535,9 +533,9 @@ replace_goto_queue_1 (gimple stmt, struct leh_tf_state *tf,
 /* A subroutine of replace_goto_queue.  Handles GIMPLE_SEQ.  */
 
 static void
-replace_goto_queue_stmt_list (gimple_seq seq, struct leh_tf_state *tf)
+replace_goto_queue_stmt_list (gimple_seq *seq, struct leh_tf_state *tf)
 {
-  gimple_stmt_iterator gsi = gsi_start (seq);
+  gimple_stmt_iterator gsi = gsi_start (*seq);
 
   while (!gsi_end_p (gsi))
     replace_goto_queue_1 (gsi_stmt (gsi), tf, &gsi);
@@ -550,8 +548,8 @@ replace_goto_queue (struct leh_tf_state *tf)
 {
   if (tf->goto_queue_active == 0)
     return;
-  replace_goto_queue_stmt_list (tf->top_p_seq, tf);
-  replace_goto_queue_stmt_list (eh_seq, tf);
+  replace_goto_queue_stmt_list (&tf->top_p_seq, tf);
+  replace_goto_queue_stmt_list (&eh_seq, tf);
 }
 
 /* Add a new record to the goto queue contained in TF. NEW_STMT is the
@@ -730,9 +728,6 @@ do_return_redirection (struct goto_queue_node *q, tree finlab, gimple_seq mod)
 
   q->cont_stmt = q->stmt.g;
 
-  if (!q->repl_stmt)
-    q->repl_stmt = gimple_seq_alloc ();
-
   if (mod)
     gimple_seq_add_seq (&q->repl_stmt, mod);
 
@@ -749,8 +744,6 @@ do_goto_redirection (struct goto_queue_node *q, tree finlab, gimple_seq mod,
   gimple x;
 
   gcc_assert (q->is_label);
-  if (!q->repl_stmt)
-    q->repl_stmt = gimple_seq_alloc ();
 
   q->cont_stmt = gimple_build_goto (VEC_index (tree, tf->dest_array, q->index));
 
@@ -1049,13 +1042,13 @@ lower_try_finally_nofallthru (struct leh_state *state,
   if (eh_else)
     {
       finally = gimple_eh_else_n_body (eh_else);
-      lower_eh_constructs_1 (state, finally);
+      lower_eh_constructs_1 (state, &finally);
       gimple_seq_add_seq (&tf->top_p_seq, finally);
 
       if (tf->may_throw)
        {
          finally = gimple_eh_else_e_body (eh_else);
-         lower_eh_constructs_1 (state, finally);
+         lower_eh_constructs_1 (state, &finally);
 
          emit_post_landing_pad (&eh_seq, tf->region);
          gimple_seq_add_seq (&eh_seq, finally);
@@ -1063,7 +1056,7 @@ lower_try_finally_nofallthru (struct leh_state *state,
     }
   else
     {
-      lower_eh_constructs_1 (state, finally);
+      lower_eh_constructs_1 (state, &finally);
       gimple_seq_add_seq (&tf->top_p_seq, finally);
 
       if (tf->may_throw)
@@ -1104,7 +1097,7 @@ lower_try_finally_onedest (struct leh_state *state, struct leh_tf_state *tf)
        finally = gimple_eh_else_n_body (x);
     }
 
-  lower_eh_constructs_1 (state, finally);
+  lower_eh_constructs_1 (state, &finally);
 
   if (tf->may_throw)
     {
@@ -1192,7 +1185,7 @@ 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);
-      lower_eh_constructs_1 (state, seq);
+      lower_eh_constructs_1 (state, &seq);
       gimple_seq_add_seq (&new_stmt, seq);
 
       tmp = lower_try_finally_fallthru_label (tf);
@@ -1208,7 +1201,7 @@ lower_try_finally_copy (struct leh_state *state, struct leh_tf_state *tf)
        seq = gimple_eh_else_e_body (eh_else);
       else
        seq = lower_try_finally_dup_block (finally, state);
-      lower_eh_constructs_1 (state, seq);
+      lower_eh_constructs_1 (state, &seq);
 
       emit_post_landing_pad (&eh_seq, tf->region);
       gimple_seq_add_seq (&eh_seq, seq);
@@ -1258,7 +1251,7 @@ lower_try_finally_copy (struct leh_state *state, struct leh_tf_state *tf)
           gimple_seq_add_stmt (&new_stmt, x);
 
          seq = lower_try_finally_dup_block (finally, state);
-         lower_eh_constructs_1 (state, seq);
+         lower_eh_constructs_1 (state, &seq);
           gimple_seq_add_seq (&new_stmt, seq);
 
           gimple_seq_add_stmt (&new_stmt, q->cont_stmt);
@@ -1305,7 +1298,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
   int nlabels, ndests, j, last_case_index;
   tree last_case;
   VEC (tree,heap) *case_label_vec;
-  gimple_seq switch_body;
+  gimple_seq switch_body = NULL;
   gimple x, eh_else;
   tree tmp;
   gimple switch_stmt;
@@ -1316,7 +1309,6 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
   /* The location of the finally block.  */
   location_t finally_loc;
 
-  switch_body = gimple_seq_alloc ();
   finally = gimple_try_cleanup (tf->top_p);
   eh_else = get_eh_else (finally);
 
@@ -1325,12 +1317,11 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
 
   /* The location of the finally is either the last stmt in the finally
      block or the location of the TRY_FINALLY itself.  */
-  finally_loc = gimple_seq_last_stmt (tf->top_p_seq) != NULL ?
-    gimple_location (gimple_seq_last_stmt (tf->top_p_seq))
-    : tf_loc;
+  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);
+  lower_eh_constructs_1 (state, &finally);
 
   /* Prepare for switch statement generation.  */
   nlabels = VEC_length (tree, tf->dest_array);
@@ -1351,7 +1342,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
 
   /* Begin inserting code for getting to the finally block.  Things
      are done in this order to correspond to the sequence the code is
-     layed out.  */
+     laid out.  */
 
   if (tf->may_fallthru)
     {
@@ -1381,7 +1372,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
       if (tf->may_throw)
        {
          finally = gimple_eh_else_e_body (eh_else);
-         lower_eh_constructs_1 (state, finally);
+         lower_eh_constructs_1 (state, &finally);
 
          emit_post_landing_pad (&eh_seq, tf->region);
          gimple_seq_add_seq (&eh_seq, finally);
@@ -1425,12 +1416,10 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
      entrance through a particular edge. */
   for (; q < qe; ++q)
     {
-      gimple_seq mod;
+      gimple_seq mod = NULL;
       int switch_id;
       unsigned int case_index;
 
-      mod = gimple_seq_alloc ();
-
       if (q->index < 0)
        {
          x = gimple_build_assign (finally_tmp,
@@ -1498,8 +1487,8 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
 
   /* 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
@@ -1538,7 +1527,20 @@ decide_copy_try_finally (int ndests, bool may_throw, gimple_seq finally)
     }
 
   if (!optimize)
-    return ndests == 1;
+    {
+      gimple_stmt_iterator gsi;
+
+      if (ndests == 1)
+        return true;
+
+      for (gsi = gsi_start (finally); !gsi_end_p (gsi); gsi_next (&gsi))
+       {
+         gimple stmt = gsi_stmt (gsi);
+         if (!is_gimple_debug (stmt) && !gimple_clobber_p (stmt))
+           return false;
+       }
+      return true;
+    }
 
   /* Finally estimate N times, plus N gotos.  */
   f_estimate = count_insns_seq (finally, &eni_size_weights);
@@ -1609,7 +1611,7 @@ lower_try_finally (struct leh_state *state, gimple tp)
   old_eh_seq = eh_seq;
   eh_seq = NULL;
 
-  lower_eh_constructs_1 (&this_state, gimple_try_eval(tp));
+  lower_eh_constructs_1 (&this_state, gimple_try_eval_ptr (tp));
 
   /* Determine if the try block is escaped through the bottom.  */
   this_tf.may_fallthru = gimple_seq_may_fallthru (gimple_try_eval (tp));
@@ -1692,7 +1694,7 @@ lower_catch (struct leh_state *state, gimple tp)
   struct leh_state this_state = *state;
   gimple_stmt_iterator gsi;
   tree out_label;
-  gimple_seq new_seq;
+  gimple_seq new_seq, cleanup;
   gimple x;
   location_t try_catch_loc = gimple_location (tp);
 
@@ -1702,7 +1704,7 @@ lower_catch (struct leh_state *state, gimple tp)
       this_state.cur_region = try_region;
     }
 
-  lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
+  lower_eh_constructs_1 (&this_state, gimple_try_eval_ptr (tp));
 
   if (!eh_region_may_contain_throw (try_region))
     return gimple_try_eval (tp);
@@ -1715,7 +1717,8 @@ lower_catch (struct leh_state *state, gimple tp)
   this_state.ehp_region = try_region;
 
   out_label = NULL;
-  for (gsi = gsi_start (gimple_try_cleanup (tp));
+  cleanup = gimple_try_cleanup (tp);
+  for (gsi = gsi_start (cleanup);
        !gsi_end_p (gsi);
        gsi_next (&gsi))
     {
@@ -1727,7 +1730,7 @@ lower_catch (struct leh_state *state, gimple tp)
       c = gen_eh_region_catch (try_region, gimple_catch_types (gcatch));
 
       handler = gimple_catch_handler (gcatch);
-      lower_eh_constructs_1 (&this_state, handler);
+      lower_eh_constructs_1 (&this_state, &handler);
 
       c->label = create_artificial_label (UNKNOWN_LOCATION);
       x = gimple_build_label (c->label);
@@ -1773,7 +1776,7 @@ lower_eh_filter (struct leh_state *state, gimple tp)
       this_state.cur_region = this_region;
     }
 
-  lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
+  lower_eh_constructs_1 (&this_state, gimple_try_eval_ptr (tp));
 
   if (!eh_region_may_contain_throw (this_region))
     return gimple_try_eval (tp);
@@ -1789,7 +1792,7 @@ lower_eh_filter (struct leh_state *state, gimple tp)
   x = gimple_build_label (this_region->u.allowed.label);
   gimple_seq_add_stmt (&new_seq, x);
 
-  lower_eh_constructs_1 (&this_state, gimple_eh_filter_failure (inner));
+  lower_eh_constructs_1 (&this_state, gimple_eh_filter_failure_ptr (inner));
   gimple_seq_add_seq (&new_seq, gimple_eh_filter_failure (inner));
 
   gimple_try_set_cleanup (tp, new_seq);
@@ -1824,7 +1827,7 @@ lower_eh_must_not_throw (struct leh_state *state, gimple tp)
       this_state.cur_region = this_region;
     }
 
-  lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
+  lower_eh_constructs_1 (&this_state, gimple_try_eval_ptr (tp));
 
   return gimple_try_eval (tp);
 }
@@ -1847,7 +1850,7 @@ lower_cleanup (struct leh_state *state, gimple tp)
       this_state.cur_region = this_region;
     }
 
-  lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
+  lower_eh_constructs_1 (&this_state, gimple_try_eval_ptr (tp));
 
   if (cleanup_dead || !eh_region_may_contain_throw (this_region))
     return gimple_try_eval (tp);
@@ -1867,7 +1870,7 @@ lower_cleanup (struct leh_state *state, gimple tp)
     {
       /* In this case honor_protect_cleanup_actions had nothing to do,
         and we should process this normally.  */
-      lower_eh_constructs_1 (state, gimple_try_cleanup (tp));
+      lower_eh_constructs_1 (state, gimple_try_cleanup_ptr (tp));
       result = frob_into_branch_around (tp, this_region,
                                         fake_tf.fallthru_label);
     }
@@ -1950,7 +1953,7 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi)
       /* If the stmt can throw use a new temporary for the assignment
          to a LHS.  This makes sure the old value of the LHS is
         available on the EH edge.  Only do so for statements that
-        potentially fall thru (no noreturn calls e.g.), otherwise
+        potentially fall through (no noreturn calls e.g.), otherwise
         this new assignment might create fake fallthru regions.  */
       if (stmt_could_throw_p (stmt)
          && gimple_has_lhs (stmt)
@@ -1996,7 +1999,7 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi)
          if (!x)
            {
              replace = gimple_try_eval (stmt);
-             lower_eh_constructs_1 (state, replace);
+             lower_eh_constructs_1 (state, &replace);
            }
          else
            switch (gimple_code (x))
@@ -2043,10 +2046,10 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi)
 /* A helper to unwrap a gimple_seq and feed stmts to lower_eh_constructs_2. */
 
 static void
-lower_eh_constructs_1 (struct leh_state *state, gimple_seq seq)
+lower_eh_constructs_1 (struct leh_state *state, gimple_seq *pseq)
 {
   gimple_stmt_iterator gsi;
-  for (gsi = gsi_start (seq); !gsi_end_p (gsi);)
+  for (gsi = gsi_start (*pseq); !gsi_end_p (gsi);)
     lower_eh_constructs_2 (state, &gsi);
 }
 
@@ -2065,7 +2068,8 @@ lower_eh_constructs (void)
   memset (&null_state, 0, sizeof (null_state));
 
   collect_finally_tree_1 (bodyp, NULL);
-  lower_eh_constructs_1 (&null_state, bodyp);
+  lower_eh_constructs_1 (&null_state, &bodyp);
+  gimple_set_body (current_function_decl, bodyp);
 
   /* We assume there's a return statement, or something, at the end of
      the function, and thus ploping the EH sequence afterward won't
@@ -2519,7 +2523,7 @@ tree_could_trap_p (tree expr)
          if (!DECL_EXTERNAL (expr))
            return false;
          node = cgraph_function_node (cgraph_get_node (expr), NULL);
-         if (node && node->in_other_partition)
+         if (node && node->symbol.in_other_partition)
            return false;
          return true;
        }
@@ -2535,7 +2539,7 @@ tree_could_trap_p (tree expr)
          if (!DECL_EXTERNAL (expr))
            return false;
          node = varpool_variable_node (varpool_get_node (expr), NULL);
-         if (node && node->in_other_partition)
+         if (node && node->symbol.in_other_partition)
            return false;
          return true;
        }
@@ -2740,7 +2744,7 @@ maybe_clean_or_replace_eh_stmt (gimple old_stmt, gimple new_stmt)
   return false;
 }
 
-/* Given a statement OLD_STMT in OLD_FUN and a duplicate statment NEW_STMT
+/* Given a statement OLD_STMT in OLD_FUN and a duplicate statement NEW_STMT
    in NEW_FUN, copy the EH table data from OLD_STMT to NEW_STMT.  The MAP
    operand is the return value of duplicate_eh_regions.  */
 
@@ -2860,8 +2864,10 @@ optimize_double_finally (gimple one, gimple two)
 {
   gimple oneh;
   gimple_stmt_iterator gsi;
+  gimple_seq cleanup;
 
-  gsi = gsi_start (gimple_try_cleanup (one));
+  cleanup = gimple_try_cleanup (one);
+  gsi = gsi_start (cleanup);
   if (!gsi_one_before_end_p (gsi))
     return;
 
@@ -3028,6 +3034,8 @@ lower_resx (basic_block bb, gimple stmt, struct pointer_map_t *mnt_map)
              gimple_stmt_iterator gsi2;
 
              new_bb = create_empty_bb (bb);
+             if (current_loops)
+               add_bb_to_loop (new_bb, bb->loop_father);
              lab = gimple_block_label (new_bb);
              gsi2 = gsi_start_bb (new_bb);
 
@@ -3173,6 +3181,92 @@ struct gimple_opt_pass pass_lower_resx =
  }
 };
 
+/* Try to optimize var = {v} {CLOBBER} stmts followed just by
+   external throw.  */
+
+static void
+optimize_clobbers (basic_block bb)
+{
+  gimple_stmt_iterator gsi = gsi_last_bb (bb);
+  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;
+      unlink_stmt_vdef (stmt);
+      gsi_remove (&gsi, true);
+      release_defs (stmt);
+    }
+}
+
+/* Try to sink var = {v} {CLOBBER} stmts followed just by
+   internal throw to successor BB.  */
+
+static int
+sink_clobbers (basic_block bb)
+{
+  edge e;
+  edge_iterator ei;
+  gimple_stmt_iterator gsi, dgsi;
+  basic_block succbb;
+  bool any_clobbers = false;
+
+  /* Only optimize if BB has a single EH successor and
+     all predecessor edges are EH too.  */
+  if (!single_succ_p (bb)
+      || (single_succ_edge (bb)->flags & EDGE_EH) == 0)
+    return 0;
+
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      if ((e->flags & EDGE_EH) == 0)
+       return 0;
+    }
+
+  /* And BB contains only CLOBBER stmts before the final
+     RESX.  */
+  gsi = gsi_last_bb (bb);
+  for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi))
+    {
+      gimple stmt = gsi_stmt (gsi);
+      if (is_gimple_debug (stmt))
+       continue;
+      if (gimple_code (stmt) == GIMPLE_LABEL)
+       break;
+      if (!gimple_clobber_p (stmt)
+         || TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME)
+       return 0;
+      any_clobbers = true;
+    }
+  if (!any_clobbers)
+    return 0;
+
+  succbb = single_succ (bb);
+  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);
+      if (is_gimple_debug (stmt))
+       continue;
+      if (gimple_code (stmt) == GIMPLE_LABEL)
+       break;
+      unlink_stmt_vdef (stmt);
+      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);
+    }
+
+  return TODO_update_ssa_only_virtuals;
+}
 
 /* At the end of inlining, we can lower EH_DISPATCH.  Return true when 
    we have found some duplicate labels and removed some edges.  */
@@ -3205,7 +3299,7 @@ lower_eh_dispatch (basic_block src, gimple stmt)
 
        /* Collect the labels for a switch.  Zero the post_landing_pad
           field becase we'll no longer have anything keeping these labels
-          in existance and the optimizer will be free to merge these
+          in existence and the optimizer will be free to merge these
           blocks at will.  */
        for (c = r->u.eh_try.first_catch; c ; c = c->next_catch)
          {
@@ -3282,7 +3376,7 @@ 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);
@@ -3329,7 +3423,7 @@ static unsigned
 execute_lower_eh_dispatch (void)
 {
   basic_block bb;
-  bool any_rewritten = false;
+  int flags = 0;
   bool redirected = false;
 
   assign_filter_values ();
@@ -3337,16 +3431,25 @@ execute_lower_eh_dispatch (void)
   FOR_EACH_BB (bb)
     {
       gimple last = last_stmt (bb);
-      if (last && gimple_code (last) == GIMPLE_EH_DISPATCH)
+      if (last == NULL)
+       continue;
+      if (gimple_code (last) == GIMPLE_EH_DISPATCH)
        {
          redirected |= lower_eh_dispatch (bb, last);
-         any_rewritten = true;
+         flags |= TODO_update_ssa_only_virtuals;
+       }
+      else if (gimple_code (last) == GIMPLE_RESX)
+       {
+         if (stmt_can_throw_external (last))
+           optimize_clobbers (bb);
+         else
+           flags |= sink_clobbers (bb);
        }
     }
 
   if (redirected)
     delete_unreachable_blocks ();
-  return any_rewritten ? TODO_update_ssa_only_virtuals : 0;
+  return flags;
 }
 
 static bool
@@ -3473,6 +3576,29 @@ remove_unreachable_handlers (void)
 #endif
 }
 
+/* Remove unreachable handlers if any landing pads have been removed after
+   last ehcleanup pass (due to gimple_purge_dead_eh_edges).  */
+
+void
+maybe_remove_unreachable_handlers (void)
+{
+  eh_landing_pad lp;
+  int i;
+
+  if (cfun->eh == NULL)
+    return;
+              
+  for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i)
+    if (lp && lp->post_landing_pad)
+      {
+       if (label_to_block (lp->post_landing_pad) == NULL)
+         {
+           remove_unreachable_handlers ();
+           return;
+         }
+      }
+}
+
 /* 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.  */
@@ -3482,14 +3608,40 @@ remove_unreachable_handlers_no_lp (void)
 {
   eh_region r;
   int i;
+  sbitmap r_reachable;
+  basic_block bb;
+
+  r_reachable = sbitmap_alloc (VEC_length (eh_region, cfun->eh->region_array));
+  sbitmap_zero (r_reachable);
+
+  FOR_EACH_BB (bb)
+    {
+      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;
+         }
+    }
 
   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)
+    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);
       }
+
+  sbitmap_free (r_reachable);
 }
 
 /* Undo critical edge splitting on an EH landing pad.  Earlier, we
@@ -3630,6 +3782,22 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
   bitmap rename_virts;
   bitmap ophi_handled;
 
+  /* The destination block must not be a regular successor for any
+     of the preds of the landing pad.  Thus, avoid turning
+        <..>
+        |  \ EH
+        |  <..>
+        |  /
+       <..>
+     into
+        <..>
+       |  | EH
+       <..>
+     which CFG verification would choke on.  See PR45172 and PR51089.  */
+  FOR_EACH_EDGE (e, ei, old_bb->preds)
+    if (find_edge (e->src, new_bb))
+      return false;
+
   FOR_EACH_EDGE (e, ei, old_bb->preds)
     redirect_edge_var_map_clear (e);
 
@@ -3689,7 +3857,7 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
        }
       /* If we didn't find the PHI, but it's a VOP, remember to rename
         it later, assuming all other tests succeed.  */
-      else if (!is_gimple_reg (nresult))
+      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
         from the fact that OLD_BB is tree_empty_eh_handler_p that the
@@ -3736,6 +3904,21 @@ cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
   for (ei = ei_start (old_bb->preds); (e = ei_safe_edge (ei)); )
     if (e->flags & EDGE_EH)
       {
+       /* ???  CFG manipluation routines do not try to update loop
+          form on edge redirection.  Do so manually here for now.  */
+       /* If we redirect a loop entry or latch edge that will either create
+          a multiple entry loop or rotate the loop.  If the loops merge
+          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)
+         {
+           e->dest->loop_father->header = NULL;
+           e->dest->loop_father->latch = NULL;
+           new_bb->loop_father->latch = NULL;
+           loops_state_set (LOOPS_NEED_FIXUP|LOOPS_MAY_HAVE_MULTIPLE_LATCHES);
+         }
        redirect_eh_edge_1 (e, new_bb, change_region);
        redirect_edge_succ (e, new_bb);
        flush_pending_stmts (e);
@@ -3775,7 +3958,7 @@ cleanup_empty_eh_move_lp (basic_block bb, edge e_out,
 
   /* Delete the RESX that was matched within the empty handler block.  */
   gsi = gsi_last_bb (bb);
-  mark_virtual_ops_for_renaming (gsi_stmt (gsi));
+  unlink_stmt_vdef (gsi_stmt (gsi));
   gsi_remove (&gsi, true);
 
   /* Clean up E_OUT for the fallthru.  */
@@ -3792,8 +3975,6 @@ cleanup_empty_eh_unsplit (basic_block bb, edge e_out, eh_landing_pad lp)
 {
   gimple_stmt_iterator gsi;
   tree lab;
-  edge_iterator ei;
-  edge e;
 
   /* We really ought not have totally lost everything following
      a landing pad label.  Given that BB is empty, there had better
@@ -3816,22 +3997,6 @@ cleanup_empty_eh_unsplit (basic_block bb, edge e_out, eh_landing_pad lp)
        return false;
     }
 
-  /* The destination block must not be a regular successor for any
-     of the preds of the landing pad.  Thus, avoid turning
-        <..>
-        |  \ EH
-        |  <..>
-        |  /
-       <..>
-     into
-        <..>
-       |  | EH
-       <..>
-     which CFG verification would choke on.  See PR45172.  */
-  FOR_EACH_EDGE (e, ei, bb->preds)
-    if (find_edge (e->src, e_out->dest))
-      return false;
-
   /* Attempt to move the PHIs into the successor block.  */
   if (cleanup_empty_eh_merge_phis (e_out->dest, bb, e_out, false))
     {
@@ -3897,6 +4062,7 @@ cleanup_empty_eh (eh_landing_pad lp)
   edge_iterator ei;
   edge e, e_out;
   bool has_non_eh_pred;
+  bool ret = false;
   int new_lp_nr;
 
   /* There can be zero or one edges out of BB.  This is the quickest test.  */
@@ -3911,6 +4077,16 @@ cleanup_empty_eh (eh_landing_pad lp)
     default:
       return false;
     }
+
+  resx = last_stmt (bb);
+  if (resx && is_gimple_resx (resx))
+    {
+      if (stmt_can_throw_external (resx))
+       optimize_clobbers (bb);
+      else if (sink_clobbers (bb))
+       ret = true;
+    }
+
   gsi = gsi_after_labels (bb);
 
   /* Make sure to skip debug statements.  */
@@ -3922,9 +4098,9 @@ cleanup_empty_eh (eh_landing_pad lp)
     {
       /* For the degenerate case of an infinite loop bail out.  */
       if (infinite_empty_loop_p (e_out))
-       return false;
+       return ret;
 
-      return cleanup_empty_eh_unsplit (bb, e_out, lp);
+      return ret | cleanup_empty_eh_unsplit (bb, e_out, lp);
     }
 
   /* The block should consist only of a single RESX statement, modulo a
@@ -3937,7 +4113,7 @@ cleanup_empty_eh (eh_landing_pad lp)
       resx = gsi_stmt (gsi);
     }
   if (!is_gimple_resx (resx))
-    return false;
+    return ret;
   gcc_assert (gsi_one_before_end_p (gsi));
 
   /* Determine if there are non-EH edges, or resx edges into the handler.  */
@@ -4013,7 +4189,7 @@ cleanup_empty_eh (eh_landing_pad lp)
       return true;
     }
 
-  return false;
+  return ret;
 
  succeed:
   if (dump_file && (dump_flags & TDF_DETAILS))