Commit 2fcb55d1 by Martin Sebor Committed by Martin Sebor

PR tree-optimization/83821 - local aggregate initialization defeats strlen optimization

gcc/ChangeLog:

	PR tree-optimization/83821
	* tree-ssa-strlen.c (maybe_invalidate): Add argument.  Consider
	the length of a string when available.
	(handle_builtin_memset) Add argument.
	(handle_store, strlen_check_and_optimize_call): Same.
	(check_and_optimize_stmt): Same.  Pass it to callees.

gcc/testsuite/ChangeLog:

	PR tree-optimization/83821
	* c-c++-common/Warray-bounds-4.c: Remove XFAIL.
	* gcc.dg/strlenopt-82.c: New test.
	* gcc.dg/strlenopt-83.c: Same.
	* gcc.dg/strlenopt-84.c: Same.
	* gcc.dg/strlenopt-85.c: Same.
	* gcc.dg/strlenopt-86.c: Same.
	* gcc.dg/tree-ssa/calloc-4.c: Same.
	* gcc.dg/tree-ssa/calloc-5.c: Same.

From-SVN: r277080
parent 27c14dbc
2019-10-16 Martin Sebor <msebor@redhat.com> 2019-10-16 Martin Sebor <msebor@redhat.com>
PR tree-optimization/83821
* tree-ssa-strlen.c (maybe_invalidate): Add argument. Consider
the length of a string when available.
(handle_builtin_memset) Add argument.
(handle_store, strlen_check_and_optimize_call): Same.
(check_and_optimize_stmt): Same. Pass it to callees.
2019-10-16 Martin Sebor <msebor@redhat.com>
PR tree-optimization/91996 PR tree-optimization/91996
* tree-ssa-strlen.c (maybe_warn_pointless_strcmp): Improve location * tree-ssa-strlen.c (maybe_warn_pointless_strcmp): Improve location
information. information.
......
2019-10-16 Martin Sebor <msebor@redhat.com> 2019-10-16 Martin Sebor <msebor@redhat.com>
PR tree-optimization/83821
* c-c++-common/Warray-bounds-4.c: Remove XFAIL.
* gcc.dg/strlenopt-82.c: New test.
* gcc.dg/strlenopt-83.c: Same.
* gcc.dg/strlenopt-84.c: Same.
* gcc.dg/strlenopt-85.c: Same.
* gcc.dg/strlenopt-86.c: Same.
* gcc.dg/tree-ssa/calloc-4.c: Same.
* gcc.dg/tree-ssa/calloc-5.c: Same.
2019-10-16 Martin Sebor <msebor@redhat.com>
PR tree-optimization/91996 PR tree-optimization/91996
* gcc.dg/strlenopt-80.c: New test. * gcc.dg/strlenopt-80.c: New test.
* gcc.dg/strlenopt-81.c: New test. * gcc.dg/strlenopt-81.c: New test.
......
...@@ -63,7 +63,7 @@ void test_strcpy_bounds_memarray_range (void) ...@@ -63,7 +63,7 @@ void test_strcpy_bounds_memarray_range (void)
TM ("0", "", ma.a5 + i, ma.a5); TM ("0", "", ma.a5 + i, ma.a5);
TM ("01", "", ma.a5 + i, ma.a5); TM ("01", "", ma.a5 + i, ma.a5);
TM ("012", "", ma.a5 + i, ma.a5); TM ("012", "", ma.a5 + i, ma.a5);
TM ("0123", "", ma.a5 + i, ma.a5); /* { dg-warning "offset 6 from the object at .ma. is out of the bounds of referenced subobject .a5. with type .char\\\[5]. at offset 0" "strcpy" { xfail *-*-* } } */ TM ("0123", "", ma.a5 + i, ma.a5); /* { dg-warning "offset 5 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char *\\\[5]. at offset 0" "strcpy" } */
#if __i386__ || __x86_64__ #if __i386__ || __x86_64__
/* Disabled for non-x86 targets due to bug 83462. */ /* Disabled for non-x86 targets due to bug 83462. */
......
/* PR tree-optimization/83821 - local aggregate initialization defeats
strlen optimization
Avoid exercising targets other than x86_64 in LP64 mode due to PR 83543
- strlen of a local array member not optimized on some targets
{ dg-do compile { target { { i?86-*-* x86_64-*-* } && lp64 } } }
{ dg-options "-O2 -Wall -fdump-tree-optimized" } */
#include "strlenopt.h"
#define CAT(x, y) x ## y
#define CONCAT(x, y) CAT (x, y)
#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
#define FAIL(name) do { \
extern void FAILNAME (name) (void); \
FAILNAME (name)(); \
} while (0)
/* Macro to emit a call to function named
call_in_true_branch_not_eliminated_on_line_NNN()
for each call that's expected to be eliminated. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that no such call appears in output. */
#define ELIM(expr) \
if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
/* Macro to emit a call to a function named
call_made_in_{true,false}_branch_on_line_NNN()
for each call that's expected to be retained. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that the expected number of both kinds of calls appears in output
(a pair for each line with the invocation of the KEEP() macro. */
#define KEEP(expr) \
if (expr) \
FAIL (made_in_true_branch); \
else \
FAIL (made_in_false_branch)
#define STR10 "0123456789"
#define STR20 STR10 STR10
#define STR30 STR20 STR10
#define STR40 STR20 STR20
void elim_char_array_init_consecutive (void)
{
char a[][10] = { "1", "12", "123", "1234", "12345", "12345" };
ELIM (strlen (a[0]) == 1);
ELIM (strlen (a[1]) == 2);
ELIM (strlen (a[2]) == 3);
ELIM (strlen (a[3]) == 4);
ELIM (strlen (a[4]) == 5);
}
void elim_char_array_cpy_consecutive (void)
{
char a[5][10];
strcpy (a[0], "12345");
strcpy (a[1], "1234");
strcpy (a[2], "123");
strcpy (a[3], "12");
strcpy (a[4], "1");
ELIM (strlen (a[0]) == 5);
ELIM (strlen (a[1]) == 4);
ELIM (strlen (a[2]) == 3);
ELIM (strlen (a[3]) == 2);
ELIM (strlen (a[4]) == 1);
}
void elim_clear_char_array_cpy_consecutive (void)
{
char a[5][10] = { };
strcpy (a[0], "12345");
strcpy (a[1], "1234");
strcpy (a[2], "123");
strcpy (a[3], "12");
strcpy (a[4], "1");
ELIM (strlen (a[0]) == 5);
ELIM (strlen (a[1]) == 4);
ELIM (strlen (a[2]) == 3);
ELIM (strlen (a[3]) == 2);
ELIM (strlen (a[4]) == 1);
}
struct Consec
{
char s1[sizeof STR40];
char s2[sizeof STR40];
const char *p1;
const char *p2;
};
void elim_struct_init_consecutive (void)
{
struct Consec a = { STR10, STR10, STR10, STR10 };
ELIM (strlen (a.s1) == sizeof STR10 - 1);
ELIM (strlen (a.s2) == sizeof STR10 - 1);
ELIM (strlen (a.p1) == sizeof STR10 - 1);
ELIM (strlen (a.p2) == sizeof STR10 - 1);
}
void elim_struct_array_init_consecutive (void)
{
struct Consec a[2] = {
{ STR10, STR20, STR30, STR40 },
{ STR40, STR30, STR20, STR10 }
};
ELIM (strlen (a[0].s1) == sizeof STR10 - 1);
ELIM (strlen (a[0].s2) == sizeof STR20 - 1);
ELIM (strlen (a[0].p1) == sizeof STR30 - 1);
ELIM (strlen (a[0].p2) == sizeof STR40 - 1);
ELIM (strlen (a[1].s1) == sizeof STR40 - 1);
ELIM (strlen (a[1].s2) == sizeof STR30 - 1);
ELIM (strlen (a[1].p1) == sizeof STR20 - 1);
ELIM (strlen (a[1].p2) == sizeof STR10 - 1);
}
struct NonConsec
{
char s1[sizeof STR40];
int i1;
char s2[sizeof STR40];
int i2;
const char *p1;
int i3;
const char *p2;
int i4;
};
void elim_struct_init_nonconsecutive (void)
{
struct NonConsec b = { STR10, 123, STR20, 456, b.s1, 789, b.s2, 123 };
ELIM (strlen (b.s1) == sizeof STR10 - 1);
ELIM (strlen (b.s2) == sizeof STR20 - 1);
ELIM (strlen (b.p1) == sizeof STR10 - 1);
ELIM (strlen (b.p2) == sizeof STR20 - 1);
}
void elim_struct_assign_tmp_nonconsecutive (void)
{
struct NonConsec b = { "a", 1, "b", 2, "c", 3, "d", 4 };
b = (struct NonConsec){ STR10, 123, STR20, 456, STR30, 789, STR40, 123 };
ELIM (strlen (b.s1) == sizeof STR10 - 1);
ELIM (strlen (b.s2) == sizeof STR20 - 1);
ELIM (strlen (b.p1) == sizeof STR30 - 1);
ELIM (strlen (b.p2) == sizeof STR40 - 1);
}
const struct NonConsec bcst = {
STR40, -1, STR30, -2, STR20, -3, STR10, -4
};
void elim_struct_assign_cst_nonconsecutive (void)
{
struct NonConsec b = { "a", 1, "b", 2, "c", 3, "d" };
b = bcst;
ELIM (strlen (b.s1) == sizeof STR40 - 1);
ELIM (strlen (b.s2) == sizeof STR30 - 1);
ELIM (strlen (b.p1) == sizeof STR20 - 1);
ELIM (strlen (b.p2) == sizeof STR10 - 1);
}
void elim_struct_copy_cst_nonconsecutive (void)
{
struct NonConsec b = { "a", 1, "b", 2, "c", 3, "d" };
memcpy (&b, &bcst, sizeof b);
/* ELIM (strlen (b.s1) == sizeof STR40 - 1);
ELIM (strlen (b.s2) == sizeof STR30 - 1); */
ELIM (strlen (b.p1) == sizeof STR20 - 1);
ELIM (strlen (b.p2) == sizeof STR10 - 1);
}
#line 1000
int sink (void*);
void keep_init_nonconsecutive (void)
{
struct NonConsec b = {
STR10, 123, STR20, 456, b.s1, 789, b.s2,
sink (&b)
};
KEEP (strlen (b.s1) == sizeof STR10 - 1);
KEEP (strlen (b.s2) == sizeof STR10 - 1);
KEEP (strlen (b.p1) == sizeof STR10 - 1);
KEEP (strlen (b.p2) == sizeof STR10 - 1);
}
void keep_assign_tmp_nonconsecutive (void)
{
struct NonConsec b = { "a", 1, "b", 2, "c", 3, "d", 4 };
b = (struct NonConsec){
STR10, 123, STR20, 456, STR30, 789, STR40,
sink (&b)
};
KEEP (strlen (b.s1) == sizeof STR10 - 1);
KEEP (strlen (b.s2) == sizeof STR20 - 1);
KEEP (strlen (b.p1) == sizeof STR30 - 1);
KEEP (strlen (b.p2) == sizeof STR40 - 1);
}
/* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } }
{ dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 8 "optimized" } }
{ dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 8 "optimized" } } */
/* PR tree-optimization/83821 - local aggregate initialization defeats
strlen optimization
{ dg-do compile }
{ dg-options "-O2 -Wall -fdump-tree-optimized" } */
#include "strlenopt.h"
char *p_p2, *p_p5, *p_p9, *p_p14;
unsigned n0, n1, n2, n3, n4;
static inline __attribute__ ((always_inline)) void
elim_strlen_of_consecutive_strcpy (char *p)
{
p_p2 = p + 2;
__builtin_strcpy (p_p2, "12");
p_p5 = p_p2 + 3;
__builtin_strcpy (p_p5, "124");
p_p9 = p_p5 + 4;
__builtin_strcpy (p_p9, "1245");
p_p14 = p_p9 + 5;
n0 = __builtin_strlen (p);
n1 = __builtin_strlen (p_p2);
n2 = __builtin_strlen (p_p5);
n3 = __builtin_strlen (p_p9);
/* The following isn't handled yet:
n4 = __builtin_strlen (p_p14); */
if (n0 || n1 != 2 || n2 != 3 || n3 != 4)
__builtin_abort ();
}
void elim_strlen_of_consecutive_strcpy_in_alloca (unsigned n)
{
/* Only known sizes are handled so far. */
n = 14;
char *p = __builtin_alloca (n);
*p = '\0';
elim_strlen_of_consecutive_strcpy (p);
}
void elim_strlen_of_consecutive_strcpy_in_vla (unsigned n)
{
/* Only known sizes are handled so far. */
n = 14;
char vla[n];
*vla = '\0';
elim_strlen_of_consecutive_strcpy (vla);
}
void elim_strlen_of_consecutive_strcpy_in_malloc (unsigned n)
{
char *p = __builtin_malloc (n);
*p = '\0';
elim_strlen_of_consecutive_strcpy (p);
}
void elim_strlen_of_consecutive_strcpy_in_calloc (unsigned n)
{
char *p = __builtin_calloc (n, 1);
/* Do not store into *P to verify that strlen knows it's zero. */
elim_strlen_of_consecutive_strcpy (p);
}
/* { dg-final { scan-tree-dump-not "abort" "optimized" } } */
/* PR tree-optimization/83821 - local aggregate initialization defeats
strlen optimization
Verify that stores that overwrite an interior nul are correctly
reflected in strlen results.
{ dg-do run }
{ dg-options "-O2 -Wall" } */
#define false (0 == 1)
#define true (0 == 0)
#define assert(e) \
((e) ? (void)0 : (__builtin_printf ("assertion failed on line %i\n", \
__LINE__), __builtin_abort ()))
#define ATTR(...) __attribute__ ((__VA_ARGS__))
static inline int ATTR (always_inline)
assign_and_get_length (char *p, _Bool clear)
{
p[0] = 'a';
if (clear)
p[1] = 0;
p[2] = 'c';
if (clear)
p[3] = 0;
p[1] = 'b';
return __builtin_strlen (p);
}
ATTR (noipa) void array_get_length (void)
{
char a[4];
unsigned n = assign_and_get_length (a, true);
assert (n == 3);
}
ATTR (noipa) void clear_array_get_length (void)
{
char a[4] = { };
unsigned n = assign_and_get_length (a, false);
assert (n == 3);
}
ATTR (noipa) void calloc_get_length (void)
{
char *p = __builtin_calloc (5, 1);
unsigned n = assign_and_get_length (p, false);
assert (n == 3);
}
ATTR (noipa) void malloc_get_length (void)
{
char *p = __builtin_malloc (5);
unsigned n = assign_and_get_length (p, true);
assert (n == 3);
}
ATTR (noipa) void vla_get_length (int n)
{
char a[n];
unsigned len = assign_and_get_length (a, true);
assert (len == 3);
}
static inline void ATTR (always_inline)
assign_and_test_length (char *p, _Bool clear)
{
p[0] = 'a';
if (clear)
p[1] = 0;
p[2] = 'c';
if (clear)
p[3] = 0;
unsigned n0 = __builtin_strlen (p);
p[1] = 'b';
unsigned n1 = __builtin_strlen (p);
assert (n0 != n1);
}
ATTR (noipa) void array_test_length (void)
{
char a[4];
assign_and_test_length (a, true);
}
ATTR (noipa) void clear_array_test_length (void)
{
char a[4] = { };
assign_and_test_length (a, false);
}
ATTR (noipa) void calloc_test_length (void)
{
char *p = __builtin_calloc (5, 1);
assign_and_test_length (p, false);
}
ATTR (noipa) void malloc_test_length (void)
{
char *p = __builtin_malloc (5);
assign_and_test_length (p, true);
}
ATTR (noipa) void vla_test_length (int n)
{
char a[n];
assign_and_test_length (a, true);
}
int main (void)
{
array_get_length ();
clear_array_get_length ();
calloc_get_length ();
malloc_get_length ();
vla_get_length (4);
array_test_length ();
clear_array_test_length ();
calloc_test_length ();
malloc_test_length ();
vla_test_length (4);
}
/* PR tree-optimization/83821 - local aggregate initialization defeats
strlen optimization
Verify that a strlen() call is eliminated for a pointer to a region
of memory allocated by calloc() even if one or more nul bytes are
written into it.
{ dg-do compile }
{ dg-options "-O2 -fdump-tree-optimized" } */
unsigned n0, n1;
void* elim_strlen_calloc_store_memset_1 (unsigned a, unsigned b)
{
char *p = __builtin_calloc (a, 1);
p[0] = '\0';
p[1] = '\0';
p[2] = '\0';
p[3] = '\0';
__builtin_memset (p, 0, b);
n0 = __builtin_strlen (p);
return p;
}
void* elim_strlen_calloc_store_memset_2 (unsigned a, unsigned b, unsigned c)
{
char *p = __builtin_calloc (a, 1);
p[1] = '\0';
__builtin_memset (p, 0, b);
n0 = __builtin_strlen (p);
p[3] = 0;
__builtin_memset (p, 0, c);
n1 = __builtin_strlen (p);
return p;
}
/* { dg-final { scan-tree-dump-not "__builtin_strlen" "optimized" } } */
/* PR tree-optimization/83821 - local aggregate initialization defeats
strlen optimization
Verify that a strlen() call is not eliminated for a pointer to a region
of memory allocated by calloc() if a byte is written into the region
that isn't known to be nul.
{ dg-do compile }
{ dg-options "-O2 -fdump-tree-optimized" } */
unsigned n0, n1;
void*
keep_strlen_calloc_store_cst_memset (unsigned a, unsigned b)
{
char *p = __builtin_calloc (a, 1);
p[1] = 'x';
__builtin_memset (p, 0, b);
n0 = __builtin_strlen (p);
return p;
}
void*
keep_strlen_calloc_store_var_memset (int x, unsigned a, unsigned b)
{
char *p = __builtin_calloc (a, 1);
p[1] = x;
__builtin_memset (p, 0, b);
n0 = __builtin_strlen (p);
return p;
}
void*
keep_strlen_calloc_store_memset_2 (int x, unsigned a, unsigned b, unsigned c)
{
char *p = __builtin_calloc (a, 1);
p[1] = x;
__builtin_memset (p, 0, b);
n0 = __builtin_strlen (p);
p[3] = x;
__builtin_memset (p, 0, c);
n1 = __builtin_strlen (p);
return p;
}
/* { dg-final { scan-tree-dump-times "__builtin_strlen" 4 "optimized" } } */
/* PR tree-optimization/83821 - local aggregate initialization defeats
strlen optimization
Verify that a memset() call to zero out a subregion of memory
allocated by calloc() is eliminated even if a zero byte is written
into it in between the two calls. See the calloc-2.c test that
verifies that the memset() calls isn't eliminated if the written
value is non-zero.
{ dg-do compile }
{ dg-options "-O2 -fdump-tree-optimized" } */
void* elim_calloc_store_memset_1 (unsigned a, unsigned b)
{
char *p = __builtin_calloc (a, 1);
p[1] = '\0';
__builtin_memset (p, 0, b); // should be eliminated
return p;
}
void* elim_calloc_store_memset_2 (unsigned a, unsigned b, unsigned c)
{
char *p = __builtin_calloc (a, 1);
p[1] = '\0';
__builtin_memset (p, 0, b); // should be eliminated
p[3] = '\0';
__builtin_memset (p, 0, c); // should also be eliminated
return p;
}
/* { dg-final { scan-tree-dump-not "malloc" "optimized" } }
{ dg-final { scan-tree-dump-times "_calloc \\\(" 2 "optimized" } }
{ dg-final { scan-tree-dump-not "_memset \\\(" "optimized" } } */
/* PR tree-optimization/83821 - local aggregate initialization defeats
strlen optimization
Verify that with DSE disabled, a memset() call to zero out a subregion
of memory allocated by calloc() is not eliminated after a non-zero byte
is written into it using memset() in between the two calls.
{ dg-do compile }
{ dg-options "-O2 -fno-tree-dse -fdump-tree-optimized" } */
char* keep_memset_calls (void)
{
char *p = __builtin_calloc (12, 1);
__builtin_memset (p + 5, 1, 2); /* dead store (not eliminated) */
__builtin_memset (p, 0, 12); /* must not be eliminated */
return p;
}
/* { dg-final { scan-tree-dump-not "malloc" "optimized" } }
{ dg-final { scan-tree-dump-times "_calloc \\\(" 1 "optimized" } }
{ dg-final { scan-tree-dump-times "_memset \\\(" 2 "optimized" } } */
...@@ -1094,11 +1094,16 @@ get_range_strlen_dynamic (tree src, c_strlen_data *pdata, ...@@ -1094,11 +1094,16 @@ get_range_strlen_dynamic (tree src, c_strlen_data *pdata,
} }
/* Invalidate string length information for strings whose length /* Invalidate string length information for strings whose length
might change due to stores in stmt. */ might change due to stores in stmt, except those marked DON'T
INVALIDATE. For string-modifying statements, ZERO_WRITE is
set when the statement wrote only zeros. */
static bool static bool
maybe_invalidate (gimple *stmt) maybe_invalidate (gimple *stmt, bool zero_write = false)
{ {
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " %s()\n", __func__);
strinfo *si; strinfo *si;
unsigned int i; unsigned int i;
bool nonempty = false; bool nonempty = false;
...@@ -1109,18 +1114,59 @@ maybe_invalidate (gimple *stmt) ...@@ -1109,18 +1114,59 @@ maybe_invalidate (gimple *stmt)
if (!si->dont_invalidate) if (!si->dont_invalidate)
{ {
ao_ref r; ao_ref r;
/* Do not use si->nonzero_chars. */ tree size = NULL_TREE;
ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE); if (si->nonzero_chars)
{
/* Include the terminating nul in the size of the string
to consider when determining possible clobber. */
tree type = TREE_TYPE (si->nonzero_chars);
size = fold_build2 (PLUS_EXPR, type, si->nonzero_chars,
build_int_cst (type, 1));
}
ao_ref_init_from_ptr_and_size (&r, si->ptr, size);
if (stmt_may_clobber_ref_p_1 (stmt, &r)) if (stmt_may_clobber_ref_p_1 (stmt, &r))
{ {
if (dump_file && (dump_flags & TDF_DETAILS))
{
if (size && tree_fits_uhwi_p (size))
fprintf (dump_file,
" statement may clobber string %zu long\n",
tree_to_uhwi (size));
else
fprintf (dump_file,
" statement may clobber string\n");
}
set_strinfo (i, NULL); set_strinfo (i, NULL);
free_strinfo (si); free_strinfo (si);
continue; continue;
} }
if (size
&& !zero_write
&& si->stmt
&& is_gimple_call (si->stmt)
&& (DECL_FUNCTION_CODE (gimple_call_fndecl (si->stmt))
== BUILT_IN_CALLOC))
{
/* If the clobber test above considered the length of
the string (including the nul), then for (potentially)
non-zero writes that might modify storage allocated by
calloc consider the whole object and if it might be
clobbered by the statement reset the allocation
statement. */
ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE);
if (stmt_may_clobber_ref_p_1 (stmt, &r))
si->stmt = NULL;
}
} }
si->dont_invalidate = false; si->dont_invalidate = false;
nonempty = true; nonempty = true;
} }
if (dump_file && (dump_flags & TDF_DETAILS))
fprintf (dump_file, " %s() ==> %i\n", __func__, nonempty);
return nonempty; return nonempty;
} }
...@@ -3213,11 +3259,15 @@ handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi) ...@@ -3213,11 +3259,15 @@ handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi)
return true when the call is transfomred, false otherwise. */ return true when the call is transfomred, false otherwise. */
static bool static bool
handle_builtin_memset (gimple_stmt_iterator *gsi) handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write)
{ {
gimple *stmt2 = gsi_stmt (*gsi); gimple *stmt2 = gsi_stmt (*gsi);
if (!integer_zerop (gimple_call_arg (stmt2, 1))) if (!integer_zerop (gimple_call_arg (stmt2, 1)))
return false; return false;
/* Let the caller know the memset call cleared the destination. */
*zero_write = true;
tree ptr = gimple_call_arg (stmt2, 0); tree ptr = gimple_call_arg (stmt2, 0);
int idx1 = get_stridx (ptr); int idx1 = get_stridx (ptr);
if (idx1 <= 0) if (idx1 <= 0)
...@@ -4223,7 +4273,7 @@ count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm, ...@@ -4223,7 +4273,7 @@ count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
'*(int*)a = 12345'). Return true when handled. */ '*(int*)a = 12345'). Return true when handled. */
static bool static bool
handle_store (gimple_stmt_iterator *gsi, const vr_values *rvals) handle_store (gimple_stmt_iterator *gsi, bool *zero_write, const vr_values *rvals)
{ {
int idx = -1; int idx = -1;
strinfo *si = NULL; strinfo *si = NULL;
...@@ -4250,7 +4300,10 @@ handle_store (gimple_stmt_iterator *gsi, const vr_values *rvals) ...@@ -4250,7 +4300,10 @@ handle_store (gimple_stmt_iterator *gsi, const vr_values *rvals)
if (offset == 0) if (offset == 0)
ssaname = TREE_OPERAND (lhs, 0); ssaname = TREE_OPERAND (lhs, 0);
else if (si == NULL || compare_nonzero_chars (si, offset, rvals) < 0) else if (si == NULL || compare_nonzero_chars (si, offset, rvals) < 0)
return true; {
*zero_write = initializer_zerop (rhs);
return true;
}
} }
} }
else else
...@@ -4285,6 +4338,7 @@ handle_store (gimple_stmt_iterator *gsi, const vr_values *rvals) ...@@ -4285,6 +4338,7 @@ handle_store (gimple_stmt_iterator *gsi, const vr_values *rvals)
{ {
rhs_minlen = lenrange[0]; rhs_minlen = lenrange[0];
storing_nonzero_p = lenrange[1] > 0; storing_nonzero_p = lenrange[1] > 0;
*zero_write = storing_all_zeros_p;
/* Avoid issuing multiple warnings for the same LHS or statement. /* Avoid issuing multiple warnings for the same LHS or statement.
For example, -Warray-bounds may have already been issued for For example, -Warray-bounds may have already been issued for
...@@ -4649,6 +4703,7 @@ is_char_type (tree type) ...@@ -4649,6 +4703,7 @@ is_char_type (tree type)
static bool static bool
strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, strlen_check_and_optimize_call (gimple_stmt_iterator *gsi,
bool *zero_write,
const vr_values *rvals) const vr_values *rvals)
{ {
gimple *stmt = gsi_stmt (*gsi); gimple *stmt = gsi_stmt (*gsi);
...@@ -4708,7 +4763,7 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, ...@@ -4708,7 +4763,7 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi,
handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi); handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi);
break; break;
case BUILT_IN_MEMSET: case BUILT_IN_MEMSET:
if (handle_builtin_memset (gsi)) if (handle_builtin_memset (gsi, zero_write))
return false; return false;
break; break;
case BUILT_IN_MEMCMP: case BUILT_IN_MEMCMP:
...@@ -4855,9 +4910,13 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh, ...@@ -4855,9 +4910,13 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
{ {
gimple *stmt = gsi_stmt (*gsi); gimple *stmt = gsi_stmt (*gsi);
/* For statements that modify a string, set to true if the write
is only zeros. */
bool zero_write = false;
if (is_gimple_call (stmt)) if (is_gimple_call (stmt))
{ {
if (!strlen_check_and_optimize_call (gsi, rvals)) if (!strlen_check_and_optimize_call (gsi, &zero_write, rvals))
return false; return false;
} }
else if (!flag_optimize_strlen || !strlen_optimize) else if (!flag_optimize_strlen || !strlen_optimize)
...@@ -4907,7 +4966,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh, ...@@ -4907,7 +4966,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
} }
/* Handle a single or multibyte assignment. */ /* Handle a single or multibyte assignment. */
if (is_char_store && !handle_store (gsi, rvals)) if (is_char_store && !handle_store (gsi, &zero_write, rvals))
return false; return false;
} }
} }
...@@ -4920,7 +4979,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh, ...@@ -4920,7 +4979,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
} }
if (gimple_vdef (stmt)) if (gimple_vdef (stmt))
maybe_invalidate (stmt); maybe_invalidate (stmt, zero_write);
return true; return true;
} }
......
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