aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2018-07-25 02:11:31 +0000
committerMartin Sebor <msebor@gcc.gnu.org>2018-07-24 20:11:31 -0600
commitbfb9bd47b2daac0a01c561effac1d8244ddd99f6 (patch)
treebf72d7afae065152f74b4f862adffad1c954d61e
parentae752f020fd86b2d34a1556124dc4e5f01a8dcce (diff)
downloadgcc-bfb9bd47b2daac0a01c561effac1d8244ddd99f6.zip
gcc-bfb9bd47b2daac0a01c561effac1d8244ddd99f6.tar.gz
gcc-bfb9bd47b2daac0a01c561effac1d8244ddd99f6.tar.bz2
PR tree-optimization/86622 - incorrect strlen of array of array plus variable offset
PR tree-optimization/86622 - incorrect strlen of array of array plus variable offset PR tree-optimization/86532 - Wrong code due to a wrong strlen folding starting with r262522 gcc/ChangeLog: PR tree-optimization/86622 PR tree-optimization/86532 * builtins.h (string_length): Declare. * builtins.c (c_strlen): Correct handling of non-constant offsets. (check_access): Be prepared for non-constant length ranges. (string_length): Make extern. * expr.c (string_constant): Only handle the minor non-constant array index. Use string_constant to compute the length of a generic string constant. gcc/testsuite/ChangeLog: PR tree-optimization/86622 PR tree-optimization/86532 * gcc.c-torture/execute/strlen-2.c: New test. * gcc.c-torture/execute/strlen-3.c: New test. * gcc.c-torture/execute/strlen-4.c: New test. From-SVN: r262958
-rw-r--r--gcc/ChangeLog12
-rw-r--r--gcc/builtins.c58
-rw-r--r--gcc/builtins.h1
-rw-r--r--gcc/expr.c62
-rw-r--r--gcc/testsuite/ChangeLog8
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/strlen-2.c210
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/strlen-3.c132
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/strlen-4.c232
8 files changed, 668 insertions, 47 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 96c7de9..ee4b212 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,15 @@
+2018-07-24 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/86622
+ PR tree-optimization/86532
+ * builtins.h (string_length): Declare.
+ * builtins.c (c_strlen): Correct handling of non-constant offsets.
+ (check_access): Be prepared for non-constant length ranges.
+ (string_length): Make extern.
+ * expr.c (string_constant): Only handle the minor non-constant
+ array index. Use string_constant to compute the length of
+ a generic string constant.
+
2018-07-24 Richard Sandiford <richard.sandiford@arm.com>
PR tree-optimization/86618
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 539a6d1..aa3e0d8 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -517,11 +517,11 @@ get_pointer_alignment (tree exp)
return align;
}
-/* Return the number of non-zero elements in the sequence
+/* Return the number of leading non-zero elements in the sequence
[ PTR, PTR + MAXELTS ) where each element's size is ELTSIZE bytes.
ELTSIZE must be a power of 2 less than 8. Used by c_strlen. */
-static unsigned
+unsigned
string_length (const void *ptr, unsigned eltsize, unsigned maxelts)
{
gcc_checking_assert (eltsize == 1 || eltsize == 2 || eltsize == 4);
@@ -605,14 +605,21 @@ c_strlen (tree src, int only_value)
/* Set MAXELTS to sizeof (SRC) / sizeof (*SRC) - 1, the maximum possible
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);
+ in case the latter is less than the size of the array, such as when
+ SRC refers to a short string literal used to initialize a large array.
+ In that case, the elements of the array after the terminating NUL are
+ all NUL. */
+ HOST_WIDE_INT strelts = TREE_STRING_LENGTH (src);
+ strelts = strelts / eltsize - 1;
+
+ HOST_WIDE_INT maxelts = strelts;
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;
+ {
+ 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*. */
@@ -620,10 +627,12 @@ c_strlen (tree src, int only_value)
if (byteoff && TREE_CODE (byteoff) != INTEGER_CST)
{
- /* If the string has an internal zero byte (e.g., "foo\0bar"), we can't
- compute the offset to the following null if we don't know where to
+ /* If the string has an internal NUL character followed by any
+ non-NUL characters (e.g., "foo\0bar"), we can't compute
+ the offset to the following NUL if we don't know where to
start searching for it. */
- if (string_length (ptr, eltsize, maxelts) < maxelts)
+ unsigned len = string_length (ptr, eltsize, strelts);
+ if (len < strelts)
{
/* Return when an embedded null character is found. */
return NULL_TREE;
@@ -633,12 +642,17 @@ c_strlen (tree src, int only_value)
return ssize_int (0);
/* We don't know the starting offset, but we do know that the string
- has no internal zero bytes. We can assume that the offset falls
- within the bounds of the string; otherwise, the programmer deserves
- 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);
+ 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;
+ 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);
+ return fold_build3_loc (loc, COND_EXPR, ssizetype, condexp, lenexp,
+ build_zero_cst (ssizetype));
}
/* Offset from the beginning of the string in elements. */
@@ -3192,15 +3206,13 @@ check_access (tree exp, tree, tree, tree dstwrite,
if (dstwrite)
get_size_range (dstwrite, range);
- /* This can happen at -O0. */
- if (range[0] && TREE_CODE (range[0]) != INTEGER_CST)
- return false;
-
tree func = get_callee_fndecl (exp);
/* First check the number of bytes to be written against the maximum
object size. */
- if (range[0] && tree_int_cst_lt (maxobjsize, range[0]))
+ if (range[0]
+ && TREE_CODE (range[0]) == INTEGER_CST
+ && tree_int_cst_lt (maxobjsize, range[0]))
{
if (TREE_NO_WARNING (exp))
return false;
@@ -3235,9 +3247,11 @@ check_access (tree exp, tree, tree, tree dstwrite,
if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
{
if (range[0]
+ && TREE_CODE (range[0]) == INTEGER_CST
&& ((tree_fits_uhwi_p (dstsize)
&& tree_int_cst_lt (dstsize, range[0]))
- || (tree_fits_uhwi_p (dstwrite)
+ || (dstwrite
+ && tree_fits_uhwi_p (dstwrite)
&& tree_int_cst_lt (dstwrite, range[0]))))
{
if (TREE_NO_WARNING (exp))
diff --git a/gcc/builtins.h b/gcc/builtins.h
index c922904..2e0a2f9 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -57,6 +57,7 @@ extern unsigned int get_object_alignment (tree);
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 void expand_builtin_setjmp_setup (rtx, rtx);
extern void expand_builtin_setjmp_receiver (rtx);
diff --git a/gcc/expr.c b/gcc/expr.c
index f665e18..de6709d 100644
--- a/gcc/expr.c
+++ b/gcc/expr.c
@@ -11294,12 +11294,15 @@ string_constant (tree arg, tree *ptr_offset)
tree idx = TREE_OPERAND (arg, 1);
if (TREE_CODE (idx) != INTEGER_CST)
{
- /* 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
+ /* From a pointer (but not array) argument 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);
+
+ if (TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE)
+ return NULL_TREE;
}
}
array = get_addr_base_and_unit_offset (ref, &base_off);
@@ -11327,6 +11330,12 @@ string_constant (tree arg, tree *ptr_offset)
tree offset;
if (tree str = string_constant (arg0, &offset))
{
+ /* Avoid pointers to arrays (see bug 86622). */
+ if (POINTER_TYPE_P (TREE_TYPE (arg))
+ && TREE_CODE (TREE_TYPE (TREE_TYPE (arg))) == ARRAY_TYPE
+ && TREE_CODE (TREE_OPERAND (arg0, 0)) == ARRAY_REF)
+ return NULL_TREE;
+
tree type = TREE_TYPE (arg1);
*ptr_offset = fold_build2 (PLUS_EXPR, type, offset, arg1);
return str;
@@ -11343,16 +11352,17 @@ string_constant (tree arg, tree *ptr_offset)
{
if (TREE_CODE (TREE_TYPE (array)) != ARRAY_TYPE)
return NULL_TREE;
- if (tree eltsize = TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (array))))
- {
- /* 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 NULL_TREE;
+
+ gcc_assert (TREE_CODE (arg) == ARRAY_REF);
+ tree chartype = TREE_TYPE (TREE_TYPE (TREE_OPERAND (arg, 0)));
+ if (TREE_CODE (chartype) != INTEGER_TYPE)
+ return NULL;
+
+ tree charsize = array_ref_element_size (arg);
+ /* Set the non-constant offset to the non-constant index scaled
+ by the size of the character type. */
+ offset = fold_build2 (MULT_EXPR, TREE_TYPE (offset),
+ fold_convert (sizetype, varidx), charsize);
}
if (TREE_CODE (array) == STRING_CST)
@@ -11371,11 +11381,6 @@ string_constant (tree arg, tree *ptr_offset)
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;
@@ -11391,11 +11396,15 @@ string_constant (tree arg, tree *ptr_offset)
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);
- }
+ if (!base_off.is_constant (&cstoff))
+ return NULL_TREE;
+
+ cstoff = (cstoff - fieldoff) / BITS_PER_UNIT;
+ tree off = build_int_cst (sizetype, cstoff);
+ if (varidx)
+ offset = fold_build2 (PLUS_EXPR, TREE_TYPE (offset), offset, off);
+ else
+ offset = off;
}
if (!init || TREE_CODE (init) != STRING_CST)
@@ -11413,8 +11422,11 @@ string_constant (tree arg, tree *ptr_offset)
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));
+ unsigned HOST_WIDE_INT charsize
+ = tree_to_uhwi (TYPE_SIZE_UNIT (TREE_TYPE (TREE_TYPE (init))));
+ 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)
return NULL_TREE;
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index f8cf9ee..af40567 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+2018-07-24 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/86622
+ PR tree-optimization/86532
+ * gcc.c-torture/execute/strlen-2.c: New test.
+ * gcc.c-torture/execute/strlen-3.c: New test.
+ * gcc.c-torture/execute/strlen-4.c: New test.
+
2018-07-24 David Malcolm <dmalcolm@redhat.com>
PR tree-optimization/86636
diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-2.c b/gcc/testsuite/gcc.c-torture/execute/strlen-2.c
new file mode 100644
index 0000000..4519f6a
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/strlen-2.c
@@ -0,0 +1,210 @@
+/* PR tree-optimization/86532 - Wrong code due to a wrong strlen folding */
+
+extern __SIZE_TYPE__ strlen (const char*);
+
+static const char a[2][3] = { "1", "12" };
+static const char b[2][2][5] = { { "1", "12" }, { "123", "1234" } };
+
+volatile int v0 = 0;
+volatile int v1 = 1;
+volatile int v2 = 2;
+
+#define A(expr) \
+ ((expr) ? (void)0 : (__builtin_printf ("assertion on line %i: %s\n", \
+ __LINE__, #expr), \
+ __builtin_abort ()))
+
+void test_array_ref_2_3 (void)
+{
+ A (strlen (a[v0]) == 1);
+ A (strlen (&a[v0][v0]) == 1);
+ A (strlen (&a[0][v0]) == 1);
+ A (strlen (&a[v0][0]) == 1);
+
+ A (strlen (a[v1]) == 2);
+ A (strlen (&a[v1][0]) == 2);
+ A (strlen (&a[1][v0]) == 2);
+ A (strlen (&a[v1][v0]) == 2);
+
+ A (strlen (&a[v1][1]) == 1);
+ A (strlen (&a[v1][1]) == 1);
+
+ A (strlen (&a[v1][2]) == 0);
+ A (strlen (&a[v1][v2]) == 0);
+
+ int i0 = 0;
+ int i1 = i0 + 1;
+ int i2 = i1 + 1;
+
+ A (strlen (a[v0]) == 1);
+ A (strlen (&a[v0][v0]) == 1);
+ A (strlen (&a[i0][v0]) == 1);
+ A (strlen (&a[v0][i0]) == 1);
+
+ A (strlen (a[v1]) == 2);
+ A (strlen (&a[v1][i0]) == 2);
+ A (strlen (&a[i1][v0]) == 2);
+ A (strlen (&a[v1][v0]) == 2);
+
+ A (strlen (&a[v1][i1]) == 1);
+ A (strlen (&a[v1][i1]) == 1);
+
+ A (strlen (&a[v1][i2]) == 0);
+ A (strlen (&a[v1][v2]) == 0);
+}
+
+void test_array_off_2_3 (void)
+{
+ A (strlen (a[0] + 0) == 1);
+ A (strlen (a[0] + v0) == 1);
+ A (strlen (a[v0] + 0) == 1);
+ A (strlen (a[v0] + v0) == 1);
+
+ A (strlen (a[v1] + 0) == 2);
+ A (strlen (a[1] + v0) == 2);
+ A (strlen (a[v1] + 0) == 2);
+ A (strlen (a[v1] + v0) == 2);
+
+ A (strlen (a[v1] + 1) == 1);
+ A (strlen (a[v1] + v1) == 1);
+
+ A (strlen (a[v1] + 2) == 0);
+ A (strlen (a[v1] + v2) == 0);
+
+ int i0 = 0;
+ int i1 = i0 + 1;
+ int i2 = i1 + 1;
+
+ A (strlen (a[i0] + i0) == 1);
+ A (strlen (a[i0] + v0) == 1);
+ A (strlen (a[v0] + i0) == 1);
+ A (strlen (a[v0] + v0) == 1);
+
+ A (strlen (a[v1] + i0) == 2);
+ A (strlen (a[i1] + v0) == 2);
+ A (strlen (a[v1] + i0) == 2);
+ A (strlen (a[v1] + v0) == 2);
+
+ A (strlen (a[v1] + i1) == 1);
+ A (strlen (a[v1] + v1) == 1);
+
+ A (strlen (a[v1] + i2) == 0);
+ A (strlen (a[v1] + v2) == 0);
+}
+
+void test_array_ref_2_2_5 (void)
+{
+ A (strlen (b[0][v0]) == 1);
+ A (strlen (b[v0][0]) == 1);
+
+ A (strlen (&b[0][0][v0]) == 1);
+ A (strlen (&b[0][v0][0]) == 1);
+ A (strlen (&b[v0][0][0]) == 1);
+
+ A (strlen (&b[0][v0][v0]) == 1);
+ A (strlen (&b[v0][0][v0]) == 1);
+ A (strlen (&b[v0][v0][0]) == 1);
+
+ A (strlen (b[0][v1]) == 2);
+ A (strlen (b[v1][0]) == 3);
+
+ A (strlen (&b[0][0][v1]) == 0);
+ A (strlen (&b[0][v1][0]) == 2);
+ A (strlen (&b[v0][0][0]) == 1);
+
+ A (strlen (&b[0][v0][v0]) == 1);
+ A (strlen (&b[v0][0][v0]) == 1);
+ A (strlen (&b[v0][v0][0]) == 1);
+
+ A (strlen (&b[0][v1][v1]) == 1);
+ A (strlen (&b[v1][0][v1]) == 2);
+ A (strlen (&b[v1][v1][0]) == 4);
+ A (strlen (&b[v1][v1][1]) == 3);
+ A (strlen (&b[v1][v1][2]) == 2);
+
+ int i0 = 0;
+ int i1 = i0 + 1;
+ int i2 = i1 + 1;
+
+ A (strlen (b[i0][v0]) == 1);
+ A (strlen (b[v0][i0]) == 1);
+
+ A (strlen (&b[i0][i0][v0]) == 1);
+ A (strlen (&b[i0][v0][i0]) == 1);
+ A (strlen (&b[v0][i0][i0]) == 1);
+
+ A (strlen (&b[i0][v0][v0]) == 1);
+ A (strlen (&b[v0][i0][v0]) == 1);
+ A (strlen (&b[v0][v0][i0]) == 1);
+
+ A (strlen (b[i0][v1]) == 2);
+ A (strlen (b[v1][i0]) == 3);
+
+ A (strlen (&b[i0][i0][v1]) == 0);
+ A (strlen (&b[i0][v1][i0]) == 2);
+ A (strlen (&b[v0][i0][i0]) == 1);
+
+ A (strlen (&b[i0][v0][v0]) == 1);
+ A (strlen (&b[v0][i0][v0]) == 1);
+ A (strlen (&b[v0][v0][i0]) == 1);
+
+ A (strlen (&b[i0][v1][v1]) == 1);
+ A (strlen (&b[v1][i0][v1]) == 2);
+ A (strlen (&b[v1][v1][i0]) == 4);
+ A (strlen (&b[v1][v1][i1]) == 3);
+ A (strlen (&b[v1][v1][i2]) == 2);
+}
+
+void test_array_off_2_2_5 (void)
+{
+ A (strlen (b[0][0] + v0) == 1);
+ A (strlen (b[0][v0] + v0) == 1);
+ A (strlen (b[v0][0] + v0) == 1);
+ A (strlen (b[v0][v0] + v0) == 1);
+
+ A (strlen (b[0][0] + v1) == 0);
+ A (strlen (b[0][v1] + 0) == 2);
+ A (strlen (b[v0][0] + 0) == 1);
+
+ A (strlen (b[0][v0] + v0) == 1);
+ A (strlen (b[v0][0] + v0) == 1);
+ A (strlen (b[v0][v0] + 0) == 1);
+
+ A (strlen (b[0][v1] + v1) == 1);
+ A (strlen (b[v1][0] + v1) == 2);
+ A (strlen (b[v1][v1] + 0) == 4);
+ A (strlen (b[v1][v1] + 1) == 3);
+ A (strlen (b[v1][v1] + 2) == 2);
+
+ int i0 = 0;
+ int i1 = i0 + 1;
+ int i2 = i1 + 1;
+
+ A (strlen (b[i0][i0] + v0) == 1);
+ A (strlen (b[i0][v0] + v0) == 1);
+ A (strlen (b[v0][i0] + v0) == 1);
+ A (strlen (b[v0][v0] + v0) == 1);
+
+ A (strlen (b[i0][i0] + v1) == 0);
+ A (strlen (b[i0][v1] + i0) == 2);
+ A (strlen (b[v0][i0] + i0) == 1);
+
+ A (strlen (b[i0][v0] + v0) == 1);
+ A (strlen (b[v0][i0] + v0) == 1);
+ A (strlen (b[v0][v0] + i0) == 1);
+
+ A (strlen (b[i0][v1] + v1) == 1);
+ A (strlen (b[v1][i0] + v1) == 2);
+ A (strlen (b[v1][v1] + i0) == 4);
+ A (strlen (b[v1][v1] + i1) == 3);
+ A (strlen (b[v1][v1] + i2) == 2);
+}
+
+int main ()
+{
+ test_array_ref_2_3 ();
+ test_array_off_2_3 ();
+
+ test_array_ref_2_2_5 ();
+ test_array_off_2_2_5 ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-3.c b/gcc/testsuite/gcc.c-torture/execute/strlen-3.c
new file mode 100644
index 0000000..182afdd
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/strlen-3.c
@@ -0,0 +1,132 @@
+/* PR tree-optimization/86532 - Wrong code due to a wrong strlen folding
+ starting with r262522
+ Exercise strlen() with a multi-dimensional array of strings with
+ embedded nuls. */
+
+extern __SIZE_TYPE__ strlen (const char*);
+
+static const char a[2][3][9] = {
+ { "1", "1\0002" },
+ { "12\0003", "123\0004" }
+};
+
+volatile int v0 = 0;
+volatile int v1 = 1;
+volatile int v2 = 2;
+volatile int v3 = 3;
+volatile int v4 = 4;
+volatile int v5 = 5;
+volatile int v6 = 6;
+volatile int v7 = 7;
+
+#define A(expr) \
+ ((expr) ? (void)0 : (__builtin_printf ("assertion on line %i: %s\n", \
+ __LINE__, #expr), \
+ __builtin_abort ()))
+
+void test_array_ref (void)
+{
+ int i0 = 0;
+ int i1 = i0 + 1;
+ int i2 = i1 + 1;
+ int i3 = i2 + 1;
+ int i4 = i3 + 1;
+ int i5 = i4 + 1;
+ int i6 = i5 + 1;
+ int i7 = i6 + 1;
+
+ A (strlen (a[0][0]) == 1);
+ A (strlen (a[0][1]) == 1);
+
+ A (strlen (a[1][0]) == 2);
+ A (strlen (a[1][1]) == 3);
+
+ A (strlen (&a[0][0][0]) == 1);
+ A (strlen (&a[0][1][0]) == 1);
+
+ A (strlen (&a[1][0][0]) == 2);
+ A (strlen (&a[1][1][0]) == 3);
+
+ A (strlen (&a[0][0][0] + 1) == 0);
+ A (strlen (&a[0][1][0] + 1) == 0);
+ A (strlen (&a[0][1][0] + 2) == 1);
+ A (strlen (&a[0][1][0] + 3) == 0);
+ A (strlen (&a[0][1][0] + 7) == 0);
+
+ A (strlen (&a[1][0][0] + 1) == 1);
+ A (strlen (&a[1][1][0] + 1) == 2);
+ A (strlen (&a[1][1][0] + 2) == 1);
+ A (strlen (&a[1][1][0] + 7) == 0);
+
+
+ A (strlen (a[i0][i0]) == 1);
+ A (strlen (a[i0][i1]) == 1);
+
+ A (strlen (a[i1][i0]) == 2);
+ A (strlen (a[i1][i1]) == 3);
+
+ A (strlen (&a[i0][i0][i0]) == 1);
+ A (strlen (&a[i0][i1][i0]) == 1);
+ A (strlen (&a[i0][i1][i1]) == 0);
+ A (strlen (&a[i0][i1][i2]) == 1);
+ A (strlen (&a[i0][i1][i3]) == 0);
+ A (strlen (&a[i0][i1][i3]) == 0);
+
+ A (strlen (&a[i1][i0][i0]) == 2);
+ A (strlen (&a[i1][i1][i0]) == 3);
+ A (strlen (&a[i1][i1][i1]) == 2);
+ A (strlen (&a[i1][i1][i2]) == 1);
+ A (strlen (&a[i1][i1][i3]) == 0);
+ A (strlen (&a[i1][i1][i4]) == 1);
+ A (strlen (&a[i1][i1][i5]) == 0);
+ A (strlen (&a[i1][i1][i6]) == 0);
+ A (strlen (&a[i1][i1][i7]) == 0);
+
+ A (strlen (&a[i0][i0][i0] + i1) == 0);
+ A (strlen (&a[i0][i1][i0] + i1) == 0);
+ A (strlen (&a[i0][i1][i0] + i7) == 0);
+
+ A (strlen (&a[i1][i0][i0] + i1) == 1);
+ A (strlen (&a[i1][i1][i0] + i1) == 2);
+ A (strlen (&a[i1][i1][i0] + i2) == 1);
+ A (strlen (&a[i1][i1][i0] + i3) == 0);
+ A (strlen (&a[i1][i1][i0] + i4) == 1);
+ A (strlen (&a[i1][i1][i0] + i5) == 0);
+ A (strlen (&a[i1][i1][i0] + i6) == 0);
+ A (strlen (&a[i1][i1][i0] + i7) == 0);
+
+
+ A (strlen (a[i0][i0]) == 1);
+ A (strlen (a[i0][i1]) == 1);
+
+ A (strlen (a[i1][i0]) == 2);
+ A (strlen (a[i1][i1]) == 3);
+
+ A (strlen (&a[i0][i0][i0]) == 1);
+ A (strlen (&a[i0][i1][i0]) == 1);
+
+ A (strlen (&a[i1][i0][i0]) == 2);
+ A (strlen (&a[i1][i1][i0]) == 3);
+
+ A (strlen (&a[i0][i0][i0] + v1) == 0);
+ A (strlen (&a[i0][i0][i0] + v2) == 0);
+ A (strlen (&a[i0][i0][i0] + v7) == 0);
+
+ A (strlen (&a[i0][i1][i0] + v1) == 0);
+ A (strlen (&a[i0][i1][i0] + v2) == 1);
+ A (strlen (&a[i0][i1][i0] + v3) == 0);
+
+ A (strlen (&a[i1][i0][i0] + v1) == 1);
+ A (strlen (&a[i1][i1][i0] + v1) == 2);
+ A (strlen (&a[i1][i1][i0] + v2) == 1);
+ A (strlen (&a[i1][i1][i0] + v3) == 0);
+ A (strlen (&a[i1][i1][i0] + v4) == 1);
+ A (strlen (&a[i1][i1][i0] + v5) == 0);
+ A (strlen (&a[i1][i1][i0] + v6) == 0);
+ A (strlen (&a[i1][i1][i0] + v7) == 0);
+}
+
+int main (void)
+{
+ test_array_ref ();
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/strlen-4.c b/gcc/testsuite/gcc.c-torture/execute/strlen-4.c
new file mode 100644
index 0000000..c3b2c77
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/strlen-4.c
@@ -0,0 +1,232 @@
+/* PR tree-optimization/86622 - incorrect strlen of array of array plus
+ variable offset
+ Exercise strlen() with a multi-dimensional array of strings with
+ offsets. */
+
+extern int printf (const char*, ...);
+extern __SIZE_TYPE__ strlen (const char*);
+
+typedef char A28[28];
+typedef A28 A3_28[3];
+typedef A3_28 A2_3_28[2];
+
+static const A2_3_28 a = {
+ /* [0][0] [0][1] [0][2] */
+ { "1\00012", "123\0001234", "12345\000123456" },
+ /* [1][0] [1][1] [1][2] */
+ { "1234567\00012345678", "123456789\0001234567890", "12345678901\000123456789012" }
+};
+
+volatile int v0 = 0;
+volatile int v1 = 1;
+volatile int v2 = 2;
+volatile int v3 = 3;
+volatile int v4 = 4;
+volatile int v5 = 5;
+volatile int v6 = 6;
+volatile int v7 = 7;
+
+#define A(expr, N) \
+ ((strlen (expr) == N) \
+ ? (void)0 : (printf ("line %i: strlen (%s = \"%s\") != %i\n", \
+ __LINE__, #expr, expr, N), \
+ __builtin_abort ()))
+
+/* Verify that strlen() involving pointer to array arguments computes
+ the correct result. */
+
+void test_array_ptr (void)
+{
+ /* Compute the length of the string at the refeenced array. */
+ A (*(&a[0][0] + 0), 1);
+ A (*(&a[0][0] + 1), 3);
+ A (*(&a[0][0] + 2), 5);
+
+ A (*(&a[0][1] - 1), 1);
+ A (*(&a[0][1] + 0), 3);
+ A (*(&a[0][1] + 1), 5);
+
+ A (*(&a[0][2] - 2), 1);
+ A (*(&a[0][2] - 1), 3);
+ A (*(&a[0][2] + 0), 5);
+
+ A (*(&a[1][0] + 0), 7);
+ A (*(&a[1][0] + 1), 9);
+ A (*(&a[1][0] + 2), 11);
+
+ A (*(&a[1][1] - 1), 7);
+ A (*(&a[1][1] + 0), 9);
+ A (*(&a[1][1] + 1), 11);
+
+ A (*(&a[1][2] - 2), 7);
+ A (*(&a[1][2] - 1), 9);
+ A (*(&a[1][2] - 0), 11);
+
+ /* Compute the length of the string past the first nul. */
+ A (*(&a[0][0] + 0) + 2, 2);
+ A (*(&a[0][0] + 1) + 4, 4);
+ A (*(&a[0][0] + 2) + 6, 6);
+
+ /* Compute the length of the string past the second nul. */
+ A (*(&a[0][0] + 0) + 5, 0);
+ A (*(&a[0][0] + 1) + 10, 0);
+ A (*(&a[0][0] + 2) + 14, 0);
+
+ int i0 = 0;
+ int i1 = i0 + 1;
+ int i2 = i1 + 1;
+ int i3 = i2 + 1;
+ int i4 = i3 + 1;
+ int i5 = i4 + 1;
+
+ A (*(&a[0][0] + i0), 1);
+ A (*(&a[0][0] + i1), 3);
+ A (*(&a[0][0] + i2), 5);
+
+ A (*(&a[0][1] - i1), 1);
+ A (*(&a[0][1] + i0), 3);
+ A (*(&a[0][1] + i1), 5);
+
+ A (*(&a[0][2] - i2), 1);
+ A (*(&a[0][2] - i1), 3);
+ A (*(&a[0][2] + i0), 5);
+
+ A (*(&a[1][0] + i0), 7);
+ A (*(&a[1][0] + i1), 9);
+ A (*(&a[1][0] + i2), 11);
+
+ A (*(&a[1][1] - i1), 7);
+ A (*(&a[1][1] + i0), 9);
+ A (*(&a[1][1] + i1), 11);
+
+ A (*(&a[1][2] - i2), 7);
+ A (*(&a[1][2] - i1), 9);
+ A (*(&a[1][2] - i0), 11);
+
+
+ A (*(&a[i0][i0] + i0), 1);
+ A (*(&a[i0][i0] + i1), 3);
+ A (*(&a[i0][i0] + i2), 5);
+
+ A (*(&a[i0][i1] - i1), 1);
+ A (*(&a[i0][i1] + i0), 3);
+ A (*(&a[i0][i1] + i1), 5);
+
+ A (*(&a[i0][i2] - i2), 1);
+ A (*(&a[i0][i2] - i1), 3);
+ A (*(&a[i0][i2] + i0), 5);
+
+ A (*(&a[i1][i0] + i0), 7);
+ A (*(&a[i1][i0] + i1), 9);
+ A (*(&a[i1][i0] + i2), 11);
+
+ A (*(&a[i1][i1] - i1), 7);
+ A (*(&a[i1][i1] + i0), 9);
+ A (*(&a[i1][i1] + i1), 11);
+
+ A (*(&a[i1][i2] - i2), 7);
+ A (*(&a[i1][i2] - i1), 9);
+ A (*(&a[i1][i2] - i0), 11);
+
+
+ A (*(&a[i0][i0] + v0), 1);
+ A (*(&a[i0][i0] + v1), 3);
+ A (*(&a[i0][i0] + v2), 5);
+
+ A (*(&a[i0][i1] - v1), 1);
+ A (*(&a[i0][i1] + v0), 3);
+ A (*(&a[i0][i1] + v1), 5);
+
+ A (*(&a[i0][i2] - v2), 1);
+ A (*(&a[i0][i2] - v1), 3);
+ A (*(&a[i0][i2] + v0), 5);
+
+ A (*(&a[i1][i0] + v0), 7);
+ A (*(&a[i1][i0] + v1), 9);
+ A (*(&a[i1][i0] + v2), 11);
+
+ A (*(&a[i1][i1] - v1), 7);
+ A (*(&a[i1][i1] + v0), 9);
+ A (*(&a[i1][i1] + v1), 11);
+
+ A (*(&a[i1][i2] - v2), 7);
+ A (*(&a[i1][i2] - v1), 9);
+ A (*(&a[i1][i2] - v0), 11);
+
+
+ A (*(&a[i0][i0] + v0) + i1, 0);
+ A (*(&a[i0][i0] + v1) + i2, 1);
+ A (*(&a[i0][i0] + v2) + i3, 2);
+
+ A (*(&a[i0][i1] - v1) + v1, 0);
+ A (*(&a[i0][i1] + v0) + v3, 0);
+ A (*(&a[i0][i1] + v1) + v5, 0);
+
+ A (*(&a[i0][v1] - i1) + i1, 0);
+ A (*(&a[i0][v1] + i0) + i3, 0);
+ A (*(&a[i0][v1] + i1) + i5, 0);
+}
+
+static const A3_28* const pa0 = &a[0];
+static const A3_28* const pa1 = &a[1];
+
+static const A3_28* const paa[] = { &a[0], &a[1] };
+
+/* Verify that strlen() involving pointers and arrays of pointers
+ to array arguments computes the correct result. */
+
+void test_ptr_array (void)
+{
+ int i0 = 0;
+ int i1 = i0 + 1;
+ int i2 = i1 + 1;
+ int i3 = i2 + 1;
+
+ A (*((*pa0) + i0), 1);
+ A (*((*pa0) + i1), 3);
+ A (*((*pa0) + i2), 5);
+
+ A (*(pa0[0] + i0), 1);
+ A (*(pa0[0] + i1), 3);
+ A (*(pa0[0] + i2), 5);
+
+ A ((*pa0)[i0] + i1, 0);
+ A ((*pa0)[i1] + i2, 1);
+ A ((*pa0)[i2] + i3, 2);
+
+
+ A (*((*pa1) + i0), 7);
+ A (*((*pa1) + i1), 9);
+ A (*((*pa1) + i2), 11);
+
+ A (*(pa1[0] + i0), 7);
+ A (*(pa1[0] + i1), 9);
+ A (*(pa1[0] + i2), 11);
+
+ A ((*pa1)[i0] + i1, 6);
+ A ((*pa1)[i1] + i2, 7);
+ A ((*pa1)[i2] + i3, 8);
+
+ A (*(*(paa[0]) + i0), 1);
+ A (*(*(paa[0]) + i1), 3);
+ A (*(*(paa[0]) + i2), 5);
+
+ A (*(*(paa[1]) + i0), 7);
+ A (*(*(paa[1]) + i1), 9);
+ A (*(*(paa[1]) + i2), 11);
+
+ A (*(*(paa[1]) - i1), 5);
+ A (*(*(paa[1]) - i2), 3);
+ A (*(*(paa[1]) - i3), 1);
+
+ A (*(*(paa[0]) + i0) + i1, 0);
+ A (*(*(paa[0]) + i1) + i2, 1);
+ A (*(*(paa[0]) + i2) + i3, 2);
+}
+
+int main (void)
+{
+ test_array_ptr ();
+
+ test_ptr_array ();
+}