From efe646c468656959236de0fbe2224561873fb189 Mon Sep 17 00:00:00 2001 From: Martin Sebor Date: Mon, 28 Oct 2019 23:53:08 +0000 Subject: [PATCH] tree-ssa-strlen.c (get_addr_stridx): Add argument and use it. gcc/ChangeLog: * tree-ssa-strlen.c (get_addr_stridx): Add argument and use it. (handle_store): Pass argument to get_addr_stridx. gcc/testsuite/ChangeLog: * gcc.dg/strlenopt-89.c: New test. * gcc.dg/strlenopt-90.c: New test. * gcc.dg/Wstringop-overflow-20.c: New test. From-SVN: r277546 --- gcc/ChangeLog | 5 ++ gcc/testsuite/ChangeLog | 6 ++ gcc/testsuite/gcc.dg/Wstringop-overflow-20.c | 40 +++++++++ gcc/testsuite/gcc.dg/strlenopt-89.c | 89 ++++++++++++++++++++ gcc/testsuite/gcc.dg/strlenopt-90.c | 83 ++++++++++++++++++ gcc/tree-ssa-strlen.c | 7 +- 6 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/Wstringop-overflow-20.c create mode 100644 gcc/testsuite/gcc.dg/strlenopt-89.c create mode 100644 gcc/testsuite/gcc.dg/strlenopt-90.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 3f4f2dc356b..8b8a6c6f647 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2019-10-28 Martin Sebor + + * 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 PR tree-optimization/92226 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 8f9859f6b22..a3ef28625e4 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2019-10-28 Martin Sebor + + * 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 PR tree-optimization/92226 diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-20.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-20.c new file mode 100644 index 00000000000..4a6fe8ea352 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-20.c @@ -0,0 +1,40 @@ +/* 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 *-*-* } } diff --git a/gcc/testsuite/gcc.dg/strlenopt-89.c b/gcc/testsuite/gcc.dg/strlenopt-89.c new file mode 100644 index 00000000000..ead451ffb0e --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-89.c @@ -0,0 +1,89 @@ +/* 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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-90.c b/gcc/testsuite/gcc.dg/strlenopt-90.c new file mode 100644 index 00000000000..2d4dad18672 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-90.c @@ -0,0 +1,83 @@ +/* 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" } } */ diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index 476bcf66236..50cc442a61f 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -281,7 +281,8 @@ get_next_strinfo (strinfo *si) *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; @@ -319,7 +320,7 @@ get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out) 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) { @@ -4328,7 +4329,7 @@ handle_store (gimple_stmt_iterator *gsi, bool *zero_write, const vr_values *rval } 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); } -- 2.30.2