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>
* 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
* tree-ssa-strlen.c (strinfo): Document that "stmt" is also used
for malloc and calloc. Document the new invariant that all related
......
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
* gcc.dg/strlenopt-31.c: New test.
* 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" } } */
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