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:
Martin Sebor 2019-07-25 00:29:17 +00:00 committed by Martin Sebor
parent 7214f11d47
commit b631bdb3c1
11 changed files with 1037 additions and 102 deletions

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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. */

View 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" }
}

View File

@ -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]" } */
}

View 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" } } */

View 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 ();
}

View 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" } } */

View File

@ -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" } } */

View File

@ -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))
{