+2019-10-28 Martin Sebor <msebor@redhat.com>
+
+ * tree-ssa-strlen.c (get_addr_stridx): Add argument and use it.
+ (handle_store): Pass argument to get_addr_stridx.
+
2019-10-28 Martin Sebor <msebor@redhat.com>
PR tree-optimization/92226
+2019-10-28 Martin Sebor <msebor@redhat.com>
+
+ * gcc.dg/strlenopt-89.c: New test.
+ * gcc.dg/strlenopt-90.c: New test.
+ * gcc.dg/Wstringop-overflow-20.c: New test.
+
2019-10-28 Martin Sebor <msebor@redhat.com>
PR tree-optimization/92226
--- /dev/null
+/* PR tree-optimization/92226 - live nul char store to array eliminated
+ Test to verify warnings are issued for overflow detected thanks to
+ the enhancement committed on top of the fix for PR 92226.
+ { dg-do compile }
+ { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#include "strlenopt.h"
+
+#define NOIPA __attribute__ ((noipa))
+
+#define T(MIN, MAX, SIZE, IDX) \
+ NOIPA void \
+ test_ ## MIN ## _ ## MAX ## _ ## SIZE ## _ ## IDX (const char *s) \
+ { \
+ size_t len = strlen (s); \
+ if (MIN <= len && len <= MAX) \
+ { \
+ extern char d[]; \
+ strcpy (d, s); \
+ d[IDX] = 0; \
+ extern char a ## SIZE[SIZE]; \
+ strcpy (a ## SIZE, d); \
+ } \
+ } typedef void dummy_type
+
+
+T (2, 2, 1, 0);
+T (2, 2, 1, 1); // { dg-warning "writing 2 bytes into a region of size 1" }
+T (2, 2, 1, 2); // { dg-warning "writing 3 bytes into a region of size 1" }
+
+T (2, 3, 1, 0);
+T (2, 3, 1, 1); // { dg-warning "writing 2 bytes into a region of size 1" }
+T (2, 3, 1, 2); // { dg-warning "writing 3 bytes into a region of size 1" "" { xfail *-*-*} }
+T (2, 3, 1, 3); // { dg-warning "writing 4 bytes into a region of size 1" "" { xfail *-*-* } }
+
+T (5, 7, 3, 1);
+T (5, 7, 3, 2);
+T (5, 7, 3, 3); // { dg-warning "writing 4 bytes into a region of size 3" }
+T (5, 7, 3, 4); // { dg-warning "writing 5 bytes into a region of size 3" }
+T (5, 7, 3, 5); // { dg-warning "writing 6 bytes into a region of size 3" "" { xfail *-*-* } }
--- /dev/null
+/* PR tree-optimization/92226 - live nul char store to array eliminated
+ { dg-do compile }
+ { dg-options "-O2 -fdump-tree-strlen" } */
+
+#include "strlenopt.h"
+
+#define NOIPA __attribute__ ((noipa))
+
+/* Verify that the nul store into the destination is only eliminated
+ when overwrites the existing terminating nul added by the strcpy call.
+ Also verify that the second strlen call is eliminated in all cases. */
+#define T(SIZE, IDX) \
+ NOIPA void test_ ## SIZE ## _store_nul_ ## IDX (const char *s) \
+ { \
+ extern char a ## SIZE[SIZE]; \
+ char *d = a ## SIZE; \
+ size_t len = SIZE - 1; \
+ size_t idx = IDX; \
+ if (strlen (s) == len) \
+ { \
+ strcpy (d, s); \
+ d[idx] = 0; \
+ if (strlen (d) != idx) \
+ abort (); \
+ } \
+ } typedef void dummy_type
+
+
+T (1, 0); // expect nul store to be eliminated
+
+T (2, 0); // nul store must be retained
+T (2, 1); // expect nul store to be eliminated
+
+// Same as above but for larger arrays.
+T (3, 0);
+T (3, 1);
+T (3, 2);
+
+T (4, 0);
+T (4, 1);
+T (4, 2);
+T (4, 3);
+
+T (5, 0);
+T (5, 1);
+T (5, 2);
+T (5, 3);
+T (5, 4);
+
+T (6, 0);
+T (6, 1);
+T (6, 2);
+T (6, 3);
+T (6, 4);
+T (6, 5);
+
+T (7, 0);
+T (7, 1);
+T (7, 2);
+T (7, 3);
+T (7, 4);
+T (7, 5);
+T (7, 6);
+
+T (8, 0);
+T (8, 1);
+T (8, 2);
+T (8, 3);
+T (8, 4);
+T (8, 5);
+T (8, 6);
+T (8, 7);
+
+/* Verify that each function makes just one call to strlen to compute
+ the length of its argument (and not also to compute the length of
+ the copy):
+ { dg-final { scan-tree-dump-times "strlen \\(s_" 36 "strlen1" } }
+ { dg-final { scan-tree-dump-not "strlen \\(\\&a" "strlen1" } }
+
+ Verify that nul stores into the last array element have been eliminated
+ (they are preceded by a strcpy storing into all the elements of the array:
+ { dg-final { scan-tree-dump-not "a1\\\] = 0;" "strlen1" } }
+ { dg-final { scan-tree-dump-not "a2 \\\+ 1B\\\] = 0;" "strlen1" } }
+ { dg-final { scan-tree-dump-not "a3 \\\+ 2B\\\] = 0;" "strlen1" } }
+ { dg-final { scan-tree-dump-not "a4 \\\+ 3B\\\] = 0;" "strlen1" } }
+ { dg-final { scan-tree-dump-not "a5 \\\+ 4B\\\] = 0;" "strlen1" } }
+ { dg-final { scan-tree-dump-not "a6 \\\+ 5B\\\] = 0;" "strlen1" } }
+ { dg-final { scan-tree-dump-not "a7 \\\+ 6B\\\] = 0;" "strlen1" } }
+ { dg-final { scan-tree-dump-not "a8 \\\+ 7B\\\] = 0;" "strlen1" } } */
--- /dev/null
+/* PR tree-optimization/92226 - live nul char store to array eliminated
+ { dg-do compile }
+ { dg-options "-O2 -fdump-tree-strlen" } */
+
+#include "strlenopt.h"
+
+#define NOIPA __attribute__ ((noipa))
+
+#define T(MIN, MAX, SIZE, IDX) \
+ NOIPA void \
+ test_ ## MIN ## _ ## MAX ## _ ## SIZE ## _ ## IDX (const char *s) \
+ { \
+ extern char a ## SIZE[SIZE]; \
+ char *d = a ## SIZE; \
+ size_t len = strlen (s); \
+ size_t idx = IDX; \
+ if (MIN <= len && len <= MAX) \
+ { \
+ strcpy (d, s); \
+ d[idx] = 0; \
+ if (strlen (d) != idx) \
+ abort (); \
+ } \
+ } typedef void dummy_type
+
+
+/* The final nul store must be retained but the second strlen call should
+ be eliminated because the final length of the destination after the nul
+ store must be equal to the index of the store. */
+T (0, 2, 4, 0);
+
+/* Not handled yet (see below):
+ T (0, 2, 4, 1); */
+
+/* Not handled yet: in addition to the cases above, the second strlen
+ call can also be eliminated in those below because in both the final
+ length of the destination after the nul store must be in the same
+ range as the length of the source.
+ T (0, 2, 4, 2);
+ T (0, 2, 4, 3); */
+
+T (2, 3, 4, 0);
+T (2, 3, 4, 1);
+
+/* Not handled yet (see above):
+ T (2, 3, 4, 2);
+ T (2, 3, 4, 3); */
+
+T (3, 4, 5, 0);
+T (3, 4, 5, 1);
+T (3, 4, 5, 2);
+
+/* Not handled yet (see above):
+ T (3, 4, 5, 3);
+ T (3, 4, 5, 4); */
+
+T (3, 4, 6, 0);
+T (3, 4, 6, 1);
+T (3, 4, 6, 2);
+
+/* Not handled yet (see above):
+ T (3, 4, 6, 3);
+ T (3, 4, 6, 4);
+ T (3, 4, 6, 5); */
+
+
+/* Verify that each function makes just one call to strlen to compute
+ the length of its argument (and not also to compute the length of
+ the copy):
+ { dg-final { scan-tree-dump-times "strlen \\(s_" 9 "strlen1" } }
+ { dg-final { scan-tree-dump-not "strlen \\(\\&a" "strlen1" } }
+
+ Verify that nul stores into the destination have not been eliminated:
+ { dg-final { scan-tree-dump-times "a4\\\] = 0;" 2 "strlen1" } }
+ { dg-final { scan-tree-dump-times "a4 \\\+ 1B\\\] = 0;" 1 "strlen1" } }
+
+ { dg-final { scan-tree-dump-times "a5\\\] = 0;" 1 "strlen1" } }
+ { dg-final { scan-tree-dump-times "a5 \\\+ 1B\\\] = 0;" 1 "strlen1" } }
+ { dg-final { scan-tree-dump-times "a5 \\\+ 2B\\\] = 0;" 1 "strlen1" } }
+
+ { dg-final { scan-tree-dump-times "a6\\\] = 0;" 1 "strlen1" } }
+ { dg-final { scan-tree-dump-times "a6 \\\+ 1B\\\] = 0;" 1 "strlen1" } }
+ { dg-final { scan-tree-dump-times "a6 \\\+ 2B\\\] = 0;" 1 "strlen1" } } */
*OFFSET_OUT. */
static int
-get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out)
+get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out,
+ const vr_values *rvals = NULL)
{
HOST_WIDE_INT off;
struct stridxlist *list, *last = NULL;
unsigned HOST_WIDE_INT rel_off
= (unsigned HOST_WIDE_INT) off - last->offset;
strinfo *si = get_strinfo (last->idx);
- if (si && compare_nonzero_chars (si, rel_off) >= 0)
+ if (si && compare_nonzero_chars (si, rel_off, rvals) >= 0)
{
if (offset_out)
{
}
else
{
- idx = get_addr_stridx (lhs, NULL_TREE, &offset);
+ idx = get_addr_stridx (lhs, NULL_TREE, &offset, rvals);
if (idx > 0)
si = get_strinfo (idx);
}