diff options
author | Richard Henderson <rth@cygnus.com> | 2000-03-24 17:06:35 -0800 |
---|---|---|
committer | Richard Henderson <rth@gcc.gnu.org> | 2000-03-24 17:06:35 -0800 |
commit | 194c7c45b14098d8ed488e57d9d37e3b290ee4cd (patch) | |
tree | 9ebdd42ff7f81a59b06dea0e7f79c96c34249f4b /gcc | |
parent | 1c33f70c9679c08daf29d6911240ff7b9da94ee0 (diff) | |
download | gcc-194c7c45b14098d8ed488e57d9d37e3b290ee4cd.zip gcc-194c7c45b14098d8ed488e57d9d37e3b290ee4cd.tar.gz gcc-194c7c45b14098d8ed488e57d9d37e3b290ee4cd.tar.bz2 |
tree.c (lang_safe_for_unsave): Remove.
* tree.c (lang_safe_for_unsave): Remove.
(unsafe_for_reeval): Transmute and rename from safe_for_unsave,
allowing for two levels of unsafeness. Remove lang hook.
* tree.h: Update declarations.
* calls.c (expand_call): Rename safe_for_reeval to try_tail_call.
Create temporary VAR_DECLs to protect very unsafe_for_reeval trees.
Always fail sibcalls when there are pending cleanups.
From-SVN: r32735
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 10 | ||||
-rw-r--r-- | gcc/calls.c | 89 | ||||
-rw-r--r-- | gcc/tree.c | 64 | ||||
-rw-r--r-- | gcc/tree.h | 10 |
4 files changed, 112 insertions, 61 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index dfd53dd..8ca4e95 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2000-03-24 Richard Henderson <rth@cygnus.com> + + * tree.c (lang_safe_for_unsave): Remove. + (unsafe_for_reeval): Transmute and rename from safe_for_unsave, + allowing for two levels of unsafeness. Remove lang hook. + * tree.h: Update declarations. + * calls.c (expand_call): Rename safe_for_reeval to try_tail_call. + Create temporary VAR_DECLs to protect very unsafe_for_reeval trees. + Always fail sibcalls when there are pending cleanups. + 2000-03-24 Geoff Keating <geoffk@cygnus.com> * flow.c (propagate_block): When we delete an ADDR_VEC, diff --git a/gcc/calls.c b/gcc/calls.c index 0a8688c..ed4b31b 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1696,7 +1696,7 @@ expand_call (exp, target, ignore) rtx before_call; #endif rtx insn; - int safe_for_reeval; + int try_tail_call; int pass; /* Register in which non-BLKmode value will be returned, @@ -2027,44 +2027,70 @@ expand_call (exp, target, ignore) currently_expanding_call++; - /* If we're considering tail recursion optimizations, verify that the - arguments are safe for re-evaluation. If we can unsave them, wrap - each argument in an UNSAVE_EXPR. */ + /* Tail calls can make things harder to debug, and we're traditionally + pushed these optimizations into -O2. Don't try if we're already + expanding a call, as that means we're an argument. Similarly, if + there's pending loops or cleanups we know there's code to follow + the call. */ - safe_for_reeval = 0; + try_tail_call = 0; if (optimize >= 2 && currently_expanding_call == 1 && stmt_loop_nest_empty () && ! any_pending_cleanups (1)) { - /* Verify that each argument is safe for re-evaluation. */ + tree new_actparms = NULL_TREE; + + /* Ok, we're going to give the tail call the old college try. + This means we're going to evaluate the function arguments + up to three times. There are two degrees of badness we can + encounter, those that can be unsaved and those that can't. + (See unsafe_for_reeval commentary for details.) + + Generate a new argument list. Pass safe arguments through + unchanged. For the easy badness wrap them in UNSAVE_EXPRs. + For hard badness, evaluate them now and put their resulting + rtx in a temporary VAR_DECL. */ + for (p = actparms; p; p = TREE_CHAIN (p)) - if (! safe_for_unsave (TREE_VALUE (p))) - break; + switch (unsafe_for_reeval (TREE_VALUE (p))) + { + case 0: /* Safe. */ + new_actparms = tree_cons (TREE_PURPOSE (p), TREE_VALUE (p), + new_actparms); + break; - if (p == NULL_TREE) - { - tree new_actparms = NULL_TREE, q; + case 1: /* Mildly unsafe. */ + new_actparms = tree_cons (TREE_PURPOSE (p), + unsave_expr (TREE_VALUE (p)), + new_actparms); + break; - for (p = actparms; p ; p = TREE_CHAIN (p)) + case 2: /* Wildly unsafe. */ { - tree np = build_tree_list (TREE_PURPOSE (p), - unsave_expr (TREE_VALUE (p))); - if (new_actparms) - TREE_CHAIN (q) = np; - else - new_actparms = np; - q = np; + tree var = build_decl (VAR_DECL, NULL_TREE, + TREE_TYPE (TREE_VALUE (p))); + DECL_RTL (var) = expand_expr (TREE_VALUE (p), NULL_RTX, + VOIDmode, EXPAND_NORMAL); + new_actparms = tree_cons (TREE_PURPOSE (p), var, new_actparms); } + break; - actparms = new_actparms; - safe_for_reeval = 1; - } + default: + abort (); + } + + /* We built the new argument chain backwards. */ + actparms = nreverse (new_actparms); + + /* Expanding one of those dangerous arguments could have added + cleanups, but otherwise give it a whirl. */ + try_tail_call = ! any_pending_cleanups (1); } /* Generate a tail recursion sequence when calling ourselves. */ - if (safe_for_reeval + if (try_tail_call && TREE_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR && TREE_OPERAND (TREE_OPERAND (exp, 0), 0) == current_function_decl) { @@ -2149,7 +2175,7 @@ expand_call (exp, target, ignore) if (pass == 0) { /* Various reasons we can not use a sibling call. */ - if (! safe_for_reeval + if (! try_tail_call #ifdef HAVE_sibcall_epilogue || ! HAVE_sibcall_epilogue #else @@ -2163,6 +2189,10 @@ expand_call (exp, target, ignore) /* If the register holding the address is a callee saved register, then we lose. We have no way to prevent that, so we only allow calls to named functions. */ + /* ??? This could be done by having the insn constraints + use a register class that is all call-clobbered. Any + reload insns generated to fix things up would appear + before the sibcall_epilogue. */ || fndecl == NULL_TREE || ! FUNCTION_OK_FOR_SIBCALL (fndecl)) continue; @@ -2819,10 +2849,13 @@ expand_call (exp, target, ignore) /* If there are cleanups to be called, don't use a hard reg as target. We need to double check this and see if it matters anymore. */ - if (any_pending_cleanups (1) - && target && REG_P (target) - && REGNO (target) < FIRST_PSEUDO_REGISTER) - target = 0, sibcall_failure = 1; + if (any_pending_cleanups (1)) + { + if (target && REG_P (target) + && REGNO (target) < FIRST_PSEUDO_REGISTER) + target = 0; + sibcall_failure = 1; + } if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode || ignore) @@ -290,9 +290,6 @@ static void print_type_hash_statistics PARAMS((void)); void (*lang_unsave) PARAMS ((tree *)); void (*lang_unsave_expr_now) PARAMS ((tree)); -/* If non-null, a language specific version of safe_for_unsave. */ -int (*lang_safe_for_unsave) PARAMS ((tree)); - /* The string used as a placeholder instead of a source file name for built-in tree nodes. The variable, which is dynamically allocated, should be used; the macro is only used to initialize it. */ @@ -2675,15 +2672,28 @@ unsave_expr_now (expr) return expr; } -/* Return nonzero if it is safe to unsave EXPR, else return zero. - It is not safe to unsave EXPR if it contains any embedded RTL_EXPRs. */ +/* Return 0 if it is safe to evaluate EXPR multiple times, + return 1 if it is safe if EXPR is unsaved afterward, or + return 2 if it is completely unsafe. + + This assumes that CALL_EXPRs and TARGET_EXPRs are never replicated in + an expression tree, so that it safe to unsave them and the surrounding + context will be correct. + + SAVE_EXPRs basically *only* appear replicated in an expression tree, + occasionally across the whole of a function. It is therefore only + safe to unsave a SAVE_EXPR if you know that all occurrences appear + below the UNSAVE_EXPR. + + RTL_EXPRs consume their rtl during evaluation. It is therefore + never possible to unsave them. */ int -safe_for_unsave (expr) +unsafe_for_reeval (expr) tree expr; { enum tree_code code; - register int i; + register int i, tmp, unsafeness; int first_rtl; if (expr == NULL_TREE) @@ -2691,10 +2701,13 @@ safe_for_unsave (expr) code = TREE_CODE (expr); first_rtl = first_rtl_op (code); + unsafeness = 0; + switch (code) { + case SAVE_EXPR: case RTL_EXPR: - return 0; + return 2; case CALL_EXPR: if (TREE_OPERAND (expr, 1) @@ -2703,26 +2716,20 @@ safe_for_unsave (expr) tree exp = TREE_OPERAND (expr, 1); while (exp) { - if (! safe_for_unsave (TREE_VALUE (exp))) - return 0; + tmp = unsafe_for_reeval (TREE_VALUE (exp)); + if (tmp > 1) + return tmp; exp = TREE_CHAIN (exp); } } + return 1; + + case TARGET_EXPR: + unsafeness = 1; break; default: - if (lang_safe_for_unsave) - switch ((*lang_safe_for_unsave) (expr)) - { - case -1: - break; - case 0: - return 0; - case 1: - return 1; - default: - abort (); - } + /* ??? Add a lang hook if it becomes necessary. */ break; } @@ -2733,7 +2740,7 @@ safe_for_unsave (expr) case 'x': /* something random, like an identifier or an ERROR_MARK. */ case 'd': /* A decl node */ case 'b': /* A block node */ - return 1; + return 0; case 'e': /* an expression */ case 'r': /* a reference */ @@ -2742,12 +2749,15 @@ safe_for_unsave (expr) case '2': /* a binary arithmetic expression */ case '1': /* a unary arithmetic expression */ for (i = first_rtl - 1; i >= 0; i--) - if (! safe_for_unsave (TREE_OPERAND (expr, i))) - return 0; - return 1; + { + tmp = unsafe_for_reeval (TREE_OPERAND (expr, i)); + if (tmp > unsafeness) + unsafeness = tmp; + } + return unsafeness; default: - return 0; + return 2; } } @@ -1963,12 +1963,10 @@ extern tree unsave_expr_now PARAMS ((tree)); extern void (*lang_unsave) PARAMS ((tree *)); extern void (*lang_unsave_expr_now) PARAMS ((tree)); -/* If non-null, a language specific version of safe_for_unsave. */ -extern int (*lang_safe_for_unsave) PARAMS ((tree)); - -/* Return nonzero if it is safe to unsave EXPR, else return zero. - It is not safe to unsave EXPR if it contains any embedded RTL_EXPRs. */ -extern int safe_for_unsave PARAMS ((tree)); +/* Return 0 if it is safe to evaluate EXPR multiple times, + return 1 if it is safe if EXPR is unsaved afterward, or + return 2 if it is completely unsafe. */ +extern int unsafe_for_reeval PARAMS ((tree)); /* Return 1 if EXP contains a PLACEHOLDER_EXPR; i.e., if it represents a size or offset that depends on a field within a record. |