This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[tree-ssa] fix c++/12770
- From: Richard Henderson <rth at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Sun, 16 Nov 2003 15:12:07 -0800
- Subject: [tree-ssa] fix c++/12770
This could have been fixed several ways. The simplest of which
would have been to extend block_may_fallthru to handle COND and
BIND expressions. Another would be to make -O0 build the CFG
and clean it up.
I went this way, exchanging .lower and .eh dumps, because it also
cures a fixme that I'd left in the .lower pass. We no longer
duplicate BIND_EXPRs before lowering, which, because of the way
tree duplication works, *should* mean that we get better debug
information for the duplicated blocks. I havn't actually
verified this; I meant to but forgot.
I also discovered a buglet in the process of now having to deal
with lowered COND_EXPRs for which we did not have a test case.
Bootstrapped and tested on alphaev67-linux.
r~
PR c++/12770
* gimple-low.c (lower_stmt_body): Take a tree, not a tree*.
(lower_stmt): Handle EH nodes.
(lower_bind_expr): Remove fixme.
(block_may_fallthru): Move from tree-eh.c. Handle COND_EXPR,
BIND_EXPR, and TRY_FINALLY_EXPR.
(lower_cond_expr): Use it.
* tree-eh.c (collect_finally_tree): Ignore COND_EXPR and BIND_EXPR.
(replace_goto_queue_cond_clause): New.
(replace_goto_queue_1): Use it. Split out statement_list handling.
(replace_goto_queue_stmt_list): New.
(-block_may_fallthru): Move to gimple-low.c.
(lower_eh_constructs_1): Ignore BIND_EXPR.
* tree-flow.h (block_may_fallthru): Declare.
* tree-dump.c (dump_files): Exchange .eh and .lower passes.
* tree-optimize.c (tree_rest_of_compilation): Likewise.
* tree.h (enum tree_dump_index): Likewise.
Index: gimple-low.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/gimple-low.c,v
retrieving revision 1.1.4.12
diff -c -p -d -u -r1.1.4.12 gimple-low.c
--- gimple-low.c 15 Nov 2003 19:44:36 -0000 1.1.4.12
+++ gimple-low.c 16 Nov 2003 22:54:55 -0000
@@ -47,7 +47,7 @@ struct lower_data
tree block;
};
-static void lower_stmt_body (tree *, struct lower_data *);
+static void lower_stmt_body (tree, struct lower_data *);
static void lower_stmt (tree_stmt_iterator *, struct lower_data *);
static void lower_bind_expr (tree_stmt_iterator *, struct lower_data *);
static void lower_cond_expr (tree_stmt_iterator *, struct lower_data *);
@@ -69,7 +69,7 @@ lower_function_body (tree *body)
record_vars (BIND_EXPR_VARS (*body));
*body = BIND_EXPR_BODY (*body);
- lower_stmt_body (body, &data);
+ lower_stmt_body (*body, &data);
if (data.block != DECL_INITIAL (current_function_decl))
abort ();
@@ -84,15 +84,16 @@ lower_function_body (tree *body)
do it explicitly. DATA is passed through the recursion. */
static void
-lower_stmt_body (tree *expr, struct lower_data *data)
+lower_stmt_body (tree expr, struct lower_data *data)
{
tree_stmt_iterator tsi;
- for (tsi = tsi_start (*expr); !tsi_end_p (tsi); )
+ for (tsi = tsi_start (expr); !tsi_end_p (tsi); )
lower_stmt (&tsi, data);
}
/* Lowers statement TSI. DATA is passed through the recursion. */
+
static void
lower_stmt (tree_stmt_iterator *tsi, struct lower_data *data)
{
@@ -105,11 +106,23 @@ lower_stmt (tree_stmt_iterator *tsi, str
{
case BIND_EXPR:
lower_bind_expr (tsi, data);
- break;
-
- case COMPOUND_EXPR:
- abort ();
+ return;
+ case COND_EXPR:
+ lower_cond_expr (tsi, data);
+ return;
+ case TRY_FINALLY_EXPR:
+ case TRY_CATCH_EXPR:
+ lower_stmt_body (TREE_OPERAND (stmt, 0), data);
+ lower_stmt_body (TREE_OPERAND (stmt, 1), data);
+ break;
+ case CATCH_EXPR:
+ lower_stmt_body (CATCH_BODY (stmt), data);
+ break;
+ case EH_FILTER_EXPR:
+ lower_stmt_body (EH_FILTER_FAILURE (stmt), data);
+ break;
+
case NOP_EXPR:
case ASM_EXPR:
case RETURN_EXPR:
@@ -118,38 +131,16 @@ lower_stmt (tree_stmt_iterator *tsi, str
case GOTO_EXPR:
case LABEL_EXPR:
case VA_ARG_EXPR:
- case RESX_EXPR:
case SWITCH_EXPR:
- tsi_next (tsi);
- break;
-
- case COND_EXPR:
- lower_cond_expr (tsi, data);
break;
default:
print_node_brief (stderr, "", stmt, 0);
+ case COMPOUND_EXPR:
abort ();
}
-}
-
-/* Record the variables in VARS. */
-
-void
-record_vars (tree vars)
-{
- for (; vars; vars = TREE_CHAIN (vars))
- {
- tree var = vars;
- /* Nothing to do in this case. */
- if (DECL_EXTERNAL (var))
- continue;
-
- /* Record the variable. */
- cfun->unexpanded_var_list = tree_cons (NULL_TREE, var,
- cfun->unexpanded_var_list);
- }
+ tsi_next (tsi);
}
/* Lowers a bind_expr TSI. DATA is passed through the recursion. */
@@ -175,11 +166,6 @@ lower_bind_expr (tree_stmt_iterator *tsi
else
{
/* We do not expect to handle duplicate blocks. */
- /* ??? This is probably wrong. We've already done some amount
- of code replication in tree-eh.c; we should probably be doing
- something like reorder_blocks, which knows how to handle
- duplicates. Either that or lower bind_exprs before this
- can matter. */
if (TREE_ASM_WRITTEN (new_block))
abort ();
TREE_ASM_WRITTEN (new_block) = 1;
@@ -197,7 +183,7 @@ lower_bind_expr (tree_stmt_iterator *tsi
}
record_vars (BIND_EXPR_VARS (stmt));
- lower_stmt_body (&BIND_EXPR_BODY (stmt), data);
+ lower_stmt_body (BIND_EXPR_BODY (stmt), data);
if (new_block)
{
@@ -214,6 +200,53 @@ lower_bind_expr (tree_stmt_iterator *tsi
tsi_delink (tsi);
}
+/* Try to determine if we can fall out of the bottom of BLOCK. This guess
+ need not be 100% accurate; simply be conservative and return true if we
+ don't know. This is used only to avoid stupidly generating extra code.
+ If we're wrong, we'll just delete the extra code later. */
+
+bool
+block_may_fallthru (tree block)
+{
+ tree stmt = expr_last (block);
+
+ switch (stmt ? TREE_CODE (stmt) : ERROR_MARK)
+ {
+ case GOTO_EXPR:
+ case RETURN_EXPR:
+ case RESX_EXPR:
+ case SWITCH_EXPR:
+ /* Easy cases. If the last statement of the block implies
+ control transfer, then we can't fall through. */
+ return false;
+
+ case COND_EXPR:
+ if (block_may_fallthru (COND_EXPR_THEN (stmt)))
+ return true;
+ return block_may_fallthru (COND_EXPR_ELSE (stmt));
+
+ case BIND_EXPR:
+ return block_may_fallthru (BIND_EXPR_BODY (stmt));
+
+ case TRY_FINALLY_EXPR:
+ return block_may_fallthru (TREE_OPERAND (stmt, 1));
+
+ case MODIFY_EXPR:
+ if (TREE_CODE (TREE_OPERAND (stmt, 1)) == CALL_EXPR)
+ stmt = TREE_OPERAND (stmt, 1);
+ else
+ return true;
+ /* FALLTHRU */
+
+ case CALL_EXPR:
+ /* Functions that do not return do not fall through. */
+ return (call_expr_flags (stmt) & ECF_NORETURN) == 0;
+
+ default:
+ return true;
+ }
+}
+
/* Lowers a cond_expr TSI. DATA is passed through the recursion. */
static void
@@ -224,12 +257,12 @@ lower_cond_expr (tree_stmt_iterator *tsi
tree then_branch, else_branch;
tree then_goto, else_goto;
- lower_stmt_body (&COND_EXPR_THEN (stmt), data);
- lower_stmt_body (&COND_EXPR_ELSE (stmt), data);
-
then_branch = COND_EXPR_THEN (stmt);
else_branch = COND_EXPR_ELSE (stmt);
+ lower_stmt_body (then_branch, data);
+ lower_stmt_body (else_branch, data);
+
then_goto = expr_only (then_branch);
then_is_goto = then_goto && simple_goto_p (then_goto);
@@ -280,10 +313,12 @@ lower_cond_expr (tree_stmt_iterator *tsi
if (then_label)
{
+ bool may_fallthru = block_may_fallthru (then_branch);
+
tsi_link_after (tsi, then_label, TSI_CONTINUE_LINKING);
tsi_link_after (tsi, then_branch, TSI_CONTINUE_LINKING);
- if (else_label)
+ if (else_label && may_fallthru)
{
end_label = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
t = build_and_jump (&LABEL_EXPR_LABEL (end_label));
@@ -305,6 +340,26 @@ lower_cond_expr (tree_stmt_iterator *tsi
COND_EXPR_ELSE (stmt) = else_goto;
tsi_next (tsi);
+}
+
+
+/* Record the variables in VARS. */
+
+void
+record_vars (tree vars)
+{
+ for (; vars; vars = TREE_CHAIN (vars))
+ {
+ tree var = vars;
+
+ /* Nothing to do in this case. */
+ if (DECL_EXTERNAL (var))
+ continue;
+
+ /* Record the variable. */
+ cfun->unexpanded_var_list = tree_cons (NULL_TREE, var,
+ cfun->unexpanded_var_list);
+ }
}
/* Check whether to expand a variable VAR. */
Index: tree-dump.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-dump.c,v
retrieving revision 1.6.2.49
diff -c -p -d -u -r1.6.2.49 tree-dump.c
--- tree-dump.c 11 Nov 2003 18:04:46 -0000 1.6.2.49
+++ tree-dump.c 16 Nov 2003 22:54:55 -0000
@@ -653,8 +653,8 @@ static struct dump_file_info dump_files[
{".inlined", "tree-inlined", 0, 0},
{".gimple", "tree-gimple", 0, 0},
{".useless", "tree-useless", 0, 0},
- {".eh", "tree-eh", 0, 0},
{".lower", "tree-lower", 0, 0},
+ {".eh", "tree-eh", 0, 0},
{".cfg", "tree-cfg", 0, 0},
{".dot", "tree-dot", 0, 0},
{".tail", "tree-tail", 0, 0},
Index: tree-eh.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-eh.c,v
retrieving revision 1.1.2.15
diff -c -p -d -u -r1.1.2.15 tree-eh.c
--- tree-eh.c 14 Nov 2003 23:29:07 -0000 1.1.2.15
+++ tree-eh.c 16 Nov 2003 22:54:55 -0000
@@ -160,15 +160,6 @@ collect_finally_tree (tree t, tree regio
t = TREE_OPERAND (t, 1);
goto tailrecurse;
- case COND_EXPR:
- collect_finally_tree (COND_EXPR_THEN (t), region);
- t = COND_EXPR_ELSE (t);
- goto tailrecurse;
-
- case BIND_EXPR:
- t = BIND_EXPR_BODY (t);
- goto tailrecurse;
-
case TRY_CATCH_EXPR:
collect_finally_tree (TREE_OPERAND (t, 0), region);
t = TREE_OPERAND (t, 1);
@@ -316,71 +307,100 @@ find_goto_replacement (struct leh_tf_sta
return (ret ? ret->repl_stmt : NULL);
}
-/* Replace all goto queue members. */
-/* ??? This search and replace nonsense wouldn't be necessary
- if we had a reasonable statement connection mechanism. The
- nature of these COMPOUND_EXPRs is such that we can't store
- a pointer to a statement and hope to be able to replace it
- later, when the tree has been restructured. */
-/* ??? We now have a reasonable connection mechansim. Update. */
+/* A subroutine of replace_goto_queue_1. Handles the sub-clauses of a
+ lowered COND_EXPR. If, by chance, the replacement is a simple goto,
+ then we can just splat it in, otherwise we add the new stmts immediately
+ after the COND_EXPR and redirect. */
-static tree
-replace_goto_queue_1 (tree t, struct leh_tf_state *tf)
+static void
+replace_goto_queue_cond_clause (tree *tp, struct leh_tf_state *tf,
+ tree_stmt_iterator *tsi)
+{
+ tree new, one, label;
+
+ new = find_goto_replacement (tf, *tp);
+ if (!new)
+ return;
+
+ one = expr_only (new);
+ if (one && TREE_CODE (one) == GOTO_EXPR)
+ {
+ *tp = one;
+ return;
+ }
+
+ label = build1 (LABEL_EXPR, void_type_node, NULL_TREE);
+ *tp = build_and_jump (&LABEL_EXPR_LABEL (label));
+
+ tsi_link_after (tsi, label, TSI_CONTINUE_LINKING);
+ tsi_link_after (tsi, new, TSI_CONTINUE_LINKING);
+}
+
+/* The real work of replace_goto_queue. Returns with TSI updated to
+ point to the next statement. */
+
+static void replace_goto_queue_stmt_list (tree, struct leh_tf_state *);
+
+static void
+replace_goto_queue_1 (tree t, struct leh_tf_state *tf, tree_stmt_iterator *tsi)
{
switch (TREE_CODE (t))
{
case GOTO_EXPR:
case RETURN_EXPR:
- return find_goto_replacement (tf, t);
-
- case STATEMENT_LIST:
- {
- tree_stmt_iterator i = tsi_start (t);
- while (!tsi_end_p (i))
- {
- t = replace_goto_queue_1 (tsi_stmt (i), tf);
- if (t)
- {
- tsi_link_before (&i, t, TSI_SAME_STMT);
- tsi_delink (&i);
- }
- else
- tsi_next (&i);
- }
- }
+ t = find_goto_replacement (tf, t);
+ if (t)
+ {
+ tsi_link_before (tsi, t, TSI_SAME_STMT);
+ tsi_delink (tsi);
+ return;
+ }
break;
case COND_EXPR:
- replace_goto_queue_1 (COND_EXPR_THEN (t), tf);
- replace_goto_queue_1 (COND_EXPR_ELSE (t), tf);
- break;
- case BIND_EXPR:
- replace_goto_queue_1 (BIND_EXPR_BODY (t), tf);
+ replace_goto_queue_cond_clause (&COND_EXPR_THEN (t), tf, tsi);
+ replace_goto_queue_cond_clause (&COND_EXPR_ELSE (t), tf, tsi);
break;
+
case TRY_FINALLY_EXPR:
case TRY_CATCH_EXPR:
- replace_goto_queue_1 (TREE_OPERAND (t, 0), tf);
- replace_goto_queue_1 (TREE_OPERAND (t, 1), tf);
+ replace_goto_queue_stmt_list (TREE_OPERAND (t, 0), tf);
+ replace_goto_queue_stmt_list (TREE_OPERAND (t, 1), tf);
break;
case CATCH_EXPR:
- replace_goto_queue_1 (CATCH_BODY (t), tf);
+ replace_goto_queue_stmt_list (CATCH_BODY (t), tf);
break;
case EH_FILTER_EXPR:
- replace_goto_queue_1 (EH_FILTER_FAILURE (t), tf);
+ replace_goto_queue_stmt_list (EH_FILTER_FAILURE (t), tf);
break;
+ case STATEMENT_LIST:
+ abort ();
+
default:
/* These won't have gotos in them. */
break;
}
- return NULL;
+ tsi_next (tsi);
}
+/* A subroutine of replace_goto_queue. Handles STATEMENT_LISTs. */
+
+static void
+replace_goto_queue_stmt_list (tree t, struct leh_tf_state *tf)
+{
+ tree_stmt_iterator i = tsi_start (t);
+ while (!tsi_end_p (i))
+ replace_goto_queue_1 (tsi_stmt (i), tf, &i);
+}
+
+/* Replace all goto queue members. */
+
static void
replace_goto_queue (struct leh_tf_state *tf)
{
- replace_goto_queue_1 (*tf->top_p, tf);
+ replace_goto_queue_stmt_list (*tf->top_p, tf);
}
/* For any GOTO_EXPR or RETURN_EXPR, decide whether it leaves a try_finally
@@ -594,42 +614,6 @@ do_goto_redirection (struct goto_queue_n
append_to_statement_list (x, &q->repl_stmt);
}
-/* Try to determine if we can fall out of the bottom of BLOCK. This guess
- need not be 100% accurate; simply be conservative and return true if we
- don't know. This is used only to avoid stupidly generating extra code.
- If we're wrong, we'll just delete the extra code later. */
-
-static bool
-block_may_fallthru (tree block)
-{
- tree stmt = expr_last (block);
-
- switch (TREE_CODE (stmt))
- {
- case GOTO_EXPR:
- case RETURN_EXPR:
- case RESX_EXPR:
- /* Easy cases. If the last statement of the block implies
- control transfer, then we can't fall through. */
- return false;
-
- case MODIFY_EXPR:
- if (TREE_CODE (TREE_OPERAND (stmt, 1)) == CALL_EXPR)
- stmt = TREE_OPERAND (stmt, 1);
- else
- return true;
- /* FALLTHRU */
-
- case CALL_EXPR:
- /* Functions that do not return do not fall through. */
- return (call_expr_flags (stmt) & ECF_NORETURN) == 0;
-
- default:
- /* ??? Could search back through other composite structures. */
- return true;
- }
-}
-
/* We want to transform
try { body; } catch { stuff; }
to
@@ -1509,9 +1493,6 @@ lower_eh_constructs_1 (struct leh_state
case COND_EXPR:
lower_eh_constructs_1 (state, &COND_EXPR_THEN (t));
lower_eh_constructs_1 (state, &COND_EXPR_ELSE (t));
- break;
- case BIND_EXPR:
- lower_eh_constructs_1 (state, &BIND_EXPR_BODY (t));
break;
case CALL_EXPR:
Index: tree-flow.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/Attic/tree-flow.h,v
retrieving revision 1.1.4.151
diff -c -p -d -u -r1.1.4.151 tree-flow.h
--- tree-flow.h 16 Nov 2003 11:00:40 -0000 1.1.4.151
+++ tree-flow.h 16 Nov 2003 22:54:55 -0000
@@ -535,7 +535,6 @@ extern void debug_dominator_optimization
/* In tree-ssa-dce.c */
void tree_ssa_dce (tree, enum tree_dump_index);
-
/* In tree-ssa-copyprop.c */
void tree_ssa_copyprop (tree, enum tree_dump_index);
void propagate_copy (tree *, tree);
@@ -558,6 +557,7 @@ extern bool tree_can_throw_external (tre
/* In gimple-low.c */
void lower_function_body (tree *);
+extern bool block_may_fallthru (tree block);
#include "tree-flow-inline.h"
Index: tree-optimize.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree-optimize.c,v
retrieving revision 1.1.4.71
diff -c -p -d -u -r1.1.4.71 tree-optimize.c
--- tree-optimize.c 12 Nov 2003 22:06:26 -0000 1.1.4.71
+++ tree-optimize.c 16 Nov 2003 22:54:55 -0000
@@ -297,13 +297,8 @@ tree_rest_of_compilation (tree fndecl, b
remove_useless_stmts (&DECL_SAVED_TREE (fndecl));
dump_function (TDI_useless, fndecl);
- /* Run a pass to lower magic exception handling constructs into,
- well, less magic though not completely mundane constructs. */
- lower_eh_constructs (&DECL_SAVED_TREE (fndecl));
-
/* Lower the structured statements. */
lower_function_body (&DECL_SAVED_TREE (fndecl));
- chain = DECL_SAVED_TREE (fndecl);
/* Avoid producing notes for blocks. */
cfun->dont_emit_block_notes = 1;
@@ -311,7 +306,12 @@ tree_rest_of_compilation (tree fndecl, b
dump_function (TDI_lower, fndecl);
+ /* Run a pass to lower magic exception handling constructs into,
+ well, less magic though not completely mundane constructs. */
+ lower_eh_constructs (&DECL_SAVED_TREE (fndecl));
+
/* Invoke the SSA tree optimizer. */
+ chain = DECL_SAVED_TREE (fndecl);
if (optimize >= 1 && !flag_disable_tree_ssa)
optimize_function_tree (fndecl, &chain);
Index: tree.h
===================================================================
RCS file: /cvs/gcc/gcc/gcc/tree.h,v
retrieving revision 1.342.2.126
diff -c -p -d -u -r1.342.2.126 tree.h
--- tree.h 16 Nov 2003 08:24:02 -0000 1.342.2.126
+++ tree.h 16 Nov 2003 22:54:55 -0000
@@ -3531,8 +3531,8 @@ enum tree_dump_index
within it. */
TDI_gimple, /* dump each function after gimplifying it. */
TDI_useless, /* dump after cleaning useless bits. */
- TDI_eh, /* dump after lowering eh. */
TDI_lower, /* dump after lowering containers. */
+ TDI_eh, /* dump after lowering eh. */
TDI_cfg, /* dump the flowgraph for each function. */
TDI_dot, /* create a dot graph file for each
function's flowgraph. */
Index: testsuite/g++.dg/eh/goto1.C
===================================================================
RCS file: testsuite/g++.dg/eh/goto1.C
diff -N testsuite/g++.dg/eh/goto1.C
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/g++.dg/eh/goto1.C 16 Nov 2003 22:54:55 -0000
@@ -0,0 +1,34 @@
+extern "C" void abort ();
+
+static int count;
+
+struct S {
+ S() { ++count; }
+ ~S() { --count; }
+};
+
+int foo(int p)
+{
+ S s1;
+ {
+ S s2;
+ if (p)
+ goto L;
+ else
+ return 1;
+ }
+ foo (p);
+ L:
+ return 0;
+}
+
+int main()
+{
+ foo(0);
+ if (count != 0)
+ abort ();
+ foo(1);
+ if (count != 0)
+ abort ();
+ return 0;
+}