diff options
author | Martin Sebor <msebor@redhat.com> | 2019-07-25 00:29:17 +0000 |
---|---|---|
committer | Martin Sebor <msebor@gcc.gnu.org> | 2019-07-24 18:29:17 -0600 |
commit | b631bdb3c16e85f35d38e39b3d315c35e4a5747c (patch) | |
tree | 946823534c8cde2d83143cbdcd1e5d6e8d8d7955 /gcc/testsuite | |
parent | 7214f11d4708a62868f4b0830ef65b87976d1826 (diff) | |
download | gcc-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/ChangeLog | 12 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/ubsan/object-size-9.c | 2 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/Wstringop-overflow-14.c | 49 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/attr-nonstring-2.c | 8 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/strlenopt-70.c | 293 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/strlenopt-71.c | 212 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/strlenopt-72.c | 64 | ||||
-rw-r--r-- | gcc/testsuite/gcc.dg/strlenopt-8.c | 8 |
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" } } */ |