From: msebor Date: Wed, 16 Oct 2019 19:24:36 +0000 (+0000) Subject: PR tree-optimization/83821 - local aggregate initialization defeats strlen optimization X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7c3dbd220797599172e5982eadce7fcfb860361c;p=thirdparty%2Fgcc.git 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. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@277080 138bc75d-0d04-0410-961f-82ee72b054a4 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 018a0a1832d6..b81945b6eb2c 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,12 @@ +2019-10-16 Martin Sebor + + 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 PR tree-optimization/91996 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 8816472a8a4b..0785e413e918 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,15 @@ +2019-10-16 Martin Sebor + + 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 PR tree-optimization/91996 diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-4.c b/gcc/testsuite/c-c++-common/Warray-bounds-4.c index 961107a34728..a09a08d658df 100644 --- a/gcc/testsuite/c-c++-common/Warray-bounds-4.c +++ b/gcc/testsuite/c-c++-common/Warray-bounds-4.c @@ -63,7 +63,7 @@ void test_strcpy_bounds_memarray_range (void) TM ("0", "", ma.a5 + i, ma.a5); TM ("01", "", 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__ /* Disabled for non-x86 targets due to bug 83462. */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-82.c b/gcc/testsuite/gcc.dg/strlenopt-82.c new file mode 100644 index 000000000000..8070f6ccf30c --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-82.c @@ -0,0 +1,224 @@ +/* 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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-83.c b/gcc/testsuite/gcc.dg/strlenopt-83.c new file mode 100644 index 000000000000..5baafea5c320 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-83.c @@ -0,0 +1,83 @@ +/* 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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-84.c b/gcc/testsuite/gcc.dg/strlenopt-84.c new file mode 100644 index 000000000000..d6102b6e6f57 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-84.c @@ -0,0 +1,135 @@ +/* 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); +} + diff --git a/gcc/testsuite/gcc.dg/strlenopt-85.c b/gcc/testsuite/gcc.dg/strlenopt-85.c new file mode 100644 index 000000000000..e049e0c30e0a --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-85.c @@ -0,0 +1,44 @@ +/* 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" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt-86.c b/gcc/testsuite/gcc.dg/strlenopt-86.c new file mode 100644 index 000000000000..3e86fa3c90ab --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-86.c @@ -0,0 +1,57 @@ +/* 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" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/calloc-4.c b/gcc/testsuite/gcc.dg/tree-ssa/calloc-4.c new file mode 100644 index 000000000000..b3a1d0c2c693 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/calloc-4.c @@ -0,0 +1,37 @@ +/* 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" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c b/gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c new file mode 100644 index 000000000000..3d3e5a145423 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/calloc-5.c @@ -0,0 +1,22 @@ +/* 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" } } */ diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index bd1eb0d7ab8a..ff9b92b5b75f 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -1094,11 +1094,16 @@ get_range_strlen_dynamic (tree src, c_strlen_data *pdata, } /* 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 -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; unsigned int i; bool nonempty = false; @@ -1109,18 +1114,59 @@ maybe_invalidate (gimple *stmt) if (!si->dont_invalidate) { ao_ref r; - /* Do not use si->nonzero_chars. */ - ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE); + tree size = 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 (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); free_strinfo (si); 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; nonempty = true; } + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, " %s() ==> %i\n", __func__, nonempty); + return nonempty; } @@ -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. */ static bool -handle_builtin_memset (gimple_stmt_iterator *gsi) +handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write) { gimple *stmt2 = gsi_stmt (*gsi); if (!integer_zerop (gimple_call_arg (stmt2, 1))) return false; + + /* Let the caller know the memset call cleared the destination. */ + *zero_write = true; + tree ptr = gimple_call_arg (stmt2, 0); int idx1 = get_stridx (ptr); if (idx1 <= 0) @@ -4223,7 +4273,7 @@ count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm, '*(int*)a = 12345'). Return true when handled. */ 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; strinfo *si = NULL; @@ -4250,7 +4300,10 @@ handle_store (gimple_stmt_iterator *gsi, const vr_values *rvals) if (offset == 0) ssaname = TREE_OPERAND (lhs, 0); else if (si == NULL || compare_nonzero_chars (si, offset, rvals) < 0) - return true; + { + *zero_write = initializer_zerop (rhs); + return true; + } } } else @@ -4285,6 +4338,7 @@ handle_store (gimple_stmt_iterator *gsi, const vr_values *rvals) { rhs_minlen = lenrange[0]; storing_nonzero_p = lenrange[1] > 0; + *zero_write = storing_all_zeros_p; /* Avoid issuing multiple warnings for the same LHS or statement. For example, -Warray-bounds may have already been issued for @@ -4649,6 +4703,7 @@ is_char_type (tree type) static bool strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, + bool *zero_write, const vr_values *rvals) { gimple *stmt = gsi_stmt (*gsi); @@ -4708,7 +4763,7 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi); break; case BUILT_IN_MEMSET: - if (handle_builtin_memset (gsi)) + if (handle_builtin_memset (gsi, zero_write)) return false; break; case BUILT_IN_MEMCMP: @@ -4855,9 +4910,13 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh, { 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 (!strlen_check_and_optimize_call (gsi, rvals)) + if (!strlen_check_and_optimize_call (gsi, &zero_write, rvals)) return false; } else if (!flag_optimize_strlen || !strlen_optimize) @@ -4907,7 +4966,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh, } /* 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; } } @@ -4920,7 +4979,7 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh, } if (gimple_vdef (stmt)) - maybe_invalidate (stmt); + maybe_invalidate (stmt, zero_write); return true; }