(save_expr): Don't evaluate something containing a PLACEHOLDER_EXPR.
(contains_placeholder_p, substitute_in_{expr,type}): New functions. From-SVN: r5581
This commit is contained in:
parent
7380d70727
commit
dec20b4bae
291
gcc/tree.c
291
gcc/tree.c
|
@ -1847,6 +1847,18 @@ save_expr (expr)
|
||||||
|| TREE_CODE (t) == SAVE_EXPR)
|
|| TREE_CODE (t) == SAVE_EXPR)
|
||||||
return t;
|
return t;
|
||||||
|
|
||||||
|
/* If T contains a PLACEHOLDER_EXPR, we must evaluate it each time, since
|
||||||
|
it means that the size or offset of some field of an object depends on
|
||||||
|
the value within another field.
|
||||||
|
|
||||||
|
Note that it must not be the case that T contains both a PLACEHOLDER_EXPR
|
||||||
|
and some variable since it would then need to be both evaluated once and
|
||||||
|
evaluated more than once. Front-ends must assure this case cannot
|
||||||
|
happen by surrounding any such subexpressions in their own SAVE_EXPR
|
||||||
|
and forcing evaluation at the proper time. */
|
||||||
|
if (contains_placeholder_p (t))
|
||||||
|
return t;
|
||||||
|
|
||||||
t = build (SAVE_EXPR, TREE_TYPE (expr), t, current_function_decl, NULL_TREE);
|
t = build (SAVE_EXPR, TREE_TYPE (expr), t, current_function_decl, NULL_TREE);
|
||||||
|
|
||||||
/* This expression might be placed ahead of a jump to ensure that the
|
/* This expression might be placed ahead of a jump to ensure that the
|
||||||
|
@ -1855,7 +1867,286 @@ save_expr (expr)
|
||||||
TREE_SIDE_EFFECTS (t) = 1;
|
TREE_SIDE_EFFECTS (t) = 1;
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
|
||||||
|
Note that we only allow such expressions within simple arithmetic
|
||||||
|
or a COND_EXPR. */
|
||||||
|
|
||||||
|
int
|
||||||
|
contains_placeholder_p (exp)
|
||||||
|
tree exp;
|
||||||
|
{
|
||||||
|
register enum tree_code code = TREE_CODE (exp);
|
||||||
|
tree inner;
|
||||||
|
|
||||||
|
switch (TREE_CODE_CLASS (code))
|
||||||
|
{
|
||||||
|
case 'r':
|
||||||
|
for (inner = TREE_OPERAND (exp, 0);
|
||||||
|
TREE_CODE_CLASS (TREE_CODE (inner)) == 'r';
|
||||||
|
inner = TREE_OPERAND (inner, 0))
|
||||||
|
;
|
||||||
|
return TREE_CODE (inner) == PLACEHOLDER_EXPR;
|
||||||
|
|
||||||
|
case '1':
|
||||||
|
case '2': case '<':
|
||||||
|
case 'e':
|
||||||
|
switch (tree_code_length[(int) code])
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return contains_placeholder_p (TREE_OPERAND (exp, 0));
|
||||||
|
case 2:
|
||||||
|
return (code != RTL_EXPR
|
||||||
|
&& ! (code == SAVE_EXPR && SAVE_EXPR_RTL (exp) != 0)
|
||||||
|
&& code != WITH_RECORD_EXPR
|
||||||
|
&& (contains_placeholder_p (TREE_OPERAND (exp, 0))
|
||||||
|
|| contains_placeholder_p (TREE_OPERAND (exp, 1))));
|
||||||
|
case 3:
|
||||||
|
return (code == COND_EXPR
|
||||||
|
&& (contains_placeholder_p (TREE_OPERAND (exp, 0))
|
||||||
|
|| contains_placeholder_p (TREE_OPERAND (exp, 1))
|
||||||
|
|| contains_placeholder_p (TREE_OPERAND (exp, 2))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given a tree EXP, a FIELD_DECL F, and a replacement value R,
|
||||||
|
return a tree with all occurrences of references to F in a
|
||||||
|
PLACEHOLDER_EXPR replaced by R. Note that we assume here that EXP
|
||||||
|
contains only arithmetic expressions. */
|
||||||
|
|
||||||
|
tree
|
||||||
|
substitute_in_expr (exp, f, r)
|
||||||
|
tree exp;
|
||||||
|
tree f;
|
||||||
|
tree r;
|
||||||
|
{
|
||||||
|
enum tree_code code = TREE_CODE (exp);
|
||||||
|
tree inner;
|
||||||
|
|
||||||
|
switch (TREE_CODE_CLASS (code))
|
||||||
|
{
|
||||||
|
case 'c':
|
||||||
|
case 'd':
|
||||||
|
return exp;
|
||||||
|
|
||||||
|
case 'x':
|
||||||
|
if (code == PLACEHOLDER_EXPR)
|
||||||
|
return exp;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '<':
|
||||||
|
case 'e':
|
||||||
|
switch (tree_code_length[(int) code])
|
||||||
|
{
|
||||||
|
case 1:
|
||||||
|
return fold (build1 (code, TREE_TYPE (exp),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 0),
|
||||||
|
f, r)));
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
if (code == RTL_EXPR)
|
||||||
|
abort ();
|
||||||
|
|
||||||
|
return fold (build (code, TREE_TYPE (exp),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 0), f, r),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 1),
|
||||||
|
f, r)));
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
if (code != COND_EXPR)
|
||||||
|
abort ();
|
||||||
|
|
||||||
|
return fold (build (code, TREE_TYPE (exp),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 0), f, r),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 1), f, r),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 2),
|
||||||
|
f, r)));
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
switch (code)
|
||||||
|
{
|
||||||
|
case COMPONENT_REF:
|
||||||
|
/* If this expression is getting a value from a PLACEHOLDER_EXPR
|
||||||
|
and it is the right field, replace it with R. */
|
||||||
|
for (inner = TREE_OPERAND (exp, 0);
|
||||||
|
TREE_CODE_CLASS (TREE_CODE (inner)) == 'r';
|
||||||
|
inner = TREE_OPERAND (inner, 0))
|
||||||
|
;
|
||||||
|
if (TREE_CODE (inner) == PLACEHOLDER_EXPR
|
||||||
|
&& TREE_OPERAND (exp, 1) == f)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
return fold (build (code, TREE_TYPE (exp),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 0), f, r),
|
||||||
|
TREE_OPERAND (exp, 1)));
|
||||||
|
case BIT_FIELD_REF:
|
||||||
|
return fold (build (code, TREE_TYPE (exp),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 0), f, r),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 1), f, r),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 2), f, r)));
|
||||||
|
case INDIRECT_REF:
|
||||||
|
case BUFFER_REF:
|
||||||
|
return fold (build1 (code, TREE_TYPE (exp),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 0),
|
||||||
|
f, r)));
|
||||||
|
case OFFSET_REF:
|
||||||
|
return fold (build (code, TREE_TYPE (exp),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 0), f, r),
|
||||||
|
substitute_in_expr (TREE_OPERAND (exp, 1), f, r)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If it wasn't one of the cases we handle, give up. */
|
||||||
|
|
||||||
|
abort ();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Given a type T, a FIELD_DECL F, and a replacement value R,
|
||||||
|
return a new type with all size expressions that contain F
|
||||||
|
updated by replacing F with R. */
|
||||||
|
|
||||||
|
tree
|
||||||
|
substitute_in_type (t, f, r)
|
||||||
|
tree t, f, r;
|
||||||
|
{
|
||||||
|
switch (TREE_CODE (t))
|
||||||
|
{
|
||||||
|
case POINTER_TYPE:
|
||||||
|
case VOID_TYPE:
|
||||||
|
return t;
|
||||||
|
case INTEGER_TYPE:
|
||||||
|
case ENUMERAL_TYPE:
|
||||||
|
case BOOLEAN_TYPE:
|
||||||
|
case CHAR_TYPE:
|
||||||
|
if ((TREE_CODE (TYPE_MIN_VALUE (t)) != INTEGER_CST
|
||||||
|
&& contains_placeholder_p (TYPE_MIN_VALUE (t)))
|
||||||
|
|| (TREE_CODE (TYPE_MAX_VALUE (t)) != INTEGER_CST
|
||||||
|
&& contains_placeholder_p (TYPE_MAX_VALUE (t))))
|
||||||
|
return build_range_type (t,
|
||||||
|
substitute_in_expr (TYPE_MIN_VALUE (t), f, r),
|
||||||
|
substitute_in_expr (TYPE_MAX_VALUE (t), f, r));
|
||||||
|
return t;
|
||||||
|
|
||||||
|
case REAL_TYPE:
|
||||||
|
if ((TREE_CODE (TYPE_MIN_VALUE (t)) != INTEGER_CST
|
||||||
|
&& contains_placeholder_p (TYPE_MIN_VALUE (t)))
|
||||||
|
|| (TREE_CODE (TYPE_MAX_VALUE (t)) != INTEGER_CST
|
||||||
|
&& contains_placeholder_p (TYPE_MAX_VALUE (t))))
|
||||||
|
{
|
||||||
|
t = build_type_copy (t);
|
||||||
|
TYPE_MIN_VALUE (t) = substitute_in_expr (TYPE_MIN_VALUE (t), f, r);
|
||||||
|
TYPE_MAX_VALUE (t) = substitute_in_expr (TYPE_MAX_VALUE (t), f, r);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
|
||||||
|
case COMPLEX_TYPE:
|
||||||
|
return build_complex_type (substitute_in_type (TREE_TYPE (t), f, r));
|
||||||
|
|
||||||
|
case OFFSET_TYPE:
|
||||||
|
case METHOD_TYPE:
|
||||||
|
case REFERENCE_TYPE:
|
||||||
|
case FILE_TYPE:
|
||||||
|
case SET_TYPE:
|
||||||
|
case STRING_TYPE:
|
||||||
|
case FUNCTION_TYPE:
|
||||||
|
case LANG_TYPE:
|
||||||
|
/* Don't know how to do these yet. */
|
||||||
|
abort ();
|
||||||
|
|
||||||
|
case ARRAY_TYPE:
|
||||||
|
t = build_array_type (substitute_in_type (TREE_TYPE (t), f, r),
|
||||||
|
substitute_in_type (TYPE_DOMAIN (t), f, r));
|
||||||
|
TYPE_SIZE (t) = 0;
|
||||||
|
layout_type (t);
|
||||||
|
return t;
|
||||||
|
|
||||||
|
case RECORD_TYPE:
|
||||||
|
case UNION_TYPE:
|
||||||
|
case QUAL_UNION_TYPE:
|
||||||
|
{
|
||||||
|
tree new = copy_node (t);
|
||||||
|
tree field;
|
||||||
|
tree last_field = 0;
|
||||||
|
|
||||||
|
/* Start out with no fields, make new fields, and chain them
|
||||||
|
in. */
|
||||||
|
|
||||||
|
TYPE_FIELDS (new) = 0;
|
||||||
|
TYPE_SIZE (new) = 0;
|
||||||
|
|
||||||
|
for (field = TYPE_FIELDS (t); field;
|
||||||
|
field = TREE_CHAIN (field))
|
||||||
|
{
|
||||||
|
tree new_field = copy_node (field);
|
||||||
|
|
||||||
|
TREE_TYPE (new_field)
|
||||||
|
= substitute_in_type (TREE_TYPE (new_field), f, r);
|
||||||
|
|
||||||
|
/* If this is an anonymous field and the type of this field is
|
||||||
|
a UNION_TYPE or RECORD_TYPE with no elements, ignore it. If
|
||||||
|
the type just has one element, treat that as the field.
|
||||||
|
But don't do this if we are processing a QUAL_UNION_TYPE. */
|
||||||
|
if (TREE_CODE (t) != QUAL_UNION_TYPE && DECL_NAME (new_field) == 0
|
||||||
|
&& (TREE_CODE (TREE_TYPE (new_field)) == UNION_TYPE
|
||||||
|
|| TREE_CODE (TREE_TYPE (new_field)) == RECORD_TYPE))
|
||||||
|
{
|
||||||
|
if (TYPE_FIELDS (TREE_TYPE (new_field)) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (TREE_CHAIN (TYPE_FIELDS (TREE_TYPE (new_field))) == 0)
|
||||||
|
new_field = TYPE_FIELDS (TREE_TYPE (new_field));
|
||||||
|
}
|
||||||
|
|
||||||
|
DECL_CONTEXT (new_field) = new;
|
||||||
|
DECL_SIZE (new_field) = 0;
|
||||||
|
|
||||||
|
if (TREE_CODE (t) == QUAL_UNION_TYPE)
|
||||||
|
{
|
||||||
|
/* Do the substitution inside the qualifier and if we find
|
||||||
|
that this field will not be present, omit it. */
|
||||||
|
DECL_QUALIFIER (new_field)
|
||||||
|
= substitute_in_expr (DECL_QUALIFIER (field), f, r);
|
||||||
|
if (integer_zerop (DECL_QUALIFIER (new_field)))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last_field == 0)
|
||||||
|
TYPE_FIELDS (new) = new_field;
|
||||||
|
else
|
||||||
|
TREE_CHAIN (last_field) = new_field;
|
||||||
|
|
||||||
|
last_field = new_field;
|
||||||
|
|
||||||
|
/* If this is a qualified type and this field will always be
|
||||||
|
present, we are done. */
|
||||||
|
if (TREE_CODE (t) == QUAL_UNION_TYPE
|
||||||
|
&& integer_onep (DECL_QUALIFIER (new_field)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If this used to be a qualified union type, but we now know what
|
||||||
|
field will be present, make this a normal union. */
|
||||||
|
if (TREE_CODE (new) == QUAL_UNION_TYPE
|
||||||
|
&& (TYPE_FIELDS (new) == 0
|
||||||
|
|| integer_onep (DECL_QUALIFIER (TYPE_FIELDS (new)))))
|
||||||
|
TREE_SET_CODE (new, UNION_TYPE);
|
||||||
|
|
||||||
|
layout_type (new);
|
||||||
|
return new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Stabilize a reference so that we can use it any number of times
|
/* Stabilize a reference so that we can use it any number of times
|
||||||
without causing its operands to be evaluated more than once.
|
without causing its operands to be evaluated more than once.
|
||||||
Returns the stabilized reference.
|
Returns the stabilized reference.
|
||||||
|
|
Loading…
Reference in New Issue