aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-eh.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-eh.c')
-rw-r--r--gcc/tree-eh.c271
1 files changed, 271 insertions, 0 deletions
diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c
index 9febe5a..20e62ed 100644
--- a/gcc/tree-eh.c
+++ b/gcc/tree-eh.c
@@ -2639,3 +2639,274 @@ struct gimple_opt_pass pass_refactor_eh =
TODO_dump_func /* todo_flags_finish */
}
};
+
+/* Walk statements, see what regions are really references and remove unreachable ones. */
+
+static void
+tree_remove_unreachable_handlers (void)
+{
+ sbitmap reachable, contains_stmt;
+ VEC(int,heap) * label_to_region;
+ basic_block bb;
+
+ label_to_region = label_to_region_map ();
+ reachable = sbitmap_alloc (num_eh_regions ());
+ sbitmap_zero (reachable);
+ contains_stmt = sbitmap_alloc (num_eh_regions ());
+ sbitmap_zero (contains_stmt);
+
+ FOR_EACH_BB (bb)
+ {
+ 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;
+
+ for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+
+ 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);
+ }
+ }
+ if (gimple_code (stmt) == RESX)
+ SET_BIT (reachable, gimple_resx_region (stmt));
+ if ((region = lookup_stmt_eh_region (stmt)) >= 0)
+ SET_BIT (contains_stmt, region);
+ }
+ }
+
+ 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, reachable);
+ fprintf (dump_file, "Regions containing insns: ");
+ dump_sbitmap_file (dump_file, contains_stmt);
+ }
+
+ remove_unreachable_regions (reachable, contains_stmt);
+ sbitmap_free (reachable);
+ sbitmap_free (contains_stmt);
+ VEC_free (int, heap, label_to_region);
+ if (dump_file)
+ {
+ fprintf (dump_file, "\n\nAfter removal of unreachable regions:\n");
+ dump_eh_tree (dump_file, cfun);
+ fprintf (dump_file, "\n\n");
+ }
+}
+
+/* Pattern match emtpy EH receiver looking like:
+
+ save_filt.6352_662 = [filter_expr] <<<filter object>>>;
+ save_eptr.6351_663 = [exc_ptr_expr] <<<exception object>>>;
+ <<<exception object>>> = save_eptr.6351_663;
+ <<<filter object>>> = save_filt.6352_662;
+ resx 1
+ */
+
+static int
+tree_empty_eh_handler_p (basic_block bb)
+{
+ gimple_stmt_iterator gsi;
+ int region;
+
+ gsi = gsi_last_bb (bb);
+
+ /* RESX */
+ if (gsi_end_p (gsi))
+ return 0;
+ if (gimple_code (gsi_stmt (gsi)) != GIMPLE_RESX)
+ return 0;
+ region = gimple_resx_region (gsi_stmt (gsi));
+
+ /* 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))) != FILTER_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;
+
+ /* 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 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;
+
+ /* label. */
+ gsi_prev (&gsi);
+ if (gsi_end_p (gsi))
+ return 0;
+ if (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
+ return region;
+ else
+ return 0;
+}
+
+static bool dominance_info_invalidated;
+
+/* 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)
+{
+ int region;
+
+ /* 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)))
+ {
+ edge_iterator ei;
+ edge e;
+ gimple_stmt_iterator si;
+
+ remove_eh_region (region);
+
+ /* 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. */
+
+ 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))));
+
+ while ((e = ei_safe_edge (ei_start (bb->preds))))
+ {
+ 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)
+ {
+ remove_edge (e);
+ dominance_info_invalidated = true;
+ }
+ else
+ ei_next (&ei);
+ }
+ 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)
+ {
+ 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 (dump_file)
+ fprintf (dump_file, "Empty EH handler %i removed\n", region);
+ delete_basic_block (bb);
+ return true;
+ }
+ return false;
+}
+
+
+/* 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.
+ */
+
+static unsigned int
+cleanup_eh (void)
+{
+ bool changed = false;
+ basic_block bb;
+ int i;
+
+ if (!cfun->eh)
+ return 0;
+ if (dump_file)
+ {
+ fprintf (dump_file, "Before cleanups:\n");
+ 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++)
+ {
+ 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);
+ }
+
+ /* Removing contained cleanup can render MUST_NOT_THROW regions empty. */
+ if (changed)
+ delete_unreachable_blocks ();
+
+ tree_remove_unreachable_handlers ();
+ if (dump_file)
+ {
+ fprintf (dump_file, "After cleanups:\n");
+ dump_eh_tree (dump_file, cfun);
+ }
+
+ return (changed ? TODO_cleanup_cfg | TODO_update_ssa : 0);
+}
+
+struct gimple_opt_pass pass_cleanup_eh = {
+ {
+ GIMPLE_PASS,
+ "ehcleanup", /* name */
+ NULL, /* gate */
+ 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 */
+ TODO_dump_func /* todo_flags_finish */
+ }
+};