/* Exception handling semantics and decomposition for trees.
- Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
+ Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
Free Software Foundation, Inc.
This file is part of GCC.
#include "except.h"
#include "pointer-set.h"
#include "tree-flow.h"
-#include "tree-dump.h"
#include "tree-inline.h"
-#include "tree-iterator.h"
#include "tree-pass.h"
-#include "timevar.h"
#include "langhooks.h"
#include "ggc.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
#include "gimple.h"
#include "target.h"
+#include "cfgloop.h"
/* In some instances a tree and a gimple need to be stored in a same table,
i.e. in hash tables. This is a structure to do this. */
/* Misc functions used in this file. */
-/* Compare and hash for any structure which begins with a canonical
- pointer. Assumes all pointers are interchangeable, which is sort
- of already assumed by gcc elsewhere IIRC. */
-
-static int
-struct_ptr_eq (const void *a, const void *b)
-{
- const void * const * x = (const void * const *) a;
- const void * const * y = (const void * const *) b;
- return *x == *y;
-}
-
-static hashval_t
-struct_ptr_hash (const void *a)
-{
- const void * const * x = (const void * const *) a;
- return (size_t)*x >> 4;
-}
-
-
/* Remember and lookup EH landing pad data for arbitrary statements.
Really this means any statement that could_throw_p. We could
stuff this information into the stmt_ann data structure, but:
gcc_assert (num != 0);
- n = GGC_NEW (struct throw_stmt_node);
+ n = ggc_alloc_throw_stmt_node ();
n->stmt = t;
n->lp_nr = num;
collect_finally_tree_1 (gimple_eh_filter_failure (stmt), region);
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);
+ break;
+
default:
/* A type, a decl, or some kind of statement that we're not
interested in. Don't walk them. */
#define LARGE_GOTO_QUEUE 20
-static void lower_eh_constructs_1 (struct leh_state *state, gimple_seq seq);
+static void lower_eh_constructs_1 (struct leh_state *state, gimple_seq *seq);
static gimple_seq
find_goto_replacement (struct leh_tf_state *tf, treemple stmt)
/* The real work of replace_goto_queue. Returns with TSI updated to
point to the next statement. */
-static void replace_goto_queue_stmt_list (gimple_seq, struct leh_tf_state *);
+static void replace_goto_queue_stmt_list (gimple_seq *, struct leh_tf_state *);
static void
replace_goto_queue_1 (gimple stmt, struct leh_tf_state *tf,
break;
case GIMPLE_TRY:
- replace_goto_queue_stmt_list (gimple_try_eval (stmt), tf);
- replace_goto_queue_stmt_list (gimple_try_cleanup (stmt), tf);
+ replace_goto_queue_stmt_list (gimple_try_eval_ptr (stmt), tf);
+ replace_goto_queue_stmt_list (gimple_try_cleanup_ptr (stmt), tf);
break;
case GIMPLE_CATCH:
- replace_goto_queue_stmt_list (gimple_catch_handler (stmt), tf);
+ replace_goto_queue_stmt_list (gimple_catch_handler_ptr (stmt), tf);
break;
case GIMPLE_EH_FILTER:
- replace_goto_queue_stmt_list (gimple_eh_filter_failure (stmt), tf);
+ 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);
break;
default:
/* A subroutine of replace_goto_queue. Handles GIMPLE_SEQ. */
static void
-replace_goto_queue_stmt_list (gimple_seq seq, struct leh_tf_state *tf)
+replace_goto_queue_stmt_list (gimple_seq *seq, struct leh_tf_state *tf)
{
- gimple_stmt_iterator gsi = gsi_start (seq);
+ gimple_stmt_iterator gsi = gsi_start (*seq);
while (!gsi_end_p (gsi))
replace_goto_queue_1 (gsi_stmt (gsi), tf, &gsi);
{
if (tf->goto_queue_active == 0)
return;
- replace_goto_queue_stmt_list (tf->top_p_seq, tf);
- replace_goto_queue_stmt_list (eh_seq, tf);
+ replace_goto_queue_stmt_list (&tf->top_p_seq, tf);
+ replace_goto_queue_stmt_list (&eh_seq, tf);
}
/* Add a new record to the goto queue contained in TF. NEW_STMT is the
#define verify_norecord_switch_expr(state, switch_expr)
#endif
-/* Redirect a RETURN_EXPR pointed to by STMT_P to FINLAB. Place in CONT_P
- whatever is needed to finish the return. If MOD is non-null, insert it
- before the new branch. RETURN_VALUE_P is a cache containing a temporary
- variable to be used in manipulating the value returned from the function. */
+/* Redirect a RETURN_EXPR pointed to by Q to FINLAB. If MOD is
+ non-null, insert it before the new branch. */
static void
-do_return_redirection (struct goto_queue_node *q, tree finlab, gimple_seq mod,
- tree *return_value_p)
+do_return_redirection (struct goto_queue_node *q, tree finlab, gimple_seq mod)
{
- tree ret_expr;
gimple x;
- /* In the case of a return, the queue node must be a gimple statement. */
+ /* In the case of a return, the queue node must be a gimple statement. */
gcc_assert (!q->is_label);
- ret_expr = gimple_return_retval (q->stmt.g);
+ /* Note that the return value may have already been computed, e.g.,
- if (ret_expr)
- {
- if (!*return_value_p)
- *return_value_p = ret_expr;
- else
- gcc_assert (*return_value_p == ret_expr);
- q->cont_stmt = q->stmt.g;
- /* The nasty part about redirecting the return value is that the
- return value itself is to be computed before the FINALLY block
- is executed. e.g.
-
- int x;
- int foo (void)
- {
- x = 0;
- try {
- return x;
- } finally {
- x++;
- }
- }
-
- should return 0, not 1. Arrange for this to happen by copying
- computed the return value into a local temporary. This also
- allows us to redirect multiple return statements through the
- same destination block; whether this is a net win or not really
- depends, I guess, but it does make generation of the switch in
- lower_try_finally_switch easier. */
-
- if (TREE_CODE (ret_expr) == RESULT_DECL)
+ int x;
+ int foo (void)
{
- if (!*return_value_p)
- *return_value_p = ret_expr;
- else
- gcc_assert (*return_value_p == ret_expr);
- q->cont_stmt = q->stmt.g;
+ x = 0;
+ try {
+ return x;
+ } finally {
+ x++;
+ }
}
- else
- gcc_unreachable ();
- }
- else
- /* If we don't return a value, all return statements are the same. */
- q->cont_stmt = q->stmt.g;
- if (!q->repl_stmt)
- q->repl_stmt = gimple_seq_alloc ();
+ should return 0, not 1. We don't have to do anything to make
+ this happens because the return value has been placed in the
+ RESULT_DECL already. */
+
+ q->cont_stmt = q->stmt.g;
if (mod)
gimple_seq_add_seq (&q->repl_stmt, mod);
gimple x;
gcc_assert (q->is_label);
- if (!q->repl_stmt)
- q->repl_stmt = gimple_seq_alloc ();
q->cont_stmt = gimple_build_goto (VEC_index (tree, tf->dest_array, q->index));
static void
note_eh_region_may_contain_throw (eh_region region)
{
- while (!bitmap_bit_p (eh_region_may_contain_throw_map, region->index))
+ while (bitmap_set_bit (eh_region_may_contain_throw_map, region->index))
{
- bitmap_set_bit (eh_region_may_contain_throw_map, region->index);
+ if (region->type == ERT_MUST_NOT_THROW)
+ break;
region = region->outer;
if (region == NULL)
break;
return label;
}
-/* A subroutine of lower_try_finally. If lang_protect_cleanup_actions
- returns non-null, then the language requires that the exception path out
- of a try_finally be treated specially. To wit: the code within the
- finally block may not itself throw an exception. We have two choices here.
- First we can duplicate the finally block and wrap it in a must_not_throw
- region. Second, we can generate code like
+/* A subroutine of lower_try_finally. If FINALLY consits of a
+ GIMPLE_EH_ELSE node, return it. */
+
+static inline gimple
+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 NULL;
+}
+
+/* A subroutine of lower_try_finally. If the eh_protect_cleanup_actions
+ langhook returns non-null, then the language requires that the exception
+ path out of a try_finally be treated specially. To wit: the code within
+ the finally block may not itself throw an exception. We have two choices
+ here. First we can duplicate the finally block and wrap it in a
+ must_not_throw region. Second, we can generate code like
try {
finally_block;
gimple_stmt_iterator gsi;
bool finally_may_fallthru;
gimple_seq finally;
- gimple x;
+ gimple x, eh_else;
/* First check for nothing to do. */
- if (lang_protect_cleanup_actions == NULL)
+ if (lang_hooks.eh_protect_cleanup_actions == NULL)
return;
- protect_cleanup_actions = lang_protect_cleanup_actions ();
+ protect_cleanup_actions = lang_hooks.eh_protect_cleanup_actions ();
if (protect_cleanup_actions == NULL)
return;
finally = gimple_try_cleanup (tf->top_p);
- finally_may_fallthru = gimple_seq_may_fallthru (finally);
+ eh_else = get_eh_else (finally);
/* Duplicate the FINALLY block. Only need to do this for try-finally,
- and not for cleanups. */
- if (this_state)
+ and not for cleanups. If we've got an EH_ELSE, extract it now. */
+ if (eh_else)
+ {
+ finally = gimple_eh_else_e_body (eh_else);
+ gimple_try_set_cleanup (tf->top_p, gimple_eh_else_n_body (eh_else));
+ }
+ else if (this_state)
finally = lower_try_finally_dup_block (finally, outer_state);
+ finally_may_fallthru = gimple_seq_may_fallthru (finally);
/* If this cleanup consists of a TRY_CATCH_EXPR with TRY_CATCH_IS_CLEANUP
set, the handler of the TRY_CATCH_EXPR is another cleanup which ought
lower_try_finally_nofallthru (struct leh_state *state,
struct leh_tf_state *tf)
{
- tree lab, return_val;
- gimple x;
+ tree lab;
+ gimple x, eh_else;
gimple_seq finally;
struct goto_queue_node *q, *qe;
x = gimple_build_label (lab);
gimple_seq_add_stmt (&tf->top_p_seq, x);
- return_val = NULL;
q = tf->goto_queue;
qe = q + tf->goto_queue_active;
for (; q < qe; ++q)
if (q->index < 0)
- do_return_redirection (q, lab, NULL, &return_val);
+ do_return_redirection (q, lab, NULL);
else
do_goto_redirection (q, lab, NULL, tf);
replace_goto_queue (tf);
- lower_eh_constructs_1 (state, finally);
- gimple_seq_add_seq (&tf->top_p_seq, finally);
+ /* Emit the finally block into the stream. Lower EH_ELSE at this time. */
+ eh_else = get_eh_else (finally);
+ if (eh_else)
+ {
+ finally = gimple_eh_else_n_body (eh_else);
+ lower_eh_constructs_1 (state, &finally);
+ gimple_seq_add_seq (&tf->top_p_seq, finally);
- if (tf->may_throw)
+ if (tf->may_throw)
+ {
+ finally = gimple_eh_else_e_body (eh_else);
+ lower_eh_constructs_1 (state, &finally);
+
+ emit_post_landing_pad (&eh_seq, tf->region);
+ gimple_seq_add_seq (&eh_seq, finally);
+ }
+ }
+ else
{
- emit_post_landing_pad (&eh_seq, tf->region);
+ lower_eh_constructs_1 (state, &finally);
+ gimple_seq_add_seq (&tf->top_p_seq, finally);
- x = gimple_build_goto (lab);
- gimple_seq_add_stmt (&eh_seq, x);
+ if (tf->may_throw)
+ {
+ emit_post_landing_pad (&eh_seq, tf->region);
+
+ x = gimple_build_goto (lab);
+ gimple_seq_add_stmt (&eh_seq, x);
+ }
}
}
finally = gimple_try_cleanup (tf->top_p);
tf->top_p_seq = gimple_try_eval (tf->top_p);
- lower_eh_constructs_1 (state, finally);
+ /* 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)
+ {
+ if (tf->may_throw)
+ finally = gimple_eh_else_e_body (x);
+ else
+ finally = gimple_eh_else_n_body (x);
+ }
+
+ lower_eh_constructs_1 (state, &finally);
if (tf->may_throw)
{
if (tf->may_return)
{
/* Reachable by return expressions only. Redirect them. */
- tree return_val = NULL;
for (; q < qe; ++q)
- do_return_redirection (q, finally_label, NULL, &return_val);
+ do_return_redirection (q, finally_label, NULL);
replace_goto_queue (tf);
}
else
gimple_seq finally;
gimple_seq new_stmt;
gimple_seq seq;
- gimple x;
+ gimple x, eh_else;
tree tmp;
location_t tf_loc = gimple_location (tf->try_finally_expr);
finally = gimple_try_cleanup (tf->top_p);
+
+ /* Notice EH_ELSE, and simplify some of the remaining code
+ by considering FINALLY to be the normal return path only. */
+ eh_else = get_eh_else (finally);
+ if (eh_else)
+ finally = gimple_eh_else_n_body (eh_else);
+
tf->top_p_seq = gimple_try_eval (tf->top_p);
new_stmt = NULL;
if (tf->may_fallthru)
{
seq = lower_try_finally_dup_block (finally, state);
- lower_eh_constructs_1 (state, seq);
+ lower_eh_constructs_1 (state, &seq);
gimple_seq_add_seq (&new_stmt, seq);
tmp = lower_try_finally_fallthru_label (tf);
if (tf->may_throw)
{
- seq = lower_try_finally_dup_block (finally, state);
- lower_eh_constructs_1 (state, seq);
+ /* We don't need to copy the EH path of EH_ELSE,
+ since it is only emitted once. */
+ if (eh_else)
+ seq = gimple_eh_else_e_body (eh_else);
+ else
+ seq = lower_try_finally_dup_block (finally, state);
+ lower_eh_constructs_1 (state, &seq);
emit_post_landing_pad (&eh_seq, tf->region);
gimple_seq_add_seq (&eh_seq, seq);
if (tf->goto_queue)
{
struct goto_queue_node *q, *qe;
- tree return_val = NULL;
int return_index, index;
struct labels_s
{
= create_artificial_label (tf_loc);
if (index == return_index)
- do_return_redirection (q, lab, NULL, &return_val);
+ do_return_redirection (q, lab, NULL);
else
do_goto_redirection (q, lab, NULL, tf);
gimple_seq_add_stmt (&new_stmt, x);
seq = lower_try_finally_dup_block (finally, state);
- lower_eh_constructs_1 (state, seq);
+ lower_eh_constructs_1 (state, &seq);
gimple_seq_add_seq (&new_stmt, seq);
gimple_seq_add_stmt (&new_stmt, q->cont_stmt);
lab = labels[index].label;
if (index == return_index)
- do_return_redirection (q, lab, NULL, &return_val);
+ do_return_redirection (q, lab, NULL);
else
do_goto_redirection (q, lab, NULL, tf);
}
lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf)
{
struct goto_queue_node *q, *qe;
- tree return_val = NULL;
tree finally_tmp, finally_label;
int return_index, eh_index, fallthru_index;
int nlabels, ndests, j, last_case_index;
tree last_case;
VEC (tree,heap) *case_label_vec;
- gimple_seq switch_body;
- gimple x;
+ gimple_seq switch_body = NULL;
+ gimple x, eh_else;
tree tmp;
gimple switch_stmt;
gimple_seq finally;
/* The location of the finally block. */
location_t finally_loc;
- switch_body = gimple_seq_alloc ();
+ finally = gimple_try_cleanup (tf->top_p);
+ eh_else = get_eh_else (finally);
/* Mash the TRY block to the head of the chain. */
- 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;
+ x = gimple_seq_last_stmt (finally);
+ finally_loc = x ? gimple_location (x) : tf_loc;
/* Lower the finally block itself. */
- lower_eh_constructs_1 (state, finally);
+ lower_eh_constructs_1 (state, &finally);
/* Prepare for switch statement generation. */
nlabels = VEC_length (tree, tf->dest_array);
return_index = nlabels;
eh_index = return_index + tf->may_return;
- fallthru_index = eh_index + tf->may_throw;
+ fallthru_index = eh_index + (tf->may_throw && !eh_else);
ndests = fallthru_index + tf->may_fallthru;
finally_tmp = create_tmp_var (integer_type_node, "finally_tmp");
/* Begin inserting code for getting to the finally block. Things
are done in this order to correspond to the sequence the code is
- layed out. */
+ laid out. */
if (tf->may_fallthru)
{
x = gimple_build_assign (finally_tmp,
- build_int_cst (NULL, fallthru_index));
+ build_int_cst (integer_type_node,
+ fallthru_index));
gimple_seq_add_stmt (&tf->top_p_seq, x);
- last_case = build3 (CASE_LABEL_EXPR, void_type_node,
- build_int_cst (NULL, fallthru_index),
- NULL, create_artificial_label (tf_loc));
+ tmp = build_int_cst (integer_type_node, fallthru_index);
+ last_case = build_case_label (tmp, NULL,
+ create_artificial_label (tf_loc));
VEC_quick_push (tree, case_label_vec, last_case);
last_case_index++;
gimple_seq_add_stmt (&switch_body, x);
}
- if (tf->may_throw)
+ /* For EH_ELSE, emit the exception path (plus resx) now, then
+ subsequently we only need consider the normal path. */
+ if (eh_else)
+ {
+ if (tf->may_throw)
+ {
+ finally = gimple_eh_else_e_body (eh_else);
+ lower_eh_constructs_1 (state, &finally);
+
+ emit_post_landing_pad (&eh_seq, tf->region);
+ gimple_seq_add_seq (&eh_seq, finally);
+ emit_resx (&eh_seq, tf->region);
+ }
+
+ finally = gimple_eh_else_n_body (eh_else);
+ }
+ else if (tf->may_throw)
{
emit_post_landing_pad (&eh_seq, tf->region);
x = gimple_build_assign (finally_tmp,
- build_int_cst (NULL, eh_index));
+ build_int_cst (integer_type_node, eh_index));
gimple_seq_add_stmt (&eh_seq, x);
x = gimple_build_goto (finally_label);
gimple_seq_add_stmt (&eh_seq, x);
- last_case = build3 (CASE_LABEL_EXPR, void_type_node,
- build_int_cst (NULL, eh_index),
- NULL, create_artificial_label (tf_loc));
+ tmp = build_int_cst (integer_type_node, eh_index);
+ last_case = build_case_label (tmp, NULL,
+ create_artificial_label (tf_loc));
VEC_quick_push (tree, case_label_vec, last_case);
last_case_index++;
entrance through a particular edge. */
for (; q < qe; ++q)
{
- gimple_seq mod;
+ gimple_seq mod = NULL;
int switch_id;
unsigned int case_index;
- mod = gimple_seq_alloc ();
-
if (q->index < 0)
{
x = gimple_build_assign (finally_tmp,
- build_int_cst (NULL, return_index));
+ build_int_cst (integer_type_node,
+ return_index));
gimple_seq_add_stmt (&mod, x);
- do_return_redirection (q, finally_label, mod, &return_val);
+ do_return_redirection (q, finally_label, mod);
switch_id = return_index;
}
else
{
x = gimple_build_assign (finally_tmp,
- build_int_cst (NULL, q->index));
+ build_int_cst (integer_type_node, q->index));
gimple_seq_add_stmt (&mod, x);
do_goto_redirection (q, finally_label, mod, tf);
switch_id = q->index;
{
tree case_lab;
void **slot;
- case_lab = build3 (CASE_LABEL_EXPR, void_type_node,
- build_int_cst (NULL, switch_id),
- NULL, NULL);
+ 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. We don't create the new label while
- walking the goto_queue because pointers don't offer a stable
- order. */
+ it in the loop below. */
if (!cont_map)
cont_map = pointer_map_create ();
slot = pointer_map_insert (cont_map, case_lab);
}
for (j = last_case_index; j < last_case_index + nlabels; j++)
{
- tree label;
gimple cont_stmt;
void **slot;
gcc_assert (cont_map);
slot = pointer_map_contains (cont_map, last_case);
- /* As the comment above suggests, CASE_LABEL (last_case) was just a
- placeholder, it does not store an actual label, yet. */
gcc_assert (slot);
cont_stmt = *(gimple *) slot;
- label = create_artificial_label (tf_loc);
- CASE_LABEL (last_case) = label;
-
- x = gimple_build_label (label);
+ x = gimple_build_label (CASE_LABEL (last_case));
gimple_seq_add_stmt (&switch_body, x);
gimple_seq_add_stmt (&switch_body, cont_stmt);
maybe_record_in_goto_queue (state, cont_stmt);
/* Build the switch statement, setting last_case to be the default
label. */
- switch_stmt = gimple_build_switch_vec (finally_tmp, last_case,
- case_label_vec);
+ switch_stmt = gimple_build_switch (finally_tmp, last_case,
+ case_label_vec);
gimple_set_location (switch_stmt, finally_loc);
/* Need to link SWITCH_STMT after running replace_goto_queue
the estimate of the size of the switch machinery we'd have to add. */
static bool
-decide_copy_try_finally (int ndests, gimple_seq finally)
+decide_copy_try_finally (int ndests, bool may_throw, gimple_seq finally)
{
int f_estimate, sw_estimate;
+ gimple eh_else;
+
+ /* If there's an EH_ELSE involved, the exception path is separate
+ and really doesn't come into play for this computation. */
+ eh_else = get_eh_else (finally);
+ if (eh_else)
+ {
+ ndests -= may_throw;
+ finally = gimple_eh_else_n_body (eh_else);
+ }
if (!optimize)
- return false;
+ {
+ gimple_stmt_iterator gsi;
+
+ if (ndests == 1)
+ return true;
+
+ for (gsi = gsi_start (finally); !gsi_end_p (gsi); gsi_next (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+ if (!is_gimple_debug (stmt) && !gimple_clobber_p (stmt))
+ return false;
+ }
+ return true;
+ }
/* Finally estimate N times, plus N gotos. */
f_estimate = count_insns_seq (finally, &eni_size_weights);
old_eh_seq = eh_seq;
eh_seq = NULL;
- lower_eh_constructs_1 (&this_state, gimple_try_eval(tp));
+ lower_eh_constructs_1 (&this_state, gimple_try_eval_ptr (tp));
/* Determine if the try block is escaped through the bottom. */
this_tf.may_fallthru = gimple_seq_may_fallthru (gimple_try_eval (tp));
/* We can easily special-case redirection to a single destination. */
else if (ndests == 1)
lower_try_finally_onedest (state, &this_tf);
- else if (decide_copy_try_finally (ndests, gimple_try_cleanup (tp)))
+ else if (decide_copy_try_finally (ndests, this_tf.may_throw,
+ gimple_try_cleanup (tp)))
lower_try_finally_copy (state, &this_tf);
else
lower_try_finally_switch (state, &this_tf);
}
VEC_free (tree, heap, this_tf.dest_array);
- if (this_tf.goto_queue)
- free (this_tf.goto_queue);
+ free (this_tf.goto_queue);
if (this_tf.goto_queue_map)
pointer_map_destroy (this_tf.goto_queue_map);
struct leh_state this_state = *state;
gimple_stmt_iterator gsi;
tree out_label;
- gimple_seq new_seq;
+ gimple_seq new_seq, cleanup;
gimple x;
location_t try_catch_loc = gimple_location (tp);
this_state.cur_region = try_region;
}
- lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
+ lower_eh_constructs_1 (&this_state, gimple_try_eval_ptr (tp));
if (!eh_region_may_contain_throw (try_region))
return gimple_try_eval (tp);
this_state.ehp_region = try_region;
out_label = NULL;
- for (gsi = gsi_start (gimple_try_cleanup (tp));
+ cleanup = gimple_try_cleanup (tp);
+ for (gsi = gsi_start (cleanup);
!gsi_end_p (gsi);
gsi_next (&gsi))
{
c = gen_eh_region_catch (try_region, gimple_catch_types (gcatch));
handler = gimple_catch_handler (gcatch);
- lower_eh_constructs_1 (&this_state, handler);
+ lower_eh_constructs_1 (&this_state, &handler);
c->label = create_artificial_label (UNKNOWN_LOCATION);
x = gimple_build_label (c->label);
this_state.cur_region = this_region;
}
- lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
+ lower_eh_constructs_1 (&this_state, gimple_try_eval_ptr (tp));
if (!eh_region_may_contain_throw (this_region))
return gimple_try_eval (tp);
x = gimple_build_label (this_region->u.allowed.label);
gimple_seq_add_stmt (&new_seq, x);
- lower_eh_constructs_1 (&this_state, gimple_eh_filter_failure (inner));
+ lower_eh_constructs_1 (&this_state, gimple_eh_filter_failure_ptr (inner));
gimple_seq_add_seq (&new_seq, gimple_eh_filter_failure (inner));
gimple_try_set_cleanup (tp, new_seq);
this_state.cur_region = this_region;
}
- lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
+ lower_eh_constructs_1 (&this_state, gimple_try_eval_ptr (tp));
return gimple_try_eval (tp);
}
this_state.cur_region = this_region;
}
- lower_eh_constructs_1 (&this_state, gimple_try_eval (tp));
+ lower_eh_constructs_1 (&this_state, gimple_try_eval_ptr (tp));
if (cleanup_dead || !eh_region_may_contain_throw (this_region))
return gimple_try_eval (tp);
{
/* In this case honor_protect_cleanup_actions had nothing to do,
and we should process this normally. */
- lower_eh_constructs_1 (state, gimple_try_cleanup (tp));
+ lower_eh_constructs_1 (state, gimple_try_cleanup_ptr (tp));
result = frob_into_branch_around (tp, this_region,
fake_tf.fallthru_label);
}
this zero argument with the current catch region number. */
if (state->ehp_region)
{
- tree nr = build_int_cst (NULL, state->ehp_region->index);
+ tree nr = build_int_cst (integer_type_node,
+ state->ehp_region->index);
gimple_call_set_arg (stmt, 0, nr);
}
else
{
/* The user has dome something silly. Remove it. */
- rhs = build_int_cst (ptr_type_node, 0);
+ rhs = null_pointer_node;
goto do_replace;
}
break;
/* 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 thru (no noreturn calls e.g.), otherwise
+ potentially fall through (no noreturn calls e.g.), otherwise
this new assignment might create fake fallthru regions. */
if (stmt_could_throw_p (stmt)
&& gimple_has_lhs (stmt)
if (!x)
{
replace = gimple_try_eval (stmt);
- lower_eh_constructs_1 (state, replace);
+ lower_eh_constructs_1 (state, &replace);
}
else
switch (gimple_code (x))
case GIMPLE_EH_MUST_NOT_THROW:
replace = lower_eh_must_not_throw (state, stmt);
break;
+ case GIMPLE_EH_ELSE:
+ /* This code is only valid with GIMPLE_TRY_FINALLY. */
+ gcc_unreachable ();
default:
replace = lower_cleanup (state, stmt);
break;
/* Return since we don't want gsi_next () */
return;
+ case GIMPLE_EH_ELSE:
+ /* We should be eliminating this in lower_try_finally et al. */
+ gcc_unreachable ();
+
default:
/* A type, a decl, or some kind of statement that we're not
interested in. Don't walk them. */
/* A helper to unwrap a gimple_seq and feed stmts to lower_eh_constructs_2. */
static void
-lower_eh_constructs_1 (struct leh_state *state, gimple_seq seq)
+lower_eh_constructs_1 (struct leh_state *state, gimple_seq *pseq)
{
gimple_stmt_iterator gsi;
- for (gsi = gsi_start (seq); !gsi_end_p (gsi);)
+ for (gsi = gsi_start (*pseq); !gsi_end_p (gsi);)
lower_eh_constructs_2 (state, &gsi);
}
memset (&null_state, 0, sizeof (null_state));
collect_finally_tree_1 (bodyp, NULL);
- lower_eh_constructs_1 (&null_state, bodyp);
+ lower_eh_constructs_1 (&null_state, &bodyp);
+ gimple_set_body (current_function_decl, bodyp);
/* We assume there's a return statement, or something, at the end of
the function, and thus ploping the EH sequence afterward won't
PROP_gimple_leh, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func /* todo_flags_finish */
+ 0 /* todo_flags_finish */
}
};
\f
return true;
return false;
+ case COMPLEX_EXPR:
+ case CONSTRUCTOR:
+ /* Constructing an object cannot trap. */
+ return false;
+
default:
/* Any floating arithmetic may trap. */
if (fp_operation && flag_trapping_math)
switch (code)
{
case TARGET_MEM_REF:
- /* For TARGET_MEM_REFs use the information based on the original
- reference. */
- expr = TMR_ORIGINAL (expr);
- code = TREE_CODE (expr);
- goto restart;
+ if (TREE_CODE (TMR_BASE (expr)) == ADDR_EXPR
+ && !TMR_INDEX (expr) && !TMR_INDEX2 (expr))
+ return false;
+ return !TREE_THIS_NOTRAP (expr);
case COMPONENT_REF:
case REALPART_EXPR:
return false;
return !in_array_bounds_p (expr);
+ case MEM_REF:
+ if (TREE_CODE (TREE_OPERAND (expr, 0)) == ADDR_EXPR)
+ return false;
+ /* Fallthru. */
case INDIRECT_REF:
- case ALIGN_INDIRECT_REF:
- case MISALIGNED_INDIRECT_REF:
return !TREE_THIS_NOTRAP (expr);
case ASM_EXPR:
case CALL_EXPR:
t = get_callee_fndecl (expr);
/* Assume that calls to weak functions may trap. */
- if (!t || !DECL_P (t) || DECL_WEAK (t))
+ if (!t || !DECL_P (t))
return true;
+ if (DECL_WEAK (t))
+ return tree_could_trap_p (t);
+ return false;
+
+ case FUNCTION_DECL:
+ /* 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))
+ {
+ struct cgraph_node *node;
+ if (!DECL_EXTERNAL (expr))
+ return false;
+ node = cgraph_function_node (cgraph_get_node (expr), NULL);
+ if (node && node->symbol.in_other_partition)
+ return false;
+ return true;
+ }
+ return false;
+
+ case VAR_DECL:
+ /* 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))
+ {
+ struct varpool_node *node;
+ if (!DECL_EXTERNAL (expr))
+ return false;
+ node = varpool_variable_node (varpool_get_node (expr), NULL);
+ if (node && node->symbol.in_other_partition)
+ return false;
+ return true;
+ }
return false;
default:
|| TREE_CODE_CLASS (code) == tcc_unary
|| TREE_CODE_CLASS (code) == tcc_binary)
{
- t = gimple_expr_type (stmt);
+ if (is_gimple_assign (stmt)
+ && TREE_CODE_CLASS (code) == tcc_comparison)
+ t = TREE_TYPE (gimple_assign_rhs1 (stmt));
+ else if (gimple_code (stmt) == GIMPLE_COND)
+ t = TREE_TYPE (gimple_cond_lhs (stmt));
+ else
+ t = gimple_expr_type (stmt);
fp_operation = FLOAT_TYPE_P (t);
if (fp_operation)
{
return false;
}
-/* Given a statement OLD_STMT in OLD_FUN and a duplicate statment NEW_STMT
+/* Given a statement OLD_STMT in OLD_FUN and a duplicate statement NEW_STMT
in NEW_FUN, copy the EH table data from OLD_STMT to NEW_STMT. The MAP
operand is the return value of duplicate_eh_regions. */
|| gimple_call_lhs (twos)
|| gimple_call_chain (ones)
|| gimple_call_chain (twos)
- || !operand_equal_p (gimple_call_fn (ones), gimple_call_fn (twos), 0)
+ || !gimple_call_same_target_p (ones, twos)
|| gimple_call_num_args (ones) != gimple_call_num_args (twos))
return false;
{
gimple oneh;
gimple_stmt_iterator gsi;
+ gimple_seq cleanup;
- gsi = gsi_start (gimple_try_cleanup (one));
+ cleanup = gimple_try_cleanup (one);
+ gsi = gsi_start (cleanup);
if (!gsi_one_before_end_p (gsi))
return;
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));
+ break;
default:
break;
}
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func /* todo_flags_finish */
+ 0 /* todo_flags_finish */
}
};
\f
Resolve this by expanding the resx node to an abort. */
- fn = implicit_built_in_decls[BUILT_IN_TRAP];
+ fn = builtin_decl_implicit (BUILT_IN_TRAP);
x = gimple_build_call (fn, 0);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
gimple_stmt_iterator gsi2;
new_bb = create_empty_bb (bb);
+ if (current_loops)
+ add_bb_to_loop (new_bb, bb->loop_father);
lab = gimple_block_label (new_bb);
gsi2 = gsi_start_bb (new_bb);
else
{
edge_iterator ei;
- tree dst_nr = build_int_cst (NULL, dst_r->index);
+ tree dst_nr = build_int_cst (integer_type_node, dst_r->index);
- fn = implicit_built_in_decls[BUILT_IN_EH_COPY_VALUES];
- src_nr = build_int_cst (NULL, src_r->index);
+ fn = builtin_decl_implicit (BUILT_IN_EH_COPY_VALUES);
+ src_nr = build_int_cst (integer_type_node, src_r->index);
x = gimple_build_call (fn, 2, dst_nr, src_nr);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
with no arguments for C++ and Java. Check for that. */
if (src_r->use_cxa_end_cleanup)
{
- fn = implicit_built_in_decls[BUILT_IN_CXA_END_CLEANUP];
+ fn = builtin_decl_implicit (BUILT_IN_CXA_END_CLEANUP);
x = gimple_build_call (fn, 0);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
}
else
{
- fn = implicit_built_in_decls[BUILT_IN_EH_POINTER];
- src_nr = build_int_cst (NULL, src_r->index);
+ 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 = make_ssa_name (var, x);
gimple_call_set_lhs (x, var);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
- fn = implicit_built_in_decls[BUILT_IN_UNWIND_RESUME];
+ fn = builtin_decl_implicit (BUILT_IN_UNWIND_RESUME);
x = gimple_build_call (fn, 1, var);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
}
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func | TODO_verify_flow /* todo_flags_finish */
+ TODO_verify_flow /* todo_flags_finish */
}
};
+/* Try to optimize var = {v} {CLOBBER} stmts followed just by
+ external throw. */
+
+static void
+optimize_clobbers (basic_block bb)
+{
+ gimple_stmt_iterator gsi = gsi_last_bb (bb);
+ for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+ if (is_gimple_debug (stmt))
+ continue;
+ if (!gimple_clobber_p (stmt)
+ || TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME)
+ return;
+ unlink_stmt_vdef (stmt);
+ gsi_remove (&gsi, true);
+ release_defs (stmt);
+ }
+}
+
+/* Try to sink var = {v} {CLOBBER} stmts followed just by
+ internal throw to successor BB. */
+
+static int
+sink_clobbers (basic_block bb)
+{
+ edge e;
+ edge_iterator ei;
+ gimple_stmt_iterator gsi, dgsi;
+ basic_block succbb;
+ bool any_clobbers = false;
+
+ /* Only optimize if BB has a single EH successor and
+ all predecessor edges are EH too. */
+ if (!single_succ_p (bb)
+ || (single_succ_edge (bb)->flags & EDGE_EH) == 0)
+ return 0;
+
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ if ((e->flags & EDGE_EH) == 0)
+ return 0;
+ }
+
+ /* And BB contains only CLOBBER stmts before the final
+ RESX. */
+ gsi = gsi_last_bb (bb);
+ for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+ if (is_gimple_debug (stmt))
+ continue;
+ if (gimple_code (stmt) == GIMPLE_LABEL)
+ break;
+ if (!gimple_clobber_p (stmt)
+ || TREE_CODE (gimple_assign_lhs (stmt)) == SSA_NAME)
+ return 0;
+ any_clobbers = true;
+ }
+ if (!any_clobbers)
+ return 0;
+
+ succbb = single_succ (bb);
+ dgsi = gsi_after_labels (succbb);
+ gsi = gsi_last_bb (bb);
+ for (gsi_prev (&gsi); !gsi_end_p (gsi); gsi_prev (&gsi))
+ {
+ gimple stmt = gsi_stmt (gsi);
+ if (is_gimple_debug (stmt))
+ continue;
+ if (gimple_code (stmt) == GIMPLE_LABEL)
+ break;
+ unlink_stmt_vdef (stmt);
+ gsi_remove (&gsi, false);
+ /* Trigger the operand scanner to cause renaming for virtual
+ operands for this statement.
+ ??? Given the simple structure of this code manually
+ figuring out the reaching definition should not be too hard. */
+ if (gimple_vuse (stmt))
+ gimple_set_vuse (stmt, NULL_TREE);
+ gsi_insert_before (&dgsi, stmt, GSI_SAME_STMT);
+ }
+
+ return TODO_update_ssa_only_virtuals;
+}
/* At the end of inlining, we can lower EH_DISPATCH. Return true when
we have found some duplicate labels and removed some edges. */
/* Collect the labels for a switch. Zero the post_landing_pad
field becase we'll no longer have anything keeping these labels
- in existance and the optimizer will be free to merge these
+ in existence and the optimizer will be free to merge these
blocks at will. */
for (c = r->u.eh_try.first_catch; c ; c = c->next_catch)
{
blocks at the end of this pass. */
if (! pointer_set_contains (seen_values, TREE_VALUE (flt_node)))
{
- tree t = build3 (CASE_LABEL_EXPR, void_type_node,
- TREE_VALUE (flt_node), NULL, lab);
+ tree t = build_case_label (TREE_VALUE (flt_node),
+ NULL, lab);
VEC_safe_push (tree, heap, labels, t);
pointer_set_insert (seen_values, TREE_VALUE (flt_node));
have_label = true;
}
else
{
- fn = implicit_built_in_decls[BUILT_IN_EH_FILTER];
- x = gimple_build_call (fn, 1, build_int_cst (NULL, region_nr));
+ 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 = make_ssa_name (filter, x);
gimple_call_set_lhs (x, filter);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
/* Turn the default label into a default case. */
- default_label = build3 (CASE_LABEL_EXPR, void_type_node,
- NULL, NULL, default_label);
+ default_label = build_case_label (NULL, NULL, default_label);
sort_case_labels (labels);
- x = gimple_build_switch_vec (filter, default_label, labels);
+ x = gimple_build_switch (filter, default_label, labels);
gsi_insert_before (&gsi, x, GSI_SAME_STMT);
VEC_free (tree, heap, labels);
edge b_e = BRANCH_EDGE (src);
edge f_e = FALLTHRU_EDGE (src);
- fn = implicit_built_in_decls[BUILT_IN_EH_FILTER];
- x = gimple_build_call (fn, 1, build_int_cst (NULL, region_nr));
+ 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 = make_ssa_name (filter, x);
gimple_call_set_lhs (x, filter);
execute_lower_eh_dispatch (void)
{
basic_block bb;
- bool any_rewritten = false;
+ int flags = 0;
bool redirected = false;
assign_filter_values ();
FOR_EACH_BB (bb)
{
gimple last = last_stmt (bb);
- if (last && gimple_code (last) == GIMPLE_EH_DISPATCH)
+ if (last == NULL)
+ continue;
+ if (gimple_code (last) == GIMPLE_EH_DISPATCH)
{
redirected |= lower_eh_dispatch (bb, last);
- any_rewritten = true;
+ flags |= TODO_update_ssa_only_virtuals;
+ }
+ else if (gimple_code (last) == GIMPLE_RESX)
+ {
+ if (stmt_can_throw_external (last))
+ optimize_clobbers (bb);
+ else
+ flags |= sink_clobbers (bb);
}
}
if (redirected)
delete_unreachable_blocks ();
- return any_rewritten ? TODO_update_ssa_only_virtuals : 0;
+ return flags;
}
static bool
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func | TODO_verify_flow /* todo_flags_finish */
+ TODO_verify_flow /* todo_flags_finish */
}
};
\f
FOR_EACH_BB (bb)
{
- gimple_stmt_iterator gsi = gsi_start_bb (bb);
+ gimple_stmt_iterator gsi;
for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
{
SET_BIT (r_reachable, region->index);
SET_BIT (lp_reachable, lp_nr);
}
+
+ /* Avoid removing regions referenced from RESX/EH_DISPATCH. */
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_RESX:
+ SET_BIT (r_reachable, gimple_resx_region (stmt));
+ break;
+ case GIMPLE_EH_DISPATCH:
+ SET_BIT (r_reachable, gimple_eh_dispatch_region (stmt));
+ break;
+ default:
+ break;
+ }
}
}
#endif
}
+/* Remove unreachable handlers if any landing pads have been removed after
+ last ehcleanup pass (due to gimple_purge_dead_eh_edges). */
+
+void
+maybe_remove_unreachable_handlers (void)
+{
+ eh_landing_pad lp;
+ int i;
+
+ if (cfun->eh == NULL)
+ return;
+
+ for (i = 1; VEC_iterate (eh_landing_pad, cfun->eh->lp_array, i, lp); ++i)
+ if (lp && lp->post_landing_pad)
+ {
+ if (label_to_block (lp->post_landing_pad) == NULL)
+ {
+ remove_unreachable_handlers ();
+ return;
+ }
+ }
+}
+
/* Remove regions that do not have landing pads. This assumes
that remove_unreachable_handlers has already been run, and
that we've just manipulated the landing pads since then. */
{
eh_region r;
int i;
+ sbitmap r_reachable;
+ basic_block bb;
+
+ r_reachable = sbitmap_alloc (VEC_length (eh_region, cfun->eh->region_array));
+ sbitmap_zero (r_reachable);
+
+ FOR_EACH_BB (bb)
+ {
+ gimple stmt = last_stmt (bb);
+ if (stmt)
+ /* Avoid removing regions referenced from RESX/EH_DISPATCH. */
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_RESX:
+ SET_BIT (r_reachable, gimple_resx_region (stmt));
+ break;
+ case GIMPLE_EH_DISPATCH:
+ SET_BIT (r_reachable, gimple_eh_dispatch_region (stmt));
+ break;
+ default:
+ break;
+ }
+ }
for (i = 1; VEC_iterate (eh_region, cfun->eh->region_array, i, r); ++i)
- if (r && r->landing_pads == NULL && r->type != ERT_MUST_NOT_THROW)
+ if (r && r->landing_pads == NULL && r->type != ERT_MUST_NOT_THROW
+ && !TEST_BIT (r_reachable, i))
{
if (dump_file)
fprintf (dump_file, "Removing unreachable region %d\n", i);
remove_eh_handler (r);
}
+
+ sbitmap_free (r_reachable);
}
/* Undo critical edge splitting on an EH landing pad. Earlier, we
bitmap rename_virts;
bitmap ophi_handled;
+ /* The destination block must not be a regular successor for any
+ of the preds of the landing pad. Thus, avoid turning
+ <..>
+ | \ EH
+ | <..>
+ | /
+ <..>
+ into
+ <..>
+ | | 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;
+
FOR_EACH_EDGE (e, ei, old_bb->preds)
redirect_edge_var_map_clear (e);
/* If we did find the corresponding PHI, copy those inputs. */
if (ophi)
{
+ /* If NOP is used somewhere else beyond phis in new_bb, give up. */
+ if (!has_single_use (nop))
+ {
+ imm_use_iterator imm_iter;
+ use_operand_p use_p;
+
+ FOR_EACH_IMM_USE_FAST (use_p, imm_iter, nop)
+ {
+ if (!gimple_debug_bind_p (USE_STMT (use_p))
+ && (gimple_code (USE_STMT (use_p)) != GIMPLE_PHI
+ || gimple_bb (USE_STMT (use_p)) != new_bb))
+ goto fail;
+ }
+ }
bitmap_set_bit (ophi_handled, SSA_NAME_VERSION (nop));
FOR_EACH_EDGE (e, ei, old_bb->preds)
{
}
/* If we didn't find the PHI, but it's a VOP, remember to rename
it later, assuming all other tests succeed. */
- else if (!is_gimple_reg (nresult))
+ else if (virtual_operand_p (nresult))
bitmap_set_bit (rename_virts, SSA_NAME_VERSION (nresult));
/* If we didn't find the PHI, and it's a real variable, we know
from the fact that OLD_BB is tree_empty_eh_handler_p that the
for (ei = ei_start (old_bb->preds); (e = ei_safe_edge (ei)); )
if (e->flags & EDGE_EH)
{
+ /* ??? CFG manipluation routines do not try to update loop
+ form on edge redirection. Do so manually here for now. */
+ /* If we redirect a loop entry or latch edge that will either create
+ a multiple entry loop or rotate the loop. If the loops merge
+ 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)
+ {
+ e->dest->loop_father->header = NULL;
+ e->dest->loop_father->latch = NULL;
+ new_bb->loop_father->latch = NULL;
+ loops_state_set (LOOPS_NEED_FIXUP|LOOPS_MAY_HAVE_MULTIPLE_LATCHES);
+ }
redirect_eh_edge_1 (e, new_bb, change_region);
redirect_edge_succ (e, new_bb);
flush_pending_stmts (e);
/* Delete the RESX that was matched within the empty handler block. */
gsi = gsi_last_bb (bb);
- mark_virtual_ops_for_renaming (gsi_stmt (gsi));
+ unlink_stmt_vdef (gsi_stmt (gsi));
gsi_remove (&gsi, true);
/* Clean up E_OUT for the fallthru. */
return false;
}
+/* Return true if edge E_FIRST is part of an empty infinite loop
+ or leads to such a loop through a series of single successor
+ empty bbs. */
+
+static bool
+infinite_empty_loop_p (edge e_first)
+{
+ bool inf_loop = false;
+ edge e;
+
+ if (e_first->dest == e_first->src)
+ return true;
+
+ e_first->src->aux = (void *) 1;
+ for (e = e_first; single_succ_p (e->dest); e = single_succ_edge (e->dest))
+ {
+ gimple_stmt_iterator gsi;
+ if (e->dest->aux)
+ {
+ inf_loop = true;
+ break;
+ }
+ e->dest->aux = (void *) 1;
+ gsi = gsi_after_labels (e->dest);
+ if (!gsi_end_p (gsi) && is_gimple_debug (gsi_stmt (gsi)))
+ gsi_next_nondebug (&gsi);
+ if (!gsi_end_p (gsi))
+ break;
+ }
+ e_first->src->aux = NULL;
+ for (e = e_first; e->dest->aux; e = single_succ_edge (e->dest))
+ e->dest->aux = NULL;
+
+ return inf_loop;
+}
+
/* Examine the block associated with LP to determine if it's an empty
handler for its EH region. If so, attempt to redirect EH edges to
an outer region. Return true the CFG was updated in any way. This
edge_iterator ei;
edge e, e_out;
bool has_non_eh_pred;
+ bool ret = false;
int new_lp_nr;
/* There can be zero or one edges out of BB. This is the quickest test. */
default:
return false;
}
+
+ resx = last_stmt (bb);
+ if (resx && is_gimple_resx (resx))
+ {
+ if (stmt_can_throw_external (resx))
+ optimize_clobbers (bb);
+ else if (sink_clobbers (bb))
+ ret = true;
+ }
+
gsi = gsi_after_labels (bb);
/* Make sure to skip debug statements. */
/* If the block is totally empty, look for more unsplitting cases. */
if (gsi_end_p (gsi))
- return cleanup_empty_eh_unsplit (bb, e_out, lp);
+ {
+ /* For the degenerate case of an infinite loop bail out. */
+ if (infinite_empty_loop_p (e_out))
+ return ret;
- /* The block should consist only of a single RESX statement. */
+ return ret | cleanup_empty_eh_unsplit (bb, e_out, lp);
+ }
+
+ /* The block should consist only of a single RESX statement, modulo a
+ preceding call to __builtin_stack_restore if there is no outgoing
+ edge, since the call can be eliminated in this case. */
resx = gsi_stmt (gsi);
+ if (!e_out && gimple_call_builtin_p (resx, BUILT_IN_STACK_RESTORE))
+ {
+ gsi_next (&gsi);
+ resx = gsi_stmt (gsi);
+ }
if (!is_gimple_resx (resx))
- return false;
+ return ret;
gcc_assert (gsi_one_before_end_p (gsi));
/* Determine if there are non-EH edges, or resx edges into the handler. */
return true;
}
- return false;
+ return ret;
succeed:
if (dump_file && (dump_flags & TDF_DETAILS))
*/
static unsigned int
-execute_cleanup_eh (void)
+execute_cleanup_eh_1 (void)
{
/* Do this first: unsplit_all_eh and cleanup_all_empty_eh can die
looking up unreachable landing pads. */
return 0;
}
+static unsigned int
+execute_cleanup_eh (void)
+{
+ int ret = execute_cleanup_eh_1 ();
+
+ /* If the function no longer needs an EH personality routine
+ clear it. This exposes cross-language inlining opportunities
+ and avoids references to a never defined personality routine. */
+ if (DECL_FUNCTION_PERSONALITY (current_function_decl)
+ && function_needs_eh_personality (cfun) != eh_personality_lang)
+ DECL_FUNCTION_PERSONALITY (current_function_decl) = NULL_TREE;
+
+ return ret;
+}
+
static bool
gate_cleanup_eh (void)
{
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func /* todo_flags_finish */
+ 0 /* todo_flags_finish */
}
};
\f
/* Verify that BB containing STMT as the last statement, has precisely the
edge that make_eh_edges would create. */
-bool
+DEBUG_FUNCTION bool
verify_eh_edges (gimple stmt)
{
basic_block bb = gimple_bb (stmt);
/* Similarly, but handle GIMPLE_EH_DISPATCH specifically. */
-bool
+DEBUG_FUNCTION bool
verify_eh_dispatch_edge (gimple stmt)
{
eh_region r;