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,414 +1323,137 @@ force_labels_r (tree *tp, int *walk_subtrees, void *data ATTRIBUTE_UNUSED) ...@@ -1323,414 +1323,137 @@ 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: tree old_type = TREE_TYPE (expr);
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 /* 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. */
CONSTRUCTOR_ELTS (ctor) = NULL_TREE; if (TREE_CODE (*expr_p) == NOP_EXPR || TREE_CODE (*expr_p) == CONVERT_EXPR)
append_to_statement_list (*expr_p, pre_p); {
} STRIP_SIGN_NOPS (TREE_OPERAND (*expr_p, 0));
for (i = 0; elt_list; i++, elt_list = TREE_CHAIN (elt_list)) /* And remove the outermost conversion if it's useless. */
{ if (tree_ssa_useless_type_conversion (*expr_p))
tree purpose, value, cref, init; *expr_p = TREE_OPERAND (*expr_p, 0);
}
purpose = TREE_PURPOSE (elt_list); /* If we still have a conversion at the toplevel,
value = TREE_VALUE (elt_list); 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)) /* If a NOP conversion is changing the type of a COMPONENT_REF
continue; 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) /* If a NOP conversion is changing a pointer to array of foo
{ to a pointer to foo, embed that change in the ADDR_EXPR. */
tree t = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (object))); else if (TREE_CODE (sub) == ADDR_EXPR)
canonicalize_addr_expr (expr_p);
/* ??? 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);
}
return GS_OK; return GS_OK;
} }
...@@ -2490,107 +2213,526 @@ gimplify_cond_expr (tree *expr_p, tree *pre_p, tree target) ...@@ -2490,107 +2213,526 @@ gimplify_cond_expr (tree *expr_p, tree *pre_p, tree target)
tree tmp, type; tree tmp, type;
enum gimplify_status ret; enum gimplify_status ret;
type = TREE_TYPE (expr); type = TREE_TYPE (expr);
if (!type) if (!type)
TREE_TYPE (expr) = void_type_node; 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 case VECTOR_TYPE:
the arms. */ /* Go ahead and simplify constant constructors to VECTOR_CST. */
else if (! VOID_TYPE_P (type)) if (TREE_CONSTANT (ctor))
{ TREE_OPERAND (*expr_p, 1) = build_vector (type, elt_list);
if (target)
{
tmp = target;
ret = GS_OK;
}
else else
{ {
tmp = create_tmp_var (TREE_TYPE (expr), "iftmp"); /* Vector types use CONSTRUCTOR all the way through gimple
ret = GS_ALL_DONE; 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 default:
if this branch is void; in C++ it can be, if it's a throw. */ /* So how did we get a CONSTRUCTOR for a scalar type? */
if (TREE_TYPE (TREE_OPERAND (expr, 1)) != void_type_node) abort ();
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; 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))
{
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 ??? 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. */
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)); case COMPOUND_EXPR:
gimplify_to_stmt_list (&TREE_OPERAND (expr, 2)); /* Remove any COMPOUND_EXPR in the RHS so the following cases will be
recalculate_side_effects (expr); 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) 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" */ {
{ *expr_p = *from_p;
TREE_OPERAND (expr, 0) = invert_truthvalue (TREE_OPERAND (expr, 0)); return gimplify_cond_expr (expr_p, pre_p, *to_p);
ret = gimplify_expr (&TREE_OPERAND (expr, 0), pre_p, NULL, }
is_gimple_condexpr, fb_rvalue); else
ret = GS_UNHANDLED;
break;
tmp = TREE_OPERAND (expr, 1); default:
TREE_OPERAND (expr, 1) = TREE_OPERAND (expr, 2); ret = GS_UNHANDLED;
TREE_OPERAND (expr, 2) = tmp; break;
} }
else
/* Both arms are empty; replace the COND_EXPR with its predicate. */
expr = TREE_OPERAND (expr, 0);
*expr_p = expr;
return ret; return ret;
} }
/* Gimplify the MODIFY_EXPR node pointed by EXPR_P. /* Gimplify the MODIFY_EXPR node pointed by EXPR_P.
modify_expr modify_expr
: varname '=' rhs : varname '=' rhs
...@@ -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