+2017-12-18 Martin Sebor <msebor@redhat.com>
+
+ PR middle-end/83373
+ PR tree-optimization/78450
+ * tree-ssa-strlen.c (maybe_set_strlen_range): New function.
+ (handle_builtin_strlen): Call it.
+
2017-12-18 Segher Boessenkool <segher@kernel.crashing.org>
PR rtl-optimization/83424
+2017-12-18 Martin Sebor <msebor@redhat.com>
+
+ PR middle-end/83373
+ PR tree-optimization/78450
+ * gcc.dg/pr83373.c: New test.
+ * gcc.dg/strlenopt-36.c: New test.
+ * gcc.dg/strlenopt-37.c: New test.
+
2017-12-18 Marek Polacek <polacek@redhat.com>
PR c++/83116
--- /dev/null
+/* PR middle-end/83373 - False positive reported by -Wstringop-overflow
+ { dg-do compile }
+ { dg-options "-O2 -Wstringop-overflow" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+char buf[100];
+
+void get_data (char*);
+
+__attribute__ ((nonnull(1, 2)))
+inline char* my_strcpy (char* dst, const char* src, size_t size)
+{
+ size_t len = __builtin_strlen (src);
+ if (len < size)
+ __builtin_memcpy (dst, src, len + 1);
+ else
+ {
+ __builtin_memcpy (dst, src, size - 1); /* { dg-bogus "\\\[-Wstringop-oveflow]" } */
+ dst[size - 1] = '\0';
+ }
+
+ return dst;
+}
+
+void test(void)
+{
+ char data[20] = "12345";
+
+ get_data (data);
+
+ my_strcpy (buf, data, sizeof buf);
+}
--- /dev/null
+/* PR tree-optimization/78450 - strlen(s) return value can be assumed
+ to be less than the size of s
+ { dg-do compile }
+ { dg-options "-O2 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+extern char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
+extern char a0[0]; /* Intentionally not tested here. */
+extern char ax[]; /* Same. */
+
+struct MemArrays {
+ char a7[7], a6[6], a5[5], a4[4], a3[3], a2[2], a1[1];
+ char a0[0]; /* Not tested here. */
+};
+
+struct NestedMemArrays {
+ struct { char a7[7]; } ma7;
+ struct { char a6[6]; } ma6;
+ struct { char a5[5]; } ma5;
+ struct { char a4[4]; } ma4;
+ struct { char a3[3]; } ma3;
+ struct { char a2[2]; } ma2;
+ struct { char a1[1]; } ma1;
+ struct { char a0[0]; } ma0;
+ char last;
+};
+
+extern void failure_on_line (int);
+
+#define TEST_FAIL(line) \
+ do { \
+ failure_on_line (line); \
+ } while (0)
+
+#define T(expr) \
+ if (!(expr)) TEST_FAIL (__LINE__); else (void)0
+
+
+void test_array (void)
+{
+ T (strlen (a7) < sizeof a7);
+ T (strlen (a6) < sizeof a6);
+ T (strlen (a5) < sizeof a5);
+ T (strlen (a4) < sizeof a4);
+ T (strlen (a3) < sizeof a3);
+
+ /* The following two calls are folded too early which defeats
+ the strlen() optimization.
+ T (strlen (a2) == 1);
+ T (strlen (a1) == 0); */
+}
+
+void test_memarray (struct MemArrays *ma)
+{
+ T (strlen (ma->a7) < sizeof ma->a7);
+ T (strlen (ma->a6) < sizeof ma->a6);
+ T (strlen (ma->a5) < sizeof ma->a5);
+ T (strlen (ma->a4) < sizeof ma->a4);
+ T (strlen (ma->a3) < sizeof ma->a3);
+
+ /* The following two calls are folded too early which defeats
+ the strlen() optimization.
+ T (strlen (ma->a2) == 1);
+ T (strlen (ma->a1) == 0); */
+}
+
+/* Verify that the range of strlen(A) of a last struct member is
+ set even when the array is the sole member of a struct as long
+ as the struct itself is a member of another struct. The converse
+ is tested in stlenopt-37.c. */
+void test_nested_memarray (struct NestedMemArrays *ma)
+{
+ T (strlen (ma->ma7.a7) < sizeof ma->ma7.a7);
+ T (strlen (ma->ma6.a6) < sizeof ma->ma6.a6);
+ T (strlen (ma->ma5.a5) < sizeof ma->ma5.a5);
+ T (strlen (ma->ma4.a4) < sizeof ma->ma4.a4);
+ T (strlen (ma->ma3.a3) < sizeof ma->ma3.a3);
+
+ /* The following two calls are folded too early which defeats
+ the strlen() optimization.
+ T (strlen (ma->ma2.a2) == 1);
+ T (strlen (ma->ma1.a1) == 0); */
+}
+
+/* { dg-final { scan-tree-dump-not "failure_on_line" "optimized" } } */
--- /dev/null
+/* PR tree-optimization/78450 - strlen(s) return value can be assumed
+ to be less than the size of s
+ { dg-do compile }
+ { dg-options "-O2 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+extern char ax[];
+
+struct MemArray7 { char a7[7]; };
+struct MemArray6 { char a6[6]; };
+struct MemArray5 { char a5[5]; };
+struct MemArray4 { char a4[4]; };
+struct MemArray3 { char a3[3]; };
+struct MemArray2 { char a2[2]; };
+struct MemArray1 { char a1[1]; };
+struct MemArray0 { int n; char a0[0]; };
+struct MemArrayX { int n; char ax[]; };
+
+struct MemArrays
+{
+ struct MemArray7 *ma7;
+ struct MemArray6 *ma6;
+ struct MemArray5 *ma5;
+ struct MemArray4 *ma4;
+ struct MemArray3 *ma3;
+ struct MemArray2 *ma2;
+ struct MemArray1 *ma1;
+ struct MemArray0 *ma0;
+ struct MemArrayX *max;
+};
+
+extern void if_stmt_on_line (int);
+extern void else_stmt_on_line (int);
+
+#define T(expr) \
+ (!!(expr) ? if_stmt_on_line (__LINE__) : else_stmt_on_line (__LINE__))
+
+void test_memarray_lt (struct MemArrays *p)
+{
+ T (strlen (p->ma7->a7) < sizeof p->ma7->a7);
+ T (strlen (p->ma6->a6) < sizeof p->ma6->a6);
+ T (strlen (p->ma5->a5) < sizeof p->ma5->a5);
+ T (strlen (p->ma4->a4) < sizeof p->ma4->a4);
+ T (strlen (p->ma3->a3) < sizeof p->ma3->a3);
+ T (strlen (p->ma2->a2) < sizeof p->ma2->a2);
+ T (strlen (p->ma1->a1) < sizeof p->ma1->a1);
+
+ T (strlen (p->ma0->a0) < 1);
+ T (strlen (p->max->ax) < 1);
+}
+
+void test_memarray_eq (struct MemArrays *p)
+{
+ T (strlen (p->ma7->a7) == sizeof p->ma7->a7);
+ T (strlen (p->ma6->a6) == sizeof p->ma6->a6);
+ T (strlen (p->ma5->a5) == sizeof p->ma5->a5);
+ T (strlen (p->ma4->a4) == sizeof p->ma4->a4);
+ T (strlen (p->ma3->a3) == sizeof p->ma3->a3);
+ T (strlen (p->ma2->a2) == sizeof p->ma2->a2);
+ T (strlen (p->ma1->a1) == sizeof p->ma1->a1);
+
+ T (strlen (p->ma0->a0) == 1);
+ T (strlen (p->max->ax) == 1);
+}
+
+void test_memarray_gt (struct MemArrays *p)
+{
+ T (strlen (p->ma7->a7) > sizeof p->ma7->a7);
+ T (strlen (p->ma6->a6) > sizeof p->ma6->a6);
+ T (strlen (p->ma5->a5) > sizeof p->ma5->a5);
+ T (strlen (p->ma4->a4) > sizeof p->ma4->a4);
+ T (strlen (p->ma3->a3) > sizeof p->ma3->a3);
+ T (strlen (p->ma2->a2) > sizeof p->ma2->a2);
+ T (strlen (p->ma1->a1) > sizeof p->ma1->a1);
+
+ T (strlen (p->ma0->a0) > 1);
+ T (strlen (p->max->ax) > 1);
+ }
+
+/* Verify that no if or else statements have been eliminated.
+ { dg-final { scan-tree-dump-times "if_stmt_on_line" 27 "optimized" } }
+ { dg-final { scan-tree-dump-times "else_stmt_on_line" 27 "optimized" } } */
update_stmt (last.stmt);
}
+/* For an LHS that is an SSA_NAME and for strlen() argument SRC, set
+ LHS range info to [0, N] if SRC refers to a character array A[N]
+ with unknown length bounded by N. */
+
+static void
+maybe_set_strlen_range (tree lhs, tree src)
+{
+ if (TREE_CODE (lhs) != SSA_NAME)
+ return;
+
+ if (TREE_CODE (src) == SSA_NAME)
+ {
+ gimple *def = SSA_NAME_DEF_STMT (src);
+ if (is_gimple_assign (def)
+ && gimple_assign_rhs_code (def) == ADDR_EXPR)
+ src = gimple_assign_rhs1 (def);
+ }
+
+ if (TREE_CODE (src) != ADDR_EXPR)
+ return;
+
+ /* The last array member of a struct can be bigger than its size
+ suggests if it's treated as a poor-man's flexible array member. */
+ src = TREE_OPERAND (src, 0);
+ if (TREE_CODE (TREE_TYPE (src)) != ARRAY_TYPE
+ || array_at_struct_end_p (src))
+ return;
+
+ tree type = TREE_TYPE (src);
+ if (tree dom = TYPE_DOMAIN (type))
+ if (tree maxval = TYPE_MAX_VALUE (dom))
+ {
+ wide_int max = wi::to_wide (maxval);
+ wide_int min = wi::zero (max.get_precision ());
+ set_range_info (lhs, VR_RANGE, min, max);
+ }
+}
+
/* Handle a strlen call. If strlen of the argument is known, replace
the strlen call with the known value, otherwise remember that strlen
of the argument is stored in the lhs SSA_NAME. */
set_strinfo (idx, si);
find_equal_ptrs (src, idx);
+ /* For SRC that is an array of N elements, set LHS's range
+ to [0, N]. */
+ maybe_set_strlen_range (lhs, src);
+
if (strlen_to_stridx)
{
location_t loc = gimple_location (stmt);