diff options
author | Jan Hubicka <jh@suse.cz> | 2009-03-29 15:32:13 +0200 |
---|---|---|
committer | Jan Hubicka <hubicka@gcc.gnu.org> | 2009-03-29 13:32:13 +0000 |
commit | a8da523f8a442f95c34dd3687771589579a08918 (patch) | |
tree | 1577a1a0b3cbaa114690527d99170c1034533481 /gcc/tree-eh.c | |
parent | 98f358e55f3a003ab3ca4213ba59ce93c5cd9fc1 (diff) | |
download | gcc-a8da523f8a442f95c34dd3687771589579a08918.zip gcc-a8da523f8a442f95c34dd3687771589579a08918.tar.gz gcc-a8da523f8a442f95c34dd3687771589579a08918.tar.bz2 |
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.c | 271 |
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 */ + } +}; |