gimplify.c (struct gimplify_init_ctor_preeval_data): New.

* gimplify.c (struct gimplify_init_ctor_preeval_data): New.
        (gimplify_init_ctor_preeval_1): New.
        (gimplify_init_ctor_preeval): New.
        (gimplify_init_ctor_eval): New.
        (gimplify_init_constructor): Use them.  Always gimplify the LHS
        object first.

From-SVN: r85845
This commit is contained in:
Richard Henderson 2004-08-11 20:54:11 -07:00 committed by Richard Henderson
parent 6e40af1ab0
commit 57d1dd8704
2 changed files with 199 additions and 53 deletions

View File

@ -1,3 +1,12 @@
2004-08-12 Richard Henderson <rth@redhat.com>
* gimplify.c (struct gimplify_init_ctor_preeval_data): New.
(gimplify_init_ctor_preeval_1): New.
(gimplify_init_ctor_preeval): New.
(gimplify_init_ctor_eval): New.
(gimplify_init_constructor): Use them. Always gimplify the LHS
object first.
2004-08-12 Ziemowit Laski <zlaski@apple.com>
(This patch is part of merge of objc-improvements-branch

View File

@ -2363,6 +2363,167 @@ gimplify_modify_expr_to_memset (tree *expr_p, tree size, bool want_value)
return GS_OK;
}
/* A subroutine of gimplify_init_ctor_preeval. Called via walk_tree,
determine, cautiously, if a CONSTRUCTOR overlaps the lhs of an
assignment. Returns non-null if we detect a potential overlap. */
struct gimplify_init_ctor_preeval_data
{
/* The base decl of the lhs object. May be NULL, in which case we
have to assume the lhs is indirect. */
tree lhs_base_decl;
/* The alias set of the lhs object. */
int lhs_alias_set;
};
static tree
gimplify_init_ctor_preeval_1 (tree *tp, int *walk_subtrees, void *xdata)
{
struct gimplify_init_ctor_preeval_data *data
= (struct gimplify_init_ctor_preeval_data *) xdata;
tree t = *tp;
/* If we find the base object, obviously we have overlap. */
if (data->lhs_base_decl == t)
return t;
/* If the constructor component is indirect, determine if we have a
potential overlap with the lhs. The only bits of information we
have to go on at this point are addressability and alias sets. */
if (TREE_CODE (t) == INDIRECT_REF
&& (!data->lhs_base_decl || TREE_ADDRESSABLE (data->lhs_base_decl))
&& alias_sets_conflict_p (data->lhs_alias_set, get_alias_set (t)))
return t;
if (DECL_P (t) || TYPE_P (t))
*walk_subtrees = 0;
return NULL;
}
/* A subroutine of gimplify_init_constructor. Pre-evaluate *EXPR_P,
force values that overlap with the lhs (as described by *DATA)
into temporaries. */
static void
gimplify_init_ctor_preeval (tree *expr_p, tree *pre_p, tree *post_p,
struct gimplify_init_ctor_preeval_data *data)
{
enum gimplify_status one;
/* If the value is invariant, then there's nothing to pre-evaluate.
But ensure it doesn't have any side-effects since a SAVE_EXPR is
invariant but has side effects and might contain a reference to
the object we're initializing. */
if (TREE_INVARIANT (*expr_p) && !TREE_SIDE_EFFECTS (*expr_p))
return;
/* If the type has non-trivial constructors, we can't pre-evaluate. */
if (TREE_ADDRESSABLE (TREE_TYPE (*expr_p)))
return;
/* Recurse for nested constructors. */
if (TREE_CODE (*expr_p) == CONSTRUCTOR)
{
tree list;
for (list = CONSTRUCTOR_ELTS (*expr_p); list ; list = TREE_CHAIN (list))
gimplify_init_ctor_preeval (&TREE_VALUE (list), pre_p, post_p, data);
return;
}
/* We can't preevaluate if the type contains a placeholder. */
if (type_contains_placeholder_p (TREE_TYPE (*expr_p)))
return;
/* Gimplify the constructor element to something appropriate for the rhs
of a MODIFY_EXPR. Given that we know the lhs is an aggregate, we know
the gimplifier will consider this a store to memory. Doing this
gimplification now means that we won't have to deal with complicated
language-specific trees, nor trees like SAVE_EXPR that can induce
exponential search behaviour. */
one = gimplify_expr (expr_p, pre_p, post_p, is_gimple_mem_rhs, fb_rvalue);
if (one == GS_ERROR)
{
*expr_p = NULL;
return;
}
/* If we gimplified to a bare decl, we can be sure that it doesn't overlap
with the lhs, since "a = { .x=a }" doesn't make sense. This will
always be true for all scalars, since is_gimple_mem_rhs insists on a
temporary variable for them. */
if (DECL_P (*expr_p))
return;
/* If this is of variable size, we have no choice but to assume it doesn't
overlap since we can't make a temporary for it. */
if (!TREE_CONSTANT (TYPE_SIZE (TREE_TYPE (*expr_p))))
return;
/* Otherwise, we must search for overlap ... */
if (!walk_tree (expr_p, gimplify_init_ctor_preeval_1, data, NULL))
return;
/* ... and if found, force the value into a temporary. */
*expr_p = get_formal_tmp_var (*expr_p, pre_p);
}
/* A subroutine of gimplify_init_constructor. Generate individual
MODIFY_EXPRs for a CONSTRUCTOR. OBJECT is the LHS against which the
assignments should happen. LIST is the CONSTRUCTOR_ELTS of the
CONSTRUCTOR. CLEARED is true if the entire LHS object has been
zeroed first. */
static void
gimplify_init_ctor_eval (tree object, tree list, tree *pre_p, bool cleared)
{
tree array_elt_type = NULL;
if (TREE_CODE (TREE_TYPE (object)) == ARRAY_TYPE)
array_elt_type = TYPE_MAIN_VARIANT (TREE_TYPE (TREE_TYPE (object)));
for (; list; list = TREE_CHAIN (list))
{
tree purpose, value, cref, init;
purpose = TREE_PURPOSE (list);
value = TREE_VALUE (list);
/* NULL values are created above for gimplification errors. */
if (value == NULL)
continue;
if (cleared && initializer_zerop (value))
continue;
if (array_elt_type)
{
/* ??? 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, array_elt_type, unshare_expr (object),
purpose, NULL_TREE, NULL_TREE);
}
else
cref = build (COMPONENT_REF, TREE_TYPE (purpose),
unshare_expr (object), purpose, NULL_TREE);
if (TREE_CODE (value) == CONSTRUCTOR)
gimplify_init_ctor_eval (cref, CONSTRUCTOR_ELTS (value),
pre_p, cleared);
else
{
init = build (MODIFY_EXPR, TREE_TYPE (cref), cref, value);
gimplify_and_add (init, pre_p);
}
}
}
/* A subroutine of gimplify_modify_expr. Break out elements of a
CONSTRUCTOR used as an initializer into separate MODIFY_EXPRs.
@ -2374,7 +2535,7 @@ 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 object;
tree ctor = TREE_OPERAND (*expr_p, 1);
tree type = TREE_TYPE (ctor);
enum gimplify_status ret;
@ -2383,6 +2544,12 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
if (TREE_CODE (ctor) != CONSTRUCTOR)
return GS_UNHANDLED;
ret = gimplify_expr (&TREE_OPERAND (*expr_p, 0), pre_p, post_p,
is_gimple_lvalue, fb_lvalue);
if (ret == GS_ERROR)
return ret;
object = TREE_OPERAND (*expr_p, 0);
elt_list = CONSTRUCTOR_ELTS (ctor);
ret = GS_ALL_DONE;
@ -2393,7 +2560,8 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
case QUAL_UNION_TYPE:
case ARRAY_TYPE:
{
HOST_WIDE_INT i, num_elements, num_nonzero_elements;
struct gimplify_init_ctor_preeval_data preeval_data;
HOST_WIDE_INT num_elements, num_nonzero_elements;
HOST_WIDE_INT num_nonconstant_elements;
bool cleared;
@ -2401,19 +2569,10 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
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;
}
break;
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. */
@ -2468,6 +2627,7 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
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;
@ -2480,7 +2640,11 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
walk_tree (&DECL_INITIAL (new), force_labels_r, NULL, NULL);
TREE_OPERAND (*expr_p, 1) = new;
break;
/* This is no longer an assignment of a CONSTRUCTOR, but
we still may have processing to do on the LHS. So
pretend we didn't do anything here to let that happen. */
return GS_UNHANDLED;
}
}
@ -2491,6 +2655,8 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
parts in, then generate code for the non-constant parts. */
/* TODO. There's code in cp/typeck.c to do this. */
num_elements = count_type_elements (TREE_TYPE (ctor));
/* If there are "lots" of zeros, then block clear the object first. */
cleared = false;
if (num_elements - num_nonzero_elements > CLEAR_RATIO
@ -2510,60 +2676,31 @@ gimplify_init_constructor (tree *expr_p, tree *pre_p,
tree nelts = array_type_nelts (type);
if (!host_integerp (nelts, 1)
|| tree_low_cst (nelts, 1) + 1 != len)
cleared = 1;;
cleared = true;
}
else if (len != fields_length (type))
cleared = 1;
cleared = true;
}
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. Make an unshared copy of
OBJECT before that so we can match a PLACEHOLDER_EXPR to it
later, if needed. */
case of variable sized types. Avoid shared tree structures. */
CONSTRUCTOR_ELTS (ctor) = NULL_TREE;
object = unshare_expr (TREE_OPERAND (*expr_p, 0));
object = unshare_expr (object);
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;
preeval_data.lhs_base_decl = get_base_address (object);
if (!DECL_P (preeval_data.lhs_base_decl))
preeval_data.lhs_base_decl = NULL;
preeval_data.lhs_alias_set = get_alias_set (object);
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, unshare_expr (object), purpose,
NULL_TREE, NULL_TREE);
}
else
cref = build (COMPONENT_REF, TREE_TYPE (purpose),
unshare_expr (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);
}
gimplify_init_ctor_preeval (&TREE_OPERAND (*expr_p, 1),
pre_p, post_p, &preeval_data);
gimplify_init_ctor_eval (object, elt_list, pre_p, cleared);
*expr_p = NULL_TREE;
}