Commit 194c7c45 by Richard Henderson Committed by Richard Henderson

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
parent 1c33f70c
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> 2000-03-24 Geoff Keating <geoffk@cygnus.com>
* flow.c (propagate_block): When we delete an ADDR_VEC, * flow.c (propagate_block): When we delete an ADDR_VEC,
......
...@@ -1696,7 +1696,7 @@ expand_call (exp, target, ignore) ...@@ -1696,7 +1696,7 @@ expand_call (exp, target, ignore)
rtx before_call; rtx before_call;
#endif #endif
rtx insn; rtx insn;
int safe_for_reeval; int try_tail_call;
int pass; int pass;
/* Register in which non-BLKmode value will be returned, /* Register in which non-BLKmode value will be returned,
...@@ -2027,44 +2027,70 @@ expand_call (exp, target, ignore) ...@@ -2027,44 +2027,70 @@ expand_call (exp, target, ignore)
currently_expanding_call++; currently_expanding_call++;
/* If we're considering tail recursion optimizations, verify that the /* Tail calls can make things harder to debug, and we're traditionally
arguments are safe for re-evaluation. If we can unsave them, wrap pushed these optimizations into -O2. Don't try if we're already
each argument in an UNSAVE_EXPR. */ 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 if (optimize >= 2
&& currently_expanding_call == 1 && currently_expanding_call == 1
&& stmt_loop_nest_empty () && stmt_loop_nest_empty ()
&& ! any_pending_cleanups (1)) && ! 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)) for (p = actparms; p; p = TREE_CHAIN (p))
if (! safe_for_unsave (TREE_VALUE (p))) switch (unsafe_for_reeval (TREE_VALUE (p)))
break; {
case 0: /* Safe. */
new_actparms = tree_cons (TREE_PURPOSE (p), TREE_VALUE (p),
new_actparms);
break;
if (p == NULL_TREE) case 1: /* Mildly unsafe. */
{ new_actparms = tree_cons (TREE_PURPOSE (p),
tree new_actparms = NULL_TREE, q; 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), tree var = build_decl (VAR_DECL, NULL_TREE,
unsave_expr (TREE_VALUE (p))); TREE_TYPE (TREE_VALUE (p)));
if (new_actparms) DECL_RTL (var) = expand_expr (TREE_VALUE (p), NULL_RTX,
TREE_CHAIN (q) = np; VOIDmode, EXPAND_NORMAL);
else new_actparms = tree_cons (TREE_PURPOSE (p), var, new_actparms);
new_actparms = np;
q = np;
} }
break;
actparms = new_actparms; default:
safe_for_reeval = 1; 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. */ /* 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_CODE (TREE_OPERAND (exp, 0)) == ADDR_EXPR
&& TREE_OPERAND (TREE_OPERAND (exp, 0), 0) == current_function_decl) && TREE_OPERAND (TREE_OPERAND (exp, 0), 0) == current_function_decl)
{ {
...@@ -2149,7 +2175,7 @@ expand_call (exp, target, ignore) ...@@ -2149,7 +2175,7 @@ expand_call (exp, target, ignore)
if (pass == 0) if (pass == 0)
{ {
/* Various reasons we can not use a sibling call. */ /* Various reasons we can not use a sibling call. */
if (! safe_for_reeval if (! try_tail_call
#ifdef HAVE_sibcall_epilogue #ifdef HAVE_sibcall_epilogue
|| ! HAVE_sibcall_epilogue || ! HAVE_sibcall_epilogue
#else #else
...@@ -2163,6 +2189,10 @@ expand_call (exp, target, ignore) ...@@ -2163,6 +2189,10 @@ expand_call (exp, target, ignore)
/* If the register holding the address is a callee saved /* If the register holding the address is a callee saved
register, then we lose. We have no way to prevent that, register, then we lose. We have no way to prevent that,
so we only allow calls to named functions. */ 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 || fndecl == NULL_TREE
|| ! FUNCTION_OK_FOR_SIBCALL (fndecl)) || ! FUNCTION_OK_FOR_SIBCALL (fndecl))
continue; continue;
...@@ -2819,10 +2849,13 @@ expand_call (exp, target, ignore) ...@@ -2819,10 +2849,13 @@ expand_call (exp, target, ignore)
/* If there are cleanups to be called, don't use a hard reg as target. /* 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. */ We need to double check this and see if it matters anymore. */
if (any_pending_cleanups (1) if (any_pending_cleanups (1))
&& target && REG_P (target) {
&& REGNO (target) < FIRST_PSEUDO_REGISTER) if (target && REG_P (target)
target = 0, sibcall_failure = 1; && REGNO (target) < FIRST_PSEUDO_REGISTER)
target = 0;
sibcall_failure = 1;
}
if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode if (TYPE_MODE (TREE_TYPE (exp)) == VOIDmode
|| ignore) || ignore)
......
...@@ -290,9 +290,6 @@ static void print_type_hash_statistics PARAMS((void)); ...@@ -290,9 +290,6 @@ static void print_type_hash_statistics PARAMS((void));
void (*lang_unsave) PARAMS ((tree *)); void (*lang_unsave) PARAMS ((tree *));
void (*lang_unsave_expr_now) 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 /* The string used as a placeholder instead of a source file name for
built-in tree nodes. The variable, which is dynamically allocated, built-in tree nodes. The variable, which is dynamically allocated,
should be used; the macro is only used to initialize it. */ should be used; the macro is only used to initialize it. */
...@@ -2675,15 +2672,28 @@ unsave_expr_now (expr) ...@@ -2675,15 +2672,28 @@ unsave_expr_now (expr)
return expr; return expr;
} }
/* Return nonzero if it is safe to unsave EXPR, else return zero. /* Return 0 if it is safe to evaluate EXPR multiple times,
It is not safe to unsave EXPR if it contains any embedded RTL_EXPRs. */ 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 int
safe_for_unsave (expr) unsafe_for_reeval (expr)
tree expr; tree expr;
{ {
enum tree_code code; enum tree_code code;
register int i; register int i, tmp, unsafeness;
int first_rtl; int first_rtl;
if (expr == NULL_TREE) if (expr == NULL_TREE)
...@@ -2691,10 +2701,13 @@ safe_for_unsave (expr) ...@@ -2691,10 +2701,13 @@ safe_for_unsave (expr)
code = TREE_CODE (expr); code = TREE_CODE (expr);
first_rtl = first_rtl_op (code); first_rtl = first_rtl_op (code);
unsafeness = 0;
switch (code) switch (code)
{ {
case SAVE_EXPR:
case RTL_EXPR: case RTL_EXPR:
return 0; return 2;
case CALL_EXPR: case CALL_EXPR:
if (TREE_OPERAND (expr, 1) if (TREE_OPERAND (expr, 1)
...@@ -2703,26 +2716,20 @@ safe_for_unsave (expr) ...@@ -2703,26 +2716,20 @@ safe_for_unsave (expr)
tree exp = TREE_OPERAND (expr, 1); tree exp = TREE_OPERAND (expr, 1);
while (exp) while (exp)
{ {
if (! safe_for_unsave (TREE_VALUE (exp))) tmp = unsafe_for_reeval (TREE_VALUE (exp));
return 0; if (tmp > 1)
return tmp;
exp = TREE_CHAIN (exp); exp = TREE_CHAIN (exp);
} }
} }
return 1;
case TARGET_EXPR:
unsafeness = 1;
break; break;
default: default:
if (lang_safe_for_unsave) /* ??? Add a lang hook if it becomes necessary. */
switch ((*lang_safe_for_unsave) (expr))
{
case -1:
break;
case 0:
return 0;
case 1:
return 1;
default:
abort ();
}
break; break;
} }
...@@ -2733,7 +2740,7 @@ safe_for_unsave (expr) ...@@ -2733,7 +2740,7 @@ safe_for_unsave (expr)
case 'x': /* something random, like an identifier or an ERROR_MARK. */ case 'x': /* something random, like an identifier or an ERROR_MARK. */
case 'd': /* A decl node */ case 'd': /* A decl node */
case 'b': /* A block node */ case 'b': /* A block node */
return 1; return 0;
case 'e': /* an expression */ case 'e': /* an expression */
case 'r': /* a reference */ case 'r': /* a reference */
...@@ -2742,12 +2749,15 @@ safe_for_unsave (expr) ...@@ -2742,12 +2749,15 @@ safe_for_unsave (expr)
case '2': /* a binary arithmetic expression */ case '2': /* a binary arithmetic expression */
case '1': /* a unary arithmetic expression */ case '1': /* a unary arithmetic expression */
for (i = first_rtl - 1; i >= 0; i--) for (i = first_rtl - 1; i >= 0; i--)
if (! safe_for_unsave (TREE_OPERAND (expr, i))) {
return 0; tmp = unsafe_for_reeval (TREE_OPERAND (expr, i));
return 1; if (tmp > unsafeness)
unsafeness = tmp;
}
return unsafeness;
default: default:
return 0; return 2;
} }
} }
......
...@@ -1963,12 +1963,10 @@ extern tree unsave_expr_now PARAMS ((tree)); ...@@ -1963,12 +1963,10 @@ extern tree unsave_expr_now PARAMS ((tree));
extern void (*lang_unsave) PARAMS ((tree *)); extern void (*lang_unsave) PARAMS ((tree *));
extern void (*lang_unsave_expr_now) PARAMS ((tree)); extern void (*lang_unsave_expr_now) PARAMS ((tree));
/* If non-null, a language specific version of safe_for_unsave. */ /* Return 0 if it is safe to evaluate EXPR multiple times,
extern int (*lang_safe_for_unsave) PARAMS ((tree)); return 1 if it is safe if EXPR is unsaved afterward, or
return 2 if it is completely unsafe. */
/* Return nonzero if it is safe to unsave EXPR, else return zero. extern int unsafe_for_reeval PARAMS ((tree));
It is not safe to unsave EXPR if it contains any embedded RTL_EXPRs. */
extern int safe_for_unsave PARAMS ((tree));
/* Return 1 if EXP contains a PLACEHOLDER_EXPR; i.e., if it represents a size /* 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. or offset that depends on a field within a record.
......
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