From 26d44ae2fd4b65d6e256a3c4a0800b9ebd52f6ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Jun 2004 17:38:18 -0700 Subject: [PATCH] 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 --- gcc/ChangeLog | 10 + gcc/gimplify.c | 1163 +++++++++++++++++++++++++----------------------- 2 files changed, 620 insertions(+), 553 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 2851a5d929f..441759fc88f 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,13 @@ +2004-06-29 Richard Henderson + + * 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 * web.c (union_defs): use all defs of an instruction to create a diff --git a/gcc/gimplify.c b/gcc/gimplify.c index 7ef88b565b3..c596ad90428 100644 --- a/gcc/gimplify.c +++ b/gcc/gimplify.c @@ -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 . 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 . 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. */ -- 2.30.2