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
This commit is contained in:
parent
7214f11d47
commit
b631bdb3c1
@ -14,6 +14,19 @@
|
||||
* config/msp430/msp430.h (ENDFILE_SPEC): Wrap uses of crtn*.o in
|
||||
if-exists.
|
||||
|
||||
2019-07-24 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
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.
|
||||
|
||||
2019-07-24 Martin Sebor <msebor@redhat.com>
|
||||
|
||||
PR driver/80545
|
||||
|
@ -3652,6 +3652,20 @@ compute_objsize (tree dest, int ostype)
|
||||
if (!ostype)
|
||||
return NULL_TREE;
|
||||
|
||||
if (TREE_CODE (dest) == MEM_REF)
|
||||
{
|
||||
tree ref = TREE_OPERAND (dest, 0);
|
||||
tree off = TREE_OPERAND (dest, 1);
|
||||
if (tree size = compute_objsize (ref, ostype))
|
||||
{
|
||||
if (tree_int_cst_lt (off, size))
|
||||
return fold_build2 (MINUS_EXPR, size_type_node, size, off);
|
||||
return integer_zero_node;
|
||||
}
|
||||
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
if (TREE_CODE (dest) != ADDR_EXPR)
|
||||
return NULL_TREE;
|
||||
|
||||
|
@ -1,3 +1,15 @@
|
||||
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
|
||||
|
@ -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. */
|
||||
|
||||
|
49
gcc/testsuite/gcc.dg/Wstringop-overflow-14.c
Normal file
49
gcc/testsuite/gcc.dg/Wstringop-overflow-14.c
Normal file
@ -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" }
|
||||
}
|
@ -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]" } */
|
||||
}
|
||||
|
293
gcc/testsuite/gcc.dg/strlenopt-70.c
Normal file
293
gcc/testsuite/gcc.dg/strlenopt-70.c
Normal file
@ -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" } } */
|
212
gcc/testsuite/gcc.dg/strlenopt-71.c
Normal file
212
gcc/testsuite/gcc.dg/strlenopt-71.c
Normal file
@ -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 ();
|
||||
}
|
64
gcc/testsuite/gcc.dg/strlenopt-72.c
Normal file
64
gcc/testsuite/gcc.dg/strlenopt-72.c
Normal file
@ -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" } } */
|
@ -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" } } */
|
||||
|
@ -3328,78 +3328,287 @@ handle_pointer_plus (gimple_stmt_iterator *gsi)
|
||||
}
|
||||
}
|
||||
|
||||
/* If RHS, either directly or indirectly, refers to a string of constant
|
||||
length, return the length. Otherwise, if it refers to a character
|
||||
constant, return 1 if the constant is non-zero and 0 if it is nul.
|
||||
Otherwise, return a negative value. */
|
||||
/* Describes recursion limits used by count_nonzero_bytes. */
|
||||
|
||||
static HOST_WIDE_INT
|
||||
get_min_string_length (tree rhs, bool *full_string_p)
|
||||
class ssa_name_limit_t
|
||||
{
|
||||
if (INTEGRAL_TYPE_P (TREE_TYPE (rhs)))
|
||||
bitmap visited; /* Bitmap of visited SSA_NAMEs. */
|
||||
unsigned ssa_def_max; /* Longest chain of SSA_NAMEs to follow. */
|
||||
|
||||
/* Not copyable or assignable. */
|
||||
ssa_name_limit_t (ssa_name_limit_t&);
|
||||
void operator= (ssa_name_limit_t&);
|
||||
|
||||
public:
|
||||
|
||||
ssa_name_limit_t ()
|
||||
: visited (NULL),
|
||||
ssa_def_max (PARAM_VALUE (PARAM_SSA_NAME_DEF_CHAIN_LIMIT)) { }
|
||||
|
||||
int next_ssa_name (tree);
|
||||
|
||||
~ssa_name_limit_t ()
|
||||
{
|
||||
if (tree_expr_nonzero_p (rhs))
|
||||
if (visited)
|
||||
BITMAP_FREE (visited);
|
||||
}
|
||||
};
|
||||
|
||||
/* If the SSA_NAME has already been "seen" return a positive value.
|
||||
Otherwise add it to VISITED. If the SSA_NAME limit has been
|
||||
reached, return a negative value. Otherwise return zero. */
|
||||
|
||||
int ssa_name_limit_t::next_ssa_name (tree ssa_name)
|
||||
{
|
||||
if (!visited)
|
||||
visited = BITMAP_ALLOC (NULL);
|
||||
|
||||
/* Return a positive value if SSA_NAME has already been visited. */
|
||||
if (!bitmap_set_bit (visited, SSA_NAME_VERSION (ssa_name)))
|
||||
return 1;
|
||||
|
||||
/* Return a negative value to let caller avoid recursing beyond
|
||||
the specified limit. */
|
||||
if (ssa_def_max == 0)
|
||||
return -1;
|
||||
|
||||
--ssa_def_max;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Determine the minimum and maximum number of leading non-zero bytes
|
||||
in the representation of EXP and set LENRANGE[0] and LENRANGE[1]
|
||||
to each. Set LENRANGE[2] to the total number of bytes in
|
||||
the representation. Set *NULTREM if the representation contains
|
||||
a zero byte, and set *ALLNUL if all the bytes are zero. Avoid
|
||||
recursing deeper than the limits in SNLIM allow. Return true
|
||||
on success and false otherwise. */
|
||||
|
||||
static bool
|
||||
count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
|
||||
bool *allnul, bool *allnonnul, ssa_name_limit_t &snlim)
|
||||
{
|
||||
if (TREE_CODE (exp) == SSA_NAME)
|
||||
{
|
||||
/* Handle a single-character specially. */
|
||||
tree type = TREE_TYPE (exp);
|
||||
if (TREE_CODE (type) == INTEGER_TYPE
|
||||
&& TYPE_MODE (type) == TYPE_MODE (char_type_node)
|
||||
&& TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node))
|
||||
{
|
||||
*full_string_p = false;
|
||||
return 1;
|
||||
/* Determine if the character EXP is known to be non-zero
|
||||
(even if its exact value is not known) and if so, recurse
|
||||
once to set the range, etc. */
|
||||
if (tree_expr_nonzero_p (exp))
|
||||
return count_nonzero_bytes (build_int_cst (type, 1), lenrange,
|
||||
nulterm, allnul, allnonnul, snlim);
|
||||
/* Don't know whether EXP is or isn't nonzero. */
|
||||
return false;
|
||||
}
|
||||
|
||||
*full_string_p = true;
|
||||
return 0;
|
||||
gimple *stmt = SSA_NAME_DEF_STMT (exp);
|
||||
if (gimple_code (stmt) != GIMPLE_PHI)
|
||||
return false;
|
||||
|
||||
/* Avoid processing an SSA_NAME that has already been visited
|
||||
or if an SSA_NAME limit has been reached. Indicate success
|
||||
if the former and failure if the latter. */
|
||||
if (int res = snlim.next_ssa_name (exp))
|
||||
return res > 0;
|
||||
|
||||
/* Determine the minimum and maximum from the PHI arguments. */
|
||||
unsigned int n = gimple_phi_num_args (stmt);
|
||||
for (unsigned i = 0; i != n; i++)
|
||||
{
|
||||
tree def = gimple_phi_arg_def (stmt, i);
|
||||
if (!count_nonzero_bytes (def, lenrange, nulterm, allnul, allnonnul,
|
||||
snlim))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TREE_CODE (rhs) == MEM_REF
|
||||
&& integer_zerop (TREE_OPERAND (rhs, 1)))
|
||||
{
|
||||
rhs = TREE_OPERAND (rhs, 0);
|
||||
if (TREE_CODE (rhs) == ADDR_EXPR)
|
||||
{
|
||||
tree rhs_addr = rhs;
|
||||
/* Offset from the beginning of the representation bytes, a pointer
|
||||
to the representation, and the number of bytes of the representation
|
||||
to consider (may be less than the object size for MEM_REF). */
|
||||
unsigned HOST_WIDE_INT offset = 0;
|
||||
const char *prep = NULL;
|
||||
unsigned nbytes = 0;
|
||||
|
||||
rhs = TREE_OPERAND (rhs, 0);
|
||||
if (TREE_CODE (rhs) != STRING_CST)
|
||||
if (TREE_CODE (exp) == MEM_REF)
|
||||
{
|
||||
/* If the MEM_REF operand is the address of an object such as
|
||||
a string or integer, extract it and the offset into it. */
|
||||
tree arg = TREE_OPERAND (exp, 0);
|
||||
if (TREE_CODE (arg) != ADDR_EXPR)
|
||||
return false;
|
||||
|
||||
tree off = TREE_OPERAND (exp, 1);
|
||||
if (TREE_CODE (off) != INTEGER_CST
|
||||
|| !tree_fits_uhwi_p (off))
|
||||
return false;
|
||||
|
||||
offset = tree_to_uhwi (off);
|
||||
if (INT_MAX < offset)
|
||||
return false;
|
||||
|
||||
/* The size of the MEM_REF access determines the number of bytes. */
|
||||
tree type = TREE_TYPE (exp);
|
||||
if (tree typesize = TYPE_SIZE_UNIT (type))
|
||||
nbytes = tree_to_uhwi (typesize);
|
||||
|
||||
if (offset == 0 && TREE_CODE (exp) != STRING_CST)
|
||||
{
|
||||
int idx = get_stridx (arg);
|
||||
if (idx > 0)
|
||||
{
|
||||
int idx = get_stridx (rhs_addr);
|
||||
if (idx > 0)
|
||||
strinfo *si = get_strinfo (idx);
|
||||
if (si && tree_fits_shwi_p (si->nonzero_chars))
|
||||
{
|
||||
strinfo *si = get_strinfo (idx);
|
||||
if (si
|
||||
&& tree_fits_shwi_p (si->nonzero_chars))
|
||||
{
|
||||
*full_string_p = si->full_string_p;
|
||||
return tree_to_shwi (si->nonzero_chars);
|
||||
}
|
||||
unsigned len = tree_to_shwi (si->nonzero_chars);
|
||||
if (len < lenrange[0])
|
||||
lenrange[0] = len;
|
||||
if (lenrange[1] < len)
|
||||
lenrange[1] = len;
|
||||
|
||||
if (!si->full_string_p)
|
||||
*nulterm = false;
|
||||
|
||||
/* Since only the length of the string are known and
|
||||
its contents, clear ALLNUL and ALLNONNUL purely on
|
||||
the basis of the length. */
|
||||
if (len)
|
||||
*allnul = false;
|
||||
else
|
||||
*allnonnul = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Proceed to extract the object representation below. */
|
||||
exp = TREE_OPERAND (arg, 0);
|
||||
}
|
||||
|
||||
if (TREE_CODE (rhs) == VAR_DECL
|
||||
&& TREE_READONLY (rhs))
|
||||
rhs = DECL_INITIAL (rhs);
|
||||
|
||||
if (rhs && TREE_CODE (rhs) == STRING_CST)
|
||||
if (TREE_CODE (exp) == VAR_DECL && TREE_READONLY (exp))
|
||||
{
|
||||
HOST_WIDE_INT len = strlen (TREE_STRING_POINTER (rhs));
|
||||
*full_string_p = len < TREE_STRING_LENGTH (rhs);
|
||||
return len;
|
||||
exp = DECL_INITIAL (exp);
|
||||
if (!exp)
|
||||
return false;
|
||||
}
|
||||
|
||||
return -1;
|
||||
if (TREE_CODE (exp) == STRING_CST)
|
||||
{
|
||||
/* Set PREP and NBYTES to the string representation. */
|
||||
gcc_assert (offset <= INT_MAX);
|
||||
|
||||
if (!nbytes)
|
||||
{
|
||||
/* Unless NBYTES has already been determined above from
|
||||
MEM_REF, set it here. It includes all internal nuls,
|
||||
including the terminating one if the string has one. */
|
||||
nbytes = TREE_STRING_LENGTH (exp);
|
||||
if (nbytes <= offset)
|
||||
return false;
|
||||
}
|
||||
|
||||
prep = TREE_STRING_POINTER (exp) + offset;
|
||||
}
|
||||
|
||||
unsigned char buf[256];
|
||||
if (!prep)
|
||||
{
|
||||
/* Try to extract the representation of the constant object. */
|
||||
nbytes = native_encode_expr (exp, buf, sizeof buf, -1);
|
||||
if (!nbytes)
|
||||
return false;
|
||||
|
||||
prep = reinterpret_cast <char *>(buf);
|
||||
}
|
||||
|
||||
/* Compute the number of leading nonzero bytes in the representation
|
||||
and update the minimum and maximum. */
|
||||
unsigned n = strnlen (prep, nbytes);
|
||||
|
||||
if (n < lenrange[0])
|
||||
lenrange[0] = n;
|
||||
if (lenrange[1] < n)
|
||||
lenrange[1] = n;
|
||||
|
||||
/* Set the size of the representation. */
|
||||
if (lenrange[2] < nbytes)
|
||||
lenrange[2] = nbytes;
|
||||
|
||||
/* Clear NULTERM if none of the bytes is zero. */
|
||||
if (n == nbytes)
|
||||
*nulterm = false;
|
||||
|
||||
if (n)
|
||||
{
|
||||
/* When the initial number of non-zero bytes N is non-zero, reset
|
||||
*ALLNUL; if N is less than that the size of the representation
|
||||
also clear *ALLNONNUL. */
|
||||
*allnul = false;
|
||||
if (n < nbytes)
|
||||
*allnonnul = false;
|
||||
}
|
||||
else if (*allnul || *allnonnul)
|
||||
{
|
||||
*allnonnul = false;
|
||||
|
||||
if (*allnul)
|
||||
{
|
||||
/* When either ALLNUL is set and N is zero, also determine
|
||||
whether all subsequent bytes after the first one (which
|
||||
is nul) are zero or nonzero and clear ALLNUL if not. */
|
||||
for (const char *p = prep; p != prep + nbytes; ++p)
|
||||
if (*p)
|
||||
{
|
||||
*allnul = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Handle a single or multiple character store either by single
|
||||
character assignment or by multi-character assignment from
|
||||
MEM_REF. */
|
||||
/* Same as above except with an implicit SSA_NAME limit. */
|
||||
|
||||
static bool
|
||||
handle_char_store (gimple_stmt_iterator *gsi)
|
||||
count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
|
||||
bool *allnul, bool *allnonnul)
|
||||
{
|
||||
/* Set to optimistic values so the caller doesn't have to worry about
|
||||
initializing these and to what. On success, the function will clear
|
||||
these if it determines their values are different but being recursive
|
||||
it never sets either to true. On failure, their values are
|
||||
unspecified. */
|
||||
*nulterm = true;
|
||||
*allnul = true;
|
||||
*allnonnul = true;
|
||||
|
||||
ssa_name_limit_t snlim;
|
||||
return count_nonzero_bytes (exp, lenrange, nulterm, allnul, allnonnul, snlim);
|
||||
}
|
||||
|
||||
/* Handle a single or multibyte store other than by a built-in function,
|
||||
either via a single character assignment or by multi-byte assignment
|
||||
either via MEM_REF or via a type other than char (such as in
|
||||
'*(int*)a = 12345'). Return true when handled. */
|
||||
|
||||
static bool
|
||||
handle_store (gimple_stmt_iterator *gsi)
|
||||
{
|
||||
int idx = -1;
|
||||
strinfo *si = NULL;
|
||||
gimple *stmt = gsi_stmt (*gsi);
|
||||
tree ssaname = NULL_TREE, lhs = gimple_assign_lhs (stmt);
|
||||
tree rhs = gimple_assign_rhs1 (stmt);
|
||||
|
||||
/* The offset of the first byte in LHS modified by the the store. */
|
||||
unsigned HOST_WIDE_INT offset = 0;
|
||||
|
||||
if (TREE_CODE (lhs) == MEM_REF
|
||||
@ -3428,23 +3637,77 @@ handle_char_store (gimple_stmt_iterator *gsi)
|
||||
si = get_strinfo (idx);
|
||||
}
|
||||
|
||||
/* Minimum and maximum leading non-zero bytes and the size of the store. */
|
||||
unsigned lenrange[] = { UINT_MAX, 0, 0 };
|
||||
|
||||
/* Set to the minimum length of the string being assigned if known. */
|
||||
unsigned HOST_WIDE_INT rhs_minlen;
|
||||
|
||||
/* STORING_NONZERO_P is true iff not all stored characters are zero.
|
||||
STORING_ALL_NONZERO_P is true if all stored characters are zero.
|
||||
STORING_ALL_ZEROS_P is true iff all stored characters are zero.
|
||||
Both are false when it's impossible to determine which is true. */
|
||||
bool storing_nonzero_p;
|
||||
bool storing_all_zeros_p = initializer_zerop (rhs, &storing_nonzero_p);
|
||||
if (!storing_nonzero_p)
|
||||
storing_nonzero_p = tree_expr_nonzero_p (rhs);
|
||||
bool full_string_p = storing_all_zeros_p;
|
||||
bool storing_all_nonzero_p;
|
||||
bool storing_all_zeros_p;
|
||||
/* FULL_STRING_P is set when the stored sequence of characters form
|
||||
a nul-terminated string. */
|
||||
bool full_string_p;
|
||||
|
||||
/* Set to the length of the string being assigned if known. */
|
||||
HOST_WIDE_INT rhslen;
|
||||
const bool ranges_valid
|
||||
= count_nonzero_bytes (rhs, lenrange, &full_string_p,
|
||||
&storing_all_zeros_p, &storing_all_nonzero_p);
|
||||
if (ranges_valid)
|
||||
{
|
||||
rhs_minlen = lenrange[0];
|
||||
storing_nonzero_p = lenrange[1] > 0;
|
||||
|
||||
if (tree dstsize = compute_objsize (lhs, 1))
|
||||
if (compare_tree_int (dstsize, lenrange[2]) < 0)
|
||||
warning_n (gimple_location (stmt), OPT_Wstringop_overflow_,
|
||||
lenrange[2],
|
||||
"%Gwriting %u byte into a region of size %E",
|
||||
"%Gwriting %u bytes into a region of size %E",
|
||||
stmt, lenrange[2], dstsize);
|
||||
}
|
||||
else
|
||||
{
|
||||
rhs_minlen = HOST_WIDE_INT_M1U;
|
||||
full_string_p = false;
|
||||
storing_nonzero_p = false;
|
||||
storing_all_zeros_p = false;
|
||||
storing_all_nonzero_p = false;
|
||||
}
|
||||
|
||||
if (si != NULL)
|
||||
{
|
||||
int cmp = compare_nonzero_chars (si, offset);
|
||||
gcc_assert (offset == 0 || cmp >= 0);
|
||||
if (storing_all_zeros_p && cmp == 0 && si->full_string_p)
|
||||
/* The corresponding element is set to 1 if the first and last
|
||||
element, respectively, of the sequence of characters being
|
||||
written over the string described by SI ends before
|
||||
the terminating nul (if it has one), to zero if the nul is
|
||||
being overwritten but not beyond, or negative otherwise. */
|
||||
int store_before_nul[2];
|
||||
if (ranges_valid)
|
||||
{
|
||||
/* The offset of the last stored byte. */
|
||||
unsigned HOST_WIDE_INT endoff = offset + lenrange[2] - 1;
|
||||
store_before_nul[0] = compare_nonzero_chars (si, offset);
|
||||
if (endoff == offset)
|
||||
store_before_nul[1] = store_before_nul[0];
|
||||
else
|
||||
store_before_nul[1] = compare_nonzero_chars (si, endoff);
|
||||
}
|
||||
else
|
||||
{
|
||||
store_before_nul[0] = compare_nonzero_chars (si, offset);
|
||||
store_before_nul[1] = store_before_nul[0];
|
||||
gcc_assert (offset == 0 || store_before_nul[0] >= 0);
|
||||
}
|
||||
|
||||
if (storing_all_zeros_p
|
||||
&& store_before_nul[0] == 0
|
||||
&& store_before_nul[1] == 0
|
||||
&& si->full_string_p)
|
||||
{
|
||||
/* When overwriting a '\0' with a '\0', the store can be removed
|
||||
if we know it has been stored in the current function. */
|
||||
@ -3463,16 +3726,21 @@ handle_char_store (gimple_stmt_iterator *gsi)
|
||||
}
|
||||
}
|
||||
|
||||
if (cmp > 0
|
||||
if (store_before_nul[1] > 0
|
||||
&& storing_nonzero_p
|
||||
&& lenrange[0] == lenrange[1]
|
||||
&& lenrange[0] == lenrange[2]
|
||||
&& TREE_CODE (TREE_TYPE (rhs)) == INTEGER_TYPE)
|
||||
{
|
||||
/* Handle a single non-nul character store.
|
||||
/* Handle a store of one or more non-nul characters that ends
|
||||
before the terminating nul of the destination and so does
|
||||
not affect its length
|
||||
If si->nonzero_chars > OFFSET, we aren't overwriting '\0',
|
||||
and if we aren't storing '\0', we know that the length of the
|
||||
string and any other zero terminated string in memory remains
|
||||
the same. In that case we move to the next gimple statement and
|
||||
return to signal the caller that it shouldn't invalidate anything.
|
||||
and if we aren't storing '\0', we know that the length of
|
||||
the string and any other zero terminated string in memory
|
||||
remains the same. In that case we move to the next gimple
|
||||
statement and return to signal the caller that it shouldn't
|
||||
invalidate anything.
|
||||
|
||||
This is benefical for cases like:
|
||||
|
||||
@ -3493,7 +3761,7 @@ handle_char_store (gimple_stmt_iterator *gsi)
|
||||
|
||||
if (storing_all_zeros_p
|
||||
|| storing_nonzero_p
|
||||
|| (offset != 0 && cmp > 0))
|
||||
|| (offset != 0 && store_before_nul[1] > 0))
|
||||
{
|
||||
/* When STORING_NONZERO_P, we know that the string will start
|
||||
with at least OFFSET + 1 nonzero characters. If storing
|
||||
@ -3506,22 +3774,15 @@ handle_char_store (gimple_stmt_iterator *gsi)
|
||||
OFFSET characters long.
|
||||
|
||||
Otherwise, we're storing an unknown value at offset OFFSET,
|
||||
so need to clip the nonzero_chars to OFFSET. */
|
||||
bool full_string_p = storing_all_zeros_p;
|
||||
HOST_WIDE_INT len = 1;
|
||||
if (storing_nonzero_p)
|
||||
{
|
||||
/* Try to get the minimum length of the string (or
|
||||
individual character) being stored. If it fails,
|
||||
STORING_NONZERO_P guarantees it's at least 1. */
|
||||
len = get_min_string_length (rhs, &full_string_p);
|
||||
if (len < 0)
|
||||
len = 1;
|
||||
}
|
||||
|
||||
so need to clip the nonzero_chars to OFFSET.
|
||||
Use the minimum length of the string (or individual character)
|
||||
being stored if it's known. Otherwise, STORING_NONZERO_P
|
||||
guarantees it's at least 1. */
|
||||
HOST_WIDE_INT len
|
||||
= storing_nonzero_p && ranges_valid ? lenrange[0] : 1;
|
||||
location_t loc = gimple_location (stmt);
|
||||
tree oldlen = si->nonzero_chars;
|
||||
if (cmp == 0 && si->full_string_p)
|
||||
if (store_before_nul[1] == 0 && si->full_string_p)
|
||||
/* We're overwriting the nul terminator with a nonzero or
|
||||
unknown character. If the previous stmt was a memcpy,
|
||||
its length may be decreased. */
|
||||
@ -3567,8 +3828,7 @@ handle_char_store (gimple_stmt_iterator *gsi)
|
||||
HOST_WIDE_INT slen = (storing_all_zeros_p
|
||||
? 0
|
||||
: (storing_nonzero_p
|
||||
? get_min_string_length (rhs, &full_string_p)
|
||||
: -1));
|
||||
&& ranges_valid ? lenrange[0] : -1));
|
||||
tree len = (slen <= 0
|
||||
? size_zero_node
|
||||
: build_int_cst (size_type_node, slen));
|
||||
@ -3583,18 +3843,18 @@ handle_char_store (gimple_stmt_iterator *gsi)
|
||||
}
|
||||
}
|
||||
else if (idx == 0
|
||||
&& (rhslen = get_min_string_length (rhs, &full_string_p)) >= 0
|
||||
&& rhs_minlen < HOST_WIDE_INT_M1U
|
||||
&& ssaname == NULL_TREE
|
||||
&& TREE_CODE (TREE_TYPE (lhs)) == ARRAY_TYPE)
|
||||
{
|
||||
HOST_WIDE_INT a = int_size_in_bytes (TREE_TYPE (lhs));
|
||||
if (a > 0 && (unsigned HOST_WIDE_INT) a > (unsigned HOST_WIDE_INT) rhslen)
|
||||
if (a > 0 && (unsigned HOST_WIDE_INT) a > rhs_minlen)
|
||||
{
|
||||
int idx = new_addr_stridx (lhs);
|
||||
if (idx != 0)
|
||||
{
|
||||
si = new_strinfo (build_fold_addr_expr (lhs), idx,
|
||||
build_int_cst (size_type_node, rhslen),
|
||||
build_int_cst (size_type_node, rhs_minlen),
|
||||
full_string_p);
|
||||
set_strinfo (idx, si);
|
||||
si->dont_invalidate = true;
|
||||
@ -3707,6 +3967,16 @@ fold_strstr_to_strncmp (tree rhs1, tree rhs2, gimple *stmt)
|
||||
}
|
||||
}
|
||||
|
||||
/* Return true if TYPE corresponds to a narrow character type. */
|
||||
|
||||
static bool
|
||||
is_char_type (tree type)
|
||||
{
|
||||
return (TREE_CODE (type) == INTEGER_TYPE
|
||||
&& TYPE_MODE (type) == TYPE_MODE (char_type_node)
|
||||
&& TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node));
|
||||
}
|
||||
|
||||
/* Attempt to check for validity of the performed access a single statement
|
||||
at *GSI using string length knowledge, and to optimize it.
|
||||
If the given basic block needs clean-up of EH, CLEANUP_EH is set to
|
||||
@ -3907,18 +4177,32 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh)
|
||||
}
|
||||
}
|
||||
else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
|
||||
{
|
||||
tree type = TREE_TYPE (lhs);
|
||||
if (TREE_CODE (type) == ARRAY_TYPE)
|
||||
type = TREE_TYPE (type);
|
||||
if (TREE_CODE (type) == INTEGER_TYPE
|
||||
&& TYPE_MODE (type) == TYPE_MODE (char_type_node)
|
||||
&& TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node))
|
||||
{
|
||||
if (! handle_char_store (gsi))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
{
|
||||
tree type = TREE_TYPE (lhs);
|
||||
if (TREE_CODE (type) == ARRAY_TYPE)
|
||||
type = TREE_TYPE (type);
|
||||
|
||||
bool is_char_store = is_char_type (type);
|
||||
if (!is_char_store && TREE_CODE (lhs) == MEM_REF)
|
||||
{
|
||||
/* To consider stores into char objects via integer types
|
||||
other than char but not those to non-character objects,
|
||||
determine the type of the destination rather than just
|
||||
the type of the access. */
|
||||
tree ref = TREE_OPERAND (lhs, 0);
|
||||
type = TREE_TYPE (ref);
|
||||
if (TREE_CODE (type) == POINTER_TYPE)
|
||||
type = TREE_TYPE (type);
|
||||
if (TREE_CODE (type) == ARRAY_TYPE)
|
||||
type = TREE_TYPE (type);
|
||||
if (is_char_type (type))
|
||||
is_char_store = true;
|
||||
}
|
||||
|
||||
/* Handle a single or multibyte assignment. */
|
||||
if (is_char_store && !handle_store (gsi))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (gcond *cond = dyn_cast<gcond *> (stmt))
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user