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
+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
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);
/* 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*. */
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;
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. */
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;
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))
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);
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);
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;
{
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)
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;
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)
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;
+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
--- /dev/null
+/* 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 ();
+}
--- /dev/null
+/* 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 ();
+}
--- /dev/null
+/* 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 ();
+}