of space by only allocating memory for those that can throw. */
static void
-record_stmt_eh_region (struct eh_region *region, gimple t)
+record_stmt_eh_region (struct eh_region_d *region, gimple t)
{
if (!region)
return;
The eh region creation is straight-forward, but frobbing all the gotos
and such into shape isn't. */
+/* The GOTO_QUEUE is is an array of GIMPLE_GOTO and GIMPLE_RETURN
+ statements that are seen to escape this GIMPLE_TRY_FINALLY node.
+ The idea is to record a gimple statement for everything except for
+ the conditionals, which get their labels recorded. Since labels are
+ of type 'tree', we need this node to store both gimple and tree
+ objects. REPL_STMT is the sequence used to replace the goto/return
+ statement. CONT_STMT is used to store the statement that allows
+ the return/goto to jump to the original destination. */
+
+struct goto_queue_node
+{
+ treemple stmt;
+ gimple_seq repl_stmt;
+ gimple cont_stmt;
+ int index;
+ /* This is used when index >= 0 to indicate that stmt is a label (as
+ opposed to a goto stmt). */
+ int is_label;
+};
+
/* State of the world while lowering. */
struct leh_state
/* What's "current" while constructing the eh region tree. These
correspond to variables of the same name in cfun->eh, which we
don't have easy access to. */
- struct eh_region *cur_region;
- struct eh_region *prev_try;
+ struct eh_region_d *cur_region;
/* Processing of TRY_FINALLY requires a bit more state. This is
split out into a separate structure so that we don't have to
struct leh_state *outer;
/* The exception region created for it. */
- struct eh_region *region;
-
- /* The GOTO_QUEUE is is an array of GIMPLE_GOTO and GIMPLE_RETURN statements
- that are seen to escape this GIMPLE_TRY_FINALLY node.
- The idea is to record a gimple statement for everything except for
- the conditionals, which get their labels recorded. Since labels are of
- type 'tree', we need this node to store both gimple and tree objects.
- REPL_STMT is the sequence used to replace the goto/return statement.
- CONT_STMT is used to store the statement that allows the return/goto to
- jump to the original destination. */
- struct goto_queue_node {
- treemple stmt;
- gimple_seq repl_stmt;
- gimple cont_stmt;
- int index;
- /* this is used when index >= 0 to indicate that stmt is a label(as
- opposed to a goto stmt) */
- int is_label;
- } *goto_queue;
+ struct eh_region_d *region;
+
+ /* The goto queue. */
+ struct goto_queue_node *goto_queue;
size_t goto_queue_size;
size_t goto_queue_active;
tree label;
gimple_seq new_seq;
treemple temp;
+ location_t loc = gimple_location (gsi_stmt (*gsi));
temp.tp = tp;
new_seq = find_goto_replacement (tf, temp);
return;
}
- label = create_artificial_label ();
+ label = create_artificial_label (loc);
/* Set the new label for the GIMPLE_COND */
*tp = label;
{
gimple x;
gimple_seq cleanup, result;
+ location_t loc = gimple_location (tp);
cleanup = gimple_try_cleanup (tp);
result = gimple_try_eval (tp);
if (gimple_seq_may_fallthru (result))
{
if (!over)
- over = create_artificial_label ();
+ over = create_artificial_label (loc);
x = gimple_build_goto (over);
gimple_seq_add_stmt (&result, x);
}
if (!label)
{
- label = create_artificial_label ();
+ label = create_artificial_label (gimple_location (tf->try_finally_expr));
tf->fallthru_label = label;
if (tf->outer->tf)
{
if (tf->may_throw)
lab = tf->eh_label;
else
- lab = create_artificial_label ();
+ lab = create_artificial_label (gimple_location (tf->try_finally_expr));
/* We expect that tf->top_p is a GIMPLE_TRY. */
finally = gimple_try_cleanup (tf->top_p);
gimple x;
gimple_seq finally;
tree finally_label;
+ location_t loc = gimple_location (tf->try_finally_expr);
finally = gimple_try_cleanup (tf->top_p);
tf->top_p_seq = gimple_try_eval (tf->top_p);
return;
}
- finally_label = create_artificial_label ();
+ finally_label = create_artificial_label (loc);
x = gimple_build_label (finally_label);
gimple_seq_add_stmt (&tf->top_p_seq, x);
gimple_seq seq;
gimple x;
tree tmp;
+ location_t tf_loc = gimple_location (tf->try_finally_expr);
finally = gimple_try_cleanup (tf->top_p);
tf->top_p_seq = gimple_try_eval (tf->top_p);
if (! q)
continue;
- lab = labels[index].label = create_artificial_label ();
+ lab = labels[index].label
+ = create_artificial_label (tf_loc);
if (index == return_index)
do_return_redirection (q, lab, NULL, &return_val);
gimple switch_stmt;
gimple_seq finally;
struct pointer_map_t *cont_map = NULL;
+ /* The location of the TRY_FINALLY stmt. */
+ location_t tf_loc = gimple_location (tf->try_finally_expr);
+ /* The location of the finally block. */
+ location_t finally_loc;
switch_body = gimple_seq_alloc ();
finally = gimple_try_cleanup (tf->top_p);
tf->top_p_seq = gimple_try_eval (tf->top_p);
+ /* The location of the finally is either the last stmt in the finally
+ block or the location of the TRY_FINALLY itself. */
+ finally_loc = gimple_seq_last_stmt (tf->top_p_seq) != NULL ?
+ gimple_location (gimple_seq_last_stmt (tf->top_p_seq))
+ : tf_loc;
+
/* Lower the finally block itself. */
lower_eh_constructs_1 (state, finally);
ndests = fallthru_index + tf->may_fallthru;
finally_tmp = create_tmp_var (integer_type_node, "finally_tmp");
- finally_label = create_artificial_label ();
+ finally_label = create_artificial_label (finally_loc);
/* We use VEC_quick_push on case_label_vec throughout this function,
since we know the size in advance and allocate precisely as muce
last_case = build3 (CASE_LABEL_EXPR, void_type_node,
build_int_cst (NULL_TREE, fallthru_index), NULL,
- create_artificial_label ());
+ create_artificial_label (tf_loc));
VEC_quick_push (tree, case_label_vec, last_case);
last_case_index++;
last_case = build3 (CASE_LABEL_EXPR, void_type_node,
build_int_cst (NULL_TREE, eh_index), NULL,
- create_artificial_label ());
+ create_artificial_label (tf_loc));
VEC_quick_push (tree, case_label_vec, last_case);
last_case_index++;
gcc_assert (slot);
cont_stmt = *(gimple *) slot;
- label = create_artificial_label ();
+ label = create_artificial_label (tf_loc);
CASE_LABEL (last_case) = label;
x = gimple_build_label (label);
label. */
switch_stmt = gimple_build_switch_vec (finally_tmp, last_case,
case_label_vec);
+ gimple_set_location (switch_stmt, finally_loc);
/* Need to link SWITCH_STMT after running replace_goto_queue
due to not wanting to process the same goto stmts twice. */
struct leh_tf_state this_tf;
struct leh_state this_state;
int ndests;
+ location_t tf_loc = gimple_location (tp);
/* Process the try block. */
this_tf.outer = state;
if (using_eh_for_cleanups_p)
this_tf.region
- = gen_eh_region_cleanup (state->cur_region, state->prev_try);
+ = gen_eh_region_cleanup (state->cur_region);
else
this_tf.region = NULL;
this_state.cur_region = this_tf.region;
- this_state.prev_try = state->prev_try;
this_state.tf = &this_tf;
lower_eh_constructs_1 (&this_state, gimple_try_eval(tp));
this_tf.may_throw = get_eh_region_may_contain_throw (this_tf.region);
if (this_tf.may_throw)
{
- this_tf.eh_label = create_artificial_label ();
+ this_tf.eh_label = create_artificial_label (tf_loc);
set_eh_region_tree_label (this_tf.region, this_tf.eh_label);
honor_protect_cleanup_actions (state, &this_state, &this_tf);
}
static gimple_seq
lower_catch (struct leh_state *state, gimple tp)
{
- struct eh_region *try_region;
+ struct eh_region_d *try_region;
struct leh_state this_state;
gimple_stmt_iterator gsi;
tree out_label;
+ location_t try_catch_loc = gimple_location (tp);
try_region = gen_eh_region_try (state->cur_region);
this_state.cur_region = try_region;
- this_state.prev_try = try_region;
this_state.tf = state->tf;
lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
out_label = NULL;
for (gsi = gsi_start (gimple_try_cleanup (tp)); !gsi_end_p (gsi); )
{
- struct eh_region *catch_region;
+ struct eh_region_d *catch_region;
tree eh_label;
gimple x, gcatch;
gimple_catch_types (gcatch));
this_state.cur_region = catch_region;
- this_state.prev_try = state->prev_try;
lower_eh_constructs_1 (&this_state, gimple_catch_handler (gcatch));
- eh_label = create_artificial_label ();
+ eh_label = create_artificial_label (try_catch_loc);
set_eh_region_tree_label (catch_region, eh_label);
x = gimple_build_label (eh_label);
if (gimple_seq_may_fallthru (gimple_catch_handler (gcatch)))
{
if (!out_label)
- out_label = create_artificial_label ();
+ out_label = create_artificial_label (try_catch_loc);
x = gimple_build_goto (out_label);
gimple_seq_add_stmt (gimple_catch_handler_ptr (gcatch), x);
lower_eh_filter (struct leh_state *state, gimple tp)
{
struct leh_state this_state;
- struct eh_region *this_region;
+ struct eh_region_d *this_region;
gimple inner;
tree eh_label;
gimple_eh_filter_types (inner));
this_state = *state;
this_state.cur_region = this_region;
- /* For must not throw regions any cleanup regions inside it
- can't reach outer catch regions. */
- if (gimple_eh_filter_must_not_throw (inner))
- this_state.prev_try = NULL;
lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
lower_eh_constructs_1 (state, gimple_eh_filter_failure (inner));
gimple_try_set_cleanup (tp, gimple_eh_filter_failure (inner));
- eh_label = create_artificial_label ();
+ eh_label = create_artificial_label (gimple_location (inner));
set_eh_region_tree_label (this_region, eh_label);
return frob_into_branch_around (tp, eh_label, NULL);
lower_cleanup (struct leh_state *state, gimple tp)
{
struct leh_state this_state;
- struct eh_region *this_region;
+ struct eh_region_d *this_region;
struct leh_tf_state fake_tf;
gimple_seq result;
return result;
}
- this_region = gen_eh_region_cleanup (state->cur_region, state->prev_try);
+ this_region = gen_eh_region_cleanup (state->cur_region);
this_state = *state;
this_state.cur_region = this_region;
/* Build enough of a try-finally state so that we can reuse
honor_protect_cleanup_actions. */
memset (&fake_tf, 0, sizeof (fake_tf));
- fake_tf.top_p = tp;
+ fake_tf.top_p = fake_tf.try_finally_expr = tp;
fake_tf.outer = state;
fake_tf.region = this_region;
fake_tf.may_fallthru = gimple_seq_may_fallthru (gimple_try_eval (tp));
fake_tf.may_throw = true;
- fake_tf.eh_label = create_artificial_label ();
+ fake_tf.eh_label = create_artificial_label (gimple_location (tp));
set_eh_region_tree_label (this_region, fake_tf.eh_label);
honor_protect_cleanup_actions (state, NULL, &fake_tf);
/* Construct EH edges for STMT. */
static void
-make_eh_edge (struct eh_region *region, void *data)
+make_eh_edge (struct eh_region_d *region, void *data)
{
gimple stmt;
tree lab;
src = gimple_bb (stmt);
dst = label_to_block (lab);
- make_edge (src, dst, EDGE_ABNORMAL | EDGE_EH);
+ make_edge (src, dst, EDGE_EH);
}
/* See if STMT is call that might be inlined. */
EDGE_SUCC (bb, 0)->probability = REG_BR_PROB_BASE;
}
+/* Redirect EH edge E to NEW_BB. */
+
+edge
+redirect_eh_edge (edge e, basic_block new_bb)
+{
+ gimple stmt = gsi_stmt (gsi_last_bb (e->src));
+ int region_nr, new_region_nr;
+ bool is_resx;
+ bool inlinable = false;
+ tree label = gimple_block_label (new_bb);
+ struct eh_region_d *r;
+
+ if (gimple_code (stmt) == GIMPLE_RESX)
+ {
+ region_nr = gimple_resx_region (stmt);
+ is_resx = true;
+ }
+ else
+ {
+ region_nr = lookup_stmt_eh_region (stmt);
+ gcc_assert (region_nr >= 0);
+ is_resx = false;
+ inlinable = inlinable_call_p (stmt);
+ }
+
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Redirecting EH edge %i->%i to %i, region %i, resx %i\n",
+ e->src->index, e->dest->index, new_bb->index, region_nr, is_resx);
+ r = redirect_eh_edge_to_label (e, label, is_resx, inlinable, region_nr);
+ new_region_nr = get_eh_region_number (r);
+ if (new_region_nr != region_nr)
+ {
+ if (is_resx)
+ gimple_resx_set_region (stmt, new_region_nr);
+ else
+ {
+ remove_stmt_from_eh_region (stmt);
+ add_stmt_to_eh_region (stmt, new_region_nr);
+ }
+ }
+ e = ssa_redirect_edge (e, new_bb);
+ return e;
+}
+
static bool mark_eh_edge_found_error;
/* Mark edge make_eh_edge would create for given region by setting it aux
field, output error if something goes wrong. */
static void
-mark_eh_edge (struct eh_region *region, void *data)
+mark_eh_edge (struct eh_region_d *region, void *data)
{
gimple stmt;
tree lab;
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));
+ SET_BIT (reachable,
+ VEC_index (eh_region, cfun->eh->region_array,
+ gimple_resx_region (stmt))->region_number);
if ((region = lookup_stmt_eh_region (stmt)) >= 0)
SET_BIT (contains_stmt, region);
}
{
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);
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;
}
operands from DATA->bb_to_remove. */
static void
-make_eh_edge_and_update_phi (struct eh_region *region, void *data)
+make_eh_edge_and_update_phi (struct eh_region_d *region, void *data)
{
struct update_info *info = (struct update_info *) data;
edge e, e2;
}
dominance_info_invalidated = true;
e2 = find_edge (info->bb_to_remove, dst);
- e = make_edge (src, dst, EDGE_ABNORMAL | EDGE_EH);
+ e = make_edge (src, dst, EDGE_EH);
e->aux = e;
gcc_assert (e2);
for (si = gsi_start_phis (dst); !gsi_end_p (si); gsi_next (&si))
/* 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;
edge_iterator ei;
edge e;
int probability_sum = 0;
+ bool removed = false;
info.bb_to_remove = bb_to_remove;
info.bb = gimple_bb (stmt);
else
{
region_nr = lookup_stmt_eh_region (stmt);
- if (region_nr < 0)
- return;
is_resx = false;
inlinable = inlinable_call_p (stmt);
}
/* 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
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
&& all_phis_safe_to_merge (bb))
{
edge e;
+ bool found = false, removed_some = false, has_non_eh_preds = false;
+ gimple_stmt_iterator gsi;
+
+ /* 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. */
+
+ 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;
- remove_eh_region (region);
+ 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;
- while ((e = ei_safe_edge (ei_start (bb->preds))))
+ /* 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)
+ {
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ fprintf (dump_file, "Empty EH handler %i removed.\n", region);
+ 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
}
}
}
- delete_basic_block (bb);
+ if (!ei_safe_edge (ei_start (bb->preds)))
+ delete_basic_block (bb);
return true;
}
return false;
{
bool changed = false;
basic_block bb;
+ VEC(int,heap) * label_to_region;
int i;
if (!cfun->eh)
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);