From 35b4d3a644222b7bd69b3a1e9c00e78f3dbf3eba Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Mon, 9 Jul 2018 20:33:48 +0000 Subject: [PATCH] PR middle-end/77357 - strlen of constant strings not folded gcc/ChangeLog: PR middle-end/77357 PR middle-end/86428 * builtins.c (c_strlen): Avoid out-of-bounds warnings when accessing implicitly initialized array elements. * expr.c (string_constant): Handle string initializers of character arrays within aggregates. * gimple-fold.c (fold_array_ctor_reference): Add argument. Store element offset. As a special case, handle zero size. (fold_nonarray_ctor_reference): Same. (fold_ctor_reference): Add argument. Store subobject offset. * gimple-fold.h (fold_ctor_reference): Add argument. gcc/testsuite/ChangeLog: PR middle-end/77357 * gcc.dg/strlenopt-49.c: New test. * gcc.dg/strlenopt-50.c: New test. * gcc.dg/strlenopt-51.c: New test. * gcc.dg/strlenopt-52.c: New test. From-SVN: r262522 --- gcc/ChangeLog | 14 + gcc/builtins.c | 12 +- gcc/expr.c | 193 +++++++----- gcc/fold-const.c | 58 +++- gcc/fold-const.h | 3 +- gcc/gimple-fold.c | 145 ++++++--- gcc/gimple-fold.h | 4 +- gcc/testsuite/ChangeLog | 9 + .../gcc.c-torture/execute/builtins/strlen-3.c | 12 +- gcc/testsuite/gcc.dg/strlenopt-49.c | 53 ++++ gcc/testsuite/gcc.dg/strlenopt-50.c | 116 +++++++ gcc/testsuite/gcc.dg/strlenopt-51.c | 121 ++++++++ gcc/testsuite/gcc.dg/strlenopt-52.c | 288 ++++++++++++++++++ 13 files changed, 885 insertions(+), 143 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/strlenopt-49.c create mode 100644 gcc/testsuite/gcc.dg/strlenopt-50.c create mode 100644 gcc/testsuite/gcc.dg/strlenopt-51.c create mode 100644 gcc/testsuite/gcc.dg/strlenopt-52.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 706dd47af20..dda22d8e180 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,17 @@ +2018-07-09 Martin Sebor + + PR middle-end/77357 + PR middle-end/86428 + * builtins.c (c_strlen): Avoid out-of-bounds warnings when + accessing implicitly initialized array elements. + * expr.c (string_constant): Handle string initializers of + character arrays within aggregates. + * gimple-fold.c (fold_array_ctor_reference): Add argument. + Store element offset. As a special case, handle zero size. + (fold_nonarray_ctor_reference): Same. + (fold_ctor_reference): Add argument. Store subobject offset. + * gimple-fold.h (fold_ctor_reference): Add argument. + 2018-07-09 Paul Koning * config/pdp11/pdp11.c (pdp11_addr_cost): New function. diff --git a/gcc/builtins.c b/gcc/builtins.c index 91658e84761..820d6c262b0 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -602,8 +602,15 @@ c_strlen (tree src, int only_value) = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (src)))); /* Set MAXELTS to sizeof (SRC) / sizeof (*SRC) - 1, the maximum possible - length of SRC. */ - unsigned maxelts = TREE_STRING_LENGTH (src) / eltsize - 1; + length of SRC. Prefer TYPE_SIZE() to TREE_STRING_LENGTH() if possible + in case the latter is less than the size of the array. */ + HOST_WIDE_INT maxelts = TREE_STRING_LENGTH (src); + tree type = TREE_TYPE (src); + if (tree size = TYPE_SIZE_UNIT (type)) + if (tree_fits_shwi_p (size)) + maxelts = tree_to_uhwi (size); + + maxelts = maxelts / eltsize - 1; /* PTR can point to the byte representation of any string type, including char* and wchar_t*. */ @@ -629,7 +636,6 @@ c_strlen (tree src, int only_value) what he gets. Subtract the offset from the length of the string, and return that. This would perhaps not be valid if we were dealing with named arrays in addition to literal string constants. */ - return size_diffop_loc (loc, size_int (maxelts * eltsize), byteoff); } diff --git a/gcc/expr.c b/gcc/expr.c index 56751df8431..797b1f5d794 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -54,11 +54,13 @@ along with GCC; see the file COPYING3. If not see #include "reload.h" #include "langhooks.h" #include "common/common-target.h" +#include "tree-dfa.h" #include "tree-ssa-live.h" #include "tree-outof-ssa.h" #include "tree-ssa-address.h" #include "builtins.h" #include "ccmp.h" +#include "gimple-fold.h" #include "rtx-vector-builder.h" @@ -11267,61 +11269,45 @@ is_aligning_offset (const_tree offset, const_tree exp) } /* Return the tree node if an ARG corresponds to a string constant or zero - if it doesn't. If we return nonzero, set *PTR_OFFSET to the offset - in bytes within the string that ARG is accessing. The type of the - offset will be `sizetype'. */ + if it doesn't. If we return nonzero, set *PTR_OFFSET to the (possibly + non-constant) offset in bytes within the string that ARG is accessing. + The type of the offset is sizetype. */ tree string_constant (tree arg, tree *ptr_offset) { - tree array, offset, lower_bound; + tree array; STRIP_NOPS (arg); + /* Non-constant index into the character array in an ARRAY_REF + expression or null. */ + tree varidx = NULL_TREE; + + poly_int64 base_off = 0; + if (TREE_CODE (arg) == ADDR_EXPR) { - if (TREE_CODE (TREE_OPERAND (arg, 0)) == STRING_CST) - { - *ptr_offset = size_zero_node; - return TREE_OPERAND (arg, 0); - } - else if (TREE_CODE (TREE_OPERAND (arg, 0)) == VAR_DECL) - { - array = TREE_OPERAND (arg, 0); - offset = size_zero_node; - } - else if (TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF) + arg = TREE_OPERAND (arg, 0); + tree ref = arg; + if (TREE_CODE (arg) == ARRAY_REF) { - array = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); - offset = TREE_OPERAND (TREE_OPERAND (arg, 0), 1); - if (TREE_CODE (array) != STRING_CST && !VAR_P (array)) - return 0; - - /* Check if the array has a nonzero lower bound. */ - lower_bound = array_ref_low_bound (TREE_OPERAND (arg, 0)); - if (!integer_zerop (lower_bound)) + tree idx = TREE_OPERAND (arg, 1); + if (TREE_CODE (idx) != INTEGER_CST) { - /* If the offset and base aren't both constants, return 0. */ - if (TREE_CODE (lower_bound) != INTEGER_CST) - return 0; - if (TREE_CODE (offset) != INTEGER_CST) - return 0; - /* Adjust offset by the lower bound. */ - offset = size_diffop (fold_convert (sizetype, offset), - fold_convert (sizetype, lower_bound)); + /* Extract the variable index to prevent + get_addr_base_and_unit_offset() from failing due to + it. Use it later to compute the non-constant offset + into the string and return it to the caller. */ + varidx = idx; + ref = TREE_OPERAND (arg, 0); } } - else if (TREE_CODE (TREE_OPERAND (arg, 0)) == MEM_REF) - { - array = TREE_OPERAND (TREE_OPERAND (arg, 0), 0); - offset = TREE_OPERAND (TREE_OPERAND (arg, 0), 1); - if (TREE_CODE (array) != ADDR_EXPR) - return 0; - array = TREE_OPERAND (array, 0); - if (TREE_CODE (array) != STRING_CST && !VAR_P (array)) - return 0; - } - else - return 0; + array = get_addr_base_and_unit_offset (ref, &base_off); + if (!array + || (TREE_CODE (array) != VAR_DECL + && TREE_CODE (array) != CONST_DECL + && TREE_CODE (array) != STRING_CST)) + return NULL_TREE; } else if (TREE_CODE (arg) == PLUS_EXPR || TREE_CODE (arg) == POINTER_PLUS_EXPR) { @@ -11331,62 +11317,107 @@ string_constant (tree arg, tree *ptr_offset) STRIP_NOPS (arg0); STRIP_NOPS (arg1); - if (TREE_CODE (arg0) == ADDR_EXPR - && (TREE_CODE (TREE_OPERAND (arg0, 0)) == STRING_CST - || TREE_CODE (TREE_OPERAND (arg0, 0)) == VAR_DECL)) + if (TREE_CODE (arg0) == ADDR_EXPR) + ; /* Do nothing. */ + else if (TREE_CODE (arg1) == ADDR_EXPR) + std::swap (arg0, arg1); + else + return NULL_TREE; + + tree offset; + if (tree str = string_constant (arg0, &offset)) { - array = TREE_OPERAND (arg0, 0); - offset = arg1; + tree type = TREE_TYPE (arg1); + *ptr_offset = fold_build2 (PLUS_EXPR, type, offset, arg1); + return str; } - else if (TREE_CODE (arg1) == ADDR_EXPR - && (TREE_CODE (TREE_OPERAND (arg1, 0)) == STRING_CST - || TREE_CODE (TREE_OPERAND (arg1, 0)) == VAR_DECL)) + return NULL_TREE; + } + else if (DECL_P (arg)) + array = arg; + else + return NULL_TREE; + + tree offset = wide_int_to_tree (sizetype, base_off); + if (varidx) + { + if (tree eltsize = TYPE_SIZE_UNIT (TREE_TYPE (array))) { - array = TREE_OPERAND (arg1, 0); - offset = arg0; + /* Add the scaled variable index to the constant offset. */ + tree eltoff = fold_build2 (MULT_EXPR, TREE_TYPE (offset), + fold_convert (sizetype, varidx), + eltsize); + offset = fold_build2 (PLUS_EXPR, TREE_TYPE (offset), offset, eltoff); } else - return 0; + return NULL_TREE; } - else - return 0; if (TREE_CODE (array) == STRING_CST) { *ptr_offset = fold_convert (sizetype, offset); return array; } - else if (VAR_P (array) || TREE_CODE (array) == CONST_DECL) - { - int length; - tree init = ctor_for_folding (array); - /* Variables initialized to string literals can be handled too. */ - if (init == error_mark_node - || !init - || TREE_CODE (init) != STRING_CST) - return 0; + if (!VAR_P (array) && TREE_CODE (array) != CONST_DECL) + return NULL_TREE; - /* Avoid const char foo[4] = "abcde"; */ - if (DECL_SIZE_UNIT (array) == NULL_TREE - || TREE_CODE (DECL_SIZE_UNIT (array)) != INTEGER_CST - || (length = TREE_STRING_LENGTH (init)) <= 0 - || compare_tree_int (DECL_SIZE_UNIT (array), length) < 0) - return 0; + tree init = ctor_for_folding (array); - /* If variable is bigger than the string literal, OFFSET must be constant - and inside of the bounds of the string literal. */ - offset = fold_convert (sizetype, offset); - if (compare_tree_int (DECL_SIZE_UNIT (array), length) > 0 - && (! tree_fits_uhwi_p (offset) - || compare_tree_int (offset, length) >= 0)) - return 0; + /* Handle variables initialized with string literals. */ + if (!init || init == error_mark_node) + return NULL_TREE; + if (TREE_CODE (init) == CONSTRUCTOR) + { + if (TREE_CODE (arg) != ARRAY_REF + && TREE_CODE (arg) == COMPONENT_REF + && TREE_CODE (arg) == MEM_REF) + return NULL_TREE; + + /* Convert the 64-bit constant offset to a wider type to avoid + overflow. */ + offset_int wioff; + if (!base_off.is_constant (&wioff)) + return NULL_TREE; - *ptr_offset = offset; - return init; + wioff *= BITS_PER_UNIT; + if (!wi::fits_uhwi_p (wioff)) + return NULL_TREE; + + base_off = wioff.to_uhwi (); + unsigned HOST_WIDE_INT fieldoff = 0; + init = fold_ctor_reference (NULL_TREE, init, base_off, 0, array, + &fieldoff); + HOST_WIDE_INT cstoff; + if (init && base_off.is_constant (&cstoff)) + { + cstoff = (cstoff - fieldoff) / BITS_PER_UNIT; + offset = build_int_cst (sizetype, cstoff); + } } - return 0; + if (!init || TREE_CODE (init) != STRING_CST) + return NULL_TREE; + + tree array_size = DECL_SIZE_UNIT (array); + if (!array_size || TREE_CODE (array_size) != INTEGER_CST) + return NULL_TREE; + + /* Avoid returning a string that doesn't fit in the array + it is stored in, like + const char a[4] = "abcde"; + but do handle those that fit even if they have excess + initializers, such as in + const char a[4] = "abc\000\000"; + The excess elements contribute to TREE_STRING_LENGTH() + but not to strlen(). */ + unsigned HOST_WIDE_INT length + = strnlen (TREE_STRING_POINTER (init), TREE_STRING_LENGTH (init)); + if (compare_tree_int (array_size, length + 1) < 0) + return NULL_TREE; + + *ptr_offset = offset; + return init; } /* Generate code to calculate OPS, and exploded expression diff --git a/gcc/fold-const.c b/gcc/fold-const.c index 5b94c700c81..97c435fa5e0 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -14546,14 +14546,19 @@ fold_build_pointer_plus_hwi_loc (location_t loc, tree ptr, HOST_WIDE_INT off) ptr, size_int (off)); } -/* Return a char pointer for a C string if it is a string constant - or sum of string constant and integer constant. We only support - string constants properly terminated with '\0' character. - If STRLEN is a valid pointer, length (including terminating character) - of returned string is stored to the argument. */ +/* Return a pointer P to a NUL-terminated string representing the sequence + of constant characters referred to by SRC (or a subsequence of such + characters within it if SRC is a reference to a string plus some + constant offset). If STRLEN is non-null, store stgrlen(P) in *STRLEN. + If STRSIZE is non-null, store in *STRSIZE the size of the array + the string is stored in; in that case, even though P points to a NUL + terminated string, SRC need not refer to one. This can happen when + SRC refers to a constant character array initialized to all non-NUL + values, as in the C declaration: char a[4] = "1234"; */ const char * -c_getstr (tree src, unsigned HOST_WIDE_INT *strlen) +c_getstr (tree src, unsigned HOST_WIDE_INT *strlen /* = NULL */, + unsigned HOST_WIDE_INT *strsize /* = NULL */) { tree offset_node; @@ -14573,18 +14578,47 @@ c_getstr (tree src, unsigned HOST_WIDE_INT *strlen) offset = tree_to_uhwi (offset_node); } + /* STRING_LENGTH is the size of the string literal, including any + embedded NULs. STRING_SIZE is the size of the array the string + literal is stored in. */ unsigned HOST_WIDE_INT string_length = TREE_STRING_LENGTH (src); + unsigned HOST_WIDE_INT string_size = string_length; + tree type = TREE_TYPE (src); + if (tree size = TYPE_SIZE_UNIT (type)) + if (tree_fits_shwi_p (size)) + string_size = tree_to_uhwi (size); + + if (strlen) + { + /* Compute and store the length of the substring at OFFSET. + All offsets past the initial length refer to null strings. */ + if (offset <= string_length) + *strlen = string_length - offset; + else + *strlen = 0; + } + const char *string = TREE_STRING_POINTER (src); - /* Support only properly null-terminated strings. */ if (string_length == 0 - || string[string_length - 1] != '\0' - || offset >= string_length) + || offset >= string_size) return NULL; - if (strlen) - *strlen = string_length - offset; - return string + offset; + if (strsize) + { + /* Support even constant character arrays that aren't proper + NUL-terminated strings. */ + *strsize = string_size; + } + else if (string[string_length - 1] != '\0') + { + /* Support only properly NUL-terminated strings but handle + consecutive strings within the same array, such as the six + substrings in "1\0002\0003". */ + return NULL; + } + + return offset <= string_length ? string + offset : ""; } /* Given a tree T, compute which bits in T may be nonzero. */ diff --git a/gcc/fold-const.h b/gcc/fold-const.h index c64b8d0ecf7..4613a62e1f6 100644 --- a/gcc/fold-const.h +++ b/gcc/fold-const.h @@ -183,7 +183,8 @@ extern bool expr_not_equal_to (tree t, const wide_int &); extern tree const_unop (enum tree_code, tree, tree); extern tree const_binop (enum tree_code, tree, tree, tree); extern bool negate_mathfn_p (combined_fn); -extern const char *c_getstr (tree, unsigned HOST_WIDE_INT *strlen = NULL); +extern const char *c_getstr (tree, unsigned HOST_WIDE_INT * = NULL, + unsigned HOST_WIDE_INT * = NULL); extern wide_int tree_nonzero_bits (const_tree); /* Return OFF converted to a pointer offset type suitable as offset for diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 6ce34bfb57c..a6b42834d32 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -6476,14 +6476,19 @@ get_base_constructor (tree base, poly_int64_pod *bit_offset, } } -/* CTOR is CONSTRUCTOR of an array type. Fold reference of type TYPE and size - SIZE to the memory at bit OFFSET. */ +/* CTOR is CONSTRUCTOR of an array type. Fold a reference of SIZE bits + to the memory at bit OFFSET. When non-null, TYPE is the expected + type of the reference; otherwise the type of the referenced element + is used instead. When SIZE is zero, attempt to fold a reference to + the entire element which OFFSET refers to. Increment *SUBOFF by + the bit offset of the accessed element. */ static tree fold_array_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset, unsigned HOST_WIDE_INT size, - tree from_decl) + tree from_decl, + unsigned HOST_WIDE_INT *suboff) { offset_int low_bound; offset_int elt_size; @@ -6508,12 +6513,13 @@ fold_array_ctor_reference (tree type, tree ctor, return NULL_TREE; elt_size = wi::to_offset (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (ctor)))); - /* We can handle only constantly sized accesses that are known to not - be larger than size of array element. */ - if (!TYPE_SIZE_UNIT (type) - || TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST - || elt_size < wi::to_offset (TYPE_SIZE_UNIT (type)) - || elt_size == 0) + /* When TYPE is non-null, verify that it specifies a constant-sized + accessed not larger than size of array element. */ + if (type + && (!TYPE_SIZE_UNIT (type) + || TREE_CODE (TYPE_SIZE_UNIT (type)) != INTEGER_CST + || elt_size < wi::to_offset (TYPE_SIZE_UNIT (type)) + || elt_size == 0)) return NULL_TREE; /* Compute the array index we look for. */ @@ -6529,21 +6535,42 @@ fold_array_ctor_reference (tree type, tree ctor, if (inner_offset + size > elt_size.to_uhwi () * BITS_PER_UNIT) return NULL_TREE; if (tree val = get_array_ctor_element_at_index (ctor, access_index)) - return fold_ctor_reference (type, val, inner_offset, size, from_decl); + { + if (!size && TREE_CODE (val) != CONSTRUCTOR) + { + /* For the final reference to the entire accessed element + (SIZE is zero), reset INNER_OFFSET, disegard TYPE (which + may be null) in favor of the type of the element, and set + SIZE to the size of the accessed element. */ + inner_offset = 0; + type = TREE_TYPE (val); + size = elt_size.to_uhwi () * BITS_PER_UNIT; + } + + *suboff += (access_index * elt_size * BITS_PER_UNIT).to_uhwi (); + return fold_ctor_reference (type, val, inner_offset, size, from_decl, + suboff); + } - /* When memory is not explicitely mentioned in constructor, - it is 0 (or out of range). */ - return build_zero_cst (type); + /* Memory not explicitly mentioned in constructor is 0 (or + the reference is out of range). */ + return type ? build_zero_cst (type) : NULL_TREE; } -/* CTOR is CONSTRUCTOR of an aggregate or vector. - Fold reference of type TYPE and size SIZE to the memory at bit OFFSET. */ +/* CTOR is CONSTRUCTOR of an aggregate or vector. Fold a reference + of SIZE bits to the memory at bit OFFSET. When non-null, TYPE + is the expected type of the reference; otherwise the type of + the referenced member is used instead. When SIZE is zero, + attempt to fold a reference to the entire member which OFFSET + refers to; in this case. Increment *SUBOFF by the bit offset + of the accessed member. */ static tree fold_nonarray_ctor_reference (tree type, tree ctor, unsigned HOST_WIDE_INT offset, unsigned HOST_WIDE_INT size, - tree from_decl) + tree from_decl, + unsigned HOST_WIDE_INT *suboff) { unsigned HOST_WIDE_INT cnt; tree cfield, cval; @@ -6554,8 +6581,13 @@ fold_nonarray_ctor_reference (tree type, tree ctor, tree byte_offset = DECL_FIELD_OFFSET (cfield); tree field_offset = DECL_FIELD_BIT_OFFSET (cfield); tree field_size = DECL_SIZE (cfield); - offset_int bitoffset; - offset_int bitoffset_end, access_end; + + if (!field_size) + { + /* Determine the size of the flexible array member from + the size of the initializer provided for it. */ + field_size = TYPE_SIZE (TREE_TYPE (cval)); + } /* Variable sized objects in static constructors makes no sense, but field_size can be NULL for flexible array members. */ @@ -6566,50 +6598,82 @@ fold_nonarray_ctor_reference (tree type, tree ctor, : TREE_CODE (TREE_TYPE (cfield)) == ARRAY_TYPE)); /* Compute bit offset of the field. */ - bitoffset = (wi::to_offset (field_offset) - + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT)); + offset_int bitoffset + = (wi::to_offset (field_offset) + + (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT)); /* Compute bit offset where the field ends. */ + offset_int bitoffset_end; if (field_size != NULL_TREE) bitoffset_end = bitoffset + wi::to_offset (field_size); else bitoffset_end = 0; - access_end = offset_int (offset) + size; + /* Compute the bit offset of the end of the desired access. + As a special case, if the size of the desired access is + zero, assume the access is to the entire field (and let + the caller make any necessary adjustments by storing + the actual bounds of the field in FIELDBOUNDS). */ + offset_int access_end = offset_int (offset); + if (size) + access_end += size; + else + access_end = bitoffset_end; - /* Is there any overlap between [OFFSET, OFFSET+SIZE) and - [BITOFFSET, BITOFFSET_END)? */ + /* Is there any overlap between the desired access at + [OFFSET, OFFSET+SIZE) and the offset of the field within + the object at [BITOFFSET, BITOFFSET_END)? */ if (wi::cmps (access_end, bitoffset) > 0 && (field_size == NULL_TREE || wi::lts_p (offset, bitoffset_end))) { - offset_int inner_offset = offset_int (offset) - bitoffset; - /* We do have overlap. Now see if field is large enough to - cover the access. Give up for accesses spanning multiple - fields. */ + *suboff += bitoffset.to_uhwi (); + + if (!size && TREE_CODE (cval) != CONSTRUCTOR) + { + /* For the final reference to the entire accessed member + (SIZE is zero), reset OFFSET, disegard TYPE (which may + be null) in favor of the type of the member, and set + SIZE to the size of the accessed member. */ + offset = bitoffset.to_uhwi (); + type = TREE_TYPE (cval); + size = (bitoffset_end - bitoffset).to_uhwi (); + } + + /* We do have overlap. Now see if the field is large enough + to cover the access. Give up for accesses that extend + beyond the end of the object or that span multiple fields. */ if (wi::cmps (access_end, bitoffset_end) > 0) return NULL_TREE; if (offset < bitoffset) return NULL_TREE; + + offset_int inner_offset = offset_int (offset) - bitoffset; return fold_ctor_reference (type, cval, inner_offset.to_uhwi (), size, - from_decl); + from_decl, suboff); } } - /* When memory is not explicitely mentioned in constructor, it is 0. */ - return build_zero_cst (type); + /* Memory not explicitly mentioned in constructor is 0. */ + return type ? build_zero_cst (type) : NULL_TREE; } -/* CTOR is value initializing memory, fold reference of type TYPE and - size POLY_SIZE to the memory at bit POLY_OFFSET. */ +/* CTOR is value initializing memory. Fold a reference of TYPE and + bit size POLY_SIZE to the memory at bit POLY_OFFSET. When SIZE + is zero, attempt to fold a reference to the entire subobject + which OFFSET refers to. This is used when folding accesses to + string members of aggregates. When non-null, set *SUBOFF to + the bit offset of the accessed subobject. */ tree -fold_ctor_reference (tree type, tree ctor, poly_uint64 poly_offset, - poly_uint64 poly_size, tree from_decl) +fold_ctor_reference (tree type, tree ctor, const poly_uint64 &poly_offset, + const poly_uint64 &poly_size, tree from_decl, + unsigned HOST_WIDE_INT *suboff /* = NULL */) { tree ret; /* We found the field with exact match. */ - if (useless_type_conversion_p (type, TREE_TYPE (ctor)) + if (type + && useless_type_conversion_p (type, TREE_TYPE (ctor)) && known_eq (poly_offset, 0U)) return canonicalize_constructor_val (unshare_expr (ctor), from_decl); @@ -6650,14 +6714,17 @@ fold_ctor_reference (tree type, tree ctor, poly_uint64 poly_offset, } if (TREE_CODE (ctor) == CONSTRUCTOR) { + unsigned HOST_WIDE_INT dummy = 0; + if (!suboff) + suboff = &dummy; if (TREE_CODE (TREE_TYPE (ctor)) == ARRAY_TYPE || TREE_CODE (TREE_TYPE (ctor)) == VECTOR_TYPE) return fold_array_ctor_reference (type, ctor, offset, size, - from_decl); - else - return fold_nonarray_ctor_reference (type, ctor, offset, size, - from_decl); + from_decl, suboff); + + return fold_nonarray_ctor_reference (type, ctor, offset, size, + from_decl, suboff); } return NULL_TREE; diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h index 0a28ec7c5a2..04e9bfa0851 100644 --- a/gcc/gimple-fold.h +++ b/gcc/gimple-fold.h @@ -45,7 +45,9 @@ extern tree follow_all_ssa_edges (tree); extern tree gimple_fold_stmt_to_constant_1 (gimple *, tree (*) (tree), tree (*) (tree) = no_follow_ssa_edges); extern tree gimple_fold_stmt_to_constant (gimple *, tree (*) (tree)); -extern tree fold_ctor_reference (tree, tree, poly_uint64, poly_uint64, tree); +extern tree fold_ctor_reference (tree, tree, const poly_uint64&, + const poly_uint64&, tree, + unsigned HOST_WIDE_INT * = NULL); extern tree fold_const_aggregate_ref_1 (tree, tree (*) (tree)); extern tree fold_const_aggregate_ref (tree); extern tree gimple_get_virt_method_for_binfo (HOST_WIDE_INT, tree, diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 6b51bc6439e..48e1f7d2436 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,12 @@ +2018-07-09 Martin Sebor + + PR middle-end/77357 + * gcc.dg/strlenopt-49.c: New test. + * gcc.dg/strlenopt-50.c: New test. + * gcc.dg/strlenopt-51.c: New test. + * gcc.dg/strlenopt-52.c: New test. + * gcc.c-torture/execute/builtins/strlen-3.c: Adjust. + 2018-07-09 Jakub Jelinek PR c/86420 diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strlen-3.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strlen-3.c index 666ca21b94f..8d36fa72cd4 100644 --- a/gcc/testsuite/gcc.c-torture/execute/builtins/strlen-3.c +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strlen-3.c @@ -2,7 +2,10 @@ Test strlen on const variables initialized to string literals. - Written by Jakub Jelinek, 9/14/2004. */ + Written by Jakub Jelinek, 9/14/2004. + + { dg-do compile } + { dg-options "-O2 -Wall -fdump-tree-optimized" } */ extern void abort (void); extern __SIZE_TYPE__ strlen (const char *); @@ -10,7 +13,6 @@ extern char *strcpy (char *, const char *); static const char bar[] = "Hello, World!"; static const char baz[] = "hello, world?"; static const char larger[20] = "short string"; -extern int inside_main; int l1 = 1; int x = 6; @@ -59,12 +61,10 @@ main_test(void) if (strlen (&larger[10]) != 2) abort (); - inside_main = 0; - /* This will result in strlen call, because larger - array is bigger than its initializer. */ if (strlen (larger + (x++ & 7)) != 5) abort (); if (x != 8) abort (); - inside_main = 1; } + +/* { dg-final { scan-tree-dump-not "strlen" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-49.c b/gcc/testsuite/gcc.dg/strlenopt-49.c new file mode 100644 index 00000000000..2e6ccd32f19 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-49.c @@ -0,0 +1,53 @@ +/* PR tree-optimization/86428 - strlen of const array initialized with + a string of the same length not folded + { dg-do compile } + { dg-options "-O0 -Wall -fdump-tree-gimple" } */ + +#include "strlenopt.h" + +const char a1[1] = "\0"; +const char a2[2] = "1\0"; +const char a3[3] = "12\0"; +const char a8[8] = "1234567\0"; +const char a9[9] = "12345678\0"; + +const char ax[9] = "12345678\0\0\0\0"; /* { dg-warning "initializer-string for array of chars is too long" } */ +const char ay[9] = "\00012345678\0\0\0\0"; /* { dg-warning "initializer-string for array of chars is too long" } */ + + +int len1 (void) +{ + size_t len0 = strlen (a1); + return len0; +} + +int len (void) +{ + size_t len = strlen (a2) + strlen (a3) + strlen (a8) + strlen (a9); + return len; +} + +int lenx (void) +{ + size_t lenx = strlen (ax); + return lenx; +} + +int leny (void) +{ + size_t leny = strlen (ay); + return leny; +} + +int cmp88 (void) +{ + int cmp88 = memcmp (a8, "1234567\0", sizeof a8); + return cmp88; +} + +/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } + { dg-final { scan-tree-dump-times "len0 = 0;" 1 "gimple" } } + { dg-final { scan-tree-dump-times "len = 18;" 1 "gimple" } } + { dg-final { scan-tree-dump-times "lenx = 8;" 1 "gimple" } } + { dg-final { scan-tree-dump-times "leny = 0;" 1 "gimple" } } + { dg-final { scan-tree-dump-times "cmp88 = 0;" 1 "gimple" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-50.c b/gcc/testsuite/gcc.dg/strlenopt-50.c new file mode 100644 index 00000000000..1d1d36808a9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-50.c @@ -0,0 +1,116 @@ +/* PR tree-optimization/86415 - strlen() not folded for substrings + within constant arrays + { dg-do compile } + { dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-ccp" } */ + +#include "strlenopt.h" + +#define CONCAT(x, y) x ## y +#define CAT(x, y) CONCAT (x, y) +#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__) + +#define FAIL(name) do { \ + extern void FAILNAME (name) (void); \ + FAILNAME (name)(); \ + } while (0) + +/* Macro to emit a call to funcation named + call_in_true_branch_not_eliminated_on_line_NNN() + for each call that's expected to be eliminated. The dg-final + scan-tree-dump-time directive at the bottom of the test verifies + that no such call appears in output. */ +#define ELIM(expr) \ + if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0 + +#define T(s, n) ELIM (strlen (s) == n) + +/* 11111 + 0 1 23 4 567 8 901234 */ +#define STR "1\00012\000123\0001234\0" + +const char a[] = STR; +const char b[20] = STR; + +void test_literal (void) +{ + /* Verify that strlen() of substrings within a string literal are + correctly folded. */ + T (STR, 1); T (STR + 1, 0); T (STR + 2, 2); T (STR + 3, 1); + T (STR + 4, 0); T (STR + 5, 3); T (STR + 6, 2); T (STR + 7, 1); + T (STR + 8, 0); T (STR + 9, 4); T (STR + 10, 3); T (STR + 11, 2); + T (STR + 12, 1); T (STR + 13, 0); T (STR + 14, 0); + + T (&(STR[0]), 1); T (&(STR[ 1]), 0); T (&(STR[ 2]), 2); + T (&(STR[ 3]), 1); T (&(STR[ 4]), 0); T (&(STR[ 5]), 3); + T (&(STR[ 6]), 2); T (&(STR[ 7]), 1); T (&(STR[ 8]), 0); + T (&(STR[ 9]), 4); T (&(STR[10]), 3); T (&(STR[11]), 2); + T (&(STR[12]), 1); T (&(STR[13]), 0); T (&(STR[14]), 0); + + T (&(STR[0]) + 1, 0); T (&(STR[ 1]) + 1, 2); T (&(STR[ 2]) + 1, 1); + T (&(STR[ 3]) + 1, 0); T (&(STR[ 4]) + 1, 3); T (&(STR[ 5]) + 1, 2); + T (&(STR[ 6]) + 1, 1); T (&(STR[ 7]) + 1, 0); T (&(STR[ 8]) + 1, 4); + T (&(STR[ 9]) + 1, 3); T (&(STR[10]) + 1, 2); T (&(STR[11]) + 1, 1); + T (&(STR[12]) + 1, 0); T (&(STR[13]) + 1, 0); T (&(STR[13]) - 13, 1); + T (&(STR[13]) - 12, 0); T (&(STR[13]) - 11, 2); T (&(STR[13]) - 10, 1); +} + +void test_array (void) +{ + /* Verify that strlen() of substrings within a fully initialized + array are correctly folded. */ + T (a, 1); T (a + 1, 0); T (a + 2, 2); T (a + 3, 1); + T (a + 4, 0); T (a + 5, 3); T (a + 6, 2); T (a + 7, 1); + T (a + 8, 0); T (a + 9, 4); T (a + 10, 3); T (a + 11, 2); + T (a + 12, 1); T (a + 13, 0); T (a + 14, 0); + + /* Verify that strlen() of substrings within a partially initialized + array are also correctly folded, including those referring to + the empty substrings in the implicitly initialized elements. */ + T (b, 1); T (b + 1, 0); T (b + 2, 2); T (b + 3, 1); + T (b + 4, 0); T (b + 5, 3); T (b + 6, 2); T (b + 7, 1); + T (b + 8, 0); T (b + 9, 4); T (b + 10, 3); T (b + 11, 2); + T (b + 12, 1); T (b + 13, 0); T (b + 14, 0); T (b + 15, 0); + T (b + 16, 0); T (b + 17, 0); T (b + 18, 0); T (b + 19, 0); +} + +void test_array_ref_plus (void) +{ + /* Verify that strlen() of substrings within a fully initialized + array referred to by array indices with offsets are correctly + folded. */ + T (&a[ 0], 1); T (&a[ 0] + 1, 0); + T (&a[ 1], 0); T (&a[ 1] + 1, 2); + T (&a[ 2], 2); T (&a[ 2] + 1, 1); T (&a[ 2] + 2, 0); + T (&a[ 3], 1); T (&a[ 3] + 1, 0); + T (&a[ 4], 0); T (&a[ 4] + 1, 3); + T (&a[ 5], 3); T (&a[ 5] + 1, 2); + T (&a[ 5] + 2, 1); T (&a[ 5] + 3, 0); T (&a[ 5] + 4, 4); + T (&a[ 6], 2); T (&a[ 6] + 1, 1); T (&a[ 6] + 2, 0); + T (&a[ 7], 1); T (&a[ 7] + 1, 0); + T (&a[ 8], 0); T (&a[ 8] + 1, 4); + T (&a[ 9], 4); T (&a[ 9] + 1, 3); T (&a[ 9] + 2, 2); + T (&a[ 9] + 3, 1); T (&a[ 9] + 4, 0); T (&a[ 9] + 5, 0); + T (&a[10], 3); T (&a[10] + 1, 2); T (&a[10] + 2, 1); + T (&a[10] + 3, 0); T (&a[10] + 4, 0); + T (&a[11], 2); T (&a[11] + 1, 1); T (&a[11] + 2, 0); + T (&a[12], 1); T (&a[12] + 1, 0); T (&a[12] + 2, 0); + T (&a[13], 0); T (&a[13] + 1, 0); + T (&a[14], 0); +} + +void test_array_ref (void) +{ + T (&a[ 0], 1); T (&a[ 1], 0); T (&a[ 2], 2); T (&a[ 3], 1); + T (&a[ 4], 0); T (&a[ 5], 3); T (&a[ 6], 2); T (&a[ 7], 1); + T (&a[ 8], 0); T (&a[ 9], 4); T (&a[10], 3); T (&a[11], 2); + T (&a[12], 1); T (&a[13], 0); T (&a[14], 0); + + T (&b[ 0], 1); T (&b[ 1], 0); T (&b[ 2], 2); T (&b[ 3], 1); + T (&b[ 4], 0); T (&b[ 5], 3); T (&b[ 6], 2); T (&b[ 7], 1); + T (&b[ 8], 0); T (&b[ 9], 4); T (&b[10], 3); T (&b[11], 2); + T (&b[12], 1); T (&b[13], 0); T (&b[14], 0); T (&b[15], 0); + T (&b[16], 0); T (&b[17], 0); T (&b[18], 0); T (&b[19], 0); +} + +/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } + { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-51.c b/gcc/testsuite/gcc.dg/strlenopt-51.c new file mode 100644 index 00000000000..cbed11bbf58 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-51.c @@ -0,0 +1,121 @@ +/* PR tree-optimization/77357 - strlen of constant strings not folded + { dg-do compile } + { dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-optimized" } */ + +#include "strlenopt.h" + +#define CONCAT(x, y) x ## y +#define CAT(x, y) CONCAT (x, y) +#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__) + +#define FAIL(name) do { \ + extern void FAILNAME (name) (void); \ + FAILNAME (name)(); \ + } while (0) + +/* Macro to emit a call to funcation named + call_in_true_branch_not_eliminated_on_line_NNN() + for each call that's expected to be eliminated. The dg-final + scan-tree-dump-time directive at the bottom of the test verifies + that no such call appears in output. */ +#define ELIM(expr) \ + if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0 + +/* Macro to emit a call to a function named + call_made_in_{true,false}_branch_on_line_NNN() + for each call that's expected to be retained. The dg-final + scan-tree-dump-time directive at the bottom of the test verifies + that the expected number of both kinds of calls appears in output + (a pair for each line with the invocation of the KEEP() macro. */ +#define KEEP(expr) \ + if (expr) \ + FAIL (made_in_true_branch); \ + else \ + FAIL (made_in_false_branch) + +#define T(s, n) ELIM (strlen (s) == n) + + +struct S +{ + char a1[1], a2[2], a3[3], a4[4], a5[5], a6[6], a7[7], a8[8], a9[9]; +}; + +#define S0 "" +#define S1 "1" +#define S2 "12" +#define S3 "123" +#define S4 "1234" +#define S5 "12345" +#define S6 "123456" +#define S7 "1234567" +#define S8 "12345678" + +const char a9[][9] = { S0, S1, S2, S3, S4, S5, S6, S7, S8 }; + +void test_elim_a9 (int i) +{ + ELIM (strlen (&a9[0][i]) > 0); + ELIM (strlen (&a9[1][i]) > 1); + ELIM (strlen (&a9[2][i]) > 2); + ELIM (strlen (&a9[3][i]) > 3); + ELIM (strlen (&a9[4][i]) > 4); + ELIM (strlen (&a9[5][i]) > 5); + ELIM (strlen (&a9[6][i]) > 6); + ELIM (strlen (&a9[7][i]) > 7); + ELIM (strlen (&a9[8][i]) > 8); +} + +const char a9_9[][9][9] = { + { S0, S1, S2, S3, S4, S5, S6, S7, S8 }, + { S1, S2, S3, S4, S5, S6, S7, S8, S0 }, + { S2, S3, S4, S5, S6, S7, S8, S0, S1 }, + { S3, S4, S5, S6, S7, S8, S0, S1, S2 }, + { S4, S5, S6, S7, S8, S0, S1, S2, S3 }, + { S5, S6, S7, S8, S0, S1, S2, S3, S4 }, + { S6, S7, S8, S0, S1, S2, S3, S4, S5 }, + { S7, S8, S0, S1, S2, S3, S4, S5, S6 }, + { S8, S0, S2, S2, S3, S4, S5, S6, S7 } +}; + +void test_elim_a9_9 (int i) +{ +#undef T +#define T(I) \ + ELIM (strlen (&a9_9[I][0][i]) > (0 + I) % 9); \ + ELIM (strlen (&a9_9[I][1][i]) > (1 + I) % 9); \ + ELIM (strlen (&a9_9[I][2][i]) > (2 + i) % 9); \ + ELIM (strlen (&a9_9[I][3][i]) > (3 + I) % 9); \ + ELIM (strlen (&a9_9[I][4][i]) > (4 + I) % 9); \ + ELIM (strlen (&a9_9[I][5][i]) > (5 + I) % 9); \ + ELIM (strlen (&a9_9[I][6][i]) > (6 + I) % 9); \ + ELIM (strlen (&a9_9[I][7][i]) > (7 + I) % 9); \ + ELIM (strlen (&a9_9[I][8][i]) > (8 + I) % 9) + + T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8); +} + +#line 1000 + +void test_keep_a9_9 (int i) +{ +#undef T +#define T(I) \ + KEEP (strlen (&a9_9[i][I][0]) > (1 + I) % 9); \ + KEEP (strlen (&a9_9[i][I][1]) > (1 + I) % 9); \ + KEEP (strlen (&a9_9[i][I][2]) > (2 + I) % 9); \ + KEEP (strlen (&a9_9[i][I][3]) > (3 + I) % 9); \ + KEEP (strlen (&a9_9[i][I][4]) > (4 + I) % 9); \ + KEEP (strlen (&a9_9[i][I][5]) > (5 + I) % 9); \ + KEEP (strlen (&a9_9[i][I][6]) > (6 + I) % 9); \ + KEEP (strlen (&a9_9[i][I][7]) > (7 + I) % 9); \ + KEEP (strlen (&a9_9[i][I][8]) > (8 + I) % 9) + + T (0); T (1); T (2); T (3); T (4); T (5); T (6); T (7); T (8); +} + +/* { dg-final { scan-tree-dump-times "strlen" 72 "gimple" } } + { dg-final { scan-tree-dump-times "strlen" 63 "optimized" } } + + { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 72 "optimized" } } + { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 81 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-52.c b/gcc/testsuite/gcc.dg/strlenopt-52.c new file mode 100644 index 00000000000..03e063b435e --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-52.c @@ -0,0 +1,288 @@ +/* PR tree-optimization/77357 - strlen of constant strings not folded + { dg-do compile } + { dg-options "-O2 -Wall -fdump-tree-gimple -fdump-tree-ccp" } */ + +#include "strlenopt.h" + +#define CONCAT(x, y) x ## y +#define CAT(x, y) CONCAT (x, y) +#define FAILNAME(name) CAT (call_ ## name ##_on_line_, __LINE__) + +#define FAIL(name) do { \ + extern void FAILNAME (name) (void); \ + FAILNAME (name)(); \ + } while (0) + +/* Macro to emit a call to funcation named + call_in_true_branch_not_eliminated_on_line_NNN() + for each call that's expected to be eliminated. The dg-final + scan-tree-dump-time directive at the bottom of the test verifies + that no such call appears in output. */ +#define ELIM(expr) \ + if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0 + +#define T(s, n) ELIM (strlen (s) == n) + + +struct S +{ + char a1[1], a2[2], a3[3], a4[4], a5[5], a6[6], a7[7], a8[8], a9[9]; +}; + +#define S0 "" +#define S1 "1" +#define S2 "12" +#define S3 "123" +#define S4 "1234" +#define S5 "12345" +#define S6 "123456" +#define S7 "1234567" +#define S8 "12345678" + +const char a9[][9] = { S0, S1, S2, S3, S4, S5, S6, S7, S8 }; + +const char a_1_9[1][9] = { S8 }; +const char a_2_9[2][9] = { S8, S7}; + +const char a9_9[][9][9] = { + { S0, S0, S0, S0, S0, S0, S0, S0, S0 }, + { S0, S1, S1, S1, S1, S1, S1, S1, S1 }, + { S0, S1, S2, S2, S2, S2, S2, S2, S2 }, + { S0, S1, S2, S3, S3, S3, S3, S3, S3 }, + { S0, S1, S2, S3, S4, S4, S4, S4, S4 }, + { S0, S1, S2, S3, S4, S5, S5, S5, S5 }, + { S0, S1, S2, S3, S4, S5, S6, S6, S6 }, + { S0, S1, S2, S3, S4, S5, S6, S7, S7 }, + { S0, S1, S2, S3, S4, S5, S6, S7, S8 } +}; + +const struct S s = { S0, S1, S2, S3, S4, S5, S6, S7, S8 }; + +const struct S sa[9] = { + { S0, S0, S0, S0, S0, S0, S0, S0, S0 }, + { S0, S1, S1, S1, S1, S1, S1, S1, S1 }, + { S0, S1, S2, S2, S2, S2, S2, S2, S2 }, + { S0, S1, S2, S3, S3, S3, S3, S3, S3 }, + { S0, S1, S2, S3, S4, S4, S4, S4, S4 }, + { S0, S1, S2, S3, S4, S5, S5, S5, S5 }, + { S0, S1, S2, S3, S4, S5, S6, S6, S6 }, + { S0, S1, S2, S3, S4, S5, S6, S7, S7 }, + { S0, S1, S2, S3, S4, S5, S6, S7, S8 } +}; + +const struct S sa3_5_7[3][5][7] = { + [1][2][3].a2 = S1, [1][3][5].a3 = S2, [2][4][5].a4 = S3 +}; + + +void test_global_array (void) +{ + T (a9[0], 0); T (a9[0] + 0, 0); T (a9[0] + 0, 0); T (a9[0] + 0, 0); + T (a9[1], 1); T (a9[1] + 1, 0); T (a9[1] + 1, 0); T (a9[1] + 1, 0); + T (a9[2], 2); + T (a9[2] + 1, 1); + T (a9[2] + 2, 0); + T (a9[2] + 2, 0); + + T (a9[3], 3); T (a9[3] + 1, 2); T (a9[3] + 2, 1); T (a9[3] + 3, 0); + T (a9[4], 4); T (a9[4] + 1, 3); T (a9[4] + 2, 2); T (a9[4] + 3, 1); + T (a9[5], 5); T (a9[5] + 1, 4); T (a9[5] + 2, 3); T (a9[5] + 3, 2); + T (a9[6], 6); T (a9[6] + 1, 5); T (a9[6] + 2, 4); T (a9[6] + 3, 3); + T (a9[7], 7); T (a9[7] + 1, 6); T (a9[7] + 2, 5); T (a9[7] + 3, 4); + T (a9[8], 8); T (a9[8] + 1, 7); T (a9[8] + 2, 6); T (a9[8] + 3, 5); + + T (a_1_9[0], 8); + T (a_1_9[0] + 1, 7); + T (a_1_9[0] + 7, 1); + T (a_1_9[0] + 8, 0); + + T (a_2_9[0], 8); + T (a_2_9[0] + 1, 7); + T (a_2_9[0] + 7, 1); + T (a_2_9[0] + 8, 0); + + T (a_2_9[1], 7); + T (a_2_9[1] + 1, 6); + T (a_2_9[1] + 6, 1); + T (a_2_9[1] + 7, 0); + T (a_2_9[1] + 8, 0); +} + +void test_global_array_array (void) +{ + T (a9_9[0][0], 0); T (a9_9[1][0], 0); T (a9_9[2][0], 0); + T (a9_9[0][1], 0); T (a9_9[1][1], 1); T (a9_9[2][1], 1); + T (a9_9[0][2], 0); T (a9_9[1][2], 1); T (a9_9[2][2], 2); + T (a9_9[0][3], 0); T (a9_9[1][3], 1); T (a9_9[2][3], 2); + T (a9_9[0][4], 0); T (a9_9[1][4], 1); T (a9_9[2][4], 2); + T (a9_9[0][5], 0); T (a9_9[1][5], 1); T (a9_9[2][5], 2); + T (a9_9[0][6], 0); T (a9_9[1][6], 1); T (a9_9[2][6], 2); + T (a9_9[0][7], 0); T (a9_9[1][7], 1); T (a9_9[2][7], 2); + T (a9_9[0][8], 0); T (a9_9[1][8], 1); T (a9_9[2][8], 2); + + T (a9_9[3][0], 0); T (a9_9[4][0], 0); T (a9_9[5][0], 0); + T (a9_9[3][1], 1); T (a9_9[4][1], 1); T (a9_9[5][1], 1); + T (a9_9[3][2], 2); T (a9_9[4][2], 2); T (a9_9[5][2], 2); + T (a9_9[3][3], 3); T (a9_9[4][3], 3); T (a9_9[5][3], 3); + T (a9_9[3][4], 3); T (a9_9[4][4], 4); T (a9_9[5][4], 4); + T (a9_9[3][5], 3); T (a9_9[4][5], 4); T (a9_9[5][5], 5); + T (a9_9[3][6], 3); T (a9_9[4][6], 4); T (a9_9[5][6], 5); + T (a9_9[3][7], 3); T (a9_9[4][7], 4); T (a9_9[5][7], 5); + T (a9_9[3][8], 3); T (a9_9[4][8], 4); T (a9_9[5][8], 5); + + T (a9_9[6][0], 0); T (a9_9[7][0], 0); T (a9_9[8][0], 0); + T (a9_9[6][1], 1); T (a9_9[7][1], 1); T (a9_9[8][1], 1); + T (a9_9[6][2], 2); T (a9_9[7][2], 2); T (a9_9[8][2], 2); + T (a9_9[6][3], 3); T (a9_9[7][3], 3); T (a9_9[8][3], 3); + T (a9_9[6][4], 4); T (a9_9[7][4], 4); T (a9_9[8][4], 4); + T (a9_9[6][5], 5); T (a9_9[7][5], 5); T (a9_9[8][5], 5); + T (a9_9[6][6], 6); T (a9_9[7][6], 6); T (a9_9[8][6], 6); + T (a9_9[6][7], 6); T (a9_9[7][7], 7); T (a9_9[8][7], 7); + T (a9_9[6][8], 6); T (a9_9[7][8], 7); T (a9_9[8][8], 8); + + + T (a9_9[0][0] + 1, 0); T (a9_9[1][0] + 1, 0); T (a9_9[2][0] + 2, 0); + T (a9_9[0][1] + 2, 0); T (a9_9[1][1] + 1, 0); T (a9_9[2][1] + 2, 0); + T (a9_9[0][2] + 3, 0); T (a9_9[1][2] + 1, 0); T (a9_9[2][2] + 2, 0); + T (a9_9[0][3] + 4, 0); T (a9_9[1][3] + 1, 0); T (a9_9[2][3] + 2, 0); + T (a9_9[0][4] + 5, 0); T (a9_9[1][4] + 1, 0); T (a9_9[2][4] + 2, 0); + T (a9_9[0][5] + 6, 0); T (a9_9[1][5] + 1, 0); T (a9_9[2][5] + 2, 0); + T (a9_9[0][6] + 7, 0); T (a9_9[1][6] + 1, 0); T (a9_9[2][6] + 2, 0); + T (a9_9[0][7] + 8, 0); T (a9_9[1][7] + 1, 0); T (a9_9[2][7] + 2, 0); +} + +void test_global_struct (void) +{ + T (s.a1, 0); + T (s.a2, 1); + T (s.a3, 2); + T (s.a4, 3); + T (s.a5, 4); + T (s.a6, 5); + T (s.a7, 6); + T (s.a8, 7); + T (s.a9, 8); +} + +void test_global_struct_array (void) +{ + T (sa[0].a1, 0); T (sa[1].a1, 0); T (sa[2].a1, 0); T (sa[3].a1, 0); + T (sa[0].a2, 0); T (sa[1].a2, 1); T (sa[2].a2, 1); T (sa[3].a2, 1); + T (sa[0].a3, 0); T (sa[1].a3, 1); T (sa[2].a3, 2); T (sa[3].a3, 2); + T (sa[0].a4, 0); T (sa[1].a4, 1); T (sa[2].a4, 2); T (sa[3].a4, 3); + T (sa[0].a5, 0); T (sa[1].a5, 1); T (sa[2].a5, 2); T (sa[3].a5, 3); + T (sa[0].a6, 0); T (sa[1].a6, 1); T (sa[2].a6, 2); T (sa[3].a6, 3); + T (sa[0].a7, 0); T (sa[1].a7, 1); T (sa[2].a7, 2); T (sa[3].a7, 3); + T (sa[0].a8, 0); T (sa[1].a8, 1); T (sa[2].a8, 2); T (sa[3].a8, 3); + T (sa[0].a9, 0); T (sa[1].a9, 1); T (sa[2].a9, 2); T (sa[3].a9, 3); + + T (sa[4].a1, 0); T (sa[5].a1, 0); T (sa[6].a1, 0); T (sa[7].a1, 0); + T (sa[4].a2, 1); T (sa[5].a2, 1); T (sa[6].a2, 1); T (sa[7].a2, 1); + T (sa[4].a3, 2); T (sa[5].a3, 2); T (sa[6].a3, 2); T (sa[7].a3, 2); + T (sa[4].a4, 3); T (sa[5].a4, 3); T (sa[6].a4, 3); T (sa[7].a4, 3); + T (sa[4].a5, 4); T (sa[5].a5, 4); T (sa[6].a5, 4); T (sa[7].a5, 4); + T (sa[4].a6, 4); T (sa[5].a6, 5); T (sa[6].a6, 5); T (sa[7].a6, 5); + T (sa[4].a7, 4); T (sa[5].a7, 5); T (sa[6].a7, 6); T (sa[7].a7, 6); + T (sa[4].a8, 4); T (sa[5].a8, 5); T (sa[6].a8, 6); T (sa[7].a8, 7); + T (sa[4].a9, 4); T (sa[5].a9, 5); T (sa[6].a9, 6); T (sa[7].a9, 7); + + T (sa[8].a1, 0); + T (sa[8].a2, 1); T (sa[8].a2 + 1, 0); + T (sa[8].a3, 2); T (sa[8].a3 + 1, 1); T (sa[8].a3 + 2, 0); + T (sa[8].a4, 3); T (sa[8].a4 + 1, 2); T (sa[8].a4 + 2, 1); + T (sa[8].a5, 4); T (sa[8].a5 + 1, 3); T (sa[8].a5 + 2, 2); + T (sa[8].a6, 5); T (sa[8].a6 + 1, 4); T (sa[8].a6 + 2, 3); + T (sa[8].a7, 6); T (sa[8].a7 + 1, 5); T (sa[8].a7 + 2, 4); + T (sa[8].a8, 7); T (sa[8].a8 + 1, 6); T (sa[8].a8 + 2, 5); + T (sa[8].a9, 8); T (sa[8].a9 + 1, 7); T (sa[8].a9 + 2, 6); + + + T (sa3_5_7[1][2][3].a2, 1); + T (sa3_5_7[1][3][5].a3, 2); + T (sa3_5_7[2][4][5].a4, 3); + + T (sa3_5_7[0][0][0].a1, 0); + T (sa3_5_7[0][0][0].a2, 0); + T (sa3_5_7[0][0][0].a3, 0); + T (sa3_5_7[0][0][0].a4, 0); + T (sa3_5_7[0][0][0].a5, 0); + T (sa3_5_7[0][0][0].a6, 0); + T (sa3_5_7[0][0][0].a7, 0); + T (sa3_5_7[0][0][0].a8, 0); + T (sa3_5_7[0][0][0].a9, 0); + + T (sa3_5_7[0][0][1].a1, 0); + T (sa3_5_7[0][0][1].a2, 0); + T (sa3_5_7[0][0][1].a3, 0); + T (sa3_5_7[0][0][1].a4, 0); + T (sa3_5_7[0][0][1].a5, 0); + T (sa3_5_7[0][0][1].a6, 0); + T (sa3_5_7[0][0][1].a7, 0); + T (sa3_5_7[0][0][1].a8, 0); + T (sa3_5_7[0][0][1].a9, 0); + + T (sa3_5_7[0][1][0].a1, 0); + T (sa3_5_7[0][1][0].a2, 0); + T (sa3_5_7[0][1][0].a3, 0); + T (sa3_5_7[0][1][0].a4, 0); + T (sa3_5_7[0][1][0].a5, 0); + T (sa3_5_7[0][1][0].a6, 0); + T (sa3_5_7[0][1][0].a7, 0); + T (sa3_5_7[0][1][0].a8, 0); + T (sa3_5_7[0][1][0].a9, 0); + + T (sa3_5_7[1][0][0].a1, 0); + T (sa3_5_7[1][0][0].a2, 0); + T (sa3_5_7[1][0][0].a3, 0); + T (sa3_5_7[1][0][0].a4, 0); + T (sa3_5_7[1][0][0].a5, 0); + T (sa3_5_7[1][0][0].a6, 0); + T (sa3_5_7[1][0][0].a7, 0); + T (sa3_5_7[1][0][0].a8, 0); + T (sa3_5_7[1][0][0].a9, 0); +} + + +struct SS { + char a9[9][9]; + struct S sa9[9]; +}; + +const struct SS ssa[] = { + [1] = { + .a9 = { [3] = S3, [7] = S7 }, + .sa9 = { [5] = { .a5 = S4, .a7 = S6 } } + }, + [5] = { + .a9 = { [1] = S8, [5] = S4 }, + .sa9 = { [3] = { .a3 = S2, .a6 = S3 } } + } +}; + +void test_global_struct_struct_array (void) +{ + T (ssa[0].a9[0], 0); + T (ssa[0].a9[3], 0); + T (ssa[0].sa9[5].a5, 0); + T (ssa[0].sa9[5].a7, 0); + + T (ssa[1].a9[0], 0); + + T (ssa[1].a9[3], 3); + T (ssa[1].a9[7], 7); + T (ssa[1].sa9[5].a5, 4); + T (ssa[1].sa9[5].a7, 6); + + T (ssa[2].a9[3], 0); + T (ssa[2].a9[7], 0); + T (ssa[2].sa9[5].a5, 0); + T (ssa[2].sa9[5].a7, 0); + + T (ssa[5].a9[1], 8); + T (ssa[5].a9[5], 4); + T (ssa[5].sa9[3].a3, 2); + T (ssa[5].sa9[3].a6, 3); +} + +/* { dg-final { scan-tree-dump-times "strlen" 0 "gimple" } } + { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "ccp1" } } */ -- 2.30.2