* gimple-pretty-print.c (dump_gimple_eh_else): Tidy. * tree-cfg.c (verify_types_in_gimple_seq_2): Handle GIMPLE_EH_ELSE. * tree-eh.c: Restore EH_ELSE from before merge. diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c index 6c49b39..d6a2f25 100644 --- a/gcc/gimple-pretty-print.c +++ b/gcc/gimple-pretty-print.c @@ -855,7 +855,7 @@ dump_gimple_eh_else (pretty_printer *buffer, gimple gs, int spc, int flags) gimple_eh_else_n_body (gs), gimple_eh_else_e_body (gs)); else dump_gimple_fmt (buffer, spc, flags, - "IF_NORMAL_EXIT%+{%S}%-ELSE_EH_EXIT%+{%S}", + "<<>>%+{%S}%-<<>>%+{%S}", gimple_eh_else_n_body (gs), gimple_eh_else_e_body (gs)); } diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c index acac3f0..43122cd 100644 --- a/gcc/tree-cfg.c +++ b/gcc/tree-cfg.c @@ -3854,6 +3854,11 @@ verify_types_in_gimple_seq_2 (gimple_seq stmts) err |= verify_types_in_gimple_seq_2 (gimple_eh_filter_failure (stmt)); break; + case GIMPLE_EH_ELSE: + err |= verify_types_in_gimple_seq_2 (gimple_eh_else_n_body (stmt)); + err |= verify_types_in_gimple_seq_2 (gimple_eh_else_e_body (stmt)); + break; + case GIMPLE_CATCH: err |= verify_types_in_gimple_seq_2 (gimple_catch_handler (stmt)); break; diff --git a/gcc/tree-eh.c b/gcc/tree-eh.c index c0ea8d3..62e0942 100644 --- a/gcc/tree-eh.c +++ b/gcc/tree-eh.c @@ -300,6 +300,11 @@ collect_finally_tree (gimple stmt, gimple region) 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. */ @@ -550,6 +555,10 @@ replace_goto_queue_1 (gimple stmt, struct leh_tf_state *tf, case GIMPLE_EH_FILTER: replace_goto_queue_stmt_list (gimple_eh_filter_failure (stmt), tf); break; + case GIMPLE_EH_ELSE: + replace_goto_queue_stmt_list (gimple_eh_else_n_body (stmt), tf); + replace_goto_queue_stmt_list (gimple_eh_else_e_body (stmt), tf); + break; default: /* These won't have gotos in them. */ @@ -958,6 +967,21 @@ lower_try_finally_fallthru_label (struct leh_tf_state *tf) return label; } +/* 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 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 @@ -987,7 +1011,7 @@ honor_protect_cleanup_actions (struct leh_state *outer_state, 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) @@ -997,12 +1021,18 @@ honor_protect_cleanup_actions (struct leh_state *outer_state, 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 @@ -1048,7 +1078,7 @@ lower_try_finally_nofallthru (struct leh_state *state, struct leh_tf_state *tf) { tree lab, return_val; - gimple x; + gimple x, eh_else; gimple_seq finally; struct goto_queue_node *q, *qe; @@ -1072,15 +1102,35 @@ lower_try_finally_nofallthru (struct leh_state *state, 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); + } } } @@ -1100,6 +1150,18 @@ lower_try_finally_onedest (struct leh_state *state, struct leh_tf_state *tf) finally = gimple_try_cleanup (tf->top_p); tf->top_p_seq = gimple_try_eval (tf->top_p); + /* 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) @@ -1171,11 +1233,18 @@ lower_try_finally_copy (struct leh_state *state, struct leh_tf_state *tf) 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; @@ -1192,12 +1261,16 @@ lower_try_finally_copy (struct leh_state *state, struct leh_tf_state *tf) if (tf->may_throw) { - emit_post_landing_pad (&eh_seq, tf->region); - - seq = lower_try_finally_dup_block (finally, state); + /* 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); - gimple_seq_add_seq (&eh_seq, seq); + emit_post_landing_pad (&eh_seq, tf->region); + gimple_seq_add_seq (&eh_seq, seq); emit_resx (&eh_seq, tf->region); } @@ -1294,7 +1367,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) tree last_case; VEC (tree,heap) *case_label_vec; gimple_seq switch_body; - gimple x; + gimple x, eh_else; tree tmp; gimple switch_stmt; gimple_seq finally; @@ -1305,9 +1378,10 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) 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 @@ -1323,7 +1397,7 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) 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"); @@ -1360,7 +1434,23 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) 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); @@ -1500,12 +1590,22 @@ lower_try_finally_switch (struct leh_state *state, struct leh_tf_state *tf) 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; + return ndests == 1; /* Finally estimate N times, plus N gotos. */ f_estimate = count_insns_seq (finally, &eni_size_weights); @@ -1589,7 +1689,8 @@ lower_try_finally (struct leh_state *state, gimple 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); @@ -1936,6 +2037,9 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi) 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; @@ -1950,6 +2054,10 @@ lower_eh_constructs_2 (struct leh_state *state, gimple_stmt_iterator *gsi) /* 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. */ @@ -2794,6 +2902,10 @@ refactor_eh_r (gimple_seq seq) 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; }