This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] set range for strlen(array) to avoid spurious -Wstringop-overflow (PR 83373 , PR 78450)


Bug 83373 - False positive reported by -Wstringop-overflow, is
another example of warning triggered by a missed optimization
opportunity, this time in the strlen pass.  The optimization
is discussed in pr78450 - strlen(s) return value can be assumed
to be less than the size of s.  The gist of it is that the result
of strlen(array) can be assumed to be less than the size of
the array (except in the corner case of last struct members).

To avoid the false positive the attached patch adds this
optimization to the strlen pass.  Although the patch passes
bootstrap and regression tests for all front-ends I'm not sure
the way it determines the upper bound of the range is 100%
correct for languages with arrays with a non-zero lower bound.
Maybe it's just not as tight as it could be.

Tested on x86_64-linux.

Martin
PR middle-end/83373 - False positive reported by -Wstringop-overflow
PR tree-optimization/78450 - strlen(s) return value can be assumed to be less than the size of s

gcc/ChangeLog:

	PR middle-end/83373
	PR tree-optimization/78450
	* tree-ssa-strlen.c (maybe_set_strlen_range): New function.
	(handle_builtin_strlen): Call it.


gcc/testsuite/ChangeLog:

	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.

diff --git a/gcc/testsuite/gcc.dg/pr83373.c b/gcc/testsuite/gcc.dg/pr83373.c
new file mode 100644
index 0000000..6b0de09
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr83373.c
@@ -0,0 +1,33 @@
+/* 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);
+}
diff --git a/gcc/testsuite/gcc.dg/strlenopt-36.c b/gcc/testsuite/gcc.dg/strlenopt-36.c
new file mode 100644
index 0000000..d6fcca2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-36.c
@@ -0,0 +1,86 @@
+/* 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" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-37.c b/gcc/testsuite/gcc.dg/strlenopt-37.c
new file mode 100644
index 0000000..865653c
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/strlenopt-37.c
@@ -0,0 +1,83 @@
+/* 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" } }  */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 94f20ef..d89677b 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -1150,6 +1150,44 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
   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.  */
@@ -1260,6 +1298,10 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
       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);

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]