+2019-02-26 Jakub Jelinek <jakub@redhat.com>
+
+ PR tree-optimization/89500
+ * tree-ssa-strlen.c (stridx_strlenloc): Adjust comment.
+ (handle_builtin_strlen): Remove noncst_bound variable. Always
+ optimize strnlen (x, 0) to 0. Optimize strnlen (x, cst) to
+ cst if the first cst bytes starting at x are known to be non-zero,
+ even if the string is not zero terminated. Don't try to modify
+ *si for strnlen. Update strlen_to_stridx only for strlen or if
+ we can prove strnlen returns the same value as strlen would.
+
2019-02-26 Martin Liska <mliska@suse.cz>
* alloc-pool.h (struct pool_usage): Remove extra
--- /dev/null
+/* { dg-do compile } */
+/* { dg-options "-O2 -Wstringop-overflow" } */
+
+void
+foo (char *a)
+{
+ char b[16] = "abcdefg";
+ __builtin_strncpy (a, b, __builtin_strlen (b)); /* { dg-warning "specified bound depends on the length of the source argument" } */
+}
+
+void
+bar (char *a)
+{
+ char b[16] = "abcdefg";
+ __builtin_strncpy (a, b, __builtin_strnlen (b, 8)); /* { dg-warning "specified bound depends on the length of the source argument" } */
+}
+
+void
+baz (char *a)
+{
+ char b[16] = "abcdefg";
+ __builtin_strncpy (a, b, __builtin_strnlen (b, 7)); /* { dg-bogus "specified bound depends on the length of the source argument" } */
+}
+
+void fill (char *);
+
+void
+qux (char *a)
+{
+ char b[16];
+ fill (b);
+ __builtin_memcpy (b, "abcdefg", 7);
+ __builtin_strncpy (a, b, __builtin_strnlen (b, 8)); /* { dg-bogus "specified bound depends on the length of the source argument" } */
+}
--- /dev/null
+/* PR tree-optimization/89500 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* { dg-final { scan-tree-dump-times "return 10;" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "return 5;" 1 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "return 0;" 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "strnlen " 1 "optimized" } } */
+
+#include "strlenopt.h"
+
+void foo (char *);
+
+size_t
+f1 (void)
+{
+ char a[10] = "0123456789";
+ return strnlen (a, 10);
+}
+
+size_t
+f2 (void)
+{
+ char a[10] = "0123456789";
+ return strnlen (a, 5);
+}
+
+size_t
+f3 (void)
+{
+ char a[10] = "0123456789";
+ return strnlen (a, 0);
+}
+
+size_t
+f4 (void)
+{
+ char a[20];
+ foo (a);
+ memcpy (a, "0123456789", 10);
+ return strnlen (a, 10);
+}
+
+size_t
+f5 (void)
+{
+ char a[20];
+ foo (a);
+ memcpy (a, "0123456789", 10);
+ return strnlen (a, 14);
+}
+
+size_t
+f6 (void)
+{
+ char a[20];
+ foo (a);
+ return strnlen (a, 0);
+}
mappings. */
static hash_map<tree_decl_hash, stridxlist> *decl_to_stridxlist_htab;
-/* Hash table mapping strlen calls to stridx instances describing
+/* Hash table mapping strlen (or strnlen with constant bound and return
+ smaller than bound) calls to stridx instances describing
the calls' arguments. Non-null only when warn_stringop_truncation
is non-zero. */
typedef std::pair<int, location_t> stridx_strlenloc;
tree bound = (DECL_FUNCTION_CODE (callee) == BUILT_IN_STRNLEN
? gimple_call_arg (stmt, 1) : NULL_TREE);
int idx = get_stridx (src);
- if (idx)
+ if (idx || (bound && integer_zerop (bound)))
{
strinfo *si = NULL;
tree rhs;
if (idx < 0)
rhs = build_int_cst (TREE_TYPE (lhs), ~idx);
+ else if (idx == 0)
+ rhs = bound;
else
{
rhs = NULL_TREE;
si = get_strinfo (idx);
if (si != NULL)
- rhs = get_string_length (si);
+ {
+ rhs = get_string_length (si);
+ /* For strnlen, if bound is constant, even if si is not known
+ to be zero terminated, if we know at least bound bytes are
+ not zero, the return value will be bound. */
+ if (rhs == NULL_TREE
+ && bound != NULL_TREE
+ && TREE_CODE (bound) == INTEGER_CST
+ && si->nonzero_chars != NULL_TREE
+ && TREE_CODE (si->nonzero_chars) == INTEGER_CST
+ && tree_int_cst_le (bound, si->nonzero_chars))
+ rhs = bound;
+ }
}
if (rhs != NULL_TREE)
{
if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs)))
rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs);
- /* Set for strnlen() calls with a non-constant bound. */
- bool noncst_bound = false;
if (bound)
- {
- tree new_rhs
- = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (rhs), rhs, bound);
-
- noncst_bound = (TREE_CODE (new_rhs) != INTEGER_CST
- || tree_int_cst_lt (new_rhs, rhs));
-
- rhs = new_rhs;
- }
+ rhs = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (rhs), rhs, bound);
if (!update_call_from_tree (gsi, rhs))
gimplify_and_update_call_from_tree (gsi, rhs);
print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
}
- /* Avoid storing the length for calls to strnlen() with
- a non-constant bound. */
- if (noncst_bound)
- return;
-
if (si != NULL
+ /* Don't update anything for strnlen. */
+ && bound == NULL_TREE
&& TREE_CODE (si->nonzero_chars) != SSA_NAME
&& TREE_CODE (si->nonzero_chars) != INTEGER_CST
&& !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
gcc_assert (si->full_string_p);
}
- if (strlen_to_stridx)
+ if (strlen_to_stridx
+ && (bound == NULL_TREE
+ /* For strnlen record this only if the call is proven
+ to return the same value as strlen would. */
+ || (TREE_CODE (bound) == INTEGER_CST
+ && TREE_CODE (rhs) == INTEGER_CST
+ && tree_int_cst_lt (rhs, bound))))
strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc));
return;