+\f
+/* Walk statements, see what regions are really referenced and remove
+ those that are unused. */
+
+static void
+remove_unreachable_handlers (void)
+{
+ sbitmap r_reachable, lp_reachable;
+ eh_region region;
+ eh_landing_pad lp;
+ basic_block bb;
+ int lp_nr, r_nr;
+
+ 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);
+
+ FOR_EACH_BB (bb)
+ {
+ 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)
+ {
+ 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);
+ }
+
+ /* 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 (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);
+ fprintf (dump_file, "Reachable landing pads: ");
+ dump_sbitmap_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);
+ }
+
+ 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))
+ {
+ if (dump_file)
+ fprintf (dump_file, "Removing unreachable landing pad %d\n", lp_nr);
+ remove_eh_landing_pad (lp);
+ }
+
+ if (dump_file)
+ {
+ fprintf (dump_file, "\n\nAfter removal of unreachable regions:\n");
+ dump_eh_tree (dump_file, cfun);
+ fprintf (dump_file, "\n\n");
+ }
+
+ sbitmap_free (r_reachable);
+ sbitmap_free (lp_reachable);
+
+#ifdef ENABLE_CHECKING
+ verify_eh_tree (cfun);
+#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. */
+
+static void
+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
+ && !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
+ optimisticaly split all sorts of edges, including EH edges. The
+ optimization passes in between may not have needed them; if not,
+ we should undo the split.
+
+ Recognize this case by having one EH edge incoming to the BB and
+ one normal edge outgoing; BB should be empty apart from the
+ post_landing_pad label.
+
+ Note that this is slightly different from the empty handler case
+ handled by cleanup_empty_eh, in that the actual handler may yet
+ have actual code but the landing pad has been separated from the
+ handler. As such, cleanup_empty_eh relies on this transformation
+ having been done first. */
+
+static bool
+unsplit_eh (eh_landing_pad lp)
+{
+ basic_block bb = label_to_block (lp->post_landing_pad);
+ gimple_stmt_iterator gsi;
+ 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)
+ return false;
+ e_in = EDGE_PRED (bb, 0);
+ e_out = EDGE_SUCC (bb, 0);
+
+ /* Input edge must be EH and output edge must be normal. */
+ if ((e_in->flags & EDGE_EH) == 0 || (e_out->flags & EDGE_EH) != 0)
+ return false;
+
+ /* The block must be empty except for the labels and debug insns. */
+ gsi = gsi_after_labels (bb);
+ if (!gsi_end_p (gsi) && is_gimple_debug (gsi_stmt (gsi)))
+ gsi_next_nondebug (&gsi);
+ if (!gsi_end_p (gsi))
+ return false;
+
+ /* The destination block must not already have a landing pad
+ for a different region. */
+ for (gsi = gsi_start_bb (e_out->dest); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+ tree lab;
+ int lp_nr;
+
+ if (gimple_code (stmt) != GIMPLE_LABEL)
+ break;
+ lab = gimple_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;
+ }
+
+ /* The new destination block must not already be a destination of
+ the source block, lest we merge fallthru and eh edges and get
+ all sorts of confused. */
+ if (find_edge (e_in->src, e_out->dest))
+ return false;
+
+ /* ??? We can get degenerate phis due to cfg cleanups. I would have
+ thought this should have been cleaned up by a phicprop pass, but
+ 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); )
+ {
+ gimple use_stmt, phi = gsi_stmt (gsi);
+ tree lhs = gimple_phi_result (phi);
+ tree rhs = gimple_phi_arg_def (phi, 0);
+ use_operand_p use_p;
+ imm_use_iterator iter;
+
+ FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
+ {
+ FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
+ SET_USE (use_p, rhs);
+ }
+
+ if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
+ SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs) = 1;
+
+ remove_phi_node (&gsi, true);
+ }
+ }
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Unsplit EH landing pad %d to block %i.\n",
+ lp->index, e_out->dest->index);
+
+ /* Redirect the edge. Since redirect_eh_edge_1 expects to be moving
+ a successor edge, humor it. But do the real CFG change with the
+ predecessor of E_OUT in order to preserve the ordering of arguments
+ to the PHI nodes in E_OUT->DEST. */
+ redirect_eh_edge_1 (e_in, e_out->dest, false);
+ redirect_edge_pred (e_out, e_in->src);
+ e_out->flags = e_in->flags;
+ e_out->probability = e_in->probability;
+ e_out->count = e_in->count;
+ remove_edge (e_in);
+
+ return true;
+}
+
+/* Examine each landing pad block and see if it matches unsplit_eh. */
+
+static bool
+unsplit_all_eh (void)
+{
+ bool changed = false;
+ eh_landing_pad lp;
+ int i;
+
+ for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i)
+ if (lp)
+ changed |= unsplit_eh (lp);
+
+ return changed;
+}
+
+/* A subroutine of cleanup_empty_eh. Redirect all EH edges incoming
+ to OLD_BB to NEW_BB; return true on success, false on failure.
+
+ OLD_BB_OUT is the edge into NEW_BB from OLD_BB, so if we miss any
+ PHI variables from OLD_BB we can pick them up from OLD_BB_OUT.
+ Virtual PHIs may be deleted and marked for renaming. */
+
+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;
+ edge_iterator ei;
+ edge e;
+ 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);
+
+ 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);
+ tree nresult, nop;
+
+ nresult = gimple_phi_result (nphi);
+ nop = gimple_phi_arg_def (nphi, old_bb_out->dest_idx);
+
+ /* Find the corresponding PHI in OLD_BB so we can forward-propagate
+ the source ssa_name. */
+ ophi = NULL;
+ for (ogsi = gsi_start_phis (old_bb); !gsi_end_p (ogsi); gsi_next (&ogsi))
+ {
+ ophi = gsi_stmt (ogsi);
+ if (gimple_phi_result (ophi) == nop)
+ break;
+ ophi = NULL;
+ }
+
+ /* If we did find the corresponding PHI, copy those inputs. */
+ if (ophi)
+ {
+ /* If NOP is used somewhere else beyond phis in new_bb, give up. */
+ if (!has_single_use (nop))
+ {
+ imm_use_iterator imm_iter;
+ use_operand_p use_p;
+
+ FOR_EACH_IMM_USE_FAST (use_p, imm_iter, nop)
+ {
+ if (!gimple_debug_bind_p (USE_STMT (use_p))
+ && (gimple_code (USE_STMT (use_p)) != GIMPLE_PHI
+ || gimple_bb (USE_STMT (use_p)) != new_bb))
+ goto fail;
+ }
+ }
+ bitmap_set_bit (ophi_handled, SSA_NAME_VERSION (nop));
+ FOR_EACH_EDGE (e, ei, old_bb->preds)
+ {
+ location_t oloc;
+ tree oop;
+
+ if ((e->flags & EDGE_EH) == 0)
+ continue;
+ oop = gimple_phi_arg_def (ophi, e->dest_idx);
+ oloc = gimple_phi_arg_location (ophi, e->dest_idx);
+ 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 (!is_gimple_reg (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
+ 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. */
+ else
+ {
+ location_t nloc
+ = gimple_phi_arg_location (nphi, old_bb_out->dest_idx);
+ FOR_EACH_EDGE (e, ei, old_bb->preds)
+ redirect_edge_var_map_add (e, nresult, nop, nloc);
+ }
+ }
+
+ /* Second, verify that all PHIs from OLD_BB have been handled. If not,
+ 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);
+ 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)
+ {
+ redirect_eh_edge_1 (e, new_bb, change_region);
+ redirect_edge_succ (e, new_bb);
+ flush_pending_stmts (e);
+ }
+ else
+ 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;
+}
+
+/* A subroutine of cleanup_empty_eh. Move a landing pad LP from its
+ old region to NEW_REGION at BB. */
+
+static void
+cleanup_empty_eh_move_lp (basic_block bb, edge e_out,
+ eh_landing_pad lp, eh_region new_region)
+{
+ gimple_stmt_iterator gsi;
+ eh_landing_pad *pp;
+
+ for (pp = &lp->region->landing_pads; *pp != lp; pp = &(*pp)->next_lp)
+ continue;
+ *pp = lp->next_lp;
+
+ lp->region = new_region;
+ lp->next_lp = new_region->landing_pads;
+ new_region->landing_pads = lp;
+
+ /* Delete the RESX that was matched within the empty handler block. */
+ gsi = gsi_last_bb (bb);
+ mark_virtual_ops_for_renaming (gsi_stmt (gsi));
+ gsi_remove (&gsi, true);
+
+ /* Clean up E_OUT for the fallthru. */
+ e_out->flags = (e_out->flags & ~EDGE_EH) | EDGE_FALLTHRU;
+ e_out->probability = REG_BR_PROB_BASE;
+}
+
+/* A subroutine of cleanup_empty_eh. Handle more complex cases of
+ unsplitting than unsplit_eh was prepared to handle, e.g. when
+ multiple incoming edges and phis are involved. */
+
+static bool
+cleanup_empty_eh_unsplit (basic_block bb, edge e_out, eh_landing_pad lp)
+{
+ gimple_stmt_iterator gsi;
+ tree lab;
+
+ /* We really ought not have totally lost everything following
+ a landing pad label. Given that BB is empty, there had better
+ be a successor. */
+ gcc_assert (e_out != NULL);
+
+ /* The destination block must not already have a landing pad
+ for a different region. */
+ lab = NULL;
+ for (gsi = gsi_start_bb (e_out->dest); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+ int lp_nr;
+
+ if (gimple_code (stmt) != GIMPLE_LABEL)
+ break;
+ lab = gimple_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;
+ }
+
+ /* Attempt to move the PHIs into the successor block. */
+ if (cleanup_empty_eh_merge_phis (e_out->dest, bb, e_out, false))
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file,
+ "Unsplit EH landing pad %d to block %i "
+ "(via cleanup_empty_eh).\n",
+ lp->index, e_out->dest->index);
+ return true;
+ }
+
+ return false;
+}
+
+/* Return true if edge E_FIRST is part of an empty infinite loop
+ or leads to such a loop through a series of single successor
+ empty bbs. */
+
+static bool
+infinite_empty_loop_p (edge e_first)
+{
+ bool inf_loop = false;
+ edge e;
+
+ if (e_first->dest == e_first->src)
+ return true;
+
+ e_first->src->aux = (void *) 1;
+ for (e = e_first; single_succ_p (e->dest); e = single_succ_edge (e->dest))
+ {
+ gimple_stmt_iterator gsi;
+ if (e->dest->aux)
+ {
+ inf_loop = true;
+ break;
+ }
+ e->dest->aux = (void *) 1;
+ gsi = gsi_after_labels (e->dest);
+ if (!gsi_end_p (gsi) && is_gimple_debug (gsi_stmt (gsi)))
+ gsi_next_nondebug (&gsi);
+ if (!gsi_end_p (gsi))
+ break;
+ }
+ e_first->src->aux = NULL;
+ for (e = e_first; e->dest->aux; e = single_succ_edge (e->dest))
+ e->dest->aux = NULL;
+
+ return inf_loop;
+}
+
+/* Examine the block associated with LP to determine if it's an empty
+ handler for its EH region. If so, attempt to redirect EH edges to
+ an outer region. Return true the CFG was updated in any way. This
+ is similar to jump forwarding, just across EH edges. */
+
+static bool
+cleanup_empty_eh (eh_landing_pad lp)
+{
+ basic_block bb = label_to_block (lp->post_landing_pad);
+ gimple_stmt_iterator gsi;
+ gimple resx;
+ eh_region new_region;
+ 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. */
+ switch (EDGE_COUNT (bb->succs))
+ {
+ case 0:
+ e_out = NULL;
+ break;
+ case 1:
+ e_out = EDGE_SUCC (bb, 0);
+ break;
+ 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. */
+ if (!gsi_end_p (gsi) && is_gimple_debug (gsi_stmt (gsi)))
+ gsi_next_nondebug (&gsi);
+
+ /* 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))
+ return ret;
+
+ return ret | cleanup_empty_eh_unsplit (bb, e_out, lp);
+ }
+
+ /* The block should consist only of a single RESX statement, modulo a
+ preceding call to __builtin_stack_restore if there is no outgoing
+ edge, since the call can be eliminated in this case. */
+ resx = gsi_stmt (gsi);
+ if (!e_out && gimple_call_builtin_p (resx, BUILT_IN_STACK_RESTORE))
+ {
+ gsi_next (&gsi);
+ resx = gsi_stmt (gsi);
+ }
+ if (!is_gimple_resx (resx))
+ return ret;
+ gcc_assert (gsi_one_before_end_p (gsi));
+
+ /* Determine if there are non-EH edges, or resx edges into the handler. */
+ has_non_eh_pred = false;
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ if (!(e->flags & EDGE_EH))
+ has_non_eh_pred = true;
+
+ /* Find the handler that's outer of the empty handler by looking at
+ where the RESX instruction was vectored. */
+ new_lp_nr = lookup_stmt_eh_lp (resx);
+ new_region = get_eh_region_from_lp_number (new_lp_nr);
+
+ /* If there's no destination region within the current function,
+ redirection is trivial via removing the throwing statements from
+ the EH region, removing the EH edges, and allowing the block
+ to go unreachable. */
+ if (new_region == NULL)
+ {
+ gcc_assert (e_out == NULL);
+ for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
+ if (e->flags & EDGE_EH)
+ {
+ gimple stmt = last_stmt (e->src);
+ remove_stmt_from_eh_lp (stmt);
+ remove_edge (e);
+ }
+ else
+ ei_next (&ei);
+ goto succeed;
+ }
+
+ /* If the destination region is a MUST_NOT_THROW, allow the runtime
+ to handle the abort and allow the blocks to go unreachable. */
+ if (new_region->type == ERT_MUST_NOT_THROW)
+ {
+ for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
+ if (e->flags & EDGE_EH)
+ {
+ gimple stmt = last_stmt (e->src);
+ remove_stmt_from_eh_lp (stmt);
+ add_stmt_to_eh_lp (stmt, new_lp_nr);
+ remove_edge (e);
+ }
+ else
+ ei_next (&ei);
+ goto succeed;
+ }
+
+ /* Try to redirect the EH edges and merge the PHIs into the destination
+ landing pad block. If the merge succeeds, we'll already have redirected
+ all the EH edges. The handler itself will go unreachable if there were
+ no normal edges. */
+ if (cleanup_empty_eh_merge_phis (e_out->dest, bb, e_out, true))
+ goto succeed;
+
+ /* Finally, if all input edges are EH edges, then we can (potentially)
+ reduce the number of transfers from the runtime by moving the landing
+ pad from the original region to the new region. This is a win when
+ we remove the last CLEANUP region along a particular exception
+ propagation path. Since nothing changes except for the region with
+ which the landing pad is associated, the PHI nodes do not need to be
+ adjusted at all. */
+ if (!has_non_eh_pred)
+ {
+ cleanup_empty_eh_move_lp (bb, e_out, lp, new_region);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Empty EH handler %i moved to EH region %i.\n",
+ lp->index, new_region->index);
+
+ /* ??? The CFG didn't change, but we may have rendered the
+ old EH region unreachable. Trigger a cleanup there. */
+ return true;
+ }
+
+ return ret;
+
+ succeed:
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Empty EH handler %i removed.\n", lp->index);
+ remove_eh_landing_pad (lp);
+ return true;
+}
+
+/* Do a post-order traversal of the EH region tree. Examine each
+ post_landing_pad block and see if we can eliminate it as empty. */
+
+static bool
+cleanup_all_empty_eh (void)
+{
+ bool changed = false;
+ eh_landing_pad lp;
+ int i;
+
+ for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i)
+ if (lp)
+ changed |= cleanup_empty_eh (lp);
+
+ return changed;
+}
+
+/* Perform cleanups and lowering of exception handling
+ 1) cleanups regions with handlers doing nothing are optimized out
+ 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.
+
+ TODO: Push MUST_NOT_THROW regions to the root of the EH tree.
+ Unify those that have the same failure decl and locus.
+*/
+
+static unsigned int
+execute_cleanup_eh_1 (void)
+{
+ /* Do this first: unsplit_all_eh and cleanup_all_empty_eh can die
+ looking up unreachable landing pads. */
+ remove_unreachable_handlers ();
+
+ /* Watch out for the region tree vanishing due to all unreachable. */
+ if (cfun->eh->region_tree && optimize)
+ {
+ bool changed = false;
+
+ changed |= unsplit_all_eh ();
+ changed |= cleanup_all_empty_eh ();
+
+ if (changed)
+ {
+ free_dominance_info (CDI_DOMINATORS);
+ free_dominance_info (CDI_POST_DOMINATORS);
+
+ /* We delayed all basic block deletion, as we may have performed
+ cleanups on EH edges while non-EH edges were still present. */
+ delete_unreachable_blocks ();
+
+ /* We manipulated the landing pads. Remove any region that no
+ longer has a landing pad. */
+ remove_unreachable_handlers_no_lp ();
+
+ return TODO_cleanup_cfg | TODO_update_ssa_only_virtuals;
+ }
+ }
+
+ return 0;
+}
+
+static unsigned int
+execute_cleanup_eh (void)
+{
+ int ret = execute_cleanup_eh_1 ();
+
+ /* If the function no longer needs an EH personality routine
+ 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)
+ DECL_FUNCTION_PERSONALITY (current_function_decl) = NULL_TREE;
+
+ return ret;
+}
+
+static bool
+gate_cleanup_eh (void)
+{
+ return cfun->eh != NULL && cfun->eh->region_tree != NULL;
+}
+
+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. */
+
+DEBUG_FUNCTION bool
+verify_eh_edges (gimple stmt)
+{
+ basic_block bb = gimple_bb (stmt);
+ eh_landing_pad lp = NULL;
+ int lp_nr;
+ edge_iterator ei;
+ edge e, eh_edge;
+
+ lp_nr = lookup_stmt_eh_lp (stmt);
+ if (lp_nr > 0)
+ lp = get_eh_landing_pad_from_number (lp_nr);
+
+ eh_edge = NULL;
+ FOR_EACH_EDGE (e, ei, bb->succs)
+ {
+ if (e->flags & EDGE_EH)
+ {
+ if (eh_edge)
+ {
+ error ("BB %i has multiple EH edges", bb->index);
+ return true;
+ }
+ else
+ eh_edge = e;
+ }
+ }
+
+ if (lp == NULL)
+ {
+ if (eh_edge)
+ {
+ error ("BB %i can not throw but has an EH edge", bb->index);
+ return true;
+ }
+ return false;
+ }
+
+ if (!stmt_could_throw_p (stmt))
+ {
+ error ("BB %i last statement has incorrectly set lp", bb->index);
+ return true;
+ }
+
+ if (eh_edge == NULL)
+ {
+ error ("BB %i is missing an EH edge", bb->index);
+ return true;
+ }
+
+ if (eh_edge->dest != label_to_block (lp->post_landing_pad))
+ {
+ error ("Incorrect EH edge %i->%i", bb->index, eh_edge->dest->index);
+ return true;
+ }
+
+ return false;
+}
+
+/* Similarly, but handle GIMPLE_EH_DISPATCH specifically. */
+
+DEBUG_FUNCTION bool
+verify_eh_dispatch_edge (gimple stmt)
+{
+ eh_region r;
+ eh_catch c;
+ basic_block src, dst;
+ bool want_fallthru = true;
+ edge_iterator ei;
+ edge e, fall_edge;
+
+ r = get_eh_region_from_number (gimple_eh_dispatch_region (stmt));
+ src = gimple_bb (stmt);
+
+ FOR_EACH_EDGE (e, ei, src->succs)
+ gcc_assert (e->aux == NULL);
+
+ switch (r->type)
+ {
+ case ERT_TRY:
+ for (c = r->u.eh_try.first_catch; c ; c = c->next_catch)
+ {
+ dst = label_to_block (c->label);
+ e = find_edge (src, dst);
+ if (e == NULL)
+ {
+ error ("BB %i is missing an edge", src->index);
+ return true;
+ }
+ e->aux = (void *)e;
+
+ /* A catch-all handler doesn't have a fallthru. */
+ if (c->type_list == NULL)
+ {
+ want_fallthru = false;
+ break;
+ }
+ }
+ break;
+
+ case ERT_ALLOWED_EXCEPTIONS:
+ dst = label_to_block (r->u.allowed.label);
+ e = find_edge (src, dst);
+ if (e == NULL)
+ {
+ error ("BB %i is missing an edge", src->index);
+ return true;
+ }
+ e->aux = (void *)e;
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ fall_edge = NULL;
+ FOR_EACH_EDGE (e, ei, src->succs)
+ {
+ if (e->flags & EDGE_FALLTHRU)
+ {
+ if (fall_edge != NULL)
+ {
+ error ("BB %i too many fallthru edges", src->index);
+ return true;
+ }
+ fall_edge = e;
+ }
+ else if (e->aux)
+ e->aux = NULL;
+ else
+ {
+ error ("BB %i has incorrect edge", src->index);
+ return true;
+ }
+ }
+ if ((fall_edge != NULL) ^ want_fallthru)
+ {
+ error ("BB %i has incorrect fallthru edge", src->index);
+ return true;
+ }
+
+ return false;
+}