summaryrefslogtreecommitdiff
path: root/gcc/tree-eh.c
diff options
context:
space:
mode:
authorhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2009-04-25 09:02:05 +0000
committerhubicka <hubicka@138bc75d-0d04-0410-961f-82ee72b054a4>2009-04-25 09:02:05 +0000
commit3d1eacdb7e7d943c99ac9f18181eab26d3e03cd1 (patch)
treed9aa3a1c938bc4df3cbd34eb918dde4d83d84237 /gcc/tree-eh.c
parenta15d1cafc1d164f1995f143feb46ea2ee4e85017 (diff)
downloadgcc-3d1eacdb7e7d943c99ac9f18181eab26d3e03cd1.tar.gz
* tree-eh.c (tree_remove_unreachable_handlers): Handle shared labels.
(tree_empty_eh_handler_p): Allow non-EH predecestors; allow region to be reached by different label than left. (update_eh_edges): Update comment; remove edge_to_remove if possible and return true if suceeded. (cleanup_empty_eh): Accept sharing map; handle shared regions. (cleanup_eh): Compute sharing map. * except.c (remove_eh_handler_and_replace): Add argument if we should update regions. (remove_unreachable_regions): Update for label sharing. (label_to_region_map): Likewise. (get_next_region_sharing_label): New function. (remove_eh_handler_and_replace): Add update_catch_try parameter; update prev_try pointers. (remove_eh_handler): Update. (remove_eh_region_and_replace_by_outer_of): New function. * except.h (struct eh_region): Add next_region_sharing_label. (remove_eh_region_and_replace_by_outer_of, get_next_region_sharing_label): Declare. * tree-cfgcleanup.c (tree_forwarder_block_p): Simplify. * tree-cfg.c (split_critical_edges): Split also edges where we can't insert code even if they are not critical. * tree-cfg.c (gimple_can_merge_blocks_p): EH edges are unmergable. (gimple_can_remove_branch_p): EH edges won't remove branch by redirection. * tree-inline.c (update_ssa_across_abnormal_edges): Do handle updating of non-abnormal EH edges. * tree-cfg.c (gimple_can_merge_blocks_p): EH edges are unmergable. (gimple_can_remove_branch_p): EH edges are unremovable by redirection. (split_critical_edges): Split also edges where emitting code on them will lead to splitting later. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@146763 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/tree-eh.c')
-rw-r--r--gcc/tree-eh.c124
1 files changed, 98 insertions, 26 deletions
diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c
index bc93ee14f3e..c9268f9853d 100644
--- a/gcc/tree-eh.c
+++ b/gcc/tree-eh.c
@@ -2695,8 +2695,11 @@ tree_remove_unreachable_handlers (void)
if (gimple_code (stmt) == GIMPLE_LABEL && has_eh_preds)
{
int uid = LABEL_DECL_UID (gimple_label_label (stmt));
- 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) == GIMPLE_RESX)
SET_BIT (reachable, gimple_resx_region (stmt));
@@ -2743,8 +2746,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);
@@ -2815,15 +2821,17 @@ tree_empty_eh_handler_p (basic_block bb)
if (gsi_end_p (gsi))
return 0;
}
- while (gimple_code (gsi_stmt (gsi)) == GIMPLE_LABEL)
- {
- if (gimple_label_label (gsi_stmt (gsi))
- == get_eh_region_no_tree_label (region))
- return region;
- gsi_prev (&gsi);
- if (gsi_end_p (gsi))
- return 0;
- }
+ if (gimple_code (gsi_stmt (gsi)) != GIMPLE_LABEL)
+ return 0;
+
+ /* 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;
return 0;
}
@@ -2955,9 +2963,12 @@ make_eh_edge_and_update_phi (struct eh_region *region, void *data)
/* 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. */
+ by EDGE_TO_REMOVE.
-static void
+ 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;
@@ -2967,6 +2978,7 @@ update_eh_edges (gimple stmt, basic_block bb_to_remove, edge edge_to_remove)
edge_iterator ei;
edge e;
int probability_sum = 0;
+ bool removed = false;
info.bb_to_remove = bb_to_remove;
info.bb = gimple_bb (stmt);
@@ -2980,8 +2992,6 @@ update_eh_edges (gimple stmt, basic_block bb_to_remove, edge edge_to_remove)
else
{
region_nr = lookup_stmt_eh_region (stmt);
- if (region_nr < 0)
- return;
is_resx = false;
inlinable = inlinable_call_p (stmt);
}
@@ -2993,9 +3003,11 @@ update_eh_edges (gimple stmt, basic_block bb_to_remove, edge edge_to_remove)
/* 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 && e != edge_to_remove)
+ if ((e->flags & EDGE_EH) && !e->aux)
{
dominance_info_invalidated = true;
+ if (e == edge_to_remove)
+ removed = true;
remove_edge (e);
}
else
@@ -3011,16 +3023,18 @@ update_eh_edges (gimple stmt, basic_block bb_to_remove, edge edge_to_remove)
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
@@ -3030,19 +3044,73 @@ cleanup_empty_eh (basic_block bb)
&& all_phis_safe_to_merge (bb))
{
edge e;
+ bool found = false, removed_some = false, has_non_eh_preds = false;
+ gimple_stmt_iterator gsi;
- remove_eh_region (region);
+ /* 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. */
- while ((e = ei_safe_edge (ei_start (bb->preds))))
+ 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;
+
+ 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;
+
+ 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)
+ remove_eh_region (region);
+ else if (!removed_some)
+ return false;
+
+ for (ei = ei_start (bb->preds); (e = ei_safe_edge (ei)); )
{
basic_block src = e->src;
- gcc_assert (e->flags & EDGE_EH);
+ if (!(e->flags & EDGE_EH))
+ {
+ ei_next (&ei);
+ continue;
+ }
if (stmt_can_throw_internal (last_stmt (src)))
- update_eh_edges (last_stmt (src), bb, e);
- remove_edge (e);
+ {
+ if (!update_eh_edges (last_stmt (src), bb, e))
+ ei_next (&ei);
+ }
+ else
+ remove_edge (e);
}
- if (dump_file)
- fprintf (dump_file, "Empty EH handler %i removed\n", region);
/* 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
@@ -3091,7 +3159,8 @@ cleanup_empty_eh (basic_block bb)
}
}
}
- delete_basic_block (bb);
+ if (!ei_safe_edge (ei_start (bb->preds)))
+ delete_basic_block (bb);
return true;
}
return false;
@@ -3111,6 +3180,7 @@ cleanup_eh (void)
{
bool changed = false;
basic_block bb;
+ VEC(int,heap) * label_to_region;
int i;
if (!cfun->eh)
@@ -3123,14 +3193,16 @@ cleanup_eh (void)
if (optimize)
{
+ 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);
+ changed |= cleanup_empty_eh (bb, label_to_region);
}
+ VEC_free (int, heap, label_to_region);
if (dominance_info_invalidated)
{
free_dominance_info (CDI_DOMINATORS);