diff options
author | aoliva <aoliva@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-04-05 18:50:34 +0000 |
---|---|---|
committer | aoliva <aoliva@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-04-05 18:50:34 +0000 |
commit | 2e4b8bcd3189ca58920bf5ddcb27ff57acf004e9 (patch) | |
tree | f71b3ed74f78953c54cfd297e8eaa10395a94836 /gcc | |
parent | 2a5151949745f9de0503607eff217a2d786b0add (diff) | |
download | gcc-2e4b8bcd3189ca58920bf5ddcb27ff57acf004e9.tar.gz |
PR middle-end/22156
* tree-sra.c (struct sra_elt): Add in_bitfld_block. Remove
all_no_warning.
(struct sra_walk_fns): Remove use_all parameter from use.
(sra_hash_tree): Handle BIT_FIELD_REFs.
(sra_elt_hash): Don't hash bitfld blocks.
(sra_elt_eq): Skip them in parent compares as well. Handle
BIT_FIELD_REFs.
(sra_walk_expr): Don't maintain or pass down use_all_p.
(scan_use): Remove use_all parameter.
(scalarize_use): Likewise. Re-expand assignment to
BIT_FIELD_REF of gimple_reg. De-scalarize before input or
output, and re-scalarize after output. Don't mark anything
for no warning.
(scalarize_ldst): Adjust.
(scalarize_walk_gimple_modify_statement): Likewise.
(build_element_name_1): Handle BIT_FIELD_REFs.
(instantiate_element): Don't warn for any element whose parent
is used as a whole.
(instantiate_missing_elements_1): Return the sra_elt.
(canon_type_for_field): New.
(try_instantiate_multiple_fields): New.
(instantiate_missing_elemnts): Use them.
(mark_no_warning): Removed.
(generate_one_element_ref): Handle BIT_FIELD_REFs.
(REPLDUP, sra_build_elt_assignment): New.
(generate_copy_inout): Use them.
(generate_element_copy): Likewise. Handle bitfld differences.
(generate_element_zero): Don't recurse for blocks. Use
sra_build_elt_assignment.
(generate_one_element_int): Take elt instead of var. Use
sra_build_elt_assignment.
(generate_element_init_1): Adjust.
(scalarize_use, scalarize_copy): Use REPLDUP.
(scalarize_ldst): Move assert before dereference.
(dump_sra_elt_name): Handle BIT_FIELD_REFs.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@123524 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 39 | ||||
-rw-r--r-- | gcc/tree-sra.c | 583 |
2 files changed, 562 insertions, 60 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 76fc5fc1e81..330c180590a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,42 @@ +2007-04-05 Alexandre Oliva <aoliva@redhat.com> + + PR middle-end/22156 + * tree-sra.c (struct sra_elt): Add in_bitfld_block. Remove + all_no_warning. + (struct sra_walk_fns): Remove use_all parameter from use. + (sra_hash_tree): Handle BIT_FIELD_REFs. + (sra_elt_hash): Don't hash bitfld blocks. + (sra_elt_eq): Skip them in parent compares as well. Handle + BIT_FIELD_REFs. + (sra_walk_expr): Don't maintain or pass down use_all_p. + (scan_use): Remove use_all parameter. + (scalarize_use): Likewise. Re-expand assignment to + BIT_FIELD_REF of gimple_reg. De-scalarize before input or + output, and re-scalarize after output. Don't mark anything + for no warning. + (scalarize_ldst): Adjust. + (scalarize_walk_gimple_modify_statement): Likewise. + (build_element_name_1): Handle BIT_FIELD_REFs. + (instantiate_element): Don't warn for any element whose parent + is used as a whole. + (instantiate_missing_elements_1): Return the sra_elt. + (canon_type_for_field): New. + (try_instantiate_multiple_fields): New. + (instantiate_missing_elemnts): Use them. + (mark_no_warning): Removed. + (generate_one_element_ref): Handle BIT_FIELD_REFs. + (REPLDUP, sra_build_elt_assignment): New. + (generate_copy_inout): Use them. + (generate_element_copy): Likewise. Handle bitfld differences. + (generate_element_zero): Don't recurse for blocks. Use + sra_build_elt_assignment. + (generate_one_element_int): Take elt instead of var. Use + sra_build_elt_assignment. + (generate_element_init_1): Adjust. + (scalarize_use, scalarize_copy): Use REPLDUP. + (scalarize_ldst): Move assert before dereference. + (dump_sra_elt_name): Handle BIT_FIELD_REFs. + 2007-04-05 Steven Bosscher <steven@gcc.gnu.org> * regmove.c: Fix unused variable warnings due to previous commit. diff --git a/gcc/tree-sra.c b/gcc/tree-sra.c index 239fd847969..a73f22d69fc 100644 --- a/gcc/tree-sra.c +++ b/gcc/tree-sra.c @@ -147,6 +147,10 @@ struct sra_elt /* True if there is BIT_FIELD_REF on the lhs with a vector. */ bool is_vector_lhs; + + /* 1 if the element is a field that is part of a block, 2 if the field + is the block itself, 0 if it's neither. */ + char in_bitfld_block; }; #define IS_ELEMENT_FOR_GROUP(ELEMENT) (TREE_CODE (ELEMENT) == RANGE_EXPR) @@ -461,6 +465,12 @@ sra_hash_tree (tree t) h = iterative_hash_expr (DECL_FIELD_BIT_OFFSET (t), h); break; + case BIT_FIELD_REF: + /* Don't take operand 0 into account, that's our parent. */ + h = iterative_hash_expr (TREE_OPERAND (t, 1), 0); + h = iterative_hash_expr (TREE_OPERAND (t, 2), h); + break; + default: gcc_unreachable (); } @@ -479,12 +489,14 @@ sra_elt_hash (const void *x) h = sra_hash_tree (e->element); - /* Take into account everything back up the chain. Given that chain - lengths are rarely very long, this should be acceptable. If we - truly identify this as a performance problem, it should work to - hash the pointer value "e->parent". */ + /* Take into account everything except bitfield blocks back up the + chain. Given that chain lengths are rarely very long, this + should be acceptable. If we truly identify this as a performance + problem, it should work to hash the pointer value + "e->parent". */ for (p = e->parent; p ; p = p->parent) - h = (h * 65521) ^ sra_hash_tree (p->element); + if (!p->in_bitfld_block) + h = (h * 65521) ^ sra_hash_tree (p->element); return h; } @@ -497,8 +509,17 @@ sra_elt_eq (const void *x, const void *y) const struct sra_elt *a = x; const struct sra_elt *b = y; tree ae, be; + const struct sra_elt *ap = a->parent; + const struct sra_elt *bp = b->parent; - if (a->parent != b->parent) + if (ap) + while (ap->in_bitfld_block) + ap = ap->parent; + if (bp) + while (bp->in_bitfld_block) + bp = bp->parent; + + if (ap != bp) return false; ae = a->element; @@ -533,6 +554,11 @@ sra_elt_eq (const void *x, const void *y) return false; return fields_compatible_p (ae, be); + case BIT_FIELD_REF: + return + tree_int_cst_equal (TREE_OPERAND (ae, 1), TREE_OPERAND (be, 1)) + && tree_int_cst_equal (TREE_OPERAND (ae, 2), TREE_OPERAND (be, 2)); + default: gcc_unreachable (); } @@ -671,10 +697,9 @@ struct sra_walk_fns /* Invoked when ELT is required as a unit. Note that ELT might refer to a leaf node, in which case this is a simple scalar reference. *EXPR_P points to the location of the expression. IS_OUTPUT is true if this - is a left-hand-side reference. USE_ALL is true if we saw something we - couldn't quite identify and had to force the use of the entire object. */ + is a left-hand-side reference. */ void (*use) (struct sra_elt *elt, tree *expr_p, - block_stmt_iterator *bsi, bool is_output, bool use_all); + block_stmt_iterator *bsi, bool is_output); /* Invoked when we have a copy between two scalarizable references. */ void (*copy) (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt, @@ -728,7 +753,6 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output, tree expr = *expr_p; tree inner = expr; bool disable_scalarization = false; - bool use_all_p = false; /* We're looking to collect a reference expression between EXPR and INNER, such that INNER is a scalarizable decl and all other nodes through EXPR @@ -749,7 +773,7 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output, if (disable_scalarization) elt->cannot_scalarize = true; else - fns->use (elt, expr_p, bsi, is_output, use_all_p); + fns->use (elt, expr_p, bsi, is_output); } return; @@ -836,7 +860,6 @@ sra_walk_expr (tree *expr_p, block_stmt_iterator *bsi, bool is_output, use_all: expr_p = &TREE_OPERAND (inner, 0); inner = expr = *expr_p; - use_all_p = true; break; default: @@ -884,11 +907,14 @@ sra_walk_asm_expr (tree expr, block_stmt_iterator *bsi, sra_walk_tree_list (ASM_OUTPUTS (expr), bsi, true, fns); } +static void sra_replace (block_stmt_iterator *bsi, tree list); +static tree sra_build_elt_assignment (struct sra_elt *elt, tree src); + /* Walk a GIMPLE_MODIFY_STMT and categorize the assignment appropriately. */ static void sra_walk_gimple_modify_stmt (tree expr, block_stmt_iterator *bsi, - const struct sra_walk_fns *fns) + const struct sra_walk_fns *fns) { struct sra_elt *lhs_elt, *rhs_elt; tree lhs, rhs; @@ -911,7 +937,7 @@ sra_walk_gimple_modify_stmt (tree expr, block_stmt_iterator *bsi, if (!rhs_elt->is_scalar && !TREE_SIDE_EFFECTS (lhs)) fns->ldst (rhs_elt, lhs, bsi, false); else - fns->use (rhs_elt, &GIMPLE_STMT_OPERAND (expr, 1), bsi, false, false); + fns->use (rhs_elt, &GIMPLE_STMT_OPERAND (expr, 1), bsi, false); } /* If it isn't scalarizable, there may be scalarizable variables within, so @@ -958,7 +984,9 @@ sra_walk_gimple_modify_stmt (tree expr, block_stmt_iterator *bsi, /* Otherwise we're being used in some context that requires the aggregate to be seen as a whole. Invoke USE. */ else - fns->use (lhs_elt, &GIMPLE_STMT_OPERAND (expr, 0), bsi, true, false); + { + fns->use (lhs_elt, &GIMPLE_STMT_OPERAND (expr, 0), bsi, true); + } } /* Similarly to above, LHS_ELT being null only means that the LHS as a @@ -1069,7 +1097,7 @@ find_candidates_for_sra (void) static void scan_use (struct sra_elt *elt, tree *expr_p ATTRIBUTE_UNUSED, block_stmt_iterator *bsi ATTRIBUTE_UNUSED, - bool is_output ATTRIBUTE_UNUSED, bool use_all ATTRIBUTE_UNUSED) + bool is_output ATTRIBUTE_UNUSED) { elt->n_uses += 1; } @@ -1177,6 +1205,15 @@ build_element_name_1 (struct sra_elt *elt) sprintf (buffer, HOST_WIDE_INT_PRINT_DEC, TREE_INT_CST_LOW (t)); obstack_grow (&sra_obstack, buffer, strlen (buffer)); } + else if (TREE_CODE (t) == BIT_FIELD_REF) + { + sprintf (buffer, "B" HOST_WIDE_INT_PRINT_DEC, + tree_low_cst (TREE_OPERAND (t, 2), 1)); + obstack_grow (&sra_obstack, buffer, strlen (buffer)); + sprintf (buffer, "F" HOST_WIDE_INT_PRINT_DEC, + tree_low_cst (TREE_OPERAND (t, 1), 1)); + obstack_grow (&sra_obstack, buffer, strlen (buffer)); + } else { tree name = DECL_NAME (t); @@ -1209,9 +1246,12 @@ instantiate_element (struct sra_elt *elt) { struct sra_elt *base_elt; tree var, base; + bool nowarn = TREE_NO_WARNING (elt->element); for (base_elt = elt; base_elt->parent; base_elt = base_elt->parent) - continue; + if (!nowarn) + nowarn = base_elt->parent->n_uses + || TREE_NO_WARNING (base_elt->parent->element); base = base_elt->element; elt->replacement = var = make_rename_temp (elt->type, "SR"); @@ -1240,9 +1280,7 @@ instantiate_element (struct sra_elt *elt) DECL_DEBUG_EXPR_IS_FROM (var) = 1; DECL_IGNORED_P (var) = 0; - TREE_NO_WARNING (var) = TREE_NO_WARNING (base); - if (elt->element && TREE_NO_WARNING (elt->element)) - TREE_NO_WARNING (var) = 1; + TREE_NO_WARNING (var) = nowarn; } else { @@ -1337,7 +1375,7 @@ sum_instantiated_sizes (struct sra_elt *elt, unsigned HOST_WIDE_INT *sizep) static void instantiate_missing_elements (struct sra_elt *elt); -static void +static struct sra_elt * instantiate_missing_elements_1 (struct sra_elt *elt, tree child, tree type) { struct sra_elt *sub = lookup_element (elt, child, type, INSERT); @@ -1348,6 +1386,262 @@ instantiate_missing_elements_1 (struct sra_elt *elt, tree child, tree type) } else instantiate_missing_elements (sub); + return sub; +} + +/* Obtain the canonical type for field F of ELEMENT. */ + +static tree +canon_type_for_field (tree f, tree element) +{ + tree field_type = TREE_TYPE (f); + + /* canonicalize_component_ref() unwidens some bit-field types (not + marked as DECL_BIT_FIELD in C++), so we must do the same, lest we + may introduce type mismatches. */ + if (INTEGRAL_TYPE_P (field_type) + && DECL_MODE (f) != TYPE_MODE (field_type)) + field_type = TREE_TYPE (get_unwidened (build3 (COMPONENT_REF, + field_type, + element, + f, NULL_TREE), + NULL_TREE)); + + return field_type; +} + +/* Look for adjacent fields of ELT starting at F that we'd like to + scalarize as a single variable. Return the last field of the + group. */ + +static tree +try_instantiate_multiple_fields (struct sra_elt *elt, tree f) +{ + unsigned HOST_WIDE_INT align, oalign, word, bit, size, alchk; + enum machine_mode mode; + tree first = f, prev; + tree type, var; + struct sra_elt *block; + + if (!is_sra_scalar_type (TREE_TYPE (f)) + || !host_integerp (DECL_FIELD_OFFSET (f), 1) + || !host_integerp (DECL_FIELD_BIT_OFFSET (f), 1) + || !host_integerp (DECL_SIZE (f), 1) + || lookup_element (elt, f, NULL, NO_INSERT)) + return f; + + /* Taking the alignment of elt->element is not enough, since it + might be just an array index or some such. */ + for (block = elt; block; block = block->parent) + if (DECL_P (block->element)) + { + align = DECL_ALIGN (block->element); + break; + } + gcc_assert (block); + + oalign = DECL_OFFSET_ALIGN (f); + word = tree_low_cst (DECL_FIELD_OFFSET (f), 1); + bit = tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1); + size = tree_low_cst (DECL_SIZE (f), 1); + + if (align > oalign) + align = oalign; + + alchk = align - 1; + alchk = ~alchk; + + if ((bit & alchk) != ((bit + size - 1) & alchk)) + return f; + + /* Find adjacent fields in the same alignment word. */ + + for (prev = f, f = TREE_CHAIN (f); + f && TREE_CODE (f) == FIELD_DECL + && is_sra_scalar_type (TREE_TYPE (f)) + && host_integerp (DECL_FIELD_OFFSET (f), 1) + && host_integerp (DECL_FIELD_BIT_OFFSET (f), 1) + && host_integerp (DECL_SIZE (f), 1) + && (HOST_WIDE_INT)word == tree_low_cst (DECL_FIELD_OFFSET (f), 1) + && !lookup_element (elt, f, NULL, NO_INSERT); + prev = f, f = TREE_CHAIN (f)) + { + unsigned HOST_WIDE_INT nbit, nsize; + + nbit = tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1); + nsize = tree_low_cst (DECL_SIZE (f), 1); + + if (bit + size == nbit) + { + if ((bit & alchk) != ((nbit + nsize - 1) & alchk)) + break; + size += nsize; + } + else if (nbit + nsize == bit) + { + if ((nbit & alchk) != ((bit + size - 1) & alchk)) + break; + bit = nbit; + size += nsize; + } + else + break; + } + + f = prev; + + if (f == first) + return f; + + gcc_assert ((bit & alchk) == ((bit + size - 1) & alchk)); + + /* Try to widen the bit range so as to cover padding bits as well. */ + + if ((bit & ~alchk) || size != align) + { + unsigned HOST_WIDE_INT mbit = bit & alchk; + unsigned HOST_WIDE_INT msize = align; + + for (f = TYPE_FIELDS (elt->type); + f; f = TREE_CHAIN (f)) + { + unsigned HOST_WIDE_INT fword, fbit, fsize; + + /* Skip the fields from first to prev. */ + if (f == first) + { + f = prev; + continue; + } + + if (!(TREE_CODE (f) == FIELD_DECL + && host_integerp (DECL_FIELD_OFFSET (f), 1) + && host_integerp (DECL_FIELD_BIT_OFFSET (f), 1))) + continue; + + fword = tree_low_cst (DECL_FIELD_OFFSET (f), 1); + /* If we're past the selected word, we're fine. */ + if (word < fword) + continue; + + fbit = tree_low_cst (DECL_FIELD_BIT_OFFSET (f), 1); + + if (host_integerp (DECL_SIZE (f), 1)) + fsize = tree_low_cst (DECL_SIZE (f), 1); + else + /* Assume a variable-sized field takes up all space till + the end of the word. ??? Endianness issues? */ + fsize = align - fbit; + + if (fword < word) + { + /* A large field might start at a previous word and + extend into the selected word. Exclude those + bits. ??? Endianness issues? */ + HOST_WIDE_INT diff = fbit + fsize + - (HOST_WIDE_INT)((word - fword) * BITS_PER_UNIT + mbit); + + if (diff <= 0) + continue; + + mbit += diff; + msize -= diff; + } + else + { + gcc_assert (fword == word); + + /* Non-overlapping, great. */ + if (fbit + fsize <= mbit + || mbit + msize <= fbit) + continue; + + if (fbit <= mbit) + { + unsigned HOST_WIDE_INT diff = fbit + fsize - mbit; + mbit += diff; + msize -= diff; + } + else if (fbit > mbit) + msize -= (mbit + msize - fbit); + else + gcc_unreachable (); + } + } + + bit = mbit; + size = msize; + } + + /* Now we know the bit range we're interested in. Find the smallest + machine mode we can use to access it. */ + + for (mode = smallest_mode_for_size (size, MODE_INT); + ; + mode = GET_MODE_WIDER_MODE (mode)) + { + gcc_assert (mode != VOIDmode); + + alchk = GET_MODE_PRECISION (mode) - 1; + alchk = ~alchk; + + if ((bit & alchk) == ((bit + size - 1) & alchk)) + break; + } + + gcc_assert (~alchk < align); + + /* Create the field group as a single variable. */ + + type = lang_hooks.types.type_for_mode (mode, 1); + gcc_assert (type); + var = build3 (BIT_FIELD_REF, type, NULL_TREE, + bitsize_int (size), + bitsize_int (word * BITS_PER_UNIT + bit)); + BIT_FIELD_REF_UNSIGNED (var) = 1; + + block = instantiate_missing_elements_1 (elt, var, type); + gcc_assert (block && block->is_scalar); + + var = block->replacement; + + if (((word * BITS_PER_UNIT + bit) & ~alchk) + || (HOST_WIDE_INT)size != tree_low_cst (DECL_SIZE (var), 1)) + { + block->replacement = build3 (BIT_FIELD_REF, + TREE_TYPE (block->element), var, + bitsize_int (size), + bitsize_int ((word * BITS_PER_UNIT + + bit) & ~alchk)); + BIT_FIELD_REF_UNSIGNED (block->replacement) = 1; + TREE_NO_WARNING (block->replacement) = 1; + } + + block->in_bitfld_block = 2; + + /* Add the member fields to the group, such that they access + portions of the group variable. */ + + for (f = first; f != TREE_CHAIN (prev); f = TREE_CHAIN (f)) + { + tree field_type = canon_type_for_field (f, elt->element); + struct sra_elt *fld = lookup_element (block, f, field_type, INSERT); + + gcc_assert (fld && fld->is_scalar && !fld->replacement); + + fld->replacement = build3 (BIT_FIELD_REF, field_type, var, + DECL_SIZE (f), + bitsize_int + ((word * BITS_PER_UNIT + + (TREE_INT_CST_LOW + (DECL_FIELD_BIT_OFFSET (f)))) + & ~alchk)); + BIT_FIELD_REF_UNSIGNED (fld->replacement) = TYPE_UNSIGNED (field_type); + TREE_NO_WARNING (block->replacement) = 1; + fld->in_bitfld_block = 1; + } + + return prev; } static void @@ -1363,21 +1657,17 @@ instantiate_missing_elements (struct sra_elt *elt) for (f = TYPE_FIELDS (type); f ; f = TREE_CHAIN (f)) if (TREE_CODE (f) == FIELD_DECL) { - tree field_type = TREE_TYPE (f); - - /* canonicalize_component_ref() unwidens some bit-field - types (not marked as DECL_BIT_FIELD in C++), so we - must do the same, lest we may introduce type - mismatches. */ - if (INTEGRAL_TYPE_P (field_type) - && DECL_MODE (f) != TYPE_MODE (field_type)) - field_type = TREE_TYPE (get_unwidened (build3 (COMPONENT_REF, - field_type, - elt->element, - f, NULL_TREE), - NULL_TREE)); - - instantiate_missing_elements_1 (elt, f, field_type); + tree last = try_instantiate_multiple_fields (elt, f); + + if (last != f) + { + f = last; + continue; + } + + instantiate_missing_elements_1 (elt, f, + canon_type_for_field + (f, elt->element)); } break; } @@ -1689,6 +1979,16 @@ generate_one_element_ref (struct sra_elt *elt, tree base) { tree field = elt->element; + /* We can't test elt->in_bitfld_blk here because, when this is + called from instantiate_element, we haven't set this field + yet. */ + if (TREE_CODE (field) == BIT_FIELD_REF) + { + tree ret = copy_node (field); + TREE_OPERAND (ret, 0) = base; + return ret; + } + /* Watch out for compatible records with differing field lists. */ if (DECL_FIELD_CONTEXT (field) != TYPE_MAIN_VARIANT (TREE_TYPE (base))) field = find_compatible_field (TREE_TYPE (base), field); @@ -1741,6 +2041,126 @@ sra_build_assignment (tree dst, tree src) return build_gimple_modify_stmt (dst, src); } +/* BIT_FIELD_REFs must not be shared. sra_build_elt_assignment() + takes care of assignments, but we must create copies for uses. */ +#define REPLDUP(t) (TREE_CODE (t) != BIT_FIELD_REF ? (t) : copy_node (t)) + +static tree +sra_build_elt_assignment (struct sra_elt *elt, tree src) +{ + tree dst = elt->replacement; + tree var, type, tmp, tmp2, tmp3; + tree list, stmt; + tree cst, cst2, mask; + tree minshift, maxshift; + + if (TREE_CODE (dst) != BIT_FIELD_REF + || !elt->in_bitfld_block) + return sra_build_assignment (REPLDUP (dst), src); + + var = TREE_OPERAND (dst, 0); + + /* Try to widen the assignment to the entire variable. + We need the source to be a BIT_FIELD_REF as well, such that, for + BIT_FIELD_REF<d,sz,dp> = BIT_FIELD_REF<s,sz,sp>, + if sp >= dp, we can turn it into + d = BIT_FIELD_REF<s,sp+sz,sp-dp>. */ + if (elt->in_bitfld_block == 2 + && TREE_CODE (src) == BIT_FIELD_REF + && !tree_int_cst_lt (TREE_OPERAND (src, 2), TREE_OPERAND (dst, 2))) + { + src = fold_build3 (BIT_FIELD_REF, TREE_TYPE (var), + TREE_OPERAND (src, 0), + size_binop (PLUS_EXPR, TREE_OPERAND (src, 1), + TREE_OPERAND (dst, 2)), + size_binop (MINUS_EXPR, TREE_OPERAND (src, 2), + TREE_OPERAND (dst, 2))); + BIT_FIELD_REF_UNSIGNED (src) = 1; + + return sra_build_assignment (var, src); + } + + if (!is_gimple_reg (var)) + return sra_build_assignment (REPLDUP (dst), src); + + list = alloc_stmt_list (); + + cst = TREE_OPERAND (dst, 2); + if (WORDS_BIG_ENDIAN) + { + cst = size_binop (MINUS_EXPR, DECL_SIZE (var), cst); + maxshift = cst; + } + else + minshift = cst; + + cst2 = size_binop (PLUS_EXPR, TREE_OPERAND (dst, 1), + TREE_OPERAND (dst, 2)); + if (WORDS_BIG_ENDIAN) + { + cst2 = size_binop (MINUS_EXPR, DECL_SIZE (var), cst2); + minshift = cst2; + } + else + maxshift = cst2; + + type = TREE_TYPE (var); + + mask = build_int_cst_wide (type, 1, 0); + cst = int_const_binop (LSHIFT_EXPR, mask, maxshift, 1); + cst2 = int_const_binop (LSHIFT_EXPR, mask, minshift, 1); + mask = int_const_binop (MINUS_EXPR, cst, cst2, 1); + mask = fold_build1 (BIT_NOT_EXPR, type, mask); + + if (!WORDS_BIG_ENDIAN) + cst2 = TREE_OPERAND (dst, 2); + + tmp = make_rename_temp (type, "SR"); + stmt = build_gimple_modify_stmt (tmp, + fold_build2 (BIT_AND_EXPR, type, + var, mask)); + append_to_statement_list (stmt, &list); + + if (is_gimple_reg (src)) + tmp2 = src; + else + { + tmp2 = make_rename_temp (TREE_TYPE (src), "SR"); + stmt = sra_build_assignment (tmp2, src); + append_to_statement_list (stmt, &list); + } + + if (!TYPE_UNSIGNED (TREE_TYPE (tmp2)) + || TYPE_MAIN_VARIANT (TREE_TYPE (tmp2)) != TYPE_MAIN_VARIANT (type)) + { + tmp3 = make_rename_temp (type, "SR"); + tmp2 = fold_build3 (BIT_FIELD_REF, type, tmp2, TREE_OPERAND (dst, 1), + bitsize_int (0)); + if (TREE_CODE (tmp2) == BIT_FIELD_REF) + BIT_FIELD_REF_UNSIGNED (tmp2) = 1; + stmt = sra_build_assignment (tmp3, tmp2); + append_to_statement_list (stmt, &list); + tmp2 = tmp3; + } + + if (!integer_zerop (minshift)) + { + tmp3 = make_rename_temp (type, "SR"); + stmt = build_gimple_modify_stmt (tmp3, + fold_build2 (LSHIFT_EXPR, type, + tmp2, minshift)); + append_to_statement_list (stmt, &list); + tmp2 = tmp3; + } + + stmt = build_gimple_modify_stmt (var, + fold_build2 (BIT_IOR_EXPR, type, + tmp, tmp2)); + append_to_statement_list (stmt, &list); + + return list; +} + /* Generate a set of assignment statements in *LIST_P to copy all instantiated elements under ELT to or from the equivalent structure rooted at EXPR. COPY_OUT controls the direction of the copy, with @@ -1771,9 +2191,9 @@ generate_copy_inout (struct sra_elt *elt, bool copy_out, tree expr, else if (elt->replacement) { if (copy_out) - t = sra_build_assignment (elt->replacement, expr); + t = sra_build_elt_assignment (elt, expr); else - t = sra_build_assignment (expr, elt->replacement); + t = sra_build_assignment (expr, REPLDUP (elt->replacement)); append_to_statement_list (t, list_p); } else @@ -1798,6 +2218,19 @@ generate_element_copy (struct sra_elt *dst, struct sra_elt *src, tree *list_p) FOR_EACH_ACTUAL_CHILD (dc, dst) { sc = lookup_element (src, dc->element, NULL, NO_INSERT); + if (!sc && dc->in_bitfld_block == 2) + { + struct sra_elt *dcs; + + FOR_EACH_ACTUAL_CHILD (dcs, dc) + { + sc = lookup_element (src, dcs->element, NULL, NO_INSERT); + gcc_assert (sc); + generate_element_copy (dcs, sc, list_p); + } + + continue; + } gcc_assert (sc); generate_element_copy (dc, sc, list_p); } @@ -1808,7 +2241,7 @@ generate_element_copy (struct sra_elt *dst, struct sra_elt *src, tree *list_p) gcc_assert (src->replacement); - t = sra_build_assignment (dst->replacement, src->replacement); + t = sra_build_elt_assignment (dst, REPLDUP (src->replacement)); append_to_statement_list (t, list_p); } } @@ -1829,8 +2262,9 @@ generate_element_zero (struct sra_elt *elt, tree *list_p) return; } - FOR_EACH_ACTUAL_CHILD (c, elt) - generate_element_zero (c, list_p); + if (!elt->in_bitfld_block) + FOR_EACH_ACTUAL_CHILD (c, elt) + generate_element_zero (c, list_p); if (elt->replacement) { @@ -1839,7 +2273,7 @@ generate_element_zero (struct sra_elt *elt, tree *list_p) gcc_assert (elt->is_scalar); t = fold_convert (elt->type, integer_zero_node); - t = sra_build_assignment (elt->replacement, t); + t = sra_build_elt_assignment (elt, t); append_to_statement_list (t, list_p); } } @@ -1848,10 +2282,10 @@ generate_element_zero (struct sra_elt *elt, tree *list_p) Add the result to *LIST_P. */ static void -generate_one_element_init (tree var, tree init, tree *list_p) +generate_one_element_init (struct sra_elt *elt, tree init, tree *list_p) { /* The replacement can be almost arbitrarily complex. Gimplify. */ - tree stmt = sra_build_assignment (var, init); + tree stmt = sra_build_elt_assignment (elt, init); gimplify_and_add (stmt, list_p); } @@ -1880,7 +2314,7 @@ generate_element_init_1 (struct sra_elt *elt, tree init, tree *list_p) { if (elt->replacement) { - generate_one_element_init (elt->replacement, init, list_p); + generate_one_element_init (elt, init, list_p); elt->visited = true; } return result; @@ -2039,7 +2473,7 @@ sra_replace (block_stmt_iterator *bsi, tree list) static void scalarize_use (struct sra_elt *elt, tree *expr_p, block_stmt_iterator *bsi, - bool is_output, bool use_all) + bool is_output) { tree list = NULL, stmt = bsi_stmt (*bsi); @@ -2048,8 +2482,27 @@ scalarize_use (struct sra_elt *elt, tree *expr_p, block_stmt_iterator *bsi, /* If we have a replacement, then updating the reference is as simple as modifying the existing statement in place. */ if (is_output) - mark_all_v_defs (stmt); - *expr_p = elt->replacement; + { + if (TREE_CODE (elt->replacement) == BIT_FIELD_REF + && is_gimple_reg (TREE_OPERAND (elt->replacement, 0)) + && TREE_CODE (stmt) == GIMPLE_MODIFY_STMT + && &GIMPLE_STMT_OPERAND (stmt, 0) == expr_p) + { + tree newstmt = sra_build_elt_assignment + (elt, GIMPLE_STMT_OPERAND (stmt, 1)); + if (TREE_CODE (newstmt) != STATEMENT_LIST) + { + tree list = alloc_stmt_list (); + append_to_statement_list (newstmt, &list); + newstmt = list; + } + sra_replace (bsi, newstmt); + return; + } + + mark_all_v_defs (stmt); + } + *expr_p = REPLDUP (elt->replacement); update_stmt (stmt); } else @@ -2067,17 +2520,23 @@ scalarize_use (struct sra_elt *elt, tree *expr_p, block_stmt_iterator *bsi, This optimization would be most effective if sra_walk_function processed the blocks in dominator order. */ - generate_copy_inout (elt, is_output, generate_element_ref (elt), &list); - if (list == NULL) - return; - mark_all_v_defs (list); - if (is_output) - sra_insert_after (bsi, list); - else + generate_copy_inout (elt, false, generate_element_ref (elt), &list); + if (list) { + mark_all_v_defs (list); sra_insert_before (bsi, list); - if (use_all) - mark_no_warning (elt); + mark_no_warning (elt); + } + + if (is_output) + { + list = NULL; + generate_copy_inout (elt, true, generate_element_ref (elt), &list); + if (list) + { + mark_all_v_defs (list); + sra_insert_after (bsi, list); + } } } } @@ -2101,7 +2560,7 @@ scalarize_copy (struct sra_elt *lhs_elt, struct sra_elt *rhs_elt, gcc_assert (TREE_CODE (stmt) == GIMPLE_MODIFY_STMT); GIMPLE_STMT_OPERAND (stmt, 0) = lhs_elt->replacement; - GIMPLE_STMT_OPERAND (stmt, 1) = rhs_elt->replacement; + GIMPLE_STMT_OPERAND (stmt, 1) = REPLDUP (rhs_elt->replacement); update_stmt (stmt); } else if (lhs_elt->use_block_copy || rhs_elt->use_block_copy) @@ -2243,7 +2702,7 @@ scalarize_ldst (struct sra_elt *elt, tree other, { /* Since ELT is not fully instantiated, we have to leave the block copy in place. Treat this as a USE. */ - scalarize_use (elt, NULL, bsi, is_output, false); + scalarize_use (elt, NULL, bsi, is_output); } else { @@ -2255,8 +2714,8 @@ scalarize_ldst (struct sra_elt *elt, tree other, mark_all_v_defs (stmt); generate_copy_inout (elt, is_output, other, &list); - mark_all_v_defs (list); gcc_assert (list); + mark_all_v_defs (list); /* Preserve EH semantics. */ if (stmt_ends_bb_p (stmt)) @@ -2352,6 +2811,10 @@ dump_sra_elt_name (FILE *f, struct sra_elt *elt) fputc ('.', f); print_generic_expr (f, elt->element, dump_flags); } + else if (TREE_CODE (elt->element) == BIT_FIELD_REF) + fprintf (f, "$B" HOST_WIDE_INT_PRINT_DEC "F" HOST_WIDE_INT_PRINT_DEC, + tree_low_cst (TREE_OPERAND (elt->element, 2), 1), + tree_low_cst (TREE_OPERAND (elt->element, 1), 1)); else if (TREE_CODE (elt->element) == RANGE_EXPR) fprintf (f, "["HOST_WIDE_INT_PRINT_DEC".."HOST_WIDE_INT_PRINT_DEC"]", TREE_INT_CST_LOW (TREE_OPERAND (elt->element, 0)), |