blob: 0853023c3b6814ef35a53d76c6214f852fabf162 [file] [log] [blame]
/* 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 __UINT16_TYPE__ uint16_t;
typedef __UINT32_TYPE__ uint32_t;
typedef __UINT64_TYPE__ uint64_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 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)
/* Evaluates to an element at index I of the literal S padded with nuls
on the right. */
#define ELT(s, i) ((s "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0")[i])
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
/* Form a big-endian 16, 32, 64, and 128-byte integer from a string. */
# define I16(s) (((uint16_t)ELT (s, 0) << 8) + (uint16_t)ELT (s, 1))
# define I32(s) \
(((uint32_t)ELT (s, 0) << 24) \
+ ((uint32_t)ELT (s, 1) << 16) \
+ ((uint32_t)ELT (s, 2) << 8) \
+ (uint32_t)ELT (s, 3))
# define I64(s) \
(((uint64_t)ELT (s, 0) << 56) \
+ ((uint64_t)ELT (s, 1) << 48) \
+ ((uint64_t)ELT (s, 2) << 40) \
+ ((uint64_t)ELT (s, 3) << 32) \
+ ((uint64_t)ELT (s, 4) << 24) \
+ ((uint64_t)ELT (s, 5) << 16) \
+ ((uint64_t)ELT (s, 6) << 8) \
+ ELT (s, 7))
# define I128(s) \
(((uint128_t)ELT (s, 0) << (64 + 56)) \
+ ((uint128_t)ELT (s, 1) << (64 + 48)) \
+ ((uint128_t)ELT (s, 2) << (64 + 40)) \
+ ((uint128_t)ELT (s, 3) << (64 + 32)) \
+ ((uint128_t)ELT (s, 4) << (64 + 24)) \
+ ((uint128_t)ELT (s, 5) << (64 + 16)) \
+ ((uint128_t)ELT (s, 6) << (64 + 8)) \
+ ((uint128_t)ELT (s, 7) << 64) \
+ ((uint128_t)ELT (s, 8) << 56) \
+ ((uint128_t)ELT (s, 9) << 48) \
+ ((uint128_t)ELT (s, 10) << 40) \
+ ((uint128_t)ELT (s, 11) << 32) \
+ ((uint128_t)ELT (s, 12) << 24) \
+ ((uint128_t)ELT (s, 13) << 16) \
+ ((uint128_t)ELT (s, 14) << 8) \
+ (uint128_t)ELT (s, 15))
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
/* Form a little-endian 16, 32, 64, and 128-byte integer from a string. */
# define I16(s) (((uint16_t)ELT (s, 1) << 8) + (uint16_t)ELT (s, 0))
# define I32(s) \
(((uint32_t)ELT (s, 3) << 24) \
+ ((uint32_t)ELT (s, 2) << 16) \
+ ((uint32_t)ELT (s, 1) << 8) \
+ (uint32_t)ELT (s, 0))
# define I64(s) \
(((uint64_t)ELT (s, 7) << 56) \
+ ((uint64_t)ELT (s, 6) << 48) \
+ ((uint64_t)ELT (s, 5) << 40) \
+ ((uint64_t)ELT (s, 4) << 32) \
+ ((uint64_t)ELT (s, 3) << 24) \
+ ((uint64_t)ELT (s, 2) << 16) \
+ ((uint64_t)ELT (s, 1) << 8) \
+ ELT (s, 0))
# define I128(s) \
(((uint128_t)ELT (s, 15) << (64 + 56)) \
+ ((uint128_t)ELT (s, 14) << (64 + 48)) \
+ ((uint128_t)ELT (s, 13) << (64 + 40)) \
+ ((uint128_t)ELT (s, 12) << (64 + 32)) \
+ ((uint128_t)ELT (s, 11) << (64 + 24)) \
+ ((uint128_t)ELT (s, 10) << (64 + 16)) \
+ ((uint128_t)ELT (s, 9) << (64 + 8)) \
+ ((uint128_t)ELT (s, 8) << 64) \
+ ((uint128_t)ELT (s, 7) << 56) \
+ ((uint128_t)ELT (s, 6) << 48) \
+ ((uint128_t)ELT (s, 5) << 40) \
+ ((uint128_t)ELT (s, 4) << 32) \
+ ((uint128_t)ELT (s, 3) << 24) \
+ ((uint128_t)ELT (s, 2) << 16) \
+ ((uint128_t)ELT (s, 1) << 8) \
+ (uint128_t)ELT (s, 0))
#endif
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
void store_16bit_be (void)
{
T ("xxx", uint16_t, 0, 0x0001, == 0);
T ("xxx", uint16_t, 0, 0x0010, == 0);
T ("xxx", uint16_t, 0, 0x0011, == 0);
T ("xxx", uint16_t, 0, 0x0100, == 1);
T ("xxx", uint16_t, 0, 0x1000, == 1);
T ("xxx", uint16_t, 0, 0x1100, == 1);
}
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
void store_16bit_le (int i)
{
uint16_t x0000 = I16 ("\0\0");
uint16_t x0001 = 0x0001;
uint16_t x0010 = 0x0010;
uint16_t x0011 = 0x0011;
uint16_t x0100 = 0x0100;
uint16_t x1000 = 0x1000;
uint16_t x1100 = 0x1100;
T (0, uint16_t, 0, x0000, == 0);
T ("x", uint16_t, 0, x0000, == 0);
T ("xx", uint16_t, 0, x0000, == 0);
T ("xxxx", uint16_t, 0, x0000, == 0);
T (0, uint16_t, 0, x0001, == 1);
T ("\0\0\0", uint16_t, 0, x0001, == 1);
T (0, uint16_t, 0, x0010, == 1);
T ("x\0\0", uint16_t, 0, x0010, == 1);
T (0, uint16_t, 0, x0011, == 1);
T ("xx\0", uint16_t, 0, x0011, == 1);
T (0, uint16_t, 0, x0100, == 0);
T ("\0\0\0", uint16_t, 0, x0100, == 0);
T (0, uint16_t, 0, x1000, == 0);
T ("x\0\0", uint16_t, 0, x1000, == 0);
T (0, uint16_t, 0, x1100, == 0);
T ("xx\0", uint16_t, 0, x1100, == 0);
// FIXME: This fails because of the next test but succeeds on its own.
// T (0, uint16_t, 0, i ? x0001 : x0010, == 1);
T ("xxx", uint16_t, 0, i ? x0100 : x1100, == 0);
}
#endif
void store_32bit (volatile int i)
{
T (0, uint32_t, 0, 0, == 0);
T ("x", uint32_t, 0, 0, == 0);
T ("xx", uint32_t, 0, 0, == 0);
T ("xxx", uint32_t, 0, 0, == 0);
T ("xxxx", uint32_t, 0, 0, == 0);
T ("\0", uint32_t, 1, 0, == 0);
T ("x", uint32_t, 1, 0, == 1);
T ("xx", uint32_t, 2, 0, == 2);
T ("xxx", uint32_t, 3, 0, == 3);
T ("xxx", uint32_t, 0, I32 ("\1\0\0\0"), == 1);
T ("xxx", uint32_t, 0, I32 ("\0\1\0\0"), == 0);
T ("xxx", uint32_t, 0, I32 ("\0\0\1\0"), == 0);
T ("xxx", uint32_t, 0, I32 ("\0\0\0\1"), == 0);
T ("xxx", uint32_t, 0, I32 ("\1\2\0\0"), == 2);
T ("xxx", uint32_t, 0, I32 ("\0\1\2\0"), == 0);
T ("xxx", uint32_t, 0, I32 ("\0\0\1\2"), == 0);
T ("xxx", uint32_t, 0, I32 ("\1\2\3\0"), == 3);
T ("xxx", uint32_t, 0, I32 ("\0\1\2\3"), == 0);
uint32_t x123_ = I32 ("123\0");
uint32_t x12__ = I32 ("12\0\0");
uint32_t x1___ = I32 ("1\0\0\0");
// FIXME: Upper bound not implemented yet.
/* T ("xxxx", uint32_t, 0, i ? x123_ : x12__, <= 3); */
T ("xxxx", uint32_t, 0, i ? x123_ : x12__, >= 2);
T ("xxxx", uint32_t, 0, i ? x12__ : x123_, >= 2);
/* T ("xxxx", uint32_t, 0, i ? x123_ : x1___, <= 3); */
T ("xxxx", uint32_t, 0, i ? x123_ : x1___, >= 1);
T ("xxxx", uint32_t, 0, i ? x1___ : x123_, >= 1);
TX ("abcde", uint32_t, 0, i ? I32 ("1234") : I32 ("1235"), == 5);
TX ("abcde", uint32_t, 1, i ? I32 ("1234") : I32 ("1235"), == 5);
TX ("abcdef", uint32_t, 0, i ? I32 ("1235") : I32 ("1234"), == 6);
TX ("abcdef", uint32_t, 1, i ? I32 ("1235") : I32 ("1234"), == 6);
TX ("abcdef", uint32_t, 2, i ? I32 ("1235") : I32 ("1234"), == 6);
TX ("abcdef", uint32_t, 3, i ? I32 ("124\0") : I32 ("123\0"), == 6);
TX ("abcdef", uint32_t, 3, i ? I32 ("12\0\0") : I32 ("13\0\0"), == 5);
TX ("abcdef", uint32_t, 3, i ? I32 ("12\0\0") : I32 ("123\0"), >= 5);
/* FIXME: Upper bound not implemented yet. */
/* TX ("abcdef", uint32_t, 3, i ? I32 ("12\0\0") : I32 ("123\0"), < 7); */
}
void store_64bit (int i)
{
T ("xxxxxxx", uint64_t, 0, I64 ("\1\0\0\0\0\0\0\0\0"), == 1);
T ("xxxxxxx", uint64_t, 0, I64 ("\0\1\0\0\0\0\0\0\0"), == 0);
T ("xxxxxxx", uint64_t, 0, I64 ("\0\0\1\0\0\0\0\0\0"), == 0);
T ("xxxxxxx", uint64_t, 0, I64 ("\0\0\0\1\0\0\0\0\0"), == 0);
T ("xxxxxxx", uint64_t, 0, I64 ("\0\0\0\0\1\0\0\0\0"), == 0);
T ("xxxxxxx", uint64_t, 0, I64 ("\0\0\0\0\0\1\0\0\0"), == 0);
T ("xxxxxxx", uint64_t, 0, I64 ("\0\0\0\0\0\0\1\0\0"), == 0);
T ("xxxxxxx", uint64_t, 0, I64 ("\0\0\0\0\0\0\0\1\0"), == 0);
T ("xxxxxxx", uint64_t, 0, I64 ("\1\2\0\0\0\0\0\0\0"), == 2);
T ("xxxxxxx", uint64_t, 0, I64 ("\0\1\2\0\0\0\0\0\0"), == 0);
T ("xxxxxxx", uint64_t, 0, I64 ("\0\0\1\2\0\0\0\0\0"), == 0);
T ("xxxxxxx", uint64_t, 0, I64 ("\1\2\3\0\0\0\0\0\0"), == 3);
T ("xxxxxxx", uint64_t, 0, I64 ("\0\1\2\3\0\0\0\0\0"), == 0);
T ("xxxxxxx", uint64_t, 0, I64 ("\1\2\3\4\0\0\0\0\0"), == 4);
T ("xxxxxxx", uint64_t, 0, I64 ("\1\2\3\4\5\0\0\0\0"), == 5);
T ("xxxxxxx", uint64_t, 0, I64 ("\1\2\3\4\5\6\0\0\0"), == 6);
T ("xxxxxxx", uint64_t, 0, I64 ("\1\2\3\4\5\6\7\0\0"), == 7);
uint64_t x7777777_ = I64 ("\7\7\7\7\7\7\7");
uint64_t x666666__ = I64 ("\6\6\6\6\6\6\0");
uint64_t x4444____ = I64 ("\4\4\4\4\0\0\0");
uint64_t x4343____ = I64 ("\4\3\4\3\0\0\0");
uint64_t x1_______ = I64 ("\1\0\0\0\0\0\0");
/* FIXME: Upper bound not implemented yet. */
/* T ("x\0xxxxxx", uint64_t, 0, i ? x7777777_ : x666666__, <= 7); */
T ("xx\0xxxxx", uint64_t, 0, i ? x7777777_ : x666666__, >= 6);
T ("xxx\0xxxx", uint64_t, 1, i ? x7777777_ : x666666__, >= 7);
/* T ("xxx\0xxxx", uint64_t, 0, i ? x666666__ : x1, <= 6); */
T ("xxxx\0xxx", uint64_t, 0, i ? x666666__ : x1_______, >= 1);
T ("xxxxxx\0x", uint64_t, 0, i ? x4444____ : x4343____, == 4);
}
#if __SIZEOF_INT128__
typedef __uint128_t uint128_t;
void store_128bit (void)
{
uint128_t x1 = I128 ("\1");
uint128_t x1z1 = I128 ("\0\1");
uint128_t x2z1 = I128 ("\0\0\1");
uint128_t x3z1 = I128 ("\0\0\0\1");
uint128_t x4z1 = I128 ("\0\0\0\0\1");
uint128_t x5z1 = I128 ("\0\0\0\0\0\1");
uint128_t x6z1 = I128 ("\0\0\0\0\0\0\1");
uint128_t x7z1 = I128 ("\0\0\0\0\0\0\0\1");
uint128_t x8z1 = I128 ("\0\0\0\0\0\0\0\0\1");
uint128_t x9z1 = I128 ("\0\0\0\0\0\0\0\0\0\1");
uint128_t x10z1 = I128 ("\0\0\0\0\0\0\0\0\0\0\1");
uint128_t x11z1 = I128 ("\0\0\0\0\0\0\0\0\0\0\0\1");
uint128_t x12z1 = I128 ("\0\0\0\0\0\0\0\0\0\0\0\0\1");
uint128_t x13z1 = I128 ("\0\0\0\0\0\0\0\0\0\0\0\0\0\1");
uint128_t x14z1 = I128 ("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1");
uint128_t x15z1 = I128 ("\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1");
T ("xxxxxxx", uint128_t, 0, x1, == 1);
T ("xxxxxxx", uint128_t, 0, x1z1, == 0);
T ("xxxxxxx", uint128_t, 0, x2z1, == 0);
T ("xxxxxxx", uint128_t, 0, x3z1, == 0);
T ("xxxxxxx", uint128_t, 0, x4z1, == 0);
T ("xxxxxxx", uint128_t, 0, x5z1, == 0);
T ("xxxxxxx", uint128_t, 0, x6z1, == 0);
T ("xxxxxxx", uint128_t, 0, x7z1, == 0);
T ("xxxxxxx", uint128_t, 0, x8z1, == 0);
T ("xxxxxxx", uint128_t, 0, x9z1, == 0);
T ("xxxxxxx", uint128_t, 0, x10z1, == 0);
T ("xxxxxxx", uint128_t, 0, x11z1, == 0);
T ("xxxxxxx", uint128_t, 0, x12z1, == 0);
T ("xxxxxxx", uint128_t, 0, x13z1, == 0);
T ("xxxxxxx", uint128_t, 0, x14z1, == 0);
T ("xxxxxxx", uint128_t, 0, x15z1, == 0);
T ("xxxxxxx", uint128_t, 0, I128 ("\2\1"), == 2);
T ("xxxxxxx", uint128_t, 0, I128 ("\0\2\1"), == 0);
T ("xxxxxxx", uint128_t, 0, I128 ("\3\2\1"), == 3);
T ("xxxxxxx", uint128_t, 0, I128 ("\0\3\2\1"), == 0);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4"), == 4);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4\5"), == 5);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4\5\6"), == 6);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4\5\6\7"), == 7);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4\5\6\7\10"), == 8);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4\5\6\7\10\11"), == 9);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4\5\6\7\10\11\12"), == 10);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4\5\6\7\10\11\12\13"), == 11);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4\5\6\7\10\11\12\13\14"), == 12);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4\5\6\7\10\11\12\13\14\15"), == 13);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4\5\6\7\10\11\12\13\14\15\16"), == 14);
T ("xxxxxxx", uint128_t, 0, I128 ("\1\2\3\4\5\6\7\10\11\12\13\14\15\16\17"), == 15);
}
#endif // __SIZEOF_INT128__
/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } }
{ dg-final { scan-tree-dump-times "_not_eliminated_" 0 "optimized" } } */