aboutsummaryrefslogtreecommitdiff
path: root/gcc/testsuite
diff options
context:
space:
mode:
authorMartin Sebor <msebor@redhat.com>2019-07-25 00:29:17 +0000
committerMartin Sebor <msebor@gcc.gnu.org>2019-07-24 18:29:17 -0600
commitb631bdb3c16e85f35d38e39b3d315c35e4a5747c (patch)
tree946823534c8cde2d83143cbdcd1e5d6e8d8d7955 /gcc/testsuite
parent7214f11d4708a62868f4b0830ef65b87976d1826 (diff)
downloadgcc-b631bdb3c16e85f35d38e39b3d315c35e4a5747c.zip
gcc-b631bdb3c16e85f35d38e39b3d315c35e4a5747c.tar.gz
gcc-b631bdb3c16e85f35d38e39b3d315c35e4a5747c.tar.bz2
PR tree-optimization/91183 - strlen of a strcpy result with a conditional source not folded
PR tree-optimization/91183 - strlen of a strcpy result with a conditional source not folded PR tree-optimization/86688 - missing -Wstringop-overflow using a non-string local array in strnlen with excessive bound gcc/ChangeLog: PR tree-optimization/91183 PR tree-optimization/86688 * builtins.c (compute_objsize): Handle MEM_REF. * tree-ssa-strlen.c (class ssa_name_limit_t): New. (get_min_string_length): Remove. (count_nonzero_bytes): New function. (handle_char_store): Rename... (handle_store): to this. Handle multibyte stores via integer types. (strlen_check_and_optimize_stmt): Adjust conditional and the called function name. gcc/testsuite/ChangeLog: PR tree-optimization/91183 PR tree-optimization/86688 * gcc.dg/Wstringop-overflow-14.c: New test. * gcc.dg/attr-nonstring-2.c: Remove xfails. * gcc.dg/strlenopt-70.c: New test. * gcc.dg/strlenopt-71.c: New test. * gcc.dg/strlenopt-72.c: New test. * gcc.dg/strlenopt-8.c: Remove xfails. From-SVN: r273783
Diffstat (limited to 'gcc/testsuite')
-rw-r--r--gcc/testsuite/ChangeLog12
-rw-r--r--gcc/testsuite/c-c++-common/ubsan/object-size-9.c2
-rw-r--r--gcc/testsuite/gcc.dg/Wstringop-overflow-14.c49
-rw-r--r--gcc/testsuite/gcc.dg/attr-nonstring-2.c8
-rw-r--r--gcc/testsuite/gcc.dg/strlenopt-70.c293
-rw-r--r--gcc/testsuite/gcc.dg/strlenopt-71.c212
-rw-r--r--gcc/testsuite/gcc.dg/strlenopt-72.c64
-rw-r--r--gcc/testsuite/gcc.dg/strlenopt-8.c8
8 files changed, 636 insertions, 12 deletions
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 4749b0c..532d1c1 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,5 +1,17 @@
2019-07-24 Martin Sebor <msebor@redhat.com>
+ PR tree-optimization/91183
+ PR tree-optimization/86688
+ * gcc/testsuite/c-c++-common/ubsan/object-size-9.c: Disable warnings.
+ * gcc.dg/Wstringop-overflow-14.c: New test.
+ * gcc.dg/attr-nonstring-2.c: Remove xfails.
+ * gcc.dg/strlenopt-70.c: New test.
+ * gcc.dg/strlenopt-71.c: New test.
+ * gcc.dg/strlenopt-72.c: New test.
+ * gcc.dg/strlenopt-8.c: Remove xfails.
+
+2019-07-24 Martin Sebor <msebor@redhat.com>
+
PR driver/80545
* gcc.misc-tests/help.exp: Add tests.
* lib/options.exp: Handle C++.
diff --git a/gcc/testsuite/c-c++-common/ubsan/object-size-9.c b/gcc/testsuite/c-c++-common/ubsan/object-size-9.c
index 3684bbe..bd2a53e 100644
--- a/gcc/testsuite/c-c++-common/ubsan/object-size-9.c
+++ b/gcc/testsuite/c-c++-common/ubsan/object-size-9.c
@@ -1,6 +1,6 @@
/* { dg-do run } */
/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
-/* { dg-options "-fsanitize=undefined" } */
+/* { dg-options "-Wno-stringop-overflow -fsanitize=undefined" } */
/* Test PARM_DECLs and RESULT_DECLs. */
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-14.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-14.c
new file mode 100644
index 0000000..d23a248
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-14.c
@@ -0,0 +1,49 @@
+/* Test to verify that past-the-end multibyte writes via lvalues of wider
+ types than char are diagnosed.
+ { dg-do compile }
+ { dg-require-effective-target int32plus }
+ { dg-options "-O2 -Wall" } */
+
+typedef __INT16_TYPE__ int16_t;
+typedef __INT32_TYPE__ int32_t;
+typedef __INT64_TYPE__ int64_t;
+typedef __SIZE_TYPE__ size_t;
+
+void* memcpy (void*, const void*, size_t);
+char* strcpy (char*, const char*);
+
+char a4[4], a8[8], a16[16];
+
+const char s4[] = "1234";
+const char t4[] = "4321";
+
+void test_memcpy_cond (int i)
+{
+ char *p = a4 + 1;
+ const char *q = i ? s4 : t4;
+ memcpy (p, q, 4); // { dg-warning "writing 4 bytes into a region of size 3" }
+}
+
+
+void test_int16 (void)
+{
+ char *p = a4 + 1;
+ *(int16_t*)p = 0;
+ *(int16_t*)(p + 2) = 0; // { dg-warning "writing 2 bytes into a region of size 1" }
+}
+
+
+void test_int32 (void)
+{
+ char *p = a8 + 3;
+ *(int32_t*)p = 0;
+ *(int32_t*)(p + 2) = 0; // { dg-warning "writing 4 bytes into a region of size 3" }
+}
+
+
+void test_int64 (void)
+{
+ char *p = a16 + 5;
+ *(int64_t*)p = 0;
+ *(int64_t*)(p + 5) = 0; // { dg-warning "writing 8 bytes into a region of size 6" }
+}
diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-2.c b/gcc/testsuite/gcc.dg/attr-nonstring-2.c
index 246a372..ef2144d 100644
--- a/gcc/testsuite/gcc.dg/attr-nonstring-2.c
+++ b/gcc/testsuite/gcc.dg/attr-nonstring-2.c
@@ -73,8 +73,8 @@ void test_strnlen_string_cst (void)
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" "bug 86688" { xfail *-*-* } } */
- T (3, "123", 3, 9); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 9" "bug 86688" { xfail *-*-* } } */
+ 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);
@@ -110,6 +110,6 @@ 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, 5]" "bug 86688" { xfail *-*-* } } */
- T (3, "123", 3, UR (5, 9)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[5, 9]" "bug 86688" { xfail *-*-* } } */
+ T (3, "123", 3, UR (4, 5)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[4, 5]" } */
+ T (3, "123", 3, UR (5, 9)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[5, 9]" } */
}
diff --git a/gcc/testsuite/gcc.dg/strlenopt-70.c b/gcc/testsuite/gcc.dg/strlenopt-70.c
new file mode 100644
index 0000000..2dc42d6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-70.c
@@ -0,0 +1,293 @@
+/* PR tree-optimization/91183 - strlen of a strcpy result with a conditional
+ source not folded
+ Test to verify that strlen can determine string lengths from wider stores
+ than narrow characters. This matters because on targets that can handle
+ unaligned stores and where GCC lowers multi-character stores into smaller
+ numbers of wider stores.
+ { dg-do compile }
+ { dg-options "-O2 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+#define CHAR_BIT __CHAR_BIT__
+
+typedef __INT16_TYPE__ int16_t;
+typedef __INT32_TYPE__ int32_t;
+typedef __INT64_TYPE__ int64_t;
+typedef __UINT64_TYPE__ uint64_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)
+
+/* Macros to emit a call to function named
+ call_failed_to_be_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 (not_eliminated); else (void)0
+
+/* Verify that 'strlen (A) EXPECT' is folded to true. When non-null,
+ the first sizeof (INIT) - 1 bytes of the INIT arrray are stored
+ in A first, followed by *(TYPE*)A = ASSIGN. */
+#define T(init, type, off, assign, expect) do { \
+ char a[32]; \
+ memcpy (a, init ? init : "", init ? sizeof init - 1 : 0); \
+ *(type*)(a + off) = assign; \
+ ELIM (!(strlen (a) expect)); \
+ } while (0)
+
+/* Same as above but the assignment consisting of the two quadwords
+ QW1 and QW2 to support int128_t. */
+#define T2(init, type, off, qw0, qw1, expect) do { \
+ char a[32]; \
+ memcpy (a, init ? init : "", init ? sizeof init - 1: 0); \
+ type assign = ((type)qw0 << (sizeof (type) * CHAR_BIT / 2)) | (type)qw1; \
+ *(type*)(a + off) = assign; \
+ ELIM (!(strlen (a) expect)); \
+ } while (0)
+
+/* Same as T but works around the optimizer dropping the initializing
+ store before the assignment and defeating the strlen optimization. */
+#define TX(init, type, off, assign, expect) do { \
+ char a[32]; \
+ strcpy (a, init + 2); \
+ strcat (a, init + sizeof (init) - 3); \
+ *(type*)(a + off) = assign; \
+ ELIM (!(strlen (a) expect)); \
+ } while (0)
+
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define I16(s) ((s[0] << 8) + s[1])
+# define I32(s) ((s[0] << 24) + (s[1] << 16) + (s[2] << 8) + s[3])
+# define I64(s) \
+ (((uint64_t)s[0] << 56) \
+ + ((uint64_t)s[1] << 48) \
+ + ((uint64_t)s[2] << 40) \
+ + ((uint64_t)s[3] << 32) \
+ + ((uint64_t)s[4] << 24) \
+ + ((uint64_t)s[5] << 16) \
+ + ((uint64_t)s[6] << 8) \
+ + s[7])
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define I16(s) ((s[1] << 8) + s[0])
+# define I32(s) ((s[3] << 24) + (s[2] << 16) + (s[1] << 8) + s[0])
+# define I64(s) \
+ (((uint64_t)s[7] << 56) \
+ + ((uint64_t)s[6] << 48) \
+ + ((uint64_t)s[5] << 40) \
+ + ((uint64_t)s[4] << 32) \
+ + ((uint64_t)s[3] << 24) \
+ + ((uint64_t)s[2] << 16) \
+ + ((uint64_t)s[1] << 8) \
+ + s[0])
+#endif
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+
+void store_16bit_be (void)
+{
+ T ("xxx", int16_t, 0, 0x0001, == 0);
+ T ("xxx", int16_t, 0, 0x0010, == 0);
+ T ("xxx", int16_t, 0, 0x0011, == 0);
+ T ("xxx", int16_t, 0, 0x0100, == 1);
+ T ("xxx", int16_t, 0, 0x1000, == 1);
+ T ("xxx", int16_t, 0, 0x1100, == 1);
+}
+
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+void store_16bit_le (int i)
+{
+ int16_t x0000 = I16 ("\0\0");
+ int16_t x0001 = 0x0001;
+ int16_t x0010 = 0x0010;
+ int16_t x0011 = 0x0011;
+ int16_t x0100 = 0x0100;
+ int16_t x1000 = 0x1000;
+ int16_t x1100 = 0x1100;
+
+ T (0, int16_t, 0, x0000, == 0);
+ T ("x", int16_t, 0, x0000, == 0);
+ T ("xx", int16_t, 0, x0000, == 0);
+ T ("xxxx", int16_t, 0, x0000, == 0);
+ T (0, int16_t, 0, x0001, == 1);
+ T ("\0\0\0", int16_t, 0, x0001, == 1);
+ T (0, int16_t, 0, x0010, == 1);
+ T ("x\0\0", int16_t, 0, x0010, == 1);
+ T (0, int16_t, 0, x0011, == 1);
+ T ("xx\0", int16_t, 0, x0011, == 1);
+ T (0, int16_t, 0, x0100, == 0);
+ T ("\0\0\0", int16_t, 0, x0100, == 0);
+ T (0, int16_t, 0, x1000, == 0);
+ T ("x\0\0", int16_t, 0, x1000, == 0);
+ T (0, int16_t, 0, x1100, == 0);
+ T ("xx\0", int16_t, 0, x1100, == 0);
+
+ // FIXME: This fails because of the next test but succeeds on its own.
+ // T (0, int16_t, 0, i ? x0001 : x0010, == 1);
+ T ("xxx", int16_t, 0, i ? x0100 : x1100, == 0);
+}
+
+#endif
+
+void store_32bit (volatile int i)
+{
+ T (0, int32_t, 0, 0, == 0);
+ T ("x", int32_t, 0, 0, == 0);
+ T ("xx", int32_t, 0, 0, == 0);
+ T ("xxx", int32_t, 0, 0, == 0);
+ T ("xxxx", int32_t, 0, 0, == 0);
+
+ T ("\0", int32_t, 1, 0, == 0);
+ T ("x", int32_t, 1, 0, == 1);
+ T ("xx", int32_t, 2, 0, == 2);
+ T ("xxx", int32_t, 3, 0, == 3);
+
+ T ("xxx", int32_t, 0, I32 ("\01\0\0\0"), == 1);
+ T ("xxx", int32_t, 0, I32 ("\0\01\0\0"), == 0);
+ T ("xxx", int32_t, 0, I32 ("\0\0\01\0"), == 0);
+ T ("xxx", int32_t, 0, I32 ("\0\0\0\01"), == 0);
+
+ T ("xxx", int32_t, 0, I32 ("\1\2\0\0"), == 2);
+ T ("xxx", int32_t, 0, I32 ("\0\1\2\0"), == 0);
+ T ("xxx", int32_t, 0, I32 ("\0\0\1\2"), == 0);
+
+ T ("xxx", int32_t, 0, I32 ("\1\2\3\0"), == 3);
+ T ("xxx", int32_t, 0, I32 ("\0\1\2\3"), == 0);
+
+ int32_t x00332211 = I32 ("123\0");
+ int32_t x00002211 = I32 ("12\0\0");
+ int32_t x00000011 = I32 ("1\0\0\0");
+
+ T ("xxxx", int32_t, 0, i ? x00332211 : x00002211, <= 3);
+ T ("xxxx", int32_t, 0, i ? x00332211 : x00002211, >= 2);
+ T ("xxxx", int32_t, 0, i ? x00332211 : x00000011, <= 3);
+ T ("xxxx", int32_t, 0, i ? x00332211 : x00000011, >= 1);
+
+ TX ("abcde", int32_t, 0, i ? I32 ("1234") : I32 ("1235"), == 5);
+ TX ("abcde", int32_t, 1, i ? I32 ("1234") : I32 ("1235"), == 5);
+
+ TX ("abcdef", int32_t, 0, i ? I32 ("1235") : I32 ("1234"), == 6);
+ TX ("abcdef", int32_t, 1, i ? I32 ("1235") : I32 ("1234"), == 6);
+ TX ("abcdef", int32_t, 2, i ? I32 ("1235") : I32 ("1234"), == 6);
+ TX ("abcdef", int32_t, 3, i ? I32 ("124\0") : I32 ("123\0"), == 6);
+ TX ("abcdef", int32_t, 3, i ? I32 ("12\0\0") : I32 ("13\0\0"), == 5);
+
+ TX ("abcdef", int32_t, 3, i ? I32 ("12\0\0") : I32 ("123\0"), >= 5);
+ TX ("abcdef", int32_t, 3, i ? I32 ("12\0\0") : I32 ("123\0"), < 7);
+}
+
+void store_64bit (int i)
+{
+ T2 ("xxxxxxx", int64_t, 0, 0, I32 ("\1\0\0\0"), == 1);
+ T2 ("xxxxxxx", int64_t, 0, 0, I32 ("\0\1\0\0"), == 0);
+ T2 ("xxxxxxx", int64_t, 0, 0, I32 ("\0\0\1\0"), == 0);
+ T2 ("xxxxxxx", int64_t, 0, 0, I32 ("\0\00\0\1"), == 0);
+ T2 ("xxxxxxx", int64_t, 0, I32 ("\1\0\0\0"), 0, == 0);
+ T2 ("xxxxxxx", int64_t, 0, I32 ("\0\1\0\0"), 0, == 0);
+ T2 ("xxxxxxx", int64_t, 0, I32 ("\0\0\1\0"), 0, == 0);
+ T2 ("xxxxxxx", int64_t, 0, I32 ("\0\0\0\1"), 0, == 0);
+
+ T2 ("xxxxxxx", int64_t, 0, 0, I32 ("\1\2\0\0"), == 2);
+ T2 ("xxxxxxx", int64_t, 0, 0, I32 ("\0\1\2\0"), == 0);
+ T2 ("xxxxxxx", int64_t, 0, 0, I32 ("\0\0\1\2"), == 0);
+
+ T2 ("xxxxxxx", int64_t, 0, 0, I32 ("\1\2\3\0"), == 3);
+ T2 ("xxxxxxx", int64_t, 0, 0, I32 ("\0\1\2\3"), == 0);
+
+ T2 ("xxxxxxx", int64_t, 0, 0, I32 ("\1\2\3\4"), == 4);
+ T2 ("xxxxxxx", int64_t, 0, I32 ("\5\0\0\0"), I32 ("\1\2\3\4"), == 5);
+ T2 ("xxxxxxx", int64_t, 0, I32 ("\5\6\0\0"), I32 ("\1\2\3\4"), == 6);
+ T2 ("xxxxxxx", int64_t, 0, I32 ("\5\6\7\0"), I32 ("\1\2\3\4"), == 7);
+
+ int64_t x7777777 = I64 ("\7\7\7\7\7\7\7");
+ int64_t x666666 = I64 ("\6\6\6\6\6\6\0");
+ int64_t x4444 = I64 ("\4\4\4\4\0\0\0");
+ int64_t x3333 = I64 ("\3\3\3\3\0\0\0");
+ int64_t x1 = I64 ("\1\0\0\0\0\0\0");
+
+ T ("x\0xxxxxx", int64_t, 0, i ? x7777777 : x666666, <= 7);
+ T ("xx\0xxxxx", int64_t, 0, i ? x7777777 : x666666, >= 6);
+ T ("xxx\0xxxx", int64_t, 0, i ? x666666 : x1, <= 6);
+ T ("xxxx\0xxx", int64_t, 0, i ? x666666 : x1, >= 1);
+ T ("xxxxxx\0x", int64_t, 0, i ? x4444 : x3333, == 4);
+}
+
+#ifdef __uint128_t
+
+typedef __uint128_t uint128_t;
+
+void store_128bit (void)
+{
+ uint64_t x1 = I64 ("\1\0\0\0\0\0\0\0");
+ uint64_t x01 = I64 ("\0\1\0\0\0\0\0\0");
+ uint64_t x001 = I64 ("\0\0\1\0\0\0\0\0");
+ uint64_t x0001 = I64 ("\0\0\0\1\0\0\0\0");
+ uint64_t x00001 = I64 ("\0\0\0\0\1\0\0\0");
+ uint64_t x000001 = I64 ("\0\0\0\0\0\1\0\0");
+ uint64_t x0000001 = I64 ("\0\0\0\0\0\0\1\0");
+ uint64_t x00000001 = I64 ("\0\0\0\0\0\0\0\1");
+
+ T2 ("xxxxxxx", uint128_t, 0, 0, x1, == 1);
+ T2 ("xxxxxxx", uint128_t, 0, 0, x01, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, 0, x001, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, 0, x0001, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, 0, x00001, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, 0, x000001, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, 0, x0000001, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, 0, x00000001, == 0);
+
+ T2 ("xxxxxxx", uint128_t, 0, x1, 0, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, x01, 0, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, x001, 0, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, x0001, 0, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, x00001, 0, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, x000001, 0, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, x0000001, 0, == 0);
+ T2 ("xxxxxxx", uint128_t, 0, x00000001, 0, == 0);
+
+ T2 ("xxxxxxx", uint128_t, 0, 0, I64 ("\2\1\0\0\0\0\0\0"), == 2);
+ T2 ("xxxxxxx", uint128_t, 0, 0, I64 ("\0\2\1\0\0\0\0\0"), == 0);
+
+ T2 ("xxxxxxx", uint128_t, 0, 0, I64 ("\3\2\1\0\0\0\0\0"), == 3);
+ T2 ("xxxxxxx", uint128_t, 0, 0, I64 ("\0\3\2\1\0\0\0\0"), == 0);
+
+ uint64_t x4321 = I64 ("\4\3\2\1\0\0\0\0");
+ uint64_t x54321 = I64 ("\5\4\3\2\1\0\0\0");
+ uint64_t x654321 = I64 ("\6\5\4\3\2\1\0\0");
+ uint64_t x7654321 = I64 ("\7\6\5\4\3\2\1\0");
+ uint64_t x87654321 = I64 ("8\7\6\5\4\3\2\1");
+ uint64_t x9 = I64 ("9\0\0\0\0\0\0\0");
+ uint64_t xa9 = I64 ("a9\0\0\0\0\0\0");
+ uint64_t xba9 = I64 ("ba9\0\0\0\0\0\0");
+ uint64_t xcba9 = I64 ("cba9\0\0\0\0\0");
+ uint64_t xdcba9 = I64 ("dcba9\0\0\0\0");
+ uint64_t xedcba9 = I64 ("edcba9\0\0\0\0");
+ uint64_t xfedcba9 = I64 ("fedcba9\0\0\0");
+
+ T2 (0, uint128_t, 0, 0, x4321, == 4);
+ T2 (0, uint128_t, 0, 0, x54321, == 5);
+ T2 (0, uint128_t, 0, 0, x654321, == 6);
+ T2 (0, uint128_t, 0, 0, x7654321, == 7);
+ T2 (0, uint128_t, 0, 0, x87654321, == 8);
+ T2 (0, uint128_t, 0, x9, x87654321, == 9);
+ T2 (0, uint128_t, 0, xa9, x87654321, == 10);
+ T2 (0, uint128_t, 0, xba9, x87654321, == 11);
+ T2 (0, uint128_t, 0, xcba9, x87654321, == 12);
+ T2 (0, uint128_t, 0, xdcba9, x87654321, == 13);
+ T2 (0, uint128_t, 0, xedcba9, x87654321, == 14);
+ T2 (0, uint128_t, 0, xfedcba9, x87654321, == 15);
+}
+
+#endif // __uint128_t
+
+/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } }
+ { dg-final { scan-tree-dump-times "_not_eliminated_" 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-71.c b/gcc/testsuite/gcc.dg/strlenopt-71.c
new file mode 100644
index 0000000..1905519
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-71.c
@@ -0,0 +1,212 @@
+/* PR tree-optimization/91183 - strlen of a strcpy result with a conditional
+ source not folded
+ Runtime test to verify that multibyte stores are handled correctly.
+ { dg-do run }
+ { dg-options "-O2 -Wall" } */
+
+#include "strlenopt.h"
+
+#define CHAR_BIT __CHAR_BIT__
+
+typedef __INT16_TYPE__ int16_t;
+typedef __INT32_TYPE__ int32_t;
+
+#define NOIPA __attribute__ ((noclone, noinline, noipa))
+
+/* Prevent the optimizer from detemining invariants from prior tests. */
+NOIPA void terminate (void)
+{
+ __builtin_abort ();
+}
+
+#define VERIFY(expr, str) \
+ do { \
+ const unsigned expect = strlen (str); \
+ const unsigned len = strlen (expr); \
+ if (len != expect) \
+ { \
+ __builtin_printf ("line %i: strlen(%s) == %u failed: " \
+ "got %u with a = \"%.*s\"\n", \
+ __LINE__, #expr, expect, len, \
+ (int)sizeof a, a); \
+ terminate (); \
+ } \
+ if (memcmp (a, str, expect + 1)) \
+ { \
+ __builtin_printf ("line %i: expected string \"%s\", " \
+ "got a = \"%.*s\"\n", \
+ __LINE__, str, (int)sizeof a, a); \
+ terminate (); \
+ } \
+ } while (0)
+
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define I16(s) ((s[0] << 8) + s[1])
+# define I32(s) ((s[0] << 24) + (s[1] << 16) + (s[2] << 8) + s[3])
+#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define I16(s) ((s[1] << 8) + s[0])
+# define I32(s) ((s[3] << 24) + (s[2] << 16) + (s[1] << 8) + s[0])
+#endif
+
+char a[32];
+
+NOIPA void
+i16_1 (void)
+{
+ *(int16_t*)a = I16 ("12");
+ *(int16_t*)(a + 2) = I16 ("3");
+ VERIFY (a, "123");
+
+ *(int16_t*)(a + 1) = I16 ("23");
+ VERIFY (a, "123");
+
+ *(int16_t*)(a) = I16 ("12");
+ VERIFY (a, "123");
+
+ *(int16_t*)(a + 1) = I16 ("2");
+ VERIFY (a, "12");
+
+ *(int16_t*)(a + 3) = I16 ("45");
+ *(int16_t*)(a + 2) = I16 ("34");
+ VERIFY (a, "12345");
+}
+
+NOIPA void
+i16_2 (void)
+{
+ strcpy (a, "12");
+ strcat (a, "34");
+
+ *(int16_t*)a = I16 ("12");
+ VERIFY (a, "1234");
+
+ *(int16_t*)(a + 1) = I16 ("12");
+ VERIFY (a, "1124");
+
+ *(int16_t*)(a + 2) = I16 ("12");
+ VERIFY (a, "1112");
+
+ *(int16_t*)(a + 3) = I16 ("12");
+ VERIFY (a, "11112");
+
+ *(int16_t*)(a + 4) = I16 ("12");
+ VERIFY (a, "111112");
+}
+
+
+NOIPA void
+i32_1 (void)
+{
+ *(int32_t*)a = I32 ("1234");
+ VERIFY (a, "1234");
+
+ *(int32_t*)(a + 1) = I32 ("2345");
+ VERIFY (a, "12345");
+}
+
+NOIPA void
+i32_2 (void)
+{
+ strcpy (a, "12");
+ strcat (a, "34");
+
+ *(int32_t*)a = I32 ("1234");
+ VERIFY (a, "1234");
+
+ *(int32_t*)(a + 4) = I32 ("567");
+ VERIFY (a, "1234567");
+
+ *(int32_t*)(a + 7) = I32 ("89\0");
+ VERIFY (a, "123456789");
+
+ *(int32_t*)(a + 3) = I32 ("4567");
+ VERIFY (a, "123456789");
+
+ *(int32_t*)(a + 2) = I32 ("3456");
+ VERIFY (a, "123456789");
+
+ *(int32_t*)(a + 1) = I32 ("2345");
+ VERIFY (a, "123456789");
+}
+
+
+NOIPA void
+i32_3 (void)
+{
+ strcpy (a, "1234");
+ strcat (a, "5678");
+
+ *(int32_t*)a = I32 ("1234");
+ VERIFY (a, "12345678");
+
+ *(int32_t*)(a + 1) = I32 ("234");
+ VERIFY (a, "1234");
+
+ *(int32_t*)(a + 2) = I32 ("3456");
+ VERIFY (a, "12345678");
+
+ *(int32_t*)(a + 3) = I32 ("4567");
+ VERIFY (a, "12345678");
+
+ *(int32_t*)(a + 4) = I32 ("5678");
+ VERIFY (a, "12345678");
+
+ *(int32_t*)(a + 5) = I32 ("6789");
+ VERIFY (a, "123456789");
+
+ *(int32_t*)(a + 6) = I32 ("789A");
+ VERIFY (a, "123456789A");
+}
+
+volatile int vzero = 0;
+
+NOIPA void
+i32_4 (void)
+{
+ strcpy (a, "1234");
+ strcat (a, "5678");
+
+ *(int32_t*)a = vzero ? I32 ("1\0\0\0") : I32 ("1234");
+ VERIFY (a, "12345678");
+
+ *(int32_t*)a = vzero ? I32 ("12\0\0") : I32 ("1234");
+ VERIFY (a, "12345678");
+
+ *(int32_t*)a = vzero ? I32 ("123\0") : I32 ("1234");
+ VERIFY (a, "12345678");
+
+ *(int32_t*)a = vzero ? I32 ("1234") : I32 ("1234");
+ VERIFY (a, "12345678");
+
+ *(int32_t*)a = vzero ? I32 ("1235") : I32 ("1234");
+ VERIFY (a, "12345678");
+
+ *(int32_t*)a = vzero ? I32 ("1234") : I32 ("123\0");
+ VERIFY (a, "123");
+
+ *(int32_t*)(a + 3) = vzero ? I32 ("456\0") : I32 ("4567");
+ VERIFY (a, "12345678");
+}
+
+
+int main ()
+{
+ memset (a, 0, sizeof a);
+ i16_1 ();
+
+ memset (a, 0, sizeof a);
+ i16_2 ();
+
+
+ memset (a, 0, sizeof a);
+ i32_1 ();
+
+ memset (a, 0, sizeof a);
+ i32_2 ();
+
+ memset (a, 0, sizeof a);
+ i32_3 ();
+
+ memset (a, 0, sizeof a);
+ i32_4 ();
+}
diff --git a/gcc/testsuite/gcc.dg/strlenopt-72.c b/gcc/testsuite/gcc.dg/strlenopt-72.c
new file mode 100644
index 0000000..a06cc4f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-72.c
@@ -0,0 +1,64 @@
+/* PR tree-optimization/91183 - strlen of a strcpy result with a conditional
+ source not folded
+ Test to verify that strlen can determine string lengths from wider stores
+ than narrow characters. This matters because on targets that can handle
+ unaligned stores and where GCC lowers multi-character stores into smaller
+ numbers of wider stores.
+ { dg-do compile }
+ { dg-options "-O2 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+#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)
+
+/* Macros to emit a call to function named
+ call_failed_to_be_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 (not_eliminated); else (void)0
+
+#undef T
+#define T(N, ncpy, expect, assign) do { \
+ char a[N], b[N]; \
+ assign; \
+ memcpy (b, a, ncpy); \
+ ELIM (!(expect == strlen (b))); \
+ } while (0)
+
+void test_copy (void)
+{
+ T (2, 1, 0, (a[0] = 0));
+ T (2, 2, 0, (a[0] = 0, a[1] = 0));
+ T (2, 2, 1, (a[0] = '1', a[1] = 0));
+ T (4, 3, 2, (a[0] = '1', a[1] = '2', a[2] = 0));
+ // Not handled due to pr83821:
+ // T (4, 3, 1, (a[0] = '1', a[1] = 0, a[2] = '2'));
+ T (4, 2, 1, (a[0] = '1', a[1] = 0, a[2] = 0, a[3] = 0));
+ // Not handled due to pr83821:
+ // T (4, 3, 1, (a[0] = '1', a[1] = 0, a[2] = 0, a[3] = 0));
+ T (4, 4, 1, (a[0] = '1', a[1] = 0, a[2] = 0, a[3] = 0));
+ T (4, 3, 2, (a[0] = '1', a[1] = '2', a[2] = 0, a[3] = 0));
+ T (4, 4, 2, (a[0] = '1', a[1] = '2', a[2] = 0, a[3] = 0));
+ T (4, 4, 3, (a[0] = '1', a[1] = '2', a[2] = '3', a[3] = 0));
+ T (5, 4, 1, (a[0] = '1', a[1] = 0, a[2] = 0, a[3] = 0));
+ T (5, 4, 2, (a[0] = '1', a[1] = '2', a[2] = 0, a[3] = 0));
+ T (5, 4, 3, (a[0] = '1', a[1] = '2', a[2] = '3', a[3] = 0));
+ // Not handled:
+ // T (5, 5, 1, (a[0] = '1', a[1] = 0, a[2] = 0, a[3] = 0, a[4] = 0));
+ // T (5, 5, 2, (a[0] = '1', a[1] = '2', a[2] = 0, a[3] = 0, a[4] = 0));
+ // T (5, 5, 3, (a[0] = '1', a[1] = '2', a[2] = '3', a[3] = 0, a[4] = 0));
+ T (5, 5, 4, (a[0] = '1', a[1] = '2', a[2] = '3', a[3] = '4', a[4] = 0));
+}
+
+
+/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } }
+ { dg-final { scan-tree-dump-times "_not_eliminated_" 0 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-8.c b/gcc/testsuite/gcc.dg/strlenopt-8.c
index 85c6d38..f43b809 100644
--- a/gcc/testsuite/gcc.dg/strlenopt-8.c
+++ b/gcc/testsuite/gcc.dg/strlenopt-8.c
@@ -43,13 +43,7 @@ main ()
return 0;
}
-/* On non-strict-align targets we inline the memcpy that strcat is turned
- into and end up with a short typed load / store which strlenopt is not
- able to analyze. */
-
-/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" { xfail non_strict_align } } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" { target { non_strict_align } } } } */
-/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" { target { ! non_strict_align } } } } */
+/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */