+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
+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
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. */
--- /dev/null
+/* 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" } } */
--- /dev/null
+/* 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" } } */
--- /dev/null
+/* 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);
+}
+
--- /dev/null
+/* 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" } } */
--- /dev/null
+/* 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" } } */
--- /dev/null
+/* 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" } } */
--- /dev/null
+/* 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" } } */
}
/* 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;
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;
}
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)
'*(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;
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
{
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
static bool
strlen_check_and_optimize_call (gimple_stmt_iterator *gsi,
+ bool *zero_write,
const vr_values *rvals)
{
gimple *stmt = gsi_stmt (*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:
{
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)
}
/* 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;
}
}
}
if (gimple_vdef (stmt))
- maybe_invalidate (stmt);
+ maybe_invalidate (stmt, zero_write);
return true;
}