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>
* web.c (union_defs): use all defs of an instruction to create a
......
......@@ -1323,414 +1323,137 @@ force_labels_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED)
return NULL_TREE;
}
/* Break out elements of a constructor used as an initializer into separate
MODIFY_EXPRs.
/* *EXPR_P is a COMPONENT_REF being used as an rvalue. If its type is
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
initializers, so if not all elements are initialized we keep the
original MODIFY_EXPR, we just remove all of the constructor elements. */
The canonical type of a COMPONENT_REF is the type of the field being
referenced--unless the field is a bit-field which can be read directly
in a smaller mode, in which case the canonical type is the
sign-appropriate type corresponding to that mode. */
static enum gimplify_status
gimplify_init_constructor (tree *expr_p, tree *pre_p,
tree *post_p, bool want_value)
static void
canonicalize_component_ref (tree *expr_p)
{
tree object = TREE_OPERAND (*expr_p, 0);
tree ctor = TREE_OPERAND (*expr_p, 1);
tree type = TREE_TYPE (ctor);
enum gimplify_status ret;
tree elt_list;
tree expr = *expr_p;
tree type;
if (TREE_CODE (ctor) != CONSTRUCTOR)
return GS_UNHANDLED;
if (TREE_CODE (expr) != COMPONENT_REF)
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;
switch (TREE_CODE (type))
if (TREE_TYPE (expr) != type)
{
case RECORD_TYPE:
case UNION_TYPE:
case QUAL_UNION_TYPE:
case ARRAY_TYPE:
{
HOST_WIDE_INT i, num_elements, num_nonzero_elements;
HOST_WIDE_INT num_nonconstant_elements;
bool cleared;
tree old_type = TREE_TYPE (expr);
/* Aggregate types must lower constructors to initialization of
individual elements. The exception is that a CONSTRUCTOR node
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;
}
/* Set the type of the COMPONENT_REF to the underlying type. */
TREE_TYPE (expr) = type;
categorize_ctor_elements (ctor, &num_nonzero_elements,
&num_nonconstant_elements);
num_elements = count_type_elements (TREE_TYPE (ctor));
/* And wrap the whole thing inside a NOP_EXPR. */
expr = build1 (NOP_EXPR, old_type, expr);
/* If a const aggregate variable is being initialized, then it
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);
*expr_p = expr;
}
}
/* ??? 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 a NOP conversion is changing a pointer to array of foo to a pointer
to foo, embed that change in the ADDR_EXPR by converting
T array[U];
(T *)&array
==>
&array[L]
where L is the lower bound. For simplicity, only do this for constant
lower bound. */
*expr_p = NULL_TREE;
break;
}
static void
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
are valid address constants, then the entire initializer can
be dropped to memory, and then memcpy'd out. */
if (num_nonconstant_elements == 0)
{
HOST_WIDE_INT size = int_size_in_bytes (type);
unsigned int align;
/* Both cast and addr_expr types should be pointers. */
if (!POINTER_TYPE_P (ctype) || !POINTER_TYPE_P (atype))
return;
/* ??? We can still get unbounded array types, at least
from the C++ front end. This seems wrong, but attempt
to work around it for now. */
if (size < 0)
{
size = int_size_in_bytes (TREE_TYPE (object));
if (size >= 0)
TREE_TYPE (ctor) = type = TREE_TYPE (object);
}
/* The addr_expr type should be a pointer to an array. */
datype = TREE_TYPE (atype);
if (TREE_CODE (datype) != ARRAY_TYPE)
return;
/* Find the maximum alignment we can assume for the object. */
/* ??? Make use of DECL_OFFSET_ALIGN. */
if (DECL_P (object))
align = DECL_ALIGN (object);
else
align = TYPE_ALIGN (type);
/* Both cast and addr_expr types should address the same object type. */
dctype = TREE_TYPE (ctype);
ddatype = TREE_TYPE (datype);
if (!lang_hooks.types_compatible_p (ddatype, dctype))
return;
if (size > 0 && !can_move_by_pieces (size, align))
{
tree new = create_tmp_var_raw (type, "C");
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);
/* The addr_expr and the object type should match. */
obj_expr = TREE_OPERAND (addr_expr, 0);
otype = TREE_TYPE (obj_expr);
if (!lang_hooks.types_compatible_p (otype, datype))
return;
TREE_OPERAND (*expr_p, 1) = new;
break;
}
}
/* The lower bound and element sizes must be constant. */
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
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. */
/* All checks succeeded. Build a new node to merge the cast. */
*expr_p = build4 (ARRAY_REF, dctype, obj_expr,
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);
}
/* If there are "lots" of zeros, then block clear the object first. */
cleared = false;
if (num_elements - num_nonzero_elements > CLEAR_RATIO
&& num_nonzero_elements < num_elements/4)
cleared = true;
/* *EXPR_P is a NOP_EXPR or CONVERT_EXPR. Remove it and/or other conversions
underneath as appropriate. */
/* ??? This bit ought not be needed. For any element not present
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
{
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;
}
static enum gimplify_status
gimplify_conversion (tree *expr_p)
{
/* Strip away as many useless type conversions as possible
at the toplevel. */
STRIP_USELESS_TYPE_CONVERSION (*expr_p);
if (cleared)
{
CONSTRUCTOR_ELTS (ctor) = NULL_TREE;
append_to_statement_list (*expr_p, pre_p);
}
/* 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)
{
STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
for (i = 0; elt_list; i++, elt_list = TREE_CHAIN (elt_list))
{
tree purpose, value, cref, init;
/* And remove the outermost conversion if it's useless. */
if (tree_ssa_useless_type_conversion (*expr_p))
*expr_p = TREE_OPERAND (*expr_p, 0);
}
purpose = TREE_PURPOSE (elt_list);
value = TREE_VALUE (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 sub = TREE_OPERAND (*expr_p, 0);
if (cleared && initializer_zerop (value))
continue;
/* If a NOP conversion is changing the type of a COMPONENT_REF
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 (TREE_CODE (type) == ARRAY_TYPE)
{
tree t = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (object)));
/* ??? Here's to hoping the front end fills in all of the
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);
}
else
cref = build (COMPONENT_REF, TREE_TYPE (purpose), object,
purpose, NULL_TREE);
init = build (MODIFY_EXPR, TREE_TYPE (purpose), cref, value);
/* Each member initialization is a full-expression. */
gimplify_and_add (init, pre_p);
}
*expr_p = NULL_TREE;
}
break;
case COMPLEX_TYPE:
{
tree r, i;
/* Extract the real and imaginary parts out of the ctor. */
r = i = NULL_TREE;
if (elt_list)
{
r = TREE_VALUE (elt_list);
elt_list = TREE_CHAIN (elt_list);
if (elt_list)
{
i = TREE_VALUE (elt_list);
if (TREE_CHAIN (elt_list))
abort ();
}
}
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
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
{
ctor = build (COMPLEX_EXPR, type, r, i);
TREE_OPERAND (*expr_p, 1) = ctor;
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
is_gimple_rhs, fb_rvalue);
}
}
break;
case VECTOR_TYPE:
/* Go ahead and simplify constant constructors to VECTOR_CST. */
if (TREE_CONSTANT (ctor))
TREE_OPERAND (*expr_p, 1) = build_vector (type, elt_list);
else
{
/* Vector types use CONSTRUCTOR all the way through gimple
compilation as a general initializer. */
for (; elt_list; elt_list = TREE_CHAIN (elt_list))
{
enum gimplify_status tret;
tret = gimplify_expr (&TREE_VALUE (elt_list), pre_p, post_p,
is_gimple_constructor_elt, fb_rvalue);
if (tret == GS_ERROR)
ret = GS_ERROR;
}
}
break;
default:
/* So how did we get a CONSTRUCTOR for a scalar type? */
abort ();
}
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;
}
/* *EXPR_P is a COMPONENT_REF being used as an rvalue. If its type is
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.
The canonical type of a COMPONENT_REF is the type of the field being
referenced--unless the field is a bit-field which can be read directly
in a smaller mode, in which case the canonical type is the
sign-appropriate type corresponding to that mode. */
static void
canonicalize_component_ref (tree *expr_p)
{
tree expr = *expr_p;
tree type;
if (TREE_CODE (expr) != COMPONENT_REF)
abort ();
if (INTEGRAL_TYPE_P (TREE_TYPE (expr)))
type = TREE_TYPE (get_unwidened (expr, NULL_TREE));
else
type = TREE_TYPE (TREE_OPERAND (expr, 1));
if (TREE_TYPE (expr) != type)
{
tree old_type = TREE_TYPE (expr);
/* Set the type of the COMPONENT_REF to the underlying type. */
TREE_TYPE (expr) = type;
/* And wrap the whole thing inside a NOP_EXPR. */
expr = build1 (NOP_EXPR, old_type, expr);
*expr_p = expr;
}
}
/* If a NOP conversion is changing a pointer to array of foo to a pointer
to foo, embed that change in the ADDR_EXPR by converting
T array[U];
(T *)&array
==>
&array[L]
where L is the lower bound. For simplicity, only do this for constant
lower bound. */
static void
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;
/* Both cast and addr_expr types should be pointers. */
if (!POINTER_TYPE_P (ctype) || !POINTER_TYPE_P (atype))
return;
/* The addr_expr type should be a pointer to an array. */
datype = TREE_TYPE (atype);
if (TREE_CODE (datype) != ARRAY_TYPE)
return;
/* Both cast and addr_expr types should address the same object type. */
dctype = TREE_TYPE (ctype);
ddatype = TREE_TYPE (datype);
if (!lang_hooks.types_compatible_p (ddatype, dctype))
return;
/* The addr_expr and the object type should match. */
obj_expr = TREE_OPERAND (addr_expr, 0);
otype = TREE_TYPE (obj_expr);
if (!lang_hooks.types_compatible_p (otype, datype))
return;
/* The lower bound and element sizes must be constant. */
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;
/* All checks succeeded. Build a new node to merge the cast. */
*expr_p = build4 (ARRAY_REF, dctype, obj_expr,
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
underneath as appropriate. */
static enum gimplify_status
gimplify_conversion (tree *expr_p)
{
/* Strip away as many useless type conversions as possible
at the toplevel. */
STRIP_USELESS_TYPE_CONVERSION (*expr_p);
/* 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)
{
STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
/* And remove the outermost conversion if it's useless. */
if (tree_ssa_useless_type_conversion (*expr_p))
*expr_p = TREE_OPERAND (*expr_p, 0);
}
/* 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 sub = TREE_OPERAND (*expr_p, 0);
/* If a NOP conversion is changing the type of a COMPONENT_REF
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 a NOP conversion is changing a pointer to array of foo
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 a NOP conversion is changing a pointer to array of foo
to a pointer to foo, embed that change in the ADDR_EXPR. */
else if (TREE_CODE (sub) == ADDR_EXPR)
canonicalize_addr_expr (expr_p);
}
return GS_OK;
}
......@@ -2490,107 +2213,526 @@ gimplify_cond_expr (tree *expr_p, tree *pre_p, tree target)
tree tmp, type;
enum gimplify_status ret;
type = TREE_TYPE (expr);
if (!type)
TREE_TYPE (expr) = void_type_node;
type = TREE_TYPE (expr);
if (!type)
TREE_TYPE (expr) = void_type_node;
/* If this COND_EXPR has a value, copy the values into a temporary within
the arms. */
else if (! VOID_TYPE_P (type))
{
if (target)
{
tmp = target;
ret = GS_OK;
}
else
{
tmp = create_tmp_var (TREE_TYPE (expr), "iftmp");
ret = GS_ALL_DONE;
}
/* 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));
/* Build the else clause, 't1 = b;'. */
if (TREE_TYPE (TREE_OPERAND (expr, 2)) != void_type_node)
TREE_OPERAND (expr, 2)
= build (MODIFY_EXPR, void_type_node, tmp, TREE_OPERAND (expr, 2));
TREE_TYPE (expr) = void_type_node;
recalculate_side_effects (expr);
/* Move the COND_EXPR to the prequeue and use the temp in its place. */
gimplify_and_add (expr, pre_p);
*expr_p = tmp;
return ret;
}
/* Make sure the condition has BOOLEAN_TYPE. */
TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0));
/* 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 (expr != *expr_p)
{
*expr_p = expr;
/* We can't rely on gimplify_expr to re-gimplify the expanded
form properly, as cleanups might cause the target labels to be
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;
}
}
/* Now do the normal gimplification. */
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL,
is_gimple_condexpr, fb_rvalue);
gimple_push_condition ();
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);
if (ret == GS_ERROR)
;
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_OPERAND (expr, 0) = invert_truthvalue (TREE_OPERAND (expr, 0));
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL,
is_gimple_condexpr, fb_rvalue);
tmp = TREE_OPERAND (expr, 1);
TREE_OPERAND (expr, 1) = TREE_OPERAND (expr, 2);
TREE_OPERAND (expr, 2) = tmp;
}
else
/* Both arms are empty; replace the COND_EXPR with its predicate. */
expr = TREE_OPERAND (expr, 0);
*expr_p = expr;
return ret;
}
/* A subroutine of gimplify_modify_expr. Replace a MODIFY_EXPR with
a call to __builtin_memcpy. */
static enum gimplify_status
gimplify_modify_expr_to_memcpy (tree *expr_p, bool want_value)
{
tree args, t, to, to_ptr, from;
to = TREE_OPERAND (*expr_p, 0);
from = TREE_OPERAND (*expr_p, 1);
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);
t = build_fold_addr_expr (from);
args = tree_cons (NULL, t, args);
to_ptr = build_fold_addr_expr (to);
args = tree_cons (NULL, to, 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 (to_ptr), t);
t = build1 (INDIRECT_REF, TREE_TYPE (to), t);
}
*expr_p = t;
return GS_OK;
}
/* A subroutine of gimplify_modify_expr. Replace a MODIFY_EXPR with
a call to __builtin_memset. In this case we know that the RHS is
a CONSTRUCTOR with an empty element list. */
static enum gimplify_status
gimplify_modify_expr_to_memset (tree *expr_p, bool want_value)
{
tree args, t, to, to_ptr;
to = TREE_OPERAND (*expr_p, 0);
t = TYPE_SIZE_UNIT (TREE_TYPE (to));
t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, to);
t = unshare_expr (t);
args = tree_cons (NULL, t, NULL);
args = tree_cons (NULL, integer_zero_node, args);
to_ptr = build_fold_addr_expr (to);
args = tree_cons (NULL, to, args);
t = implicit_built_in_decls[BUILT_IN_MEMSET];
t = build_function_call_expr (t, args);
if (want_value)
{
t = build1 (NOP_EXPR, TREE_TYPE (to_ptr), t);
t = build1 (INDIRECT_REF, TREE_TYPE (to), t);
}
*expr_p = t;
return GS_OK;
}
/* A subroutine of gimplify_modify_expr. Break out elements of a
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 object = TREE_OPERAND (*expr_p, 0);
tree ctor = TREE_OPERAND (*expr_p, 1);
tree type = TREE_TYPE (ctor);
enum gimplify_status ret;
tree elt_list;
if (TREE_CODE (ctor) != CONSTRUCTOR)
return GS_UNHANDLED;
elt_list = CONSTRUCTOR_ELTS (ctor);
ret = GS_ALL_DONE;
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;
HOST_WIDE_INT num_nonconstant_elements;
bool cleared;
/* Aggregate types must lower constructors to initialization of
individual elements. The exception is that a CONSTRUCTOR node
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,
&num_nonconstant_elements);
num_elements = count_type_elements (TREE_TYPE (ctor));
/* If a const aggregate variable is being initialized, then it
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
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);
*expr_p = NULL_TREE;
break;
}
/* If there are "lots" of initialized elements, and all of them
are valid address constants, then the entire initializer can
be dropped to memory, and then memcpy'd out. */
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
from the C++ front end. This seems wrong, but attempt
to work around it for now. */
if (size < 0)
{
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. */
/* ??? Make use of DECL_OFFSET_ALIGN. */
if (DECL_P (object))
align = DECL_ALIGN (object);
else
align = TYPE_ALIGN (type);
if (size > 0 && !can_move_by_pieces (size, align))
{
tree new = create_tmp_var_raw (type, "C");
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);
TREE_OPERAND (*expr_p, 1) = new;
break;
}
}
/* 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. */
/* If there are "lots" of zeros, then block clear the object first. */
cleared = false;
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
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
{
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)
{
/* Zap the CONSTRUCTOR element list, which simplifies this case.
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);
}
for (i = 0; elt_list; i++, elt_list = TREE_CHAIN (elt_list))
{
tree purpose, value, cref, init;
purpose = TREE_PURPOSE (elt_list);
value = TREE_VALUE (elt_list);
if (cleared && initializer_zerop (value))
continue;
if (TREE_CODE (type) == ARRAY_TYPE)
{
tree t = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (object)));
/* ??? Here's to hoping the front end fills in all of the
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);
}
else
cref = build (COMPONENT_REF, TREE_TYPE (purpose), object,
purpose, NULL_TREE);
init = build (MODIFY_EXPR, TREE_TYPE (purpose), cref, value);
/* Each member initialization is a full-expression. */
gimplify_and_add (init, pre_p);
}
*expr_p = NULL_TREE;
}
break;
case COMPLEX_TYPE:
{
tree r, i;
/* Extract the real and imaginary parts out of the ctor. */
r = i = NULL_TREE;
if (elt_list)
{
r = TREE_VALUE (elt_list);
elt_list = TREE_CHAIN (elt_list);
if (elt_list)
{
i = TREE_VALUE (elt_list);
if (TREE_CHAIN (elt_list))
abort ();
}
}
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
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
{
ctor = build (COMPLEX_EXPR, type, r, i);
TREE_OPERAND (*expr_p, 1) = ctor;
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 1), pre_p, post_p,
is_gimple_rhs, fb_rvalue);
}
}
break;
/* If this COND_EXPR has a value, copy the values into a temporary within
the arms. */
else if (! VOID_TYPE_P (type))
{
if (target)
{
tmp = target;
ret = GS_OK;
}
case VECTOR_TYPE:
/* Go ahead and simplify constant constructors to VECTOR_CST. */
if (TREE_CONSTANT (ctor))
TREE_OPERAND (*expr_p, 1) = build_vector (type, elt_list);
else
{
tmp = create_tmp_var (TREE_TYPE (expr), "iftmp");
ret = GS_ALL_DONE;
/* Vector types use CONSTRUCTOR all the way through gimple
compilation as a general initializer. */
for (; elt_list; elt_list = TREE_CHAIN (elt_list))
{
enum gimplify_status tret;
tret = gimplify_expr (&TREE_VALUE (elt_list), pre_p, post_p,
is_gimple_constructor_elt, fb_rvalue);
if (tret == GS_ERROR)
ret = GS_ERROR;
}
}
break;
/* 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));
/* Build the else clause, 't1 = b;'. */
if (TREE_TYPE (TREE_OPERAND (expr, 2)) != void_type_node)
TREE_OPERAND (expr, 2)
= build (MODIFY_EXPR, void_type_node, tmp, TREE_OPERAND (expr, 2));
TREE_TYPE (expr) = void_type_node;
recalculate_side_effects (expr);
/* Move the COND_EXPR to the prequeue and use the temp in its place. */
gimplify_and_add (expr, pre_p);
*expr_p = tmp;
default:
/* So how did we get a CONSTRUCTOR for a scalar type? */
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. */
TREE_OPERAND (expr, 0) = gimple_boolify (TREE_OPERAND (expr, 0));
/* Subroutine of gimplify_modify_expr to do simplifications of MODIFY_EXPRs
based on the code of the RHS. We loop for as long as something changes. */
/* 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);
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;
if (expr != *expr_p)
while (ret != GS_UNHANDLED)
switch (TREE_CODE (*from_p))
{
case TARGET_EXPR:
{
*expr_p = 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
form properly, as cleanups might cause the target labels to be
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);
??? 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);
return GS_ALL_DONE;
if (!VOID_TYPE_P (TREE_TYPE (init)))
{
*from_p = init;
ret = GS_OK;
}
else
ret = GS_UNHANDLED;
}
}
/* Now do the normal gimplification. */
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL,
is_gimple_condexpr, fb_rvalue);
gimple_push_condition ();
break;
gimplify_to_stmt_list (&TREE_OPERAND (expr, 1));
gimplify_to_stmt_list (&TREE_OPERAND (expr, 2));
recalculate_side_effects (expr);
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;
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)
;
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_OPERAND (expr, 0) = invert_truthvalue (TREE_OPERAND (expr, 0));
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL,
is_gimple_condexpr, fb_rvalue);
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;
tmp = TREE_OPERAND (expr, 1);
TREE_OPERAND (expr, 1) = TREE_OPERAND (expr, 2);
TREE_OPERAND (expr, 2) = tmp;
}
else
/* Both arms are empty; replace the COND_EXPR with its predicate. */
expr = TREE_OPERAND (expr, 0);
default:
ret = GS_UNHANDLED;
break;
}
*expr_p = expr;
return ret;
}
/* Gimplify the MODIFY_EXPR node pointed by EXPR_P.
/* Gimplify the MODIFY_EXPR node pointed by EXPR_P.
modify_expr
: varname '=' rhs
......@@ -2628,31 +2770,15 @@ gimplify_modify_expr (tree *expr_p, tree *pre_p, tree *post_p, bool want_value)
return ret;
/* 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
we need to do this before gimplifying any of the operands
if the copy by converting the whole thing to a memcpy/memset.
Note that we need to do this before gimplifying any of the operands
so that we can resolve any PLACEHOLDER_EXPRs in the size. */
if (TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (*to_p))) != INTEGER_CST)
{
tree args, t, dest;
t = TYPE_SIZE_UNIT (TREE_TYPE (*to_p));
t = SUBSTITUTE_PLACEHOLDER_IN_EXPR (t, *to_p);
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;
if (TREE_CODE (*from_p) == CONSTRUCTOR)
return gimplify_modify_expr_to_memset (expr_p, want_value);
else
return gimplify_modify_expr_to_memcpy (expr_p, want_value);
}
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)
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
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