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
This commit is contained in:
parent
1c33f70c96
commit
194c7c45b1
@ -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,
|
||||
|
89
gcc/calls.c
89
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)
|
||||
|
64
gcc/tree.c
64
gcc/tree.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
10
gcc/tree.h
10
gcc/tree.h
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user