GCC folds accesses to members of constant aggregates except for character arrays/strings. For example, the strlen() call below is not folded:
const char a[][4] = { "1", "12" }; int f (void) { retturn strlen (a[1]); } The attached change set enhances the string_constant() function to make it possible to extract string constants from aggregate initializers (CONSTRUCTORS). The initial solution was much simpler but as is often the case, MEM_REF made it fail to fold things like: int f (void) { retturn strlen (a[1] + 1); } Handling those made the project a bit more interesting and the final solution somewhat more involved. To handle offsets into aggregate string members the patch also extends the fold_ctor_reference() function to extract entire string array initializers even if the offset points past the beginning of the string and even though the size and exact type of the reference are not known (there isn't enough information in a MEM_REF to determine that). Tested along with the patch for PR 86415 on x86_64-linux. Martin
PR middle-end/77357 - strlen of constant strings not folded gcc/ChangeLog: * 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. diff --git a/gcc/builtins.c b/gcc/builtins.c index 91658e8..2f9d5d7 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*. */ diff --git a/gcc/expr.c b/gcc/expr.c index 56751df..be3ab93 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" @@ -11274,54 +11276,20 @@ is_aligning_offset (const_tree offset, const_tree exp) tree string_constant (tree arg, tree *ptr_offset) { - tree array, offset, lower_bound; + tree array; STRIP_NOPS (arg); + 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) - { - 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)) - { - /* 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)); - } - } - 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; + arg = TREE_OPERAND (arg, 0); + array = get_addr_base_and_unit_offset (arg, &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,25 +11299,26 @@ 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)) - { - array = TREE_OPERAND (arg0, 0); - offset = arg1; - } - else if (TREE_CODE (arg1) == ADDR_EXPR - && (TREE_CODE (TREE_OPERAND (arg1, 0)) == STRING_CST - || TREE_CODE (TREE_OPERAND (arg1, 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 (arg1, 0); - offset = arg0; + tree type = TREE_TYPE (arg1); + *ptr_offset = fold_build2 (PLUS_EXPR, type, offset, arg1); + return str; } - else - return 0; + return NULL_TREE; } else - return 0; + return NULL_TREE; + + tree offset = wide_int_to_tree (sizetype, base_off); if (TREE_CODE (array) == STRING_CST) { @@ -11362,9 +11331,40 @@ string_constant (tree arg, tree *ptr_offset) 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) + if (!init || init == error_mark_node) + return NULL_TREE; + if (TREE_CODE (init) == CONSTRUCTOR) + { + tree type; + if (TREE_CODE (arg) == ARRAY_REF + || TREE_CODE (arg) == MEM_REF) + type = TREE_TYPE (arg); + else if (TREE_CODE (arg) == COMPONENT_REF) + { + tree field = TREE_OPERAND (arg, 1); + type = TREE_TYPE (field); + } + else + return NULL_TREE; + + tree typesize = TYPE_SIZE (type); + poly_int64 size = 0; + if (typesize && TREE_CODE (arg) != MEM_REF) + size = tree_to_uhwi (typesize); + + base_off *= BITS_PER_UNIT; + unsigned HOST_WIDE_INT fieldoff = 0; + init = fold_ctor_reference (type, init, base_off, size, 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); + } + } + + if (!init || TREE_CODE (init) != STRING_CST) return 0; /* Avoid const char foo[4] = "abcde"; */ diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index 6ce34bf..148ad43 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -6476,14 +6476,18 @@ 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 TYPE + and SIZE bits to the memory at bit OFFSET. 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; @@ -6529,21 +6533,40 @@ 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 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, + /* When memory is not explicitly mentioned in constructor, it is 0 (or out of range). */ return build_zero_cst (type); } -/* 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 + TYPE and SIZE bits to the memory at bit OFFSET. When SIZE is zero, + attempt to fold a reference to the entire member which OFFSET + refers to. 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 +6577,16 @@ 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 && TREE_CODE (cval) == STRING_CST) + { + /* Determine the size of the flexible array member from + the size of the string initializer provided for it. */ + unsigned HOST_WIDE_INT len = TREE_STRING_LENGTH (cval); + tree eltype = TREE_TYPE (TREE_TYPE (cval)); + len *= tree_to_uhwi (TYPE_SIZE (eltype)); + field_size = build_int_cst (size_type_node, len); + } /* Variable sized objects in static constructors makes no sense, but field_size can be NULL for flexible array members. */ @@ -6566,45 +6597,72 @@ 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) + { + /* Set the OFFSET and SIZE to encompass the entire field. */ + offset = bitoffset.to_uhwi (); + 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); } -/* 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; @@ -6650,14 +6708,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 0a28ec7..04e9bfa 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/gcc.dg/strlenopt-49.c b/gcc/testsuite/gcc.dg/strlenopt-49.c new file mode 100644 index 0000000..03e063b --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-49.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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-50.c b/gcc/testsuite/gcc.dg/strlenopt-50.c new file mode 100644 index 0000000..1d1d368 --- /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" } } */