The attached changes implement the detection of nul-terminated constant arrays and incorrect or early folding of such arrays. This resolves PR 86711 - wrong folding of memchr, and prevents PR 86714 - tree-ssa-forwprop.c confused by too long initializer. No warnings are issued.
PR tree-optimization/86714 - tree-ssa-forwprop.c confused by too long initializer PR tree-optimization/86711 - wrong folding of memchr
gcc/ChangeLog: PR tree-optimization/86714 PR tree-optimization/86711 * builtins.h (c_strlen): Add argument. * builtins.c (c_strlen): Add argument and use it. * expr.c (string_constant): Add arguments. Detect missing nul terminator and outermost declaration it's missing in. * expr.h (string_constant): Add argument. * fold-const.c (c_getstr): Change argument to tree*, rename other arguments. * fold-const-call.c (fold_const_call): Avoid folding calls with unterminated arrays. * gimple-fold.c (get_range_strlen): Add argument. (get_maxval_strlen): Adjust. * gimple-fold.h (get_range_strlen): Add argument. gcc/testsuite/ChangeLog: PR tree-optimization/86714 PR tree-optimization/86711 * gcc.c-torture/execute/memchr-1.c: New test. * gcc.c-torture/execute/pr86714.c: New test. * gcc/testsuite/gcc.dg/strlenopt-56.c: New test. diff --git a/gcc/builtins.c b/gcc/builtins.c index 39611de..a7aa4b2 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -567,37 +567,55 @@ string_length (const void *ptr, unsigned eltsize, unsigned maxelts) accesses. Note that this implies the result is not going to be emitted into the instruction stream. + When NONSTR is non-null and the string is not properly nul-terminated, + set *NONSTR to the declaration of the outermost constant object whose + initializer (or one of its elements) is not nul-terminated. + The value returned is of type `ssizetype'. Unfortunately, string_constant can't access the values of const char arrays with initializers, so neither can we do so here. */ tree -c_strlen (tree src, int only_value) +c_strlen (tree src, int only_value, tree *nonstr /* = NULL */) { STRIP_NOPS (src); + + /* Used to detect non-nul-terminated strings in subexpressions + of a conditional expression. When NONSTR is null, point it + arbitrarily at one of the elements for simplicity. */ + tree nstrs[] = { NULL_TREE, NULL_TREE }; + if (!nonstr) + nonstr = nstrs; + if (TREE_CODE (src) == COND_EXPR && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) { - tree len1, len2; - - len1 = c_strlen (TREE_OPERAND (src, 1), only_value); - len2 = c_strlen (TREE_OPERAND (src, 2), only_value); + tree len1 = c_strlen (TREE_OPERAND (src, 1), only_value, nstrs); + tree len2 = c_strlen (TREE_OPERAND (src, 2), only_value, nstrs + 1); if (tree_int_cst_equal (len1, len2)) - return len1; + { + *nonstr = nstrs[0] ? nstrs[0] : nstrs[1]; + return len1; + } } if (TREE_CODE (src) == COMPOUND_EXPR && (only_value || !TREE_SIDE_EFFECTS (TREE_OPERAND (src, 0)))) - return c_strlen (TREE_OPERAND (src, 1), only_value); + return c_strlen (TREE_OPERAND (src, 1), only_value, nonstr); location_t loc = EXPR_LOC_OR_LOC (src, input_location); /* Offset from the beginning of the string in bytes. */ tree byteoff; - src = string_constant (src, &byteoff); - if (src == 0) - return NULL_TREE; + src = string_constant (src, &byteoff, nonstr); + if (!src) + { + /* On failure set *NONSTR to the first non-null NSTRS element + if one is non-null, or to null. */ + *nonstr = nstrs[0] ? nstrs[0] : nstrs[1]; + return NULL_TREE; + } /* Determine the size of the string element. */ unsigned eltsize @@ -641,21 +659,25 @@ c_strlen (tree src, int only_value) if (!maxelts) return ssize_int (0); - /* We don't know the starting offset, but we do know that the string - has no internal zero bytes. If the offset falls within the bounds - of the string subtract the offset from the length of the string, - and return that. Otherwise the length is zero. Take care to - use SAVE_EXPR in case the OFFSET has side-effects. */ - tree offsave = TREE_SIDE_EFFECTS (byteoff) ? save_expr (byteoff) : byteoff; + /* We don't know the starting offset, but we do know that + the string has no internal NUL characters. If the byte + offset falls within the bounds of the string subtract + the offset from the length of the string in bytes, and + return the result. Otherwise the length is zero. Take + care to use SAVE_EXPR in case the OFFSET has side-effects. */ + tree offsave + = TREE_SIDE_EFFECTS (byteoff) ? save_expr (byteoff) : byteoff; offsave = fold_convert (ssizetype, offsave); tree condexp = fold_build2_loc (loc, LE_EXPR, boolean_type_node, offsave, build_int_cst (ssizetype, len * eltsize)); - tree lenexp = size_diffop_loc (loc, ssize_int (strelts * eltsize), offsave); + tree lenexp = size_diffop_loc (loc, ssize_int (strelts * eltsize), + offsave); return fold_build3_loc (loc, COND_EXPR, ssizetype, condexp, lenexp, build_zero_cst (ssizetype)); } - /* Offset from the beginning of the string in elements. */ + /* Offset in (possibly wide) characters from the beginning of the string + in elements. */ HOST_WIDE_INT eltoff; /* We have a known offset into the string. Start searching there for @@ -683,14 +705,11 @@ c_strlen (tree src, int only_value) return NULL_TREE; } - /* Use strlen to search for the first zero byte. Since any strings - constructed with build_string will have nulls appended, we win even - if we get handed something like (char[4])"abcd". - - Since ELTOFF is our starting index into the string, no further - calculation is needed. */ + /* Search at most STRELTS - ELTOFF characters for the first (possibly + wide) NUL character starting at the byte offset. Return the length + of the substring. */ unsigned len = string_length (ptr + eltoff * eltsize, eltsize, - maxelts - eltoff); + strelts - eltoff); return ssize_int (len); } diff --git a/gcc/builtins.h b/gcc/builtins.h index 2e0a2f9..27e6959 100644 --- a/gcc/builtins.h +++ b/gcc/builtins.h @@ -58,7 +58,7 @@ extern bool get_pointer_alignment_1 (tree, unsigned int *, unsigned HOST_WIDE_INT *); extern unsigned int get_pointer_alignment (tree); extern unsigned string_length (const void*, unsigned, unsigned); -extern tree c_strlen (tree, int); +extern tree c_strlen (tree, int, tree * = NULL); extern void expand_builtin_setjmp_setup (rtx, rtx); extern void expand_builtin_setjmp_receiver (rtx); extern void expand_builtin_update_setjmp_buf (rtx); diff --git a/gcc/expr.c b/gcc/expr.c index de6709d..d228af5 100644 --- a/gcc/expr.c +++ b/gcc/expr.c @@ -11271,10 +11271,13 @@ 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 (possibly non-constant) offset in bytes within the string that ARG is accessing. + If NONSTR is non-null, consider valid even sequences of characters that + aren't nul-terminated strings. In that case, if ARG refers to such + a sequence set *NONSTR to its declaration and clear it otherwise. The type of the offset is sizetype. */ tree -string_constant (tree arg, tree *ptr_offset) +string_constant (tree arg, tree *ptr_offset, tree *nonstr /* = NULL */) { tree array; STRIP_NOPS (arg); @@ -11328,7 +11331,7 @@ string_constant (tree arg, tree *ptr_offset) return NULL_TREE; tree offset; - if (tree str = string_constant (arg0, &offset)) + if (tree str = string_constant (arg0, &offset, nonstr)) { /* Avoid pointers to arrays (see bug 86622). */ if (POINTER_TYPE_P (TREE_TYPE (arg)) @@ -11368,6 +11371,8 @@ string_constant (tree arg, tree *ptr_offset) if (TREE_CODE (array) == STRING_CST) { *ptr_offset = fold_convert (sizetype, offset); + if (nonstr) + *nonstr = NULL_TREE; return array; } @@ -11414,20 +11419,34 @@ string_constant (tree arg, tree *ptr_offset) 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 + /* Avoid returning an array that is unterminated because it lacks + a terminating nul, like const char a[4] = "abcde"; - but do handle those that fit even if they have excess + but do handle those that are strings 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 charsize = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (init)))); + /* Compute the lower bound number of elements (not bytes) in the array + that the string is used to initialize. The actual size of the array + will be may be greater if the string is shorter, but the important + data point is whether the literal, including the terminating nul, + fits in the array. */ + unsigned HOST_WIDE_INT array_elts + = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (init))) / charsize; + + /* Compute the string length in (wide) characters. */ unsigned HOST_WIDE_INT length = TREE_STRING_LENGTH (init); length = string_length (TREE_STRING_POINTER (init), charsize, length / charsize); - if (compare_tree_int (array_size, length + 1) < 0) + /* If the caller is prepared to handle unterminated arrays (as + indicated by a non-nul NONSTR), set *NONSTR to the array and + return the initializer. Otherwise fail. */ + if (nonstr) + *nonstr = array_elts > length ? NULL_TREE : array; + else if (array_elts <= length) return NULL_TREE; *ptr_offset = offset; diff --git a/gcc/expr.h b/gcc/expr.h index cf047d4..d4d2564 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -288,7 +288,7 @@ expand_normal (tree exp) /* Return the tree node and offset if a given argument corresponds to a string constant. */ -extern tree string_constant (tree, tree *); +extern tree string_constant (tree, tree *, tree * = NULL); /* Two different ways of generating switch statements. */ extern int try_casesi (tree, tree, tree, tree, rtx, rtx, rtx, profile_probability); diff --git a/gcc/fold-const-call.c b/gcc/fold-const-call.c index 06a42060..f6bab7b 100644 --- a/gcc/fold-const-call.c +++ b/gcc/fold-const-call.c @@ -1199,9 +1199,14 @@ fold_const_call (combined_fn fn, tree type, tree arg) switch (fn) { case CFN_BUILT_IN_STRLEN: - if (const char *str = c_getstr (arg)) - return build_int_cst (type, strlen (str)); - return NULL_TREE; + { + tree nonstr = NULL_TREE; + if (const char *str = c_getstr (arg, NULL, &nonstr)) + if (!nonstr) + return build_int_cst (type, strlen (str)); + + return NULL_TREE; + } CASE_CFN_NAN: CASE_FLT_FN_FLOATN_NX (CFN_BUILT_IN_NAN): diff --git a/gcc/fold-const.c b/gcc/fold-const.c index b318fc77..97a35f5 100644 --- a/gcc/fold-const.c +++ b/gcc/fold-const.c @@ -14577,24 +14577,26 @@ fold_build_pointer_plus_hwi_loc (location_t loc, tree ptr, HOST_WIDE_INT off) /* 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"; */ + constant offset). If STRSIZE is non-null, store the size of the string + literal in *STRSIZE, including any embedded or terminating nuls. If + SRC refers to an array that is not a nul-terminated string and NONSTR + is non-null, set it to the declaration of the array, otherwise clear it. + The former can happen in the case of valid C declarations such as: + const char a[3] = "123"; */ const char * -c_getstr (tree src, unsigned HOST_WIDE_INT *strlen /* = NULL */, - unsigned HOST_WIDE_INT *strsize /* = NULL */) +c_getstr (tree src, unsigned HOST_WIDE_INT *strsize /* = NULL */, + tree *nonstr /* = NULL */) { tree offset_node; - if (strlen) - *strlen = 0; + if (strsize) + *strsize = 0; - src = string_constant (src, &offset_node); - if (src == 0) + /* Set to non-null if SRC refers to an unterminated array. */ + tree mynonstr; + src = string_constant (src, &offset_node, &mynonstr); + if (src == NULL_TREE) return NULL; unsigned HOST_WIDE_INT offset = 0; @@ -14606,47 +14608,45 @@ c_getstr (tree src, unsigned HOST_WIDE_INT *strlen /* = NULL */, 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; + /* STRING_SIZE is the size of the string literal, including any + embedded and trailing NULs, in bytes. ARRAY_SIZE is the size + of the array the string literal is stored in, in bytes. */ + unsigned HOST_WIDE_INT string_size = TREE_STRING_LENGTH (src); + unsigned HOST_WIDE_INT array_size = string_size; tree type = TREE_TYPE (src); if (tree size = TYPE_SIZE_UNIT (type)) if (tree_fits_shwi_p (size)) - string_size = tree_to_uhwi (size); + array_size = tree_to_uhwi (size); + + /* Pointer to the (possibly wide) string representation. */ + const char *strdata = TREE_STRING_POINTER (src); - if (strlen) + if (strsize) { - /* Compute and store the length of the substring at OFFSET. + /* Compute and store the size of the substring at OFFSET. All offsets past the initial length refer to null strings. */ - if (offset <= string_length) - *strlen = string_length - offset; + if (offset <= string_size) + *strsize = string_size - offset; else - *strlen = 0; + *strsize = 0; } - const char *string = TREE_STRING_POINTER (src); - - if (string_length == 0 - || offset >= string_size) + if (string_size == 0 + || offset >= array_size) return NULL; - if (strsize) - { - /* Support even constant character arrays that aren't proper - NUL-terminated strings. */ - *strsize = string_size; - } - else if (string[string_length - 1] != '\0') + if (nonstr) + *nonstr = mynonstr; + else if (mynonstr) { - /* Support only properly NUL-terminated strings but handle - consecutive strings within the same array, such as the six - substrings in "1\0002\0003". */ + /* When NONSTR is null, support only properly nul-terminated + strings but handle consecutive strings within the same array, + such as the six substrings in "1\0002\0003". Otherwise, let + the caller deal with non-nul-terminated arrays. */ return NULL; } - return offset <= string_length ? string + offset : ""; + return offset <= string_size ? strdata + 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 1b9ccc0..e3fec20 100644 --- a/gcc/fold-const.h +++ b/gcc/fold-const.h @@ -188,7 +188,7 @@ 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 * = NULL, - unsigned HOST_WIDE_INT * = NULL); + tree * = 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 506a296..5c88e33 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -1275,11 +1275,13 @@ gimple_fold_builtin_memset (gimple_stmt_iterator *gsi, tree c, tree len) Set *FLEXP to true if the range of the string lengths has been obtained from the upper bound of an array at the end of a struct. Such an array may hold a string that's longer than its upper bound - due to it being used as a poor-man's flexible array member. */ + due to it being used as a poor-man's flexible array member. + Clear *NULTERM if ARG refers to a constant array that is known + not be nul-terminated. */ static bool get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, - int fuzzy, bool *flexp) + int fuzzy, bool *flexp, tree *nonstr) { tree var, val = NULL_TREE; gimple *def_stmt; @@ -1301,7 +1303,8 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, if (TREE_CODE (aop0) == INDIRECT_REF && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME) return get_range_strlen (TREE_OPERAND (aop0, 0), - length, visited, type, fuzzy, flexp); + length, visited, type, fuzzy, flexp, + nonstr); } else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF && fuzzy) { @@ -1329,13 +1332,20 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, return false; } else - val = c_strlen (arg, 1); + { + /* Determine the string length. If NONSTR is non-nul, also + consider non-terminated arrays. */ + tree tmparr; + val = c_strlen (arg, 1, nonstr ? &tmparr : NULL); + if (val && tmparr) + *nonstr = tmparr; + } if (!val && fuzzy) { if (TREE_CODE (arg) == ADDR_EXPR) return get_range_strlen (TREE_OPERAND (arg, 0), length, - visited, type, fuzzy, flexp); + visited, type, fuzzy, flexp, nonstr); if (TREE_CODE (arg) == ARRAY_REF) { @@ -1477,7 +1487,8 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, || gimple_assign_unary_nop_p (def_stmt)) { tree rhs = gimple_assign_rhs1 (def_stmt); - return get_range_strlen (rhs, length, visited, type, fuzzy, flexp); + return get_range_strlen (rhs, length, visited, type, fuzzy, flexp, + nonstr); } else if (gimple_assign_rhs_code (def_stmt) == COND_EXPR) { @@ -1486,7 +1497,7 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, for (unsigned int i = 0; i < 2; i++) if (!get_range_strlen (ops[i], length, visited, type, fuzzy, - flexp)) + flexp, nonstr)) { if (fuzzy == 2) *maxlen = build_all_ones_cst (size_type_node); @@ -1513,7 +1524,8 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, if (arg == gimple_phi_result (def_stmt)) continue; - if (!get_range_strlen (arg, length, visited, type, fuzzy, flexp)) + if (!get_range_strlen (arg, length, visited, type, fuzzy, flexp, + nonstr)) { if (fuzzy == 2) *maxlen = build_all_ones_cst (size_type_node); @@ -1545,19 +1557,28 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type, and false if PHIs and COND_EXPRs are to be handled optimistically, if we can determine string length minimum and maximum; it will use the minimum from the ones where it can be determined. - STRICT false should be only used for warning code. */ + STRICT false should be only used for warning code. + When non-null, clear *NONSTR if ARG refers to a constant array + that is known not be nul-terminated. Otherwise set it to + the declaration of the constant non-terminated array. */ bool -get_range_strlen (tree arg, tree minmaxlen[2], bool strict) +get_range_strlen (tree arg, tree minmaxlen[2], bool strict /* = false */, + tree *nonstr /* = NULL */) { bitmap visited = NULL; minmaxlen[0] = NULL_TREE; minmaxlen[1] = NULL_TREE; + tree nonstrbuf; + if (!nonstr) + nonstr = &nonstrbuf; + *nonstr = NULL_TREE; + bool flexarray = false; if (!get_range_strlen (arg, minmaxlen, &visited, 1, strict ? 1 : 2, - &flexarray)) + &flexarray, nonstr)) { minmaxlen[0] = NULL_TREE; minmaxlen[1] = NULL_TREE; @@ -1576,12 +1597,15 @@ get_maxval_strlen (tree arg, int type) tree len[2] = { NULL_TREE, NULL_TREE }; bool dummy; - if (!get_range_strlen (arg, len, &visited, type, 0, &dummy)) + /* Set to non-null if ARG refers to an unterminated array. */ + tree nonstr = NULL_TREE; + if (!get_range_strlen (arg, len, &visited, type, 0, &dummy, &nonstr)) len[1] = NULL_TREE; if (visited) BITMAP_FREE (visited); - return len[1]; + /* Fail if the constant array isn't nul-terminated. */ + return nonstr ? NULL_TREE : len[1]; } @@ -3495,12 +3519,15 @@ static bool gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi) { gimple *stmt = gsi_stmt (*gsi); + tree arg = gimple_call_arg (stmt, 0); wide_int minlen; wide_int maxlen; + /* Set to non-null if ARG refers to an unterminated array. */ + tree nonstr; tree lenrange[2]; - if (!get_range_strlen (gimple_call_arg (stmt, 0), lenrange, true) + if (!get_range_strlen (arg, lenrange, true, &nonstr) && lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST && lenrange[1] && TREE_CODE (lenrange[1]) == INTEGER_CST) { diff --git a/gcc/gimple-fold.h b/gcc/gimple-fold.h index 04e9bfa..9bfc468 100644 --- a/gcc/gimple-fold.h +++ b/gcc/gimple-fold.h @@ -25,7 +25,7 @@ along with GCC; see the file COPYING3. If not see extern tree create_tmp_reg_or_ssa_name (tree, gimple *stmt = NULL); extern tree canonicalize_constructor_val (tree, tree); extern tree get_symbol_constant_value (tree); -extern bool get_range_strlen (tree, tree[2], bool = false); +extern bool get_range_strlen (tree, tree[2], bool = false, tree * = NULL); extern tree get_maxval_strlen (tree, int); extern void gimplify_and_update_call_from_tree (gimple_stmt_iterator *, tree); extern bool fold_stmt (gimple_stmt_iterator *); diff --git a/gcc/testsuite/gcc.c-torture/execute/memchr-1.c b/gcc/testsuite/gcc.c-torture/execute/memchr-1.c new file mode 100644 index 0000000..ec37632 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/memchr-1.c @@ -0,0 +1,153 @@ +/* PR tree-optimization/86711 - wrong folding of memchr + + Verify that memchr() of arrays initialized with string literals + where the nul doesn't fit in the array doesn't find the nul. */ +typedef __SIZE_TYPE__ size_t; +typedef __WCHAR_TYPE__ wchar_t; + +extern void* memchr (const void*, int, size_t); + +#define A(expr) \ + ((expr) \ + ? (void)0 \ + : (__builtin_printf ("assertion failed on line %i: %s\n", \ + __LINE__, #expr), \ + __builtin_abort ())) + +static const char c = '1'; +static const char s1[1] = "1"; +static const char s4[4] = "1234"; + +static const char s4_2[2][4] = { "1234", "5678" }; +static const char s5_3[3][5] = { "12345", "6789", "01234" }; + +volatile int v0 = 0; +volatile int v1 = 1; +volatile int v2 = 2; +volatile int v3 = 3; +volatile int v4 = 3; + +void test_narrow (void) +{ + int i0 = 0; + int i1 = i0 + 1; + int i2 = i1 + 1; + int i3 = i2 + 1; + int i4 = i3 + 1; + + A (memchr ("" + 1, 0, 0) == 0); + + A (memchr (&c, 0, sizeof c) == 0); + A (memchr (&c + 1, 0, sizeof c - 1) == 0); + A (memchr (&c + i1, 0, sizeof c - i1) == 0); + A (memchr (&c + v1, 0, sizeof c - v1) == 0); + + A (memchr (s1, 0, sizeof s1) == 0); + A (memchr (s1 + 1, 0, sizeof s1 - 1) == 0); + A (memchr (s1 + i1, 0, sizeof s1 - i1) == 0); + A (memchr (s1 + v1, 0, sizeof s1 - v1) == 0); + + A (memchr (&s1, 0, sizeof s1) == 0); + A (memchr (&s1 + 1, 0, sizeof s1 - 1) == 0); + A (memchr (&s1 + i1, 0, sizeof s1 - i1) == 0); + A (memchr (&s1 + v1, 0, sizeof s1 - v1) == 0); + + A (memchr (&s1[0], 0, sizeof s1) == 0); + A (memchr (&s1[0] + 1, 0, sizeof s1 - 1) == 0); + A (memchr (&s1[0] + i1, 0, sizeof s1 - i1) == 0); + A (memchr (&s1[0] + v1, 0, sizeof s1 - v1) == 0); + + A (memchr (&s1[i0], 0, sizeof s1) == 0); + A (memchr (&s1[i0] + 1, 0, sizeof s1 - 1) == 0); + A (memchr (&s1[i0] + i1, 0, sizeof s1 - i1) == 0); + A (memchr (&s1[i0] + v1, 0, sizeof s1 - v1) == 0); + + A (memchr (&s1[v0], 0, sizeof s1) == 0); + A (memchr (&s1[v0] + 1, 0, sizeof s1 - 1) == 0); + A (memchr (&s1[v0] + i1, 0, sizeof s1 - i1) == 0); + A (memchr (&s1[v0] + v1, 0, sizeof s1 - v1) == 0); + + + A (memchr (s4 + i0, 0, sizeof s4 - i0) == 0); + A (memchr (s4 + i1, 0, sizeof s4 - i1) == 0); + A (memchr (s4 + i2, 0, sizeof s4 - i2) == 0); + A (memchr (s4 + i3, 0, sizeof s4 - i3) == 0); + A (memchr (s4 + i4, 0, sizeof s4 - i4) == 0); + + A (memchr (s4 + v0, 0, sizeof s4 - v0) == 0); + A (memchr (s4 + v1, 0, sizeof s4 - v1) == 0); + A (memchr (s4 + v2, 0, sizeof s4 - v2) == 0); + A (memchr (s4 + v3, 0, sizeof s4 - v3) == 0); + A (memchr (s4 + v4, 0, sizeof s4 - v4) == 0); + + + A (memchr (s4_2, 0, sizeof s4_2) == 0); + + A (memchr (s4_2[0], 0, sizeof s4_2[0]) == 0); + A (memchr (s4_2[1], 0, sizeof s4_2[1]) == 0); + + A (memchr (s4_2[0] + 1, 0, sizeof s4_2[0] - 1) == 0); + A (memchr (s4_2[1] + 2, 0, sizeof s4_2[1] - 2) == 0); + A (memchr (s4_2[1] + 3, 0, sizeof s4_2[1] - 3) == 0); + + A (memchr (s4_2[v0], 0, sizeof s4_2[v0]) == 0); + A (memchr (s4_2[v0] + 1, 0, sizeof s4_2[v0] - 1) == 0); + + + /* The following calls must find the nul. */ + A (memchr ("", 0, 1) != 0); + A (memchr (s5_3, 0, sizeof s5_3) == &s5_3[1][4]); + + A (memchr (&s5_3[0][0] + i0, 0, sizeof s5_3 - i0) == &s5_3[1][4]); + A (memchr (&s5_3[0][0] + i1, 0, sizeof s5_3 - i1) == &s5_3[1][4]); + A (memchr (&s5_3[0][0] + i2, 0, sizeof s5_3 - i2) == &s5_3[1][4]); + A (memchr (&s5_3[0][0] + i4, 0, sizeof s5_3 - i4) == &s5_3[1][4]); + + A (memchr (&s5_3[1][i0], 0, sizeof s5_3[1] - i0) == &s5_3[1][4]); +} + +static const wchar_t wc = L'1'; +static const wchar_t ws1[] = L"1"; +static const wchar_t ws4[] = L"\x00123456\x12005678\x12340078\x12345600"; + +void test_wide (void) +{ + int i0 = 0; + int i1 = i0 + 1; + int i2 = i1 + 1; + int i3 = i2 + 1; + int i4 = i3 + 1; + + A (memchr (L"" + 1, 0, 0) == 0); + A (memchr (&wc + 1, 0, 0) == 0); + A (memchr (L"\x12345678", 0, sizeof (wchar_t)) == 0); + + const size_t nb = sizeof ws4; + const size_t nwb = sizeof (wchar_t); + + const char *pws1 = (const char*)ws1; + const char *pws4 = (const char*)ws4; + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + A (memchr (ws1, 0, sizeof ws1) == pws1 + 1); + + A (memchr (&ws4[0], 0, nb) == pws4 + 3); + A (memchr (&ws4[1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 2); + A (memchr (&ws4[2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 1); + A (memchr (&ws4[3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 0); +#else + A (memchr (ws1, 0, sizeof ws1) == pws1 + 0); + + A (memchr (&ws4[0], 0, nb) == pws4 + 0); + A (memchr (&ws4[1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 0); + A (memchr (&ws4[2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 1); + A (memchr (&ws4[3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 2); +#endif +} + + +int main () +{ + test_narrow (); + test_wide (); +} diff --git a/gcc/testsuite/gcc.c-torture/execute/pr86714.c b/gcc/testsuite/gcc.c-torture/execute/pr86714.c new file mode 100644 index 0000000..3ad6852 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/pr86714.c @@ -0,0 +1,26 @@ +/* PR tree-optimization/86714 - tree-ssa-forwprop.c confused by too + long initializer + + The excessively long initializer for a[0] is undefined but this + test verifies that the excess elements are not considered a part + of the value of the array as a matter of QoI. */ + +const char a[2][3] = { "1234", "xyz" }; +char b[6]; + +void *pb = b; + +int main () +{ + __builtin_memcpy (b, a, 4); + __builtin_memset (b + 4, 'a', 2); + + if (b[0] != '1' || b[1] != '2' || b[2] != '3' + || b[3] != 'x' || b[4] != 'a' || b[5] != 'a') + __builtin_abort (); + + if (__builtin_memcmp (pb, "123xaa", 6)) + __builtin_abort (); + + return 0; +} diff --git a/gcc/testsuite/gcc.dg/strlenopt-56.c b/gcc/testsuite/gcc.dg/strlenopt-56.c new file mode 100644 index 0000000..e0e8068 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-56.c @@ -0,0 +1,93 @@ +/* PR tree-optimization/86711 - wrong folding of memchr + + Verify that calls to memchr() with constant arrays initialized + with wide string literals are folded. + + { dg-do compile } + { dg-options "-O1 -Wall -fdump-tree-optimized" } */ + +#include "strlenopt.h" + +typedef __WCHAR_TYPE__ wchar_t; + +extern void* memchr (const void*, int, size_t); + +#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) + + +static const wchar_t wc = L'1'; +static const wchar_t ws1[] = L"1"; +static const wchar_t wsx[] = L"\x12345678"; +static const wchar_t ws4[] = L"\x00123456\x12005678\x12340078\x12345600"; + +void test_wide (void) +{ + int i0 = 0; + int i1 = i0 + 1; + int i2 = i1 + 1; + int i3 = i2 + 1; + int i4 = i3 + 1; + + ELIM (memchr (L"" + 1, 0, 0) == 0); + ELIM (memchr (&wc + 1, 0, 0) == 0); + ELIM (memchr (L"\x12345678", 0, sizeof (wchar_t)) == 0); + + const size_t nb = sizeof ws4; + const size_t nwb = sizeof (wchar_t); + + const char *pws1 = (const char*)ws1; + const char *pws4 = (const char*)ws4; + const char *pwsx = (const char*)wsx; + +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + ELIM (memchr (ws1, 0, sizeof ws1) == pws1 + 1); + ELIM (memchr (wsx, 0, sizeof wsx) == pwsx + sizeof *wsx); + + ELIM (memchr (&ws4[0], 0, nb) == pws4 + 3); + ELIM (memchr (&ws4[1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 2); + ELIM (memchr (&ws4[2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 1); + ELIM (memchr (&ws4[3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 0); + ELIM (memchr (&ws4[4], 0, nb - 4 * nwb) == pws4 + 4 * nwb + 0); + + ELIM (memchr (&ws4[i0], 0, nb) == pws4 + 3); + ELIM (memchr (&ws4[i1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 2); + ELIM (memchr (&ws4[i2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 1); + ELIM (memchr (&ws4[i3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 0); + ELIM (memchr (&ws4[i4], 0, nb - 4 * nwb) == pws4 + 4 * nwb + 0); +#else + ELIM (memchr (ws1, 0, sizeof ws1) == pws1 + 0); + ELIM (memchr (wsx, 0, sizeof wsx) == pwsx + sizeof *wsx); + + ELIM (memchr (&ws4[0], 0, nb) == pws4 + 0); + ELIM (memchr (&ws4[1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 1); + ELIM (memchr (&ws4[2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 2); + ELIM (memchr (&ws4[3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 3); + ELIM (memchr (&ws4[4], 0, nb - 4 * nwb) == pws4 + 4 * nwb + 0); + + ELIM (memchr (&ws4[i0], 0, nb) == pws4 + 0); + ELIM (memchr (&ws4[i1], 0, nb - 1 * nwb) == pws4 + 1 * nwb + 1); + ELIM (memchr (&ws4[i2], 0, nb - 2 * nwb) == pws4 + 2 * nwb + 2); + ELIM (memchr (&ws4[i3], 0, nb - 3 * nwb) == pws4 + 3 * nwb + 3); + ELIM (memchr (&ws4[i4], 0, nb - 4 * nwb) == pws4 + 4 * nwb + 0); +#endif +} + +/* { dg-final { scan-tree-dump-times "memchr" 0 "optimized" } } + { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "optimized" } } */