This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH 1/6] prevent folding of unterminated const arrays in memchr calls (PR 86711, 86714)
- From: Martin Sebor <msebor at gmail dot com>
- To: Gcc Patch List <gcc-patches at gcc dot gnu dot org>, Jeff Law <law at redhat dot com>
- Date: Mon, 13 Aug 2018 15:25:11 -0600
- Subject: [PATCH 1/6] prevent folding of unterminated const arrays in memchr calls (PR 86711, 86714)
- References: <a0d277c4-41c5-61a8-0284-5b1b245e2c74@gmail.com> <ff946355-efec-e286-d7b7-1505a8acc55b@gmail.com> <fcafd5f3-b8fa-faa3-29f4-41fab9d927c8@gmail.com>
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" } } */