diff --git a/gcc/expr.c b/gcc/expr.c index 87305aa5dc9..c10ff7b90e9 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -4905,6 +4905,14 @@ expand_expr (exp, target, tmode, modifier) return SAVE_EXPR_RTL (exp); + case UNSAVE_EXPR: + { + rtx temp; + temp = expand_expr (TREE_OPERAND (exp, 0), target, tmode, modifier); + TREE_OPERAND (exp, 0) = unsave_expr_now (TREE_OPERAND (exp, 0)); + return temp; + } + case PLACEHOLDER_EXPR: /* If there is an object on the head of the placeholder list, see if some object in it's references is of type TYPE. For diff --git a/gcc/tree.c b/gcc/tree.c index 7aa6c7736b1..fc4c983568a 100644 --- a/gcc/tree.c +++ b/gcc/tree.c @@ -2156,6 +2156,91 @@ save_expr (expr) TREE_SIDE_EFFECTS (t) = 1; return t; } + +/* Arrange for an expression to be expanded multiple independent + times. This is useful for cleanup actions, as the backend can + expand them multiple times in different places. */ +tree +unsave_expr (expr) + tree expr; +{ + tree t; + + /* If this is already protected, no sense in protecting it again. */ + if (TREE_CODE (expr) == UNSAVE_EXPR) + return expr; + + t = build1 (UNSAVE_EXPR, TREE_TYPE (expr), expr); + TREE_SIDE_EFFECTS (t) = TREE_SIDE_EFFECTS (expr); + return t; +} + +/* Modify a tree in place so that all the evaluate only once things + are cleared out. Return the EXPR given. */ +tree +unsave_expr_now (expr) + tree expr; +{ + enum tree_code code; + register int i; + + if (expr == NULL_TREE) + return expr; + + code = TREE_CODE (expr); + switch (code) + { + case SAVE_EXPR: + SAVE_EXPR_RTL (expr) = NULL_RTX; + break; + + case TARGET_EXPR: + sorry ("TARGET_EXPR reused inside UNSAVE_EXPR"); + break; + + case RTL_EXPR: + warning ("RTL_EXPR reused inside UNSAVE_EXPR"); + RTL_EXPR_SEQUENCE (expr) = NULL_RTX; + break; + + case CALL_EXPR: + CALL_EXPR_RTL (expr) = NULL_RTX; + if (TREE_OPERAND (expr, 1) + && TREE_CODE (TREE_OPERAND (expr, 1)) == TREE_LIST) + { + tree exp = TREE_OPERAND (expr, 1); + while (exp) + { + unsave_expr_now (TREE_VALUE (exp)); + exp = TREE_CHAIN (exp); + } + } + break; + } + + switch (TREE_CODE_CLASS (code)) + { + case 'c': /* a constant */ + case 't': /* a type node */ + case 'x': /* something random, like an identifier or an ERROR_MARK. */ + case 'd': /* A decl node */ + case 'b': /* A block node */ + return expr; + + case 'e': /* an expression */ + case 'r': /* a reference */ + case 's': /* an expression with side effects */ + case '<': /* a comparison expression */ + case '2': /* a binary arithmetic expression */ + case '1': /* a unary arithmetic expression */ + for (i = tree_code_length[(int) code] - 1; i >= 0; i--) + unsave_expr_now (TREE_OPERAND (expr, i)); + return expr; + + default: + abort (); + } +} /* 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. diff --git a/gcc/tree.def b/gcc/tree.def index 9de2ca01d99..61f06a94a68 100644 --- a/gcc/tree.def +++ b/gcc/tree.def @@ -646,6 +646,15 @@ DEFTREECODE (NON_LVALUE_EXPR, "non_lvalue_expr", "1", 1) nonzero only after the expression has been computed. */ DEFTREECODE (SAVE_EXPR, "save_expr", "e", 3) +/* For a UNSAVE_EXPR, operand 0 is the value to unsave. By unsave, we + mean that all _EXPRs such as TARGET_EXPRs, SAVE_EXPRs, + CALL_EXPRs and RTL_EXPRs, that are protected + from being evaluated more than once should be reset so that a new + expand_expr call of this expr will cause those to be re-evaluated. + This is useful when we want to reuse a tree in different places, + but where we must re-expand. */ +DEFTREECODE (UNSAVE_EXPR, "unsave_expr", "e", 1) + /* Represents something whose RTL has already been expanded as a sequence which should be emitted when this expression is expanded. The first operand is the RTL to emit. It is the first of a chain of insns. diff --git a/gcc/tree.h b/gcc/tree.h index dea5d77e123..ab2df453a5d 100644 --- a/gcc/tree.h +++ b/gcc/tree.h @@ -1436,6 +1436,17 @@ extern int lvalue_or_else PROTO((tree, char *)); extern tree save_expr PROTO((tree)); +/* unsave_expr (EXP) returns an expression equivalent to EXP but it + can be used multiple times and will evaluate EXP, in it's entirety + each time. */ + +extern tree unsave_expr PROTO((tree)); + +/* unsave_expr_now (EXP) resets EXP in place, so that it can be + expanded again. */ + +extern tree unsave_expr_now PROTO((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.