This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[patch] for PR 24996
- From: Zdenek Dvorak <rakdver at atrey dot karlin dot mff dot cuni dot cz>
- To: gcc-patches at gcc dot gnu dot org
- Date: Thu, 2 Feb 2006 18:56:16 +0100
- Subject: [patch] for PR 24996
Hello,
this PR is caused by the fact that gimplification does not handle
TARGET_EXPR cleanups coming from structured statements (other than
conditionals). The code we get in this PR looks like
cleanup_point
{
TARGET_EXPR <D.2069, __cxa_allocate_exception (1)>;
try
{
*(struct logic_error *) D.2069 = exp;
}
catch
{
__cxa_free_exception (D.2069);
};
__cxa_throw (D.2069, (void *) &_ZTI11logic_error, 0B);
}
where exp contains the TARGET_EXPR with cleanup. Unless there is
a way to avoid producing a code like that, gimplification needs
to be improved to handle this case.
This patch is a very conservative attempt to do this. Before, for
stmts1;
target_expr (something, cleanup);
stmts2;
we produced
stmts1
try
{
something;
stmts2;
}
finally
{
cleanup;
}
With the patch, we produce code like
bool flag = false;
try
{
stmts1;
flag = true;
something;
stmts2;
}
finally
{
if (flag)
cleanup;
}
This is a bit less efficient (although the flag manipulation will
usually be removed later by optimizers, anyway), however it works
even if the target_expr is nested within a structured statement
(the current solution produces a similar code for target_exprs within
a conditional context).
Bootstrapped & regtested on i686.
Zdenek
* gimplify.c (struct cleanup): New.
(struct gimplify_ctx): Removed conditional_cleanups and conditions
fields. Added cleanups field.
(gimple_conditional_context, gimple_push_condition,
gimple_pop_condition): Removed.
(gimplify_cond_expr): Do not record conditional context.
(gimplify_cleanup_point_expr): Use gimplify_ctxp->cleanups
instead of cleanup_exprs to find cleanups.
(gimple_push_cleanup): Record cleanups to gimplify_ctxp->cleanups.
* tree-pretty-print.c (print_call_name, do_niy): Obey indentation.
(dump_generic_node): Improve indentation.
Index: gimplify.c
===================================================================
*** gimplify.c (revision 110257)
--- gimplify.c (working copy)
*************** struct gimplify_omp_ctx
*** 75,87 ****
bool is_parallel;
};
struct gimplify_ctx
{
struct gimplify_ctx *prev_context;
tree current_bind_expr;
tree temps;
! tree conditional_cleanups;
tree exit_label;
tree return_temp;
--- 75,105 ----
bool is_parallel;
};
+ /* Records a scheduled cleanup. */
+
+ struct cleanup
+ {
+ /* True if the cleanup should be run only on exception, not on normal
+ exit. */
+ bool eh_only;
+
+ /* The variable used to guard the cleanup. */
+ tree guard;
+
+ /* The cleanup. */
+ tree cleanup;
+
+ /* Next cleanup in the list. */
+ struct cleanup *next;
+ };
+
struct gimplify_ctx
{
struct gimplify_ctx *prev_context;
tree current_bind_expr;
tree temps;
! struct cleanup *cleanups;
tree exit_label;
tree return_temp;
*************** struct gimplify_ctx
*** 89,95 ****
/* The formal temporary table. Should this be persistent? */
htab_t temp_htab;
- int conditions;
bool save_stack;
bool into_ssa;
};
--- 107,112 ----
*************** gimple_current_bind_expr (void)
*** 208,250 ****
return gimplify_ctxp->current_bind_expr;
}
- /* Returns true iff there is a COND_EXPR between us and the innermost
- CLEANUP_POINT_EXPR. This info is used by gimple_push_cleanup. */
-
- static bool
- gimple_conditional_context (void)
- {
- return gimplify_ctxp->conditions > 0;
- }
-
- /* Note that we've entered a COND_EXPR. */
-
- static void
- gimple_push_condition (void)
- {
- #ifdef ENABLE_CHECKING
- if (gimplify_ctxp->conditions == 0)
- gcc_assert (!gimplify_ctxp->conditional_cleanups);
- #endif
- ++(gimplify_ctxp->conditions);
- }
-
- /* Note that we've left a COND_EXPR. If we're back at unconditional scope
- now, add any conditional cleanups we've seen to the prequeue. */
-
- static void
- gimple_pop_condition (tree *pre_p)
- {
- int conds = --(gimplify_ctxp->conditions);
-
- gcc_assert (conds >= 0);
- if (conds == 0)
- {
- append_to_statement_list (gimplify_ctxp->conditional_cleanups, pre_p);
- gimplify_ctxp->conditional_cleanups = NULL_TREE;
- }
- }
-
/* A stable comparison routine for use with splay trees and DECLs. */
static int
--- 225,230 ----
*************** gimplify_cond_expr (tree *expr_p, tree *
*** 2430,2444 ****
if (expr != *expr_p)
{
*expr_p = expr;
-
- /* We can't rely on gimplify_expr to re-gimplify the expanded
- form properly, as cleanups might cause the target labels to be
- wrapped in a TRY_FINALLY_EXPR. To prevent that, we need to
- set up a conditional context. */
- gimple_push_condition ();
gimplify_stmt (expr_p);
- gimple_pop_condition (pre_p);
-
return GS_ALL_DONE;
}
}
--- 2410,2416 ----
*************** gimplify_cond_expr (tree *expr_p, tree *
*** 2447,2460 ****
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL,
is_gimple_condexpr, fb_rvalue);
- gimple_push_condition ();
-
gimplify_to_stmt_list (&TREE_OPERAND (expr, 1));
gimplify_to_stmt_list (&TREE_OPERAND (expr, 2));
recalculate_side_effects (expr);
- gimple_pop_condition (pre_p);
-
if (ret == GS_ERROR)
;
else if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1)))
--- 2419,2428 ----
*************** gimplify_asm_expr (tree *expr_p, tree *p
*** 3895,3971 ****
return ret;
}
! /* Gimplify a CLEANUP_POINT_EXPR. Currently this works by adding
! WITH_CLEANUP_EXPRs to the prequeue as we encounter cleanups while
! gimplifying the body, and converting them to TRY_FINALLY_EXPRs when we
! return to this function.
!
! FIXME should we complexify the prequeue handling instead? Or use flags
! for all the cleanups and let the optimizer tighten them up? The current
! code seems pretty fragile; it will break on a cleanup within any
! non-conditional nesting. But any such nesting would be broken, anyway;
! we can't write a TRY_FINALLY_EXPR that starts inside a nesting construct
! and continues out of it. We can do that at the RTL level, though, so
! having an optimizer to tighten up try/finally regions would be a Good
! Thing. */
static enum gimplify_status
gimplify_cleanup_point_expr (tree *expr_p, tree *pre_p)
{
- tree_stmt_iterator iter;
tree body;
-
tree temp = voidify_wrapper_expr (*expr_p, NULL);
! /* We only care about the number of conditions between the innermost
! CLEANUP_POINT_EXPR and the cleanup. So save and reset the count and
! any cleanups collected outside the CLEANUP_POINT_EXPR. */
! int old_conds = gimplify_ctxp->conditions;
! tree old_cleanups = gimplify_ctxp->conditional_cleanups;
! gimplify_ctxp->conditions = 0;
! gimplify_ctxp->conditional_cleanups = NULL_TREE;
body = TREE_OPERAND (*expr_p, 0);
gimplify_to_stmt_list (&body);
! gimplify_ctxp->conditions = old_conds;
! gimplify_ctxp->conditional_cleanups = old_cleanups;
!
! for (iter = tsi_start (body); !tsi_end_p (iter); )
{
! tree *wce_p = tsi_stmt_ptr (iter);
! tree wce = *wce_p;
! if (TREE_CODE (wce) == WITH_CLEANUP_EXPR)
! {
! if (tsi_one_before_end_p (iter))
! {
! tsi_link_before (&iter, TREE_OPERAND (wce, 0), TSI_SAME_STMT);
! tsi_delink (&iter);
! break;
! }
! else
! {
! tree sl, tfe;
! enum tree_code code;
! if (CLEANUP_EH_ONLY (wce))
! code = TRY_CATCH_EXPR;
! else
! code = TRY_FINALLY_EXPR;
! sl = tsi_split_statement_list_after (&iter);
! tfe = build2 (code, void_type_node, sl, NULL_TREE);
! append_to_statement_list (TREE_OPERAND (wce, 0),
! &TREE_OPERAND (tfe, 1));
! *wce_p = tfe;
! iter = tsi_start (sl);
! }
! }
! else
! tsi_next (&iter);
}
if (temp)
{
*expr_p = temp;
--- 3863,3908 ----
return ret;
}
! /* Gimplify a CLEANUP_POINT_EXPR. */
static enum gimplify_status
gimplify_cleanup_point_expr (tree *expr_p, tree *pre_p)
{
tree body;
tree temp = voidify_wrapper_expr (*expr_p, NULL);
+ struct cleanup *old_cleanups = gimplify_ctxp->cleanups;
+ struct cleanup *ac, *next;
! gimplify_ctxp->cleanups = NULL;
body = TREE_OPERAND (*expr_p, 0);
gimplify_to_stmt_list (&body);
! /* Execute the cleanups in the reverse order. */
! for (ac = gimplify_ctxp->cleanups; ac; ac = next)
{
! tree cleanups = NULL_TREE;
! tree cond;
! tree nbody = NULL_TREE;
! next = ac->next;
! cond = build3 (COND_EXPR, void_type_node,
! ac->guard, ac->cleanup, alloc_stmt_list ());
! append_to_statement_list (cond, &cleanups);
! append_to_statement_list (build2 (MODIFY_EXPR, void_type_node,
! ac->guard, boolean_false_node),
! &nbody);
! append_to_statement_list (body, &nbody);
!
! body = build2 (ac->eh_only ? TRY_CATCH_EXPR : TRY_FINALLY_EXPR,
! void_type_node, nbody, cleanups);
! free (ac);
}
+ gimplify_ctxp->cleanups = old_cleanups;
+
if (temp)
{
*expr_p = temp;
*************** gimplify_cleanup_point_expr (tree *expr_
*** 3985,4044 ****
static void
gimple_push_cleanup (tree var, tree cleanup, bool eh_only, tree *pre_p)
{
! tree wce;
/* Errors can result in improperly nested cleanups. Which results in
confusion when trying to resolve the WITH_CLEANUP_EXPR. */
if (errorcount || sorrycount)
return;
! if (gimple_conditional_context ())
! {
! /* If we're in a conditional context, this is more complex. We only
! want to run the cleanup if we actually ran the initialization that
! necessitates it, but we want to run it after the end of the
! conditional context. So we wrap the try/finally around the
! condition and use a flag to determine whether or not to actually
! run the destructor. Thus
!
! test ? f(A()) : 0
!
! becomes (approximately)
!
! flag = 0;
! try {
! if (test) { A::A(temp); flag = 1; val = f(temp); }
! else { val = 0; }
! } finally {
! if (flag) A::~A(temp);
! }
! val
! */
!
! tree flag = create_tmp_var (boolean_type_node, "cleanup");
! tree ffalse = build2 (MODIFY_EXPR, void_type_node, flag,
! boolean_false_node);
! tree ftrue = build2 (MODIFY_EXPR, void_type_node, flag,
! boolean_true_node);
! cleanup = build3 (COND_EXPR, void_type_node, flag, cleanup, NULL);
! wce = build1 (WITH_CLEANUP_EXPR, void_type_node, cleanup);
! append_to_statement_list (ffalse, &gimplify_ctxp->conditional_cleanups);
! append_to_statement_list (wce, &gimplify_ctxp->conditional_cleanups);
! append_to_statement_list (ftrue, pre_p);
!
! /* Because of this manipulation, and the EH edges that jump
! threading cannot redirect, the temporary (VAR) will appear
! to be used uninitialized. Don't warn. */
! TREE_NO_WARNING (var) = 1;
! }
! else
! {
! wce = build1 (WITH_CLEANUP_EXPR, void_type_node, cleanup);
! CLEANUP_EH_ONLY (wce) = eh_only;
! append_to_statement_list (wce, pre_p);
! }
! gimplify_stmt (&TREE_OPERAND (wce, 0));
}
/* Gimplify a TARGET_EXPR which doesn't appear on the rhs of an INIT_EXPR. */
--- 3922,3951 ----
static void
gimple_push_cleanup (tree var, tree cleanup, bool eh_only, tree *pre_p)
{
! struct cleanup *ac;
/* Errors can result in improperly nested cleanups. Which results in
confusion when trying to resolve the WITH_CLEANUP_EXPR. */
if (errorcount || sorrycount)
return;
! gimplify_to_stmt_list (&cleanup);
!
! ac = xmalloc (sizeof (struct cleanup));
! ac->eh_only = eh_only;
! ac->cleanup = cleanup;
! ac->guard = create_tmp_var (boolean_type_node, "cleanup");
! ac->next = gimplify_ctxp->cleanups;
! gimplify_ctxp->cleanups = ac;
!
! /* Because of the guards, and the EH edges that jump threading cannot
! redirect, the temporary (VAR) may appear to be used uninitialized
! in the cleanup. Don't warn. */
! TREE_NO_WARNING (var) = 1;
! append_to_statement_list (build2 (MODIFY_EXPR, void_type_node,
! ac->guard, boolean_true_node),
! pre_p);
}
/* Gimplify a TARGET_EXPR which doesn't appear on the rhs of an INIT_EXPR. */
Index: tree-pretty-print.c
===================================================================
*** tree-pretty-print.c (revision 110257)
--- tree-pretty-print.c (working copy)
*************** static int op_prio (tree);
*** 39,57 ****
static const char *op_symbol_1 (enum tree_code);
static const char *op_symbol (tree);
static void pretty_print_string (pretty_printer *, const char*);
! static void print_call_name (pretty_printer *, tree);
static void newline_and_indent (pretty_printer *, int);
static void maybe_init_pretty_print (FILE *);
static void print_declaration (pretty_printer *, tree, int, int);
static void print_struct_decl (pretty_printer *, tree, int, int);
! static void do_niy (pretty_printer *, tree);
static void dump_vops (pretty_printer *, tree, int, int);
static void dump_generic_bb_buff (pretty_printer *, basic_block, int, int);
#define INDENT(SPACE) do { \
int i; for (i = 0; i<SPACE; i++) pp_space (buffer); } while (0)
! #define NIY do_niy(buffer,node)
#define PRINT_FUNCTION_NAME(NODE) pp_printf \
(buffer, "%s", TREE_CODE (NODE) == NOP_EXPR ? \
--- 39,57 ----
static const char *op_symbol_1 (enum tree_code);
static const char *op_symbol (tree);
static void pretty_print_string (pretty_printer *, const char*);
! static void print_call_name (pretty_printer *, tree, int);
static void newline_and_indent (pretty_printer *, int);
static void maybe_init_pretty_print (FILE *);
static void print_declaration (pretty_printer *, tree, int, int);
static void print_struct_decl (pretty_printer *, tree, int, int);
! static void do_niy (pretty_printer *, tree, int);
static void dump_vops (pretty_printer *, tree, int, int);
static void dump_generic_bb_buff (pretty_printer *, basic_block, int, int);
#define INDENT(SPACE) do { \
int i; for (i = 0; i<SPACE; i++) pp_space (buffer); } while (0)
! #define NIY do_niy(buffer, node, spc)
#define PRINT_FUNCTION_NAME(NODE) pp_printf \
(buffer, "%s", TREE_CODE (NODE) == NOP_EXPR ? \
*************** static int initialized = 0;
*** 64,70 ****
/* Try to print something for an unknown tree code. */
static void
! do_niy (pretty_printer *buffer, tree node)
{
int i, len;
--- 64,70 ----
/* Try to print something for an unknown tree code. */
static void
! do_niy (pretty_printer *buffer, tree node, int spc)
{
int i, len;
*************** do_niy (pretty_printer *buffer, tree nod
*** 76,83 ****
len = TREE_CODE_LENGTH (TREE_CODE (node));
for (i = 0; i < len; ++i)
{
! newline_and_indent (buffer, 2);
! dump_generic_node (buffer, TREE_OPERAND (node, i), 2, 0, false);
}
}
--- 76,83 ----
len = TREE_CODE_LENGTH (TREE_CODE (node));
for (i = 0; i < len; ++i)
{
! newline_and_indent (buffer, spc + 2);
! dump_generic_node (buffer, TREE_OPERAND (node, i), spc + 2, 0, true);
}
}
*************** dump_generic_node (pretty_printer *buffe
*** 980,987 ****
}
dump_generic_node (buffer, TREE_OPERAND (node, 0),
! spc, flags, !(flags & TDF_SLIM));
! if (flags & TDF_SLIM)
newline_and_indent (buffer, spc);
else
{
--- 980,987 ----
}
dump_generic_node (buffer, TREE_OPERAND (node, 0),
! spc, flags, is_stmt);
! if (is_stmt)
newline_and_indent (buffer, spc);
else
{
*************** dump_generic_node (pretty_printer *buffe
*** 994,1001 ****
tp = &TREE_OPERAND (*tp, 1))
{
dump_generic_node (buffer, TREE_OPERAND (*tp, 0),
! spc, flags, !(flags & TDF_SLIM));
! if (flags & TDF_SLIM)
newline_and_indent (buffer, spc);
else
{
--- 994,1001 ----
tp = &TREE_OPERAND (*tp, 1))
{
dump_generic_node (buffer, TREE_OPERAND (*tp, 0),
! spc, flags, is_stmt);
! if (is_stmt)
newline_and_indent (buffer, spc);
else
{
*************** dump_generic_node (pretty_printer *buffe
*** 1004,1010 ****
}
}
! dump_generic_node (buffer, *tp, spc, flags, !(flags & TDF_SLIM));
}
break;
--- 1004,1010 ----
}
}
! dump_generic_node (buffer, *tp, spc, flags, is_stmt);
}
break;
*************** dump_generic_node (pretty_printer *buffe
*** 1042,1050 ****
case TARGET_EXPR:
pp_string (buffer, "TARGET_EXPR <");
dump_generic_node (buffer, TARGET_EXPR_SLOT (node), spc, flags, false);
! pp_character (buffer, ',');
! pp_space (buffer);
! dump_generic_node (buffer, TARGET_EXPR_INITIAL (node), spc, flags, false);
pp_character (buffer, '>');
break;
--- 1042,1057 ----
case TARGET_EXPR:
pp_string (buffer, "TARGET_EXPR <");
dump_generic_node (buffer, TARGET_EXPR_SLOT (node), spc, flags, false);
! newline_and_indent (buffer, spc+2);
! pp_string (buffer, "init: ");
! dump_generic_node (buffer, TARGET_EXPR_INITIAL (node), spc + 8, flags, true);
! if (TARGET_EXPR_CLEANUP (node))
! {
! newline_and_indent (buffer, spc+2);
! pp_string (buffer, "clean: ");
! dump_generic_node (buffer, TARGET_EXPR_CLEANUP (node), spc + 9, flags, true);
! }
! newline_and_indent (buffer, spc+2);
pp_character (buffer, '>');
break;
*************** dump_generic_node (pretty_printer *buffe
*** 1140,1146 ****
break;
case CALL_EXPR:
! print_call_name (buffer, node);
/* Print parameters. */
pp_space (buffer);
--- 1147,1153 ----
break;
case CALL_EXPR:
! print_call_name (buffer, node, spc);
/* Print parameters. */
pp_space (buffer);
*************** dump_generic_node (pretty_printer *buffe
*** 1169,1176 ****
break;
case CLEANUP_POINT_EXPR:
! pp_string (buffer, "<<cleanup_point ");
! dump_generic_node (buffer, TREE_OPERAND (node, 0), spc, flags, false);
pp_string (buffer, ">>");
break;
--- 1176,1186 ----
break;
case CLEANUP_POINT_EXPR:
! pp_string (buffer, "<<cleanup_point");
! newline_and_indent (buffer, spc + 2);
! dump_generic_node (buffer, TREE_OPERAND (node, 0),
! spc + 2, flags, true);
! newline_and_indent (buffer, spc + 2);
pp_string (buffer, ">>");
break;
*************** dump_generic_node (pretty_printer *buffe
*** 1414,1421 ****
newline_and_indent (buffer, spc+2);
pp_string (buffer, "}");
newline_and_indent (buffer, spc);
! pp_string (buffer,
! (TREE_CODE (node) == TRY_CATCH_EXPR) ? "catch" : "finally");
newline_and_indent (buffer, spc+2);
pp_string (buffer, "{");
newline_and_indent (buffer, spc+4);
--- 1424,1430 ----
newline_and_indent (buffer, spc+2);
pp_string (buffer, "}");
newline_and_indent (buffer, spc);
! pp_string (buffer, (TREE_CODE (node) == TRY_CATCH_EXPR) ? "catch" : "finally");
newline_and_indent (buffer, spc+2);
pp_string (buffer, "{");
newline_and_indent (buffer, spc+4);
*************** op_symbol (tree op)
*** 2402,2408 ****
/* Prints the name of a CALL_EXPR. */
static void
! print_call_name (pretty_printer *buffer, tree node)
{
tree op0;
--- 2411,2417 ----
/* Prints the name of a CALL_EXPR. */
static void
! print_call_name (pretty_printer *buffer, tree node, int spc)
{
tree op0;
*************** print_call_name (pretty_printer *buffer,
*** 2423,2438 ****
case ADDR_EXPR:
case INDIRECT_REF:
case NOP_EXPR:
! dump_generic_node (buffer, TREE_OPERAND (op0, 0), 0, 0, false);
break;
case COND_EXPR:
pp_string (buffer, "(");
! dump_generic_node (buffer, TREE_OPERAND (op0, 0), 0, 0, false);
pp_string (buffer, ") ? ");
! dump_generic_node (buffer, TREE_OPERAND (op0, 1), 0, 0, false);
pp_string (buffer, " : ");
! dump_generic_node (buffer, TREE_OPERAND (op0, 2), 0, 0, false);
break;
case COMPONENT_REF:
--- 2432,2447 ----
case ADDR_EXPR:
case INDIRECT_REF:
case NOP_EXPR:
! dump_generic_node (buffer, TREE_OPERAND (op0, 0), spc, 0, false);
break;
case COND_EXPR:
pp_string (buffer, "(");
! dump_generic_node (buffer, TREE_OPERAND (op0, 0), spc, 0, false);
pp_string (buffer, ") ? ");
! dump_generic_node (buffer, TREE_OPERAND (op0, 1), spc, 0, false);
pp_string (buffer, " : ");
! dump_generic_node (buffer, TREE_OPERAND (op0, 2), spc, 0, false);
break;
case COMPONENT_REF:
*************** print_call_name (pretty_printer *buffer,
*** 2441,2447 ****
TREE_CODE (TREE_OPERAND (op0, 0)) == VAR_DECL)
dump_function_name (buffer, TREE_OPERAND (op0, 1));
else
! dump_generic_node (buffer, TREE_OPERAND (op0, 0), 0, 0, false);
/* else
We can have several levels of structures and a function
pointer inside. This is not implemented yet... */
--- 2450,2456 ----
TREE_CODE (TREE_OPERAND (op0, 0)) == VAR_DECL)
dump_function_name (buffer, TREE_OPERAND (op0, 1));
else
! dump_generic_node (buffer, TREE_OPERAND (op0, 0), spc, 0, false);
/* else
We can have several levels of structures and a function
pointer inside. This is not implemented yet... */
*************** print_call_name (pretty_printer *buffer,
*** 2452,2463 ****
if (TREE_CODE (TREE_OPERAND (op0, 0)) == VAR_DECL)
dump_function_name (buffer, TREE_OPERAND (op0, 0));
else
! dump_generic_node (buffer, op0, 0, 0, false);
break;
case SSA_NAME:
case OBJ_TYPE_REF:
! dump_generic_node (buffer, op0, 0, 0, false);
break;
default:
--- 2461,2472 ----
if (TREE_CODE (TREE_OPERAND (op0, 0)) == VAR_DECL)
dump_function_name (buffer, TREE_OPERAND (op0, 0));
else
! dump_generic_node (buffer, op0, spc, 0, false);
break;
case SSA_NAME:
case OBJ_TYPE_REF:
! dump_generic_node (buffer, op0, spc, 0, false);
break;
default: