PR tree-optimization/83671 - Fix for false positive reported by -Wstringop-overflow...
authorMartin Sebor <msebor@redhat.com>
Wed, 10 Jan 2018 21:40:14 +0000 (21:40 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Wed, 10 Jan 2018 21:40:14 +0000 (14:40 -0700)
gcc/testsuite/ChangeLog:

PR tree-optimization/83671
* gcc.dg/strlenopt-40.c: New test.
* gcc.dg/strlenopt-41.c: New test.

gcc/ChangeLog:

PR tree-optimization/83671
* builtins.c (c_strlen): Unconditionally return zero for the empty
string.
Use -Warray-bounds for warnings.
* gimple-fold.c (get_range_strlen): Handle non-constant lengths
for non-constant array indices with COMPONENT_REF, arrays of
arrays, and pointers to arrays.
(gimple_fold_builtin_strlen): Determine and set length range for
non-constant character arrays.

From-SVN: r256457

gcc/ChangeLog
gcc/builtins.c
gcc/gimple-fold.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/warn/string1.C
gcc/testsuite/gcc.dg/strlenopt-40.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/strlenopt-41.c [new file with mode: 0644]

index ba1a84475174fefa8b15cacba7a5e13ba6358450..081c04aa8a783a050858539607a9fbe68a471bf4 100644 (file)
@@ -1,3 +1,15 @@
+2018-01-10  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/83671
+       * builtins.c (c_strlen): Unconditionally return zero for the empty
+       string.
+       Use -Warray-bounds for warnings.
+       * gimple-fold.c (get_range_strlen): Handle non-constant lengths
+       for non-constant array indices with COMPONENT_REF, arrays of
+       arrays, and pointers to arrays.
+       (gimple_fold_builtin_strlen): Determine and set length range for
+       non-constant character arrays.
+
 2018-01-10  Aldy Hernandez  <aldyh@redhat.com>
 
        PR middle-end/81897
index 1d6e69d30ce6a2d0506c42a9c9b2933a114502ab..a0d0a10d38fcdb3e3204f32e887d77bff6ba28a4 100644 (file)
@@ -621,6 +621,9 @@ c_strlen (tree src, int only_value)
          return NULL_TREE;
        }
 
+      if (!maxelts)
+       return ssize_int (0);
+
       /* We don't know the starting offset, but we do know that the string
         has no internal zero bytes.  We can assume that the offset falls
         within the bounds of the string; otherwise, the programmer deserves
@@ -651,7 +654,8 @@ c_strlen (tree src, int only_value)
       if (only_value != 2
          && !TREE_NO_WARNING (src))
         {
-         warning_at (loc, 0, "offset %qwi outside bounds of constant string",
+         warning_at (loc, OPT_Warray_bounds,
+                     "offset %qwi outside bounds of constant string",
                      eltoff);
           TREE_NO_WARNING (src) = 1;
         }
index 7e4cb74d7cc853cb9a30004aa1769083d2068b11..504a85d1441da6814229dee3028793b13cba2cde 100644 (file)
@@ -1299,7 +1299,7 @@ static bool
 get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
                  bool fuzzy, bool *flexp)
 {
-  tree var, val;
+  tree var, val = NULL_TREE;
   gimple *def_stmt;
 
   /* The minimum and maximum length.  The MAXLEN pointer stays unchanged
@@ -1311,14 +1311,33 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
     {
       /* We can end up with &(*iftmp_1)[0] here as well, so handle it.  */
       if (TREE_CODE (arg) == ADDR_EXPR
-         && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF
-         && integer_zerop (TREE_OPERAND (TREE_OPERAND (arg, 0), 1)))
+         && TREE_CODE (TREE_OPERAND (arg, 0)) == ARRAY_REF)
        {
-         tree aop0 = TREE_OPERAND (TREE_OPERAND (arg, 0), 0);
-         if (TREE_CODE (aop0) == INDIRECT_REF
-             && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
-           return get_range_strlen (TREE_OPERAND (aop0, 0),
-                                    length, visited, type, fuzzy, flexp);
+         tree op = TREE_OPERAND (arg, 0);
+         if (integer_zerop (TREE_OPERAND (op, 1)))
+           {
+             tree aop0 = TREE_OPERAND (op, 0);
+             if (TREE_CODE (aop0) == INDIRECT_REF
+                 && TREE_CODE (TREE_OPERAND (aop0, 0)) == SSA_NAME)
+               return get_range_strlen (TREE_OPERAND (aop0, 0),
+                                        length, visited, type, fuzzy, flexp);
+           }
+         else if (TREE_CODE (TREE_OPERAND (op, 0)) == COMPONENT_REF && fuzzy)
+           {
+             /* Fail if an array is the last member of a struct object
+                since it could be treated as a (fake) flexible array
+                member.  */
+             tree idx = TREE_OPERAND (op, 1);
+
+             arg = TREE_OPERAND (op, 0);
+             tree optype = TREE_TYPE (arg);
+             if (tree dom = TYPE_DOMAIN (optype))
+               if (tree bound = TYPE_MAX_VALUE (dom))
+                 if (TREE_CODE (bound) == INTEGER_CST
+                     && TREE_CODE (idx) == INTEGER_CST
+                     && tree_int_cst_lt (bound, idx))
+                   return false;
+           }
        }
 
       if (type == 2)
@@ -1337,21 +1356,48 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
            return get_range_strlen (TREE_OPERAND (arg, 0), length,
                                     visited, type, fuzzy, flexp);
 
-         if (TREE_CODE (arg) == COMPONENT_REF
+         if (TREE_CODE (arg) == ARRAY_REF)
+           {
+             tree type = TREE_TYPE (TREE_OPERAND (arg, 0));
+
+             while (TREE_CODE (type) == ARRAY_TYPE
+                    && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
+               type = TREE_TYPE (type);
+
+             val = TYPE_SIZE_UNIT (type);
+             if (!val || integer_zerop (val))
+               return false;
+
+             val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
+                                integer_one_node);
+             /* Set the minimum size to zero since the string in
+                the array could have zero length.  */
+             *minlen = ssize_int (0);
+           }
+         else if (TREE_CODE (arg) == COMPONENT_REF
              && TREE_CODE (TREE_TYPE (TREE_OPERAND (arg, 1))) == ARRAY_TYPE)
            {
              /* Use the type of the member array to determine the upper
                 bound on the length of the array.  This may be overly
                 optimistic if the array itself isn't NUL-terminated and
                 the caller relies on the subsequent member to contain
-                the NUL.
+                the NUL but that would only be considered valid if
+                the array were the last member of a struct.
                 Set *FLEXP to true if the array whose bound is being
                 used is at the end of a struct.  */
              if (array_at_struct_end_p (arg))
                *flexp = true;
 
              arg = TREE_OPERAND (arg, 1);
-             val = TYPE_SIZE_UNIT (TREE_TYPE (arg));
+
+             tree type = TREE_TYPE (arg);
+
+             while (TREE_CODE (type) == ARRAY_TYPE
+                    && TREE_CODE (TREE_TYPE (type)) == ARRAY_TYPE)
+               type = TREE_TYPE (type);
+
+             /* Fail when the array bound is unknown or zero.  */
+             val = TYPE_SIZE_UNIT (type);
              if (!val || integer_zerop (val))
                return false;
              val = fold_build2 (MINUS_EXPR, TREE_TYPE (val), val,
@@ -1361,17 +1407,25 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
              *minlen = ssize_int (0);
            }
 
-         if (VAR_P (arg) 
-             && TREE_CODE (TREE_TYPE (arg)) == ARRAY_TYPE)
+         if (VAR_P (arg))
            {
-             val = TYPE_SIZE_UNIT (TREE_TYPE (arg));
-             if (!val || TREE_CODE (val) != INTEGER_CST || integer_zerop (val))
-               return false;
-             val = wide_int_to_tree (TREE_TYPE (val), 
-                                     wi::sub(wi::to_wide (val), 1));
-             /* Set the minimum size to zero since the string in
-                the array could have zero length.  */
-             *minlen = ssize_int (0);
+             tree type = TREE_TYPE (arg);
+             if (POINTER_TYPE_P (type))
+               type = TREE_TYPE (type);
+
+             if (TREE_CODE (type) == ARRAY_TYPE)
+               {
+                 val = TYPE_SIZE_UNIT (type);
+                 if (!val
+                     || TREE_CODE (val) != INTEGER_CST
+                     || integer_zerop (val))
+                   return false;
+                 val = wide_int_to_tree (TREE_TYPE (val),
+                                         wi::sub(wi::to_wide (val), 1));
+                 /* Set the minimum size to zero since the string in
+                    the array could have zero length.  */
+                 *minlen = ssize_int (0);
+               }
            }
        }
 
@@ -3462,12 +3516,44 @@ static bool
 gimple_fold_builtin_strlen (gimple_stmt_iterator *gsi)
 {
   gimple *stmt = gsi_stmt (*gsi);
-  tree len = get_maxval_strlen (gimple_call_arg (stmt, 0), 0);
-  if (!len)
-    return false;
-  len = force_gimple_operand_gsi (gsi, len, true, NULL, true, GSI_SAME_STMT);
-  replace_call_with_value (gsi, len);
-  return true;
+
+  wide_int minlen;
+  wide_int maxlen;
+
+  tree lenrange[2];
+  if (!get_range_strlen (gimple_call_arg (stmt, 0), lenrange)
+      && lenrange[0] && TREE_CODE (lenrange[0]) == INTEGER_CST
+      && lenrange[1] && TREE_CODE (lenrange[1]) == INTEGER_CST)
+    {
+      /* The range of lengths refers to either a single constant
+        string or to the longest and shortest constant string
+        referenced by the argument of the strlen() call, or to
+        the strings that can possibly be stored in the arrays
+        the argument refers to.  */
+      minlen = wi::to_wide (lenrange[0]);
+      maxlen = wi::to_wide (lenrange[1]);
+    }
+  else
+    {
+      unsigned prec = TYPE_PRECISION (sizetype);
+
+      minlen = wi::shwi (0, prec);
+      maxlen = wi::to_wide (max_object_size (), prec) - 2;
+    }
+
+  if (minlen == maxlen)
+    {
+      lenrange[0] = force_gimple_operand_gsi (gsi, lenrange[0], true, NULL,
+                                             true, GSI_SAME_STMT);
+      replace_call_with_value (gsi, lenrange[0]);
+      return true;
+    }
+
+  tree lhs = gimple_call_lhs (stmt);
+  if (lhs && TREE_CODE (lhs) == SSA_NAME)
+    set_range_info (lhs, VR_RANGE, minlen, maxlen);
+
+  return false;
 }
 
 /* Fold a call to __builtin_acc_on_device.  */
index 825224d8e58924bb94472314a947684d420ac458..10273a9d8a68f3312aec89cf7fea186aec8f21ff 100644 (file)
@@ -1,3 +1,9 @@
+2018-01-10  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/83671
+       * gcc.dg/strlenopt-40.c: New test.
+       * gcc.dg/strlenopt-41.c: New test.
+
 2018-01-10  Steven G. Kargl  <kargl@gcc.gnu.org>
 
        PR fortran/83093
index 8f24a78f70901331381eaef058219898d8dd5b1d..a0b0d47de1b1b6c6d50af8417220ef5802f32416 100644 (file)
@@ -1,5 +1,5 @@
 // PR c++/35652
-// { dg-options "-O" }
+// { dg-options "-O -Wall" }
 
 #include <string>
 int test() {
diff --git a/gcc/testsuite/gcc.dg/strlenopt-40.c b/gcc/testsuite/gcc.dg/strlenopt-40.c
new file mode 100644 (file)
index 0000000..f4577d6
--- /dev/null
@@ -0,0 +1,393 @@
+/* PR tree-optimization/83671 - fix for false positive reported by
+   -Wstringop-overflow does not work with inlining
+   { dg-do compile }
+   { dg-options "-O1 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+#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)
+
+/* Macros to emit a call to funcation named
+     call_in_{true,false}_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_TRUE(expr) \
+  if (!(expr)) FAIL (in_true_branch_not_eliminated); else (void)0
+
+#define ELIM_FALSE(expr)                                       \
+  if (!!(expr)) FAIL (in_false_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)
+
+typedef char A3[3], A5[5], A7[7], AX[];
+
+typedef A3 A7_3[7];
+typedef A3 AX_3[];
+typedef A5 A7_5[7];
+typedef A7 A5_7[5];
+
+extern A7_3 a7_3;
+extern A5_7 a5_7;
+extern AX_3 ax_3;
+
+extern A3 a3;
+extern A7 a5;
+extern A7 a7;
+extern AX ax;
+
+extern A3 *pa3;
+extern A5 *pa5;
+extern A7 *pa7;
+
+extern A7_3 *pa7_3;
+extern AX_3 *pax_3;
+extern A5_7 *pa5_7;
+extern A7_5 *pa7_5;
+
+extern char *ptr;
+
+struct MemArrays0 {
+  A7_3 a7_3;
+  A5_7 a5_7;
+  char a3[3], a5[5], a0[0];
+};
+struct MemArraysX { char a3[3], a5[5], ax[]; };
+struct MemArrays7 { char a3[3], a5[5], a7[7]; };
+
+struct MemArrays0 ma0_3_5_7[3][5][7];
+
+void elim_strings (int i)
+{
+  ELIM_TRUE (strlen (i < 0 ? "123" : "321") == 3);
+  ELIM_FALSE (strlen (i < 0 ? "123" : "321") > 3);
+  ELIM_FALSE (strlen (i < 0 ? "123" : "321") < 3);
+
+  ELIM_TRUE (strlen (i < 0 ? "123" : "4321") >= 3);
+  ELIM_FALSE (strlen (i < 0 ? "123" : "4321") > 4);
+  ELIM_FALSE (strlen (i < 0 ? "123" : "4321") < 3);
+
+  ELIM_TRUE (strlen (i < 0 ? "1234" : "321") >= 3);
+  ELIM_FALSE (strlen (i < 0 ? "1234" : "321") < 3);
+  ELIM_FALSE (strlen (i < 0 ? "1234" : "321") > 4);
+
+  ELIM_TRUE (strlen (i < 0 ? "123" : "4321") <= 4);
+  ELIM_TRUE (strlen (i < 0 ? "1234" : "321") <= 4);
+
+  ELIM_TRUE (strlen (i < 0 ? "1" : "123456789") <= 9);
+  ELIM_TRUE (strlen (i < 0 ? "1" : "123456789") >= 1);
+}
+
+/* Verify that strlen calls involving uninitialized global arrays
+   of known size are eliminated when they appear in expressions
+   that test for results that must be true.  */
+void elim_global_arrays (int i)
+{
+  /* Verify that the expression involving the strlen call as well
+     as whatever depends on it is eliminated  from the test output.
+     All these expressions must be trivially true.  */
+  ELIM_TRUE (strlen (a7_3[0]) < sizeof a7_3[0]);
+  ELIM_TRUE (strlen (a7_3[1]) < sizeof a7_3[1]);
+  ELIM_TRUE (strlen (a7_3[6]) < sizeof a7_3[6]);
+  ELIM_TRUE (strlen (a7_3[i]) < sizeof a7_3[i]);
+
+  ELIM_TRUE (strlen (a5_7[0]) < sizeof a5_7[0]);
+  ELIM_TRUE (strlen (a5_7[1]) < sizeof a5_7[1]);
+  ELIM_TRUE (strlen (a5_7[4]) < sizeof a5_7[4]);
+  ELIM_TRUE (strlen (a5_7[i]) < sizeof a5_7[0]);
+
+  ELIM_TRUE (strlen (ax_3[0]) < sizeof ax_3[0]);
+  ELIM_TRUE (strlen (ax_3[1]) < sizeof ax_3[1]);
+  ELIM_TRUE (strlen (ax_3[9]) < sizeof ax_3[9]);
+  ELIM_TRUE (strlen (ax_3[i]) < sizeof ax_3[i]);
+
+  ELIM_TRUE (strlen (a3) < sizeof a3);
+  ELIM_TRUE (strlen (a7) < sizeof a7);
+
+  ELIM_TRUE (strlen (ax) != DIFF_MAX);
+  ELIM_TRUE (strlen (ax) != DIFF_MAX - 1);
+  ELIM_TRUE (strlen (ax) < DIFF_MAX - 1);
+}
+
+void elim_pointer_to_arrays (void)
+{
+  ELIM_TRUE (strlen (*pa7) < 7);
+  ELIM_TRUE (strlen (*pa5) < 5);
+  ELIM_TRUE (strlen (*pa3) < 3);
+
+  ELIM_TRUE (strlen ((*pa7_3)[0]) < 3);
+  ELIM_TRUE (strlen ((*pa7_3)[1]) < 3);
+  ELIM_TRUE (strlen ((*pa7_3)[6]) < 3);
+
+  ELIM_TRUE (strlen ((*pax_3)[0]) < 3);
+  ELIM_TRUE (strlen ((*pax_3)[1]) < 3);
+  ELIM_TRUE (strlen ((*pax_3)[9]) < 3);
+
+  ELIM_TRUE (strlen ((*pa5_7)[0]) < 7);
+  ELIM_TRUE (strlen ((*pa5_7)[1]) < 7);
+  ELIM_TRUE (strlen ((*pa5_7)[4]) < 7);
+}
+
+void elim_global_arrays_and_strings (int i)
+{
+  ELIM_TRUE (strlen (i < 0 ? a3 : "") < 3);
+  ELIM_TRUE (strlen (i < 0 ? a3 : "1") < 3);
+  ELIM_TRUE (strlen (i < 0 ? a3 : "12") < 3);
+  ELIM_TRUE (strlen (i < 0 ? a3 : "123") < 4);
+
+  ELIM_FALSE (strlen (i < 0 ? a3 : "") > 3);
+  ELIM_FALSE (strlen (i < 0 ? a3 : "1") > 3);
+  ELIM_FALSE (strlen (i < 0 ? a3 : "12") > 3);
+  ELIM_FALSE (strlen (i < 0 ? a3 : "123") > 4);
+
+  ELIM_TRUE (strlen (i < 0 ? a7 : "") < 7);
+  ELIM_TRUE (strlen (i < 0 ? a7 : "1") < 7);
+  ELIM_TRUE (strlen (i < 0 ? a7 : "12") < 7);
+  ELIM_TRUE (strlen (i < 0 ? a7 : "123") < 7);
+  ELIM_TRUE (strlen (i < 0 ? a7 : "123456") < 7);
+  ELIM_TRUE (strlen (i < 0 ? a7 : "1234567") < 8);
+
+  ELIM_FALSE (strlen (i < 0 ? a7 : "") > 6);
+  ELIM_FALSE (strlen (i < 0 ? a7 : "1") > 6);
+  ELIM_FALSE (strlen (i < 0 ? a7 : "12") > 6);
+  ELIM_FALSE (strlen (i < 0 ? a7 : "123") > 6);
+  ELIM_FALSE (strlen (i < 0 ? a7 : "123456") > 7);
+  ELIM_FALSE (strlen (i < 0 ? a7 : "1234567") > 8);
+}
+
+void elim_member_arrays_obj (int i)
+{
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a3) < 3);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a3) < 3);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a3) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a3) < 3);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][1].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][2].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][6].a5) < 5);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[1][0][0].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][0][1].a5) < 5);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[1][1][0].a5) < 5);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5) < 5);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < 3);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < 3);
+
+  ELIM_TRUE (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 7);
+  ELIM_TRUE (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 7);
+}
+
+void elim_member_arrays_ptr (struct MemArrays0 *ma0,
+                            struct MemArraysX *max,
+                            struct MemArrays7 *ma7,
+                            int i)
+{
+  ELIM_TRUE (strlen (ma0->a7_3[0]) < 3);
+  ELIM_TRUE (strlen (ma0->a7_3[1]) < 3);
+  ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
+  ELIM_TRUE (strlen (ma0->a7_3[6]) < 3);
+  ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
+  ELIM_TRUE (strlen (ma0->a7_3[i]) < 3);
+
+  ELIM_TRUE (strlen (ma0->a5_7[0]) < 7);
+  ELIM_TRUE (strlen (ma0[0].a5_7[0]) < 7);
+  ELIM_TRUE (strlen (ma0[1].a5_7[0]) < 7);
+  ELIM_TRUE (strlen (ma0[1].a5_7[4]) < 7);
+  ELIM_TRUE (strlen (ma0[9].a5_7[0]) < 7);
+  ELIM_TRUE (strlen (ma0[9].a5_7[4]) < 7);
+
+  ELIM_TRUE (strlen (ma0->a3) < sizeof ma0->a3);
+  ELIM_TRUE (strlen (ma0->a5) < sizeof ma0->a5);
+  ELIM_TRUE (strlen (ma0->a0) < DIFF_MAX - 1);
+
+  ELIM_TRUE (strlen (max->a3) < sizeof max->a3);
+  ELIM_TRUE (strlen (max->a5) < sizeof max->a5);
+  ELIM_TRUE (strlen (max->ax) < DIFF_MAX - 1);
+
+  ELIM_TRUE (strlen (ma7->a3) < sizeof max->a3);
+  ELIM_TRUE (strlen (ma7->a5) < sizeof max->a5);
+  ELIM_TRUE (strlen (ma7->a7) < DIFF_MAX - 1);
+}
+
+
+#line 1000
+
+/* Verify that strlen calls involving uninitialized global arrays
+   of unknown size are not eliminated when they appear in expressions
+   that test for results that need not be true.  */
+void keep_global_arrays (int i)
+{
+  KEEP (strlen (a7_3[0]) < 2);
+  KEEP (strlen (a7_3[1]) < 2);
+  KEEP (strlen (a7_3[6]) < 2);
+  KEEP (strlen (a7_3[i]) < 2);
+
+  KEEP (strlen (a5_7[0]) < 6);
+  KEEP (strlen (a5_7[1]) < 6);
+  KEEP (strlen (a5_7[4]) < 6);
+  KEEP (strlen (a5_7[i]) < 6);
+
+  KEEP (strlen (ax_3[0]) < 2);
+  KEEP (strlen (ax_3[1]) < 2);
+  KEEP (strlen (ax_3[2]) < 2);
+  KEEP (strlen (ax_3[i]) < 2);
+
+  KEEP (strlen (a3) < 2);
+  KEEP (strlen (a7) < 6);
+
+  KEEP (strlen (a3 + i) < 2);
+  KEEP (strlen (a7 + i) < 2);
+
+  /* The length of an array of unknown size may be as large as
+     DIFF_MAX - 2.  */
+  KEEP (strlen (ax) != DIFF_MAX - 2);
+  KEEP (strlen (ax) < DIFF_MAX - 2);
+  KEEP (strlen (ax) < 999);
+  KEEP (strlen (ax) < 1);
+}
+
+void keep_pointer_to_arrays (void)
+{
+  KEEP (strlen (*pa7) < 6);
+  KEEP (strlen (*pa5) < 4);
+  KEEP (strlen (*pa3) < 2);
+
+  KEEP (strlen ((*pa7_3)[0]) < 2);
+  KEEP (strlen ((*pa7_3)[1]) < 2);
+  KEEP (strlen ((*pa7_3)[6]) < 2);
+
+  KEEP (strlen ((*pax_3)[0]) < 2);
+  KEEP (strlen ((*pax_3)[1]) < 2);
+  KEEP (strlen ((*pax_3)[9]) < 2);
+
+  KEEP (strlen ((*pa5_7)[0]) < 6);
+  KEEP (strlen ((*pa5_7)[1]) < 6);
+  KEEP (strlen ((*pa5_7)[4]) < 6);
+}
+
+void keep_global_arrays_and_strings (int i)
+{
+  KEEP (strlen (i < 0 ? a3 : "") < 2);
+  KEEP (strlen (i < 0 ? a3 : "1") < 2);
+  KEEP (strlen (i < 0 ? a3 : "12") < 2);
+  KEEP (strlen (i < 0 ? a3 : "123") < 3);
+
+  KEEP (strlen (i < 0 ? a7 : "") < 5);
+  KEEP (strlen (i < 0 ? a7 : "1") < 5);
+  KEEP (strlen (i < 0 ? a7 : "12") < 5);
+  KEEP (strlen (i < 0 ? a7 : "123") < 5);
+  KEEP (strlen (i < 0 ? a7 : "123456") < 6);
+  KEEP (strlen (i < 0 ? a7 : "1234567") < 6);
+}
+
+void keep_member_arrays_obj (int i)
+{
+  KEEP (strlen (ma0_3_5_7[0][0][0].a3) < 2);
+  KEEP (strlen (ma0_3_5_7[0][0][1].a3) < 2);
+  KEEP (strlen (ma0_3_5_7[0][0][2].a3) < 2);
+  KEEP (strlen (ma0_3_5_7[0][0][6].a3) < 2);
+
+  KEEP (strlen (ma0_3_5_7[1][0][0].a3) < 2);
+  KEEP (strlen (ma0_3_5_7[2][0][1].a3) < 2);
+
+  KEEP (strlen (ma0_3_5_7[1][1][0].a3) < 2);
+  KEEP (strlen (ma0_3_5_7[2][4][6].a3) < 2);
+
+  KEEP (strlen (ma0_3_5_7[0][0][0].a5) < 4);
+  KEEP (strlen (ma0_3_5_7[0][0][1].a5) < 4);
+  KEEP (strlen (ma0_3_5_7[0][0][2].a5) < 4);
+  KEEP (strlen (ma0_3_5_7[0][0][6].a5) < 4);
+
+  KEEP (strlen (ma0_3_5_7[1][0][0].a5) < 4);
+  KEEP (strlen (ma0_3_5_7[2][0][1].a5) < 4);
+
+  KEEP (strlen (ma0_3_5_7[1][1][0].a5) < 4);
+  KEEP (strlen (ma0_3_5_7[2][4][6].a5) < 4);
+
+  KEEP (strlen (ma0_3_5_7[0][0][0].a7_3[0]) < 2);
+  KEEP (strlen (ma0_3_5_7[2][4][6].a7_3[2]) < 2);
+
+  KEEP (strlen (ma0_3_5_7[0][0][0].a5_7[0]) < 6);
+  KEEP (strlen (ma0_3_5_7[2][4][6].a5_7[4]) < 6);
+}
+
+void keep_member_arrays_ptr (struct MemArrays0 *ma0,
+                            struct MemArraysX *max,
+                            struct MemArrays7 *ma7,
+                            int i)
+{
+  KEEP (strlen (ma0->a7_3[0]) > 0);
+  KEEP (strlen (ma0->a7_3[0]) < 2);
+  KEEP (strlen (ma0->a7_3[1]) < 2);
+  KEEP (strlen (ma0->a7_3[6]) < 2);
+  KEEP (strlen (ma0->a7_3[6]) < 2);
+  KEEP (strlen (ma0->a7_3[i]) > 0);
+  KEEP (strlen (ma0->a7_3[i]) < 2);
+  KEEP (strlen (ma0->a7_3[i]) < 2);
+
+  KEEP (strlen (ma0->a5_7[0]) < 5);
+  KEEP (strlen (ma0[0].a5_7[0]) < 5);
+  KEEP (strlen (ma0[1].a5_7[0]) < 5);
+  KEEP (strlen (ma0[9].a5_7[0]) < 5);
+  KEEP (strlen (ma0[9].a5_7[4]) < 5);
+  KEEP (strlen (ma0[i].a5_7[4]) < 5);
+  KEEP (strlen (ma0[i].a5_7[i]) < 5);
+
+  KEEP (strlen (ma0->a0) < DIFF_MAX - 2);
+  KEEP (strlen (ma0->a0) < 999);
+  KEEP (strlen (ma0->a0) < 1);
+
+  KEEP (strlen (max->ax) < DIFF_MAX - 2);
+  KEEP (strlen (max->ax) < 999);
+  KEEP (strlen (max->ax) < 1);
+
+  KEEP (strlen (ma7->a7) < DIFF_MAX - 2);
+  KEEP (strlen (ma7->a7) < 999);
+  KEEP (strlen (ma7->a7) < 1);
+}
+
+void keep_pointers (const char *s)
+{
+  KEEP (strlen (ptr) < DIFF_MAX - 2);
+  KEEP (strlen (ptr) < 999);
+  KEEP (strlen (ptr) < 1);
+
+  KEEP (strlen (s) < DIFF_MAX - 2);
+  KEEP (strlen (s) < 999);
+  KEEP (strlen (s) < 1);
+}
+
+
+/* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } }
+   { dg-final { scan-tree-dump-times "call_in_false_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\]" 92 "optimized" } }
+   { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 92 "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/strlenopt-41.c b/gcc/testsuite/gcc.dg/strlenopt-41.c
new file mode 100644 (file)
index 0000000..c5e8eb6
--- /dev/null
@@ -0,0 +1,34 @@
+/* PR tree-optimization/83671 - fix for false positive reported by
+   -Wstringop-overflow does not work with inlining
+   Verify that the length the empty string is folded to zero even at -O1
+   regardless of offset into it.
+   Also verify that the length of a non-empty string isn't folded given
+   a variable offset.
+   { dg-do compile }
+   { dg-options "-O1 -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+inline unsigned length (const char *s)
+{
+  return __builtin_strlen (s);
+}
+
+void check_length_cst (int i)
+{
+  unsigned len = length (&""[i]);
+
+  if (len)
+    __builtin_abort ();
+}
+
+void check_length_var (int i)
+{
+  unsigned len = length (&"1"[i]);
+
+  if (len != 1)
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-tree-dump-times "abort" 1 "optimized" } }
+   { dg-final { scan-tree-dump-times "strlen" 1 "optimized" } } */