diff options
author | Martin Sebor <msebor@redhat.com> | 2018-06-18 16:32:59 +0000 |
---|---|---|
committer | Martin Sebor <msebor@gcc.gnu.org> | 2018-06-18 10:32:59 -0600 |
commit | 781ff3d80e88d7d0df019eb3e82ef2a3fb64429c (patch) | |
tree | 01c31801fe48b1c21114b772785de9eefe119e7b /gcc/testsuite | |
parent | 7314856c61938db90d66f4cead8e4df73ea5d3af (diff) | |
download | gcc-781ff3d80e88d7d0df019eb3e82ef2a3fb64429c.zip gcc-781ff3d80e88d7d0df019eb3e82ef2a3fb64429c.tar.gz gcc-781ff3d80e88d7d0df019eb3e82ef2a3fb64429c.tar.bz2 |
PR tree-optimization/81384 - built-in form of strnlen missing
gcc/ChangeLog:
PR tree-optimization/81384
* builtin-types.def (BT_FN_SIZE_CONST_STRING_SIZE): New.
* builtins.c (expand_builtin_strnlen): New function.
(expand_builtin): Call it.
(fold_builtin_n): Avoid setting TREE_NO_WARNING.
* builtins.def (BUILT_IN_STRNLEN): New.
* calls.c (maybe_warn_nonstring_arg): Handle BUILT_IN_STRNLEN.
Warn for bounds in excess of maximum object size.
* tree-ssa-strlen.c (maybe_set_strlen_range): Return tree representing
single-value ranges. Handle strnlen.
(handle_builtin_strlen): Handle strnlen.
(strlen_check_and_optimize_stmt): Same.
* doc/extend.texi (Other Builtins): Document strnlen.
gcc/testsuite/ChangeLog:
PR tree-optimization/81384
* gcc.c-torture/execute/builtins/lib/strnlen.c: New test.
* gcc.c-torture/execute/builtins/strnlen-lib.c: New test.
* gcc.c-torture/execute/builtins/strnlen.c: New test.
* gcc.dg/attr-nonstring-2.c: New test.
* gcc.dg/attr-nonstring-3.c: New test.
* gcc.dg/attr-nonstring-4.c: New test.
* gcc.dg/strlenopt-45.c: New test.
* gcc.dg/strlenopt.h (strnlen): Declare.
From-SVN: r261705
Diffstat (limited to 'gcc/testsuite')
-rw-r--r-- | gcc/testsuite/ChangeLog | 12 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/attr-nonstring-3.c | 6 | ||||
-rw-r--r-- | gcc/testsuite/gcc.c-torture/execute/builtins/lib/strnlen.c | 22 | ||||
-rw-r--r-- | gcc/testsuite/gcc.c-torture/execute/builtins/strnlen-lib.c | 1 | ||||
-rw-r--r-- | gcc/testsuite/gcc.c-torture/execute/builtins/strnlen.c | 95 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/attr-nonstring-2.c | 115 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/attr-nonstring-3.c | 117 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/attr-nonstring-4.c | 64 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/strlenopt-45.c | 335 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/strlenopt.h | 1 |
10 files changed, 765 insertions, 3 deletions
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 5672f8b..024cf6b 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,15 @@ +2018-06-18 Martin Sebor <msebor@redhat.com> + + PR tree-optimization/81384 + * gcc.c-torture/execute/builtins/lib/strnlen.c: New test. + * gcc.c-torture/execute/builtins/strnlen-lib.c: New test. + * gcc.c-torture/execute/builtins/strnlen.c: New test. + * gcc.dg/attr-nonstring-2.c: New test. + * gcc.dg/attr-nonstring-3.c: New test. + * gcc.dg/attr-nonstring-4.c: New test. + * gcc.dg/strlenopt-45.c: New test. + * gcc.dg/strlenopt.h (strnlen): Declare. + 2018-06-18 Wilco Dijkstra <wdijkstr@arm.com> PR tree-optimization/86076 diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-3.c b/gcc/testsuite/c-c++-common/attr-nonstring-3.c index a54e73a..b1eb8b6 100644 --- a/gcc/testsuite/c-c++-common/attr-nonstring-3.c +++ b/gcc/testsuite/c-c++-common/attr-nonstring-3.c @@ -66,7 +66,7 @@ struct MemArrays void sink (int, ...); -#define T(call) sink (0, (call)) +#define T(call) sink (0, call) void test_printf (struct MemArrays *p) { @@ -250,14 +250,14 @@ void test_stpncpy_warn (struct MemArrays *p, unsigned n) T (stpncpy (ptr, str, N + 1)); T (stpncpy (ptr, arr, N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */ - T (stpncpy (arr, str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " "bug 82609" { xfail c++ } } */ + T (stpncpy (arr, str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */ T (stpncpy (ptr, ptr, N + 1)); T (stpncpy (ptr, parr, N + 1)); T (stpncpy (parr, str, N + 1)); T (stpncpy (ptr, p->arr, N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller" } */ - T (stpncpy (p->arr, p->str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows" "bug 82609" { xfail c++ } } */ + T (stpncpy (p->arr, p->str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */ T (stpncpy (p->parr, p->str, N + 1)); } diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strnlen.c b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strnlen.c new file mode 100644 index 0000000..73ada14 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strnlen.c @@ -0,0 +1,22 @@ +typedef __SIZE_TYPE__ size_t; + +extern void abort (void); +extern int inside_main; + +__attribute__ ((__noinline__)) +size_t +strnlen (const char *s, size_t n) +{ + size_t i; + +#ifdef __OPTIMIZE__ + if (inside_main) + abort (); +#endif + + i = 0; + while (s[i] != 0 && n--) + i++; + + return i; +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen-lib.c new file mode 100644 index 0000000..bf91940 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen-lib.c @@ -0,0 +1 @@ +#include "lib/strnlen.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen.c new file mode 100644 index 0000000..2368d06 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen.c @@ -0,0 +1,95 @@ +/* PR tree-optimization/81384 - built-in form of strnlen missing + Test to verify that strnlen built-in expansion works correctly + in the absence of tree strlen optimization. + { dg-do run } + { do-options "-O2 -fno-tree-strlen" } */ + +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#define SIZE_MAX __SIZE_MAX__ +#define NOIPA __attribute__ ((noipa)) + +typedef __SIZE_TYPE__ size_t; + +extern void abort (void); +extern size_t strnlen (const char *, size_t); + +#define A(expr) \ + ((expr) ? (void)0 \ + : (__builtin_printf ("assertion on line %i failed: %s\n", \ + __LINE__, #expr), \ + abort ())) + +NOIPA void test_strnlen_str_cst (void) +{ + A (strnlen ("", 0) == 0); + A (strnlen ("", 1) == 0); + A (strnlen ("", 9) == 0); + A (strnlen ("", PTRDIFF_MAX) == 0); + A (strnlen ("", SIZE_MAX) == 0); + A (strnlen ("", -1) == 0); + + A (strnlen ("1", 0) == 0); + A (strnlen ("1", 1) == 1); + A (strnlen ("1", 9) == 1); + A (strnlen ("1", PTRDIFF_MAX) == 1); + A (strnlen ("1", SIZE_MAX) == 1); + A (strnlen ("1", -2) == 1); + + A (strnlen ("123", 0) == 0); + A (strnlen ("123", 1) == 1); + A (strnlen ("123", 2) == 2); + A (strnlen ("123", 3) == 3); + A (strnlen ("123", 9) == 3); + A (strnlen ("123", PTRDIFF_MAX) == 3); + A (strnlen ("123", SIZE_MAX) == 3); + A (strnlen ("123", -2) == 3); +} + +NOIPA void test_strnlen_str_range (size_t x) +{ + size_t r_0_3 = x & 3; + size_t r_1_3 = r_0_3 | 1; + size_t r_2_3 = r_0_3 | 2; + + A (strnlen ("", r_0_3) == 0); + A (strnlen ("1", r_0_3) <= 1); + A (strnlen ("12", r_0_3) <= 2); + A (strnlen ("123", r_0_3) <= 3); + A (strnlen ("1234", r_0_3) <= 3); + + A (strnlen ("", r_1_3) == 0); + A (strnlen ("1", r_1_3) == 1); + A (strnlen ("12", r_1_3) <= 2); + A (strnlen ("123", r_1_3) <= 3); + A (strnlen ("1234", r_1_3) <= 3); + + A (strnlen ("", r_2_3) == 0); + A (strnlen ("1", r_2_3) == 1); + A (strnlen ("12", r_2_3) == 2); + A (strnlen ("123", r_2_3) <= 3); + A (strnlen ("1234", r_2_3) <= 3); +} + +NOIPA void test_strnlen_str_range_side_effect (size_t x) +{ + size_t r_0_3 = x & 3; + size_t r_1_3 = r_0_3 | 1; + size_t r_2_3 = r_0_3 | 2; + size_t n = r_2_3; + + int i = 0; + + A (strnlen ("1234" + i++, n) <= 3); + A (i == 1); + + A (strnlen ("1234", n++) <= 3); + A (n == r_2_3 + 1); +} + +void +main_test (void) +{ + test_strnlen_str_cst (); + test_strnlen_str_range ((size_t)""); + test_strnlen_str_range_side_effect ((size_t)""); +} diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-2.c b/gcc/testsuite/gcc.dg/attr-nonstring-2.c new file mode 100644 index 0000000..e9edb66 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-nonstring-2.c @@ -0,0 +1,115 @@ +/* PR middle-end/81384 - built-in form of strnlen missing + { dg-do compile } + { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */ + +#include "range.h" + +extern void* memcpy (void*, const void*, size_t); +extern size_t strnlen (const char*, size_t); + +#define NONSTRING __attribute__ ((nonstring)) + +#define _CAT(s, n) s ## n +#define CAT(s, n) _CAT (s, n) +#define UNIQ(n) CAT (n, __LINE__) + +void sink (size_t, ...); + +#define T(expr) sink (expr) + +void test_strnlen_array_cst (void) +{ + NONSTRING char ns3[3]; + sink (0, ns3); // "initialize" ns3 + + T (strnlen (ns3, 0)); + T (strnlen (ns3, 1)); + T (strnlen (ns3, 2)); + T (strnlen (ns3, 3)); + T (strnlen (ns3, 4)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" } */ + T (strnlen (ns3, DIFF_MAX)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */ + T (strnlen (ns3, SIZE_MAX)); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + NONSTRING char ns5[5]; + sink (0, ns5); + + T (strnlen (ns5, 0)); + T (strnlen (ns5, 1)); + T (strnlen (ns5, 2)); + T (strnlen (ns5, 3)); + T (strnlen (ns5, 6)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 6" } */ + T (strnlen (ns5, DIFF_MAX)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */ + T (strnlen (ns5, SIZE_MAX)); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ +} + + +void test_strnlen_array_range (void) +{ + NONSTRING char ns3[3]; + sink (0, ns3); // "initialize" ns3 + + T (strnlen (ns3, UR (0, 3))); + T (strnlen (ns3, UR (0, 9))); + T (strnlen (ns3, UR (3, 4))); + T (strnlen (ns3, UR (3, DIFF_MAX))); + T (strnlen (ns3, UR (4, 5))); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */ + T (strnlen (ns3, UR (DIFF_MAX, SIZE_MAX))); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller " } */ +} + + +#undef T +#define T(N, init, nelts, bound) \ + do { \ + extern NONSTRING char UNIQ (arr)[N]; \ + memcpy (UNIQ (arr), init, nelts); \ + sink (strnlen (UNIQ (arr), bound), UNIQ (arr)); \ + } while (0) + +void test_strnlen_string_cst (void) +{ + T (3, "1", 2, 1); + T (3, "1", 2, 2); + T (3, "1", 2, 3); + T (3, "12", 3, 1); + T (3, "12", 3, 9); + T (3, "123", 3, 1); + T (3, "123", 3, 4); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" } */ + T (3, "123", 3, 9); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 9" } */ + + T (5, "1", 2, 1); + T (5, "1", 2, 2); + T (5, "1", 2, 9); + + T (5, "12", 3, 1); + T (5, "12", 3, 9); + T (5, "123", 3, 1); + T (5, "123", 3, 5); + T (5, "123", 3, 6); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 6" } */ + + /* Strnlen shouldn't trigger a warning for arrays of unknown size + (except for accesses to uninitialized elements when those are + detected). */ + T (/* [] */, "1", 1, 1); + T (/* [] */, "1", 1, 2); + T (/* [] */, "1", 2, 1); + T (/* [] */, "1", 2, 2); + T (/* [] */, "1", 2, 3); + T (/* [] */, "1", 2, 9); + T (/* [] */, "1", 2, DIFF_MAX); + T (/* [] */, "1", 2, SIZE_MAX); + + size_t n = DIFF_MAX; + T (/* [] */, "123", 3, n); + T (/* [] */, "123", 3, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size " } */ + n = SIZE_MAX; + T (/* [] */, "123", 3, n); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size " } */ +} + + +void test_strnlen_string_range (void) +{ + T (3, "1", 2, UR (0, 1)); + T (3, "1", 2, UR (3, 9)); + T (3, "123", 3, UR (4, 5)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" } */ + T (3, "123", 3, UR (5, 9)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */ +} diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-3.c b/gcc/testsuite/gcc.dg/attr-nonstring-3.c new file mode 100644 index 0000000..4a10450 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-nonstring-3.c @@ -0,0 +1,117 @@ +/* PR middle-end/81384 - built-in form of strnlen missing + + Since the strnlen patch affects the handling for strncmp and other + bounded functions, verify that a bound in excess of the maximum + object size specified for strncmp is diagnosed regardless of + attribute nonstring. Also verify that a bound that's greater than + the size of a non-string array is diagnosed, even if it's not in + excess of the maximum object size. + + { dg-do compile } + { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */ + +#include "range.h" + +extern int strncmp (const char*, const char*, size_t); + +#define STR /* not non-string */ +#define NS __attribute__ ((nonstring)) + +#define _CAT(s, n) s ## n +#define CAT(s, n) _CAT (s, n) +#define UNIQ(n) CAT (n, __LINE__) + +void sink (int); + +#define T(at1, N1, at2, N2, bound) \ + do { \ + extern at1 char UNIQ (a)[N1]; \ + extern at2 char UNIQ (b)[N2]; \ + sink (strncmp (UNIQ (a), UNIQ (b), bound)); \ + } while (0) + +void strncmp_cst (void) +{ + size_t n = DIFF_MAX; + + T (STR, /* [] */, STR, /* [] */, n); + T (STR, /* [] */, STR, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, 1, STR, /* [] */, n); + T (STR, 2, STR, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, STR, 3, n); + T (STR, /* [] */, STR, 4, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, NS, /* [] */, n); + T (STR, /* [] */, NS, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, 5, NS, /* [] */, n); + T (STR, 6, NS, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, NS, 7, n); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */ + + T (STR, /* [] */, NS, 8, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, STR, /* [] */, n); + T (NS, /* [] */, STR, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, 9, STR, /* [] */, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 10, STR, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, STR, 11, n); + T (NS, /* [] */, STR, 12, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, NS, /* [] */, n); + T (NS, /* [] */, NS, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, 13, NS, /* [] */, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 14, NS, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, NS, 15, n); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, /* [] */, NS, 16, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ +} + + +void strncmp_range (void) +{ + size_t n = DIFF_MAX; + n = UR (n, n + 1); + + T (STR, /* [] */, STR, /* [] */, n); + T (STR, /* [] */, STR, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, 1, STR, /* [] */, n); + T (STR, 2, STR, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, STR, 3, n); + T (STR, /* [] */, STR, 4, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, NS, /* [] */, n); + T (STR, /* [] */, NS, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, 5, NS, /* [] */, n); + T (STR, 6, NS, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, NS, 7, n); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */ + + T (STR, /* [] */, NS, 8, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, STR, /* [] */, n); + T (NS, /* [] */, STR, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, 9, STR, /* [] */, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 10, STR, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, STR, 11, n); + T (NS, /* [] */, STR, 12, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, NS, /* [] */, n); + T (NS, /* [] */, NS, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, 13, NS, /* [] */, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 14, NS, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, NS, 15, n); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, /* [] */, NS, 16, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ +} diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-4.c b/gcc/testsuite/gcc.dg/attr-nonstring-4.c new file mode 100644 index 0000000..7daff97 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-nonstring-4.c @@ -0,0 +1,64 @@ +/* PR middle-end/81384 - built-in form of strnlen missing + + Verify that a strnlen bound in excess of the maximum object size + is diagnosed regardless of attribute nonstring. Also verify that + a bound that's greater than the size of a non-string array is + diagnosed, even if it's not in excess of the maximum object size. + + { dg-do compile } + { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */ + +#include "range.h" + +extern size_t strnlen (const char*, size_t); + +#define STR /* not non-string */ +#define NS __attribute__ ((nonstring)) + +#define _CAT(s, n) s ## n +#define CAT(s, n) _CAT (s, n) +#define UNIQ(n) CAT (n, __LINE__) + +void sink (size_t); + +#define T(attr, N, bound) \ + do { \ + extern attr char UNIQ (a)[N]; \ + sink (strnlen (UNIQ (a), bound)); \ + } while (0) + +void strnlen_cst (void) +{ + size_t n = DIFF_MAX; + + T (STR, /* [] */, n); + T (STR, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, 1, n); + T (STR, 2, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, n); + T (NS, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, 9, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 10, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ +} + + +void strnlen_range (void) +{ + size_t n = DIFF_MAX; + n = UR (n, n + 1); + + T (STR, /* [] */, n); + T (STR, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, 1, n); + T (STR, 2, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, n); + T (NS, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, 9, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 10, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ +} diff --git a/gcc/testsuite/gcc.dg/strlenopt-45.c b/gcc/testsuite/gcc.dg/strlenopt-45.c new file mode 100644 index 0000000..bd9b197 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-45.c @@ -0,0 +1,335 @@ +/* PR tree-optimization/81384 - built-in form of strnlen missing + Test to verify that strnlen built-in expansion works correctly + in the absence of tree strlen optimization. + { dg-do compile } + { dg-options "-O2 -Wall -fdump-tree-optimized" } */ + +#include "strlenopt.h" + +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#define SIZE_MAX __SIZE_MAX__ + +typedef __SIZE_TYPE__ size_t; + +extern void abort (void); +extern size_t strnlen (const char *, size_t); + +#define CAT(x, y) x ## y +#define CONCAT(x, y) CAT (x, y) +#define FAILNAME(name) CONCAT (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 + +/* Macro to emit a call to a function named + call_made_in_{true,false}_branch_on_line_NNN() + for each call that's expected to be retained. The dg-final + scan-tree-dump-time directive at the bottom of the test verifies + that the expected number of both kinds of calls appears in output + (a pair for each line with the invocation of the KEEP() macro. */ +#define KEEP(expr) \ + if (expr) \ + FAIL (made_in_true_branch); \ + else \ + FAIL (made_in_false_branch) + +extern char c; +extern char a1[1]; +extern char a3[3]; +extern char a5[5]; +extern char a3_7[3][7]; +extern char ax[]; + +void elim_strnlen_arr_cst (void) +{ + /* The length of a string stored in a one-element array must be zero. + The result reported by strnlen() for such an array can be non-zero + only when the bound is equal to 1 (in which case the result must + be one). */ + ELIM (strnlen (&c, 0) == 0); + ELIM (strnlen (&c, 1) < 2); + ELIM (strnlen (&c, 2) == 0); + ELIM (strnlen (&c, 9) == 0); + ELIM (strnlen (&c, PTRDIFF_MAX) == 0); + ELIM (strnlen (&c, SIZE_MAX) == 0); + ELIM (strnlen (&c, -1) == 0); + + ELIM (strnlen (a1, 0) == 0); + ELIM (strnlen (a1, 1) < 2); + ELIM (strnlen (a1, 2) == 0); + ELIM (strnlen (a1, 9) == 0); + ELIM (strnlen (a1, PTRDIFF_MAX) == 0); + ELIM (strnlen (a1, SIZE_MAX) == 0); + ELIM (strnlen (a1, -1) == 0); + + ELIM (strnlen (a3, 0) == 0); + ELIM (strnlen (a3, 1) < 2); + ELIM (strnlen (a3, 2) < 3); + ELIM (strnlen (a3, 3) < 4); + ELIM (strnlen (a3, 9) < 4); + ELIM (strnlen (a3, PTRDIFF_MAX) < 4); + ELIM (strnlen (a3, SIZE_MAX) < 4); + ELIM (strnlen (a3, -1) < 4); + + ELIM (strnlen (a3_7[0], 0) == 0); + ELIM (strnlen (a3_7[0], 1) < 2); + ELIM (strnlen (a3_7[0], 2) < 3); + ELIM (strnlen (a3_7[0], 3) < 4); + ELIM (strnlen (a3_7[0], 9) < 8); + ELIM (strnlen (a3_7[0], PTRDIFF_MAX) < 8); + ELIM (strnlen (a3_7[0], SIZE_MAX) < 8); + ELIM (strnlen (a3_7[0], -1) < 8); + + ELIM (strnlen (a3_7[2], 0) == 0); + ELIM (strnlen (a3_7[2], 1) < 2); + ELIM (strnlen (a3_7[2], 2) < 3); + ELIM (strnlen (a3_7[2], 3) < 4); + ELIM (strnlen (a3_7[2], 9) < 8); + ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < 8); + ELIM (strnlen (a3_7[2], SIZE_MAX) < 8); + ELIM (strnlen (a3_7[2], -1) < 8); + + ELIM (strnlen ((char*)a3_7, 0) == 0); + ELIM (strnlen ((char*)a3_7, 1) < 2); + ELIM (strnlen ((char*)a3_7, 2) < 3); + ELIM (strnlen ((char*)a3_7, 3) < 4); + ELIM (strnlen ((char*)a3_7, 9) < 10); + ELIM (strnlen ((char*)a3_7, 19) < 20); + ELIM (strnlen ((char*)a3_7, 21) < 22); + ELIM (strnlen ((char*)a3_7, 23) < 22); + ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) < 22); + ELIM (strnlen ((char*)a3_7, SIZE_MAX) < 22); + ELIM (strnlen ((char*)a3_7, -1) < 22); + + ELIM (strnlen (ax, 0) == 0); + ELIM (strnlen (ax, 1) < 2); + ELIM (strnlen (ax, 2) < 3); + ELIM (strnlen (ax, 9) < 10); + ELIM (strnlen (a3, PTRDIFF_MAX) <= PTRDIFF_MAX); + ELIM (strnlen (a3, SIZE_MAX) < PTRDIFF_MAX); + ELIM (strnlen (a3, -1) < PTRDIFF_MAX); +} + +struct MemArrays +{ + char c; + char a0[0]; + char a1[1]; + char a3[3]; + char a5[5]; + char a3_7[3][7]; + char ax[1]; +}; + +void elim_strnlen_memarr_cst (struct MemArrays *p, int i) +{ + ELIM (strnlen (&p->c, 0) == 0); + ELIM (strnlen (&p->c, 1) < 2); + ELIM (strnlen (&p->c, 9) == 0); + ELIM (strnlen (&p->c, PTRDIFF_MAX) == 0); + ELIM (strnlen (&p->c, SIZE_MAX) == 0); + ELIM (strnlen (&p->c, -1) == 0); + + /* Other accesses to internal zero-length arrays are undefined. */ + ELIM (strnlen (p->a0, 0) == 0); + + ELIM (strnlen (p->a1, 0) == 0); + ELIM (strnlen (p->a1, 1) < 2); + ELIM (strnlen (p->a1, 9) == 0); + ELIM (strnlen (p->a1, PTRDIFF_MAX) == 0); + ELIM (strnlen (p->a1, SIZE_MAX) == 0); + ELIM (strnlen (p->a1, -1) == 0); + + ELIM (strnlen (p->a3, 0) == 0); + ELIM (strnlen (p->a3, 1) < 2); + ELIM (strnlen (p->a3, 2) < 3); + ELIM (strnlen (p->a3, 3) < 4); + ELIM (strnlen (p->a3, 9) < 4); + ELIM (strnlen (p->a3, PTRDIFF_MAX) < 4); + ELIM (strnlen (p->a3, SIZE_MAX) < 4); + ELIM (strnlen (p->a3, -1) < 4); + + ELIM (strnlen (p[i].a3, 0) == 0); + ELIM (strnlen (p[i].a3, 1) < 2); + ELIM (strnlen (p[i].a3, 2) < 3); + ELIM (strnlen (p[i].a3, 3) < 4); + ELIM (strnlen (p[i].a3, 9) < 4); + ELIM (strnlen (p[i].a3, PTRDIFF_MAX) < 4); + ELIM (strnlen (p[i].a3, SIZE_MAX) < 4); + ELIM (strnlen (p[i].a3, -1) < 4); + + ELIM (strnlen (p->a3_7[0], 0) == 0); + ELIM (strnlen (p->a3_7[0], 1) < 2); + ELIM (strnlen (p->a3_7[0], 2) < 3); + ELIM (strnlen (p->a3_7[0], 3) < 4); + ELIM (strnlen (p->a3_7[0], 9) < 8); + ELIM (strnlen (p->a3_7[0], PTRDIFF_MAX) < 8); + ELIM (strnlen (p->a3_7[0], SIZE_MAX) < 8); + ELIM (strnlen (p->a3_7[0], -1) < 8); + + ELIM (strnlen (p->a3_7[2], 0) == 0); + ELIM (strnlen (p->a3_7[2], 1) < 2); + ELIM (strnlen (p->a3_7[2], 2) < 3); + ELIM (strnlen (p->a3_7[2], 3) < 4); + ELIM (strnlen (p->a3_7[2], 9) < 8); + ELIM (strnlen (p->a3_7[2], PTRDIFF_MAX) < 8); + ELIM (strnlen (p->a3_7[2], SIZE_MAX) < 8); + ELIM (strnlen (p->a3_7[2], -1) < 8); + + ELIM (strnlen (p->a3_7[i], 0) == 0); + ELIM (strnlen (p->a3_7[i], 1) < 2); + ELIM (strnlen (p->a3_7[i], 2) < 3); + ELIM (strnlen (p->a3_7[i], 3) < 4); + +#if 0 + /* This is tranformed into strnlen ((char*)p + offsetof (a3_7[i]), N) + which makes it impssible to determine the size of the array. */ + ELIM (strnlen (p->a3_7[i], 9) < 8); + ELIM (strnlen (p->a3_7[i], PTRDIFF_MAX) < 8); + ELIM (strnlen (p->a3_7[i], SIZE_MAX) < 8); + ELIM (strnlen (p->a3_7[i], -1) < 8); +#else + ELIM (strnlen (p->a3_7[i], 9) < 10); + ELIM (strnlen (p->a3_7[i], 19) < 20); +#endif + + ELIM (strnlen ((char*)p->a3_7, 0) == 0); + ELIM (strnlen ((char*)p->a3_7, 1) < 2); + ELIM (strnlen ((char*)p->a3_7, 2) < 3); + ELIM (strnlen ((char*)p->a3_7, 3) < 4); + ELIM (strnlen ((char*)p->a3_7, 9) < 10); + ELIM (strnlen ((char*)p->a3_7, 19) < 20); + ELIM (strnlen ((char*)p->a3_7, 21) < 22); + ELIM (strnlen ((char*)p->a3_7, 23) < 22); + ELIM (strnlen ((char*)p->a3_7, PTRDIFF_MAX) < 22); + ELIM (strnlen ((char*)p->a3_7, SIZE_MAX) < 22); + ELIM (strnlen ((char*)p->a3_7, -1) < 22); + + ELIM (strnlen (p->ax, 0) == 0); + ELIM (strnlen (p->ax, 1) < 2); + ELIM (strnlen (p->ax, 2) < 3); + ELIM (strnlen (p->ax, 9) < 10); + ELIM (strnlen (p->a3, PTRDIFF_MAX) <= PTRDIFF_MAX); + ELIM (strnlen (p->a3, SIZE_MAX) < PTRDIFF_MAX); + ELIM (strnlen (p->a3, -1) < PTRDIFF_MAX); +} + + +void elim_strnlen_str_cst (void) +{ + const char *s0 = ""; + const char *s1 = "1"; + const char *s3 = "123"; + + ELIM (strnlen (s0, 0) == 0); + ELIM (strnlen (s0, 1) == 0); + ELIM (strnlen (s0, 9) == 0); + ELIM (strnlen (s0, PTRDIFF_MAX) == 0); + ELIM (strnlen (s0, SIZE_MAX) == 0); + ELIM (strnlen (s0, -1) == 0); + + ELIM (strnlen (s1, 0) == 0); + ELIM (strnlen (s1, 1) == 1); + ELIM (strnlen (s1, 9) == 1); + ELIM (strnlen (s1, PTRDIFF_MAX) == 1); + ELIM (strnlen (s1, SIZE_MAX) == 1); + ELIM (strnlen (s1, -2) == 1); + + ELIM (strnlen (s3, 0) == 0); + ELIM (strnlen (s3, 1) == 1); + ELIM (strnlen (s3, 2) == 2); + ELIM (strnlen (s3, 3) == 3); + ELIM (strnlen (s3, 9) == 3); + ELIM (strnlen (s3, PTRDIFF_MAX) == 3); + ELIM (strnlen (s3, SIZE_MAX) == 3); + ELIM (strnlen (s3, -2) == 3); +} + +void elim_strnlen_range (char *s) +{ + const char *s0 = ""; + const char *s1 = "1"; + const char *s3 = "123"; + + size_t n_0_1 = (size_t)s & 1; + size_t n_0_2 = ((size_t)s & 3) < 3 ? ((size_t)s & 3) : 2; + size_t n_0_3 = (size_t)s & 3; + size_t n_1_2 = n_0_1 + 1; + + ELIM (strnlen (s0, n_0_1) == 0); + ELIM (strnlen (s0, n_0_2) == 0); + ELIM (strnlen (s0, n_1_2) == 0); + + ELIM (strnlen (s1, n_0_1) < 2); + ELIM (strnlen (s1, n_0_2) < 2); + ELIM (strnlen (s1, n_0_3) < 2); + + ELIM (strnlen (s1, n_1_2) > 0); + ELIM (strnlen (s1, n_1_2) < 2); + + ELIM (strnlen (s3, n_0_1) < 2); + ELIM (strnlen (s3, n_0_2) < 3); + ELIM (strnlen (s3, n_0_3) < 4); + + ELIM (strnlen (s3, n_1_2) > 0); + ELIM (strnlen (s3, n_1_2) < 4); +} + + +#line 1000 + +void keep_strnlen_arr_cst (void) +{ + KEEP (strnlen (&c, 1) == 0); + KEEP (strnlen (&c, 1) == 1); + + KEEP (strnlen (a1, 1) == 0); + KEEP (strnlen (a1, 1) == 1); + + KEEP (strnlen (ax, 9) < 9); +} + +struct FlexArrays +{ + char c; + char a0[0]; /* Access to internal zero-length arrays are undefined. */ + char a1[1]; +}; + +void keep_strnlen_memarr_cst (struct FlexArrays *p) +{ + KEEP (strnlen (&p->c, 1) == 0); + KEEP (strnlen (&p->c, 1) == 1); + +#if 0 + /* Accesses to internal zero-length arrays are undefined so avoid + exercising them. */ + KEEP (strnlen (p->a0, 1) == 0); + KEEP (strnlen (p->a0, 1) == 1); + KEEP (strnlen (p->a0, 9) < 9); +#endif + + KEEP (strnlen (p->a1, 1) == 0); + KEEP (strnlen (p->a1, 1) == 1); + + KEEP (strnlen (p->a1, 2) == 0); + KEEP (strnlen (p->a1, 2) == 1); + KEEP (strnlen (p->a1, 2) == 2); + + KEEP (strnlen (p->a1, 9) < 9); +} + +/* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } } + + { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 13 "optimized" } } + { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 13 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt.h b/gcc/testsuite/gcc.dg/strlenopt.h index 8f69940..a4044fd 100644 --- a/gcc/testsuite/gcc.dg/strlenopt.h +++ b/gcc/testsuite/gcc.dg/strlenopt.h @@ -9,6 +9,7 @@ void *malloc (size_t); void free (void *); char *strdup (const char *); size_t strlen (const char *); +size_t strnlen (const char *, size_t); void *memcpy (void *__restrict, const void *__restrict, size_t); void *memmove (void *, const void *, size_t); char *strcpy (char *__restrict, const char *__restrict); |