Commit 26d44ae2 by Richard Henderson Committed by Richard Henderson

gimplify.c (gimplify_modify_expr_rhs): Move immediately before gimplify_modify_expr.

        * gimplify.c (gimplify_modify_expr_rhs): Move immediately before
        gimplify_modify_expr.
        (gimplify_init_constructor): Likewise.  Gimplify the null
        CONSTRUCTOR assignment.
        (gimplify_modify_expr_to_memcpy): New.
        (gimplify_modify_expr_to_memset): New.
        (gimplify_modify_expr): Use them.

From-SVN: r83888
parent 9d75385f
2004-06-29 Richard Henderson <rth@redhat.com>
* gimplify.c (gimplify_modify_expr_rhs): Move immediately before
gimplify_modify_expr.
(gimplify_init_constructor): Likewise. Gimplify the null
CONSTRUCTOR assignment.
(gimplify_modify_expr_to_memcpy): New.
(gimplify_modify_expr_to_memset): New.
(gimplify_modify_expr): Use them.
2004-06-29 Roman Zippel <zippel@linux-m68k.org> 2004-06-29 Roman Zippel <zippel@linux-m68k.org>
* web.c (union_defs): use all defs of an instruction to create a * web.c (union_defs): use all defs of an instruction to create a
......
...@@ -1323,1270 +1323,1412 @@ force_labels_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) ...@@ -1323,1270 +1323,1412 @@ force_labels_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
return NULL_TREE; return NULL_TREE;
} }
/* Break out elements of a constructor used as an initializer into separate /* *EXPR_P is a COMPONENT_REF being used as an rvalue. If its type is
MODIFY_EXPRs. different from its canonical type, wrap the whole thing inside a
NOP_EXPR and force the type of the COMPONENT_REF to be the canonical
type.
Note that we still need to clear any elements that don't have explicit The canonical type of a COMPONENT_REF is the type of the field being
initializers, so if not all elements are initialized we keep the referenced--unless the field is a bit-field which can be read directly
original MODIFY_EXPR, we just remove all of the constructor elements. */ in a smaller mode, in which case the canonical type is the
sign-appropriate type corresponding to that mode. */
static enum gimplify_status static void
gimplify_init_constructor (tree *expr_p, tree *pre_p, canonicalize_component_ref (tree *expr_p)
tree *post_p, bool want_value)
{ {
tree object = TREE_OPERAND (*expr_p, 0); tree expr = *expr_p;
tree ctor = TREE_OPERAND (*expr_p, 1); tree type;
tree type = TREE_TYPE (ctor);
enum gimplify_status ret;
tree elt_list;
if (TREE_CODE (ctor) != CONSTRUCTOR) if (TREE_CODE (expr) != COMPONENT_REF)
return GS_UNHANDLED; abort ();
elt_list = CONSTRUCTOR_ELTS (ctor); if (INTEGRAL_TYPE_P (TREE_TYPE (expr)))
type = TREE_TYPE (get_unwidened (expr, NULL_TREE));
else
type = TREE_TYPE (TREE_OPERAND (expr, 1));
ret = GS_ALL_DONE; if (TREE_TYPE (expr) != type)
switch (TREE_CODE (type))
{
case RECORD_TYPE:
case UNION_TYPE:
case QUAL_UNION_TYPE:
case ARRAY_TYPE:
{ {
HOST_WIDE_INT i, num_elements, num_nonzero_elements; tree old_type = TREE_TYPE (expr);
HOST_WIDE_INT num_nonconstant_elements;
bool cleared;
/* Aggregate types must lower constructors to initialization of /* Set the type of the COMPONENT_REF to the underlying type. */
individual elements. The exception is that a CONSTRUCTOR node TREE_TYPE (expr) = type;
with no elements indicates zero-initialization of the whole. */
if (elt_list == NULL)
{
if (want_value)
{
*expr_p = object;
return GS_OK;
}
else
return GS_UNHANDLED;
}
categorize_ctor_elements (ctor, &num_nonzero_elements, /* And wrap the whole thing inside a NOP_EXPR. */
&num_nonconstant_elements); expr = build1 (NOP_EXPR, old_type, expr);
num_elements = count_type_elements (TREE_TYPE (ctor));
/* If a const aggregate variable is being initialized, then it *expr_p = expr;
should never be a lose to promote the variable to be static. */ }
if (num_nonconstant_elements == 0 }
&& TREE_READONLY (object)
&& TREE_CODE (object) == VAR_DECL)
{
DECL_INITIAL (object) = ctor;
TREE_STATIC (object) = 1;
if (!DECL_NAME (object))
DECL_NAME (object) = create_tmp_var_name ("C");
walk_tree (&DECL_INITIAL (object), force_labels_r, NULL, NULL);
/* ??? C++ doesn't automatically append a .<number> to the /* If a NOP conversion is changing a pointer to array of foo to a pointer
assembler name, and even when it does, it looks a FE private to foo, embed that change in the ADDR_EXPR by converting
data structures to figure out what that number should be, T array[U];
which are not set for this variable. I suppose this is (T *)&array
important for local statics for inline functions, which aren't ==>
"local" in the object file sense. So in order to get a unique &array[L]
TU-local symbol, we must invoke the lhd version now. */ where L is the lower bound. For simplicity, only do this for constant
lhd_set_decl_assembler_name (object); lower bound. */
*expr_p = NULL_TREE; static void
break; canonicalize_addr_expr (tree *expr_p)
} {
tree expr = *expr_p;
tree ctype = TREE_TYPE (expr);
tree addr_expr = TREE_OPERAND (expr, 0);
tree atype = TREE_TYPE (addr_expr);
tree dctype, datype, ddatype, otype, obj_expr;
/* If there are "lots" of initialized elements, and all of them /* Both cast and addr_expr types should be pointers. */
are valid address constants, then the entire initializer can if (!POINTER_TYPE_P (ctype) || !POINTER_TYPE_P (atype))
be dropped to memory, and then memcpy'd out. */ return;
if (num_nonconstant_elements == 0)
{
HOST_WIDE_INT size = int_size_in_bytes (type);
unsigned int align;
/* ??? We can still get unbounded array types, at least /* The addr_expr type should be a pointer to an array. */
from the C++ front end. This seems wrong, but attempt datype = TREE_TYPE (atype);
to work around it for now. */ if (TREE_CODE (datype) != ARRAY_TYPE)
if (size < 0) return;
{
size = int_size_in_bytes (TREE_TYPE (object));
if (size >= 0)
TREE_TYPE (ctor) = type = TREE_TYPE (object);
}
/* Find the maximum alignment we can assume for the object. */ /* Both cast and addr_expr types should address the same object type. */
/* ??? Make use of DECL_OFFSET_ALIGN. */ dctype = TREE_TYPE (ctype);
if (DECL_P (object)) ddatype = TREE_TYPE (datype);
align = DECL_ALIGN (object); if (!lang_hooks.types_compatible_p (ddatype, dctype))
else return;
align = TYPE_ALIGN (type);
if (size > 0 && !can_move_by_pieces (size, align)) /* The addr_expr and the object type should match. */
{ obj_expr = TREE_OPERAND (addr_expr, 0);
tree new = create_tmp_var_raw (type, "C"); otype = TREE_TYPE (obj_expr);
gimple_add_tmp_var (new); if (!lang_hooks.types_compatible_p (otype, datype))
TREE_STATIC (new) = 1; return;
TREE_READONLY (new) = 1;
DECL_INITIAL (new) = ctor;
if (align > DECL_ALIGN (new))
{
DECL_ALIGN (new) = align;
DECL_USER_ALIGN (new) = 1;
}
walk_tree (&DECL_INITIAL (new), force_labels_r, NULL, NULL);
TREE_OPERAND (*expr_p, 1) = new; /* The lower bound and element sizes must be constant. */
break; if (TREE_CODE (TYPE_SIZE_UNIT (dctype)) != INTEGER_CST
} || !TYPE_DOMAIN (datype) || !TYPE_MIN_VALUE (TYPE_DOMAIN (datype))
} || TREE_CODE (TYPE_MIN_VALUE (TYPE_DOMAIN (datype))) != INTEGER_CST)
return;
/* If there are "lots" of initialized elements, even discounting /* All checks succeeded. Build a new node to merge the cast. */
those that are not address constants (and thus *must* be *expr_p = build4 (ARRAY_REF, dctype, obj_expr,
computed at runtime), then partition the constructor into TYPE_MIN_VALUE (TYPE_DOMAIN (datype)),
constant and non-constant parts. Block copy the constant TYPE_MIN_VALUE (TYPE_DOMAIN (datype)),
parts in, then generate code for the non-constant parts. */ size_binop (EXACT_DIV_EXPR, TYPE_SIZE_UNIT (dctype),
/* TODO. There's code in cp/typeck.c to do this. */ size_int (TYPE_ALIGN (dctype)
/ BITS_PER_UNIT)));
*expr_p = build1 (ADDR_EXPR, ctype, *expr_p);
}
/* If there are "lots" of zeros, then block clear the object first. */ /* *EXPR_P is a NOP_EXPR or CONVERT_EXPR. Remove it and/or other conversions
cleared = false; underneath as appropriate. */
if (num_elements - num_nonzero_elements > CLEAR_RATIO
&& num_nonzero_elements < num_elements/4)
cleared = true;
/* ??? This bit ought not be needed. For any element not present static enum gimplify_status
in the initializer, we should simply set them to zero. Except gimplify_conversion (tree *expr_p)
we'd need to *find* the elements that are not present, and that {
requires trickery to avoid quadratic compile-time behavior in /* Strip away as many useless type conversions as possible
large cases or excessive memory use in small cases. */ at the toplevel. */
else STRIP_USELESS_TYPE_CONVERSION (*expr_p);
{
HOST_WIDE_INT len = list_length (elt_list);
if (TREE_CODE (type) == ARRAY_TYPE)
{
tree nelts = array_type_nelts (type);
if (!host_integerp (nelts, 1)
|| tree_low_cst (nelts, 1) != len)
cleared = 1;;
}
else if (len != fields_length (type))
cleared = 1;
}
if (cleared) /* If we still have a conversion at the toplevel, then strip
away all but the outermost conversion. */
if (TREE_CODE (*expr_p) == NOP_EXPR || TREE_CODE (*expr_p) == CONVERT_EXPR)
{ {
CONSTRUCTOR_ELTS (ctor) = NULL_TREE; STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
append_to_statement_list (*expr_p, pre_p);
/* And remove the outermost conversion if it's useless. */
if (tree_ssa_useless_type_conversion (*expr_p))
*expr_p = TREE_OPERAND (*expr_p, 0);
} }
for (i = 0; elt_list; i++, elt_list = TREE_CHAIN (elt_list)) /* If we still have a conversion at the toplevel,
then canonicalize some constructs. */
if (TREE_CODE (*expr_p) == NOP_EXPR || TREE_CODE (*expr_p) == CONVERT_EXPR)
{ {
tree purpose, value, cref, init; tree sub = TREE_OPERAND (*expr_p, 0);
purpose = TREE_PURPOSE (elt_list); /* If a NOP conversion is changing the type of a COMPONENT_REF
value = TREE_VALUE (elt_list); expression, then canonicalize its type now in order to expose more
redundant conversions. */
if (TREE_CODE (sub) == COMPONENT_REF)
canonicalize_component_ref (&TREE_OPERAND (*expr_p, 0));
if (cleared && initializer_zerop (value)) /* If a NOP conversion is changing a pointer to array of foo
continue; to a pointer to foo, embed that change in the ADDR_EXPR. */
else if (TREE_CODE (sub) == ADDR_EXPR)
canonicalize_addr_expr (expr_p);
}
if (TREE_CODE (type) == ARRAY_TYPE) return GS_OK;
{ }
tree t = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (object)));
/* ??? Here's to hoping the front end fills in all of the /* Reduce MIN/MAX_EXPR to a COND_EXPR for further gimplification. */
indicies, so we don't have to figure out what's missing
ourselves. */
if (!purpose)
abort ();
/* ??? Need to handle this. */
if (TREE_CODE (purpose) == RANGE_EXPR)
abort ();
cref = build (ARRAY_REF, t, object, purpose, NULL_TREE, NULL_TREE); static enum gimplify_status
} gimplify_minimax_expr (tree *expr_p, tree *pre_p, tree *post_p)
{
tree op1 = TREE_OPERAND (*expr_p, 0);
tree op2 = TREE_OPERAND (*expr_p, 1);
enum tree_code code;
enum gimplify_status r0, r1;
if (TREE_CODE (*expr_p) == MIN_EXPR)
code = LE_EXPR;
else else
cref = build (COMPONENT_REF, TREE_TYPE (purpose), object, code = GE_EXPR;
purpose, NULL_TREE);
init = build (MODIFY_EXPR, TREE_TYPE (purpose), cref, value); r0 = gimplify_expr (&op1, pre_p, post_p, is_gimple_val, fb_rvalue);
r1 = gimplify_expr (&op2, pre_p, post_p, is_gimple_val, fb_rvalue);
/* Each member initialization is a full-expression. */ *expr_p = build (COND_EXPR, TREE_TYPE (*expr_p),
gimplify_and_add (init, pre_p); build (code, boolean_type_node, op1, op2),
} op1, op2);
*expr_p = NULL_TREE; if (r0 == GS_ERROR || r1 == GS_ERROR)
} return GS_ERROR;
break; else
return GS_OK;
}
case COMPLEX_TYPE: /* Subroutine of gimplify_compound_lval.
{ Converts an ARRAY_REF to the equivalent *(&array + offset) form. */
tree r, i;
/* Extract the real and imaginary parts out of the ctor. */ static enum gimplify_status
r = i = NULL_TREE; gimplify_array_ref_to_plus (tree *expr_p, tree *pre_p, tree *post_p)
if (elt_list) {
{ tree array = TREE_OPERAND (*expr_p, 0);
r = TREE_VALUE (elt_list); tree arrtype = TREE_TYPE (array);
elt_list = TREE_CHAIN (elt_list); tree elttype = TREE_TYPE (arrtype);
if (elt_list) tree size = array_ref_element_size (*expr_p);
{ tree ptrtype = build_pointer_type (elttype);
i = TREE_VALUE (elt_list); enum tree_code add_code = PLUS_EXPR;
if (TREE_CHAIN (elt_list)) tree idx = TREE_OPERAND (*expr_p, 1);
abort (); tree minidx = unshare_expr (array_ref_low_bound (*expr_p));
} tree offset, addr, result;
} enum gimplify_status ret;
if (r == NULL || i == NULL)
{
tree zero = convert (TREE_TYPE (type), integer_zero_node);
if (r == NULL)
r = zero;
if (i == NULL)
i = zero;
}
/* Complex types have either COMPLEX_CST or COMPLEX_EXPR to /* If the array domain does not start at zero, apply the offset. */
represent creation of a complex value. */ if (!integer_zerop (minidx))
if (TREE_CONSTANT (r) && TREE_CONSTANT (i))
{
ctor = build_complex (type, r, i);
TREE_OPERAND (*expr_p, 1) = ctor;
}
else
{ {
ctor = build (COMPLEX_EXPR, type, r, i); idx = convert (TREE_TYPE (minidx), idx);
TREE_OPERAND (*expr_p, 1) = ctor; idx = fold (build (MINUS_EXPR, TREE_TYPE (minidx), idx, minidx));
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
is_gimple_rhs, fb_rvalue);
}
} }
break;
case VECTOR_TYPE: /* If the index is negative -- a technically invalid situation now
/* Go ahead and simplify constant constructors to VECTOR_CST. */ that we've biased the index back to zero -- then casting it to
if (TREE_CONSTANT (ctor)) unsigned has ill effects. In particular, -1*4U/4U != -1.
TREE_OPERAND (*expr_p, 1) = build_vector (type, elt_list); Represent this as a subtraction of a positive rather than addition
else of a negative. This will prevent any conversion back to ARRAY_REF
{ from getting the wrong results from the division. */
/* Vector types use CONSTRUCTOR all the way through gimple if (TREE_CODE (idx) == INTEGER_CST && tree_int_cst_sgn (idx) < 0)
compilation as a general initializer. */
for (; elt_list; elt_list = TREE_CHAIN (elt_list))
{ {
enum gimplify_status tret; idx = fold (build1 (NEGATE_EXPR, TREE_TYPE (idx), idx));
tret = gimplify_expr (&TREE_VALUE (elt_list), pre_p, post_p, add_code = MINUS_EXPR;
is_gimple_constructor_elt, fb_rvalue);
if (tret == GS_ERROR)
ret = GS_ERROR;
}
} }
break;
default: /* Pointer arithmetic must be done in sizetype. */
/* So how did we get a CONSTRUCTOR for a scalar type? */ idx = fold_convert (sizetype, idx);
abort ();
}
/* Convert the index to a byte offset. */
offset = size_binop (MULT_EXPR, size, idx);
ret = gimplify_expr (&array, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
if (ret == GS_ERROR) if (ret == GS_ERROR)
return GS_ERROR; return ret;
else if (want_value)
{ addr = build_fold_addr_expr_with_type (array, ptrtype);
append_to_statement_list (*expr_p, pre_p); result = fold (build (add_code, ptrtype, addr, offset));
*expr_p = object; *expr_p = build1 (INDIRECT_REF, elttype, result);
return GS_OK; return GS_OK;
}
else
return GS_ALL_DONE;
} }
/* *EXPR_P is a COMPONENT_REF being used as an rvalue. If its type is /* Gimplify the COMPONENT_REF, ARRAY_REF, REALPART_EXPR or IMAGPART_EXPR
different from its canonical type, wrap the whole thing inside a node pointed by EXPR_P.
NOP_EXPR and force the type of the COMPONENT_REF to be the canonical
type.
The canonical type of a COMPONENT_REF is the type of the field being compound_lval
referenced--unless the field is a bit-field which can be read directly : min_lval '[' val ']'
in a smaller mode, in which case the canonical type is the | min_lval '.' ID
sign-appropriate type corresponding to that mode. */ | compound_lval '[' val ']'
| compound_lval '.' ID
static void This is not part of the original SIMPLE definition, which separates
canonicalize_component_ref (tree *expr_p) array and member references, but it seems reasonable to handle them
{ together. Also, this way we don't run into problems with union
tree expr = *expr_p; aliasing; gcc requires that for accesses through a union to alias, the
tree type; union reference must be explicit, which was not always the case when we
were splitting up array and member refs.
if (TREE_CODE (expr) != COMPONENT_REF) PRE_P points to the list where side effects that must happen before
abort (); *EXPR_P should be stored.
if (INTEGRAL_TYPE_P (TREE_TYPE (expr))) POST_P points to the list where side effects that must happen after
type = TREE_TYPE (get_unwidened (expr, NULL_TREE)); *EXPR_P should be stored. */
else
type = TREE_TYPE (TREE_OPERAND (expr, 1));
if (TREE_TYPE (expr) != type) static enum gimplify_status
{ gimplify_compound_lval (tree *expr_p, tree *pre_p,
tree old_type = TREE_TYPE (expr); tree *post_p, bool want_lvalue)
{
tree *p;
varray_type stack;
enum gimplify_status ret = GS_OK, tret;
int i;
/* Set the type of the COMPONENT_REF to the underlying type. */ #if defined ENABLE_CHECKING
TREE_TYPE (expr) = type; if (TREE_CODE (*expr_p) != ARRAY_REF
&& TREE_CODE (*expr_p) != ARRAY_RANGE_REF
&& TREE_CODE (*expr_p) != COMPONENT_REF
&& TREE_CODE (*expr_p) != BIT_FIELD_REF
&& TREE_CODE (*expr_p) != REALPART_EXPR
&& TREE_CODE (*expr_p) != IMAGPART_EXPR)
abort ();
#endif
/* And wrap the whole thing inside a NOP_EXPR. */ /* Create a stack of the subexpressions so later we can walk them in
expr = build1 (NOP_EXPR, old_type, expr); order from inner to outer. */
VARRAY_TREE_INIT (stack, 10, "stack");
*expr_p = expr; /* We can either handle REALPART_EXPR, IMAGEPART_EXPR anything that
} handled_components can deal with. */
} for (p = expr_p;
(handled_component_p (*p)
|| TREE_CODE (*p) == REALPART_EXPR || TREE_CODE (*p) == IMAGPART_EXPR);
p = &TREE_OPERAND (*p, 0))
VARRAY_PUSH_TREE (stack, *p);
/* If a NOP conversion is changing a pointer to array of foo to a pointer /* Now STACK is a stack of pointers to all the refs we've walked through
to foo, embed that change in the ADDR_EXPR by converting and P points to the innermost expression.
T array[U];
(T *)&array
==>
&array[L]
where L is the lower bound. For simplicity, only do this for constant
lower bound. */
static void Java requires that we elaborated nodes in source order. That
canonicalize_addr_expr (tree *expr_p) means we must gimplify the inner expression followed by each of
{ the indices, in order. But we can't gimplify the inner
tree expr = *expr_p; expression until we deal with any variable bounds, sizes, or
tree ctype = TREE_TYPE (expr); positions in order to deal with PLACEHOLDER_EXPRs.
tree addr_expr = TREE_OPERAND (expr, 0);
tree atype = TREE_TYPE (addr_expr);
tree dctype, datype, ddatype, otype, obj_expr;
/* Both cast and addr_expr types should be pointers. */ So we do this in three steps. First we deal with the annotations
if (!POINTER_TYPE_P (ctype) || !POINTER_TYPE_P (atype)) for any variables in the components, then we gimplify the base,
return; then we gimplify any indices, from left to right. */
for (i = VARRAY_ACTIVE_SIZE (stack) - 1; i >= 0; i--)
{
tree t = VARRAY_TREE (stack, i);
/* The addr_expr type should be a pointer to an array. */ if (TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
datype = TREE_TYPE (atype); {
if (TREE_CODE (datype) != ARRAY_TYPE) /* Gimplify the low bound and element type size and put them into
return; the ARRAY_REF. If these values are set, they have already been
gimplified. */
if (!TREE_OPERAND (t, 2))
{
tree low = unshare_expr (array_ref_low_bound (t));
if (!is_gimple_min_invariant (low))
{
TREE_OPERAND (t, 2) = low;
tret = gimplify_expr (&TREE_OPERAND (t, 2), pre_p, post_p,
is_gimple_tmp_var, fb_rvalue);
ret = MIN (ret, tret);
}
}
/* Both cast and addr_expr types should address the same object type. */ if (!TREE_OPERAND (t, 3))
dctype = TREE_TYPE (ctype); {
ddatype = TREE_TYPE (datype); tree elmt_type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (t, 0)));
if (!lang_hooks.types_compatible_p (ddatype, dctype)) tree elmt_size = unshare_expr (array_ref_element_size (t));
return; tree factor = size_int (TYPE_ALIGN (elmt_type) / BITS_PER_UNIT);
/* The addr_expr and the object type should match. */ /* Divide the element size by the alignment of the element
obj_expr = TREE_OPERAND (addr_expr, 0); type (above). */
otype = TREE_TYPE (obj_expr); elmt_size = size_binop (EXACT_DIV_EXPR, elmt_size, factor);
if (!lang_hooks.types_compatible_p (otype, datype))
return;
/* The lower bound and element sizes must be constant. */ if (!is_gimple_min_invariant (elmt_size))
if (TREE_CODE (TYPE_SIZE_UNIT (dctype)) != INTEGER_CST {
|| !TYPE_DOMAIN (datype) || !TYPE_MIN_VALUE (TYPE_DOMAIN (datype)) TREE_OPERAND (t, 3) = elmt_size;
|| TREE_CODE (TYPE_MIN_VALUE (TYPE_DOMAIN (datype))) != INTEGER_CST) tret = gimplify_expr (&TREE_OPERAND (t, 3), pre_p, post_p,
return; is_gimple_tmp_var, fb_rvalue);
ret = MIN (ret, tret);
}
}
}
else if (TREE_CODE (t) == COMPONENT_REF)
{
/* Set the field offset into T and gimplify it. */
if (!TREE_OPERAND (t, 2))
{
tree offset = unshare_expr (component_ref_field_offset (t));
tree field = TREE_OPERAND (t, 1);
tree factor
= size_int (DECL_OFFSET_ALIGN (field) / BITS_PER_UNIT);
/* All checks succeeded. Build a new node to merge the cast. */ /* Divide the offset by its alignment. */
*expr_p = build4 (ARRAY_REF, dctype, obj_expr, offset = size_binop (EXACT_DIV_EXPR, offset, factor);
TYPE_MIN_VALUE (TYPE_DOMAIN (datype)),
TYPE_MIN_VALUE (TYPE_DOMAIN (datype)),
size_binop (EXACT_DIV_EXPR, TYPE_SIZE_UNIT (dctype),
size_int (TYPE_ALIGN (dctype)
/ BITS_PER_UNIT)));
*expr_p = build1 (ADDR_EXPR, ctype, *expr_p);
}
/* *EXPR_P is a NOP_EXPR or CONVERT_EXPR. Remove it and/or other conversions if (!is_gimple_min_invariant (offset))
underneath as appropriate. */ {
TREE_OPERAND (t, 2) = offset;
tret = gimplify_expr (&TREE_OPERAND (t, 2), pre_p, post_p,
is_gimple_tmp_var, fb_rvalue);
ret = MIN (ret, tret);
}
}
}
}
static enum gimplify_status /* Step 2 is to gimplify the base expression. */
gimplify_conversion (tree *expr_p) tret = gimplify_expr (p, pre_p, post_p, is_gimple_min_lval,
{ want_lvalue ? fb_lvalue : fb_rvalue);
/* Strip away as many useless type conversions as possible ret = MIN (ret, tret);
at the toplevel. */
STRIP_USELESS_TYPE_CONVERSION (*expr_p);
/* If we still have a conversion at the toplevel, then strip /* And finally, the indices and operands to BIT_FIELD_REF. */
away all but the outermost conversion. */ for (; VARRAY_ACTIVE_SIZE (stack) > 0; )
if (TREE_CODE (*expr_p) == NOP_EXPR || TREE_CODE (*expr_p) == CONVERT_EXPR) {
tree t = VARRAY_TOP_TREE (stack);
if (TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF)
{
/* Gimplify the dimension.
Temporary fix for gcc.c-torture/execute/20040313-1.c.
Gimplify non-constant array indices into a temporary
variable.
FIXME - The real fix is to gimplify post-modify
expressions into a minimal gimple lvalue. However, that
exposes bugs in alias analysis. The alias analyzer does
not handle &PTR->FIELD very well. Will fix after the
branch is merged into mainline (dnovillo 2004-05-03). */
if (!is_gimple_min_invariant (TREE_OPERAND (t, 1)))
{
tret = gimplify_expr (&TREE_OPERAND (t, 1), pre_p, post_p,
is_gimple_tmp_var, fb_rvalue);
ret = MIN (ret, tret);
}
}
else if (TREE_CODE (t) == BIT_FIELD_REF)
{ {
STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0)); tret = gimplify_expr (&TREE_OPERAND (t, 1), pre_p, post_p,
is_gimple_val, fb_rvalue);
/* And remove the outermost conversion if it's useless. */ ret = MIN (ret, tret);
if (tree_ssa_useless_type_conversion (*expr_p)) tret = gimplify_expr (&TREE_OPERAND (t, 2), pre_p, post_p,
*expr_p = TREE_OPERAND (*expr_p, 0); is_gimple_val, fb_rvalue);
ret = MIN (ret, tret);
} }
/* If we still have a conversion at the toplevel, /* The innermost expression P may have originally had TREE_SIDE_EFFECTS
then canonicalize some constructs. */ set which would have caused all the outer expressions in EXPR_P
if (TREE_CODE (*expr_p) == NOP_EXPR || TREE_CODE (*expr_p) == CONVERT_EXPR) leading to P to also have had TREE_SIDE_EFFECTS set. */
{ recalculate_side_effects (t);
tree sub = TREE_OPERAND (*expr_p, 0); VARRAY_POP (stack);
}
/* If a NOP conversion is changing the type of a COMPONENT_REF tret = gimplify_expr (p, pre_p, post_p, is_gimple_min_lval,
expression, then canonicalize its type now in order to expose more want_lvalue ? fb_lvalue : fb_rvalue);
redundant conversions. */ ret = MIN (ret, tret);
if (TREE_CODE (sub) == COMPONENT_REF)
canonicalize_component_ref (&TREE_OPERAND (*expr_p, 0));
/* If a NOP conversion is changing a pointer to array of foo /* If the outermost expression is a COMPONENT_REF, canonicalize its type. */
to a pointer to foo, embed that change in the ADDR_EXPR. */ if (!want_lvalue && TREE_CODE (*expr_p) == COMPONENT_REF)
else if (TREE_CODE (sub) == ADDR_EXPR) {
canonicalize_addr_expr (expr_p); canonicalize_component_ref (expr_p);
ret = MIN (ret, GS_OK);
} }
return GS_OK; return ret;
} }
/* Reduce MIN/MAX_EXPR to a COND_EXPR for further gimplification. */ /* Gimplify the self modifying expression pointed by EXPR_P (++, --, +=, -=).
PRE_P points to the list where side effects that must happen before
*EXPR_P should be stored.
POST_P points to the list where side effects that must happen after
*EXPR_P should be stored.
WANT_VALUE is nonzero iff we want to use the value of this expression
in another expression. */
static enum gimplify_status static enum gimplify_status
gimplify_minimax_expr (tree *expr_p, tree *pre_p, tree *post_p) gimplify_self_mod_expr (tree *expr_p, tree *pre_p, tree *post_p,
bool want_value)
{ {
tree op1 = TREE_OPERAND (*expr_p, 0);
tree op2 = TREE_OPERAND (*expr_p, 1);
enum tree_code code; enum tree_code code;
enum gimplify_status r0, r1; tree lhs, lvalue, rhs, t1;
bool postfix;
enum tree_code arith_code;
enum gimplify_status ret;
if (TREE_CODE (*expr_p) == MIN_EXPR) code = TREE_CODE (*expr_p);
code = LE_EXPR;
#if defined ENABLE_CHECKING
if (code != POSTINCREMENT_EXPR
&& code != POSTDECREMENT_EXPR
&& code != PREINCREMENT_EXPR
&& code != PREDECREMENT_EXPR)
abort ();
#endif
/* Prefix or postfix? */
if (code == POSTINCREMENT_EXPR || code == POSTDECREMENT_EXPR)
/* Faster to treat as prefix if result is not used. */
postfix = want_value;
else else
code = GE_EXPR; postfix = false;
r0 = gimplify_expr (&op1, pre_p, post_p, is_gimple_val, fb_rvalue); /* Add or subtract? */
r1 = gimplify_expr (&op2, pre_p, post_p, is_gimple_val, fb_rvalue); if (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR)
arith_code = PLUS_EXPR;
else
arith_code = MINUS_EXPR;
*expr_p = build (COND_EXPR, TREE_TYPE (*expr_p), /* Gimplify the LHS into a GIMPLE lvalue. */
build (code, boolean_type_node, op1, op2), lvalue = TREE_OPERAND (*expr_p, 0);
op1, op2); ret = gimplify_expr (&lvalue, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
if (ret == GS_ERROR)
return ret;
if (r0 == GS_ERROR || r1 == GS_ERROR) /* Extract the operands to the arithmetic operation. */
return GS_ERROR; lhs = lvalue;
rhs = TREE_OPERAND (*expr_p, 1);
/* For postfix operator, we evaluate the LHS to an rvalue and then use
that as the result value and in the postqueue operation. */
if (postfix)
{
ret = gimplify_expr (&lhs, pre_p, post_p, is_gimple_val, fb_rvalue);
if (ret == GS_ERROR)
return ret;
}
t1 = build (arith_code, TREE_TYPE (*expr_p), lhs, rhs);
t1 = build (MODIFY_EXPR, TREE_TYPE (lvalue), lvalue, t1);
if (postfix)
{
gimplify_and_add (t1, post_p);
*expr_p = lhs;
return GS_ALL_DONE;
}
else else
{
*expr_p = t1;
return GS_OK; return GS_OK;
}
} }
/* Subroutine of gimplify_compound_lval. /* Gimplify the CALL_EXPR node pointed by EXPR_P.
Converts an ARRAY_REF to the equivalent *(&array + offset) form. */
call_expr
: ID '(' arglist ')'
arglist
: arglist ',' val
| val
PRE_P points to the list where side effects that must happen before
*EXPR_P should be stored. */
static enum gimplify_status static enum gimplify_status
gimplify_array_ref_to_plus (tree *expr_p, tree *pre_p, tree *post_p) gimplify_call_expr (tree *expr_p, tree *pre_p, bool (*gimple_test_f) (tree))
{ {
tree array = TREE_OPERAND (*expr_p, 0); tree decl;
tree arrtype = TREE_TYPE (array); tree arglist;
tree elttype = TREE_TYPE (arrtype);
tree size = array_ref_element_size (*expr_p);
tree ptrtype = build_pointer_type (elttype);
enum tree_code add_code = PLUS_EXPR;
tree idx = TREE_OPERAND (*expr_p, 1);
tree minidx = unshare_expr (array_ref_low_bound (*expr_p));
tree offset, addr, result;
enum gimplify_status ret; enum gimplify_status ret;
/* If the array domain does not start at zero, apply the offset. */ #if defined ENABLE_CHECKING
if (!integer_zerop (minidx)) if (TREE_CODE (*expr_p) != CALL_EXPR)
abort ();
#endif
/* For reliable diagnostics during inlining, it is necessary that
every call_expr be annotated with file and line. */
if (!EXPR_LOCUS (*expr_p))
annotate_with_locus (*expr_p, input_location);
/* This may be a call to a builtin function.
Builtin function calls may be transformed into different
(and more efficient) builtin function calls under certain
circumstances. Unfortunately, gimplification can muck things
up enough that the builtin expanders are not aware that certain
transformations are still valid.
So we attempt transformation/gimplification of the call before
we gimplify the CALL_EXPR. At this time we do not manage to
transform all calls in the same manner as the expanders do, but
we do transform most of them. */
decl = get_callee_fndecl (*expr_p);
if (decl && DECL_BUILT_IN (decl))
{ {
idx = convert (TREE_TYPE (minidx), idx); tree new;
idx = fold (build (MINUS_EXPR, TREE_TYPE (minidx), idx, minidx));
/* If it is allocation of stack, record the need to restore the memory
when the enclosing bind_expr is exited. */
if (DECL_FUNCTION_CODE (decl) == BUILT_IN_STACK_ALLOC)
gimplify_ctxp->save_stack = true;
/* If it is restore of the stack, reset it, since it means we are
regimplifying the bind_expr. Note that we use the fact that
for try_finally_expr, try part is processed first. */
if (DECL_FUNCTION_CODE (decl) == BUILT_IN_STACK_RESTORE)
gimplify_ctxp->save_stack = false;
new = simplify_builtin (*expr_p, gimple_test_f == is_gimple_stmt);
if (new && new != *expr_p)
{
/* There was a transformation of this call which computes the
same value, but in a more efficient way. Return and try
again. */
*expr_p = new;
return GS_OK;
}
} }
/* If the index is negative -- a technically invalid situation now /* There is a sequence point before the call, so any side effects in
that we've biased the index back to zero -- then casting it to the calling expression must occur before the actual call. Force
unsigned has ill effects. In particular, -1*4U/4U != -1. gimplify_expr to use an internal post queue. */
Represent this as a subtraction of a positive rather than addition ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, NULL,
of a negative. This will prevent any conversion back to ARRAY_REF is_gimple_call_addr, fb_rvalue);
from getting the wrong results from the division. */
if (TREE_CODE (idx) == INTEGER_CST && tree_int_cst_sgn (idx) < 0) if (PUSH_ARGS_REVERSED)
TREE_OPERAND (*expr_p, 1) = nreverse (TREE_OPERAND (*expr_p, 1));
for (arglist = TREE_OPERAND (*expr_p, 1); arglist;
arglist = TREE_CHAIN (arglist))
{ {
idx = fold (build1 (NEGATE_EXPR, TREE_TYPE (idx), idx)); enum gimplify_status t;
add_code = MINUS_EXPR;
/* There is a sequence point before a function call. Side effects in
the argument list must occur before the actual call. So, when
gimplifying arguments, force gimplify_expr to use an internal
post queue which is then appended to the end of PRE_P. */
t = gimplify_expr (&TREE_VALUE (arglist), pre_p, NULL, is_gimple_val,
fb_rvalue);
if (t == GS_ERROR)
ret = GS_ERROR;
} }
if (PUSH_ARGS_REVERSED)
TREE_OPERAND (*expr_p, 1) = nreverse (TREE_OPERAND (*expr_p, 1));
/* Pointer arithmetic must be done in sizetype. */ /* Try this again in case gimplification exposed something. */
idx = fold_convert (sizetype, idx); if (ret != GS_ERROR && decl && DECL_BUILT_IN (decl))
{
tree new = simplify_builtin (*expr_p, gimple_test_f == is_gimple_stmt);
/* Convert the index to a byte offset. */ if (new && new != *expr_p)
offset = size_binop (MULT_EXPR, size, idx); {
/* There was a transformation of this call which computes the
same value, but in a more efficient way. Return and try
again. */
*expr_p = new;
return GS_OK;
}
}
/* If the function is "const" or "pure", then clear TREE_SIDE_EFFECTS on its
decl. This allows us to eliminate redundant or useless
calls to "const" functions. */
if (TREE_CODE (*expr_p) == CALL_EXPR
&& (call_expr_flags (*expr_p) & (ECF_CONST | ECF_PURE)))
TREE_SIDE_EFFECTS (*expr_p) = 0;
ret = gimplify_expr (&array, pre_p, post_p, is_gimple_min_lval, fb_lvalue);
if (ret == GS_ERROR)
return ret; return ret;
addr = build_fold_addr_expr_with_type (array, ptrtype);
result = fold (build (add_code, ptrtype, addr, offset));
*expr_p = build1 (INDIRECT_REF, elttype, result);
return GS_OK;
} }
/* Gimplify the COMPONENT_REF, ARRAY_REF, REALPART_EXPR or IMAGPART_EXPR /* Handle shortcut semantics in the predicate operand of a COND_EXPR by
node pointed by EXPR_P. rewriting it into multiple COND_EXPRs, and possibly GOTO_EXPRs.
compound_lval
: min_lval '[' val ']'
| min_lval '.' ID
| compound_lval '[' val ']'
| compound_lval '.' ID
This is not part of the original SIMPLE definition, which separates TRUE_LABEL_P and FALSE_LABEL_P point to the labels to jump to if the
array and member references, but it seems reasonable to handle them condition is true or false, respectively. If null, we should generate
together. Also, this way we don't run into problems with union our own to skip over the evaluation of this specific expression.
aliasing; gcc requires that for accesses through a union to alias, the
union reference must be explicit, which was not always the case when we
were splitting up array and member refs.
PRE_P points to the list where side effects that must happen before This function is the tree equivalent of do_jump.
*EXPR_P should be stored.
POST_P points to the list where side effects that must happen after shortcut_cond_r should only be called by shortcut_cond_expr. */
*EXPR_P should be stored. */
static enum gimplify_status static tree
gimplify_compound_lval (tree *expr_p, tree *pre_p, shortcut_cond_r (tree pred, tree *true_label_p, tree *false_label_p)
tree *post_p, bool want_lvalue)
{ {
tree *p; tree local_label = NULL_TREE;
varray_type stack; tree t, expr = NULL;
enum gimplify_status ret = GS_OK, tret;
int i;
#if defined ENABLE_CHECKING
if (TREE_CODE (*expr_p) != ARRAY_REF
&& TREE_CODE (*expr_p) != ARRAY_RANGE_REF
&& TREE_CODE (*expr_p) != COMPONENT_REF
&& TREE_CODE (*expr_p) != BIT_FIELD_REF
&& TREE_CODE (*expr_p) != REALPART_EXPR
&& TREE_CODE (*expr_p) != IMAGPART_EXPR)
abort ();
#endif
/* Create a stack of the subexpressions so later we can walk them in /* OK, it's not a simple case; we need to pull apart the COND_EXPR to
order from inner to outer. */ retain the shortcut semantics. Just insert the gotos here;
VARRAY_TREE_INIT (stack, 10, "stack"); shortcut_cond_expr will append the real blocks later. */
if (TREE_CODE (pred) == TRUTH_ANDIF_EXPR)
{
/* Turn if (a && b) into
/* We can either handle REALPART_EXPR, IMAGEPART_EXPR anything that if (a); else goto no;
handled_components can deal with. */ if (b) goto yes; else goto no;
for (p = expr_p; (no:) */
(handled_component_p (*p)
|| TREE_CODE (*p) == REALPART_EXPR || TREE_CODE (*p) == IMAGPART_EXPR);
p = &TREE_OPERAND (*p, 0))
VARRAY_PUSH_TREE (stack, *p);
/* Now STACK is a stack of pointers to all the refs we've walked through if (false_label_p == NULL)
and P points to the innermost expression. false_label_p = &local_label;
Java requires that we elaborated nodes in source order. That t = shortcut_cond_r (TREE_OPERAND (pred, 0), NULL, false_label_p);
means we must gimplify the inner expression followed by each of append_to_statement_list (t, &expr);
the indices, in order. But we can't gimplify the inner
expression until we deal with any variable bounds, sizes, or
positions in order to deal with PLACEHOLDER_EXPRs.
So we do this in three steps. First we deal with the annotations t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p,
for any variables in the components, then we gimplify the base, false_label_p);
then we gimplify any indices, from left to right. */ append_to_statement_list (t, &expr);
for (i = VARRAY_ACTIVE_SIZE (stack) - 1; i >= 0; i--) }
else if (TREE_CODE (pred) == TRUTH_ORIF_EXPR)
{ {
tree t = VARRAY_TREE (stack, i); /* Turn if (a || b) into
if (TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF) if (a) goto yes;
{ if (b) goto yes; else goto no;
/* Gimplify the low bound and element type size and put them into (yes:) */
the ARRAY_REF. If these values are set, they have already been
gimplified. */
if (!TREE_OPERAND (t, 2))
{
tree low = unshare_expr (array_ref_low_bound (t));
if (!is_gimple_min_invariant (low))
{
TREE_OPERAND (t, 2) = low;
tret = gimplify_expr (&TREE_OPERAND (t, 2), pre_p, post_p,
is_gimple_tmp_var, fb_rvalue);
ret = MIN (ret, tret);
}
}
if (!TREE_OPERAND (t, 3)) if (true_label_p == NULL)
{ true_label_p = &local_label;
tree elmt_type = TREE_TYPE (TREE_TYPE (TREE_OPERAND (t, 0)));
tree elmt_size = unshare_expr (array_ref_element_size (t));
tree factor = size_int (TYPE_ALIGN (elmt_type) / BITS_PER_UNIT);
/* Divide the element size by the alignment of the element t = shortcut_cond_r (TREE_OPERAND (pred, 0), true_label_p, NULL);
type (above). */ append_to_statement_list (t, &expr);
elmt_size = size_binop (EXACT_DIV_EXPR, elmt_size, factor);
if (!is_gimple_min_invariant (elmt_size)) t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p,
{ false_label_p);
TREE_OPERAND (t, 3) = elmt_size; append_to_statement_list (t, &expr);
tret = gimplify_expr (&TREE_OPERAND (t, 3), pre_p, post_p,
is_gimple_tmp_var, fb_rvalue);
ret = MIN (ret, tret);
}
}
} }
else if (TREE_CODE (t) == COMPONENT_REF) else if (TREE_CODE (pred) == COND_EXPR)
{ {
/* Set the field offset into T and gimplify it. */ /* As long as we're messing with gotos, turn if (a ? b : c) into
if (!TREE_OPERAND (t, 2)) if (a)
if (b) goto yes; else goto no;
else
if (c) goto yes; else goto no; */
expr = build (COND_EXPR, void_type_node, TREE_OPERAND (pred, 0),
shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p,
false_label_p),
shortcut_cond_r (TREE_OPERAND (pred, 2), true_label_p,
false_label_p));
}
else
{ {
tree offset = unshare_expr (component_ref_field_offset (t)); expr = build (COND_EXPR, void_type_node, pred,
tree field = TREE_OPERAND (t, 1); build_and_jump (true_label_p),
tree factor build_and_jump (false_label_p));
= size_int (DECL_OFFSET_ALIGN (field) / BITS_PER_UNIT); }
/* Divide the offset by its alignment. */
offset = size_binop (EXACT_DIV_EXPR, offset, factor);
if (!is_gimple_min_invariant (offset)) if (local_label)
{ {
TREE_OPERAND (t, 2) = offset; t = build1 (LABEL_EXPR, void_type_node, local_label);
tret = gimplify_expr (&TREE_OPERAND (t, 2), pre_p, post_p, append_to_statement_list (t, &expr);
is_gimple_tmp_var, fb_rvalue);
ret = MIN (ret, tret);
}
}
}
} }
/* Step 2 is to gimplify the base expression. */ return expr;
tret = gimplify_expr (p, pre_p, post_p, is_gimple_min_lval, }
want_lvalue ? fb_lvalue : fb_rvalue);
ret = MIN (ret, tret);
/* And finally, the indices and operands to BIT_FIELD_REF. */ static tree
for (; VARRAY_ACTIVE_SIZE (stack) > 0; ) shortcut_cond_expr (tree expr)
{ {
tree t = VARRAY_TOP_TREE (stack); tree pred = TREE_OPERAND (expr, 0);
tree then_ = TREE_OPERAND (expr, 1);
tree else_ = TREE_OPERAND (expr, 2);
tree true_label, false_label, end_label, t;
tree *true_label_p;
tree *false_label_p;
bool emit_end, emit_false;
bool then_se = then_ && TREE_SIDE_EFFECTS (then_);
bool else_se = else_ && TREE_SIDE_EFFECTS (else_);
if (TREE_CODE (t) == ARRAY_REF || TREE_CODE (t) == ARRAY_RANGE_REF) /* First do simple transformations. */
if (!else_se)
{ {
/* Gimplify the dimension. /* If there is no 'else', turn (a && b) into if (a) if (b). */
Temporary fix for gcc.c-torture/execute/20040313-1.c. while (TREE_CODE (pred) == TRUTH_ANDIF_EXPR)
Gimplify non-constant array indices into a temporary
variable.
FIXME - The real fix is to gimplify post-modify
expressions into a minimal gimple lvalue. However, that
exposes bugs in alias analysis. The alias analyzer does
not handle &PTR->FIELD very well. Will fix after the
branch is merged into mainline (dnovillo 2004-05-03). */
if (!is_gimple_min_invariant (TREE_OPERAND (t, 1)))
{ {
tret = gimplify_expr (&TREE_OPERAND (t, 1), pre_p, post_p, TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1);
is_gimple_tmp_var, fb_rvalue); then_ = shortcut_cond_expr (expr);
ret = MIN (ret, tret); pred = TREE_OPERAND (pred, 0);
expr = build (COND_EXPR, void_type_node, pred, then_, NULL_TREE);
} }
} }
else if (TREE_CODE (t) == BIT_FIELD_REF) if (!then_se)
{ {
tret = gimplify_expr (&TREE_OPERAND (t, 1), pre_p, post_p, /* If there is no 'then', turn
is_gimple_val, fb_rvalue); if (a || b); else d
ret = MIN (ret, tret); into
tret = gimplify_expr (&TREE_OPERAND (t, 2), pre_p, post_p, if (a); else if (b); else d. */
is_gimple_val, fb_rvalue); while (TREE_CODE (pred) == TRUTH_ORIF_EXPR)
ret = MIN (ret, tret); {
TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1);
else_ = shortcut_cond_expr (expr);
pred = TREE_OPERAND (pred, 0);
expr = build (COND_EXPR, void_type_node, pred, NULL_TREE, else_);
} }
}
/* If we're done, great. */
if (TREE_CODE (pred) != TRUTH_ANDIF_EXPR
&& TREE_CODE (pred) != TRUTH_ORIF_EXPR)
return expr;
/* Otherwise we need to mess with gotos. Change
if (a) c; else d;
to
if (a); else goto no;
c; goto end;
no: d; end:
and recursively gimplify the condition. */
true_label = false_label = end_label = NULL_TREE;
/* If our arms just jump somewhere, hijack those labels so we don't
generate jumps to jumps. */
/* The innermost expression P may have originally had TREE_SIDE_EFFECTS if (then_
set which would have caused all the outer expressions in EXPR_P && TREE_CODE (then_) == GOTO_EXPR
leading to P to also have had TREE_SIDE_EFFECTS set. */ && TREE_CODE (GOTO_DESTINATION (then_)) == LABEL_DECL)
recalculate_side_effects (t); {
VARRAY_POP (stack); true_label = GOTO_DESTINATION (then_);
then_ = NULL;
then_se = false;
} }
tret = gimplify_expr (p, pre_p, post_p, is_gimple_min_lval, if (else_
want_lvalue ? fb_lvalue : fb_rvalue); && TREE_CODE (else_) == GOTO_EXPR
ret = MIN (ret, tret); && TREE_CODE (GOTO_DESTINATION (else_)) == LABEL_DECL)
/* If the outermost expression is a COMPONENT_REF, canonicalize its type. */
if (!want_lvalue && TREE_CODE (*expr_p) == COMPONENT_REF)
{ {
canonicalize_component_ref (expr_p); false_label = GOTO_DESTINATION (else_);
ret = MIN (ret, GS_OK); else_ = NULL;
else_se = false;
} }
return ret; /* If we aren't hijacking a label for the 'then' branch, it falls through. */
} if (true_label)
true_label_p = &true_label;
else
true_label_p = NULL;
/* Gimplify the self modifying expression pointed by EXPR_P (++, --, +=, -=). /* The 'else' branch also needs a label if it contains interesting code. */
if (false_label || else_se)
false_label_p = &false_label;
else
false_label_p = NULL;
PRE_P points to the list where side effects that must happen before /* If there was nothing else in our arms, just forward the label(s). */
*EXPR_P should be stored. if (!then_se && !else_se)
return shortcut_cond_r (pred, true_label_p, false_label_p);
POST_P points to the list where side effects that must happen after /* If our last subexpression already has a terminal label, reuse it. */
*EXPR_P should be stored. if (else_se)
expr = expr_last (else_);
else if (then_se)
expr = expr_last (then_);
else
expr = NULL;
if (expr && TREE_CODE (expr) == LABEL_EXPR)
end_label = LABEL_EXPR_LABEL (expr);
WANT_VALUE is nonzero iff we want to use the value of this expression /* If we don't care about jumping to the 'else' branch, jump to the end
in another expression. */ if the condition is false. */
if (!false_label_p)
false_label_p = &end_label;
static enum gimplify_status /* We only want to emit these labels if we aren't hijacking them. */
gimplify_self_mod_expr (tree *expr_p, tree *pre_p, tree *post_p, emit_end = (end_label == NULL_TREE);
bool want_value) emit_false = (false_label == NULL_TREE);
{
enum tree_code code;
tree lhs, lvalue, rhs, t1;
bool postfix;
enum tree_code arith_code;
enum gimplify_status ret;
code = TREE_CODE (*expr_p); pred = shortcut_cond_r (pred, true_label_p, false_label_p);
#if defined ENABLE_CHECKING expr = NULL;
if (code != POSTINCREMENT_EXPR append_to_statement_list (pred, &expr);
&& code != POSTDECREMENT_EXPR
&& code != PREINCREMENT_EXPR
&& code != PREDECREMENT_EXPR)
abort ();
#endif
/* Prefix or postfix? */ append_to_statement_list (then_, &expr);
if (code == POSTINCREMENT_EXPR || code == POSTDECREMENT_EXPR) if (else_se)
/* Faster to treat as prefix if result is not used. */ {
postfix = want_value; t = build_and_jump (&end_label);
else append_to_statement_list (t, &expr);
postfix = false; if (emit_false)
{
t = build1 (LABEL_EXPR, void_type_node, false_label);
append_to_statement_list (t, &expr);
}
append_to_statement_list (else_, &expr);
}
if (emit_end && end_label)
{
t = build1 (LABEL_EXPR, void_type_node, end_label);
append_to_statement_list (t, &expr);
}
/* Add or subtract? */ return expr;
if (code == PREINCREMENT_EXPR || code == POSTINCREMENT_EXPR) }
arith_code = PLUS_EXPR;
else
arith_code = MINUS_EXPR;
/* Gimplify the LHS into a GIMPLE lvalue. */ /* EXPR is used in a boolean context; make sure it has BOOLEAN_TYPE. */
lvalue = TREE_OPERAND (*expr_p, 0);
ret = gimplify_expr (&lvalue, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
if (ret == GS_ERROR)
return ret;
/* Extract the operands to the arithmetic operation. */ static tree
lhs = lvalue; gimple_boolify (tree expr)
rhs = TREE_OPERAND (*expr_p, 1); {
tree type = TREE_TYPE (expr);
/* For postfix operator, we evaluate the LHS to an rvalue and then use if (TREE_CODE (type) == BOOLEAN_TYPE)
that as the result value and in the postqueue operation. */ return expr;
if (postfix)
{
ret = gimplify_expr (&lhs, pre_p, post_p, is_gimple_val, fb_rvalue);
if (ret == GS_ERROR)
return ret;
}
t1 = build (arith_code, TREE_TYPE (*expr_p), lhs, rhs); /* If this is the predicate of a COND_EXPR, it might not even be a
t1 = build (MODIFY_EXPR, TREE_TYPE (lvalue), lvalue, t1); truthvalue yet. */
expr = lang_hooks.truthvalue_conversion (expr);
if (postfix) switch (TREE_CODE (expr))
{
gimplify_and_add (t1, post_p);
*expr_p = lhs;
return GS_ALL_DONE;
}
else
{ {
*expr_p = t1; case TRUTH_AND_EXPR:
return GS_OK; case TRUTH_OR_EXPR:
case TRUTH_XOR_EXPR:
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
/* Also boolify the arguments of truth exprs. */
TREE_OPERAND (expr, 1) = gimple_boolify (TREE_OPERAND (expr, 1));
/* FALLTHRU */
case TRUTH_NOT_EXPR:
TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0));
/* FALLTHRU */
case EQ_EXPR: case NE_EXPR:
case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR:
/* These expressions always produce boolean results. */
TREE_TYPE (expr) = boolean_type_node;
return expr;
default:
/* Other expressions that get here must have boolean values, but
might need to be converted to the appropriate mode. */
return convert (boolean_type_node, expr);
} }
} }
/* Gimplify the CALL_EXPR node pointed by EXPR_P. /* Convert the conditional expression pointed by EXPR_P '(p) ? a : b;'
into
call_expr if (p) if (p)
: ID '(' arglist ')' t1 = a; a;
else or else
t1 = b; b;
t1;
arglist The second form is used when *EXPR_P is of type void.
: arglist ',' val
| val TARGET is the tree for T1 above.
PRE_P points to the list where side effects that must happen before PRE_P points to the list where side effects that must happen before
*EXPR_P should be stored. */ *EXPR_P should be stored. */
static enum gimplify_status static enum gimplify_status
gimplify_call_expr (tree *expr_p, tree *pre_p, bool (*gimple_test_f) (tree)) gimplify_cond_expr (tree *expr_p, tree *pre_p, tree target)
{ {
tree decl; tree expr = *expr_p;
tree arglist; tree tmp, type;
enum gimplify_status ret; enum gimplify_status ret;
#if defined ENABLE_CHECKING type = TREE_TYPE (expr);
if (TREE_CODE (*expr_p) != CALL_EXPR) if (!type)
abort (); TREE_TYPE (expr) = void_type_node;
#endif
/* For reliable diagnostics during inlining, it is necessary that /* If this COND_EXPR has a value, copy the values into a temporary within
every call_expr be annotated with file and line. */ the arms. */
if (!EXPR_LOCUS (*expr_p)) else if (! VOID_TYPE_P (type))
annotate_with_locus (*expr_p, input_location); {
if (target)
{
tmp = target;
ret = GS_OK;
}
else
{
tmp = create_tmp_var (TREE_TYPE (expr), "iftmp");
ret = GS_ALL_DONE;
}
/* This may be a call to a builtin function. /* Build the then clause, 't1 = a;'. But don't build an assignment
if this branch is void; in C++ it can be, if it's a throw. */
if (TREE_TYPE (TREE_OPERAND (expr, 1)) != void_type_node)
TREE_OPERAND (expr, 1)
= build (MODIFY_EXPR, void_type_node, tmp, TREE_OPERAND (expr, 1));
Builtin function calls may be transformed into different /* Build the else clause, 't1 = b;'. */
(and more efficient) builtin function calls under certain if (TREE_TYPE (TREE_OPERAND (expr, 2)) != void_type_node)
circumstances. Unfortunately, gimplification can muck things TREE_OPERAND (expr, 2)
up enough that the builtin expanders are not aware that certain = build (MODIFY_EXPR, void_type_node, tmp, TREE_OPERAND (expr, 2));
transformations are still valid.
So we attempt transformation/gimplification of the call before TREE_TYPE (expr) = void_type_node;
we gimplify the CALL_EXPR. At this time we do not manage to recalculate_side_effects (expr);
transform all calls in the same manner as the expanders do, but
we do transform most of them. */
decl = get_callee_fndecl (*expr_p);
if (decl && DECL_BUILT_IN (decl))
{
tree new;
/* If it is allocation of stack, record the need to restore the memory /* Move the COND_EXPR to the prequeue and use the temp in its place. */
when the enclosing bind_expr is exited. */ gimplify_and_add (expr, pre_p);
if (DECL_FUNCTION_CODE (decl) == BUILT_IN_STACK_ALLOC) *expr_p = tmp;
gimplify_ctxp->save_stack = true;
return ret;
}
/* If it is restore of the stack, reset it, since it means we are /* Make sure the condition has BOOLEAN_TYPE. */
regimplifying the bind_expr. Note that we use the fact that TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0));
for try_finally_expr, try part is processed first. */
if (DECL_FUNCTION_CODE (decl) == BUILT_IN_STACK_RESTORE)
gimplify_ctxp->save_stack = false;
new = simplify_builtin (*expr_p, gimple_test_f == is_gimple_stmt); /* Break apart && and || conditions. */
if (TREE_CODE (TREE_OPERAND (expr, 0)) == TRUTH_ANDIF_EXPR
|| TREE_CODE (TREE_OPERAND (expr, 0)) == TRUTH_ORIF_EXPR)
{
expr = shortcut_cond_expr (expr);
if (new && new != *expr_p) if (expr != *expr_p)
{ {
/* There was a transformation of this call which computes the *expr_p = expr;
same value, but in a more efficient way. Return and try
again. */ /* We can't rely on gimplify_expr to re-gimplify the expanded
*expr_p = new; form properly, as cleanups might cause the target labels to be
return GS_OK; wrapped in a TRY_FINALLY_EXPR. To prevent that, we need to
set up a conditional context. */
gimple_push_condition ();
gimplify_stmt (expr_p);
gimple_pop_condition (pre_p);
return GS_ALL_DONE;
} }
} }
/* There is a sequence point before the call, so any side effects in /* Now do the normal gimplification. */
the calling expression must occur before the actual call. Force ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL,
gimplify_expr to use an internal post queue. */ is_gimple_condexpr, fb_rvalue);
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, NULL,
is_gimple_call_addr, fb_rvalue);
if (PUSH_ARGS_REVERSED) gimple_push_condition ();
TREE_OPERAND (*expr_p, 1) = nreverse (TREE_OPERAND (*expr_p, 1));
for (arglist = TREE_OPERAND (*expr_p, 1); arglist;
arglist = TREE_CHAIN (arglist))
{
enum gimplify_status t;
/* There is a sequence point before a function call. Side effects in gimplify_to_stmt_list (&TREE_OPERAND (expr, 1));
the argument list must occur before the actual call. So, when gimplify_to_stmt_list (&TREE_OPERAND (expr, 2));
gimplifying arguments, force gimplify_expr to use an internal recalculate_side_effects (expr);
post queue which is then appended to the end of PRE_P. */
t = gimplify_expr (&TREE_VALUE (arglist), pre_p, NULL, is_gimple_val,
fb_rvalue);
if (t == GS_ERROR) gimple_pop_condition (pre_p);
ret = GS_ERROR;
}
if (PUSH_ARGS_REVERSED)
TREE_OPERAND (*expr_p, 1) = nreverse (TREE_OPERAND (*expr_p, 1));
/* Try this again in case gimplification exposed something. */ if (ret == GS_ERROR)
if (ret != GS_ERROR && decl && DECL_BUILT_IN (decl)) ;
else if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1)))
ret = GS_ALL_DONE;
else if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 2)))
/* Rewrite "if (a); else b" to "if (!a) b" */
{ {
tree new = simplify_builtin (*expr_p, gimple_test_f == is_gimple_stmt); TREE_OPERAND (expr, 0) = invert_truthvalue (TREE_OPERAND (expr, 0));
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL,
is_gimple_condexpr, fb_rvalue);
if (new && new != *expr_p) tmp = TREE_OPERAND (expr, 1);
{ TREE_OPERAND (expr, 1) = TREE_OPERAND (expr, 2);
/* There was a transformation of this call which computes the TREE_OPERAND (expr, 2) = tmp;
same value, but in a more efficient way. Return and try
again. */
*expr_p = new;
return GS_OK;
}
} }
else
/* Both arms are empty; replace the COND_EXPR with its predicate. */
expr = TREE_OPERAND (expr, 0);
/* If the function is "const" or "pure", then clear TREE_SIDE_EFFECTS on its *expr_p = expr;
decl. This allows us to eliminate redundant or useless
calls to "const" functions. */
if (TREE_CODE (*expr_p) == CALL_EXPR
&& (call_expr_flags (*expr_p) & (ECF_CONST | ECF_PURE)))
TREE_SIDE_EFFECTS (*expr_p) = 0;
return ret; return ret;
} }
/* Handle shortcut semantics in the predicate operand of a COND_EXPR by /* A subroutine of gimplify_modify_expr. Replace a MODIFY_EXPR with
rewriting it into multiple COND_EXPRs, and possibly GOTO_EXPRs. a call to __builtin_memcpy. */
TRUE_LABEL_P and FALSE_LABEL_P point to the labels to jump to if the static enum gimplify_status
condition is true or false, respectively. If null, we should generate gimplify_modify_expr_to_memcpy (tree *expr_p, bool want_value)
our own to skip over the evaluation of this specific expression. {
tree args, t, to, to_ptr, from;
This function is the tree equivalent of do_jump. to = TREE_OPERAND (*expr_p, 0);
from = TREE_OPERAND (*expr_p, 1);
shortcut_cond_r should only be called by shortcut_cond_expr. */ t = TYPE_SIZE_UNIT (TREE_TYPE (to));
t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, to);
t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, from);
t = unshare_expr (t);
args = tree_cons (NULL, t, NULL);
static tree t = build_fold_addr_expr (from);
shortcut_cond_r (tree pred, tree *true_label_p, tree *false_label_p) args = tree_cons (NULL, t, args);
{
tree local_label = NULL_TREE;
tree t, expr = NULL;
/* OK, it's not a simple case; we need to pull apart the COND_EXPR to to_ptr = build_fold_addr_expr (to);
retain the shortcut semantics. Just insert the gotos here; args = tree_cons (NULL, to, args);
shortcut_cond_expr will append the real blocks later. */ t = implicit_built_in_decls[BUILT_IN_MEMCPY];
if (TREE_CODE (pred) == TRUTH_ANDIF_EXPR) t = build_function_call_expr (t, args);
{
/* Turn if (a && b) into
if (a); else goto no; if (want_value)
if (b) goto yes; else goto no; {
(no:) */ t = build1 (NOP_EXPR, TREE_TYPE (to_ptr), t);
t = build1 (INDIRECT_REF, TREE_TYPE (to), t);
}
if (false_label_p == NULL) *expr_p = t;
false_label_p = &local_label; return GS_OK;
}
t = shortcut_cond_r (TREE_OPERAND (pred, 0), NULL, false_label_p); /* A subroutine of gimplify_modify_expr. Replace a MODIFY_EXPR with
append_to_statement_list (t, &expr); a call to __builtin_memset. In this case we know that the RHS is
a CONSTRUCTOR with an empty element list. */
t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p, static enum gimplify_status
false_label_p); gimplify_modify_expr_to_memset (tree *expr_p, bool want_value)
append_to_statement_list (t, &expr); {
} tree args, t, to, to_ptr;
else if (TREE_CODE (pred) == TRUTH_ORIF_EXPR)
{
/* Turn if (a || b) into
if (a) goto yes; to = TREE_OPERAND (*expr_p, 0);
if (b) goto yes; else goto no;
(yes:) */
if (true_label_p == NULL) t = TYPE_SIZE_UNIT (TREE_TYPE (to));
true_label_p = &local_label; t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, to);
t = unshare_expr (t);
args = tree_cons (NULL, t, NULL);
t = shortcut_cond_r (TREE_OPERAND (pred, 0), true_label_p, NULL); args = tree_cons (NULL, integer_zero_node, args);
append_to_statement_list (t, &expr);
t = shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p, to_ptr = build_fold_addr_expr (to);
false_label_p); args = tree_cons (NULL, to, args);
append_to_statement_list (t, &expr); t = implicit_built_in_decls[BUILT_IN_MEMSET];
} t = build_function_call_expr (t, args);
else if (TREE_CODE (pred) == COND_EXPR)
{
/* As long as we're messing with gotos, turn if (a ? b : c) into
if (a)
if (b) goto yes; else goto no;
else
if (c) goto yes; else goto no; */
expr = build (COND_EXPR, void_type_node, TREE_OPERAND (pred, 0),
shortcut_cond_r (TREE_OPERAND (pred, 1), true_label_p,
false_label_p),
shortcut_cond_r (TREE_OPERAND (pred, 2), true_label_p,
false_label_p));
}
else
{
expr = build (COND_EXPR, void_type_node, pred,
build_and_jump (true_label_p),
build_and_jump (false_label_p));
}
if (local_label) if (want_value)
{ {
t = build1 (LABEL_EXPR, void_type_node, local_label); t = build1 (NOP_EXPR, TREE_TYPE (to_ptr), t);
append_to_statement_list (t, &expr); t = build1 (INDIRECT_REF, TREE_TYPE (to), t);
} }
return expr; *expr_p = t;
return GS_OK;
} }
static tree /* A subroutine of gimplify_modify_expr. Break out elements of a
shortcut_cond_expr (tree expr) CONSTRUCTOR used as an initializer into separate MODIFY_EXPRs.
Note that we still need to clear any elements that don't have explicit
initializers, so if not all elements are initialized we keep the
original MODIFY_EXPR, we just remove all of the constructor elements. */
static enum gimplify_status
gimplify_init_constructor (tree *expr_p, tree *pre_p,
tree *post_p, bool want_value)
{ {
tree pred = TREE_OPERAND (expr, 0); tree object = TREE_OPERAND (*expr_p, 0);
tree then_ = TREE_OPERAND (expr, 1); tree ctor = TREE_OPERAND (*expr_p, 1);
tree else_ = TREE_OPERAND (expr, 2); tree type = TREE_TYPE (ctor);
tree true_label, false_label, end_label, t; enum gimplify_status ret;
tree *true_label_p; tree elt_list;
tree *false_label_p;
bool emit_end, emit_false;
bool then_se = then_ && TREE_SIDE_EFFECTS (then_);
bool else_se = else_ && TREE_SIDE_EFFECTS (else_);
/* First do simple transformations. */ if (TREE_CODE (ctor) != CONSTRUCTOR)
if (!else_se) return GS_UNHANDLED;
elt_list = CONSTRUCTOR_ELTS (ctor);
ret = GS_ALL_DONE;
switch (TREE_CODE (type))
{ {
/* If there is no 'else', turn (a && b) into if (a) if (b). */ case RECORD_TYPE:
while (TREE_CODE (pred) == TRUTH_ANDIF_EXPR) case UNION_TYPE:
case QUAL_UNION_TYPE:
case ARRAY_TYPE:
{ {
TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1); HOST_WIDE_INT i, num_elements, num_nonzero_elements;
then_ = shortcut_cond_expr (expr); HOST_WIDE_INT num_nonconstant_elements;
pred = TREE_OPERAND (pred, 0); bool cleared;
expr = build (COND_EXPR, void_type_node, pred, then_, NULL_TREE);
} /* Aggregate types must lower constructors to initialization of
} individual elements. The exception is that a CONSTRUCTOR node
if (!then_se) with no elements indicates zero-initialization of the whole. */
if (elt_list == NULL)
{ {
/* If there is no 'then', turn if (want_value)
if (a || b); else d
into
if (a); else if (b); else d. */
while (TREE_CODE (pred) == TRUTH_ORIF_EXPR)
{ {
TREE_OPERAND (expr, 0) = TREE_OPERAND (pred, 1); *expr_p = object;
else_ = shortcut_cond_expr (expr); return GS_OK;
pred = TREE_OPERAND (pred, 0);
expr = build (COND_EXPR, void_type_node, pred, NULL_TREE, else_);
} }
else
return GS_UNHANDLED;
} }
/* If we're done, great. */ categorize_ctor_elements (ctor, &num_nonzero_elements,
if (TREE_CODE (pred) != TRUTH_ANDIF_EXPR &num_nonconstant_elements);
&& TREE_CODE (pred) != TRUTH_ORIF_EXPR) num_elements = count_type_elements (TREE_TYPE (ctor));
return expr;
/* Otherwise we need to mess with gotos. Change /* If a const aggregate variable is being initialized, then it
if (a) c; else d; should never be a lose to promote the variable to be static. */
to if (num_nonconstant_elements == 0
if (a); else goto no; && TREE_READONLY (object)
c; goto end; && TREE_CODE (object) == VAR_DECL)
no: d; end: {
and recursively gimplify the condition. */ DECL_INITIAL (object) = ctor;
TREE_STATIC (object) = 1;
if (!DECL_NAME (object))
DECL_NAME (object) = create_tmp_var_name ("C");
walk_tree (&DECL_INITIAL (object), force_labels_r, NULL, NULL);
true_label = false_label = end_label = NULL_TREE; /* ??? C++ doesn't automatically append a .<number> to the
assembler name, and even when it does, it looks a FE private
data structures to figure out what that number should be,
which are not set for this variable. I suppose this is
important for local statics for inline functions, which aren't
"local" in the object file sense. So in order to get a unique
TU-local symbol, we must invoke the lhd version now. */
lhd_set_decl_assembler_name (object);
/* If our arms just jump somewhere, hijack those labels so we don't *expr_p = NULL_TREE;
generate jumps to jumps. */ break;
}
if (then_ /* If there are "lots" of initialized elements, and all of them
&& TREE_CODE (then_) == GOTO_EXPR are valid address constants, then the entire initializer can
&& TREE_CODE (GOTO_DESTINATION (then_)) == LABEL_DECL) be dropped to memory, and then memcpy'd out. */
if (num_nonconstant_elements == 0)
{ {
true_label = GOTO_DESTINATION (then_); HOST_WIDE_INT size = int_size_in_bytes (type);
then_ = NULL; unsigned int align;
then_se = false;
}
if (else_ /* ??? We can still get unbounded array types, at least
&& TREE_CODE (else_) == GOTO_EXPR from the C++ front end. This seems wrong, but attempt
&& TREE_CODE (GOTO_DESTINATION (else_)) == LABEL_DECL) to work around it for now. */
if (size < 0)
{ {
false_label = GOTO_DESTINATION (else_); size = int_size_in_bytes (TREE_TYPE (object));
else_ = NULL; if (size >= 0)
else_se = false; TREE_TYPE (ctor) = type = TREE_TYPE (object);
} }
/* If we aren't hijacking a label for the 'then' branch, it falls through. */ /* Find the maximum alignment we can assume for the object. */
if (true_label) /* ??? Make use of DECL_OFFSET_ALIGN. */
true_label_p = &true_label; if (DECL_P (object))
else align = DECL_ALIGN (object);
true_label_p = NULL;
/* The 'else' branch also needs a label if it contains interesting code. */
if (false_label || else_se)
false_label_p = &false_label;
else
false_label_p = NULL;
/* If there was nothing else in our arms, just forward the label(s). */
if (!then_se && !else_se)
return shortcut_cond_r (pred, true_label_p, false_label_p);
/* If our last subexpression already has a terminal label, reuse it. */
if (else_se)
expr = expr_last (else_);
else if (then_se)
expr = expr_last (then_);
else else
expr = NULL; align = TYPE_ALIGN (type);
if (expr && TREE_CODE (expr) == LABEL_EXPR)
end_label = LABEL_EXPR_LABEL (expr);
/* If we don't care about jumping to the 'else' branch, jump to the end if (size > 0 && !can_move_by_pieces (size, align))
if the condition is false. */ {
if (!false_label_p) tree new = create_tmp_var_raw (type, "C");
false_label_p = &end_label; gimple_add_tmp_var (new);
TREE_STATIC (new) = 1;
TREE_READONLY (new) = 1;
DECL_INITIAL (new) = ctor;
if (align > DECL_ALIGN (new))
{
DECL_ALIGN (new) = align;
DECL_USER_ALIGN (new) = 1;
}
walk_tree (&DECL_INITIAL (new), force_labels_r, NULL, NULL);
/* We only want to emit these labels if we aren't hijacking them. */ TREE_OPERAND (*expr_p, 1) = new;
emit_end = (end_label == NULL_TREE); break;
emit_false = (false_label == NULL_TREE); }
}
pred = shortcut_cond_r (pred, true_label_p, false_label_p); /* If there are "lots" of initialized elements, even discounting
those that are not address constants (and thus *must* be
computed at runtime), then partition the constructor into
constant and non-constant parts. Block copy the constant
parts in, then generate code for the non-constant parts. */
/* TODO. There's code in cp/typeck.c to do this. */
expr = NULL; /* If there are "lots" of zeros, then block clear the object first. */
append_to_statement_list (pred, &expr); cleared = false;
if (num_elements - num_nonzero_elements > CLEAR_RATIO
&& num_nonzero_elements < num_elements/4)
cleared = true;
append_to_statement_list (then_, &expr); /* ??? This bit ought not be needed. For any element not present
if (else_se) in the initializer, we should simply set them to zero. Except
we'd need to *find* the elements that are not present, and that
requires trickery to avoid quadratic compile-time behavior in
large cases or excessive memory use in small cases. */
else
{ {
t = build_and_jump (&end_label); HOST_WIDE_INT len = list_length (elt_list);
append_to_statement_list (t, &expr); if (TREE_CODE (type) == ARRAY_TYPE)
if (emit_false)
{ {
t = build1 (LABEL_EXPR, void_type_node, false_label); tree nelts = array_type_nelts (type);
append_to_statement_list (t, &expr); if (!host_integerp (nelts, 1)
|| tree_low_cst (nelts, 1) != len)
cleared = 1;;
} }
append_to_statement_list (else_, &expr); else if (len != fields_length (type))
cleared = 1;
} }
if (emit_end && end_label)
if (cleared)
{ {
t = build1 (LABEL_EXPR, void_type_node, end_label); /* Zap the CONSTRUCTOR element list, which simplifies this case.
append_to_statement_list (t, &expr); Note that we still have to gimplify, in order to handle the
case of variable sized types. */
CONSTRUCTOR_ELTS (ctor) = NULL_TREE;
gimplify_stmt (expr_p);
append_to_statement_list (*expr_p, pre_p);
} }
return expr; for (i = 0; elt_list; i++, elt_list = TREE_CHAIN (elt_list))
} {
tree purpose, value, cref, init;
/* EXPR is used in a boolean context; make sure it has BOOLEAN_TYPE. */
static tree
gimple_boolify (tree expr)
{
tree type = TREE_TYPE (expr);
if (TREE_CODE (type) == BOOLEAN_TYPE) purpose = TREE_PURPOSE (elt_list);
return expr; value = TREE_VALUE (elt_list);
/* If this is the predicate of a COND_EXPR, it might not even be a if (cleared && initializer_zerop (value))
truthvalue yet. */ continue;
expr = lang_hooks.truthvalue_conversion (expr);
switch (TREE_CODE (expr)) if (TREE_CODE (type) == ARRAY_TYPE)
{ {
case TRUTH_AND_EXPR: tree t = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (object)));
case TRUTH_OR_EXPR:
case TRUTH_XOR_EXPR:
case TRUTH_ANDIF_EXPR:
case TRUTH_ORIF_EXPR:
/* Also boolify the arguments of truth exprs. */
TREE_OPERAND (expr, 1) = gimple_boolify (TREE_OPERAND (expr, 1));
/* FALLTHRU */
case TRUTH_NOT_EXPR:
TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0));
/* FALLTHRU */
case EQ_EXPR: case NE_EXPR: /* ??? Here's to hoping the front end fills in all of the
case LE_EXPR: case GE_EXPR: case LT_EXPR: case GT_EXPR: indicies, so we don't have to figure out what's missing
/* These expressions always produce boolean results. */ ourselves. */
TREE_TYPE (expr) = boolean_type_node; if (!purpose)
return expr; abort ();
/* ??? Need to handle this. */
if (TREE_CODE (purpose) == RANGE_EXPR)
abort ();
default: cref = build (ARRAY_REF, t, object, purpose,
/* Other expressions that get here must have boolean values, but NULL_TREE, NULL_TREE);
might need to be converted to the appropriate mode. */
return convert (boolean_type_node, expr);
} }
} else
cref = build (COMPONENT_REF, TREE_TYPE (purpose), object,
/* Convert the conditional expression pointed by EXPR_P '(p) ? a : b;' purpose, NULL_TREE);
into
if (p) if (p)
t1 = a; a;
else or else
t1 = b; b;
t1;
The second form is used when *EXPR_P is of type void.
TARGET is the tree for T1 above. init = build (MODIFY_EXPR, TREE_TYPE (purpose), cref, value);
PRE_P points to the list where side effects that must happen before /* Each member initialization is a full-expression. */
*EXPR_P should be stored. */ gimplify_and_add (init, pre_p);
}
static enum gimplify_status *expr_p = NULL_TREE;
gimplify_cond_expr (tree *expr_p, tree *pre_p, tree target) }
{ break;
tree expr = *expr_p;
tree tmp, type;
enum gimplify_status ret;
type = TREE_TYPE (expr); case COMPLEX_TYPE:
if (!type) {
TREE_TYPE (expr) = void_type_node; tree r, i;
/* If this COND_EXPR has a value, copy the values into a temporary within /* Extract the real and imaginary parts out of the ctor. */
the arms. */ r = i = NULL_TREE;
else if (! VOID_TYPE_P (type)) if (elt_list)
{
r = TREE_VALUE (elt_list);
elt_list = TREE_CHAIN (elt_list);
if (elt_list)
{ {
if (target) i = TREE_VALUE (elt_list);
if (TREE_CHAIN (elt_list))
abort ();
}
}
if (r == NULL || i == NULL)
{ {
tmp = target; tree zero = convert (TREE_TYPE (type), integer_zero_node);
ret = GS_OK; if (r == NULL)
r = zero;
if (i == NULL)
i = zero;
}
/* Complex types have either COMPLEX_CST or COMPLEX_EXPR to
represent creation of a complex value. */
if (TREE_CONSTANT (r) && TREE_CONSTANT (i))
{
ctor = build_complex (type, r, i);
TREE_OPERAND (*expr_p, 1) = ctor;
} }
else else
{ {
tmp = create_tmp_var (TREE_TYPE (expr), "iftmp"); ctor = build (COMPLEX_EXPR, type, r, i);
ret = GS_ALL_DONE; TREE_OPERAND (*expr_p, 1) = ctor;
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
is_gimple_rhs, fb_rvalue);
} }
}
break;
/* Build the then clause, 't1 = a;'. But don't build an assignment case VECTOR_TYPE:
if this branch is void; in C++ it can be, if it's a throw. */ /* Go ahead and simplify constant constructors to VECTOR_CST. */
if (TREE_TYPE (TREE_OPERAND (expr, 1)) != void_type_node) if (TREE_CONSTANT (ctor))
TREE_OPERAND (expr, 1) TREE_OPERAND (*expr_p, 1) = build_vector (type, elt_list);
= build (MODIFY_EXPR, void_type_node, tmp, TREE_OPERAND (expr, 1)); else
{
/* Build the else clause, 't1 = b;'. */ /* Vector types use CONSTRUCTOR all the way through gimple
if (TREE_TYPE (TREE_OPERAND (expr, 2)) != void_type_node) compilation as a general initializer. */
TREE_OPERAND (expr, 2) for (; elt_list; elt_list = TREE_CHAIN (elt_list))
= build (MODIFY_EXPR, void_type_node, tmp, TREE_OPERAND (expr, 2)); {
enum gimplify_status tret;
TREE_TYPE (expr) = void_type_node; tret = gimplify_expr (&TREE_VALUE (elt_list), pre_p, post_p,
recalculate_side_effects (expr); is_gimple_constructor_elt, fb_rvalue);
if (tret == GS_ERROR)
ret = GS_ERROR;
}
}
break;
/* Move the COND_EXPR to the prequeue and use the temp in its place. */ default:
gimplify_and_add (expr, pre_p); /* So how did we get a CONSTRUCTOR for a scalar type? */
*expr_p = tmp; abort ();
}
return ret; if (ret == GS_ERROR)
return GS_ERROR;
else if (want_value)
{
append_to_statement_list (*expr_p, pre_p);
*expr_p = object;
return GS_OK;
} }
else
return GS_ALL_DONE;
}
/* Make sure the condition has BOOLEAN_TYPE. */ /* Subroutine of gimplify_modify_expr to do simplifications of MODIFY_EXPRs
TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0)); based on the code of the RHS. We loop for as long as something changes. */
/* Break apart && and || conditions. */ static enum gimplify_status
if (TREE_CODE (TREE_OPERAND (expr, 0)) == TRUTH_ANDIF_EXPR gimplify_modify_expr_rhs (tree *expr_p, tree *from_p, tree *to_p, tree *pre_p,
|| TREE_CODE (TREE_OPERAND (expr, 0)) == TRUTH_ORIF_EXPR) tree *post_p, bool want_value)
{ {
expr = shortcut_cond_expr (expr); enum gimplify_status ret = GS_OK;
if (expr != *expr_p) while (ret != GS_UNHANDLED)
switch (TREE_CODE (*from_p))
{ {
*expr_p = expr; case TARGET_EXPR:
{
/* If we are initializing something from a TARGET_EXPR, strip the
TARGET_EXPR and initialize it directly, if possible. This can't
be done if the initializer is void, since that implies that the
temporary is set in some non-trivial way.
/* We can't rely on gimplify_expr to re-gimplify the expanded ??? What about code that pulls out the temp and uses it
form properly, as cleanups might cause the target labels to be elsewhere? I think that such code never uses the TARGET_EXPR as
wrapped in a TRY_FINALLY_EXPR. To prevent that, we need to an initializer. If I'm wrong, we'll abort because the temp won't
set up a conditional context. */ have any RTL. In that case, I guess we'll need to replace
gimple_push_condition (); references somehow. */
gimplify_stmt (expr_p); tree init = TARGET_EXPR_INITIAL (*from_p);
gimple_pop_condition (pre_p);
return GS_ALL_DONE; if (!VOID_TYPE_P (TREE_TYPE (init)))
{
*from_p = init;
ret = GS_OK;
} }
else
ret = GS_UNHANDLED;
} }
break;
/* Now do the normal gimplification. */ case COMPOUND_EXPR:
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL, /* Remove any COMPOUND_EXPR in the RHS so the following cases will be
is_gimple_condexpr, fb_rvalue); caught. */
gimplify_compound_expr (from_p, pre_p, true);
gimple_push_condition (); ret = GS_OK;
break;
gimplify_to_stmt_list (&TREE_OPERAND (expr, 1));
gimplify_to_stmt_list (&TREE_OPERAND (expr, 2));
recalculate_side_effects (expr);
gimple_pop_condition (pre_p); case CONSTRUCTOR:
/* If we're initializing from a CONSTRUCTOR, break this into
individual MODIFY_EXPRs. */
return gimplify_init_constructor (expr_p, pre_p, post_p, want_value);
if (ret == GS_ERROR) case COND_EXPR:
; /* If we're assigning from a ?: expression with ADDRESSABLE type, push
else if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 1))) the assignment down into the branches, since we can't generate a
ret = GS_ALL_DONE; temporary of such a type. */
else if (TREE_SIDE_EFFECTS (TREE_OPERAND (expr, 2))) if (TREE_ADDRESSABLE (TREE_TYPE (*from_p)))
/* Rewrite "if (a); else b" to "if (!a) b" */
{ {
TREE_OPERAND (expr, 0) = invert_truthvalue (TREE_OPERAND (expr, 0)); *expr_p = *from_p;
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL, return gimplify_cond_expr (expr_p, pre_p, *to_p);
is_gimple_condexpr, fb_rvalue);
tmp = TREE_OPERAND (expr, 1);
TREE_OPERAND (expr, 1) = TREE_OPERAND (expr, 2);
TREE_OPERAND (expr, 2) = tmp;
} }
else else
/* Both arms are empty; replace the COND_EXPR with its predicate. */ ret = GS_UNHANDLED;
expr = TREE_OPERAND (expr, 0); break;
default:
ret = GS_UNHANDLED;
break;
}
*expr_p = expr;
return ret; return ret;
} }
...@@ -2628,31 +2770,15 @@ gimplify_modify_expr (tree *expr_p, tree *pre_p, tree *post_p, bool want_value) ...@@ -2628,31 +2770,15 @@ gimplify_modify_expr (tree *expr_p, tree *pre_p, tree *post_p, bool want_value)
return ret; return ret;
/* If the value being copied is of variable width, expose the length /* If the value being copied is of variable width, expose the length
if the copy by converting the whole thing to a memcpy. Note that if the copy by converting the whole thing to a memcpy/memset.
we need to do this before gimplifying any of the operands Note that we need to do this before gimplifying any of the operands
so that we can resolve any PLACEHOLDER_EXPRs in the size. */ so that we can resolve any PLACEHOLDER_EXPRs in the size. */
if (TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (*to_p))) != INTEGER_CST) if (TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (*to_p))) != INTEGER_CST)
{ {
tree args, t, dest; if (TREE_CODE (*from_p) == CONSTRUCTOR)
return gimplify_modify_expr_to_memset (expr_p, want_value);
t = TYPE_SIZE_UNIT (TREE_TYPE (*to_p)); else
t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, *to_p); return gimplify_modify_expr_to_memcpy (expr_p, want_value);
t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, *from_p);
t = unshare_expr (t);
args = tree_cons (NULL, t, NULL);
t = build_fold_addr_expr (*from_p);
args = tree_cons (NULL, t, args);
dest = build_fold_addr_expr (*to_p);
args = tree_cons (NULL, dest, args);
t = implicit_built_in_decls[BUILT_IN_MEMCPY];
t = build_function_call_expr (t, args);
if (want_value)
{
t = build1 (NOP_EXPR, TREE_TYPE (dest), t);
t = build1 (INDIRECT_REF, TREE_TYPE (*to_p), t);
}
*expr_p = t;
return GS_OK;
} }
ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_lvalue, fb_lvalue); ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
...@@ -2707,75 +2833,6 @@ gimplify_modify_expr (tree *expr_p, tree *pre_p, tree *post_p, bool want_value) ...@@ -2707,75 +2833,6 @@ gimplify_modify_expr (tree *expr_p, tree *pre_p, tree *post_p, bool want_value)
return ret; return ret;
} }
/* Subroutine of above to do simplifications of MODIFY_EXPRs based on
the code of the RHS. We loop for as long as we can do something. */
static enum gimplify_status
gimplify_modify_expr_rhs (tree *expr_p, tree *from_p, tree *to_p, tree *pre_p,
tree *post_p, bool want_value)
{
enum gimplify_status ret = GS_OK;
while (ret != GS_UNHANDLED)
switch (TREE_CODE (*from_p))
{
case TARGET_EXPR:
{
/* If we are initializing something from a TARGET_EXPR, strip the
TARGET_EXPR and initialize it directly, if possible. This can't
be done if the initializer is void, since that implies that the
temporary is set in some non-trivial way.
??? What about code that pulls out the temp and uses it
elsewhere? I think that such code never uses the TARGET_EXPR as
an initializer. If I'm wrong, we'll abort because the temp won't
have any RTL. In that case, I guess we'll need to replace
references somehow. */
tree init = TARGET_EXPR_INITIAL (*from_p);
if (!VOID_TYPE_P (TREE_TYPE (init)))
{
*from_p = init;
ret = GS_OK;
}
else
ret = GS_UNHANDLED;
}
break;
case COMPOUND_EXPR:
/* Remove any COMPOUND_EXPR in the RHS so the following cases will be
caught. */
gimplify_compound_expr (from_p, pre_p, true);
ret = GS_OK;
break;
case CONSTRUCTOR:
/* If we're initializing from a CONSTRUCTOR, break this into
individual MODIFY_EXPRs. */
return gimplify_init_constructor (expr_p, pre_p, post_p, want_value);
case COND_EXPR:
/* If we're assigning from a ?: expression with ADDRESSABLE type, push
the assignment down into the branches, since we can't generate a
temporary of such a type. */
if (TREE_ADDRESSABLE (TREE_TYPE (*from_p)))
{
*expr_p = *from_p;
return gimplify_cond_expr (expr_p, pre_p, *to_p);
}
else
ret = GS_UNHANDLED;
break;
default:
ret = GS_UNHANDLED;
break;
}
return ret;
}
/* Gimplify a comparison between two variable-sized objects. Do this /* Gimplify a comparison between two variable-sized objects. Do this
with a call to BUILT_IN_MEMCMP. */ with a call to BUILT_IN_MEMCMP. */
......
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