Commit a24549d4 by Jason Merrill Committed by Jason Merrill

re PR c++/15764 (no cleanup if temporary's dtor terminates with an exception)

        PR c++/15764
        * cp/decl.c (wrap_cleanups_r): New fn.
        (wrap_temporary_cleanups): New fn.
        (initialize_local_var): Call it.
        * tree-eh.c (same_handler_p): New fn.
        (optimize_double_finally): New fn.
        (refactor_eh_r): New fn.
        (refactor_eh): New fn.
        (pass_refactor_eh): New pass.
        * tree-pass.h: Declare it.
        * passes.c (init_optimization_passes): Add it.

From-SVN: r128979
parent 7a3c4b3e
2007-10-03 Jason Merrill <jason@redhat.com>
PR c++/15764
* tree-eh.c (same_handler_p): New fn.
(optimize_double_finally): New fn.
(refactor_eh_r): New fn.
(refactor_eh): New fn.
(pass_refactor_eh): New pass.
* tree-pass.h: Declare it.
* passes.c (init_optimization_passes): Add it.
2007-10-03 Doug Kwan <dougkwan@google.com> 2007-10-03 Doug Kwan <dougkwan@google.com>
Richard Guenther <rguenther@suse.de> Richard Guenther <rguenther@suse.de>
2007-10-03 Jason Merrill <jason@redhat.com>
PR c++/15764
* decl.c (wrap_cleanups_r): New fn.
(wrap_temporary_cleanups): New fn.
(initialize_local_var): Call it.
2007-09-29 Jason Merrill <jason@redhat.com> 2007-09-29 Jason Merrill <jason@redhat.com>
PR c++/33094 PR c++/33094
......
...@@ -5136,6 +5136,41 @@ make_rtl_for_nonlocal_decl (tree decl, tree init, const char* asmspec) ...@@ -5136,6 +5136,41 @@ make_rtl_for_nonlocal_decl (tree decl, tree init, const char* asmspec)
rest_of_decl_compilation (decl, toplev, at_eof); rest_of_decl_compilation (decl, toplev, at_eof);
} }
/* walk_tree helper for wrap_temporary_cleanups, below. */
static tree
wrap_cleanups_r (tree *stmt_p, int *walk_subtrees, void *data)
{
if (TYPE_P (*stmt_p))
{
*walk_subtrees = 0;
return NULL_TREE;
}
if (TREE_CODE (*stmt_p) == TARGET_EXPR)
{
tree guard = (tree)data;
tree tcleanup = TARGET_EXPR_CLEANUP (*stmt_p);
tcleanup = build2 (TRY_CATCH_EXPR, void_type_node, tcleanup, guard);
TARGET_EXPR_CLEANUP (*stmt_p) = tcleanup;
}
return NULL_TREE;
}
/* We're initializing a local variable which has a cleanup GUARD. If there
are any temporaries used in the initializer INIT of this variable, we
need to wrap their cleanups with TRY_CATCH_EXPR (, GUARD) so that the
variable will be cleaned up properly if one of them throws. */
static void
wrap_temporary_cleanups (tree init, tree guard)
{
cp_walk_tree_without_duplicates (&init, wrap_cleanups_r, (void *)guard);
}
/* Generate code to initialize DECL (a local variable). */ /* Generate code to initialize DECL (a local variable). */
static void static void
...@@ -5143,6 +5178,7 @@ initialize_local_var (tree decl, tree init) ...@@ -5143,6 +5178,7 @@ initialize_local_var (tree decl, tree init)
{ {
tree type = TREE_TYPE (decl); tree type = TREE_TYPE (decl);
tree cleanup; tree cleanup;
int already_used;
gcc_assert (TREE_CODE (decl) == VAR_DECL gcc_assert (TREE_CODE (decl) == VAR_DECL
|| TREE_CODE (decl) == RESULT_DECL); || TREE_CODE (decl) == RESULT_DECL);
...@@ -5153,46 +5189,53 @@ initialize_local_var (tree decl, tree init) ...@@ -5153,46 +5189,53 @@ initialize_local_var (tree decl, tree init)
/* If we used it already as memory, it must stay in memory. */ /* If we used it already as memory, it must stay in memory. */
DECL_INITIAL (decl) = NULL_TREE; DECL_INITIAL (decl) = NULL_TREE;
TREE_ADDRESSABLE (decl) = TREE_USED (decl); TREE_ADDRESSABLE (decl) = TREE_USED (decl);
return;
} }
if (DECL_SIZE (decl) && type != error_mark_node) if (type == error_mark_node)
{ return;
int already_used;
/* Compute and store the initial value. */ /* Compute and store the initial value. */
already_used = TREE_USED (decl) || TREE_USED (type); already_used = TREE_USED (decl) || TREE_USED (type);
/* Perform the initialization. */ /* Generate a cleanup, if necessary. */
if (init) cleanup = cxx_maybe_build_cleanup (decl);
{
int saved_stmts_are_full_exprs_p;
gcc_assert (building_stmt_tree ()); /* Perform the initialization. */
saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p (); if (init)
current_stmt_tree ()->stmts_are_full_exprs_p = 1; {
finish_expr_stmt (init); int saved_stmts_are_full_exprs_p;
current_stmt_tree ()->stmts_are_full_exprs_p =
saved_stmts_are_full_exprs_p;
}
/* Set this to 0 so we can tell whether an aggregate which was /* If we're only initializing a single object, guard the destructors
initialized was ever used. Don't do this if it has a of any temporaries used in its initializer with its destructor.
destructor, so we don't complain about the 'resource This isn't right for arrays because each element initialization is
allocation is initialization' idiom. Now set a full-expression. */
attribute((unused)) on types so decls of that type will be if (cleanup && TREE_CODE (type) != ARRAY_TYPE)
marked used. (see TREE_USED, above.) */ wrap_temporary_cleanups (init, cleanup);
if (TYPE_NEEDS_CONSTRUCTING (type)
&& ! already_used
&& TYPE_HAS_TRIVIAL_DESTRUCTOR (type)
&& DECL_NAME (decl))
TREE_USED (decl) = 0;
else if (already_used)
TREE_USED (decl) = 1;
}
/* Generate a cleanup, if necessary. */ gcc_assert (building_stmt_tree ());
cleanup = cxx_maybe_build_cleanup (decl); saved_stmts_are_full_exprs_p = stmts_are_full_exprs_p ();
if (DECL_SIZE (decl) && cleanup) current_stmt_tree ()->stmts_are_full_exprs_p = 1;
finish_expr_stmt (init);
current_stmt_tree ()->stmts_are_full_exprs_p =
saved_stmts_are_full_exprs_p;
}
/* Set this to 0 so we can tell whether an aggregate which was
initialized was ever used. Don't do this if it has a
destructor, so we don't complain about the 'resource
allocation is initialization' idiom. Now set
attribute((unused)) on types so decls of that type will be
marked used. (see TREE_USED, above.) */
if (TYPE_NEEDS_CONSTRUCTING (type)
&& ! already_used
&& TYPE_HAS_TRIVIAL_DESTRUCTOR (type)
&& DECL_NAME (decl))
TREE_USED (decl) = 0;
else if (already_used)
TREE_USED (decl) = 1;
if (cleanup)
finish_decl_cleanup (decl, cleanup); finish_decl_cleanup (decl, cleanup);
} }
......
...@@ -480,6 +480,7 @@ init_optimization_passes (void) ...@@ -480,6 +480,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_mudflap_1); NEXT_PASS (pass_mudflap_1);
NEXT_PASS (pass_lower_omp); NEXT_PASS (pass_lower_omp);
NEXT_PASS (pass_lower_cf); NEXT_PASS (pass_lower_cf);
NEXT_PASS (pass_refactor_eh);
NEXT_PASS (pass_lower_eh); NEXT_PASS (pass_lower_eh);
NEXT_PASS (pass_build_cfg); NEXT_PASS (pass_build_cfg);
NEXT_PASS (pass_lower_complex_O0); NEXT_PASS (pass_lower_complex_O0);
......
// PR c++/15764
extern "C" void abort ();
int counter = 0;
int thrown;
struct a {
~a () { if (thrown++ == 0) throw 42; }
};
int f (a const&) { return 1; }
int f (a const&, a const&) { return 1; }
struct b {
b (...) { ++counter; }
~b () { --counter; }
};
bool p;
void g()
{
if (p) throw 42;
}
int main () {
thrown = 0;
try {
b tmp(f (a(), a()));
g();
}
catch (...) {}
thrown = 0;
try {
b tmp(f (a()));
g();
}
catch (...) {}
if (counter != 0)
abort ();
}
...@@ -2093,3 +2093,159 @@ maybe_clean_or_replace_eh_stmt (tree old_stmt, tree new_stmt) ...@@ -2093,3 +2093,159 @@ maybe_clean_or_replace_eh_stmt (tree old_stmt, tree new_stmt)
return false; return false;
} }
/* Returns TRUE if oneh and twoh are exception handlers (op 1 of
TRY_CATCH_EXPR or TRY_FINALLY_EXPR that are similar enough to be
considered the same. Currently this only handles handlers consisting of
a single call, as that's the important case for C++: a destructor call
for a particular object showing up in multiple handlers. */
static bool
same_handler_p (tree oneh, tree twoh)
{
tree_stmt_iterator i;
tree ones, twos;
int ai;
i = tsi_start (oneh);
if (!tsi_one_before_end_p (i))
return false;
ones = tsi_stmt (i);
i = tsi_start (twoh);
if (!tsi_one_before_end_p (i))
return false;
twos = tsi_stmt (i);
if (TREE_CODE (ones) != CALL_EXPR
|| TREE_CODE (twos) != CALL_EXPR
|| !operand_equal_p (CALL_EXPR_FN (ones), CALL_EXPR_FN (twos), 0)
|| call_expr_nargs (ones) != call_expr_nargs (twos))
return false;
for (ai = 0; ai < call_expr_nargs (ones); ++ai)
if (!operand_equal_p (CALL_EXPR_ARG (ones, ai),
CALL_EXPR_ARG (twos, ai), 0))
return false;
return true;
}
/* Optimize
try { A() } finally { try { ~B() } catch { ~A() } }
try { ... } finally { ~A() }
into
try { A() } catch { ~B() }
try { ~B() ... } finally { ~A() }
This occurs frequently in C++, where A is a local variable and B is a
temporary used in the initializer for A. */
static void
optimize_double_finally (tree one, tree two)
{
tree oneh;
tree_stmt_iterator i;
i = tsi_start (TREE_OPERAND (one, 1));
if (!tsi_one_before_end_p (i))
return;
oneh = tsi_stmt (i);
if (TREE_CODE (oneh) != TRY_CATCH_EXPR)
return;
if (same_handler_p (TREE_OPERAND (oneh, 1), TREE_OPERAND (two, 1)))
{
tree twoh;
tree b = TREE_OPERAND (oneh, 0);
TREE_OPERAND (one, 1) = b;
TREE_SET_CODE (one, TRY_CATCH_EXPR);
b = tsi_stmt (tsi_start (b));
twoh = TREE_OPERAND (two, 0);
/* same_handler_p only handles single-statement handlers,
so there must only be one statement. */
i = tsi_start (twoh);
tsi_link_before (&i, unshare_expr (b), TSI_SAME_STMT);
}
}
/* Perform EH refactoring optimizations that are simpler to do when code
flow has been lowered but EH structurs haven't. */
static void
refactor_eh_r (tree t)
{
tailrecurse:
switch (TREE_CODE (t))
{
case TRY_FINALLY_EXPR:
case TRY_CATCH_EXPR:
refactor_eh_r (TREE_OPERAND (t, 0));
t = TREE_OPERAND (t, 1);
goto tailrecurse;
case CATCH_EXPR:
t = CATCH_BODY (t);
goto tailrecurse;
case EH_FILTER_EXPR:
t = EH_FILTER_FAILURE (t);
goto tailrecurse;
case STATEMENT_LIST:
{
tree_stmt_iterator i;
tree one = NULL_TREE, two = NULL_TREE;
/* Try to refactor double try/finally. */
for (i = tsi_start (t); !tsi_end_p (i); tsi_next (&i))
{
one = two;
two = tsi_stmt (i);
if (one && two
&& TREE_CODE (one) == TRY_FINALLY_EXPR
&& TREE_CODE (two) == TRY_FINALLY_EXPR)
optimize_double_finally (one, two);
if (one)
refactor_eh_r (one);
}
if (two)
{
t = two;
goto tailrecurse;
}
}
break;
default:
/* A type, a decl, or some kind of statement that we're not
interested in. Don't walk them. */
break;
}
}
static unsigned
refactor_eh (void)
{
refactor_eh_r (DECL_SAVED_TREE (current_function_decl));
return 0;
}
struct tree_opt_pass pass_refactor_eh =
{
"ehopt", /* name */
NULL, /* gate */
refactor_eh, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
TV_TREE_EH, /* tv_id */
PROP_gimple_lcf, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
TODO_dump_func, /* todo_flags_finish */
0 /* letter */
};
...@@ -246,6 +246,7 @@ extern struct tree_opt_pass pass_mudflap_1; ...@@ -246,6 +246,7 @@ extern struct tree_opt_pass pass_mudflap_1;
extern struct tree_opt_pass pass_mudflap_2; extern struct tree_opt_pass pass_mudflap_2;
extern struct tree_opt_pass pass_remove_useless_stmts; extern struct tree_opt_pass pass_remove_useless_stmts;
extern struct tree_opt_pass pass_lower_cf; extern struct tree_opt_pass pass_lower_cf;
extern struct tree_opt_pass pass_refactor_eh;
extern struct tree_opt_pass pass_lower_eh; extern struct tree_opt_pass pass_lower_eh;
extern struct tree_opt_pass pass_build_cfg; extern struct tree_opt_pass pass_build_cfg;
extern struct tree_opt_pass pass_tree_profile; extern struct tree_opt_pass pass_tree_profile;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment