OSDN Git Service

* config/mips/mips.md (store): Add attributes for QI and HI.
[pf3gnuchains/gcc-fork.git] / gcc / tree-eh.c
index 20e62ed..204bf7e 100644 (file)
@@ -344,6 +344,26 @@ outside_finally_tree (treemple start, gimple target)
    The eh region creation is straight-forward, but frobbing all the gotos
    and such into shape isn't.  */
 
+/* The GOTO_QUEUE is is an array of GIMPLE_GOTO and GIMPLE_RETURN
+   statements that are seen to escape this GIMPLE_TRY_FINALLY node.
+   The idea is to record a gimple statement for everything except for
+   the conditionals, which get their labels recorded. Since labels are
+   of type 'tree', we need this node to store both gimple and tree
+   objects.  REPL_STMT is the sequence used to replace the goto/return
+   statement.  CONT_STMT is used to store the statement that allows
+   the return/goto to jump to the original destination. */
+
+struct goto_queue_node
+{
+  treemple stmt;
+  gimple_seq repl_stmt;
+  gimple cont_stmt;
+  int index;
+  /* This is used when index >= 0 to indicate that stmt is a label (as
+     opposed to a goto stmt).  */
+  int is_label;
+};
+
 /* State of the world while lowering.  */
 
 struct leh_state
@@ -352,7 +372,6 @@ struct leh_state
      correspond to variables of the same name in cfun->eh, which we
      don't have easy access to.  */
   struct eh_region *cur_region;
-  struct eh_region *prev_try;
 
   /* Processing of TRY_FINALLY requires a bit more state.  This is
      split out into a separate structure so that we don't have to
@@ -378,23 +397,8 @@ struct leh_tf_state
   /* The exception region created for it.  */
   struct eh_region *region;
 
-  /* The GOTO_QUEUE is is an array of GIMPLE_GOTO and GIMPLE_RETURN statements
-     that are seen to escape this GIMPLE_TRY_FINALLY node.
-     The idea is to record a gimple statement for everything except for 
-     the conditionals, which get their labels recorded. Since labels are of
-     type 'tree', we need this node to store both gimple and tree objects.
-     REPL_STMT is the sequence used to replace the goto/return statement.
-     CONT_STMT is used to store the statement that allows the return/goto to
-     jump to the original destination. */
-  struct goto_queue_node {
-    treemple stmt;
-    gimple_seq repl_stmt;
-    gimple cont_stmt;
-    int index;
-    /* this is used when index >= 0 to indicate that stmt is a label(as
-       opposed to a goto stmt) */
-    int is_label;
-  } *goto_queue;
+  /* The goto queue.  */
+  struct goto_queue_node *goto_queue;
   size_t goto_queue_size;
   size_t goto_queue_active;
 
@@ -1566,12 +1570,11 @@ lower_try_finally (struct leh_state *state, gimple tp)
   this_tf.outer = state;
   if (using_eh_for_cleanups_p)
     this_tf.region
-      = gen_eh_region_cleanup (state->cur_region, state->prev_try);
+      = gen_eh_region_cleanup (state->cur_region);
   else
     this_tf.region = NULL;
 
   this_state.cur_region = this_tf.region;
-  this_state.prev_try = state->prev_try;
   this_state.tf = &this_tf;
 
   lower_eh_constructs_1 (&this_state, gimple_try_eval(tp));
@@ -1650,7 +1653,6 @@ lower_catch (struct leh_state *state, gimple tp)
 
   try_region = gen_eh_region_try (state->cur_region);
   this_state.cur_region = try_region;
-  this_state.prev_try = try_region;
   this_state.tf = state->tf;
 
   lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
@@ -1672,7 +1674,6 @@ lower_catch (struct leh_state *state, gimple tp)
                                           gimple_catch_types (gcatch));
 
       this_state.cur_region = catch_region;
-      this_state.prev_try = state->prev_try;
       lower_eh_constructs_1 (&this_state, gimple_catch_handler (gcatch));
 
       eh_label = create_artificial_label ();
@@ -1719,10 +1720,6 @@ lower_eh_filter (struct leh_state *state, gimple tp)
                                         gimple_eh_filter_types (inner));
   this_state = *state;
   this_state.cur_region = this_region;
-  /* For must not throw regions any cleanup regions inside it
-     can't reach outer catch regions.  */
-  if (gimple_eh_filter_must_not_throw (inner))
-    this_state.prev_try = NULL;
 
   lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
 
@@ -1759,7 +1756,7 @@ lower_cleanup (struct leh_state *state, gimple tp)
       return result;
     }
 
-  this_region = gen_eh_region_cleanup (state->cur_region, state->prev_try);
+  this_region = gen_eh_region_cleanup (state->cur_region);
   this_state = *state;
   this_state.cur_region = this_region;
 
@@ -1823,6 +1820,25 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi)
     {
     case GIMPLE_CALL:
     case GIMPLE_ASSIGN:
+      /* 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.  */
+      if (stmt_could_throw_p (stmt)
+         && gimple_has_lhs (stmt)
+         && !tree_could_throw_p (gimple_get_lhs (stmt))
+         && 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);
+         gimple s = gimple_build_assign (lhs, tmp);
+         gimple_set_location (s, gimple_location (stmt));
+         gimple_set_block (s, gimple_block (stmt));
+         gimple_set_lhs (stmt, tmp);
+         if (TREE_CODE (TREE_TYPE (tmp)) == COMPLEX_TYPE
+             || TREE_CODE (TREE_TYPE (tmp)) == VECTOR_TYPE)
+           DECL_GIMPLE_REG_P (tmp) = 1;
+         gsi_insert_after (gsi, s, GSI_SAME_STMT);
+       }
       /* Look for things that can throw exceptions, and record them.  */
       if (state->cur_region && stmt_could_throw_p (stmt))
        {
@@ -1943,7 +1959,7 @@ make_eh_edge (struct eh_region *region, void *data)
   src = gimple_bb (stmt);
   dst = label_to_block (lab);
 
-  make_edge (src, dst, EDGE_ABNORMAL | EDGE_EH);
+  make_edge (src, dst, EDGE_EH);
 }
 
 /* See if STMT is call that might be inlined.  */
@@ -1974,6 +1990,7 @@ make_eh_edges (gimple stmt)
   int region_nr;
   bool is_resx;
   bool inlinable = false;
+  basic_block bb;
 
   if (gimple_code (stmt) == GIMPLE_RESX)
     {
@@ -1990,6 +2007,57 @@ make_eh_edges (gimple stmt)
     }
 
   foreach_reachable_handler (region_nr, is_resx, inlinable, make_eh_edge, stmt);
+
+  /* Make CFG profile more consistent assuming that exception will resume to first
+     available EH handler.  In practice this makes little difference, but we get
+     fewer consistency errors in the dumps.  */
+  bb = gimple_bb (stmt);
+  if (is_resx && EDGE_COUNT (bb->succs))
+    EDGE_SUCC (bb, 0)->probability = REG_BR_PROB_BASE;
+}
+
+/* Redirect EH edge E to NEW_BB.  */
+
+edge
+redirect_eh_edge (edge e, basic_block new_bb)
+{
+  gimple stmt = gsi_stmt (gsi_last_bb (e->src));
+  int region_nr, new_region_nr;
+  bool is_resx;
+  bool inlinable = false;
+  tree label = gimple_block_label (new_bb);
+  struct eh_region *r;
+
+  if (gimple_code (stmt) == GIMPLE_RESX)
+    {
+      region_nr = gimple_resx_region (stmt);
+      is_resx = true;
+    }
+  else
+    {
+      region_nr = lookup_stmt_eh_region (stmt);
+      gcc_assert (region_nr >= 0);
+      is_resx = false;
+      inlinable = inlinable_call_p (stmt);
+    }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "Redirecting EH edge %i->%i to %i, region %i, resx %i\n",
+            e->src->index, e->dest->index, new_bb->index, region_nr, is_resx);
+  r = redirect_eh_edge_to_label (e, label, is_resx, inlinable, region_nr);
+  new_region_nr = get_eh_region_number (r);
+  if (new_region_nr != region_nr)
+    {
+      if (is_resx)
+        gimple_resx_set_region (stmt, new_region_nr);
+      else
+        {
+         remove_stmt_from_eh_region (stmt);
+         add_stmt_to_eh_region (stmt, new_region_nr);
+        }
+    }
+  e = ssa_redirect_edge (e, new_bb);
+  return e;
 }
 
 static bool mark_eh_edge_found_error;
@@ -2659,12 +2727,7 @@ tree_remove_unreachable_handlers (void)
   {
     gimple_stmt_iterator gsi;
     int region;
-    bool has_eh_preds = false;
-    edge e;
-    edge_iterator ei;
-
-    FOR_EACH_EDGE (e, ei, bb->preds) if (e->flags & EDGE_EH)
-      has_eh_preds = true;
+    bool has_eh_preds = bb_has_eh_pred (bb);
 
     for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
       {
@@ -2673,14 +2736,16 @@ tree_remove_unreachable_handlers (void)
        if (gimple_code (stmt) == GIMPLE_LABEL && has_eh_preds)
          {
            int uid = LABEL_DECL_UID (gimple_label_label (stmt));
-           if (uid <= cfun->cfg->last_label_uid)
-             {
-               int region = VEC_index (int, label_to_region, uid);
-               SET_BIT (reachable, region);
-             }
+           int region;
+
+           for (region = VEC_index (int, label_to_region, uid);
+                region; region = get_next_region_sharing_label (region))
+             SET_BIT (reachable, region);
          }
-       if (gimple_code (stmt) == RESX)
-         SET_BIT (reachable, gimple_resx_region (stmt));
+       if (gimple_code (stmt) == GIMPLE_RESX)
+         SET_BIT (reachable,
+                  VEC_index (eh_region, cfun->eh->region_array,
+                             gimple_resx_region (stmt))->region_number);
        if ((region = lookup_stmt_eh_region (stmt)) >= 0)
          SET_BIT (contains_stmt, region);
       }
@@ -2715,6 +2780,8 @@ tree_remove_unreachable_handlers (void)
    <<<exception object>>> = save_eptr.6351_663;
    <<<filter object>>> = save_filt.6352_662;
    resx 1
+
+   And various minor variants after DCE or copy propagation.
  */
 
 static int
@@ -2722,6 +2789,11 @@ tree_empty_eh_handler_p (basic_block bb)
 {
   gimple_stmt_iterator gsi;
   int region;
+  edge_iterator ei;
+  edge e;
+  use_operand_p imm_use;
+  gimple use_stmt;
+  bool found = false;
 
   gsi = gsi_last_bb (bb);
 
@@ -2736,106 +2808,406 @@ tree_empty_eh_handler_p (basic_block bb)
   gsi_prev (&gsi);
   if (gsi_end_p (gsi))
     return 0;
-  if (gimple_code (gsi_stmt (gsi)) != GIMPLE_ASSIGN)
-    return 0;
-  if (TREE_CODE (gimple_assign_lhs (gsi_stmt (gsi))) != FILTER_EXPR)
-    return 0;
+  if (gimple_code (gsi_stmt (gsi)) == GIMPLE_ASSIGN)
+    {
+      tree filter_tmp;
+      tree exc_ptr_tmp;
 
-  /* filter_object set.  */
-  gsi_prev (&gsi);
-  if (gsi_end_p (gsi))
-    return 0;
-  if (gimple_code (gsi_stmt (gsi)) != GIMPLE_ASSIGN)
-    return 0;
-  if (TREE_CODE (gimple_assign_lhs (gsi_stmt (gsi))) != EXC_PTR_EXPR)
-    return 0;
+      if (TREE_CODE (gimple_assign_lhs (gsi_stmt (gsi))) != FILTER_EXPR)
+       return 0;
+      filter_tmp = gimple_assign_rhs1 (gsi_stmt (gsi));
 
-  /* filter_object get.  */
-  gsi_prev (&gsi);
-  if (gsi_end_p (gsi))
-    return 0;
-  if (gimple_code (gsi_stmt (gsi)) != GIMPLE_ASSIGN)
-    return 0;
-  if (TREE_CODE (gimple_assign_rhs1 (gsi_stmt (gsi))) != EXC_PTR_EXPR)
-    return 0;
+      /* filter_object set.  */
+      gsi_prev (&gsi);
+      if (gsi_end_p (gsi))
+       return 0;
+      if (gimple_code (gsi_stmt (gsi)) != GIMPLE_ASSIGN)
+       return 0;
+      if (TREE_CODE (gimple_assign_lhs (gsi_stmt (gsi))) != EXC_PTR_EXPR)
+       return 0;
+      exc_ptr_tmp = gimple_assign_rhs1 (gsi_stmt (gsi));
+
+      /* exc_ptr get.  */
+      if (TREE_CODE (exc_ptr_tmp) != EXC_PTR_EXPR)
+       {
+         gsi_prev (&gsi);
+         if (gsi_end_p (gsi))
+           return 0;
+         if (gimple_code (gsi_stmt (gsi)) != GIMPLE_ASSIGN)
+           return 0;
+         if (TREE_CODE (gimple_assign_rhs1 (gsi_stmt (gsi))) != EXC_PTR_EXPR)
+           return 0;
+         if (exc_ptr_tmp != gimple_assign_lhs (gsi_stmt (gsi)))
+           return 0;
+         if (!single_imm_use (exc_ptr_tmp, &imm_use, &use_stmt))
+           return 0;
+       }
 
-  /* filter_object get.  */
-  gsi_prev (&gsi);
-  if (gsi_end_p (gsi))
-    return 0;
-  if (gimple_code (gsi_stmt (gsi)) != GIMPLE_ASSIGN)
-    return 0;
-  if (TREE_CODE (gimple_assign_rhs1 (gsi_stmt (gsi))) != FILTER_EXPR)
-    return 0;
+      /* filter_object get.  */
+      if (TREE_CODE (filter_tmp) != FILTER_EXPR)
+       {
+         gsi_prev (&gsi);
+         if (gsi_end_p (gsi))
+           return 0;
+         if (gimple_code (gsi_stmt (gsi)) != GIMPLE_ASSIGN)
+           return 0;
+         if (TREE_CODE (gimple_assign_rhs1 (gsi_stmt (gsi))) != FILTER_EXPR)
+           return 0;
+         if (filter_tmp != gimple_assign_lhs (gsi_stmt (gsi)))
+           return 0;
+         if (!single_imm_use (filter_tmp, &imm_use, &use_stmt))
+           return 0;
+       }
 
-  /* label.  */
-  gsi_prev (&gsi);
-  if (gsi_end_p (gsi))
+      /* label.  */
+      gsi_prev (&gsi);
+      if (gsi_end_p (gsi))
+       return 0;
+    }
+  if (gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL)
     return 0;
-  if (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
+
+  /* Be sure that there is at least on EH region reaching the block directly.
+     After EH edge redirection, it is possible that block is reached by one handler
+     but resumed by different.  */
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    if ((e->flags & EDGE_EH))
+      found = true;
+  if (found)
     return region;
-  else
-    return 0;
+  return 0;
+}
+
+/* Return true if it is possible to remove basic block BB and propagate
+   through PHIs.  
+
+   This means that every PHI in BB has all uses such that they are PHIs
+   of basic blocks reachable througt BB and they appears only in use
+   reachable by the edge from BB to the block contianing the use.  
+   
+   This is same as in merge-phi code, but in slightly more general setting
+   because BB can have multiple successors.  */
+
+static bool
+all_phis_safe_to_merge (basic_block bb)
+{
+  gimple_stmt_iterator si;
+  bool ok = true;
+
+  for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
+    {
+      gimple phi = gsi_stmt (si);
+      tree result = gimple_phi_result (phi);
+      gimple stmt;
+      use_operand_p imm_use;
+      imm_use_iterator imm_iter;
+
+      /* If the PHI's result is never used, then we can just
+        ignore it.  */
+      if (has_zero_uses (result))
+        continue;
+      /* We can always rebuild virtuals if needed.  */
+      if (!is_gimple_reg (result))
+       continue;
+      FOR_EACH_IMM_USE_STMT (stmt, imm_iter, result)
+        {
+         if (gimple_code (stmt) != GIMPLE_PHI)
+           {
+             if (dump_file && (dump_flags & TDF_DETAILS))
+               fprintf (dump_file,
+                        "PHI result has use in non-PHI statement.\n");
+             ok = false;
+             BREAK_FROM_IMM_USE_STMT (imm_iter);
+           }
+         else
+            FOR_EACH_IMM_USE_ON_STMT (imm_use, imm_iter)
+             {
+               edge e;
+              e = gimple_phi_arg_edge (stmt, PHI_ARG_INDEX_FROM_USE (imm_use));
+              if (e->src != bb)
+                {
+                 if (dump_file && (dump_flags & TDF_DETAILS))
+                   fprintf (dump_file, "PHI has use in PHI not reached from"
+                            "empty cleanup itself.\n");
+                 ok = false;
+                 break;
+                }
+             }
+          if (!ok)
+            BREAK_FROM_IMM_USE_STMT (imm_iter);
+        }
+       if (!ok)
+         return false;
+    }
+  return ok;
 }
 
 static bool dominance_info_invalidated;
 
+/* Information to pass into make_eh_edge_and_update_phi.  */
+
+struct update_info
+{
+  basic_block bb_to_remove, bb;
+  edge edge_to_remove;
+};
+
+/* DATA points to update-info structure.
+   Like make_eh_edge create EH edge from DATA->bb to basic block containing
+   handler of REGION.  In addition also update PHI operands by copying
+   operands from DATA->bb_to_remove.  */
+
+static void
+make_eh_edge_and_update_phi (struct eh_region *region, void *data)
+{
+  struct update_info *info = (struct update_info *) data;
+  edge e, e2;
+  tree lab;
+  basic_block src, dst;
+  gimple_stmt_iterator si;
+
+  lab = get_eh_region_tree_label (region);
+
+  src = info->bb;
+  dst = label_to_block (lab);
+
+  e = find_edge (src, dst);
+  if (e)
+    {
+      gcc_assert (e->flags & EDGE_EH);
+      e->aux = e;
+      return;
+    }
+  dominance_info_invalidated = true;
+  e2 = find_edge (info->bb_to_remove, dst);
+  e = make_edge (src, dst, EDGE_EH);
+  e->aux = e;
+  gcc_assert (e2);
+  for (si = gsi_start_phis (dst); !gsi_end_p (si); gsi_next (&si))
+    {
+      gimple phi = gsi_stmt (si);
+      tree use = USE_FROM_PTR (PHI_ARG_DEF_PTR_FROM_EDGE (phi, e2));
+      gimple def = (TREE_CODE (use) == SSA_NAME
+                   ? SSA_NAME_DEF_STMT (use) : NULL);
+
+      if (def && gimple_bb (def) == info->bb_to_remove)
+        {
+         use = USE_FROM_PTR (PHI_ARG_DEF_PTR_FROM_EDGE (def,
+                                                        info->edge_to_remove));
+         gcc_assert (info->bb_to_remove == info->edge_to_remove->dest);
+          def = TREE_CODE (use) == SSA_NAME ? SSA_NAME_DEF_STMT (use) : NULL;
+         gcc_assert (!def
+                     || gimple_bb (def) != info->bb_to_remove
+                     || !is_gimple_reg (use));
+        }
+      SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (phi, e), use);
+    }
+}
+
+/* Make EH edges corresponding to STMT while updating PHI nodes after removal
+   empty cleanup BB_TO_REMOVE joined to BB containing STMT
+   by EDGE_TO_REMOVE.
+
+   Return if EDGE_TO_REMOVE was really removed.  It might stay reachable when
+   not all EH regions are cleaned up.  */
+
+static bool
+update_eh_edges (gimple stmt, basic_block bb_to_remove, edge edge_to_remove)
+{
+  int region_nr;
+  bool is_resx;
+  bool inlinable = false;
+  struct update_info info;
+  edge_iterator ei;
+  edge e;
+  int probability_sum = 0;
+  bool removed = false;
+
+  info.bb_to_remove = bb_to_remove;
+  info.bb = gimple_bb (stmt);
+  info.edge_to_remove = edge_to_remove;
+
+  if (gimple_code (stmt) == GIMPLE_RESX)
+    {
+      region_nr = gimple_resx_region (stmt);
+      is_resx = true;
+    }
+  else
+    {
+      region_nr = lookup_stmt_eh_region (stmt);
+      is_resx = false;
+      inlinable = inlinable_call_p (stmt);
+    }
+
+  /* First add new edges as neccesary.  */
+  foreach_reachable_handler (region_nr, is_resx, inlinable,
+                            make_eh_edge_and_update_phi, &info);
+
+  /* And remove edges we didn't marked. */
+  for (ei = ei_start (info.bb->succs); (e = ei_safe_edge (ei)); )
+    {
+      if ((e->flags & EDGE_EH) && !e->aux)
+       {
+         dominance_info_invalidated = true;
+         if (e == edge_to_remove)
+           removed = true;
+         remove_edge (e);
+       }
+      else
+        {
+         e->aux = NULL;
+         probability_sum += e->probability;
+         ei_next (&ei);
+       }
+    }
+
+  /* Make CFG profile more consistent assuming that exception will resume to
+     first available EH handler.  In practice this makes little difference, but
+     we get fewer consistency errors in the dumps.  */
+  if (is_resx && EDGE_COUNT (info.bb->succs) && !probability_sum)
+    EDGE_SUCC (info.bb, 0)->probability = REG_BR_PROB_BASE;
+  return removed;
+}
+
 /* Look for basic blocks containing empty exception handler and remove them.
    This is similar to jump forwarding, just across EH edges.  */
 
 static bool
-cleanup_empty_eh (basic_block bb)
+cleanup_empty_eh (basic_block bb, VEC(int,heap) * label_to_region)
 {
   int region;
+  gimple_stmt_iterator si;
+  edge_iterator ei;
 
   /* When handler of EH region winds up to be empty, we can safely
      remove it.  This leads to inner EH regions to be redirected
      to outer one, if present in function. So we need to rebuild
      EH edges in all sources.   */
-  if ((region = tree_empty_eh_handler_p (bb)))
+  if ((region = tree_empty_eh_handler_p (bb))
+      && all_phis_safe_to_merge (bb))
     {
-      edge_iterator ei;
       edge e;
-      gimple_stmt_iterator si;
+      bool found = false, removed_some = false, has_non_eh_preds = false;
+      gimple_stmt_iterator gsi;
+
+      /* Look for all EH regions sharing label of this block.
+         If they are not same as REGION, remove them and replace them
+        by outer region of REGION.  Also note if REGION itself is one
+        of them.  */
 
-      remove_eh_region (region);
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+        if (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
+         {
+           int uid = LABEL_DECL_UID (gimple_label_label (gsi_stmt (gsi)));
+           int r = VEC_index (int, label_to_region, uid);
+           int next;
 
-      /* It is safe to mark symbol for renaming because we have abnormal PHI
-         here.  Once EH edges are made redirectable we might need to add here
-         similar updating as jump threading does.  */
+           while (r)
+             {
+               next = get_next_region_sharing_label (r);
+               if (r == region)
+                 found = true;
+               else
+                 {
+                    removed_some = true;
+                    remove_eh_region_and_replace_by_outer_of (r, region);
+                    if (dump_file && (dump_flags & TDF_DETAILS))
+                      fprintf (dump_file, "Empty EH handler %i removed and "
+                               "replaced by %i\n", r, region);
+                 }
+               r = next;
+             }
+         }
+       else
+         break;
 
-      for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
-       mark_sym_for_renaming (SSA_NAME_VAR (PHI_RESULT (gsi_stmt (si))));
+      gcc_assert (found || removed_some);
+      FOR_EACH_EDGE (e, ei, bb->preds)
+       if (!(e->flags & EDGE_EH))
+         has_non_eh_preds = true;
+
+      /* When block is empty EH cleanup, but it is reachable via non-EH code too,
+        we can not remove the region it is resumed via, because doing so will
+        lead to redirection of its RESX edges.
+
+        This case will be handled later after edge forwarding if the EH cleanup
+        is really dead.  */
+
+      if (found && !has_non_eh_preds)
+        {
+          if (dump_file && (dump_flags & TDF_DETAILS))
+            fprintf (dump_file, "Empty EH handler %i removed.\n", region);
+          remove_eh_region (region);
+       }
+      else if (!removed_some)
+        return false;
 
-      while ((e = ei_safe_edge (ei_start (bb->preds))))
+      for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
        {
          basic_block src = e->src;
-         gcc_assert (e->flags & EDGE_EH);
-         for (ei = ei_start (src->succs); (e = ei_safe_edge (ei));)
+         if (!(e->flags & EDGE_EH))
            {
-             if (e->flags & EDGE_EH)
-               {
-                 remove_edge (e);
-                 dominance_info_invalidated = true;
-               }
-             else
-               ei_next (&ei);
+             ei_next (&ei);
+             continue;
            }
-         if (!stmt_can_throw_internal (last_stmt (src)))
-           continue;
-         make_eh_edges (last_stmt (src));
-         FOR_EACH_EDGE (e, ei, src->succs) if (e->flags & EDGE_EH)
+         if (stmt_can_throw_internal (last_stmt (src)))
            {
-             dominance_info_invalidated = true;
-             for (si = gsi_start_phis (e->dest); !gsi_end_p (si);
-                  gsi_next (&si))
-               mark_sym_for_renaming (SSA_NAME_VAR
-                                      (PHI_RESULT (gsi_stmt (si))));
+             if (!update_eh_edges (last_stmt (src), bb, e))
+               ei_next (&ei);
+           }
+         else
+           remove_edge (e);
+       }
+
+      /* Verify that we eliminated all uses of PHI we are going to remove.
+         If we didn't, rebuild SSA on affected variable (this is allowed only
+         for virtuals).  */
+      for (si = gsi_start_phis (bb); !gsi_end_p (si); gsi_next (&si))
+        {
+          gimple phi = gsi_stmt (si);
+          tree result = gimple_phi_result (phi);
+          if (!has_zero_uses (result))
+           {
+              use_operand_p use_p;
+              imm_use_iterator iter;
+             gimple stmt;
+
+             FOR_EACH_IMM_USE_STMT (stmt, iter, result)
+               {
+                 /* We have use, see if it won't disappear after
+                    removing BB.  */
+                 if (gimple_bb (stmt) == bb)
+                   continue;
+                 if (gimple_code (stmt) == GIMPLE_PHI)
+                   {
+                     bool bad = false;
+
+                     FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+                       if (gimple_phi_arg_edge (stmt,
+                               PHI_ARG_INDEX_FROM_USE (use_p))->src != bb)
+                         {
+                           bad = true;
+                           break;
+                         }
+
+                     if (!bad)
+                       continue;
+                   }
+
+                 gcc_assert (!is_gimple_reg (result));
+                 mark_sym_for_renaming (SSA_NAME_VAR (result));
+                 /* As we are going to delete this block we will release all
+                    defs which makes the immediate uses on use stmts invalid.
+                    Avoid that by replacing all uses with the bare variable
+                    and updating the stmts.  */
+                 FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+                   SET_USE (use_p, SSA_NAME_VAR (result));
+                 update_stmt (stmt);
+               }
            }
        }
-      if (dump_file)
-       fprintf (dump_file, "Empty EH handler %i removed\n", region);
-      delete_basic_block (bb);
+      if (!ei_safe_edge (ei_start (bb->preds)))
+        delete_basic_block (bb);
       return true;
     }
   return false;
@@ -2855,6 +3227,7 @@ cleanup_eh (void)
 {
   bool changed = false;
   basic_block bb;
+  VEC(int,heap) * label_to_region;
   int i;
 
   if (!cfun->eh)
@@ -2865,23 +3238,28 @@ cleanup_eh (void)
       dump_eh_tree (dump_file, cfun);
     }
 
-  dominance_info_invalidated = false;
-  /* We cannot use FOR_EACH_BB, since the basic blocks may get removed.  */
-  for (i = NUM_FIXED_BLOCKS; i < last_basic_block; i++)
+  if (optimize)
     {
-      bb = BASIC_BLOCK (i);
-      if (bb)
-       changed |= cleanup_empty_eh (bb);
-    }
-  if (dominance_info_invalidated)
-    {
-      free_dominance_info (CDI_DOMINATORS);
-      free_dominance_info (CDI_POST_DOMINATORS);
-    }
+      label_to_region = label_to_region_map ();
+      dominance_info_invalidated = false;
+      /* We cannot use FOR_EACH_BB, since the basic blocks may get removed.  */
+      for (i = NUM_FIXED_BLOCKS; i < last_basic_block; i++)
+       {
+         bb = BASIC_BLOCK (i);
+         if (bb)
+           changed |= cleanup_empty_eh (bb, label_to_region);
+       }
+      VEC_free (int, heap, label_to_region);
+      if (dominance_info_invalidated)
+       {
+         free_dominance_info (CDI_DOMINATORS);
+         free_dominance_info (CDI_POST_DOMINATORS);
+       }
 
-  /* Removing contained cleanup can render MUST_NOT_THROW regions empty.  */
-  if (changed)
-    delete_unreachable_blocks ();
+      /* Removing contained cleanup can render MUST_NOT_THROW regions empty.  */
+      if (changed)
+       delete_unreachable_blocks ();
+    }
 
   tree_remove_unreachable_handlers ();
   if (dump_file)