diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 7 | ||||
-rw-r--r-- | gcc/cp/ChangeLog | 13 | ||||
-rw-r--r-- | gcc/cp/Make-lang.in | 2 | ||||
-rw-r--r-- | gcc/cp/except.c | 155 | ||||
-rw-r--r-- | gcc/cp/init.c | 20 | ||||
-rw-r--r-- | gcc/cp/tree.c | 33 | ||||
-rw-r--r-- | gcc/tree.h | 5 |
7 files changed, 197 insertions, 38 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 5aa2209e581..2066310cc36 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2002-03-01 Jason Merrill <jason@redhat.com> + + * tree.h (TARGET_EXPR_SLOT, TARGET_EXPR_INITIAL): New macros. + (TARGET_EXPR_CLEANUP): New macro. + 2002-02-28 Steve Ellcey <sje@cup.hp.com> * doc/rtl.texi (SUBREG_PROMOTED_UNSIGNED_P): Change definition @@ -78,7 +83,7 @@ (STARTFILE_SPEC): Add 64 bit files. (ENDFILE_SPEC): Likewise. -2002-02-25 Jason Merrill <jason@redhat.com> +2002-02-28 Jason Merrill <jason@redhat.com> * c-decl.c (finish_function): Only warn about missing return statement with -Wreturn-type. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 85f254704ec..540e8749d44 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,16 @@ +2002-03-01 Jason Merrill <jason@redhat.com> + + * except.c: Don't include decl.h or obstack.h. Do include + tree-inline.h. + (build_throw): Destroy temporaries from the thrown + expression before calling __cxa_throw. Construct a thrown + temporary directly into the exception object. + (stabilize_throw_expr): New function. + (wrap_cleanups_r): New function. + * tree.c (stabilize_expr): New function. + * init.c (build_init): New function. + * Make-lang.in (cp/except.o): Adjust .h deps. + 2002-02-28 Jason Merrill <jason@redhat.com> * search.c (lookup_base_r): Don't clear is_non_public just because diff --git a/gcc/cp/Make-lang.in b/gcc/cp/Make-lang.in index f8d023a1cb7..1e721b6ac56 100644 --- a/gcc/cp/Make-lang.in +++ b/gcc/cp/Make-lang.in @@ -277,7 +277,7 @@ cp/tree.o: cp/tree.c $(CXX_TREE_H) flags.h toplev.h $(GGC_H) $(RTL_H) \ cp/ptree.o: cp/ptree.c $(CXX_TREE_H) $(SYSTEM_H) cp/rtti.o: cp/rtti.c $(CXX_TREE_H) flags.h toplev.h cp/except.o: cp/except.c $(CXX_TREE_H) flags.h $(RTL_H) except.h toplev.h \ - cp/cfns.h $(EXPR_H) libfuncs.h cp/decl.h $(OBSTACK_H) + cp/cfns.h $(EXPR_H) libfuncs.h tree-inline.h cp/expr.o: cp/expr.c $(CXX_TREE_H) $(RTL_H) flags.h $(EXPR_H) toplev.h \ except.h $(TM_P_H) cp/pt.o: cp/pt.c $(CXX_TREE_H) cp/decl.h cp/parse.h cp/lex.h toplev.h \ diff --git a/gcc/cp/except.c b/gcc/cp/except.c index 3f3cdcfd3be..8cbbfa916cb 100644 --- a/gcc/cp/except.c +++ b/gcc/cp/except.c @@ -35,6 +35,7 @@ Boston, MA 02111-1307, USA. */ #include "output.h" #include "except.h" #include "toplev.h" +#include "tree-inline.h" static void push_eh_cleanup PARAMS ((tree)); static tree prepare_eh_type PARAMS ((tree)); @@ -46,15 +47,14 @@ static void push_eh_cleanup PARAMS ((tree)); static bool decl_is_java_type PARAMS ((tree decl, int err)); static void initialize_handler_parm PARAMS ((tree, tree)); static tree do_allocate_exception PARAMS ((tree)); +static tree stabilize_throw_expr PARAMS ((tree, tree *)); +static tree wrap_cleanups_r PARAMS ((tree *, int *, void *)); static int complete_ptr_ref_or_void_ptr_p PARAMS ((tree, tree)); static bool is_admissible_throw_operand PARAMS ((tree)); static int can_convert_eh PARAMS ((tree, tree)); static void check_handlers_1 PARAMS ((tree, tree)); static tree cp_protect_cleanup_actions PARAMS ((void)); -#include "decl.h" -#include "obstack.h" - /* Sets up all the global eh stuff that needs to be initialized at the start of compilation. */ @@ -518,7 +518,7 @@ do_allocate_exception (type) #if 0 /* Call __cxa_free_exception from a cleanup. This is never invoked - directly. */ + directly, but see the comment for stabilize_throw_expr. */ static tree do_free_exception (ptr) @@ -540,6 +540,89 @@ do_free_exception (ptr) } #endif +/* Wrap all cleanups for TARGET_EXPRs in MUST_NOT_THROW_EXPR. + Called from build_throw via walk_tree_without_duplicates. */ + +static tree +wrap_cleanups_r (tp, walk_subtrees, data) + tree *tp; + int *walk_subtrees ATTRIBUTE_UNUSED; + void *data ATTRIBUTE_UNUSED; +{ + tree exp = *tp; + tree cleanup; + + /* Don't walk into types. */ + if (TYPE_P (exp)) + { + *walk_subtrees = 0; + return NULL_TREE; + } + if (TREE_CODE (exp) != TARGET_EXPR) + return NULL_TREE; + + cleanup = TARGET_EXPR_CLEANUP (exp); + if (cleanup) + { + cleanup = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (cleanup), cleanup); + TARGET_EXPR_CLEANUP (exp) = cleanup; + } + + /* Keep iterating. */ + return NULL_TREE; +} + +/* Like stabilize_expr, but specifically for a thrown expression. When + throwing a temporary class object, we want to construct it directly into + the thrown exception, so we look past the TARGET_EXPR and stabilize the + arguments of the call instead. + + The case where EXP is a call to a function returning a class is a bit of + a grey area in the standard; it's unclear whether or not it should be + allowed to throw. I'm going to say no, as that allows us to optimize + this case without worrying about deallocating the exception object if it + does. The alternatives would be either not optimizing this case, or + wrapping the initialization in a TRY_CATCH_EXPR to call do_free_exception + rather than in a MUST_NOT_THROW_EXPR, for this case only. */ + +static tree +stabilize_throw_expr (exp, initp) + tree exp; + tree *initp; +{ + tree init_expr; + + if (TREE_CODE (exp) == TARGET_EXPR + && TREE_CODE (TARGET_EXPR_INITIAL (exp)) == AGGR_INIT_EXPR + && flag_elide_constructors) + { + tree aggr_init = AGGR_INIT_EXPR_CHECK (TARGET_EXPR_INITIAL (exp)); + tree args = TREE_OPERAND (aggr_init, 1); + tree newargs = NULL_TREE; + tree *p = &newargs; + + init_expr = void_zero_node; + for (; args; args = TREE_CHAIN (args)) + { + tree arg_init_expr; + tree newarg = stabilize_expr (TREE_VALUE (args), &arg_init_expr); + + if (arg_init_expr != void_zero_node) + init_expr = build (COMPOUND_EXPR, void_type_node, arg_init_expr, init_expr); + *p = tree_cons (NULL_TREE, newarg, NULL_TREE); + p = &TREE_CHAIN (*p); + } + TREE_OPERAND (aggr_init, 1) = newargs; + } + else + { + exp = stabilize_expr (exp, &init_expr); + } + + *initp = init_expr; + return exp; +} + /* Build a throw expression. */ tree @@ -585,10 +668,9 @@ build_throw (exp) { tree throw_type; tree cleanup; - tree stmt_expr; - tree compound_stmt; tree object, ptr; tree tmp; + tree temp_expr, allocate_expr; fn = get_identifier ("__cxa_throw"); if (IDENTIFIER_GLOBAL_VALUE (fn)) @@ -614,8 +696,6 @@ build_throw (exp) fn = push_throw_library_fn (fn, tmp); } - begin_init_stmts (&stmt_expr, &compound_stmt); - /* throw expression */ /* First, decay it. */ exp = decay_conversion (exp); @@ -633,37 +713,40 @@ build_throw (exp) the call to __cxa_allocate_exception first (which doesn't matter, since it can't throw). */ - my_friendly_assert (stmts_are_full_exprs_p () == 1, 19990926); - - /* Store the throw expression into a temp. This can be less - efficient than storing it into the allocated space directly, but - if we allocated the space first we would have to deal with - cleaning it up if evaluating this expression throws. */ - if (TREE_SIDE_EFFECTS (exp)) - { - tmp = create_temporary_var (TREE_TYPE (exp)); - DECL_INITIAL (tmp) = exp; - cp_finish_decl (tmp, exp, NULL_TREE, LOOKUP_ONLYCONVERTING); - exp = tmp; - } + /* Pre-evaluate the thrown expression first, since if we allocated + the space first we would have to deal with cleaning it up if + evaluating this expression throws. */ + exp = stabilize_throw_expr (exp, &temp_expr); /* Allocate the space for the exception. */ - ptr = create_temporary_var (ptr_type_node); - DECL_REGISTER (ptr) = 1; - cp_finish_decl (ptr, NULL_TREE, NULL_TREE, LOOKUP_ONLYCONVERTING); - tmp = do_allocate_exception (TREE_TYPE (exp)); - tmp = build_modify_expr (ptr, INIT_EXPR, tmp); - finish_expr_stmt (tmp); - + allocate_expr = do_allocate_exception (TREE_TYPE (exp)); + allocate_expr = get_target_expr (allocate_expr); + ptr = TARGET_EXPR_SLOT (allocate_expr); object = build1 (NOP_EXPR, build_pointer_type (TREE_TYPE (exp)), ptr); object = build_indirect_ref (object, NULL); - exp = build_modify_expr (object, INIT_EXPR, exp); + /* And initialize the exception object. */ + exp = build_init (object, exp, LOOKUP_ONLYCONVERTING); if (exp == error_mark_node) - error (" in thrown expression"); + { + error (" in thrown expression"); + return error_mark_node; + } exp = build1 (MUST_NOT_THROW_EXPR, TREE_TYPE (exp), exp); - finish_expr_stmt (exp); + /* Prepend the allocation. */ + exp = build (COMPOUND_EXPR, TREE_TYPE (exp), allocate_expr, exp); + if (temp_expr != void_zero_node) + { + /* Prepend the calculation of the throw expression. Also, force + any cleanups from the expression to be evaluated here so that + we don't have to do them during unwinding. But first wrap + them in MUST_NOT_THROW_EXPR, since they are run after the + exception object is initialized. */ + walk_tree_without_duplicates (&temp_expr, wrap_cleanups_r, 0); + exp = build (COMPOUND_EXPR, TREE_TYPE (exp), temp_expr, exp); + exp = build1 (CLEANUP_POINT_EXPR, TREE_TYPE (exp), exp); + } throw_type = build_eh_type_type (prepare_eh_type (TREE_TYPE (object))); @@ -686,13 +769,11 @@ build_throw (exp) tmp = tree_cons (NULL_TREE, cleanup, NULL_TREE); tmp = tree_cons (NULL_TREE, throw_type, tmp); tmp = tree_cons (NULL_TREE, ptr, tmp); - tmp = build_function_call (fn, tmp); - /* ??? Indicate that this function call throws throw_type. */ + tmp = build_function_call (fn, tmp); - finish_expr_stmt (tmp); - - exp = finish_init_stmts (stmt_expr, compound_stmt); + /* Tack on the initialization stuff. */ + exp = build (COMPOUND_EXPR, TREE_TYPE (tmp), exp, tmp); } else { @@ -708,6 +789,8 @@ build_throw (exp) (fn, build_function_type (void_type_node, void_list_node)); } + /* ??? Indicate that this function call allows exceptions of the type + of the enclosing catch block (if known). */ exp = build_function_call (fn, NULL_TREE); } diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 017b8943906..7eaafc99354 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -1201,6 +1201,26 @@ build_aggr_init (exp, init, flags) return stmt_expr; } +/* Like build_aggr_init, but not just for aggregates. */ + +tree +build_init (decl, init, flags) + tree decl, init; + int flags; +{ + tree expr; + + if (IS_AGGR_TYPE (TREE_TYPE (decl)) + || TREE_CODE (TREE_TYPE (decl)) == ARRAY_TYPE) + expr = build_aggr_init (decl, init, flags); + else + { + expr = build (INIT_EXPR, TREE_TYPE (decl), decl, init); + TREE_SIDE_EFFECTS (expr) = 1; + } + return expr; +} + static void expand_default_init (binfo, true_exp, exp, init, flags) tree binfo; diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 8b0a1984780..a49a2dcc664 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -2497,3 +2497,36 @@ decl_linkage (decl) /* Everything else has internal linkage. */ return lk_internal; } + +/* EXP is an expression that we want to pre-evaluate. Returns via INITP an + expression to perform the pre-evaluation, and returns directly an + expression to use the precalculated result. */ + +tree +stabilize_expr (exp, initp) + tree exp; + tree *initp; +{ + tree init_expr; + + if (!TREE_SIDE_EFFECTS (exp)) + { + init_expr = void_zero_node; + } + else if (!real_lvalue_p (exp) + || !TYPE_NEEDS_CONSTRUCTING (TREE_TYPE (exp))) + { + init_expr = get_target_expr (exp); + exp = TARGET_EXPR_SLOT (init_expr); + } + else + { + exp = build_unary_op (ADDR_EXPR, exp, 1); + init_expr = get_target_expr (exp); + exp = TARGET_EXPR_SLOT (init_expr); + exp = build_indirect_ref (exp, 0); + } + + *initp = init_expr; + return exp; +} diff --git a/gcc/tree.h b/gcc/tree.h index 06d9815ba15..b13fc53cdb5 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -868,6 +868,11 @@ struct tree_vec #define EXPR_WFL_SET_LINECOL(NODE, LINE, COL) \ (EXPR_WFL_LINECOL(NODE) = ((LINE) << 12) | ((COL) & 0xfff)) +/* In a TARGET_EXPR node. */ +#define TARGET_EXPR_SLOT(NODE) TREE_OPERAND (TARGET_EXPR_CHECK (NODE), 0) +#define TARGET_EXPR_INITIAL(NODE) TREE_OPERAND (TARGET_EXPR_CHECK (NODE), 1) +#define TARGET_EXPR_CLEANUP(NODE) TREE_OPERAND (TARGET_EXPR_CHECK (NODE), 2) + struct tree_exp { struct tree_common common; |