PR tree-optimization/86043 - strlen after memcpy partially overwriting a string not optimized
PR tree-optimization/86042 - missing strlen optimization after second strcpy
gcc/ChangeLog:
PR tree-optimization/86043
PR tree-optimization/86042
* tree-ssa-strlen.c (handle_builtin_memcpy): Handle strict overlaps.
(get_string_cst_length): Rename...
(get_min_string_length): ...to this. Add argument.
(handle_char_store): Extend to handle multi-character stores by
MEM_REF.
* tree.c (initializer_zerop): Use new argument. Handle MEM_REF.
* tree.h (initializer_zerop): Add argument.
gcc/testsuite/ChangeLog:
PR tree-optimization/86043
PR tree-optimization/86042
* gcc/testsuite/gcc.dg/attr-nonstring-2.c: Xfail test cases due to
pr86688.
* gcc.dg/strlenopt-44.c: New test.
From-SVN: r263018
+2018-07-26 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/86043
+ PR tree-optimization/86042
+ * tree-ssa-strlen.c (handle_builtin_memcpy): Handle strict overlaps.
+ (get_string_cst_length): Rename...
+ (get_min_string_length): ...to this. Add argument.
+ (handle_char_store): Extend to handle multi-character stores by
+ MEM_REF.
+ * tree.c (initializer_zerop): Use new argument. Handle MEM_REF.
+ * tree.h (initializer_zerop): Add argument.
+
2018-07-26 Jakub Jelinek <jakub@redhat.com>
PR middle-end/86660
+2018-07-26 Martin Sebor <msebor@redhat.com>
+
+ PR tree-optimization/86043
+ PR tree-optimization/86042
+ * gcc/testsuite/gcc.dg/attr-nonstring-2.c: Xfail test cases due to
+ pr86688.
+ * gcc.dg/strlenopt-44.c: New test.
+
2018-07-26 Martin Liska <mliska@suse.cz>
PR gcov-profile/86536
T (3, "12", 3, 1);
T (3, "12", 3, 9);
T (3, "123", 3, 1);
- T (3, "123", 3, 4); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" } */
- T (3, "123", 3, 9); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 9" } */
+ T (3, "123", 3, 4); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" "bug 86688" { xfail *-*-* } } */
+ T (3, "123", 3, 9); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 9" "bug 86688" { xfail *-*-* } } */
T (5, "1", 2, 1);
T (5, "1", 2, 2);
{
T (3, "1", 2, UR (0, 1));
T (3, "1", 2, UR (3, 9));
- T (3, "123", 3, UR (4, 5)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[4, 5]" } */
- T (3, "123", 3, UR (5, 9)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[5, 9]" } */
+ T (3, "123", 3, UR (4, 5)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[4, 5]" "bug 86688" { xfail *-*-* } } */
+ T (3, "123", 3, UR (5, 9)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[5, 9]" "bug 86688" { xfail *-*-* } } */
}
--- /dev/null
+/* PR tree-optimization/86042 - missing strlen optimization after second strcpy
+ { dg-do compile }
+ { 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
+
+void elim_after_duplicate_strcpy (void)
+{
+#define T(N, assign, off, str, r0, r1) \
+ do { \
+ char a[N]; \
+ strcpy (a, assign); \
+ unsigned n0 = strlen (a); \
+ strcpy (a + off, str); \
+ unsigned n1 = strlen (a); \
+ ELIM (n0 == r0 && n1 == r1); \
+ } while (0)
+
+ T (2, "", 0, "1", 0, 1);
+
+ T (2, "1", 0, "2", 1, 1);
+ T (2, "1", 1, "", 1, 1);
+
+ T (3, "\0", 0, "1", 0, 1);
+ T (3, "1", 1, "2", 1, 2);
+
+ T (3, "12", 0, "23", 2, 2);
+ T (3, "12", 1, "3", 2, 2);
+ T (3, "12", 2, "", 2, 2);
+
+ T (4, "1", 1, "23", 1, 3);
+ T (4, "12", 1, "23", 2, 3);
+
+ T (4, "123", 0, "234", 3, 3);
+ T (4, "123", 1, "34", 3, 3);
+ T (4, "123", 2, "4", 3, 3);
+ T (4, "123", 3, "", 3, 3);
+
+ T (5, "1234", 0, "1", 4, 1);
+ T (5, "1234", 0, "12", 4, 2);
+ T (5, "1234", 0, "123", 4, 3);
+ T (5, "1234", 0, "1234", 4, 4);
+
+ T (5, "123", 1, "234", 3, 4);
+ T (6, "123", 2, "234", 3, 5);
+}
+
+void elim_after_init_memcpy (void)
+{
+#undef T
+#define T(init, off, str, n, res) \
+ do { \
+ char a[] = init; \
+ memcpy (a + off, str, n); \
+ ELIM (strlen (a) == res); \
+ } while (0)
+
+ T ("\0", 0, "1", 2, 1);
+ T ("\0\0", 0, "12", 3, 2);
+
+#define INIT { '1', '2', '3', '4' }
+ T (INIT, 0, "", 1, 0);
+ T (INIT, 0, "1", 2, 1);
+ T (INIT, 0, "12", 3, 2);
+ T (INIT, 0, "123", 4, 3);
+
+ T ("1234", 0, "2", 1, 4);
+ T ("1234", 0, "23", 2, 4);
+ T ("1234", 0, "234", 3, 4);
+ T ("1234", 0, "2345", 4, 4);
+ T ("1234", 0, "2345", 5, 4);
+
+ T ("1234", 1, "2", 1, 4);
+ T ("1234", 1, "23", 2, 4);
+ T ("1234", 1, "234", 3, 4);
+ T ("1234", 1, "234", 4, 4);
+
+ T ("1234", 2, "3", 1, 4);
+ T ("1234", 2, "3", 2, 3);
+ T ("1234", 2, "34", 2, 4);
+ T ("1234", 2, "34", 3, 4);
+
+ T ("12\00034", 0, "1", 1, 2);
+ T ("12\00034", 0, "1", 2, 1);
+
+ T ("AB\000CD", 0, "ab", 2, 2);
+ T ("AB\000CD", 0, "ab", 3, 2);
+ T ("AB\000CD", 0, "ab\000c", 4, 2);
+}
+
+/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } }
+ { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated" 0 "optimized" } } */
full_string_p = clen > nonzero_chars;
}
+ if (!full_string_p
+ && olddsi
+ && olddsi->nonzero_chars
+ && TREE_CODE (olddsi->nonzero_chars) == INTEGER_CST
+ && tree_int_cst_le (newlen, olddsi->nonzero_chars))
+ {
+ /* The SRC substring being written strictly overlaps
+ a subsequence of the existing string OLDDSI. */
+ newlen = olddsi->nonzero_chars;
+ full_string_p = olddsi->full_string_p;
+ }
+
if (olddsi != NULL && TREE_CODE (len) == SSA_NAME)
adjust_last_stmt (olddsi, stmt, false);
}
/* If RHS, either directly or indirectly, refers to a string of constant
- length, return it. Otherwise return a negative value. */
+ length, return the length. Otherwise, if it refers to a character
+ constant, return 1 if the constant is non-zero and 0 if it is nul.
+ Otherwise, return a negative value. */
static HOST_WIDE_INT
-get_string_cst_length (tree rhs)
+get_min_string_length (tree rhs, bool *full_string_p)
{
+ if (TREE_CODE (TREE_TYPE (rhs)) == INTEGER_TYPE)
+ {
+ if (tree_expr_nonzero_p (rhs))
+ {
+ *full_string_p = false;
+ return 1;
+ }
+
+ *full_string_p = true;
+ return 0;
+ }
+
if (TREE_CODE (rhs) == MEM_REF
&& integer_zerop (TREE_OPERAND (rhs, 1)))
{
{
strinfo *si = get_strinfo (idx);
if (si
- && si->full_string_p
&& tree_fits_shwi_p (si->nonzero_chars))
- return tree_to_shwi (si->nonzero_chars);
+ {
+ *full_string_p = si->full_string_p;
+ return tree_to_shwi (si->nonzero_chars);
+ }
}
}
}
rhs = DECL_INITIAL (rhs);
if (rhs && TREE_CODE (rhs) == STRING_CST)
- return strlen (TREE_STRING_POINTER (rhs));
+ {
+ *full_string_p = true;
+ return strlen (TREE_STRING_POINTER (rhs));
+ }
return -1;
}
-/* Handle a single character store. */
+/* Handle a single or multiple character store either by single
+ character assignment or by multi-character assignment from
+ MEM_REF. */
static bool
handle_char_store (gimple_stmt_iterator *gsi)
si = get_strinfo (idx);
}
- bool storing_zero_p = initializer_zerop (rhs);
- bool storing_nonzero_p = !storing_zero_p && tree_expr_nonzero_p (rhs);
+ /* STORING_NONZERO_P is true iff not all stored characters are zero.
+ STORING_ALL_ZEROS_P is true iff all stored characters are zero.
+ Both are false when it's impossible to determine which is true. */
+ bool storing_nonzero_p;
+ bool storing_all_zeros_p = initializer_zerop (rhs, &storing_nonzero_p);
+ if (!storing_nonzero_p)
+ storing_nonzero_p = tree_expr_nonzero_p (rhs);
+ bool full_string_p = storing_all_zeros_p;
+
/* Set to the length of the string being assigned if known. */
HOST_WIDE_INT rhslen;
{
int cmp = compare_nonzero_chars (si, offset);
gcc_assert (offset == 0 || cmp >= 0);
- if (storing_zero_p && cmp == 0 && si->full_string_p)
+ if (storing_all_zeros_p && cmp == 0 && si->full_string_p)
{
/* When overwriting a '\0' with a '\0', the store can be removed
if we know it has been stored in the current function. */
gsi_next (gsi);
return false;
}
- else if (storing_zero_p || storing_nonzero_p || (offset != 0 && cmp > 0))
+ else if (storing_all_zeros_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_NONZERO_P, we know that the string will start
+ with at least OFFSET + 1 nonzero characters. If storing
+ a single character, set si->NONZERO_CHARS to the result.
+ If storing multiple characters, try to determine the number
+ of leading non-zero characters and set si->NONZERO_CHARS to
+ the result instead.
- When storing_zero_p, we know that the string is now OFFSET
- characters long.
+ When STORING_ALL_ZEROS_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. */
+ bool full_string_p = storing_all_zeros_p;
+ HOST_WIDE_INT len = (storing_nonzero_p
+ ? get_min_string_length (rhs, &full_string_p)
+ : 1);
+
location_t loc = gimple_location (stmt);
tree oldlen = si->nonzero_chars;
if (cmp == 0 && si->full_string_p)
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);
+ {
+ gcc_assert (len >= 0);
+ si->nonzero_chars = build_int_cst (size_type_node, offset + len);
+ }
else
si->nonzero_chars = build_int_cst (size_type_node, offset);
- si->full_string_p = storing_zero_p;
- if (storing_zero_p
+ si->full_string_p = full_string_p;
+ if (storing_all_zeros_p
&& ssaname
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
si->endptr = ssaname;
si->prev = 0;
}
}
- else if (idx == 0 && (storing_zero_p || storing_nonzero_p))
+ else if (idx == 0 && (storing_all_zeros_p || storing_nonzero_p))
{
if (ssaname)
idx = new_stridx (ssaname);
if (idx != 0)
{
tree ptr = (ssaname ? ssaname : build_fold_addr_expr (lhs));
- tree len = storing_nonzero_p ? size_one_node : size_zero_node;
- si = new_strinfo (ptr, idx, len, storing_zero_p);
+ HOST_WIDE_INT slen = (storing_all_zeros_p
+ ? 0
+ : (storing_nonzero_p
+ ? get_min_string_length (rhs, &full_string_p)
+ : -1));
+ tree len = (slen <= 0
+ ? size_zero_node
+ : build_int_cst (size_type_node, slen));
+ si = new_strinfo (ptr, idx, len, slen >= 0 && full_string_p);
set_strinfo (idx, si);
- if (storing_zero_p
+ if (storing_all_zeros_p
&& ssaname
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
si->endptr = ssaname;
}
}
else if (idx == 0
- && (rhslen = get_string_cst_length (gimple_assign_rhs1 (stmt))) >= 0
+ && (rhslen = get_min_string_length (rhs, &full_string_p)) >= 0
&& ssaname == NULL_TREE
&& TREE_CODE (TREE_TYPE (lhs)) == ARRAY_TYPE)
{
if (idx != 0)
{
si = new_strinfo (build_fold_addr_expr (lhs), idx,
- build_int_cst (size_type_node, rhslen), true);
+ build_int_cst (size_type_node, rhslen),
+ full_string_p);
set_strinfo (idx, si);
si->dont_invalidate = true;
}
}
}
- if (si != NULL && offset == 0 && storing_zero_p)
+ if (si != NULL && offset == 0 && storing_all_zeros_p)
{
/* Allow adjust_last_stmt to remove it if the stored '\0'
is immediately overwritten. */
}
/* Given an initializer INIT, return TRUE if INIT is zero or some
- aggregate of zeros. Otherwise return FALSE. */
+ aggregate of zeros. Otherwise return FALSE. If NONZERO is not
+ null, set *NONZERO if and only if INIT is known not to be all
+ zeros. The combination of return value of false and *NONZERO
+ false implies that INIT may but need not be all zeros. Other
+ combinations indicate definitive answers. */
+
bool
-initializer_zerop (const_tree init)
+initializer_zerop (const_tree init, bool *nonzero /* = NULL */)
{
- tree elt;
+ bool dummy;
+ if (!nonzero)
+ nonzero = &dummy;
+
+ /* Conservatively clear NONZERO and set it only if INIT is definitely
+ not all zero. */
+ *nonzero = false;
STRIP_NOPS (init);
+ unsigned HOST_WIDE_INT off = 0;
+
switch (TREE_CODE (init))
{
case INTEGER_CST:
- return integer_zerop (init);
+ if (integer_zerop (init))
+ return true;
+
+ *nonzero = true;
+ return false;
case REAL_CST:
/* ??? Note that this is not correct for C4X float formats. There,
a bit pattern of all zeros is 1.0; 0.0 is encoded with the most
negative exponent. */
- return real_zerop (init)
- && ! REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (init));
+ if (real_zerop (init)
+ && !REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (init)))
+ return true;
+
+ *nonzero = true;
+ return false;
case FIXED_CST:
- return fixed_zerop (init);
+ if (fixed_zerop (init))
+ return true;
+
+ *nonzero = true;
+ return false;
case COMPLEX_CST:
- return integer_zerop (init)
- || (real_zerop (init)
- && ! REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (TREE_REALPART (init)))
- && ! REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (TREE_IMAGPART (init))));
+ if (integer_zerop (init)
+ || (real_zerop (init)
+ && !REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (TREE_REALPART (init)))
+ && !REAL_VALUE_MINUS_ZERO (TREE_REAL_CST (TREE_IMAGPART (init)))))
+ return true;
+
+ *nonzero = true;
+ return false;
case VECTOR_CST:
- return (VECTOR_CST_NPATTERNS (init) == 1
- && VECTOR_CST_DUPLICATE_P (init)
- && initializer_zerop (VECTOR_CST_ENCODED_ELT (init, 0)));
+ if (VECTOR_CST_NPATTERNS (init) == 1
+ && VECTOR_CST_DUPLICATE_P (init)
+ && initializer_zerop (VECTOR_CST_ENCODED_ELT (init, 0)))
+ return true;
+
+ *nonzero = true;
+ return false;
case CONSTRUCTOR:
{
- unsigned HOST_WIDE_INT idx;
-
if (TREE_CLOBBER_P (init))
return false;
+
+ unsigned HOST_WIDE_INT idx;
+ tree elt;
+
FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (init), idx, elt)
- if (!initializer_zerop (elt))
+ if (!initializer_zerop (elt, nonzero))
return false;
+
return true;
}
+ case MEM_REF:
+ {
+ tree arg = TREE_OPERAND (init, 0);
+ if (TREE_CODE (arg) != ADDR_EXPR)
+ return false;
+ tree offset = TREE_OPERAND (init, 1);
+ if (TREE_CODE (offset) != INTEGER_CST
+ || !tree_fits_uhwi_p (offset))
+ return false;
+ off = tree_to_uhwi (offset);
+ if (INT_MAX < off)
+ return false;
+ arg = TREE_OPERAND (arg, 0);
+ if (TREE_CODE (arg) != STRING_CST)
+ return false;
+ init = arg;
+ }
+ /* Fall through. */
+
case STRING_CST:
{
- int i;
+ gcc_assert (off <= INT_MAX);
+
+ int i = off;
+ int n = TREE_STRING_LENGTH (init);
+ if (n <= i)
+ return false;
/* We need to loop through all elements to handle cases like
"\0" and "\0foobar". */
- for (i = 0; i < TREE_STRING_LENGTH (init); ++i)
+ for (i = 0; i < n; ++i)
if (TREE_STRING_POINTER (init)[i] != '\0')
- return false;
+ {
+ *nonzero = true;
+ return false;
+ }
return true;
}
extern tree first_field (const_tree);
/* Given an initializer INIT, return TRUE if INIT is zero or some
- aggregate of zeros. Otherwise return FALSE. */
+ aggregate of zeros. Otherwise return FALSE. If NONZERO is not
+ null, set *NONZERO if and only if INIT is known not to be all
+ zeros. The combination of return value of false and *NONZERO
+ false implies that INIT may but need not be all zeros. Other
+ combinations indicate definitive answers. */
-extern bool initializer_zerop (const_tree);
+extern bool initializer_zerop (const_tree, bool * = NULL);
extern wide_int vector_cst_int_elt (const_tree, unsigned int);
extern tree vector_cst_elt (const_tree, unsigned int);