aboutsummaryrefslogtreecommitdiff
path: root/gcc/testsuite
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2018-06-18 16:32:59 +0000
committerMartin Sebor <msebor@gcc.gnu.org>2018-06-18 10:32:59 -0600
commit781ff3d80e88d7d0df019eb3e82ef2a3fb64429c (patch)
tree01c31801fe48b1c21114b772785de9eefe119e7b /gcc/testsuite
parent7314856c61938db90d66f4cead8e4df73ea5d3af (diff)
downloadgcc-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/ChangeLog12
-rw-r--r--gcc/testsuite/c-c++-common/attr-nonstring-3.c6
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/builtins/lib/strnlen.c22
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/builtins/strnlen-lib.c1
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/builtins/strnlen.c95
-rw-r--r--gcc/testsuite/gcc.dg/attr-nonstring-2.c115
-rw-r--r--gcc/testsuite/gcc.dg/attr-nonstring-3.c117
-rw-r--r--gcc/testsuite/gcc.dg/attr-nonstring-4.c64
-rw-r--r--gcc/testsuite/gcc.dg/strlenopt-45.c335
-rw-r--r--gcc/testsuite/gcc.dg/strlenopt.h1
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);