Commit e3f9a279 by Richard Sandiford Committed by Richard Sandiford

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
parent 862088aa
2017-07-02 Richard Sandiford <richard.sandiford@linaro.org> 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 PR tree-optimization/80769
* tree-ssa-strlen.c (strinfo): Document that "stmt" is also used * tree-ssa-strlen.c (strinfo): Document that "stmt" is also used
for malloc and calloc. Document the new invariant that all related for malloc and calloc. Document the new invariant that all related
......
2017-07-02 Richard Sandiford <richard.sandiford@linaro.org> 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 PR tree-optimization/80769
* gcc.dg/strlenopt-31.c: New test. * gcc.dg/strlenopt-31.c: New test.
* gcc.dg/strlenopt-31g.c: Likewise. * gcc.dg/strlenopt-31g.c: Likewise.
......
/* { 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" } } */
/* { 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" } } */
/* { 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" } } */
/* { 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" } } */
/* { 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; ...@@ -57,8 +57,13 @@ static int max_stridx;
/* String information record. */ /* String information record. */
struct strinfo struct strinfo
{ {
/* String length of this string. */ /* Number of leading characters that are known to be nonzero. This is
tree length; 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. */ /* Any of the corresponding pointers for querying alias oracle. */
tree ptr; tree ptr;
/* This is used for two things: /* This is used for two things:
...@@ -105,6 +110,10 @@ struct strinfo ...@@ -105,6 +110,10 @@ struct strinfo
/* A flag for the next maybe_invalidate that this strinfo shouldn't /* A flag for the next maybe_invalidate that this strinfo shouldn't
be invalidated. Always cleared by maybe_invalidate. */ be invalidated. Always cleared by maybe_invalidate. */
bool dont_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. */ /* Pool for allocating strinfo_struct entries. */
...@@ -150,7 +159,34 @@ struct laststmt_struct ...@@ -150,7 +159,34 @@ struct laststmt_struct
int stridx; int stridx;
} laststmt; } 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. */ /* Return strinfo vector entry IDX. */
...@@ -175,10 +211,13 @@ get_next_strinfo (strinfo *si) ...@@ -175,10 +211,13 @@ get_next_strinfo (strinfo *si)
return nextsi; 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 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; HOST_WIDE_INT off;
struct stridxlist *list, *last = NULL; struct stridxlist *list, *last = NULL;
...@@ -198,7 +237,11 @@ get_addr_stridx (tree exp, tree ptr) ...@@ -198,7 +237,11 @@ get_addr_stridx (tree exp, tree ptr)
do do
{ {
if (list->offset == off) if (list->offset == off)
return list->idx; {
if (offset_out)
*offset_out = 0;
return list->idx;
}
if (list->offset > off) if (list->offset > off)
return 0; return 0;
last = list; last = list;
...@@ -206,14 +249,21 @@ get_addr_stridx (tree exp, tree ptr) ...@@ -206,14 +249,21 @@ get_addr_stridx (tree exp, tree ptr)
} }
while (list); 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); strinfo *si = get_strinfo (last->idx);
if (si if (si && compare_nonzero_chars (si, rel_off) >= 0)
&& si->length {
&& TREE_CODE (si->length) == INTEGER_CST if (offset_out)
&& compare_tree_int (si->length, off - last->offset) != -1) {
return get_stridx_plus_constant (si, off - last->offset, ptr); *offset_out = rel_off;
return last->idx;
}
else
return get_stridx_plus_constant (si, rel_off, ptr);
}
} }
return 0; return 0;
} }
...@@ -253,10 +303,7 @@ get_stridx (tree exp) ...@@ -253,10 +303,7 @@ get_stridx (tree exp)
{ {
strinfo *si strinfo *si
= get_strinfo (ssa_ver_to_stridx[SSA_NAME_VERSION (rhs1)]); = get_strinfo (ssa_ver_to_stridx[SSA_NAME_VERSION (rhs1)]);
if (si if (si && compare_nonzero_chars (si, off) >= 0)
&& si->length
&& TREE_CODE (si->length) == INTEGER_CST
&& compare_tree_int (si->length, off) != -1)
return get_stridx_plus_constant (si, off, exp); return get_stridx_plus_constant (si, off, exp);
} }
e = rhs1; e = rhs1;
...@@ -266,7 +313,7 @@ get_stridx (tree exp) ...@@ -266,7 +313,7 @@ get_stridx (tree exp)
if (TREE_CODE (exp) == ADDR_EXPR) 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) if (idx != 0)
return idx; return idx;
} }
...@@ -419,10 +466,10 @@ new_addr_stridx (tree exp) ...@@ -419,10 +466,10 @@ new_addr_stridx (tree exp)
/* Create a new strinfo. */ /* Create a new strinfo. */
static 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 (); strinfo *si = strinfo_pool.allocate ();
si->length = length; si->nonzero_chars = nonzero_chars;
si->ptr = ptr; si->ptr = ptr;
si->stmt = NULL; si->stmt = NULL;
si->endptr = NULL_TREE; si->endptr = NULL_TREE;
...@@ -433,6 +480,7 @@ new_strinfo (tree ptr, int idx, tree length) ...@@ -433,6 +480,7 @@ new_strinfo (tree ptr, int idx, tree length)
si->next = 0; si->next = 0;
si->writable = false; si->writable = false;
si->dont_invalidate = false; si->dont_invalidate = false;
si->full_string_p = full_string_p;
return si; return si;
} }
...@@ -492,8 +540,9 @@ set_endptr_and_length (location_t loc, strinfo *si, tree endptr) ...@@ -492,8 +540,9 @@ set_endptr_and_length (location_t loc, strinfo *si, tree endptr)
si->stmt = NULL; si->stmt = NULL;
tree start_as_size = fold_convert_loc (loc, size_type_node, si->ptr); 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); tree end_as_size = fold_convert_loc (loc, size_type_node, endptr);
si->length = fold_build2_loc (loc, MINUS_EXPR, size_type_node, si->nonzero_chars = fold_build2_loc (loc, MINUS_EXPR, size_type_node,
end_as_size, start_as_size); end_as_size, start_as_size);
si->full_string_p = true;
} }
/* Return string length, or NULL if it can't be computed. */ /* 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) ...@@ -501,8 +550,8 @@ set_endptr_and_length (location_t loc, strinfo *si, tree endptr)
static tree static tree
get_string_length (strinfo *si) get_string_length (strinfo *si)
{ {
if (si->length) if (si->nonzero_chars)
return si->length; return si->full_string_p ? si->nonzero_chars : NULL;
if (si->stmt) if (si->stmt)
{ {
...@@ -595,19 +644,19 @@ get_string_length (strinfo *si) ...@@ -595,19 +644,19 @@ get_string_length (strinfo *si)
for (strinfo *chainsi = verify_related_strinfos (si); for (strinfo *chainsi = verify_related_strinfos (si);
chainsi != NULL; chainsi != NULL;
chainsi = get_next_strinfo (chainsi)) chainsi = get_next_strinfo (chainsi))
if (chainsi->length == NULL) if (chainsi->nonzero_chars == NULL)
set_endptr_and_length (loc, chainsi, lhs); set_endptr_and_length (loc, chainsi, lhs);
break; break;
case BUILT_IN_MALLOC: case BUILT_IN_MALLOC:
break; break;
/* BUILT_IN_CALLOC always has si->length set. */ /* BUILT_IN_CALLOC always has si->nonzero_chars set. */
default: default:
gcc_unreachable (); gcc_unreachable ();
break; break;
} }
} }
return si->length; return si->nonzero_chars;
} }
/* Invalidate string length information for strings whose length /* Invalidate string length information for strings whose length
...@@ -626,7 +675,7 @@ maybe_invalidate (gimple *stmt) ...@@ -626,7 +675,7 @@ maybe_invalidate (gimple *stmt)
if (!si->dont_invalidate) if (!si->dont_invalidate)
{ {
ao_ref r; 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); ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE);
if (stmt_may_clobber_ref_p_1 (stmt, &r)) if (stmt_may_clobber_ref_p_1 (stmt, &r))
{ {
...@@ -653,7 +702,7 @@ unshare_strinfo (strinfo *si) ...@@ -653,7 +702,7 @@ unshare_strinfo (strinfo *si)
if (si->refcount == 1 && !strinfo_shared ()) if (si->refcount == 1 && !strinfo_shared ())
return si; 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->stmt = si->stmt;
nsi->endptr = si->endptr; nsi->endptr = si->endptr;
nsi->first = si->first; nsi->first = si->first;
...@@ -670,39 +719,39 @@ unshare_strinfo (strinfo *si) ...@@ -670,39 +719,39 @@ unshare_strinfo (strinfo *si)
been created. */ been created. */
static int 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)) if (TREE_CODE (ptr) == SSA_NAME && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ptr))
return 0; return 0;
if (basesi->length == NULL_TREE if (compare_nonzero_chars (basesi, off) < 0
|| TREE_CODE (basesi->length) != INTEGER_CST || !tree_fits_uhwi_p (basesi->nonzero_chars))
|| compare_tree_int (basesi->length, off) == -1
|| !tree_fits_shwi_p (basesi->length))
return 0; 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; strinfo *si = basesi, *chainsi;
if (si->first || si->prev || si->next) if (si->first || si->prev || si->next)
si = verify_related_strinfos (basesi); si = verify_related_strinfos (basesi);
if (si == NULL if (si == NULL
|| si->length == NULL_TREE || si->nonzero_chars == NULL_TREE
|| TREE_CODE (si->length) != INTEGER_CST) || TREE_CODE (si->nonzero_chars) != INTEGER_CST)
return 0; return 0;
if (TREE_CODE (ptr) == SSA_NAME if (TREE_CODE (ptr) == SSA_NAME
&& ssa_ver_to_stridx.length () <= SSA_NAME_VERSION (ptr)) && ssa_ver_to_stridx.length () <= SSA_NAME_VERSION (ptr))
ssa_ver_to_stridx.safe_grow_cleared (num_ssa_names); 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) for (chainsi = si; chainsi->next; chainsi = si)
{ {
si = get_next_strinfo (chainsi); si = get_next_strinfo (chainsi);
if (si == NULL if (si == NULL
|| si->length == NULL_TREE || si->nonzero_chars == NULL_TREE
|| TREE_CODE (si->length) != INTEGER_CST) || TREE_CODE (si->nonzero_chars) != INTEGER_CST)
break; break;
int r = compare_tree_int (si->length, len); int r = compare_tree_int (si->nonzero_chars, nonzero_chars);
if (r != 1) if (r != 1)
{ {
if (r == 0) if (r == 0)
...@@ -724,7 +773,8 @@ get_stridx_plus_constant (strinfo *basesi, HOST_WIDE_INT off, tree ptr) ...@@ -724,7 +773,8 @@ get_stridx_plus_constant (strinfo *basesi, HOST_WIDE_INT off, tree ptr)
int idx = new_stridx (ptr); int idx = new_stridx (ptr);
if (idx == 0) if (idx == 0)
return 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); set_strinfo (idx, si);
if (chainsi->next) if (chainsi->next)
{ {
...@@ -736,7 +786,7 @@ get_stridx_plus_constant (strinfo *basesi, HOST_WIDE_INT off, tree ptr) ...@@ -736,7 +786,7 @@ get_stridx_plus_constant (strinfo *basesi, HOST_WIDE_INT off, tree ptr)
if (chainsi->first == 0) if (chainsi->first == 0)
chainsi->first = chainsi->idx; chainsi->first = chainsi->idx;
chainsi->next = idx; chainsi->next = idx;
if (chainsi->endptr == NULL_TREE && len == 0) if (chainsi->endptr == NULL_TREE && zero_length_string_p (si))
chainsi->endptr = ptr; chainsi->endptr = ptr;
si->endptr = chainsi->endptr; si->endptr = chainsi->endptr;
si->prev = chainsi->idx; si->prev = chainsi->idx;
...@@ -769,7 +819,7 @@ zero_length_string (tree ptr, strinfo *chainsi) ...@@ -769,7 +819,7 @@ zero_length_string (tree ptr, strinfo *chainsi)
do do
{ {
/* We shouldn't mix delayed and non-delayed lengths. */ /* We shouldn't mix delayed and non-delayed lengths. */
gcc_assert (si->length); gcc_assert (si->full_string_p);
if (si->endptr == NULL_TREE) if (si->endptr == NULL_TREE)
{ {
si = unshare_strinfo (si); si = unshare_strinfo (si);
...@@ -779,7 +829,7 @@ zero_length_string (tree ptr, strinfo *chainsi) ...@@ -779,7 +829,7 @@ zero_length_string (tree ptr, strinfo *chainsi)
si = get_next_strinfo (si); si = get_next_strinfo (si);
} }
while (si != NULL); while (si != NULL);
if (chainsi->length && integer_zerop (chainsi->length)) if (zero_length_string_p (chainsi))
{ {
if (chainsi->next) if (chainsi->next)
{ {
...@@ -793,7 +843,7 @@ zero_length_string (tree ptr, strinfo *chainsi) ...@@ -793,7 +843,7 @@ zero_length_string (tree ptr, strinfo *chainsi)
else else
{ {
/* We shouldn't mix delayed and non-delayed lengths. */ /* 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) if (chainsi->first || chainsi->prev || chainsi->next)
{ {
chainsi = unshare_strinfo (chainsi); chainsi = unshare_strinfo (chainsi);
...@@ -806,7 +856,7 @@ zero_length_string (tree ptr, strinfo *chainsi) ...@@ -806,7 +856,7 @@ zero_length_string (tree ptr, strinfo *chainsi)
idx = new_stridx (ptr); idx = new_stridx (ptr);
if (idx == 0) if (idx == 0)
return NULL; 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); set_strinfo (idx, si);
si->endptr = ptr; si->endptr = ptr;
if (chainsi != NULL) if (chainsi != NULL)
...@@ -824,9 +874,11 @@ zero_length_string (tree ptr, strinfo *chainsi) ...@@ -824,9 +874,11 @@ zero_length_string (tree ptr, strinfo *chainsi)
return si; return si;
} }
/* For strinfo ORIGSI whose length has been just updated /* For strinfo ORIGSI whose length has been just updated, adjust other
update also related strinfo lengths (add ADJ to each, related strinfos so that they match the new ORIGSI. This involves:
but don't adjust ORIGSI). */
- adding ADJ to the nonzero_chars fields
- copying full_string_p from the new ORIGSI. */
static void static void
adjust_related_strinfos (location_t loc, strinfo *origsi, tree adj) adjust_related_strinfos (location_t loc, strinfo *origsi, tree adj)
...@@ -848,10 +900,12 @@ 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 /* We shouldn't see delayed lengths here; the caller must have
calculated the old length in order to calculate the calculated the old length in order to calculate the
adjustment. */ adjustment. */
gcc_assert (si->length); gcc_assert (si->nonzero_chars);
tem = fold_convert_loc (loc, TREE_TYPE (si->length), adj); tem = fold_convert_loc (loc, TREE_TYPE (si->nonzero_chars), adj);
si->length = fold_build2_loc (loc, PLUS_EXPR, TREE_TYPE (si->length), si->nonzero_chars = fold_build2_loc (loc, PLUS_EXPR,
si->length, tem); TREE_TYPE (si->nonzero_chars),
si->nonzero_chars, tem);
si->full_string_p = origsi->full_string_p;
si->endptr = NULL_TREE; si->endptr = NULL_TREE;
si->dont_invalidate = true; si->dont_invalidate = true;
...@@ -1020,11 +1074,8 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat) ...@@ -1020,11 +1074,8 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
} }
} }
if (!is_strcat) if (!is_strcat && !zero_length_string_p (si))
{ return;
if (si->length == NULL_TREE || !integer_zerop (si->length))
return;
}
if (is_gimple_assign (last.stmt)) if (is_gimple_assign (last.stmt))
{ {
...@@ -1138,12 +1189,13 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) ...@@ -1138,12 +1189,13 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
} }
if (si != NULL if (si != NULL
&& TREE_CODE (si->length) != SSA_NAME && TREE_CODE (si->nonzero_chars) != SSA_NAME
&& TREE_CODE (si->length) != INTEGER_CST && TREE_CODE (si->nonzero_chars) != INTEGER_CST
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs)) && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
{ {
si = unshare_strinfo (si); si = unshare_strinfo (si);
si->length = lhs; si->nonzero_chars = lhs;
gcc_assert (si->full_string_p);
} }
return; return;
} }
...@@ -1152,11 +1204,25 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) ...@@ -1152,11 +1204,25 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
return; return;
if (idx == 0) if (idx == 0)
idx = new_stridx (src); idx = new_stridx (src);
else if (get_strinfo (idx) != NULL) else
return; {
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) if (idx)
{ {
strinfo *si = new_strinfo (src, idx, lhs); strinfo *si = new_strinfo (src, idx, lhs, true);
set_strinfo (idx, si); set_strinfo (idx, si);
find_equal_ptrs (src, idx); find_equal_ptrs (src, idx);
} }
...@@ -1260,7 +1326,7 @@ handle_builtin_strchr (gimple_stmt_iterator *gsi) ...@@ -1260,7 +1326,7 @@ handle_builtin_strchr (gimple_stmt_iterator *gsi)
tree srcu = fold_convert_loc (loc, size_type_node, src); tree srcu = fold_convert_loc (loc, size_type_node, src);
tree length = fold_build2_loc (loc, MINUS_EXPR, tree length = fold_build2_loc (loc, MINUS_EXPR,
size_type_node, lhsu, srcu); size_type_node, lhsu, srcu);
strinfo *si = new_strinfo (src, idx, length); strinfo *si = new_strinfo (src, idx, length, true);
si->endptr = lhs; si->endptr = lhs;
set_strinfo (idx, si); set_strinfo (idx, si);
find_equal_ptrs (src, idx); find_equal_ptrs (src, idx);
...@@ -1349,9 +1415,10 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -1349,9 +1415,10 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
} }
if (olddsi != NULL) if (olddsi != NULL)
{ {
oldlen = olddsi->length; oldlen = olddsi->nonzero_chars;
dsi = unshare_strinfo (olddsi); 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 /* Break the chain, so adjust_related_strinfo on later pointers in
the chain won't adjust this one anymore. */ the chain won't adjust this one anymore. */
dsi->next = 0; dsi->next = 0;
...@@ -1360,14 +1427,14 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -1360,14 +1427,14 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
} }
else else
{ {
dsi = new_strinfo (dst, didx, srclen); dsi = new_strinfo (dst, didx, srclen, srclen != NULL_TREE);
set_strinfo (didx, dsi); set_strinfo (didx, dsi);
find_equal_ptrs (dst, didx); find_equal_ptrs (dst, didx);
} }
dsi->writable = true; dsi->writable = true;
dsi->dont_invalidate = true; dsi->dont_invalidate = true;
if (dsi->length == NULL_TREE) if (dsi->nonzero_chars == NULL_TREE)
{ {
strinfo *chainsi; strinfo *chainsi;
...@@ -1388,7 +1455,8 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -1388,7 +1455,8 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
invalidated. */ invalidated. */
chainsi = unshare_strinfo (chainsi); chainsi = unshare_strinfo (chainsi);
chainsi->stmt = stmt; chainsi->stmt = stmt;
chainsi->length = NULL_TREE; chainsi->nonzero_chars = NULL_TREE;
chainsi->full_string_p = false;
chainsi->endptr = NULL_TREE; chainsi->endptr = NULL_TREE;
chainsi->dont_invalidate = true; chainsi->dont_invalidate = true;
} }
...@@ -1556,31 +1624,61 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -1556,31 +1624,61 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
&& !integer_zerop (len)) && !integer_zerop (len))
adjust_last_stmt (olddsi, stmt, false); adjust_last_stmt (olddsi, stmt, false);
bool full_string_p;
if (idx > 0) if (idx > 0)
{ {
gimple *def_stmt; 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); si = get_strinfo (idx);
if (si == NULL || si->length == NULL_TREE) if (si == NULL || si->nonzero_chars == 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)))
return; 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 else
{ {
si = NULL; si = NULL;
/* Handle memcpy (x, "abcd", 5) or /* Handle memcpy (x, "abcd", 5) or
memcpy (x, "abc\0uvw", 7). */ memcpy (x, "abc\0uvw", 7). */
if (!tree_fits_uhwi_p (len) if (!tree_fits_uhwi_p (len))
|| tree_to_uhwi (len) <= (unsigned HOST_WIDE_INT) ~idx)
return; 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) if (olddsi != NULL && TREE_CODE (len) == SSA_NAME)
...@@ -1592,16 +1690,13 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -1592,16 +1690,13 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
if (didx == 0) if (didx == 0)
return; return;
} }
if (si != NULL)
newlen = si->length;
else
newlen = build_int_cst (size_type_node, ~idx);
oldlen = NULL_TREE; oldlen = NULL_TREE;
if (olddsi != NULL) if (olddsi != NULL)
{ {
dsi = unshare_strinfo (olddsi); dsi = unshare_strinfo (olddsi);
oldlen = olddsi->length; oldlen = olddsi->nonzero_chars;
dsi->length = newlen; dsi->nonzero_chars = newlen;
dsi->full_string_p = full_string_p;
/* Break the chain, so adjust_related_strinfo on later pointers in /* Break the chain, so adjust_related_strinfo on later pointers in
the chain won't adjust this one anymore. */ the chain won't adjust this one anymore. */
dsi->next = 0; dsi->next = 0;
...@@ -1610,7 +1705,7 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -1610,7 +1705,7 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
} }
else else
{ {
dsi = new_strinfo (dst, didx, newlen); dsi = new_strinfo (dst, didx, newlen, full_string_p);
set_strinfo (didx, dsi); set_strinfo (didx, dsi);
find_equal_ptrs (dst, didx); find_equal_ptrs (dst, didx);
} }
...@@ -1623,12 +1718,11 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -1623,12 +1718,11 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
if (oldlen == NULL_TREE) if (oldlen == NULL_TREE)
; ;
else if (integer_zerop (oldlen)) else if (integer_zerop (oldlen))
adj = dsi->length; adj = newlen;
else if (TREE_CODE (oldlen) == INTEGER_CST else if (TREE_CODE (oldlen) == INTEGER_CST
|| TREE_CODE (dsi->length) == INTEGER_CST) || TREE_CODE (newlen) == INTEGER_CST)
adj = fold_build2_loc (loc, MINUS_EXPR, adj = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (newlen), newlen,
TREE_TYPE (dsi->length), dsi->length, fold_convert_loc (loc, TREE_TYPE (newlen),
fold_convert_loc (loc, TREE_TYPE (dsi->length),
oldlen)); oldlen));
if (adj != NULL_TREE) if (adj != NULL_TREE)
adjust_related_strinfos (loc, dsi, adj); adjust_related_strinfos (loc, dsi, adj);
...@@ -1640,27 +1734,30 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -1640,27 +1734,30 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
if (si != NULL) if (si != NULL)
si->dont_invalidate = true; si->dont_invalidate = true;
lhs = gimple_call_lhs (stmt); if (full_string_p)
switch (bcode)
{ {
case BUILT_IN_MEMCPY: lhs = gimple_call_lhs (stmt);
case BUILT_IN_MEMCPY_CHK: switch (bcode)
case BUILT_IN_MEMCPY_CHKP: {
case BUILT_IN_MEMCPY_CHK_CHKP: case BUILT_IN_MEMCPY:
/* Allow adjust_last_stmt to decrease this memcpy's size. */ case BUILT_IN_MEMCPY_CHK:
laststmt.stmt = stmt; case BUILT_IN_MEMCPY_CHKP:
laststmt.len = dsi->length; case BUILT_IN_MEMCPY_CHK_CHKP:
laststmt.stridx = dsi->idx; /* Allow adjust_last_stmt to decrease this memcpy's size. */
if (lhs) laststmt.stmt = stmt;
ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx; laststmt.len = dsi->nonzero_chars;
break; laststmt.stridx = dsi->idx;
case BUILT_IN_MEMPCPY: if (lhs)
case BUILT_IN_MEMPCPY_CHK: ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx;
case BUILT_IN_MEMPCPY_CHKP: break;
case BUILT_IN_MEMPCPY_CHK_CHKP: case BUILT_IN_MEMPCPY:
break; case BUILT_IN_MEMPCPY_CHK:
default: case BUILT_IN_MEMPCPY_CHKP:
gcc_unreachable (); 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) ...@@ -1709,14 +1806,15 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
} }
if (dsi == NULL) if (dsi == NULL)
{ {
dsi = new_strinfo (dst, didx, NULL_TREE); dsi = new_strinfo (dst, didx, NULL_TREE, false);
set_strinfo (didx, dsi); set_strinfo (didx, dsi);
find_equal_ptrs (dst, didx); find_equal_ptrs (dst, didx);
} }
else else
{ {
dsi = unshare_strinfo (dsi); dsi = unshare_strinfo (dsi);
dsi->length = NULL_TREE; dsi->nonzero_chars = NULL_TREE;
dsi->full_string_p = false;
dsi->next = 0; dsi->next = 0;
dsi->endptr = NULL_TREE; dsi->endptr = NULL_TREE;
} }
...@@ -1740,7 +1838,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -1740,7 +1838,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
} }
loc = gimple_location (stmt); loc = gimple_location (stmt);
dstlen = dsi->length; dstlen = dsi->nonzero_chars;
endptr = dsi->endptr; endptr = dsi->endptr;
dsi = unshare_strinfo (dsi); dsi = unshare_strinfo (dsi);
...@@ -1750,14 +1848,17 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -1750,14 +1848,17 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
if (srclen != NULL_TREE) if (srclen != NULL_TREE)
{ {
dsi->length = fold_build2_loc (loc, PLUS_EXPR, TREE_TYPE (dsi->length), dsi->nonzero_chars = fold_build2_loc (loc, PLUS_EXPR,
dsi->length, srclen); TREE_TYPE (dsi->nonzero_chars),
dsi->nonzero_chars, srclen);
gcc_assert (dsi->full_string_p);
adjust_related_strinfos (loc, dsi, srclen); adjust_related_strinfos (loc, dsi, srclen);
dsi->dont_invalidate = true; dsi->dont_invalidate = true;
} }
else else
{ {
dsi->length = NULL; dsi->nonzero_chars = NULL;
dsi->full_string_p = false;
if (lhs == NULL_TREE && builtin_decl_implicit_p (BUILT_IN_STPCPY)) if (lhs == NULL_TREE && builtin_decl_implicit_p (BUILT_IN_STPCPY))
dsi->dont_invalidate = true; dsi->dont_invalidate = true;
} }
...@@ -1890,7 +1991,7 @@ handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -1890,7 +1991,7 @@ handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi)
tree length = NULL_TREE; tree length = NULL_TREE;
if (bcode == BUILT_IN_CALLOC) if (bcode == BUILT_IN_CALLOC)
length = build_int_cst (size_type_node, 0); 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) if (bcode == BUILT_IN_CALLOC)
si->endptr = lhs; si->endptr = lhs;
set_strinfo (idx, si); set_strinfo (idx, si);
...@@ -1932,7 +2033,8 @@ handle_builtin_memset (gimple_stmt_iterator *gsi) ...@@ -1932,7 +2033,8 @@ handle_builtin_memset (gimple_stmt_iterator *gsi)
gimple_stmt_iterator gsi1 = gsi_for_stmt (stmt1); gimple_stmt_iterator gsi1 = gsi_for_stmt (stmt1);
update_gimple_call (&gsi1, builtin_decl_implicit (BUILT_IN_CALLOC), 2, update_gimple_call (&gsi1, builtin_decl_implicit (BUILT_IN_CALLOC), 2,
size, build_one_cst (size_type_node)); 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); si1->stmt = gsi_stmt (gsi1);
} }
else else
...@@ -2064,18 +2166,20 @@ handle_pointer_plus (gimple_stmt_iterator *gsi) ...@@ -2064,18 +2166,20 @@ handle_pointer_plus (gimple_stmt_iterator *gsi)
} }
si = get_strinfo (idx); si = get_strinfo (idx);
if (si == NULL || si->length == NULL_TREE) if (si == NULL || si->nonzero_chars == NULL_TREE)
return; return;
off = gimple_assign_rhs2 (stmt); off = gimple_assign_rhs2 (stmt);
zsi = NULL; 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); zsi = zero_length_string (lhs, si);
else if (TREE_CODE (off) == SSA_NAME) else if (TREE_CODE (off) == SSA_NAME)
{ {
gimple *def_stmt = SSA_NAME_DEF_STMT (off); gimple *def_stmt = SSA_NAME_DEF_STMT (off);
if (gimple_assign_single_p (def_stmt) 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); zsi = zero_length_string (lhs, si);
} }
if (zsi != NULL if (zsi != NULL
...@@ -2101,63 +2205,63 @@ handle_char_store (gimple_stmt_iterator *gsi) ...@@ -2101,63 +2205,63 @@ handle_char_store (gimple_stmt_iterator *gsi)
strinfo *si = NULL; strinfo *si = NULL;
gimple *stmt = gsi_stmt (*gsi); gimple *stmt = gsi_stmt (*gsi);
tree ssaname = NULL_TREE, lhs = gimple_assign_lhs (stmt); 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 if (TREE_CODE (lhs) == MEM_REF
&& TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME) && 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); /* Get the strinfo for the base, and use it if it starts with at
idx = get_stridx (ssaname); 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 else
idx = get_addr_stridx (lhs, NULL_TREE); {
idx = get_addr_stridx (lhs, NULL_TREE, &offset);
if (idx > 0)
si = get_strinfo (idx);
}
if (idx > 0) 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)
{ {
si = get_strinfo (idx); int cmp = compare_nonzero_chars (si, offset);
if (si != NULL && si->length != NULL_TREE && integer_zerop (si->length)) 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 unlink_stmt_vdef (stmt);
if we know it has been stored in the current function. */ release_defs (stmt);
if (!stmt_could_throw_p (stmt) && si->writable) gsi_remove (gsi, true);
{ return false;
unlink_stmt_vdef (stmt);
release_defs (stmt);
gsi_remove (gsi, true);
return false;
}
else
{
si->writable = true;
gsi_next (gsi);
return false;
}
} }
else else
/* Otherwise this statement overwrites the '\0' with {
something, if the previous stmt was a memcpy, si->writable = true;
its length may be decreased. */ gsi_next (gsi);
adjust_last_stmt (si, stmt, false); 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 and if we aren't storing '\0', we know that the length of the
string and any other zero terminated string in memory remains string and any other zero terminated string in memory remains
the same. In that case we move to the next gimple statement and the same. In that case we move to the next gimple statement and
...@@ -2177,35 +2281,74 @@ handle_char_store (gimple_stmt_iterator *gsi) ...@@ -2177,35 +2281,74 @@ handle_char_store (gimple_stmt_iterator *gsi)
bar (len, len2, len3, len4); bar (len, len2, len3, len4);
} }
*/ */
else if (si != NULL && si->length != NULL_TREE else if (storing_nonzero_p && cmp > 0)
&& TREE_CODE (si->length) == INTEGER_CST
&& integer_nonzerop (gimple_assign_rhs1 (stmt)))
{ {
gsi_next (gsi); gsi_next (gsi);
return false; 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) if (ssaname)
{ idx = new_stridx (ssaname);
si = zero_length_string (ssaname, NULL);
if (si != NULL)
si->dont_invalidate = true;
}
else else
idx = new_addr_stridx (lhs);
if (idx != 0)
{ {
int idx = new_addr_stridx (lhs); tree ptr = (ssaname ? ssaname : build_fold_addr_expr (lhs));
if (idx != 0) tree len = storing_nonzero_p ? size_one_node : size_zero_node;
{ si = new_strinfo (ptr, idx, len, storing_zero_p);
si = new_strinfo (build_fold_addr_expr (lhs), idx, set_strinfo (idx, si);
build_int_cst (size_type_node, 0)); if (storing_zero_p
set_strinfo (idx, si); && ssaname
si->dont_invalidate = true; && !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 else if (idx == 0
&& TREE_CODE (gimple_assign_rhs1 (stmt)) == STRING_CST && TREE_CODE (gimple_assign_rhs1 (stmt)) == STRING_CST
...@@ -2220,14 +2363,14 @@ handle_char_store (gimple_stmt_iterator *gsi) ...@@ -2220,14 +2363,14 @@ handle_char_store (gimple_stmt_iterator *gsi)
if (idx != 0) if (idx != 0)
{ {
si = new_strinfo (build_fold_addr_expr (lhs), idx, 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); set_strinfo (idx, si);
si->dont_invalidate = true; 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' /* Allow adjust_last_stmt to remove it if the stored '\0'
is immediately overwritten. */ is immediately overwritten. */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment