This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
fix gcc.dg/c99-complit-2.c
- From: Richard Henderson <rth at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Tue, 29 Jun 2004 17:41:44 -0700
- Subject: fix gcc.dg/c99-complit-2.c
Yet more fallout from Kenner's gimplification changes for Ada.
In this case, the change to do the memcpy transformation early
meant that we tried to take the address of a CONSTRUCTOR, which
lead to us trying to instantiate a variable sized temporary.
Fixed by reorganizing things a bit, such that the CONSTRUCTOR
will be known to be empty, which translates to a call to memset.
r~
* 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.
Index: gimplify.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/gimplify.c,v
retrieving revision 2.35
diff -c -p -d -r2.35 gimplify.c
*** gimplify.c 26 Jun 2004 21:11:11 -0000 2.35
--- gimplify.c 30 Jun 2004 00:35:08 -0000
*************** force_labels_r (tree *tp, int *walk_subt
*** 1323,1605 ****
return NULL_TREE;
}
- /* 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)
- {
- CONSTRUCTOR_ELTS (ctor) = NULL_TREE;
- 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;
-
- 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
--- 1323,1328 ----
*************** gimplify_cond_expr (tree *expr_p, tree *
*** 2590,2714 ****
return ret;
}
! /* Gimplify the MODIFY_EXPR node pointed by EXPR_P.
! modify_expr
! : varname '=' rhs
! | '*' ID '=' rhs
! 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
! gimplify_modify_expr (tree *expr_p, tree *pre_p, tree *post_p, bool want_value)
{
! tree *from_p = &TREE_OPERAND (*expr_p, 1);
! tree *to_p = &TREE_OPERAND (*expr_p, 0);
! enum gimplify_status ret = GS_UNHANDLED;
! #if defined ENABLE_CHECKING
! if (TREE_CODE (*expr_p) != MODIFY_EXPR && TREE_CODE (*expr_p) != INIT_EXPR)
! abort ();
! #endif
! /* The distinction between MODIFY_EXPR and INIT_EXPR is no longer useful. */
! if (TREE_CODE (*expr_p) == INIT_EXPR)
! TREE_SET_CODE (*expr_p, MODIFY_EXPR);
! /* See if any simplifications can be done based on what the RHS is. */
! ret = gimplify_modify_expr_rhs (expr_p, from_p, to_p, pre_p, post_p,
! want_value);
! if (ret != GS_UNHANDLED)
! 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
! 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;
}
! ret = gimplify_expr (to_p, pre_p, post_p, is_gimple_lvalue, fb_lvalue);
! if (ret == GS_ERROR)
! return ret;
! ret = gimplify_expr (from_p, pre_p, post_p, is_gimple_rhs, fb_rvalue);
! if (ret == GS_ERROR)
! return ret;
! /* Now see if the above changed *from_p to something we handle specially. */
! ret = gimplify_modify_expr_rhs (expr_p, from_p, to_p, pre_p, post_p,
! want_value);
! if (ret != GS_UNHANDLED)
! return ret;
! /* If the destination is already simple, nothing else needed. */
! if (is_gimple_tmp_var (*to_p))
! ret = GS_ALL_DONE;
! else
{
! /* If the RHS of the MODIFY_EXPR may throw or make a nonlocal goto and
! the LHS is a user variable, then we need to introduce a temporary.
! ie temp = RHS; LHS = temp.
! This way the optimizers can determine that the user variable is
! only modified if evaluation of the RHS does not throw.
! FIXME this should be handled by the is_gimple_rhs predicate. */
! if (aggregate_value_p (TREE_TYPE (*from_p), NULL_TREE))
! /* Don't force a temp of a large aggregate type; the copy could be
! arbitrarily expensive. Instead we will generate a V_MAY_DEF for
! the assignment. */;
! else if (TREE_CODE (*from_p) == CALL_EXPR
! || (flag_non_call_exceptions && tree_could_trap_p (*from_p))
! /* If we're dealing with a renamable type, either source or dest
! must be a renamed variable. */
! || (is_gimple_reg_type (TREE_TYPE (*from_p))
! && !is_gimple_reg (*to_p)))
! gimplify_expr (from_p, pre_p, post_p, is_gimple_val, fb_rvalue);
! ret = want_value ? GS_OK : GS_ALL_DONE;
}
! if (want_value)
{
append_to_statement_list (*expr_p, pre_p);
! *expr_p = *to_p;
}
!
! 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,
--- 2313,2670 ----
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;
!
! 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;
}
! /* 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. */
static enum gimplify_status
gimplify_modify_expr_rhs (tree *expr_p, tree *from_p, tree *to_p, tree *pre_p,
*************** gimplify_modify_expr_rhs (tree *expr_p,
*** 2776,2781 ****
--- 2732,2838 ----
return ret;
}
+ /* Gimplify the MODIFY_EXPR node pointed by EXPR_P.
+
+ modify_expr
+ : varname '=' rhs
+ | '*' ID '=' rhs
+
+ 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
+ gimplify_modify_expr (tree *expr_p, tree *pre_p, tree *post_p, bool want_value)
+ {
+ tree *from_p = &TREE_OPERAND (*expr_p, 1);
+ tree *to_p = &TREE_OPERAND (*expr_p, 0);
+ enum gimplify_status ret = GS_UNHANDLED;
+
+ #if defined ENABLE_CHECKING
+ if (TREE_CODE (*expr_p) != MODIFY_EXPR && TREE_CODE (*expr_p) != INIT_EXPR)
+ abort ();
+ #endif
+
+ /* The distinction between MODIFY_EXPR and INIT_EXPR is no longer useful. */
+ if (TREE_CODE (*expr_p) == INIT_EXPR)
+ TREE_SET_CODE (*expr_p, MODIFY_EXPR);
+
+ /* See if any simplifications can be done based on what the RHS is. */
+ ret = gimplify_modify_expr_rhs (expr_p, from_p, to_p, pre_p, post_p,
+ want_value);
+ if (ret != GS_UNHANDLED)
+ 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/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)
+ {
+ 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);
+ if (ret == GS_ERROR)
+ return ret;
+
+ ret = gimplify_expr (from_p, pre_p, post_p, is_gimple_rhs, fb_rvalue);
+ if (ret == GS_ERROR)
+ return ret;
+
+ /* Now see if the above changed *from_p to something we handle specially. */
+ ret = gimplify_modify_expr_rhs (expr_p, from_p, to_p, pre_p, post_p,
+ want_value);
+ if (ret != GS_UNHANDLED)
+ return ret;
+
+ /* If the destination is already simple, nothing else needed. */
+ if (is_gimple_tmp_var (*to_p))
+ ret = GS_ALL_DONE;
+ else
+ {
+ /* If the RHS of the MODIFY_EXPR may throw or make a nonlocal goto and
+ the LHS is a user variable, then we need to introduce a temporary.
+ ie temp = RHS; LHS = temp.
+
+ This way the optimizers can determine that the user variable is
+ only modified if evaluation of the RHS does not throw.
+
+ FIXME this should be handled by the is_gimple_rhs predicate. */
+
+ if (aggregate_value_p (TREE_TYPE (*from_p), NULL_TREE))
+ /* Don't force a temp of a large aggregate type; the copy could be
+ arbitrarily expensive. Instead we will generate a V_MAY_DEF for
+ the assignment. */;
+ else if (TREE_CODE (*from_p) == CALL_EXPR
+ || (flag_non_call_exceptions && tree_could_trap_p (*from_p))
+ /* If we're dealing with a renamable type, either source or dest
+ must be a renamed variable. */
+ || (is_gimple_reg_type (TREE_TYPE (*from_p))
+ && !is_gimple_reg (*to_p)))
+ gimplify_expr (from_p, pre_p, post_p, is_gimple_val, fb_rvalue);
+
+ ret = want_value ? GS_OK : GS_ALL_DONE;
+ }
+
+ if (want_value)
+ {
+ append_to_statement_list (*expr_p, pre_p);
+ *expr_p = *to_p;
+ }
+
+ return ret;
+ }
+
/* Gimplify a comparison between two variable-sized objects. Do this
with a call to BUILT_IN_MEMCMP. */