Make tree-ssa-strlen.c handle partial unterminated strings
tree-ssa-strlen.c looks for cases in which a string is built up using operations like: memcpy (a, "foo", 4); memcpy (a + 3, "bar", 4); int x = strlen (a); As a side-effect, it optimises the non-final memcpys so that they don't include the nul terminator. However, after removing some "& ~0x1"s from tree-ssa-dse.c, the DSE pass does this optimisation itself (because it can tell that later memcpys overwrite the terminators). The strlen pass wasn't able to handle these pre-optimised calls in the same way as the unoptimised ones. This patch adds support for tracking unterminated strings. [Based on the code ARM contributed in branches/ARM/sve-branch@246236] 2017-07-02 Richard Sandiford <richard.sandiford@linaro.org> gcc/ * tree-ssa-strlen.c (strinfo): Rename the length field to nonzero_chars. Add a full_string_p field. (compare_nonzero_chars, zero_length_string_p): New functions. (get_addr_stridx): Add an offset_out parameter. Use compare_nonzero_chars. (get_stridx): Update accordingly. Use compare_nonzero_chars. (new_strinfo): Update after above changes to strinfo. (set_endptr_and_length): Set full_string_p. (get_string_length): Update after above changes to strinfo. (unshare_strinfo): Update call to new_strinfo. (maybe_invalidate): Likewise. (get_stridx_plus_constant): Change off to unsigned HOST_WIDE_INT. Use compare_nonzero_chars and zero_string_p. Treat nonzero_chars as a uhwi instead of an shwi. Update after above changes to strinfo and new_strinfo. (zero_length_string): Assert that chainsi contains full strings. Use zero_length_string_p. Update call to new_strinfo. (adjust_related_strinfos): Update after above changes to strinfo. Copy full_string_p from origsi. (adjust_last_stmt): Use zero_length_string_p. (handle_builtin_strlen): Update after above changes to strinfo and new_strinfo. Install the lhs as the string length if the previous entry didn't describe a full string. (handle_builtin_strchr): Update after above changes to strinfo and new_strinfo. (handle_builtin_strcpy): Likewise. (handle_builtin_strcat): Likewise. (handle_builtin_malloc): Likewise. (handle_pointer_plus): Likewise. (handle_builtin_memcpy): Likewise. Track nonzero characters that aren't necessarily followed by a nul terminator. (handle_char_store): Likewise. gcc/testsuite/ * gcc.dg/strlenopt-32.c: New testcase. * gcc.dg/strlenopt-33.c: Likewise. * gcc.dg/strlenopt-33g.c: Likewise. * gcc.dg/strlenopt-34.c: Likewise. * gcc.dg/strlenopt-35.c: Likewise. From-SVN: r249880
This commit is contained in:
parent
862088aa63
commit
e3f9a279f5
8 changed files with 724 additions and 189 deletions
|
@ -1,3 +1,38 @@
|
|||
2017-07-02 Richard Sandiford <richard.sandiford@linaro.org>
|
||||
|
||||
* tree-ssa-strlen.c (strinfo): Rename the length field to
|
||||
nonzero_chars. Add a full_string_p field.
|
||||
(compare_nonzero_chars, zero_length_string_p): New functions.
|
||||
(get_addr_stridx): Add an offset_out parameter.
|
||||
Use compare_nonzero_chars.
|
||||
(get_stridx): Update accordingly. Use compare_nonzero_chars.
|
||||
(new_strinfo): Update after above changes to strinfo.
|
||||
(set_endptr_and_length): Set full_string_p.
|
||||
(get_string_length): Update after above changes to strinfo.
|
||||
(unshare_strinfo): Update call to new_strinfo.
|
||||
(maybe_invalidate): Likewise.
|
||||
(get_stridx_plus_constant): Change off to unsigned HOST_WIDE_INT.
|
||||
Use compare_nonzero_chars and zero_string_p. Treat nonzero_chars
|
||||
as a uhwi instead of an shwi. Update after above changes to
|
||||
strinfo and new_strinfo.
|
||||
(zero_length_string): Assert that chainsi contains full strings.
|
||||
Use zero_length_string_p. Update call to new_strinfo.
|
||||
(adjust_related_strinfos): Update after above changes to strinfo.
|
||||
Copy full_string_p from origsi.
|
||||
(adjust_last_stmt): Use zero_length_string_p.
|
||||
(handle_builtin_strlen): Update after above changes to strinfo and
|
||||
new_strinfo. Install the lhs as the string length if the previous
|
||||
entry didn't describe a full string.
|
||||
(handle_builtin_strchr): Update after above changes to strinfo
|
||||
and new_strinfo.
|
||||
(handle_builtin_strcpy): Likewise.
|
||||
(handle_builtin_strcat): Likewise.
|
||||
(handle_builtin_malloc): Likewise.
|
||||
(handle_pointer_plus): Likewise.
|
||||
(handle_builtin_memcpy): Likewise. Track nonzero characters
|
||||
that aren't necessarily followed by a nul terminator.
|
||||
(handle_char_store): Likewise.
|
||||
|
||||
2017-07-02 Richard Sandiford <richard.sandiford@linaro.org>
|
||||
|
||||
PR tree-optimization/80769
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
2017-07-02 Richard Sandiford <richard.sandiford@linaro.org>
|
||||
|
||||
* gcc.dg/strlenopt-32.c: New testcase.
|
||||
* gcc.dg/strlenopt-33.c: Likewise.
|
||||
* gcc.dg/strlenopt-33g.c: Likewise.
|
||||
* gcc.dg/strlenopt-34.c: Likewise.
|
||||
* gcc.dg/strlenopt-35.c: Likewise.
|
||||
|
||||
2017-07-02 Richard Sandiford <richard.sandiford@linaro.org>
|
||||
|
||||
PR tree-optimization/80769
|
||||
|
|
193
gcc/testsuite/gcc.dg/strlenopt-32.c
Normal file
193
gcc/testsuite/gcc.dg/strlenopt-32.c
Normal file
|
@ -0,0 +1,193 @@
|
|||
/* { dg-do run } */
|
||||
/* { dg-options "-O2 -fdump-tree-strlen" } */
|
||||
|
||||
#include "strlenopt.h"
|
||||
|
||||
char temp[30];
|
||||
volatile int v;
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f1 (void)
|
||||
{
|
||||
char a[30];
|
||||
v += 1;
|
||||
memcpy (a, "1234567", 7);
|
||||
memcpy (a + 7, "89abcdefg", 9);
|
||||
memcpy (a + 16, "h", 2);
|
||||
return strlen (a); // This strlen should be optimized into 17.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f2 (char *a)
|
||||
{
|
||||
v += 2;
|
||||
memcpy (a, "1234567", 7);
|
||||
memcpy (a + 7, "89abcdefg", 9);
|
||||
memcpy (a + 16, "h", 2);
|
||||
return strlen (a); // This strlen should be optimized into 17.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f3 (void)
|
||||
{
|
||||
char a[30];
|
||||
v += 3;
|
||||
a[0] = '1';
|
||||
memcpy (a + 1, "2345678", 8);
|
||||
return strlen (a); // This strlen should be optimized into 8.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f4 (char *a)
|
||||
{
|
||||
v += 4;
|
||||
a[0] = '1';
|
||||
memcpy (a + 1, "2345678", 8);
|
||||
return strlen (a); // This strlen should be optimized into 8.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f5 (void)
|
||||
{
|
||||
char a[30];
|
||||
v += 5;
|
||||
a[0] = '1';
|
||||
a[1] = '2';
|
||||
a[2] = '3';
|
||||
memcpy (a + 3, "456", 3);
|
||||
a[6] = '7';
|
||||
a[7] = 0;
|
||||
return strlen (a); // This strlen should be optimized into 7.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f6 (char *a)
|
||||
{
|
||||
v += 6;
|
||||
a[0] = '1';
|
||||
a[1] = '2';
|
||||
a[2] = '3';
|
||||
memcpy (a + 3, "456", 3);
|
||||
a[6] = '7';
|
||||
a[7] = 0;
|
||||
return strlen (a); // This strlen should be optimized into 7.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f7 (void)
|
||||
{
|
||||
char a[30];
|
||||
v += 7;
|
||||
strcpy (a, "abcde");
|
||||
int len1 = strlen (a);
|
||||
a[2] = '_';
|
||||
int len2 = strlen (a);
|
||||
return len1 + len2; // This should be optimized into 10.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f8 (char *a)
|
||||
{
|
||||
v += 8;
|
||||
strcpy (a, "abcde");
|
||||
int len1 = strlen (a);
|
||||
a[2] = '_';
|
||||
int len2 = strlen (a);
|
||||
return len1 + len2; // This should be optimized into 10.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f9 (char b)
|
||||
{
|
||||
char a[30];
|
||||
v += 9;
|
||||
strcpy (a, "foo.bar");
|
||||
a[4] = b;
|
||||
a[3] = 0;
|
||||
return strlen (a); // This should be optimized into 3.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f10 (char *a, char b)
|
||||
{
|
||||
v += 10;
|
||||
strcpy (a, "foo.bar");
|
||||
a[4] = b;
|
||||
a[3] = 0;
|
||||
return strlen (a); // This should be optimized into 3.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f11 (void)
|
||||
{
|
||||
char a[30];
|
||||
v += 11;
|
||||
strcpy (temp, "123456");
|
||||
memcpy (a, temp, 7);
|
||||
return strlen (a); // This should be optimized into 6.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f12 (char *a)
|
||||
{
|
||||
v += 12;
|
||||
strcpy (temp, "123456");
|
||||
memcpy (a, temp, 7);
|
||||
return strlen (a); // This should be optimized into 6.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f13 (void)
|
||||
{
|
||||
char a[30];
|
||||
v += 13;
|
||||
strcpy (temp, "1234567");
|
||||
memcpy (a, temp, 7);
|
||||
a[7] = 0;
|
||||
return strlen (a); // This should be optimized into 7.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f14 (char *a)
|
||||
{
|
||||
v += 14;
|
||||
strcpy (temp, "1234567");
|
||||
memcpy (a, temp, 7);
|
||||
a[7] = 0;
|
||||
return strlen (a); // This should be optimized into 7.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f15 (void)
|
||||
{
|
||||
char a[30];
|
||||
v += 15;
|
||||
strcpy (temp, "12345679");
|
||||
memcpy (a, temp, 7);
|
||||
a[7] = 0;
|
||||
return strlen (a); // This should be optimized into 7.
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f16 (char *a)
|
||||
{
|
||||
v += 16;
|
||||
strcpy (temp, "123456789");
|
||||
memcpy (a, temp, 7);
|
||||
a[7] = 0;
|
||||
return strlen (a); // This should be optimized into 7.
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
char a[30];
|
||||
if (f1 () != 17 || f2 (a) != 17 || f3 () != 8 || f4 (a) != 8
|
||||
|| f5 () != 7 || f6 (a) != 7 || f7 () != 10 || f8 (a) != 10
|
||||
|| f9 ('_') != 3 || f10 (a, '_') != 3 || f11 () != 6 || f12 (a) != 6
|
||||
|| f13 () != 7 || f14 (a) != 7 || f15 () != 7 || f16 (a) != 7)
|
||||
abort ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
|
42
gcc/testsuite/gcc.dg/strlenopt-33.c
Normal file
42
gcc/testsuite/gcc.dg/strlenopt-33.c
Normal file
|
@ -0,0 +1,42 @@
|
|||
/* { dg-do run } */
|
||||
/* { dg-options "-O2 -fdump-tree-strlen" } */
|
||||
|
||||
#include "strlenopt.h"
|
||||
|
||||
volatile int v;
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f1 (char *b)
|
||||
{
|
||||
char a[30];
|
||||
v += 1;
|
||||
strcpy (a, b);
|
||||
// This needs to stay.
|
||||
int len1 = strlen (a);
|
||||
a[0] = '_';
|
||||
a[1] = 0;
|
||||
return len1 + strlen (a);
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f2 (char *a, char *b)
|
||||
{
|
||||
v += 2;
|
||||
strcpy (a, b);
|
||||
// This needs to stay.
|
||||
int len1 = strlen (a);
|
||||
a[0] = '_';
|
||||
a[1] = 0;
|
||||
return len1 + strlen (a);
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
char a[30];
|
||||
if (f1 ("foo") != 4 || f2 (a, "foobar") != 7)
|
||||
abort ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
|
45
gcc/testsuite/gcc.dg/strlenopt-33g.c
Normal file
45
gcc/testsuite/gcc.dg/strlenopt-33g.c
Normal file
|
@ -0,0 +1,45 @@
|
|||
/* { dg-do run { target *-*-linux* *-*-gnu* } } */
|
||||
/* { dg-do run } */
|
||||
/* { dg-options "-O2 -fdump-tree-strlen" } */
|
||||
|
||||
#define USE_GNU
|
||||
#include "strlenopt.h"
|
||||
|
||||
volatile int v;
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f1 (char *b)
|
||||
{
|
||||
char a[30];
|
||||
v += 1;
|
||||
// Should be converted to stpcpy.
|
||||
strcpy (a, b);
|
||||
int len1 = strlen (a);
|
||||
a[0] = '_';
|
||||
a[1] = 0;
|
||||
return len1 + strlen (a);
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f2 (char *a, char *b)
|
||||
{
|
||||
v += 2;
|
||||
// Should be converted to stpcpy.
|
||||
strcpy (a, b);
|
||||
int len1 = strlen (a);
|
||||
a[0] = '_';
|
||||
a[1] = 0;
|
||||
return len1 + strlen (a);
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
char a[30];
|
||||
if (f1 ("foo") != 4 || f2 (a, "foobar") != 7)
|
||||
abort ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
|
||||
/* { dg-final { scan-tree-dump-times "stpcpy \\(" 2 "strlen" } } */
|
38
gcc/testsuite/gcc.dg/strlenopt-34.c
Normal file
38
gcc/testsuite/gcc.dg/strlenopt-34.c
Normal file
|
@ -0,0 +1,38 @@
|
|||
/* { dg-do run } */
|
||||
/* { dg-options "-O2 -fdump-tree-strlen" } */
|
||||
|
||||
#include "strlenopt.h"
|
||||
|
||||
volatile int v;
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f1 (char b)
|
||||
{
|
||||
char a[30];
|
||||
v += 1;
|
||||
strcpy (a, "foo.bar");
|
||||
a[3] = b;
|
||||
a[4] = 0;
|
||||
return strlen (a);
|
||||
}
|
||||
|
||||
size_t __attribute__ ((noinline, noclone))
|
||||
f2 (char *a, char b)
|
||||
{
|
||||
v += 2;
|
||||
strcpy (a, "foo.bar");
|
||||
a[3] = b;
|
||||
a[4] = 0;
|
||||
return strlen (a);
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
char a[30];
|
||||
if (f1 ('_') != 4 || f1 (0) != 3 || f2 (a, '_') != 4 || f2 (a, 0) != 3)
|
||||
abort ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "strlen \\(" 2 "strlen" } } */
|
31
gcc/testsuite/gcc.dg/strlenopt-35.c
Normal file
31
gcc/testsuite/gcc.dg/strlenopt-35.c
Normal file
|
@ -0,0 +1,31 @@
|
|||
/* { dg-do run } */
|
||||
/* { dg-options "-O2 -fdump-tree-strlen" } */
|
||||
|
||||
#include "strlenopt.h"
|
||||
|
||||
volatile int v;
|
||||
|
||||
size_t
|
||||
f1 (char *a1)
|
||||
{
|
||||
v += 1;
|
||||
size_t x = strlen (a1);
|
||||
char *a2 = a1 + x;
|
||||
a2[0] = '1';
|
||||
a2[1] = '2';
|
||||
a2[2] = '3';
|
||||
a2[3] = 0;
|
||||
return strlen (a1);
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
char a[30];
|
||||
strcpy (a, "abcd");
|
||||
if (f1 (a) != 7)
|
||||
abort ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* { dg-final { scan-tree-dump-times "strlen \\(" 1 "strlen" } } */
|
|
@ -57,8 +57,13 @@ static int max_stridx;
|
|||
/* String information record. */
|
||||
struct strinfo
|
||||
{
|
||||
/* String length of this string. */
|
||||
tree length;
|
||||
/* Number of leading characters that are known to be nonzero. This is
|
||||
also the length of the string if FULL_STRING_P.
|
||||
|
||||
The values in a list of related string pointers must be consistent;
|
||||
that is, if strinfo B comes X bytes after strinfo A, it must be
|
||||
the case that A->nonzero_chars == X + B->nonzero_chars. */
|
||||
tree nonzero_chars;
|
||||
/* Any of the corresponding pointers for querying alias oracle. */
|
||||
tree ptr;
|
||||
/* This is used for two things:
|
||||
|
@ -105,6 +110,10 @@ struct strinfo
|
|||
/* A flag for the next maybe_invalidate that this strinfo shouldn't
|
||||
be invalidated. Always cleared by maybe_invalidate. */
|
||||
bool dont_invalidate;
|
||||
/* True if the string is known to be nul-terminated after NONZERO_CHARS
|
||||
characters. False is useful when detecting strings that are built
|
||||
up via successive memcpys. */
|
||||
bool full_string_p;
|
||||
};
|
||||
|
||||
/* Pool for allocating strinfo_struct entries. */
|
||||
|
@ -150,7 +159,34 @@ struct laststmt_struct
|
|||
int stridx;
|
||||
} laststmt;
|
||||
|
||||
static int get_stridx_plus_constant (strinfo *, HOST_WIDE_INT, tree);
|
||||
static int get_stridx_plus_constant (strinfo *, unsigned HOST_WIDE_INT, tree);
|
||||
|
||||
/* Return:
|
||||
|
||||
- 1 if SI is known to start with more than OFF nonzero characters.
|
||||
|
||||
- 0 if SI is known to start with OFF nonzero characters,
|
||||
but is not known to start with more.
|
||||
|
||||
- -1 if SI might not start with OFF nonzero characters. */
|
||||
|
||||
static inline int
|
||||
compare_nonzero_chars (strinfo *si, unsigned HOST_WIDE_INT off)
|
||||
{
|
||||
if (si->nonzero_chars
|
||||
&& TREE_CODE (si->nonzero_chars) == INTEGER_CST)
|
||||
return compare_tree_int (si->nonzero_chars, off);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Return true if SI is known to be a zero-length string. */
|
||||
|
||||
static inline bool
|
||||
zero_length_string_p (strinfo *si)
|
||||
{
|
||||
return si->full_string_p && integer_zerop (si->nonzero_chars);
|
||||
}
|
||||
|
||||
/* Return strinfo vector entry IDX. */
|
||||
|
||||
|
@ -175,10 +211,13 @@ get_next_strinfo (strinfo *si)
|
|||
return nextsi;
|
||||
}
|
||||
|
||||
/* Helper function for get_stridx. */
|
||||
/* Helper function for get_stridx. Return the strinfo index of the address
|
||||
of EXP, which is available in PTR if nonnull. If OFFSET_OUT, it is
|
||||
OK to return the index for some X <= &EXP and store &EXP - X in
|
||||
*OFFSET_OUT. */
|
||||
|
||||
static int
|
||||
get_addr_stridx (tree exp, tree ptr)
|
||||
get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out)
|
||||
{
|
||||
HOST_WIDE_INT off;
|
||||
struct stridxlist *list, *last = NULL;
|
||||
|
@ -198,7 +237,11 @@ get_addr_stridx (tree exp, tree ptr)
|
|||
do
|
||||
{
|
||||
if (list->offset == off)
|
||||
return list->idx;
|
||||
{
|
||||
if (offset_out)
|
||||
*offset_out = 0;
|
||||
return list->idx;
|
||||
}
|
||||
if (list->offset > off)
|
||||
return 0;
|
||||
last = list;
|
||||
|
@ -206,14 +249,21 @@ get_addr_stridx (tree exp, tree ptr)
|
|||
}
|
||||
while (list);
|
||||
|
||||
if (ptr && last && last->idx > 0)
|
||||
if ((offset_out || ptr) && last && last->idx > 0)
|
||||
{
|
||||
unsigned HOST_WIDE_INT rel_off
|
||||
= (unsigned HOST_WIDE_INT) off - last->offset;
|
||||
strinfo *si = get_strinfo (last->idx);
|
||||
if (si
|
||||
&& si->length
|
||||
&& TREE_CODE (si->length) == INTEGER_CST
|
||||
&& compare_tree_int (si->length, off - last->offset) != -1)
|
||||
return get_stridx_plus_constant (si, off - last->offset, ptr);
|
||||
if (si && compare_nonzero_chars (si, rel_off) >= 0)
|
||||
{
|
||||
if (offset_out)
|
||||
{
|
||||
*offset_out = rel_off;
|
||||
return last->idx;
|
||||
}
|
||||
else
|
||||
return get_stridx_plus_constant (si, rel_off, ptr);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -253,10 +303,7 @@ get_stridx (tree exp)
|
|||
{
|
||||
strinfo *si
|
||||
= get_strinfo (ssa_ver_to_stridx[SSA_NAME_VERSION (rhs1)]);
|
||||
if (si
|
||||
&& si->length
|
||||
&& TREE_CODE (si->length) == INTEGER_CST
|
||||
&& compare_tree_int (si->length, off) != -1)
|
||||
if (si && compare_nonzero_chars (si, off) >= 0)
|
||||
return get_stridx_plus_constant (si, off, exp);
|
||||
}
|
||||
e = rhs1;
|
||||
|
@ -266,7 +313,7 @@ get_stridx (tree exp)
|
|||
|
||||
if (TREE_CODE (exp) == ADDR_EXPR)
|
||||
{
|
||||
int idx = get_addr_stridx (TREE_OPERAND (exp, 0), exp);
|
||||
int idx = get_addr_stridx (TREE_OPERAND (exp, 0), exp, NULL);
|
||||
if (idx != 0)
|
||||
return idx;
|
||||
}
|
||||
|
@ -419,10 +466,10 @@ new_addr_stridx (tree exp)
|
|||
/* Create a new strinfo. */
|
||||
|
||||
static strinfo *
|
||||
new_strinfo (tree ptr, int idx, tree length)
|
||||
new_strinfo (tree ptr, int idx, tree nonzero_chars, bool full_string_p)
|
||||
{
|
||||
strinfo *si = strinfo_pool.allocate ();
|
||||
si->length = length;
|
||||
si->nonzero_chars = nonzero_chars;
|
||||
si->ptr = ptr;
|
||||
si->stmt = NULL;
|
||||
si->endptr = NULL_TREE;
|
||||
|
@ -433,6 +480,7 @@ new_strinfo (tree ptr, int idx, tree length)
|
|||
si->next = 0;
|
||||
si->writable = false;
|
||||
si->dont_invalidate = false;
|
||||
si->full_string_p = full_string_p;
|
||||
return si;
|
||||
}
|
||||
|
||||
|
@ -492,8 +540,9 @@ set_endptr_and_length (location_t loc, strinfo *si, tree endptr)
|
|||
si->stmt = NULL;
|
||||
tree start_as_size = fold_convert_loc (loc, size_type_node, si->ptr);
|
||||
tree end_as_size = fold_convert_loc (loc, size_type_node, endptr);
|
||||
si->length = fold_build2_loc (loc, MINUS_EXPR, size_type_node,
|
||||
end_as_size, start_as_size);
|
||||
si->nonzero_chars = fold_build2_loc (loc, MINUS_EXPR, size_type_node,
|
||||
end_as_size, start_as_size);
|
||||
si->full_string_p = true;
|
||||
}
|
||||
|
||||
/* Return string length, or NULL if it can't be computed. */
|
||||
|
@ -501,8 +550,8 @@ set_endptr_and_length (location_t loc, strinfo *si, tree endptr)
|
|||
static tree
|
||||
get_string_length (strinfo *si)
|
||||
{
|
||||
if (si->length)
|
||||
return si->length;
|
||||
if (si->nonzero_chars)
|
||||
return si->full_string_p ? si->nonzero_chars : NULL;
|
||||
|
||||
if (si->stmt)
|
||||
{
|
||||
|
@ -595,19 +644,19 @@ get_string_length (strinfo *si)
|
|||
for (strinfo *chainsi = verify_related_strinfos (si);
|
||||
chainsi != NULL;
|
||||
chainsi = get_next_strinfo (chainsi))
|
||||
if (chainsi->length == NULL)
|
||||
if (chainsi->nonzero_chars == NULL)
|
||||
set_endptr_and_length (loc, chainsi, lhs);
|
||||
break;
|
||||
case BUILT_IN_MALLOC:
|
||||
break;
|
||||
/* BUILT_IN_CALLOC always has si->length set. */
|
||||
/* BUILT_IN_CALLOC always has si->nonzero_chars set. */
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return si->length;
|
||||
return si->nonzero_chars;
|
||||
}
|
||||
|
||||
/* Invalidate string length information for strings whose length
|
||||
|
@ -626,7 +675,7 @@ maybe_invalidate (gimple *stmt)
|
|||
if (!si->dont_invalidate)
|
||||
{
|
||||
ao_ref r;
|
||||
/* Do not use si->length. */
|
||||
/* Do not use si->nonzero_chars. */
|
||||
ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE);
|
||||
if (stmt_may_clobber_ref_p_1 (stmt, &r))
|
||||
{
|
||||
|
@ -653,7 +702,7 @@ unshare_strinfo (strinfo *si)
|
|||
if (si->refcount == 1 && !strinfo_shared ())
|
||||
return si;
|
||||
|
||||
nsi = new_strinfo (si->ptr, si->idx, si->length);
|
||||
nsi = new_strinfo (si->ptr, si->idx, si->nonzero_chars, si->full_string_p);
|
||||
nsi->stmt = si->stmt;
|
||||
nsi->endptr = si->endptr;
|
||||
nsi->first = si->first;
|
||||
|
@ -670,39 +719,39 @@ unshare_strinfo (strinfo *si)
|
|||
been created. */
|
||||
|
||||
static int
|
||||
get_stridx_plus_constant (strinfo *basesi, HOST_WIDE_INT off, tree ptr)
|
||||
get_stridx_plus_constant (strinfo *basesi, unsigned HOST_WIDE_INT off,
|
||||
tree ptr)
|
||||
{
|
||||
if (TREE_CODE (ptr) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr))
|
||||
return 0;
|
||||
|
||||
if (basesi->length == NULL_TREE
|
||||
|| TREE_CODE (basesi->length) != INTEGER_CST
|
||||
|| compare_tree_int (basesi->length, off) == -1
|
||||
|| !tree_fits_shwi_p (basesi->length))
|
||||
if (compare_nonzero_chars (basesi, off) < 0
|
||||
|| !tree_fits_uhwi_p (basesi->nonzero_chars))
|
||||
return 0;
|
||||
|
||||
HOST_WIDE_INT len = tree_to_shwi (basesi->length) - off;
|
||||
unsigned HOST_WIDE_INT nonzero_chars
|
||||
= tree_to_uhwi (basesi->nonzero_chars) - off;
|
||||
strinfo *si = basesi, *chainsi;
|
||||
if (si->first || si->prev || si->next)
|
||||
si = verify_related_strinfos (basesi);
|
||||
if (si == NULL
|
||||
|| si->length == NULL_TREE
|
||||
|| TREE_CODE (si->length) != INTEGER_CST)
|
||||
|| si->nonzero_chars == NULL_TREE
|
||||
|| TREE_CODE (si->nonzero_chars) != INTEGER_CST)
|
||||
return 0;
|
||||
|
||||
if (TREE_CODE (ptr) == SSA_NAME
|
||||
&& ssa_ver_to_stridx.length () <= SSA_NAME_VERSION (ptr))
|
||||
ssa_ver_to_stridx.safe_grow_cleared (num_ssa_names);
|
||||
|
||||
gcc_checking_assert (compare_tree_int (si->length, off) != -1);
|
||||
gcc_checking_assert (compare_tree_int (si->nonzero_chars, off) != -1);
|
||||
for (chainsi = si; chainsi->next; chainsi = si)
|
||||
{
|
||||
si = get_next_strinfo (chainsi);
|
||||
if (si == NULL
|
||||
|| si->length == NULL_TREE
|
||||
|| TREE_CODE (si->length) != INTEGER_CST)
|
||||
|| si->nonzero_chars == NULL_TREE
|
||||
|| TREE_CODE (si->nonzero_chars) != INTEGER_CST)
|
||||
break;
|
||||
int r = compare_tree_int (si->length, len);
|
||||
int r = compare_tree_int (si->nonzero_chars, nonzero_chars);
|
||||
if (r != 1)
|
||||
{
|
||||
if (r == 0)
|
||||
|
@ -724,7 +773,8 @@ get_stridx_plus_constant (strinfo *basesi, HOST_WIDE_INT off, tree ptr)
|
|||
int idx = new_stridx (ptr);
|
||||
if (idx == 0)
|
||||
return 0;
|
||||
si = new_strinfo (ptr, idx, build_int_cst (size_type_node, len));
|
||||
si = new_strinfo (ptr, idx, build_int_cst (size_type_node, nonzero_chars),
|
||||
basesi->full_string_p);
|
||||
set_strinfo (idx, si);
|
||||
if (chainsi->next)
|
||||
{
|
||||
|
@ -736,7 +786,7 @@ get_stridx_plus_constant (strinfo *basesi, HOST_WIDE_INT off, tree ptr)
|
|||
if (chainsi->first == 0)
|
||||
chainsi->first = chainsi->idx;
|
||||
chainsi->next = idx;
|
||||
if (chainsi->endptr == NULL_TREE && len == 0)
|
||||
if (chainsi->endptr == NULL_TREE && zero_length_string_p (si))
|
||||
chainsi->endptr = ptr;
|
||||
si->endptr = chainsi->endptr;
|
||||
si->prev = chainsi->idx;
|
||||
|
@ -769,7 +819,7 @@ zero_length_string (tree ptr, strinfo *chainsi)
|
|||
do
|
||||
{
|
||||
/* We shouldn't mix delayed and non-delayed lengths. */
|
||||
gcc_assert (si->length);
|
||||
gcc_assert (si->full_string_p);
|
||||
if (si->endptr == NULL_TREE)
|
||||
{
|
||||
si = unshare_strinfo (si);
|
||||
|
@ -779,7 +829,7 @@ zero_length_string (tree ptr, strinfo *chainsi)
|
|||
si = get_next_strinfo (si);
|
||||
}
|
||||
while (si != NULL);
|
||||
if (chainsi->length && integer_zerop (chainsi->length))
|
||||
if (zero_length_string_p (chainsi))
|
||||
{
|
||||
if (chainsi->next)
|
||||
{
|
||||
|
@ -793,7 +843,7 @@ zero_length_string (tree ptr, strinfo *chainsi)
|
|||
else
|
||||
{
|
||||
/* We shouldn't mix delayed and non-delayed lengths. */
|
||||
gcc_assert (chainsi->length);
|
||||
gcc_assert (chainsi->full_string_p);
|
||||
if (chainsi->first || chainsi->prev || chainsi->next)
|
||||
{
|
||||
chainsi = unshare_strinfo (chainsi);
|
||||
|
@ -806,7 +856,7 @@ zero_length_string (tree ptr, strinfo *chainsi)
|
|||
idx = new_stridx (ptr);
|
||||
if (idx == 0)
|
||||
return NULL;
|
||||
si = new_strinfo (ptr, idx, build_int_cst (size_type_node, 0));
|
||||
si = new_strinfo (ptr, idx, build_int_cst (size_type_node, 0), true);
|
||||
set_strinfo (idx, si);
|
||||
si->endptr = ptr;
|
||||
if (chainsi != NULL)
|
||||
|
@ -824,9 +874,11 @@ zero_length_string (tree ptr, strinfo *chainsi)
|
|||
return si;
|
||||
}
|
||||
|
||||
/* For strinfo ORIGSI whose length has been just updated
|
||||
update also related strinfo lengths (add ADJ to each,
|
||||
but don't adjust ORIGSI). */
|
||||
/* For strinfo ORIGSI whose length has been just updated, adjust other
|
||||
related strinfos so that they match the new ORIGSI. This involves:
|
||||
|
||||
- adding ADJ to the nonzero_chars fields
|
||||
- copying full_string_p from the new ORIGSI. */
|
||||
|
||||
static void
|
||||
adjust_related_strinfos (location_t loc, strinfo *origsi, tree adj)
|
||||
|
@ -848,10 +900,12 @@ adjust_related_strinfos (location_t loc, strinfo *origsi, tree adj)
|
|||
/* We shouldn't see delayed lengths here; the caller must have
|
||||
calculated the old length in order to calculate the
|
||||
adjustment. */
|
||||
gcc_assert (si->length);
|
||||
tem = fold_convert_loc (loc, TREE_TYPE (si->length), adj);
|
||||
si->length = fold_build2_loc (loc, PLUS_EXPR, TREE_TYPE (si->length),
|
||||
si->length, tem);
|
||||
gcc_assert (si->nonzero_chars);
|
||||
tem = fold_convert_loc (loc, TREE_TYPE (si->nonzero_chars), adj);
|
||||
si->nonzero_chars = fold_build2_loc (loc, PLUS_EXPR,
|
||||
TREE_TYPE (si->nonzero_chars),
|
||||
si->nonzero_chars, tem);
|
||||
si->full_string_p = origsi->full_string_p;
|
||||
|
||||
si->endptr = NULL_TREE;
|
||||
si->dont_invalidate = true;
|
||||
|
@ -1020,11 +1074,8 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
|
|||
}
|
||||
}
|
||||
|
||||
if (!is_strcat)
|
||||
{
|
||||
if (si->length == NULL_TREE || !integer_zerop (si->length))
|
||||
return;
|
||||
}
|
||||
if (!is_strcat && !zero_length_string_p (si))
|
||||
return;
|
||||
|
||||
if (is_gimple_assign (last.stmt))
|
||||
{
|
||||
|
@ -1138,12 +1189,13 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
|
|||
print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
|
||||
}
|
||||
if (si != NULL
|
||||
&& TREE_CODE (si->length) != SSA_NAME
|
||||
&& TREE_CODE (si->length) != INTEGER_CST
|
||||
&& TREE_CODE (si->nonzero_chars) != SSA_NAME
|
||||
&& TREE_CODE (si->nonzero_chars) != INTEGER_CST
|
||||
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
|
||||
{
|
||||
si = unshare_strinfo (si);
|
||||
si->length = lhs;
|
||||
si->nonzero_chars = lhs;
|
||||
gcc_assert (si->full_string_p);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -1152,11 +1204,25 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
|
|||
return;
|
||||
if (idx == 0)
|
||||
idx = new_stridx (src);
|
||||
else if (get_strinfo (idx) != NULL)
|
||||
return;
|
||||
else
|
||||
{
|
||||
strinfo *si = get_strinfo (idx);
|
||||
if (si != NULL)
|
||||
{
|
||||
if (!si->full_string_p && !si->stmt)
|
||||
{
|
||||
/* Until now we only had a lower bound on the string length.
|
||||
Install LHS as the actual length. */
|
||||
si = unshare_strinfo (si);
|
||||
si->nonzero_chars = lhs;
|
||||
si->full_string_p = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (idx)
|
||||
{
|
||||
strinfo *si = new_strinfo (src, idx, lhs);
|
||||
strinfo *si = new_strinfo (src, idx, lhs, true);
|
||||
set_strinfo (idx, si);
|
||||
find_equal_ptrs (src, idx);
|
||||
}
|
||||
|
@ -1260,7 +1326,7 @@ handle_builtin_strchr (gimple_stmt_iterator *gsi)
|
|||
tree srcu = fold_convert_loc (loc, size_type_node, src);
|
||||
tree length = fold_build2_loc (loc, MINUS_EXPR,
|
||||
size_type_node, lhsu, srcu);
|
||||
strinfo *si = new_strinfo (src, idx, length);
|
||||
strinfo *si = new_strinfo (src, idx, length, true);
|
||||
si->endptr = lhs;
|
||||
set_strinfo (idx, si);
|
||||
find_equal_ptrs (src, idx);
|
||||
|
@ -1349,9 +1415,10 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
}
|
||||
if (olddsi != NULL)
|
||||
{
|
||||
oldlen = olddsi->length;
|
||||
oldlen = olddsi->nonzero_chars;
|
||||
dsi = unshare_strinfo (olddsi);
|
||||
dsi->length = srclen;
|
||||
dsi->nonzero_chars = srclen;
|
||||
dsi->full_string_p = (srclen != NULL_TREE);
|
||||
/* Break the chain, so adjust_related_strinfo on later pointers in
|
||||
the chain won't adjust this one anymore. */
|
||||
dsi->next = 0;
|
||||
|
@ -1360,14 +1427,14 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
}
|
||||
else
|
||||
{
|
||||
dsi = new_strinfo (dst, didx, srclen);
|
||||
dsi = new_strinfo (dst, didx, srclen, srclen != NULL_TREE);
|
||||
set_strinfo (didx, dsi);
|
||||
find_equal_ptrs (dst, didx);
|
||||
}
|
||||
dsi->writable = true;
|
||||
dsi->dont_invalidate = true;
|
||||
|
||||
if (dsi->length == NULL_TREE)
|
||||
if (dsi->nonzero_chars == NULL_TREE)
|
||||
{
|
||||
strinfo *chainsi;
|
||||
|
||||
|
@ -1388,7 +1455,8 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
invalidated. */
|
||||
chainsi = unshare_strinfo (chainsi);
|
||||
chainsi->stmt = stmt;
|
||||
chainsi->length = NULL_TREE;
|
||||
chainsi->nonzero_chars = NULL_TREE;
|
||||
chainsi->full_string_p = false;
|
||||
chainsi->endptr = NULL_TREE;
|
||||
chainsi->dont_invalidate = true;
|
||||
}
|
||||
|
@ -1556,31 +1624,61 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
&& !integer_zerop (len))
|
||||
adjust_last_stmt (olddsi, stmt, false);
|
||||
|
||||
bool full_string_p;
|
||||
if (idx > 0)
|
||||
{
|
||||
gimple *def_stmt;
|
||||
|
||||
/* Handle memcpy (x, y, l) where l is strlen (y) + 1. */
|
||||
/* Handle memcpy (x, y, l) where l's relationship with strlen (y)
|
||||
is known. */
|
||||
si = get_strinfo (idx);
|
||||
if (si == NULL || si->length == NULL_TREE)
|
||||
return;
|
||||
if (TREE_CODE (len) != SSA_NAME)
|
||||
return;
|
||||
def_stmt = SSA_NAME_DEF_STMT (len);
|
||||
if (!is_gimple_assign (def_stmt)
|
||||
|| gimple_assign_rhs_code (def_stmt) != PLUS_EXPR
|
||||
|| gimple_assign_rhs1 (def_stmt) != si->length
|
||||
|| !integer_onep (gimple_assign_rhs2 (def_stmt)))
|
||||
if (si == NULL || si->nonzero_chars == NULL_TREE)
|
||||
return;
|
||||
if (TREE_CODE (len) == INTEGER_CST
|
||||
&& TREE_CODE (si->nonzero_chars) == INTEGER_CST)
|
||||
{
|
||||
if (tree_int_cst_le (len, si->nonzero_chars))
|
||||
{
|
||||
/* Copying LEN nonzero characters, where LEN is constant. */
|
||||
newlen = len;
|
||||
full_string_p = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Copying the whole of the analyzed part of SI. */
|
||||
newlen = si->nonzero_chars;
|
||||
full_string_p = si->full_string_p;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!si->full_string_p)
|
||||
return;
|
||||
if (TREE_CODE (len) != SSA_NAME)
|
||||
return;
|
||||
def_stmt = SSA_NAME_DEF_STMT (len);
|
||||
if (!is_gimple_assign (def_stmt)
|
||||
|| gimple_assign_rhs_code (def_stmt) != PLUS_EXPR
|
||||
|| gimple_assign_rhs1 (def_stmt) != si->nonzero_chars
|
||||
|| !integer_onep (gimple_assign_rhs2 (def_stmt)))
|
||||
return;
|
||||
/* Copying variable-length string SI (and no more). */
|
||||
newlen = si->nonzero_chars;
|
||||
full_string_p = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
si = NULL;
|
||||
/* Handle memcpy (x, "abcd", 5) or
|
||||
memcpy (x, "abc\0uvw", 7). */
|
||||
if (!tree_fits_uhwi_p (len)
|
||||
|| tree_to_uhwi (len) <= (unsigned HOST_WIDE_INT) ~idx)
|
||||
if (!tree_fits_uhwi_p (len))
|
||||
return;
|
||||
|
||||
unsigned HOST_WIDE_INT clen = tree_to_uhwi (len);
|
||||
unsigned HOST_WIDE_INT nonzero_chars = ~idx;
|
||||
newlen = build_int_cst (size_type_node, MIN (nonzero_chars, clen));
|
||||
full_string_p = clen > nonzero_chars;
|
||||
}
|
||||
|
||||
if (olddsi != NULL && TREE_CODE (len) == SSA_NAME)
|
||||
|
@ -1592,16 +1690,13 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
if (didx == 0)
|
||||
return;
|
||||
}
|
||||
if (si != NULL)
|
||||
newlen = si->length;
|
||||
else
|
||||
newlen = build_int_cst (size_type_node, ~idx);
|
||||
oldlen = NULL_TREE;
|
||||
if (olddsi != NULL)
|
||||
{
|
||||
dsi = unshare_strinfo (olddsi);
|
||||
oldlen = olddsi->length;
|
||||
dsi->length = newlen;
|
||||
oldlen = olddsi->nonzero_chars;
|
||||
dsi->nonzero_chars = newlen;
|
||||
dsi->full_string_p = full_string_p;
|
||||
/* Break the chain, so adjust_related_strinfo on later pointers in
|
||||
the chain won't adjust this one anymore. */
|
||||
dsi->next = 0;
|
||||
|
@ -1610,7 +1705,7 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
}
|
||||
else
|
||||
{
|
||||
dsi = new_strinfo (dst, didx, newlen);
|
||||
dsi = new_strinfo (dst, didx, newlen, full_string_p);
|
||||
set_strinfo (didx, dsi);
|
||||
find_equal_ptrs (dst, didx);
|
||||
}
|
||||
|
@ -1623,12 +1718,11 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
if (oldlen == NULL_TREE)
|
||||
;
|
||||
else if (integer_zerop (oldlen))
|
||||
adj = dsi->length;
|
||||
adj = newlen;
|
||||
else if (TREE_CODE (oldlen) == INTEGER_CST
|
||||
|| TREE_CODE (dsi->length) == INTEGER_CST)
|
||||
adj = fold_build2_loc (loc, MINUS_EXPR,
|
||||
TREE_TYPE (dsi->length), dsi->length,
|
||||
fold_convert_loc (loc, TREE_TYPE (dsi->length),
|
||||
|| TREE_CODE (newlen) == INTEGER_CST)
|
||||
adj = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (newlen), newlen,
|
||||
fold_convert_loc (loc, TREE_TYPE (newlen),
|
||||
oldlen));
|
||||
if (adj != NULL_TREE)
|
||||
adjust_related_strinfos (loc, dsi, adj);
|
||||
|
@ -1640,27 +1734,30 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
if (si != NULL)
|
||||
si->dont_invalidate = true;
|
||||
|
||||
lhs = gimple_call_lhs (stmt);
|
||||
switch (bcode)
|
||||
if (full_string_p)
|
||||
{
|
||||
case BUILT_IN_MEMCPY:
|
||||
case BUILT_IN_MEMCPY_CHK:
|
||||
case BUILT_IN_MEMCPY_CHKP:
|
||||
case BUILT_IN_MEMCPY_CHK_CHKP:
|
||||
/* Allow adjust_last_stmt to decrease this memcpy's size. */
|
||||
laststmt.stmt = stmt;
|
||||
laststmt.len = dsi->length;
|
||||
laststmt.stridx = dsi->idx;
|
||||
if (lhs)
|
||||
ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx;
|
||||
break;
|
||||
case BUILT_IN_MEMPCPY:
|
||||
case BUILT_IN_MEMPCPY_CHK:
|
||||
case BUILT_IN_MEMPCPY_CHKP:
|
||||
case BUILT_IN_MEMPCPY_CHK_CHKP:
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
lhs = gimple_call_lhs (stmt);
|
||||
switch (bcode)
|
||||
{
|
||||
case BUILT_IN_MEMCPY:
|
||||
case BUILT_IN_MEMCPY_CHK:
|
||||
case BUILT_IN_MEMCPY_CHKP:
|
||||
case BUILT_IN_MEMCPY_CHK_CHKP:
|
||||
/* Allow adjust_last_stmt to decrease this memcpy's size. */
|
||||
laststmt.stmt = stmt;
|
||||
laststmt.len = dsi->nonzero_chars;
|
||||
laststmt.stridx = dsi->idx;
|
||||
if (lhs)
|
||||
ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx;
|
||||
break;
|
||||
case BUILT_IN_MEMPCPY:
|
||||
case BUILT_IN_MEMPCPY_CHK:
|
||||
case BUILT_IN_MEMPCPY_CHKP:
|
||||
case BUILT_IN_MEMPCPY_CHK_CHKP:
|
||||
break;
|
||||
default:
|
||||
gcc_unreachable ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1709,14 +1806,15 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
}
|
||||
if (dsi == NULL)
|
||||
{
|
||||
dsi = new_strinfo (dst, didx, NULL_TREE);
|
||||
dsi = new_strinfo (dst, didx, NULL_TREE, false);
|
||||
set_strinfo (didx, dsi);
|
||||
find_equal_ptrs (dst, didx);
|
||||
}
|
||||
else
|
||||
{
|
||||
dsi = unshare_strinfo (dsi);
|
||||
dsi->length = NULL_TREE;
|
||||
dsi->nonzero_chars = NULL_TREE;
|
||||
dsi->full_string_p = false;
|
||||
dsi->next = 0;
|
||||
dsi->endptr = NULL_TREE;
|
||||
}
|
||||
|
@ -1740,7 +1838,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
}
|
||||
|
||||
loc = gimple_location (stmt);
|
||||
dstlen = dsi->length;
|
||||
dstlen = dsi->nonzero_chars;
|
||||
endptr = dsi->endptr;
|
||||
|
||||
dsi = unshare_strinfo (dsi);
|
||||
|
@ -1750,14 +1848,17 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
|
||||
if (srclen != NULL_TREE)
|
||||
{
|
||||
dsi->length = fold_build2_loc (loc, PLUS_EXPR, TREE_TYPE (dsi->length),
|
||||
dsi->length, srclen);
|
||||
dsi->nonzero_chars = fold_build2_loc (loc, PLUS_EXPR,
|
||||
TREE_TYPE (dsi->nonzero_chars),
|
||||
dsi->nonzero_chars, srclen);
|
||||
gcc_assert (dsi->full_string_p);
|
||||
adjust_related_strinfos (loc, dsi, srclen);
|
||||
dsi->dont_invalidate = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
dsi->length = NULL;
|
||||
dsi->nonzero_chars = NULL;
|
||||
dsi->full_string_p = false;
|
||||
if (lhs == NULL_TREE && builtin_decl_implicit_p (BUILT_IN_STPCPY))
|
||||
dsi->dont_invalidate = true;
|
||||
}
|
||||
|
@ -1890,7 +1991,7 @@ handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi)
|
|||
tree length = NULL_TREE;
|
||||
if (bcode == BUILT_IN_CALLOC)
|
||||
length = build_int_cst (size_type_node, 0);
|
||||
strinfo *si = new_strinfo (lhs, idx, length);
|
||||
strinfo *si = new_strinfo (lhs, idx, length, length != NULL_TREE);
|
||||
if (bcode == BUILT_IN_CALLOC)
|
||||
si->endptr = lhs;
|
||||
set_strinfo (idx, si);
|
||||
|
@ -1932,7 +2033,8 @@ handle_builtin_memset (gimple_stmt_iterator *gsi)
|
|||
gimple_stmt_iterator gsi1 = gsi_for_stmt (stmt1);
|
||||
update_gimple_call (&gsi1, builtin_decl_implicit (BUILT_IN_CALLOC), 2,
|
||||
size, build_one_cst (size_type_node));
|
||||
si1->length = build_int_cst (size_type_node, 0);
|
||||
si1->nonzero_chars = build_int_cst (size_type_node, 0);
|
||||
si1->full_string_p = true;
|
||||
si1->stmt = gsi_stmt (gsi1);
|
||||
}
|
||||
else
|
||||
|
@ -2064,18 +2166,20 @@ handle_pointer_plus (gimple_stmt_iterator *gsi)
|
|||
}
|
||||
|
||||
si = get_strinfo (idx);
|
||||
if (si == NULL || si->length == NULL_TREE)
|
||||
if (si == NULL || si->nonzero_chars == NULL_TREE)
|
||||
return;
|
||||
|
||||
off = gimple_assign_rhs2 (stmt);
|
||||
zsi = NULL;
|
||||
if (operand_equal_p (si->length, off, 0))
|
||||
if (si->full_string_p && operand_equal_p (si->nonzero_chars, off, 0))
|
||||
zsi = zero_length_string (lhs, si);
|
||||
else if (TREE_CODE (off) == SSA_NAME)
|
||||
{
|
||||
gimple *def_stmt = SSA_NAME_DEF_STMT (off);
|
||||
if (gimple_assign_single_p (def_stmt)
|
||||
&& operand_equal_p (si->length, gimple_assign_rhs1 (def_stmt), 0))
|
||||
&& si->full_string_p
|
||||
&& operand_equal_p (si->nonzero_chars,
|
||||
gimple_assign_rhs1 (def_stmt), 0))
|
||||
zsi = zero_length_string (lhs, si);
|
||||
}
|
||||
if (zsi != NULL
|
||||
|
@ -2101,63 +2205,63 @@ handle_char_store (gimple_stmt_iterator *gsi)
|
|||
strinfo *si = NULL;
|
||||
gimple *stmt = gsi_stmt (*gsi);
|
||||
tree ssaname = NULL_TREE, lhs = gimple_assign_lhs (stmt);
|
||||
tree rhs = gimple_assign_rhs1 (stmt);
|
||||
unsigned HOST_WIDE_INT offset = 0;
|
||||
|
||||
if (TREE_CODE (lhs) == MEM_REF
|
||||
&& TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME)
|
||||
{
|
||||
if (integer_zerop (TREE_OPERAND (lhs, 1)))
|
||||
tree mem_offset = TREE_OPERAND (lhs, 1);
|
||||
if (tree_fits_uhwi_p (mem_offset))
|
||||
{
|
||||
ssaname = TREE_OPERAND (lhs, 0);
|
||||
idx = get_stridx (ssaname);
|
||||
/* Get the strinfo for the base, and use it if it starts with at
|
||||
least OFFSET nonzero characters. This is trivially true if
|
||||
OFFSET is zero. */
|
||||
offset = tree_to_uhwi (mem_offset);
|
||||
idx = get_stridx (TREE_OPERAND (lhs, 0));
|
||||
if (idx > 0)
|
||||
si = get_strinfo (idx);
|
||||
if (offset == 0)
|
||||
ssaname = TREE_OPERAND (lhs, 0);
|
||||
else if (si == NULL || compare_nonzero_chars (si, offset) < 0)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
idx = get_addr_stridx (lhs, NULL_TREE);
|
||||
|
||||
if (idx > 0)
|
||||
{
|
||||
si = get_strinfo (idx);
|
||||
if (si != NULL && si->length != NULL_TREE && integer_zerop (si->length))
|
||||
idx = get_addr_stridx (lhs, NULL_TREE, &offset);
|
||||
if (idx > 0)
|
||||
si = get_strinfo (idx);
|
||||
}
|
||||
|
||||
bool storing_zero_p = initializer_zerop (rhs);
|
||||
bool storing_nonzero_p = (!storing_zero_p
|
||||
&& TREE_CODE (rhs) == INTEGER_CST
|
||||
&& integer_nonzerop (rhs));
|
||||
|
||||
if (si != NULL)
|
||||
{
|
||||
int cmp = compare_nonzero_chars (si, offset);
|
||||
gcc_assert (offset == 0 || cmp >= 0);
|
||||
if (storing_zero_p && cmp == 0 && si->full_string_p)
|
||||
{
|
||||
if (initializer_zerop (gimple_assign_rhs1 (stmt)))
|
||||
/* When overwriting a '\0' with a '\0', the store can be removed
|
||||
if we know it has been stored in the current function. */
|
||||
if (!stmt_could_throw_p (stmt) && si->writable)
|
||||
{
|
||||
/* When storing '\0', the store can be removed
|
||||
if we know it has been stored in the current function. */
|
||||
if (!stmt_could_throw_p (stmt) && si->writable)
|
||||
{
|
||||
unlink_stmt_vdef (stmt);
|
||||
release_defs (stmt);
|
||||
gsi_remove (gsi, true);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
si->writable = true;
|
||||
gsi_next (gsi);
|
||||
return false;
|
||||
}
|
||||
unlink_stmt_vdef (stmt);
|
||||
release_defs (stmt);
|
||||
gsi_remove (gsi, true);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
/* Otherwise this statement overwrites the '\0' with
|
||||
something, if the previous stmt was a memcpy,
|
||||
its length may be decreased. */
|
||||
adjust_last_stmt (si, stmt, false);
|
||||
{
|
||||
si->writable = true;
|
||||
gsi_next (gsi);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (si != NULL && integer_zerop (gimple_assign_rhs1 (stmt)))
|
||||
{
|
||||
si = unshare_strinfo (si);
|
||||
si->length = build_int_cst (size_type_node, 0);
|
||||
si->endptr = NULL;
|
||||
si->prev = 0;
|
||||
si->next = 0;
|
||||
si->stmt = NULL;
|
||||
si->first = 0;
|
||||
si->writable = true;
|
||||
if (ssaname && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
|
||||
si->endptr = ssaname;
|
||||
si->dont_invalidate = true;
|
||||
}
|
||||
/* If si->length is non-zero constant, we aren't overwriting '\0',
|
||||
/* 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
|
||||
|
@ -2177,35 +2281,74 @@ handle_char_store (gimple_stmt_iterator *gsi)
|
|||
bar (len, len2, len3, len4);
|
||||
}
|
||||
*/
|
||||
else if (si != NULL && si->length != NULL_TREE
|
||||
&& TREE_CODE (si->length) == INTEGER_CST
|
||||
&& integer_nonzerop (gimple_assign_rhs1 (stmt)))
|
||||
else if (storing_nonzero_p && cmp > 0)
|
||||
{
|
||||
gsi_next (gsi);
|
||||
return false;
|
||||
}
|
||||
else if (storing_zero_p || storing_nonzero_p || (offset != 0 && cmp > 0))
|
||||
{
|
||||
/* When storing_nonzero_p, we know that the string now starts
|
||||
with OFFSET + 1 nonzero characters, but don't know whether
|
||||
there's a following nul terminator.
|
||||
|
||||
When storing_zero_p, we know that the string is now OFFSET
|
||||
characters long.
|
||||
|
||||
Otherwise, we're storing an unknown value at offset OFFSET,
|
||||
so need to clip the nonzero_chars to OFFSET. */
|
||||
location_t loc = gimple_location (stmt);
|
||||
tree oldlen = si->nonzero_chars;
|
||||
if (cmp == 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. */
|
||||
adjust_last_stmt (si, stmt, false);
|
||||
si = unshare_strinfo (si);
|
||||
if (storing_nonzero_p)
|
||||
si->nonzero_chars = build_int_cst (size_type_node, offset + 1);
|
||||
else
|
||||
si->nonzero_chars = build_int_cst (size_type_node, offset);
|
||||
si->full_string_p = storing_zero_p;
|
||||
if (storing_zero_p
|
||||
&& ssaname
|
||||
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
|
||||
si->endptr = ssaname;
|
||||
else
|
||||
si->endptr = NULL;
|
||||
si->next = 0;
|
||||
si->stmt = NULL;
|
||||
si->writable = true;
|
||||
si->dont_invalidate = true;
|
||||
if (oldlen)
|
||||
{
|
||||
tree adj = fold_build2_loc (loc, MINUS_EXPR, size_type_node,
|
||||
si->nonzero_chars, oldlen);
|
||||
adjust_related_strinfos (loc, si, adj);
|
||||
}
|
||||
else
|
||||
si->prev = 0;
|
||||
}
|
||||
}
|
||||
else if (idx == 0 && initializer_zerop (gimple_assign_rhs1 (stmt)))
|
||||
else if (idx == 0 && (storing_zero_p || storing_nonzero_p))
|
||||
{
|
||||
if (ssaname)
|
||||
{
|
||||
si = zero_length_string (ssaname, NULL);
|
||||
if (si != NULL)
|
||||
si->dont_invalidate = true;
|
||||
}
|
||||
idx = new_stridx (ssaname);
|
||||
else
|
||||
idx = new_addr_stridx (lhs);
|
||||
if (idx != 0)
|
||||
{
|
||||
int idx = new_addr_stridx (lhs);
|
||||
if (idx != 0)
|
||||
{
|
||||
si = new_strinfo (build_fold_addr_expr (lhs), idx,
|
||||
build_int_cst (size_type_node, 0));
|
||||
set_strinfo (idx, si);
|
||||
si->dont_invalidate = true;
|
||||
}
|
||||
tree ptr = (ssaname ? ssaname : build_fold_addr_expr (lhs));
|
||||
tree len = storing_nonzero_p ? size_one_node : size_zero_node;
|
||||
si = new_strinfo (ptr, idx, len, storing_zero_p);
|
||||
set_strinfo (idx, si);
|
||||
if (storing_zero_p
|
||||
&& ssaname
|
||||
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
|
||||
si->endptr = ssaname;
|
||||
si->dont_invalidate = true;
|
||||
si->writable = true;
|
||||
}
|
||||
if (si != NULL)
|
||||
si->writable = true;
|
||||
}
|
||||
else if (idx == 0
|
||||
&& TREE_CODE (gimple_assign_rhs1 (stmt)) == STRING_CST
|
||||
|
@ -2220,14 +2363,14 @@ handle_char_store (gimple_stmt_iterator *gsi)
|
|||
if (idx != 0)
|
||||
{
|
||||
si = new_strinfo (build_fold_addr_expr (lhs), idx,
|
||||
build_int_cst (size_type_node, l));
|
||||
build_int_cst (size_type_node, l), true);
|
||||
set_strinfo (idx, si);
|
||||
si->dont_invalidate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (si != NULL && initializer_zerop (gimple_assign_rhs1 (stmt)))
|
||||
if (si != NULL && offset == 0 && storing_zero_p)
|
||||
{
|
||||
/* Allow adjust_last_stmt to remove it if the stored '\0'
|
||||
is immediately overwritten. */
|
||||
|
|
Loading…
Add table
Reference in a new issue