summaryrefslogtreecommitdiff
path: root/gcc/tree-eh.c
diff options
context:
space:
mode:
authorJan Hubicka <jh@suse.cz>2009-03-29 15:32:13 +0200
committerJan Hubicka <hubicka@gcc.gnu.org>2009-03-29 13:32:13 +0000
commita8da523f8a442f95c34dd3687771589579a08918 (patch)
tree1577a1a0b3cbaa114690527d99170c1034533481 /gcc/tree-eh.c
parent98f358e55f3a003ab3ca4213ba59ce93c5cd9fc1 (diff)
downloadgcc-a8da523f8a442f95c34dd3687771589579a08918.tar.gz
re PR tree-optimization/28850 (missed call -> jmp transformation; redundant unwind stuff with empty finally)
PR middle-end/28850 * tree-pass.h (pass_cleanup_eh): New function. (remove_unreachable_regions): Break code handling RTL to rtl_remove_unreachable_regions; remove ERT_MUST_NOT_THROW that can not be reached by runtime. (can_be_reached_by_runtime): New function. (label_to_region_map): New function. (num_eh_regions): New function. (rtl_remove_unreachable_regions): New function. (convert_from_eh_region_ranges): Call rtl_remove_unreachable_regions. (remove_eh_region): New function. * except.h: Include sbitmap and vecprim. (remove_eh_region, remove_unreachable_regions, label_to_region_map, num_eh_regions): Declare. * passes.c (init_optimization_passes): Schedule cleanup_eh. * Makefile.in (EXCEPT_H): New; replace all uses of except.h by it. * tree-eh.c (tree_remove_unreachable_handlers): New function. (tree_empty_eh_handler_p): New function. (cleanup_empty_eh): New function. (cleanup_eh): New function. (pass_cleanup_eh): New function. From-SVN: r145233
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 9febe5a1288..20e62edbf05 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 */
+ }
+};