+2019-07-04 Alexandre Oliva <oliva@adacore.com>
+
+ * doc/generic.texi (Cleanups): Document EH_ELSE_EXPR.
+ * except.c: Likewise.
+ * expr.c (expand_expr_real_1): Reject it.
+ * gimplify.c (gimplify_expr): Gimplify it, within
+ TRY_FINALLY_EXPR.
+ * tree-dump.c (dequeue_and_dump): Dump it.
+ * tree-pretty-print.c (dump_generic_node): Likewise.
+ * tree.c (block_may_fallthru): Handle it.
+ * tree.def (EH_ELSE_EXPR): Introduce it.
+ * gimple-pretty-print.c (dump_gimple_try): Dump TRY_FINALLY
+ with GIMPLE_EH_ELSE as try/finally/else.
+
2019-07-04 Richard Biener <rguenther@suse.de>
PR ipa/91062
falling off the end, execution continues wherever the first sequence
would have continued, by falling off the end, or doing a goto, etc.
+If the second sequence is an @code{EH_ELSE_EXPR} selector, then the
+sequence in its first operand is used when the first sequence completes
+normally, and that in its second operand is used for exceptional
+cleanups, i.e., when an exception propagates out of the first sequence.
+
@code{TRY_FINALLY_EXPR} complicates the flow graph, since the cleanup
needs to appear on every edge out of the controlled block; this
reduces the freedom to move code across these edges. Therefore, the
the compilation process:
In the beginning, in the front end, we have the GENERIC trees
- TRY_CATCH_EXPR, TRY_FINALLY_EXPR, WITH_CLEANUP_EXPR,
+ TRY_CATCH_EXPR, TRY_FINALLY_EXPR, EH_ELSE_EXPR, WITH_CLEANUP_EXPR,
CLEANUP_POINT_EXPR, CATCH_EXPR, and EH_FILTER_EXPR.
- During initial gimplification (gimplify.c) these are lowered
- to the GIMPLE_TRY, GIMPLE_CATCH, and GIMPLE_EH_FILTER nodes.
- The WITH_CLEANUP_EXPR and CLEANUP_POINT_EXPR nodes are converted
- into GIMPLE_TRY_FINALLY nodes; the others are a more direct 1-1
- conversion.
+ During initial gimplification (gimplify.c) these are lowered to the
+ GIMPLE_TRY, GIMPLE_CATCH, GIMPLE_EH_ELSE, and GIMPLE_EH_FILTER
+ nodes. The WITH_CLEANUP_EXPR and CLEANUP_POINT_EXPR nodes are
+ converted into GIMPLE_TRY_FINALLY nodes; the others are a more
+ direct 1-1 conversion.
During pass_lower_eh (tree-eh.c) we record the nested structure
of the TRY nodes in EH_REGION nodes in CFUN->EH->REGION_TREE.
case CATCH_EXPR:
case EH_FILTER_EXPR:
case TRY_FINALLY_EXPR:
+ case EH_ELSE_EXPR:
/* Lowered by tree-eh.c. */
gcc_unreachable ();
newline_and_indent (buffer, spc + 2);
pp_right_brace (buffer);
+ gimple_seq seq = gimple_try_cleanup (gs);
+
if (gimple_try_kind (gs) == GIMPLE_TRY_CATCH)
{
newline_and_indent (buffer, spc);
pp_string (buffer, "finally");
newline_and_indent (buffer, spc + 2);
pp_left_brace (buffer);
+
+ if (seq && is_a <geh_else *> (gimple_seq_first_stmt (seq))
+ && gimple_seq_nondebug_singleton_p (seq))
+ {
+ geh_else *stmt = as_a <geh_else *> (gimple_seq_first_stmt (seq));
+ seq = gimple_eh_else_n_body (stmt);
+ pp_newline (buffer);
+ dump_gimple_seq (buffer, seq, spc + 4, flags);
+ newline_and_indent (buffer, spc + 2);
+ pp_right_brace (buffer);
+ seq = gimple_eh_else_e_body (stmt);
+ newline_and_indent (buffer, spc);
+ pp_string (buffer, "else");
+ newline_and_indent (buffer, spc + 2);
+ pp_left_brace (buffer);
+ }
}
else
pp_string (buffer, " <UNKNOWN GIMPLE_TRY> {");
pp_newline (buffer);
- dump_gimple_seq (buffer, gimple_try_cleanup (gs), spc + 4, flags);
+ dump_gimple_seq (buffer, seq, spc + 4, flags);
newline_and_indent (buffer, spc + 2);
pp_right_brace (buffer);
}
input_location = UNKNOWN_LOCATION;
eval = cleanup = NULL;
gimplify_and_add (TREE_OPERAND (*expr_p, 0), &eval);
- gimplify_and_add (TREE_OPERAND (*expr_p, 1), &cleanup);
+ if (TREE_CODE (*expr_p) == TRY_FINALLY_EXPR
+ && TREE_CODE (TREE_OPERAND (*expr_p, 1)) == EH_ELSE_EXPR)
+ {
+ gimple_seq n = NULL, e = NULL;
+ gimplify_and_add (TREE_OPERAND (TREE_OPERAND (*expr_p, 1),
+ 0), &n);
+ gimplify_and_add (TREE_OPERAND (TREE_OPERAND (*expr_p, 1),
+ 1), &e);
+ if (!gimple_seq_empty_p (n) && !gimple_seq_empty_p (e))
+ {
+ geh_else *stmt = gimple_build_eh_else (n, e);
+ gimple_seq_add_stmt (&cleanup, stmt);
+ }
+ }
+ else
+ gimplify_and_add (TREE_OPERAND (*expr_p, 1), &cleanup);
/* Don't create bogus GIMPLE_TRY with empty cleanup. */
if (gimple_seq_empty_p (cleanup))
{
&& code != LOOP_EXPR
&& code != SWITCH_EXPR
&& code != TRY_FINALLY_EXPR
+ && code != EH_ELSE_EXPR
&& code != OACC_PARALLEL
&& code != OACC_KERNELS
&& code != OACC_DATA
break;
case TRY_FINALLY_EXPR:
+ case EH_ELSE_EXPR:
dump_child ("op 0", TREE_OPERAND (t, 0));
dump_child ("op 1", TREE_OPERAND (t, 1));
break;
newline_and_indent (pp, spc+2);
pp_right_brace (pp);
newline_and_indent (pp, spc);
- pp_string (pp,
- (TREE_CODE (node) == TRY_CATCH_EXPR) ? "catch" : "finally");
+ if (TREE_CODE (node) == TRY_CATCH_EXPR)
+ {
+ node = TREE_OPERAND (node, 1);
+ pp_string (pp, "catch");
+ }
+ else
+ {
+ gcc_assert (TREE_CODE (node) == TRY_FINALLY_EXPR);
+ node = TREE_OPERAND (node, 1);
+ pp_string (pp, "finally");
+ if (TREE_CODE (node) == EH_ELSE_EXPR)
+ {
+ newline_and_indent (pp, spc+2);
+ pp_left_brace (pp);
+ newline_and_indent (pp, spc+4);
+ dump_generic_node (pp, TREE_OPERAND (node, 0), spc+4,
+ flags, true);
+ newline_and_indent (pp, spc+2);
+ pp_right_brace (pp);
+ newline_and_indent (pp, spc);
+ node = TREE_OPERAND (node, 1);
+ pp_string (pp, "else");
+ }
+ }
newline_and_indent (pp, spc+2);
pp_left_brace (pp);
newline_and_indent (pp, spc+4);
- dump_generic_node (pp, TREE_OPERAND (node, 1), spc+4, flags, true);
+ dump_generic_node (pp, node, spc+4, flags, true);
newline_and_indent (pp, spc+2);
pp_right_brace (pp);
is_expr = false;
return (block_may_fallthru (TREE_OPERAND (stmt, 0))
&& block_may_fallthru (TREE_OPERAND (stmt, 1)));
+ case EH_ELSE_EXPR:
+ return block_may_fallthru (TREE_OPERAND (stmt, 0));
+
case MODIFY_EXPR:
if (TREE_CODE (TREE_OPERAND (stmt, 1)) == CALL_EXPR)
stmt = TREE_OPERAND (stmt, 1);
/* Evaluate the first operand.
The second operand is a cleanup expression which is evaluated
on any exit (normal, exception, or jump out) from this expression. */
-DEFTREECODE (TRY_FINALLY_EXPR, "try_finally", tcc_statement, 2)
+DEFTREECODE (TRY_FINALLY_EXPR, "try_finally_expr", tcc_statement, 2)
+
+/* Evaluate either the normal or the exceptional cleanup. This must
+ only be present as the cleanup expression in a TRY_FINALLY_EXPR.
+ If the TRY_FINALLY_EXPR completes normally, the first operand of
+ EH_ELSE_EXPR is used as a cleanup, otherwise the second operand is
+ used. */
+DEFTREECODE (EH_ELSE_EXPR, "eh_else_expr", tcc_statement, 2)
\f
/* These types of expressions have no useful value,
and always have side effects. */