/* Exception handling semantics and decomposition for trees.
- Copyright (C) 2003-2018 Free Software Foundation, Inc.
+ Copyright (C) 2003-2020 Free Software Foundation, Inc.
This file is part of GCC.
statement is not recorded in the region table. */
int
-lookup_stmt_eh_lp_fn (struct function *ifun, gimple *t)
+lookup_stmt_eh_lp_fn (struct function *ifun, const gimple *t)
{
if (ifun->eh->throw_stmt_table == NULL)
return 0;
- int *lp_nr = ifun->eh->throw_stmt_table->get (t);
+ int *lp_nr = ifun->eh->throw_stmt_table->get (const_cast <gimple *> (t));
return lp_nr ? *lp_nr : 0;
}
/* Likewise, but always use the current function. */
int
-lookup_stmt_eh_lp (gimple *t)
+lookup_stmt_eh_lp (const gimple *t)
{
/* We can get called from initialized data when -fnon-call-exceptions
is on; prevent crash. */
seq = find_goto_replacement (tf, temp);
if (seq)
{
- gsi_insert_seq_before (gsi, gimple_seq_copy (seq), GSI_SAME_STMT);
+ gimple_stmt_iterator i;
+ seq = gimple_seq_copy (seq);
+ for (i = gsi_start (seq); !gsi_end_p (i); gsi_next (&i))
+ gimple_set_location (gsi_stmt (i), gimple_location (stmt));
+ gsi_insert_seq_before (gsi, seq, GSI_SAME_STMT);
gsi_remove (gsi, false);
return;
}
record_stmt_eh_region (region->outer, x);
}
-/* Emit an EH_DISPATCH statement into SEQ for REGION. */
-
-static void
-emit_eh_dispatch (gimple_seq *seq, eh_region region)
-{
- geh_dispatch *x = gimple_build_eh_dispatch (region->index);
- gimple_seq_add_stmt (seq, x);
-}
-
/* Note that the current EH region may contain a throw, or a
call to a function which itself may contain a throw. */
gimple_try_set_cleanup (tf->top_p, gimple_eh_else_n_body (eh_else));
finally = gimple_eh_else_e_body (eh_else);
- /* Let the ELSE see the exception that's being processed. */
- eh_region save_ehp = this_state->ehp_region;
- this_state->ehp_region = this_state->cur_region;
- lower_eh_constructs_1 (this_state, &finally);
- this_state->ehp_region = save_ehp;
+ /* Let the ELSE see the exception that's being processed, but
+ since the cleanup is outside the try block, process it with
+ outer_state, otherwise it may be used as a cleanup for
+ itself, and Bad Things (TM) ensue. */
+ eh_region save_ehp = outer_state->ehp_region;
+ outer_state->ehp_region = this_state->cur_region;
+ lower_eh_constructs_1 (outer_state, &finally);
+ outer_state->ehp_region = save_ehp;
}
else
{
tree out_label;
gimple_seq new_seq, cleanup;
gimple *x;
+ geh_dispatch *eh_dispatch;
location_t try_catch_loc = gimple_location (tp);
+ location_t catch_loc = UNKNOWN_LOCATION;
if (flag_exceptions)
{
return gimple_try_eval (tp);
new_seq = NULL;
- emit_eh_dispatch (&new_seq, try_region);
+ eh_dispatch = gimple_build_eh_dispatch (try_region->index);
+ gimple_seq_add_stmt (&new_seq, eh_dispatch);
emit_resx (&new_seq, try_region);
this_state.cur_region = state->cur_region;
gimple_seq handler;
catch_stmt = as_a <gcatch *> (gsi_stmt (gsi));
+ if (catch_loc == UNKNOWN_LOCATION)
+ catch_loc = gimple_location (catch_stmt);
c = gen_eh_region_catch (try_region, gimple_catch_types (catch_stmt));
handler = gimple_catch_handler (catch_stmt);
break;
}
+ /* Try to set a location on the dispatching construct to avoid inheriting
+ the location of the previous statement. */
+ gimple_set_location (eh_dispatch, catch_loc);
+
gimple_try_set_cleanup (tp, new_seq);
gimple_seq new_eh_seq = eh_seq;
if (!eh_region_may_contain_throw (this_region))
return gimple_try_eval (tp);
- new_seq = NULL;
this_state.cur_region = state->cur_region;
this_state.ehp_region = this_region;
- emit_eh_dispatch (&new_seq, this_region);
+ new_seq = NULL;
+ x = gimple_build_eh_dispatch (this_region->index);
+ gimple_set_location (x, gimple_location (tp));
+ gimple_seq_add_stmt (&new_seq, x);
emit_resx (&new_seq, this_region);
this_region->u.allowed.label = create_artificial_label (UNKNOWN_LOCATION);
tree fndecl = gimple_call_fndecl (stmt);
tree rhs, lhs;
- if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+ if (fndecl && fndecl_built_in_p (fndecl, BUILT_IN_NORMAL))
switch (DECL_FUNCTION_CODE (fndecl))
{
case BUILT_IN_EH_POINTER:
/* FALLTHRU */
case GIMPLE_ASSIGN:
- /* If the stmt can throw use a new temporary for the assignment
+ /* If the stmt can throw, use a new temporary for the assignment
to a LHS. This makes sure the old value of the LHS is
available on the EH edge. Only do so for statements that
potentially fall through (no noreturn calls e.g.), otherwise
this new assignment might create fake fallthru regions. */
- if (stmt_could_throw_p (stmt)
+ if (stmt_could_throw_p (cfun, stmt)
&& gimple_has_lhs (stmt)
&& gimple_stmt_may_fallthru (stmt)
&& !tree_could_throw_p (gimple_get_lhs (stmt))
gsi_insert_after (gsi, s, GSI_SAME_STMT);
}
/* Look for things that can throw exceptions, and record them. */
- if (state->cur_region && stmt_could_throw_p (stmt))
+ if (state->cur_region && stmt_could_throw_p (cfun, stmt))
{
record_stmt_eh_region (state->cur_region, stmt);
note_eh_region_may_contain_throw (state->cur_region);
case ERT_TRY:
for (c = r->u.eh_try.first_catch; c ; c = c->next_catch)
{
- dst = label_to_block (c->label);
+ dst = label_to_block (cfun, c->label);
make_edge (src, dst, 0);
/* A catch-all handler doesn't have a fallthru. */
break;
case ERT_ALLOWED_EXCEPTIONS:
- dst = label_to_block (r->u.allowed.label);
+ dst = label_to_block (cfun, r->u.allowed.label);
make_edge (src, dst, 0);
break;
gcc_assert (lp != NULL);
src = gimple_bb (stmt);
- dst = label_to_block (lp->post_landing_pad);
+ dst = label_to_block (cfun, lp->post_landing_pad);
make_edge (src, dst, EDGE_EH);
}
old_lp = get_eh_landing_pad_from_number (old_lp_nr);
throw_stmt = last_stmt (edge_in->src);
- gcc_assert (lookup_stmt_eh_lp (throw_stmt) == old_lp_nr);
+ gcc_checking_assert (lookup_stmt_eh_lp (throw_stmt) == old_lp_nr);
new_label = gimple_block_label (new_bb);
case ERT_TRY:
for (c = r->u.eh_try.first_catch; c ; c = c->next_catch)
{
- old_bb = label_to_block (c->label);
+ old_bb = label_to_block (cfun, c->label);
if (old_bb == e->dest)
{
c->label = new_lab;
break;
case ERT_ALLOWED_EXCEPTIONS:
- old_bb = label_to_block (r->u.allowed.label);
+ old_bb = label_to_block (cfun, r->u.allowed.label);
gcc_assert (old_bb == e->dest);
r->u.allowed.label = new_lab;
any_changed = true;
case ROUND_MOD_EXPR:
case TRUNC_MOD_EXPR:
case RDIV_EXPR:
- if (honor_snans || honor_trapv)
+ if (honor_snans)
return true;
if (fp_operation)
return flag_trapping_math;
return true;
return false;
+ case ABSU_EXPR:
+ /* ABSU_EXPR never traps. */
+ return false;
+
case PLUS_EXPR:
case MINUS_EXPR:
case MULT_EXPR:
/* Constructing an object cannot trap. */
return false;
+ case COND_EXPR:
+ case VEC_COND_EXPR:
+ /* Whether *COND_EXPR can trap depends on whether the
+ first argument can trap, so signal it as not handled.
+ Whether lhs is floating or not doesn't matter. */
+ *handled = false;
+ return false;
+
default:
/* Any floating arithmetic may trap. */
if (fp_operation && flag_trapping_math)
bool honor_snans = fp_operation && flag_signaling_nans != 0;
bool handled;
+ /* This function cannot tell whether or not COND_EXPR and VEC_COND_EXPR could
+ trap, because that depends on the respective condition op. */
+ gcc_assert (op != COND_EXPR && op != VEC_COND_EXPR);
+
if (TREE_CODE_CLASS (op) != tcc_comparison
&& TREE_CODE_CLASS (op) != tcc_unary
- && TREE_CODE_CLASS (op) != tcc_binary
- && op != FMA_EXPR)
+ && TREE_CODE_CLASS (op) != tcc_binary)
return false;
return operation_could_trap_helper_p (op, fp_operation, honor_trapv,
if (!expr)
return false;
+ /* In COND_EXPR and VEC_COND_EXPR only the condition may trap, but
+ they won't appear as operands in GIMPLE form, so this is just for the
+ GENERIC uses where it needs to recurse on the operands and so
+ *COND_EXPR itself doesn't trap. */
+ if (TREE_CODE (expr) == COND_EXPR || TREE_CODE (expr) == VEC_COND_EXPR)
+ return false;
+
code = TREE_CODE (expr);
t = TREE_TYPE (expr);
find_trapping_overflow (tree *tp, int *walk_subtrees, void *data)
{
if (EXPR_P (*tp)
+ && ANY_INTEGRAL_TYPE_P (TREE_TYPE (*tp))
&& !operation_no_trapping_overflow (TREE_TYPE (*tp), TREE_CODE (*tp)))
return *tp;
if (IS_TYPE_OR_DECL_P (*tp)
if (TREE_CODE (*tp) == ABS_EXPR)
{
- tree op = TREE_OPERAND (*tp, 0);
- op = save_expr (op);
- /* save_expr skips simple arithmetics, which is undesirable
- here, if it might trap due to flag_trapv. We need to
- force a SAVE_EXPR in the COND_EXPR condition, to evaluate
- it before the comparison. */
- if (EXPR_P (op)
- && TREE_CODE (op) != SAVE_EXPR
- && walk_tree (&op, find_trapping_overflow, NULL, NULL))
- {
- op = build1_loc (EXPR_LOCATION (op), SAVE_EXPR, type, op);
- TREE_SIDE_EFFECTS (op) = 1;
- }
- /* Change abs (op) to op < 0 ? -op : op and handle the NEGATE_EXPR
- like other signed integer trapping operations. */
- tree cond = fold_build2 (LT_EXPR, boolean_type_node,
- op, build_int_cst (type, 0));
- tree neg = fold_build1 (NEGATE_EXPR, utype,
- fold_convert (utype, op));
- *tp = fold_build3 (COND_EXPR, type, cond,
- fold_convert (type, neg), op);
+ TREE_SET_CODE (*tp, ABSU_EXPR);
+ TREE_TYPE (*tp) = utype;
+ *tp = fold_convert (type, *tp);
}
else
{
if (TREE_CODE_CLASS (code) == tcc_comparison
|| TREE_CODE_CLASS (code) == tcc_unary
- || TREE_CODE_CLASS (code) == tcc_binary
- || code == FMA_EXPR)
+ || TREE_CODE_CLASS (code) == tcc_binary)
{
if (TREE_CODE_CLASS (code) == tcc_comparison)
t = TREE_TYPE (gimple_assign_rhs1 (stmt));
}
-/* Return true if statement STMT could throw an exception. */
+/* Return true if statement STMT within FUN could throw an exception. */
bool
-stmt_could_throw_p (gimple *stmt)
+stmt_could_throw_p (function *fun, gimple *stmt)
{
if (!flag_exceptions)
return false;
case GIMPLE_COND:
{
- if (!cfun->can_throw_non_call_exceptions)
+ if (fun && !fun->can_throw_non_call_exceptions)
return false;
gcond *cond = as_a <gcond *> (stmt);
tree lhs = gimple_cond_lhs (cond);
}
case GIMPLE_ASSIGN:
- if (!cfun->can_throw_non_call_exceptions
+ if ((fun && !fun->can_throw_non_call_exceptions)
|| gimple_clobber_p (stmt))
return false;
return stmt_could_throw_1_p (as_a <gassign *> (stmt));
case GIMPLE_ASM:
- if (!cfun->can_throw_non_call_exceptions)
+ if (fun && !fun->can_throw_non_call_exceptions)
return false;
return gimple_asm_volatile_p (as_a <gasm *> (stmt));
return false;
}
-/* Return true if STMT can throw an exception that is not caught within
- the current function (CFUN). */
+/* Return true if STMT can throw an exception that is not caught within its
+ function FUN. FUN can be NULL but the function is extra conservative
+ then. */
bool
-stmt_can_throw_external (gimple *stmt)
+stmt_can_throw_external (function *fun, gimple *stmt)
{
int lp_nr;
- if (!stmt_could_throw_p (stmt))
+ if (!stmt_could_throw_p (fun, stmt))
return false;
+ if (!fun)
+ return true;
- lp_nr = lookup_stmt_eh_lp (stmt);
+ lp_nr = lookup_stmt_eh_lp_fn (fun, stmt);
return lp_nr == 0;
}
-/* Return true if STMT can throw an exception that is caught within
- the current function (CFUN). */
+/* Return true if STMT can throw an exception that is caught within its
+ function FUN. */
bool
-stmt_can_throw_internal (gimple *stmt)
+stmt_can_throw_internal (function *fun, gimple *stmt)
{
int lp_nr;
- if (!stmt_could_throw_p (stmt))
+ gcc_checking_assert (fun);
+ if (!stmt_could_throw_p (fun, stmt))
return false;
- lp_nr = lookup_stmt_eh_lp (stmt);
+ lp_nr = lookup_stmt_eh_lp_fn (fun, stmt);
return lp_nr > 0;
}
bool
maybe_clean_eh_stmt_fn (struct function *ifun, gimple *stmt)
{
- if (stmt_could_throw_p (stmt))
+ if (stmt_could_throw_p (ifun, stmt))
return false;
return remove_stmt_from_eh_lp_fn (ifun, stmt);
}
if (lp_nr != 0)
{
- bool new_stmt_could_throw = stmt_could_throw_p (new_stmt);
+ bool new_stmt_could_throw = stmt_could_throw_p (cfun, new_stmt);
if (new_stmt == old_stmt && new_stmt_could_throw)
return false;
{
int old_lp_nr, new_lp_nr;
- if (!stmt_could_throw_p (new_stmt))
+ if (!stmt_could_throw_p (new_fun, new_stmt))
return false;
old_lp_nr = lookup_stmt_eh_lp_fn (old_fun, old_stmt);
{
int lp_nr;
- if (!stmt_could_throw_p (new_stmt))
+ if (!stmt_could_throw_p (cfun, new_stmt))
return false;
lp_nr = lookup_stmt_eh_lp (old_stmt);
else
{
lab = *slot;
- new_bb = label_to_block (lab);
+ new_bb = label_to_block (cfun, lab);
}
gcc_assert (EDGE_COUNT (bb->succs) == 0);
}
/* Try to sink var = {v} {CLOBBER} stmts followed just by
- internal throw to successor BB. */
+ internal throw to successor BB.
+ SUNK, if not NULL, is an array of sequences indexed by basic-block
+ index to sink to and to pick up sinking opportunities from.
+ If FOUND_OPPORTUNITY is not NULL then do not perform the optimization
+ but set *FOUND_OPPORTUNITY to true. */
static int
-sink_clobbers (basic_block bb)
+sink_clobbers (basic_block bb,
+ gimple_seq *sunk = NULL, bool *found_opportunity = NULL)
{
edge e;
edge_iterator ei;
return 0;
any_clobbers = true;
}
- if (!any_clobbers)
+ if (!any_clobbers && (!sunk || gimple_seq_empty_p (sunk[bb->index])))
return 0;
+ /* If this was a dry run, tell it we found clobbers to sink. */
+ if (found_opportunity)
+ {
+ *found_opportunity = true;
+ return 0;
+ }
+
edge succe = single_succ_edge (bb);
succbb = succe->dest;
/* See if there is a virtual PHI node to take an updated virtual
operand from. */
gphi *vphi = NULL;
- tree vuse = NULL_TREE;
for (gphi_iterator gpi = gsi_start_phis (succbb);
!gsi_end_p (gpi); gsi_next (&gpi))
{
if (virtual_operand_p (res))
{
vphi = gpi.phi ();
- vuse = res;
break;
}
}
- dgsi = gsi_after_labels (succbb);
+ gimple *first_sunk = NULL;
+ gimple *last_sunk = NULL;
+ if (sunk)
+ dgsi = gsi_start (sunk[succbb->index]);
+ else
+ dgsi = gsi_after_labels (succbb);
gsi = gsi_last_bb (bb);
for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi))
{
forwarder edge we can keep virtual operands in place. */
gsi_remove (&gsi, false);
gsi_insert_before (&dgsi, stmt, GSI_NEW_STMT);
-
- /* But adjust virtual operands if we sunk across a PHI node. */
- if (vuse)
+ if (!first_sunk)
+ first_sunk = stmt;
+ last_sunk = stmt;
+ }
+ if (sunk && !gimple_seq_empty_p (sunk[bb->index]))
+ {
+ if (!first_sunk)
+ first_sunk = gsi_stmt (gsi_last (sunk[bb->index]));
+ last_sunk = gsi_stmt (gsi_start (sunk[bb->index]));
+ gsi_insert_seq_before_without_update (&dgsi,
+ sunk[bb->index], GSI_NEW_STMT);
+ sunk[bb->index] = NULL;
+ }
+ if (first_sunk)
+ {
+ /* Adjust virtual operands if we sunk across a virtual PHI. */
+ if (vphi)
{
- gimple *use_stmt;
imm_use_iterator iter;
use_operand_p use_p;
- FOR_EACH_IMM_USE_STMT (use_stmt, iter, vuse)
+ gimple *use_stmt;
+ tree phi_def = gimple_phi_result (vphi);
+ FOR_EACH_IMM_USE_STMT (use_stmt, iter, phi_def)
FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
- SET_USE (use_p, gimple_vdef (stmt));
- if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (vuse))
+ SET_USE (use_p, gimple_vdef (first_sunk));
+ if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (phi_def))
{
- SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_vdef (stmt)) = 1;
- SSA_NAME_OCCURS_IN_ABNORMAL_PHI (vuse) = 0;
+ SSA_NAME_OCCURS_IN_ABNORMAL_PHI (gimple_vdef (first_sunk)) = 1;
+ SSA_NAME_OCCURS_IN_ABNORMAL_PHI (phi_def) = 0;
}
- /* Adjust the incoming virtual operand. */
- SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (vphi, succe), gimple_vuse (stmt));
- SET_USE (gimple_vuse_op (stmt), vuse);
+ SET_USE (PHI_ARG_DEF_PTR_FROM_EDGE (vphi, succe),
+ gimple_vuse (last_sunk));
+ SET_USE (gimple_vuse_op (last_sunk), phi_def);
}
/* If there isn't a single predecessor but no virtual PHI node
arrange for virtual operands to be renamed. */
- else if (gimple_vuse_op (stmt) != NULL_USE_OPERAND_P
- && !single_pred_p (succbb))
+ else if (!single_pred_p (succbb)
+ && TREE_CODE (gimple_vuse (last_sunk)) == SSA_NAME)
{
- /* In this case there will be no use of the VDEF of this stmt.
- ??? Unless this is a secondary opportunity and we have not
- removed unreachable blocks yet, so we cannot assert this.
- Which also means we will end up renaming too many times. */
- SET_USE (gimple_vuse_op (stmt), gimple_vop (cfun));
- mark_virtual_operands_for_renaming (cfun);
+ mark_virtual_operand_for_renaming (gimple_vuse (last_sunk));
todo |= TODO_update_ssa_only_virtuals;
}
}
while (tp_node);
if (! have_label)
{
- remove_edge (find_edge (src, label_to_block (lab)));
+ remove_edge (find_edge (src, label_to_block (cfun, lab)));
redirected = true;
}
}
filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)));
filter = make_ssa_name (filter, x);
gimple_call_set_lhs (x, filter);
+ gimple_set_location (x, gimple_location (stmt));
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
/* Turn the default label into a default case. */
sort_case_labels (labels);
x = gimple_build_switch (filter, default_label, labels);
+ gimple_set_location (x, gimple_location (stmt));
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
}
}
filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)));
filter = make_ssa_name (filter, x);
gimple_call_set_lhs (x, filter);
+ gimple_set_location (x, gimple_location (stmt));
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
r->u.allowed.label = NULL;
basic_block bb;
int flags = 0;
bool redirected = false;
+ bool any_resx_to_process = false;
assign_filter_values ();
}
else if (gimple_code (last) == GIMPLE_RESX)
{
- if (stmt_can_throw_external (last))
+ if (stmt_can_throw_external (fun, last))
optimize_clobbers (bb);
- else
- flags |= sink_clobbers (bb);
+ else if (!any_resx_to_process)
+ sink_clobbers (bb, NULL, &any_resx_to_process);
}
}
-
if (redirected)
{
free_dominance_info (CDI_DOMINATORS);
delete_unreachable_blocks ();
}
+
+ if (any_resx_to_process)
+ {
+ /* Make sure to catch all secondary sinking opportunities by processing
+ blocks in RPO order and after all CFG modifications from lowering
+ and unreachable block removal. */
+ int *rpo = XNEWVEC (int, n_basic_blocks_for_fn (fun));
+ int rpo_n = pre_and_rev_post_order_compute_fn (fun, NULL, rpo, false);
+ gimple_seq *sunk = XCNEWVEC (gimple_seq, last_basic_block_for_fn (fun));
+ for (int i = 0; i < rpo_n; ++i)
+ {
+ bb = BASIC_BLOCK_FOR_FN (fun, rpo[i]);
+ gimple *last = last_stmt (bb);
+ if (last
+ && gimple_code (last) == GIMPLE_RESX
+ && !stmt_can_throw_external (fun, last))
+ flags |= sink_clobbers (bb, sunk);
+ /* If there were any clobbers sunk into this BB, insert them now. */
+ if (!gimple_seq_empty_p (sunk[bb->index]))
+ {
+ gimple_stmt_iterator gsi = gsi_after_labels (bb);
+ gsi_insert_seq_before (&gsi, sunk[bb->index], GSI_NEW_STMT);
+ sunk[bb->index] = NULL;
+ }
+ }
+ free (rpo);
+ free (sunk);
+ }
+
return flags;
}
if (cfun->eh == NULL)
return;
-
+
FOR_EACH_VEC_SAFE_ELT (cfun->eh->lp_array, i, lp)
- if (lp && lp->post_landing_pad)
+ if (lp
+ && (lp->post_landing_pad == NULL_TREE
+ || label_to_block (cfun, lp->post_landing_pad) == NULL))
{
- if (label_to_block (lp->post_landing_pad) == NULL)
- {
- remove_unreachable_handlers ();
- return;
- }
+ remove_unreachable_handlers ();
+ return;
}
}
static bool
unsplit_eh (eh_landing_pad lp)
{
- basic_block bb = label_to_block (lp->post_landing_pad);
+ basic_block bb = label_to_block (cfun, lp->post_landing_pad);
gimple_stmt_iterator gsi;
edge e_in, e_out;
return changed;
}
+/* Wrapper around unsplit_all_eh that makes it usable everywhere. */
+
+void
+unsplit_eh_edges (void)
+{
+ bool changed;
+
+ /* unsplit_all_eh can die looking up unreachable landing pads. */
+ maybe_remove_unreachable_handlers ();
+
+ changed = unsplit_all_eh ();
+
+ /* If EH edges have been unsplit, delete unreachable forwarder blocks. */
+ if (changed)
+ {
+ free_dominance_info (CDI_DOMINATORS);
+ free_dominance_info (CDI_POST_DOMINATORS);
+ delete_unreachable_blocks ();
+ }
+}
+
/* A subroutine of cleanup_empty_eh. Redirect all EH edges incoming
to OLD_BB to NEW_BB; return true on success, false on failure.
| | EH
<..>
which CFG verification would choke on. See PR45172 and PR51089. */
- FOR_EACH_EDGE (e, ei, old_bb->preds)
- if (find_edge (e->src, new_bb))
- return false;
+ if (!single_pred_p (new_bb))
+ FOR_EACH_EDGE (e, ei, old_bb->preds)
+ if (find_edge (e->src, new_bb))
+ return false;
FOR_EACH_EDGE (e, ei, old_bb->preds)
redirect_edge_var_map_clear (e);
static bool
cleanup_empty_eh (eh_landing_pad lp)
{
- basic_block bb = label_to_block (lp->post_landing_pad);
+ basic_block bb = label_to_block (cfun, lp->post_landing_pad);
gimple_stmt_iterator gsi;
gimple *resx;
eh_region new_region;
resx = gsi_stmt (gsi);
if (resx && is_gimple_resx (resx))
{
- if (stmt_can_throw_external (resx))
+ if (stmt_can_throw_external (cfun, resx))
optimize_clobbers (bb);
else if (sink_clobbers (bb))
ret = true;
eh_landing_pad lp;
int i;
- for (i = 1; vec_safe_iterate (cfun->eh->lp_array, i, &lp); ++i)
- if (lp)
- changed |= cleanup_empty_eh (lp);
+ /* Ideally we'd walk the region tree and process LPs inner to outer
+ to avoid quadraticness in EH redirection. Walking the LP array
+ in reverse seems to be an approximation of that. */
+ for (i = vec_safe_length (cfun->eh->lp_array) - 1; i >= 1; --i)
+ {
+ lp = (*cfun->eh->lp_array)[i];
+ if (lp)
+ changed |= cleanup_empty_eh (lp);
+ }
return changed;
}
return new pass_cleanup_eh (ctxt);
}
\f
+/* Disable warnings about missing quoting in GCC diagnostics for
+ the verification errors. Their format strings don't follow GCC
+ diagnostic conventions but are only used for debugging. */
+#if __GNUC__ >= 10
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wformat-diag"
+#endif
+
/* Verify that BB containing STMT as the last statement, has precisely the
edge that make_eh_edges would create. */
{
if (eh_edge)
{
- error ("BB %i can not throw but has an EH edge", bb->index);
+ error ("BB %i cannot throw but has an EH edge", bb->index);
return true;
}
return false;
}
- if (!stmt_could_throw_p (stmt))
+ if (!stmt_could_throw_p (cfun, stmt))
{
error ("BB %i last statement has incorrectly set lp", bb->index);
return true;
return true;
}
- if (eh_edge->dest != label_to_block (lp->post_landing_pad))
+ if (eh_edge->dest != label_to_block (cfun, lp->post_landing_pad))
{
error ("Incorrect EH edge %i->%i", bb->index, eh_edge->dest->index);
return true;
case ERT_TRY:
for (c = r->u.eh_try.first_catch; c ; c = c->next_catch)
{
- dst = label_to_block (c->label);
+ dst = label_to_block (cfun, c->label);
e = find_edge (src, dst);
if (e == NULL)
{
break;
case ERT_ALLOWED_EXCEPTIONS:
- dst = label_to_block (r->u.allowed.label);
+ dst = label_to_block (cfun, r->u.allowed.label);
e = find_edge (src, dst);
if (e == NULL)
{
return false;
}
+
+#if __GNUC__ >= 10
+# pragma GCC diagnostic pop
+#endif