/* Exception handling semantics and decomposition for trees.
- Copyright (C) 2003-2014 Free Software Foundation, Inc.
+ Copyright (C) 2003-2015 Free Software Foundation, Inc.
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "hash-table.h"
#include "tm.h"
+#include "alias.h"
+#include "symtab.h"
#include "tree.h"
-#include "expr.h"
-#include "calls.h"
-#include "flags.h"
+#include "fold-const.h"
+#include "hard-reg-set.h"
#include "function.h"
+#include "rtl.h"
+#include "flags.h"
+#include "insn-config.h"
+#include "expmed.h"
+#include "dojump.h"
+#include "explow.h"
+#include "calls.h"
+#include "emit-rtl.h"
+#include "varasm.h"
+#include "stmt.h"
+#include "expr.h"
#include "except.h"
-#include "pointer-set.h"
+#include "predict.h"
+#include "dominance.h"
+#include "cfg.h"
+#include "cfganal.h"
+#include "cfgcleanup.h"
#include "basic-block.h"
#include "tree-ssa-alias.h"
#include "internal-fn.h"
#include "tree-eh.h"
#include "gimple-expr.h"
-#include "is-a.h"
#include "gimple.h"
#include "gimple-iterator.h"
#include "gimple-ssa.h"
static void
add_stmt_to_eh_lp_fn (struct function *ifun, gimple t, int num)
{
- struct throw_stmt_node *n;
- void **slot;
-
gcc_assert (num != 0);
- n = ggc_alloc<throw_stmt_node> ();
- n->stmt = t;
- n->lp_nr = num;
-
if (!get_eh_throw_stmt_table (ifun))
- set_eh_throw_stmt_table (ifun, htab_create_ggc (31, struct_ptr_hash,
- struct_ptr_eq,
- ggc_free));
+ set_eh_throw_stmt_table (ifun, hash_map<gimple, int>::create_ggc (31));
- slot = htab_find_slot (get_eh_throw_stmt_table (ifun), n, INSERT);
- gcc_assert (!*slot);
- *slot = n;
+ gcc_assert (!get_eh_throw_stmt_table (ifun)->put (t, num));
}
/* Add statement T in the current function (cfun) to EH landing pad NUM. */
bool
remove_stmt_from_eh_lp_fn (struct function *ifun, gimple t)
{
- struct throw_stmt_node dummy;
- void **slot;
-
if (!get_eh_throw_stmt_table (ifun))
return false;
- dummy.stmt = t;
- slot = htab_find_slot (get_eh_throw_stmt_table (ifun), &dummy,
- NO_INSERT);
- if (slot)
- {
- htab_clear_slot (get_eh_throw_stmt_table (ifun), slot);
- return true;
- }
- else
+ if (!get_eh_throw_stmt_table (ifun)->get (t))
return false;
+
+ get_eh_throw_stmt_table (ifun)->remove (t);
+ return true;
}
int
lookup_stmt_eh_lp_fn (struct function *ifun, gimple t)
{
- struct throw_stmt_node *p, n;
-
if (ifun->eh->throw_stmt_table == NULL)
return 0;
- n.stmt = t;
- p = (struct throw_stmt_node *) htab_find (ifun->eh->throw_stmt_table, &n);
- return p ? p->lp_nr : 0;
+ int *lp_nr = ifun->eh->throw_stmt_table->get (t);
+ return lp_nr ? *lp_nr : 0;
}
/* Likewise, but always use the current function. */
tree) leaves the TRY block, its necessary to record a tree in
this field. Thus a treemple is used. */
treemple child;
- gimple parent;
+ gtry *parent;
};
/* Hashtable helpers. */
-struct finally_tree_hasher : typed_free_remove <finally_tree_node>
+struct finally_tree_hasher : free_ptr_hash <finally_tree_node>
{
- typedef finally_tree_node value_type;
- typedef finally_tree_node compare_type;
- static inline hashval_t hash (const value_type *);
- static inline bool equal (const value_type *, const compare_type *);
+ static inline hashval_t hash (const finally_tree_node *);
+ static inline bool equal (const finally_tree_node *,
+ const finally_tree_node *);
};
inline hashval_t
-finally_tree_hasher::hash (const value_type *v)
+finally_tree_hasher::hash (const finally_tree_node *v)
{
return (intptr_t)v->child.t >> 4;
}
inline bool
-finally_tree_hasher::equal (const value_type *v, const compare_type *c)
+finally_tree_hasher::equal (const finally_tree_node *v,
+ const finally_tree_node *c)
{
return v->child.t == c->child.t;
}
/* Note that this table is *not* marked GTY. It is short-lived. */
-static hash_table <finally_tree_hasher> finally_tree;
+static hash_table<finally_tree_hasher> *finally_tree;
static void
-record_in_finally_tree (treemple child, gimple parent)
+record_in_finally_tree (treemple child, gtry *parent)
{
struct finally_tree_node *n;
finally_tree_node **slot;
n->child = child;
n->parent = parent;
- slot = finally_tree.find_slot (n, INSERT);
+ slot = finally_tree->find_slot (n, INSERT);
gcc_assert (!*slot);
*slot = n;
}
static void
-collect_finally_tree (gimple stmt, gimple region);
+collect_finally_tree (gimple stmt, gtry *region);
/* Go through the gimple sequence. Works with collect_finally_tree to
record all GIMPLE_LABEL and GIMPLE_TRY statements. */
static void
-collect_finally_tree_1 (gimple_seq seq, gimple region)
+collect_finally_tree_1 (gimple_seq seq, gtry *region)
{
gimple_stmt_iterator gsi;
}
static void
-collect_finally_tree (gimple stmt, gimple region)
+collect_finally_tree (gimple stmt, gtry *region)
{
treemple temp;
switch (gimple_code (stmt))
{
case GIMPLE_LABEL:
- temp.t = gimple_label_label (stmt);
+ temp.t = gimple_label_label (as_a <glabel *> (stmt));
record_in_finally_tree (temp, region);
break;
{
temp.g = stmt;
record_in_finally_tree (temp, region);
- collect_finally_tree_1 (gimple_try_eval (stmt), stmt);
+ collect_finally_tree_1 (gimple_try_eval (stmt),
+ as_a <gtry *> (stmt));
collect_finally_tree_1 (gimple_try_cleanup (stmt), region);
}
else if (gimple_try_kind (stmt) == GIMPLE_TRY_CATCH)
break;
case GIMPLE_CATCH:
- collect_finally_tree_1 (gimple_catch_handler (stmt), region);
+ collect_finally_tree_1 (gimple_catch_handler (
+ as_a <gcatch *> (stmt)),
+ region);
break;
case GIMPLE_EH_FILTER:
break;
case GIMPLE_EH_ELSE:
- collect_finally_tree_1 (gimple_eh_else_n_body (stmt), region);
- collect_finally_tree_1 (gimple_eh_else_e_body (stmt), region);
+ {
+ geh_else *eh_else_stmt = as_a <geh_else *> (stmt);
+ collect_finally_tree_1 (gimple_eh_else_n_body (eh_else_stmt), region);
+ collect_finally_tree_1 (gimple_eh_else_e_body (eh_else_stmt), region);
+ }
break;
default:
do
{
n.child = start;
- p = finally_tree.find (&n);
+ p = finally_tree->find (&n);
if (!p)
return true;
start.g = p->parent;
try_finally_expr is the original GIMPLE_TRY_FINALLY. We need to retain
this so that outside_finally_tree can reliably reference the tree used
in the collect_finally_tree data structures. */
- gimple try_finally_expr;
- gimple top_p;
+ gtry *try_finally_expr;
+ gtry *top_p;
/* While lowering a top_p usually it is expanded into multiple statements,
thus we need the following field to store them. */
size_t goto_queue_active;
/* Pointer map to help in searching goto_queue when it is large. */
- struct pointer_map_t *goto_queue_map;
+ hash_map<gimple, goto_queue_node *> *goto_queue_map;
/* The set of unique labels seen as entries in the goto queue. */
vec<tree> dest_array;
bool may_throw;
};
-static gimple_seq lower_eh_must_not_throw (struct leh_state *, gimple);
+static gimple_seq lower_eh_must_not_throw (struct leh_state *, gtry *);
/* Search for STMT in the goto queue. Return the replacement,
or null if the statement isn't in the queue. */
find_goto_replacement (struct leh_tf_state *tf, treemple stmt)
{
unsigned int i;
- void **slot;
if (tf->goto_queue_active < LARGE_GOTO_QUEUE)
{
if (!tf->goto_queue_map)
{
- tf->goto_queue_map = pointer_map_create ();
+ tf->goto_queue_map = new hash_map<gimple, goto_queue_node *>;
for (i = 0; i < tf->goto_queue_active; i++)
{
- slot = pointer_map_insert (tf->goto_queue_map,
- tf->goto_queue[i].stmt.g);
- gcc_assert (*slot == NULL);
- *slot = &tf->goto_queue[i];
+ bool existed = tf->goto_queue_map->put (tf->goto_queue[i].stmt.g,
+ &tf->goto_queue[i]);
+ gcc_assert (!existed);
}
}
- slot = pointer_map_contains (tf->goto_queue_map, stmt.g);
+ goto_queue_node **slot = tf->goto_queue_map->get (stmt.g);
if (slot != NULL)
- return (((struct goto_queue_node *) *slot)->repl_stmt);
+ return ((*slot)->repl_stmt);
return NULL;
}
replace_goto_queue_stmt_list (gimple_try_cleanup_ptr (stmt), tf);
break;
case GIMPLE_CATCH:
- replace_goto_queue_stmt_list (gimple_catch_handler_ptr (stmt), tf);
+ replace_goto_queue_stmt_list (gimple_catch_handler_ptr (
+ as_a <gcatch *> (stmt)),
+ tf);
break;
case GIMPLE_EH_FILTER:
replace_goto_queue_stmt_list (gimple_eh_filter_failure_ptr (stmt), tf);
break;
case GIMPLE_EH_ELSE:
- replace_goto_queue_stmt_list (gimple_eh_else_n_body_ptr (stmt), tf);
- replace_goto_queue_stmt_list (gimple_eh_else_e_body_ptr (stmt), tf);
+ {
+ geh_else *eh_else_stmt = as_a <geh_else *> (stmt);
+ replace_goto_queue_stmt_list (gimple_eh_else_n_body_ptr (eh_else_stmt),
+ tf);
+ replace_goto_queue_stmt_list (gimple_eh_else_e_body_ptr (eh_else_stmt),
+ tf);
+ }
break;
default:
switch (gimple_code (stmt))
{
case GIMPLE_COND:
- new_stmt.tp = gimple_op_ptr (stmt, 2);
- record_in_goto_queue_label (tf, new_stmt, gimple_cond_true_label (stmt),
- EXPR_LOCATION (*new_stmt.tp));
- new_stmt.tp = gimple_op_ptr (stmt, 3);
- record_in_goto_queue_label (tf, new_stmt, gimple_cond_false_label (stmt),
- EXPR_LOCATION (*new_stmt.tp));
+ {
+ gcond *cond_stmt = as_a <gcond *> (stmt);
+ new_stmt.tp = gimple_op_ptr (cond_stmt, 2);
+ record_in_goto_queue_label (tf, new_stmt,
+ gimple_cond_true_label (cond_stmt),
+ EXPR_LOCATION (*new_stmt.tp));
+ new_stmt.tp = gimple_op_ptr (cond_stmt, 3);
+ record_in_goto_queue_label (tf, new_stmt,
+ gimple_cond_false_label (cond_stmt),
+ EXPR_LOCATION (*new_stmt.tp));
+ }
break;
case GIMPLE_GOTO:
new_stmt.g = stmt;
of the labels will leave outer GIMPLE_TRY_FINALLY nodes. Verify this. */
static void
-verify_norecord_switch_expr (struct leh_state *state, gimple switch_expr)
+verify_norecord_switch_expr (struct leh_state *state,
+ gswitch *switch_expr)
{
struct leh_tf_state *tf = state->tf;
size_t i, n;
do_goto_redirection (struct goto_queue_node *q, tree finlab, gimple_seq mod,
struct leh_tf_state *tf)
{
- gimple x;
+ ggoto *x;
gcc_assert (q->is_label);
emit_post_landing_pad (gimple_seq *seq, eh_region region)
{
eh_landing_pad lp = region->landing_pads;
- gimple x;
+ glabel *x;
if (lp == NULL)
lp = gen_eh_landing_pad (region);
static void
emit_resx (gimple_seq *seq, eh_region region)
{
- gimple x = gimple_build_resx (region->index);
+ gresx *x = gimple_build_resx (region->index);
gimple_seq_add_stmt (seq, x);
if (region->outer)
record_stmt_eh_region (region->outer, x);
static void
emit_eh_dispatch (gimple_seq *seq, eh_region region)
{
- gimple x = gimple_build_eh_dispatch (region->index);
+ geh_dispatch *x = gimple_build_eh_dispatch (region->index);
gimple_seq_add_stmt (seq, x);
}
/* We want to transform
try { body; } catch { stuff; }
to
- normal_seqence:
+ normal_sequence:
body;
over:
- eh_seqence:
+ eh_sequence:
landing_pad:
stuff;
goto over;
an existing label that should be put at the exit, or NULL. */
static gimple_seq
-frob_into_branch_around (gimple tp, eh_region region, tree over)
+frob_into_branch_around (gtry *tp, eh_region region, tree over)
{
gimple x;
gimple_seq cleanup, result;
lower_try_finally_dup_block (gimple_seq seq, struct leh_state *outer_state,
location_t loc)
{
- gimple region = NULL;
+ gtry *region = NULL;
gimple_seq new_seq;
gimple_stmt_iterator gsi;
/* A subroutine of lower_try_finally. If FINALLY consits of a
GIMPLE_EH_ELSE node, return it. */
-static inline gimple
+static inline geh_else *
get_eh_else (gimple_seq finally)
{
gimple x = gimple_seq_first_stmt (finally);
if (gimple_code (x) == GIMPLE_EH_ELSE)
{
gcc_assert (gimple_seq_singleton_p (finally));
- return x;
+ return as_a <geh_else *> (x);
}
return NULL;
}
gimple_stmt_iterator gsi;
bool finally_may_fallthru;
gimple_seq finally;
- gimple x, eh_else;
+ gimple x;
+ geh_mnt *eh_mnt;
+ gtry *try_stmt;
+ geh_else *eh_else;
/* First check for nothing to do. */
if (lang_hooks.eh_protect_cleanup_actions == NULL)
}
/* Wrap the block with protect_cleanup_actions as the action. */
- x = gimple_build_eh_must_not_throw (protect_cleanup_actions);
- x = gimple_build_try (finally, gimple_seq_alloc_with_stmt (x),
- GIMPLE_TRY_CATCH);
- finally = lower_eh_must_not_throw (outer_state, x);
+ eh_mnt = gimple_build_eh_must_not_throw (protect_cleanup_actions);
+ try_stmt = gimple_build_try (finally, gimple_seq_alloc_with_stmt (eh_mnt),
+ GIMPLE_TRY_CATCH);
+ finally = lower_eh_must_not_throw (outer_state, try_stmt);
/* Drop all of this into the exception sequence. */
emit_post_landing_pad (&eh_seq, tf->region);
struct leh_tf_state *tf)
{
tree lab;
- gimple x, eh_else;
+ gimple x;
+ geh_else *eh_else;
gimple_seq finally;
struct goto_queue_node *q, *qe;
lower_try_finally_onedest (struct leh_state *state, struct leh_tf_state *tf)
{
struct goto_queue_node *q, *qe;
+ geh_else *eh_else;
+ glabel *label_stmt;
gimple x;
gimple_seq finally;
gimple_stmt_iterator gsi;
/* Since there's only one destination, and the destination edge can only
either be EH or non-EH, that implies that all of our incoming edges
are of the same type. Therefore we can lower EH_ELSE immediately. */
- x = get_eh_else (finally);
- if (x)
+ eh_else = get_eh_else (finally);
+ if (eh_else)
{
if (tf->may_throw)
- finally = gimple_eh_else_e_body (x);
+ finally = gimple_eh_else_e_body (eh_else);
else
- finally = gimple_eh_else_n_body (x);
+ finally = gimple_eh_else_n_body (eh_else);
}
lower_eh_constructs_1 (state, &finally);
}
finally_label = create_artificial_label (loc);
- x = gimple_build_label (finally_label);
- gimple_seq_add_stmt (&tf->top_p_seq, x);
+ label_stmt = gimple_build_label (finally_label);
+ gimple_seq_add_stmt (&tf->top_p_seq, label_stmt);
gimple_seq_add_seq (&tf->top_p_seq, finally);
gimple_seq finally;
gimple_seq new_stmt;
gimple_seq seq;
- gimple x, eh_else;
+ gimple x;
+ geh_else *eh_else;
tree tmp;
location_t tf_loc = gimple_location (tf->try_finally_expr);
tree last_case;
vec<tree> case_label_vec;
gimple_seq switch_body = NULL;
- gimple x, eh_else;
+ gimple x;
+ geh_else *eh_else;
tree tmp;
gimple switch_stmt;
gimple_seq finally;
- struct pointer_map_t *cont_map = NULL;
+ hash_map<tree, gimple> *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. */
if (case_label_vec.length () <= case_index || !case_label_vec[case_index])
{
tree case_lab;
- void **slot;
tmp = build_int_cst (integer_type_node, switch_id);
case_lab = build_case_label (tmp, NULL,
create_artificial_label (tf_loc));
/* We store the cont_stmt in the pointer map, so that we can recover
it in the loop below. */
if (!cont_map)
- cont_map = pointer_map_create ();
- slot = pointer_map_insert (cont_map, case_lab);
- *slot = q->cont_stmt;
+ cont_map = new hash_map<tree, gimple>;
+ cont_map->put (case_lab, q->cont_stmt);
case_label_vec.quick_push (case_lab);
}
}
for (j = last_case_index; j < last_case_index + nlabels; j++)
{
gimple cont_stmt;
- void **slot;
last_case = case_label_vec[j];
gcc_assert (last_case);
gcc_assert (cont_map);
- slot = pointer_map_contains (cont_map, last_case);
- gcc_assert (slot);
- cont_stmt = *(gimple *) slot;
+ cont_stmt = *cont_map->get (last_case);
x = gimple_build_label (CASE_LABEL (last_case));
gimple_seq_add_stmt (&switch_body, x);
maybe_record_in_goto_queue (state, cont_stmt);
}
if (cont_map)
- pointer_map_destroy (cont_map);
+ delete cont_map;
replace_goto_queue (tf);
decide_copy_try_finally (int ndests, bool may_throw, gimple_seq finally)
{
int f_estimate, sw_estimate;
- gimple eh_else;
+ geh_else *eh_else;
/* If there's an EH_ELSE involved, the exception path is separate
and really doesn't come into play for this computation. */
arrange for the FINALLY block to be executed on all exits. */
static gimple_seq
-lower_try_finally (struct leh_state *state, gimple tp)
+lower_try_finally (struct leh_state *state, gtry *tp)
{
struct leh_tf_state this_tf;
struct leh_state this_state;
this_tf.dest_array.release ();
free (this_tf.goto_queue);
if (this_tf.goto_queue_map)
- pointer_map_destroy (this_tf.goto_queue_map);
+ delete this_tf.goto_queue_map;
/* If there was an old (aka outer) eh_seq, append the current eh_seq.
If there was no old eh_seq, then the append is trivially already done. */
exception region trees that records all the magic. */
static gimple_seq
-lower_catch (struct leh_state *state, gimple tp)
+lower_catch (struct leh_state *state, gtry *tp)
{
eh_region try_region = NULL;
struct leh_state this_state = *state;
this_state.cur_region = state->cur_region;
this_state.ehp_region = try_region;
+ /* Add eh_seq from lowering EH in the cleanup sequence after the cleanup
+ itself, so that e.g. for coverage purposes the nested cleanups don't
+ appear before the cleanup body. See PR64634 for details. */
+ gimple_seq old_eh_seq = eh_seq;
+ eh_seq = NULL;
+
out_label = NULL;
cleanup = gimple_try_cleanup (tp);
for (gsi = gsi_start (cleanup);
gsi_next (&gsi))
{
eh_catch c;
- gimple gcatch;
+ gcatch *catch_stmt;
gimple_seq handler;
- gcatch = gsi_stmt (gsi);
- c = gen_eh_region_catch (try_region, gimple_catch_types (gcatch));
+ catch_stmt = as_a <gcatch *> (gsi_stmt (gsi));
+ c = gen_eh_region_catch (try_region, gimple_catch_types (catch_stmt));
- handler = gimple_catch_handler (gcatch);
+ handler = gimple_catch_handler (catch_stmt);
lower_eh_constructs_1 (&this_state, &handler);
c->label = create_artificial_label (UNKNOWN_LOCATION);
gimple_try_set_cleanup (tp, new_seq);
- return frob_into_branch_around (tp, try_region, out_label);
+ gimple_seq new_eh_seq = eh_seq;
+ eh_seq = old_eh_seq;
+ gimple_seq ret_seq = frob_into_branch_around (tp, try_region, out_label);
+ gimple_seq_add_seq (&eh_seq, new_eh_seq);
+ return ret_seq;
}
/* A subroutine of lower_eh_constructs_1. Lower a GIMPLE_TRY with a
region trees that record all the magic. */
static gimple_seq
-lower_eh_filter (struct leh_state *state, gimple tp)
+lower_eh_filter (struct leh_state *state, gtry *tp)
{
struct leh_state this_state = *state;
eh_region this_region = NULL;
plus the exception region trees that record all the magic. */
static gimple_seq
-lower_eh_must_not_throw (struct leh_state *state, gimple tp)
+lower_eh_must_not_throw (struct leh_state *state, gtry *tp)
{
struct leh_state this_state = *state;
this_region = gen_eh_region_must_not_throw (state->cur_region);
this_region->u.must_not_throw.failure_decl
- = gimple_eh_must_not_throw_fndecl (inner);
+ = gimple_eh_must_not_throw_fndecl (
+ as_a <geh_mnt *> (inner));
this_region->u.must_not_throw.failure_loc
= LOCATION_LOCUS (gimple_location (tp));
except that we only execute the cleanup block for exception edges. */
static gimple_seq
-lower_cleanup (struct leh_state *state, gimple tp)
+lower_cleanup (struct leh_state *state, gtry *tp)
{
struct leh_state this_state = *state;
eh_region this_region = NULL;
&& is_gimple_reg_type (TREE_TYPE (gimple_get_lhs (stmt))))
{
tree lhs = gimple_get_lhs (stmt);
- tree tmp = create_tmp_var (TREE_TYPE (lhs), NULL);
+ tree tmp = create_tmp_var (TREE_TYPE (lhs));
gimple s = gimple_build_assign (lhs, tmp);
gimple_set_location (s, gimple_location (stmt));
gimple_set_block (s, gimple_block (stmt));
break;
case GIMPLE_SWITCH:
- verify_norecord_switch_expr (state, stmt);
+ verify_norecord_switch_expr (state, as_a <gswitch *> (stmt));
break;
case GIMPLE_TRY:
- if (gimple_try_kind (stmt) == GIMPLE_TRY_FINALLY)
- replace = lower_try_finally (state, stmt);
- else
- {
- x = gimple_seq_first_stmt (gimple_try_cleanup (stmt));
- if (!x)
- {
- replace = gimple_try_eval (stmt);
- lower_eh_constructs_1 (state, &replace);
- }
- else
- switch (gimple_code (x))
+ {
+ gtry *try_stmt = as_a <gtry *> (stmt);
+ if (gimple_try_kind (try_stmt) == GIMPLE_TRY_FINALLY)
+ replace = lower_try_finally (state, try_stmt);
+ else
+ {
+ x = gimple_seq_first_stmt (gimple_try_cleanup (try_stmt));
+ if (!x)
{
+ replace = gimple_try_eval (try_stmt);
+ lower_eh_constructs_1 (state, &replace);
+ }
+ else
+ switch (gimple_code (x))
+ {
case GIMPLE_CATCH:
- replace = lower_catch (state, stmt);
- break;
+ replace = lower_catch (state, try_stmt);
+ break;
case GIMPLE_EH_FILTER:
- replace = lower_eh_filter (state, stmt);
- break;
+ replace = lower_eh_filter (state, try_stmt);
+ break;
case GIMPLE_EH_MUST_NOT_THROW:
- replace = lower_eh_must_not_throw (state, stmt);
- break;
+ replace = lower_eh_must_not_throw (state, try_stmt);
+ break;
case GIMPLE_EH_ELSE:
- /* This code is only valid with GIMPLE_TRY_FINALLY. */
- gcc_unreachable ();
+ /* This code is only valid with GIMPLE_TRY_FINALLY. */
+ gcc_unreachable ();
default:
- replace = lower_cleanup (state, stmt);
- break;
- }
- }
+ replace = lower_cleanup (state, try_stmt);
+ break;
+ }
+ }
+ }
/* Remove the old stmt and insert the transformed sequence
instead. */
GIMPLE_PASS, /* type */
"eh", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_execute */
TV_TREE_EH, /* tv_id */
PROP_gimple_lcf, /* properties_required */
PROP_gimple_leh, /* properties_provided */
if (bodyp == NULL)
return 0;
- finally_tree.create (31);
+ finally_tree = new hash_table<finally_tree_hasher> (31);
eh_region_may_contain_throw_map = BITMAP_ALLOC (NULL);
memset (&null_state, 0, sizeof (null_state));
didn't change its value, and we don't have to re-set the function. */
gcc_assert (bodyp == gimple_body (current_function_decl));
- finally_tree.dispose ();
+ delete finally_tree;
+ finally_tree = NULL;
BITMAP_FREE (eh_region_may_contain_throw_map);
eh_seq = NULL;
no fallthru edge; false if there is. */
bool
-make_eh_dispatch_edges (gimple stmt)
+make_eh_dispatch_edges (geh_dispatch *stmt)
{
eh_region r;
eh_catch c;
The actual edge update will happen in the caller. */
void
-redirect_eh_dispatch_edge (gimple stmt, edge e, basic_block new_bb)
+redirect_eh_dispatch_edge (geh_dispatch *stmt, edge e, basic_block new_bb)
{
tree new_lab = gimple_block_label (new_bb);
bool any_changed = false;
case UNEQ_EXPR:
return honor_snans;
- case CONVERT_EXPR:
- case FIX_TRUNC_EXPR:
- /* Conversion of floating point might trap. */
- return honor_nans;
-
case NEGATE_EXPR:
case ABS_EXPR:
case CONJ_EXPR:
/* Assume that accesses to weak functions may trap, unless we know
they are certainly defined in current TU or in some other
LTO partition. */
- if (DECL_WEAK (expr) && !DECL_COMDAT (expr))
+ if (DECL_WEAK (expr) && !DECL_COMDAT (expr) && DECL_EXTERNAL (expr))
{
- struct cgraph_node *node;
- if (!DECL_EXTERNAL (expr))
- return false;
- node = cgraph_function_node (cgraph_get_node (expr), NULL);
- if (node && node->in_other_partition)
- return false;
- return true;
+ cgraph_node *node = cgraph_node::get (expr);
+ if (node)
+ node = node->function_symbol ();
+ return !(node && node->in_other_partition);
}
return false;
/* Assume that accesses to weak vars may trap, unless we know
they are certainly defined in current TU or in some other
LTO partition. */
- if (DECL_WEAK (expr) && !DECL_COMDAT (expr))
+ if (DECL_WEAK (expr) && !DECL_COMDAT (expr) && DECL_EXTERNAL (expr))
{
- varpool_node *node;
- if (!DECL_EXTERNAL (expr))
- return false;
- node = varpool_variable_node (varpool_get_node (expr), NULL);
- if (node && node->in_other_partition)
- return false;
- return true;
+ varpool_node *node = varpool_node::get (expr);
+ if (node)
+ node = node->ultimate_alias_target ();
+ return !(node && node->in_other_partition);
}
return false;
return true;
case GIMPLE_CALL:
- return !gimple_call_nothrow_p (stmt);
+ return !gimple_call_nothrow_p (as_a <gcall *> (stmt));
case GIMPLE_ASSIGN:
case GIMPLE_COND:
case GIMPLE_ASM:
if (!cfun->can_throw_non_call_exceptions)
return false;
- return gimple_asm_volatile_p (stmt);
+ return gimple_asm_volatile_p (as_a <gasm *> (stmt));
default:
return false;
bool
maybe_duplicate_eh_stmt_fn (struct function *new_fun, gimple new_stmt,
struct function *old_fun, gimple old_stmt,
- struct pointer_map_t *map, int default_lp_nr)
+ hash_map<void *, void *> *map,
+ int default_lp_nr)
{
int old_lp_nr, new_lp_nr;
- void **slot;
if (!stmt_could_throw_p (new_stmt))
return false;
eh_landing_pad old_lp, new_lp;
old_lp = (*old_fun->eh->lp_array)[old_lp_nr];
- slot = pointer_map_contains (map, old_lp);
- new_lp = (eh_landing_pad) *slot;
+ new_lp = static_cast<eh_landing_pad> (*map->get (old_lp));
new_lp_nr = new_lp->index;
}
else
eh_region old_r, new_r;
old_r = (*old_fun->eh->region_array)[-old_lp_nr];
- slot = pointer_map_contains (map, old_r);
- new_r = (eh_region) *slot;
+ new_r = static_cast<eh_region> (*map->get (old_r));
new_lp_nr = -new_r->index;
}
temporary used in the initializer for A. */
static void
-optimize_double_finally (gimple one, gimple two)
+optimize_double_finally (gtry *one, gtry *two)
{
gimple oneh;
gimple_stmt_iterator gsi;
two = NULL;
else
two = gsi_stmt (gsi);
- if (one
- && two
- && gimple_code (one) == GIMPLE_TRY
- && gimple_code (two) == GIMPLE_TRY
- && gimple_try_kind (one) == GIMPLE_TRY_FINALLY
- && gimple_try_kind (two) == GIMPLE_TRY_FINALLY)
- optimize_double_finally (one, two);
+ if (one && two)
+ if (gtry *try_one = dyn_cast <gtry *> (one))
+ if (gtry *try_two = dyn_cast <gtry *> (two))
+ if (gimple_try_kind (try_one) == GIMPLE_TRY_FINALLY
+ && gimple_try_kind (try_two) == GIMPLE_TRY_FINALLY)
+ optimize_double_finally (try_one, try_two);
if (one)
switch (gimple_code (one))
{
refactor_eh_r (gimple_try_cleanup (one));
break;
case GIMPLE_CATCH:
- refactor_eh_r (gimple_catch_handler (one));
+ refactor_eh_r (gimple_catch_handler (as_a <gcatch *> (one)));
break;
case GIMPLE_EH_FILTER:
refactor_eh_r (gimple_eh_filter_failure (one));
break;
case GIMPLE_EH_ELSE:
- refactor_eh_r (gimple_eh_else_n_body (one));
- refactor_eh_r (gimple_eh_else_e_body (one));
+ {
+ geh_else *eh_else_stmt = as_a <geh_else *> (one);
+ refactor_eh_r (gimple_eh_else_n_body (eh_else_stmt));
+ refactor_eh_r (gimple_eh_else_e_body (eh_else_stmt));
+ }
break;
default:
break;
GIMPLE_PASS, /* type */
"ehopt", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_execute */
TV_TREE_EH, /* tv_id */
PROP_gimple_lcf, /* properties_required */
0, /* properties_provided */
/* At the end of gimple optimization, we can lower RESX. */
static bool
-lower_resx (basic_block bb, gimple stmt, struct pointer_map_t *mnt_map)
+lower_resx (basic_block bb, gresx *stmt,
+ hash_map<eh_region, tree> *mnt_map)
{
int lp_nr;
eh_region src_r, dst_r;
if (lp_nr < 0)
{
basic_block new_bb;
- void **slot;
tree lab;
/* We are resuming into a MUST_NOT_CALL region. Expand a call to
the failure decl into a new block, if needed. */
gcc_assert (dst_r->type == ERT_MUST_NOT_THROW);
- slot = pointer_map_contains (mnt_map, dst_r);
+ tree *slot = mnt_map->get (dst_r);
if (slot == NULL)
{
gimple_stmt_iterator gsi2;
new_bb = create_empty_bb (bb);
- if (current_loops)
- add_bb_to_loop (new_bb, bb->loop_father);
+ add_bb_to_loop (new_bb, bb->loop_father);
lab = gimple_block_label (new_bb);
gsi2 = gsi_start_bb (new_bb);
gimple_set_location (x, dst_r->u.must_not_throw.failure_loc);
gsi_insert_after (&gsi2, x, GSI_CONTINUE_LINKING);
- slot = pointer_map_insert (mnt_map, dst_r);
- *slot = lab;
+ mnt_map->put (dst_r, lab);
}
else
{
- lab = (tree) *slot;
+ lab = *slot;
new_bb = label_to_block (lab);
}
fn = builtin_decl_implicit (BUILT_IN_EH_POINTER);
src_nr = build_int_cst (integer_type_node, src_r->index);
x = gimple_build_call (fn, 1, src_nr);
- var = create_tmp_var (ptr_type_node, NULL);
+ var = create_tmp_var (ptr_type_node);
var = make_ssa_name (var, x);
gimple_call_set_lhs (x, var);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
GIMPLE_PASS, /* type */
"resx", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_execute */
TV_TREE_EH, /* tv_id */
PROP_gimple_lcf, /* properties_required */
0, /* properties_provided */
pass_lower_resx::execute (function *fun)
{
basic_block bb;
- struct pointer_map_t *mnt_map;
bool dominance_invalidated = false;
bool any_rewritten = false;
- mnt_map = pointer_map_create ();
+ hash_map<eh_region, tree> mnt_map;
FOR_EACH_BB_FN (bb, fun)
{
gimple last = last_stmt (bb);
if (last && is_gimple_resx (last))
{
- dominance_invalidated |= lower_resx (bb, last, mnt_map);
+ dominance_invalidated |=
+ lower_resx (bb, as_a <gresx *> (last), &mnt_map);
any_rewritten = true;
}
}
- pointer_map_destroy (mnt_map);
-
if (dominance_invalidated)
{
free_dominance_info (CDI_DOMINATORS);
/* See if there is a virtual PHI node to take an updated virtual
operand from. */
- gimple vphi = NULL;
+ gphi *vphi = NULL;
tree vuse = NULL_TREE;
- for (gsi = gsi_start_phis (succbb); !gsi_end_p (gsi); gsi_next (&gsi))
+ for (gphi_iterator gpi = gsi_start_phis (succbb);
+ !gsi_end_p (gpi); gsi_next (&gpi))
{
- tree res = gimple_phi_result (gsi_stmt (gsi));
+ tree res = gimple_phi_result (gpi.phi ());
if (virtual_operand_p (res))
{
- vphi = gsi_stmt (gsi);
+ vphi = gpi.phi ();
vuse = res;
break;
}
we have found some duplicate labels and removed some edges. */
static bool
-lower_eh_dispatch (basic_block src, gimple stmt)
+lower_eh_dispatch (basic_block src, geh_dispatch *stmt)
{
gimple_stmt_iterator gsi;
int region_nr;
eh_catch c;
edge_iterator ei;
edge e;
- struct pointer_set_t *seen_values = pointer_set_create ();
+ hash_set<tree> seen_values;
/* Collect the labels for a switch. Zero the post_landing_pad
field becase we'll no longer have anything keeping these labels
attached to the handler anymore, we remove
the corresponding edge and then we delete unreachable
blocks at the end of this pass. */
- if (! pointer_set_contains (seen_values, TREE_VALUE (flt_node)))
+ if (! seen_values.contains (TREE_VALUE (flt_node)))
{
tree t = build_case_label (TREE_VALUE (flt_node),
NULL, lab);
labels.safe_push (t);
- pointer_set_insert (seen_values, TREE_VALUE (flt_node));
+ seen_values.add (TREE_VALUE (flt_node));
have_label = true;
}
fn = builtin_decl_implicit (BUILT_IN_EH_FILTER);
x = gimple_build_call (fn, 1, build_int_cst (integer_type_node,
region_nr));
- filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)), NULL);
+ filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)));
filter = make_ssa_name (filter, x);
gimple_call_set_lhs (x, filter);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
x = gimple_build_switch (filter, default_label, labels);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
}
- pointer_set_destroy (seen_values);
}
break;
fn = builtin_decl_implicit (BUILT_IN_EH_FILTER);
x = gimple_build_call (fn, 1, build_int_cst (integer_type_node,
region_nr));
- filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)), NULL);
+ filter = create_tmp_var (TREE_TYPE (TREE_TYPE (fn)));
filter = make_ssa_name (filter, x);
gimple_call_set_lhs (x, filter);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
GIMPLE_PASS, /* type */
"ehdisp", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_execute */
TV_TREE_EH, /* tv_id */
PROP_gimple_lcf, /* properties_required */
0, /* properties_provided */
continue;
if (gimple_code (last) == GIMPLE_EH_DISPATCH)
{
- redirected |= lower_eh_dispatch (bb, last);
+ redirected |= lower_eh_dispatch (bb,
+ as_a <geh_dispatch *> (last));
flags |= TODO_update_ssa_only_virtuals;
}
else if (gimple_code (last) == GIMPLE_RESX)
switch (gimple_code (stmt))
{
case GIMPLE_RESX:
- bitmap_set_bit (r_reachable, gimple_resx_region (stmt));
+ bitmap_set_bit (r_reachable,
+ gimple_resx_region (as_a <gresx *> (stmt)));
break;
case GIMPLE_EH_DISPATCH:
- bitmap_set_bit (r_reachable, gimple_eh_dispatch_region (stmt));
+ bitmap_set_bit (r_reachable,
+ gimple_eh_dispatch_region (
+ as_a <geh_dispatch *> (stmt)));
+ break;
+ case GIMPLE_CALL:
+ if (gimple_call_builtin_p (stmt, BUILT_IN_EH_COPY_VALUES))
+ for (int i = 0; i < 2; ++i)
+ {
+ tree rt = gimple_call_arg (stmt, i);
+ HOST_WIDE_INT ri = tree_to_shwi (rt);
+
+ gcc_assert (ri = (int)ri);
+ bitmap_set_bit (r_reachable, ri);
+ }
break;
default:
break;
for a different region. */
for (gsi = gsi_start_bb (e_out->dest); !gsi_end_p (gsi); gsi_next (&gsi))
{
- gimple stmt = gsi_stmt (gsi);
+ glabel *label_stmt = dyn_cast <glabel *> (gsi_stmt (gsi));
tree lab;
int lp_nr;
- if (gimple_code (stmt) != GIMPLE_LABEL)
+ if (!label_stmt)
break;
- lab = gimple_label_label (stmt);
+ lab = gimple_label_label (label_stmt);
lp_nr = EH_LANDING_PAD_NR (lab);
if (lp_nr && get_eh_region_from_lp_number (lp_nr) != lp->region)
return false;
that doesn't appear to handle virtuals. Propagate by hand. */
if (!gimple_seq_empty_p (phi_nodes (bb)))
{
- for (gsi = gsi_start_phis (bb); !gsi_end_p (gsi); )
+ for (gphi_iterator gpi = gsi_start_phis (bb); !gsi_end_p (gpi); )
{
- gimple use_stmt, phi = gsi_stmt (gsi);
+ gimple use_stmt;
+ gphi *phi = gpi.phi ();
tree lhs = gimple_phi_result (phi);
tree rhs = gimple_phi_arg_def (phi, 0);
use_operand_p use_p;
if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
SSA_NAME_OCCURS_IN_ABNORMAL_PHI (rhs) = 1;
- remove_phi_node (&gsi, true);
+ remove_phi_node (&gpi, true);
}
}
cleanup_empty_eh_merge_phis (basic_block new_bb, basic_block old_bb,
edge old_bb_out, bool change_region)
{
- gimple_stmt_iterator ngsi, ogsi;
+ gphi_iterator ngsi, ogsi;
edge_iterator ei;
edge e;
bitmap ophi_handled;
for the edges we're going to move. */
for (ngsi = gsi_start_phis (new_bb); !gsi_end_p (ngsi); gsi_next (&ngsi))
{
- gimple ophi, nphi = gsi_stmt (ngsi);
+ gphi *ophi, *nphi = ngsi.phi ();
tree nresult, nop;
nresult = gimple_phi_result (nphi);
ophi = NULL;
for (ogsi = gsi_start_phis (old_bb); !gsi_end_p (ogsi); gsi_next (&ogsi))
{
- ophi = gsi_stmt (ogsi);
+ ophi = ogsi.phi ();
if (gimple_phi_result (ophi) == nop)
break;
ophi = NULL;
we don't know what values from the other edges into NEW_BB to use. */
for (ogsi = gsi_start_phis (old_bb); !gsi_end_p (ogsi); gsi_next (&ogsi))
{
- gimple ophi = gsi_stmt (ogsi);
+ gphi *ophi = ogsi.phi ();
tree oresult = gimple_phi_result (ophi);
if (!bitmap_bit_p (ophi_handled, SSA_NAME_VERSION (oresult)))
goto fail;
we may have created a loop with multiple latches.
All of this isn't easily fixed thus cancel the affected loop
and mark the other loop as possibly having multiple latches. */
- if (current_loops
- && e->dest == e->dest->loop_father->header)
+ if (e->dest == e->dest->loop_father->header)
{
- e->dest->loop_father->header = NULL;
- e->dest->loop_father->latch = NULL;
+ mark_loop_for_removal (e->dest->loop_father);
new_bb->loop_father->latch = NULL;
- loops_state_set (LOOPS_NEED_FIXUP|LOOPS_MAY_HAVE_MULTIPLE_LATCHES);
+ loops_state_set (LOOPS_MAY_HAVE_MULTIPLE_LATCHES);
}
redirect_eh_edge_1 (e, new_bb, change_region);
redirect_edge_succ (e, new_bb);
lab = NULL;
for (gsi = gsi_start_bb (e_out->dest); !gsi_end_p (gsi); gsi_next (&gsi))
{
- gimple stmt = gsi_stmt (gsi);
+ glabel *stmt = dyn_cast <glabel *> (gsi_stmt (gsi));
int lp_nr;
- if (gimple_code (stmt) != GIMPLE_LABEL)
+ if (!stmt)
break;
lab = gimple_label_label (stmt);
lp_nr = EH_LANDING_PAD_NR (lab);
GIMPLE_PASS, /* type */
"ehcleanup", /* name */
OPTGROUP_NONE, /* optinfo_flags */
- true, /* has_execute */
TV_TREE_EH, /* tv_id */
PROP_gimple_lcf, /* properties_required */
0, /* properties_provided */
/* Similarly, but handle GIMPLE_EH_DISPATCH specifically. */
DEBUG_FUNCTION bool
-verify_eh_dispatch_edge (gimple stmt)
+verify_eh_dispatch_edge (geh_dispatch *stmt)
{
eh_region r;
eh_catch c;