PR tree-optimization/81384 - built-in form of strnlen missing
authorMartin Sebor <msebor@redhat.com>
Mon, 18 Jun 2018 16:32:59 +0000 (16:32 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Mon, 18 Jun 2018 16:32:59 +0000 (10:32 -0600)
gcc/ChangeLog:

PR tree-optimization/81384
* builtin-types.def (BT_FN_SIZE_CONST_STRING_SIZE): New.
* builtins.c (expand_builtin_strnlen): New function.
(expand_builtin): Call it.
(fold_builtin_n): Avoid setting TREE_NO_WARNING.
* builtins.def (BUILT_IN_STRNLEN): New.
* calls.c (maybe_warn_nonstring_arg): Handle BUILT_IN_STRNLEN.
Warn for bounds in excess of maximum object size.
* tree-ssa-strlen.c (maybe_set_strlen_range): Return tree representing
single-value ranges.  Handle strnlen.
(handle_builtin_strlen): Handle strnlen.
(strlen_check_and_optimize_stmt): Same.
* doc/extend.texi (Other Builtins): Document strnlen.

gcc/testsuite/ChangeLog:

PR tree-optimization/81384
* gcc.c-torture/execute/builtins/lib/strnlen.c: New test.
* gcc.c-torture/execute/builtins/strnlen-lib.c: New test.
* gcc.c-torture/execute/builtins/strnlen.c: New test.
* gcc.dg/attr-nonstring-2.c: New test.
* gcc.dg/attr-nonstring-3.c: New test.
* gcc.dg/attr-nonstring-4.c: New test.
* gcc.dg/strlenopt-45.c: New test.
* gcc.dg/strlenopt.h (strnlen):  Declare.

From-SVN: r261705

17 files changed:
gcc/ChangeLog
gcc/builtin-types.def
gcc/builtins.c
gcc/builtins.def
gcc/calls.c
gcc/doc/extend.texi
gcc/testsuite/ChangeLog
gcc/testsuite/c-c++-common/attr-nonstring-3.c
gcc/testsuite/gcc.c-torture/execute/builtins/lib/strnlen.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/strnlen-lib.c [new file with mode: 0644]
gcc/testsuite/gcc.c-torture/execute/builtins/strnlen.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-nonstring-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-nonstring-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/attr-nonstring-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/strlenopt-45.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/strlenopt.h
gcc/tree-ssa-strlen.c

index 56401fbf9b29438d572c0e539fca9555ccac2640..6a386d93691ef4444aa0791c829b7bd145621c10 100644 (file)
@@ -1,3 +1,19 @@
+2018-06-18  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/81384
+       * builtin-types.def (BT_FN_SIZE_CONST_STRING_SIZE): New.
+       * builtins.c (expand_builtin_strnlen): New function.
+       (expand_builtin): Call it.
+       (fold_builtin_n): Avoid setting TREE_NO_WARNING.
+       * builtins.def (BUILT_IN_STRNLEN): New.
+       * calls.c (maybe_warn_nonstring_arg): Handle BUILT_IN_STRNLEN.
+       Warn for bounds in excess of maximum object size.
+       * tree-ssa-strlen.c (maybe_set_strlen_range): Return tree representing
+       single-value ranges.  Handle strnlen.
+       (handle_builtin_strlen): Handle strnlen.
+       (strlen_check_and_optimize_stmt): Same.
+       * doc/extend.texi (Other Builtins): Document strnlen.
+
 2018-06-18  Prathamesh Kulkarni  <prathamesh.kulkarni@linaro.org>
 
        * tree.c (escaped_string::escape): Replace cast to char * by
index 845810c3736b00a0b8b9437299d57e06e30727eb..b01095c420fe486ecb8547dff0665b088aa9fc22 100644 (file)
@@ -318,6 +318,8 @@ DEF_FUNCTION_TYPE_2 (BT_FN_STRING_CONST_STRING_INT,
                     BT_STRING, BT_CONST_STRING, BT_INT)
 DEF_FUNCTION_TYPE_2 (BT_FN_STRING_CONST_STRING_SIZE,
                     BT_STRING, BT_CONST_STRING, BT_SIZE)
+DEF_FUNCTION_TYPE_2 (BT_FN_SIZE_CONST_STRING_SIZE,
+                    BT_SIZE, BT_CONST_STRING, BT_SIZE)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_STRING_FILEPTR,
                     BT_INT, BT_CONST_STRING, BT_FILEPTR)
 DEF_FUNCTION_TYPE_2 (BT_FN_INT_INT_FILEPTR,
index 6b3e6b2ea961f49e366a34ec10502ab5af316623..91658e84761b2f6573aa57cfaf920c66dca3b14d 100644 (file)
@@ -140,6 +140,7 @@ static rtx expand_builtin_memset (tree, rtx, machine_mode);
 static rtx expand_builtin_memset_args (tree, tree, tree, rtx, machine_mode, tree);
 static rtx expand_builtin_bzero (tree);
 static rtx expand_builtin_strlen (tree, rtx, machine_mode);
+static rtx expand_builtin_strnlen (tree, rtx, machine_mode);
 static rtx expand_builtin_alloca (tree);
 static rtx expand_builtin_unop (machine_mode, tree, rtx, rtx, optab);
 static rtx expand_builtin_frame_address (tree, tree);
@@ -2820,7 +2821,7 @@ expand_builtin_powi (tree exp, rtx target)
 }
 
 /* Expand expression EXP which is a call to the strlen builtin.  Return
-   NULL_RTX if we failed the caller should emit a normal call, otherwise
+   NULL_RTX if we failed and the caller should emit a normal call, otherwise
    try to get the result in TARGET, if convenient.  */
 
 static rtx
@@ -2925,6 +2926,74 @@ expand_builtin_strlen (tree exp, rtx target,
   return target;
 }
 
+/* Expand call EXP to the strnlen built-in, returning the result
+   and setting it in TARGET.  Otherwise return NULL_RTX on failure.  */
+
+static rtx
+expand_builtin_strnlen (tree exp, rtx target, machine_mode target_mode)
+{
+  if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE))
+    return NULL_RTX;
+
+  tree src = CALL_EXPR_ARG (exp, 0);
+  tree bound = CALL_EXPR_ARG (exp, 1);
+
+  if (!bound)
+    return NULL_RTX;
+
+  location_t loc = UNKNOWN_LOCATION;
+  if (EXPR_HAS_LOCATION (exp))
+    loc = EXPR_LOCATION (exp);
+
+  tree maxobjsize = max_object_size ();
+  tree func = get_callee_fndecl (exp);
+
+  tree len = c_strlen (src, 0);
+
+  if (TREE_CODE (bound) == INTEGER_CST)
+    {
+      if (!TREE_NO_WARNING (exp)
+         && tree_int_cst_lt (maxobjsize, bound)
+         && warning_at (loc, OPT_Wstringop_overflow_,
+                        "%K%qD specified bound %E "
+                        "exceeds maximum object size %E",
+                        exp, func, bound, maxobjsize))
+         TREE_NO_WARNING (exp) = true;
+
+      if (!len || TREE_CODE (len) != INTEGER_CST)
+       return NULL_RTX;
+
+      len = fold_convert_loc (loc, size_type_node, len);
+      len = fold_build2_loc (loc, MIN_EXPR, size_type_node, len, bound);
+      return expand_expr (len, target, target_mode, EXPAND_NORMAL);
+    }
+
+  if (TREE_CODE (bound) != SSA_NAME)
+    return NULL_RTX;
+
+  wide_int min, max;
+  enum value_range_type rng = get_range_info (bound, &min, &max);
+  if (rng != VR_RANGE)
+    return NULL_RTX;
+
+  if (!TREE_NO_WARNING (exp)
+      && wi::ltu_p (wi::to_wide (maxobjsize), min)
+      && warning_at (loc, OPT_Wstringop_overflow_,
+                    "%K%qD specified bound [%wu, %wu] "
+                    "exceeds maximum object size %E",
+                    exp, func, min.to_uhwi (), max.to_uhwi (), maxobjsize))
+      TREE_NO_WARNING (exp) = true;
+
+  if (!len || TREE_CODE (len) != INTEGER_CST)
+    return NULL_RTX;
+
+  if (wi::gtu_p (min, wi::to_wide (len)))
+    return expand_expr (len, target, target_mode, EXPAND_NORMAL);
+
+  len = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (len), len, bound);
+  return expand_expr (len, target, target_mode, EXPAND_NORMAL);
+}
+
 /* Callback routine for store_by_pieces.  Read GET_MODE_BITSIZE (MODE)
    bytes from constant string DATA + OFFSET and return it as target
    constant.  */
@@ -3121,20 +3190,27 @@ check_access (tree exp, tree, tree, tree dstwrite,
      object size.  */
   if (range[0] && tree_int_cst_lt (maxobjsize, range[0]))
     {
+      if (TREE_NO_WARNING (exp))
+       return false;
+
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
+      bool warned;
       if (range[0] == range[1])
-       warning_at (loc, opt,
-                   "%K%qD specified size %E "
-                   "exceeds maximum object size %E",
-                   exp, func, range[0], maxobjsize);
-         else
-           warning_at (loc, opt,
-                       "%K%qD specified size between %E and %E "
-                       "exceeds maximum object size %E",
-                       exp, func,
-                       range[0], range[1], maxobjsize);
+       warned = warning_at (loc, opt,
+                            "%K%qD specified size %E "
+                            "exceeds maximum object size %E",
+                            exp, func, range[0], maxobjsize);
+      else
+       warned = warning_at (loc, opt,
+                            "%K%qD specified size between %E and %E "
+                            "exceeds maximum object size %E",
+                            exp, func,
+                            range[0], range[1], maxobjsize);
+      if (warned)
+       TREE_NO_WARNING (exp) = true;
+
       return false;
     }
 
@@ -6963,6 +7039,12 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode,
        return target;
       break;
 
+    case BUILT_IN_STRNLEN:
+      target = expand_builtin_strnlen (exp, target, target_mode);
+      if (target)
+       return target;
+      break;
+
     case BUILT_IN_STRCAT:
       target = expand_builtin_strcat (exp, target);
       if (target)
@@ -9174,7 +9256,6 @@ fold_builtin_n (location_t loc, tree fndecl, tree *args, int nargs, bool)
     {
       ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
       SET_EXPR_LOCATION (ret, loc);
-      TREE_NO_WARNING (ret) = 1;
       return ret;
     }
   return NULL_TREE;
index 9776dfabddf9cb101a0ff2405ff762e75bc02732..aacbd513a16692b2733aabc1cee2854d8d3eb99b 100644 (file)
@@ -709,6 +709,7 @@ DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNCASECMP, "strncasecmp", BT_FN_INT_CONST_STR
 DEF_LIB_BUILTIN        (BUILT_IN_STRNCAT, "strncat", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRNCMP, "strncmp", BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRNCPY, "strncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF)
+DEF_EXT_LIB_BUILTIN    (BUILT_IN_STRNLEN, "strnlen", BT_FN_SIZE_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRPBRK, "strpbrk", BT_FN_STRING_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRRCHR, "strrchr", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF)
 DEF_LIB_BUILTIN        (BUILT_IN_STRSPN, "strspn", BT_FN_SIZE_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF)
index 3311d1cbe2b335678245f66ed04f3455b101186b..02562ddc1ee87029e0ce59b6f965eb5e89f5c5a9 100644 (file)
@@ -1613,6 +1613,9 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
   if (!fndecl || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL)
     return;
 
+  if (TREE_NO_WARNING (exp))
+    return;
+
   unsigned nargs = call_expr_nargs (exp);
 
   /* The bound argument to a bounded string function like strncpy.  */
@@ -1647,18 +1650,23 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
 
     case BUILT_IN_STPNCPY:
     case BUILT_IN_STRNCPY:
-      {
-       unsigned argno = 2;
-       if (argno < nargs)
-         bound = CALL_EXPR_ARG (exp, argno);
-       break;
-      }
+      if (2 < nargs)
+       bound = CALL_EXPR_ARG (exp, 2);
+      break;
 
     case BUILT_IN_STRNDUP:
+      if (1 < nargs)
+       bound = CALL_EXPR_ARG (exp, 1);
+      break;
+
+    case BUILT_IN_STRNLEN:
       {
-       unsigned argno = 1;
-       if (argno < nargs)
-         bound = CALL_EXPR_ARG (exp, argno);
+       tree arg = CALL_EXPR_ARG (exp, 0);
+       if (!get_attr_nonstring_decl (arg))
+         get_range_strlen (arg, lenrng);
+
+       if (1 < nargs)
+         bound = CALL_EXPR_ARG (exp, 1);
        break;
       }
 
@@ -1674,6 +1682,29 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
       get_size_range (bound, bndrng);
     }
 
+  location_t loc = EXPR_LOCATION (exp);
+
+  if (bndrng[0])
+    {
+      /* Diagnose excessive bound prior the adjustment below and
+        regardless of attribute nonstring.  */
+      tree maxobjsize = max_object_size ();
+      if (tree_int_cst_lt (maxobjsize, bndrng[0]))
+       {
+         if (tree_int_cst_equal (bndrng[0], bndrng[1]))
+           warning_at (loc, OPT_Wstringop_overflow_,
+                       "%K%qD specified bound %E "
+                       "exceeds maximum object size %E",
+                       exp, fndecl, bndrng[0], maxobjsize);
+         else
+           warning_at (loc, OPT_Wstringop_overflow_,
+                       "%K%qD specified bound [%E, %E] "
+                       "exceeds maximum object size %E",
+                       exp, fndecl, bndrng[0], bndrng[1], maxobjsize);
+         return;
+       }
+    }
+
   if (*lenrng)
     {
       /* Add one for the nul.  */
@@ -1766,8 +1797,6 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp)
       else if (bound == void_type_node)
        bound = NULL_TREE;
 
-      location_t loc = EXPR_LOCATION (exp);
-
       bool warned = false;
 
       if (wi::ltu_p (asize, wibnd))
index ca96c083f9fbed7907ae458776e26ad4f58b872f..cf88175e7959a62fbc1772196f11cee9296a223a 100644 (file)
@@ -11255,6 +11255,7 @@ is called and the @var{flag} argument passed to it.
 @findex strncmp
 @findex strncpy
 @findex strndup
+@findex strnlen
 @findex strpbrk
 @findex strrchr
 @findex strspn
@@ -11338,8 +11339,8 @@ Outside strict ISO C mode (@option{-ansi}, @option{-std=c90},
 @code{significandl}, @code{significand}, @code{sincosf},
 @code{sincosl}, @code{sincos}, @code{stpcpy}, @code{stpncpy},
 @code{strcasecmp}, @code{strdup}, @code{strfmon}, @code{strncasecmp},
-@code{strndup}, @code{toascii}, @code{y0f}, @code{y0l}, @code{y0},
-@code{y1f}, @code{y1l}, @code{y1}, @code{ynf}, @code{ynl} and
+@code{strndup}, @code{strnlen}, @code{toascii}, @code{y0f}, @code{y0l},
+@code{y0}, @code{y1f}, @code{y1l}, @code{y1}, @code{ynf}, @code{ynl} and
 @code{yn}
 may be handled as built-in functions.
 All these functions have corresponding versions
index 5672f8b886edfd9a832ca7d8cdb38779c4a8b08f..024cf6b2437e79b34b3699621b10e31247f8d781 100644 (file)
@@ -1,3 +1,15 @@
+2018-06-18  Martin Sebor  <msebor@redhat.com>
+
+       PR tree-optimization/81384
+       * gcc.c-torture/execute/builtins/lib/strnlen.c: New test.
+       * gcc.c-torture/execute/builtins/strnlen-lib.c: New test.
+       * gcc.c-torture/execute/builtins/strnlen.c: New test.
+       * gcc.dg/attr-nonstring-2.c: New test.
+       * gcc.dg/attr-nonstring-3.c: New test.
+       * gcc.dg/attr-nonstring-4.c: New test.
+       * gcc.dg/strlenopt-45.c: New test.
+       * gcc.dg/strlenopt.h (strnlen):  Declare.
+
 2018-06-18  Wilco Dijkstra  <wdijkstr@arm.com>
 
        PR tree-optimization/86076
index a54e73a3285b5f4c3d235d60e10743c6a8808b23..b1eb8b63004268d0605ac80c38e8f6778099c2a3 100644 (file)
@@ -66,7 +66,7 @@ struct MemArrays
 void sink (int, ...);
 
 
-#define T(call)  sink (0, (call))
+#define T(call)  sink (0, call)
 
 void test_printf (struct MemArrays *p)
 {
@@ -250,14 +250,14 @@ void test_stpncpy_warn (struct MemArrays *p, unsigned n)
 
   T (stpncpy (ptr, str, N + 1));
   T (stpncpy (ptr, arr, N + 1));          /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */
-  T (stpncpy (arr, str, N + 1));          /* { dg-warning "writing 5 bytes into a region of size 4 overflows " "bug 82609" { xfail c++ } } */
+  T (stpncpy (arr, str, N + 1));          /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */
 
   T (stpncpy (ptr, ptr, N + 1));
   T (stpncpy (ptr, parr, N + 1));
   T (stpncpy (parr, str, N + 1));
 
   T (stpncpy (ptr, p->arr, N + 1));       /* { dg-warning "argument 2 declared attribute .nonstring. is smaller" } */
-  T (stpncpy (p->arr, p->str, N + 1));    /* { dg-warning "writing 5 bytes into a region of size 4 overflows" "bug 82609" { xfail c++ } } */
+  T (stpncpy (p->arr, p->str, N + 1));    /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */
   T (stpncpy (p->parr, p->str, N + 1));
 }
 
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strnlen.c b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strnlen.c
new file mode 100644 (file)
index 0000000..73ada14
--- /dev/null
@@ -0,0 +1,22 @@
+typedef __SIZE_TYPE__ size_t;
+
+extern void abort (void);
+extern int inside_main;
+
+__attribute__ ((__noinline__))
+size_t
+strnlen (const char *s, size_t n)
+{
+  size_t i;
+
+#ifdef __OPTIMIZE__
+  if (inside_main)
+    abort ();
+#endif
+
+  i = 0;
+  while (s[i] != 0 && n--)
+    i++;
+
+  return i;
+}
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen-lib.c
new file mode 100644 (file)
index 0000000..bf91940
--- /dev/null
@@ -0,0 +1 @@
+#include "lib/strnlen.c"
diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen.c
new file mode 100644 (file)
index 0000000..2368d06
--- /dev/null
@@ -0,0 +1,95 @@
+/* PR tree-optimization/81384 - built-in form of strnlen missing
+   Test to verify that strnlen built-in expansion works correctly
+   in the absence of tree strlen optimization.
+   { dg-do run }
+   { do-options "-O2 -fno-tree-strlen" }  */
+
+#define PTRDIFF_MAX __PTRDIFF_MAX__
+#define SIZE_MAX    __SIZE_MAX__
+#define NOIPA __attribute__ ((noipa))
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void abort (void);
+extern size_t strnlen (const char *, size_t);
+
+#define A(expr)                                                        \
+  ((expr) ? (void)0                                            \
+   : (__builtin_printf ("assertion on line %i failed: %s\n",   \
+                       __LINE__, #expr),                       \
+      abort ()))
+
+NOIPA void test_strnlen_str_cst (void)
+{
+  A (strnlen ("", 0) == 0);
+  A (strnlen ("", 1) == 0);
+  A (strnlen ("", 9) == 0);
+  A (strnlen ("", PTRDIFF_MAX) == 0);
+  A (strnlen ("", SIZE_MAX) == 0);
+  A (strnlen ("", -1) == 0);
+
+  A (strnlen ("1", 0) == 0);
+  A (strnlen ("1", 1) == 1);
+  A (strnlen ("1", 9) == 1);
+  A (strnlen ("1", PTRDIFF_MAX) == 1);
+  A (strnlen ("1", SIZE_MAX) == 1);
+  A (strnlen ("1", -2) == 1);
+
+  A (strnlen ("123", 0) == 0);
+  A (strnlen ("123", 1) == 1);
+  A (strnlen ("123", 2) == 2);
+  A (strnlen ("123", 3) == 3);
+  A (strnlen ("123", 9) == 3);
+  A (strnlen ("123", PTRDIFF_MAX) == 3);
+  A (strnlen ("123", SIZE_MAX) == 3);
+  A (strnlen ("123", -2) == 3);
+}
+
+NOIPA void test_strnlen_str_range (size_t x)
+{
+  size_t r_0_3 = x & 3;
+  size_t r_1_3 = r_0_3 | 1;
+  size_t r_2_3 = r_0_3 | 2;
+
+  A (strnlen ("",     r_0_3) == 0);
+  A (strnlen ("1",    r_0_3) <= 1);
+  A (strnlen ("12",   r_0_3) <= 2);
+  A (strnlen ("123",  r_0_3) <= 3);
+  A (strnlen ("1234", r_0_3) <= 3);
+
+  A (strnlen ("",     r_1_3) == 0);
+  A (strnlen ("1",    r_1_3) == 1);
+  A (strnlen ("12",   r_1_3) <= 2);
+  A (strnlen ("123",  r_1_3) <= 3);
+  A (strnlen ("1234", r_1_3) <= 3);
+
+  A (strnlen ("",     r_2_3) == 0);
+  A (strnlen ("1",    r_2_3) == 1);
+  A (strnlen ("12",   r_2_3) == 2);
+  A (strnlen ("123",  r_2_3) <= 3);
+  A (strnlen ("1234", r_2_3) <= 3);
+}
+
+NOIPA void test_strnlen_str_range_side_effect (size_t x)
+{
+  size_t r_0_3 = x & 3;
+  size_t r_1_3 = r_0_3 | 1;
+  size_t r_2_3 = r_0_3 | 2;
+  size_t n = r_2_3;
+
+  int i = 0;
+
+  A (strnlen ("1234" + i++, n) <= 3);
+  A (i == 1);
+
+  A (strnlen ("1234", n++) <= 3);
+  A (n == r_2_3 + 1);
+}
+
+void
+main_test (void)
+{
+  test_strnlen_str_cst ();
+  test_strnlen_str_range ((size_t)"");
+  test_strnlen_str_range_side_effect ((size_t)"");
+}
diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-2.c b/gcc/testsuite/gcc.dg/attr-nonstring-2.c
new file mode 100644 (file)
index 0000000..e9edb66
--- /dev/null
@@ -0,0 +1,115 @@
+/* PR middle-end/81384 - built-in form of strnlen missing
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+extern void* memcpy (void*, const void*, size_t);
+extern size_t strnlen (const char*, size_t);
+
+#define NONSTRING __attribute__ ((nonstring))
+
+#define _CAT(s, n)   s ## n
+#define CAT(s, n)    _CAT (s, n)
+#define UNIQ(n)      CAT (n, __LINE__)
+
+void sink (size_t, ...);
+
+#define T(expr)   sink (expr)
+
+void test_strnlen_array_cst (void)
+{
+  NONSTRING char ns3[3];
+  sink (0, ns3);    // "initialize" ns3
+
+  T (strnlen (ns3, 0));
+  T (strnlen (ns3, 1));
+  T (strnlen (ns3, 2));
+  T (strnlen (ns3, 3));
+  T (strnlen (ns3, 4));             /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" } */
+  T (strnlen (ns3, DIFF_MAX));      /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */
+  T (strnlen (ns3, SIZE_MAX));      /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  NONSTRING char ns5[5];
+  sink (0, ns5);
+
+  T (strnlen (ns5, 0));
+  T (strnlen (ns5, 1));
+  T (strnlen (ns5, 2));
+  T (strnlen (ns5, 3));
+  T (strnlen (ns5, 6));             /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 6" } */
+  T (strnlen (ns5, DIFF_MAX));      /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */
+  T (strnlen (ns5, SIZE_MAX));      /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+}
+
+
+void test_strnlen_array_range (void)
+{
+  NONSTRING char ns3[3];
+  sink (0, ns3);    // "initialize" ns3
+
+  T (strnlen (ns3, UR (0, 3)));
+  T (strnlen (ns3, UR (0, 9)));
+  T (strnlen (ns3, UR (3, 4)));
+  T (strnlen (ns3, UR (3, DIFF_MAX)));
+  T (strnlen (ns3, UR (4, 5)));     /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */
+  T (strnlen (ns3, UR (DIFF_MAX, SIZE_MAX)));  /* { dg-warning "argument 1 declared attribute .nonstring. is smaller " } */
+}
+
+
+#undef T
+#define T(N, init, nelts, bound)                       \
+  do {                                                 \
+    extern NONSTRING char UNIQ (arr)[N];               \
+    memcpy (UNIQ (arr), init, nelts);                  \
+    sink (strnlen (UNIQ (arr), bound), UNIQ (arr));    \
+  } while (0)
+
+void test_strnlen_string_cst (void)
+{
+  T (3, "1",   2, 1);
+  T (3, "1",   2, 2);
+  T (3, "1",   2, 3);
+  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 (5, "1",   2, 1);
+  T (5, "1",   2, 2);
+  T (5, "1",   2, 9);
+
+  T (5, "12",  3, 1);
+  T (5, "12",  3, 9);
+  T (5, "123", 3, 1);
+  T (5, "123", 3, 5);
+  T (5, "123", 3, 6);               /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 6" } */
+
+  /* Strnlen shouldn't trigger a warning for arrays of unknown size
+     (except for accesses to uninitialized elements when those are
+     detected).  */
+  T (/* [] */, "1", 1, 1);
+  T (/* [] */, "1", 1, 2);
+  T (/* [] */, "1", 2, 1);
+  T (/* [] */, "1", 2, 2);
+  T (/* [] */, "1", 2, 3);
+  T (/* [] */, "1", 2, 9);
+  T (/* [] */, "1", 2, DIFF_MAX);
+  T (/* [] */, "1", 2, SIZE_MAX);
+
+  size_t n = DIFF_MAX;
+  T (/* [] */, "123", 3, n);
+  T (/* [] */, "123", 3, n + 1);    /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size " } */
+  n = SIZE_MAX;
+  T (/* [] */, "123", 3, n);        /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size " } */
+}
+
+
+void test_strnlen_string_range (void)
+{
+  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" } */
+  T (3, "123", 3, UR (5, 9));       /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */
+}
diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-3.c b/gcc/testsuite/gcc.dg/attr-nonstring-3.c
new file mode 100644 (file)
index 0000000..4a10450
--- /dev/null
@@ -0,0 +1,117 @@
+/* PR middle-end/81384 - built-in form of strnlen missing
+
+   Since the strnlen patch affects the handling for strncmp and other
+   bounded functions, verify that a bound in excess of the maximum
+   object size specified for strncmp is diagnosed regardless of
+   attribute nonstring.  Also verify that a bound that's greater than
+   the size of a non-string array is diagnosed, even if it's not in
+   excess of the maximum object size.
+
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+extern int strncmp (const char*, const char*, size_t);
+
+#define STR   /* not non-string */
+#define NS    __attribute__ ((nonstring))
+
+#define _CAT(s, n)   s ## n
+#define CAT(s, n)    _CAT (s, n)
+#define UNIQ(n)      CAT (n, __LINE__)
+
+void sink (int);
+
+#define T(at1, N1, at2, N2, bound)             \
+  do {                                         \
+    extern at1 char UNIQ (a)[N1];              \
+    extern at2 char UNIQ (b)[N2];              \
+    sink (strncmp (UNIQ (a), UNIQ (b), bound));        \
+  } while (0)
+
+void strncmp_cst (void)
+{
+  size_t n = DIFF_MAX;
+
+  T (STR, /* [] */, STR, /* [] */, n);
+  T (STR, /* [] */, STR, /* [] */, n + 1);    /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, 1, STR, /* [] */, n);
+  T (STR, 2, STR, /* [] */, n + 1);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, /* [] */, STR, 3, n);
+  T (STR, /* [] */, STR, 4, n + 1);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, /* [] */, NS, /* [] */, n);
+  T (STR, /* [] */, NS, /* [] */, n + 1);     /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, 5, NS, /* [] */, n);
+  T (STR, 6, NS, /* [] */, n + 1);            /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, /* [] */, NS, 7, n);                /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */
+
+  T (STR, /* [] */, NS, 8, n + 1);            /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, /* [] */, STR, /* [] */, n);
+  T (NS, /* [] */, STR, /* [] */, n + 1);     /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, 9, STR, /* [] */, n);                /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, 10, STR, /* [] */, n + 1);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, /* [] */, STR, 11, n);
+  T (NS, /* [] */, STR, 12, n + 1);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, /* [] */, NS, /* [] */, n);
+  T (NS, /* [] */, NS, /* [] */, n + 1);      /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, 13, NS, /* [] */, n);                /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, 14, NS, /* [] */, n + 1);            /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, /* [] */, NS, 15, n);                /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, /* [] */, NS, 16, n + 1);            /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+}
+
+
+void strncmp_range (void)
+{
+  size_t n = DIFF_MAX;
+  n = UR (n, n + 1);
+
+  T (STR, /* [] */, STR, /* [] */, n);
+  T (STR, /* [] */, STR, /* [] */, n + 1);    /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, 1, STR, /* [] */, n);
+  T (STR, 2, STR, /* [] */, n + 1);           /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, /* [] */, STR, 3, n);
+  T (STR, /* [] */, STR, 4, n + 1);           /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, /* [] */, NS, /* [] */, n);
+  T (STR, /* [] */, NS, /* [] */, n + 1);     /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, 5, NS, /* [] */, n);
+  T (STR, 6, NS, /* [] */, n + 1);            /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, /* [] */, NS, 7, n);                /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */
+
+  T (STR, /* [] */, NS, 8, n + 1);            /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, /* [] */, STR, /* [] */, n);
+  T (NS, /* [] */, STR, /* [] */, n + 1);     /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, 9, STR, /* [] */, n);                /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, 10, STR, /* [] */, n + 1);           /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, /* [] */, STR, 11, n);
+  T (NS, /* [] */, STR, 12, n + 1);           /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, /* [] */, NS, /* [] */, n);
+  T (NS, /* [] */, NS, /* [] */, n + 1);      /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, 13, NS, /* [] */, n);                /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, 14, NS, /* [] */, n + 1);            /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, /* [] */, NS, 15, n);                /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, /* [] */, NS, 16, n + 1);            /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+}
diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-4.c b/gcc/testsuite/gcc.dg/attr-nonstring-4.c
new file mode 100644 (file)
index 0000000..7daff97
--- /dev/null
@@ -0,0 +1,64 @@
+/* PR middle-end/81384 - built-in form of strnlen missing
+
+   Verify that a strnlen bound in excess of the maximum object size
+   is diagnosed regardless of attribute nonstring.  Also verify that
+   a bound that's greater than the size of a non-string array is
+   diagnosed, even if it's not in excess of the maximum object size.
+
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+extern size_t strnlen (const char*, size_t);
+
+#define STR   /* not non-string */
+#define NS    __attribute__ ((nonstring))
+
+#define _CAT(s, n)   s ## n
+#define CAT(s, n)    _CAT (s, n)
+#define UNIQ(n)      CAT (n, __LINE__)
+
+void sink (size_t);
+
+#define T(attr, N, bound)                      \
+  do {                                         \
+    extern attr char UNIQ (a)[N];              \
+    sink (strnlen (UNIQ (a), bound));          \
+  } while (0)
+
+void strnlen_cst (void)
+{
+  size_t n = DIFF_MAX;
+
+  T (STR, /* [] */, n);
+  T (STR, /* [] */, n + 1);    /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, 1, n);
+  T (STR, 2, n + 1);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, /* [] */, n);
+  T (NS, /* [] */, n + 1);     /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, 9, n);                /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, 10, n + 1);           /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */
+}
+
+
+void strnlen_range (void)
+{
+  size_t n = DIFF_MAX;
+  n = UR (n, n + 1);
+
+  T (STR, /* [] */, n);
+  T (STR, /* [] */, n + 1);    /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (STR, 1, n);
+  T (STR, 2, n + 1);           /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, /* [] */, n);
+  T (NS, /* [] */, n + 1);     /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+
+  T (NS, 9, n);                /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */
+  T (NS, 10, n + 1);           /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */
+}
diff --git a/gcc/testsuite/gcc.dg/strlenopt-45.c b/gcc/testsuite/gcc.dg/strlenopt-45.c
new file mode 100644 (file)
index 0000000..bd9b197
--- /dev/null
@@ -0,0 +1,335 @@
+/* PR tree-optimization/81384 - built-in form of strnlen missing
+   Test to verify that strnlen built-in expansion works correctly
+   in the absence of tree strlen optimization.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -fdump-tree-optimized" } */
+
+#include "strlenopt.h"
+
+#define PTRDIFF_MAX __PTRDIFF_MAX__
+#define SIZE_MAX    __SIZE_MAX__
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void abort (void);
+extern size_t strnlen (const char *, size_t);
+
+#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 funcation 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
+
+/* 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)
+
+extern char c;
+extern char a1[1];
+extern char a3[3];
+extern char a5[5];
+extern char a3_7[3][7];
+extern char ax[];
+
+void elim_strnlen_arr_cst (void)
+{
+  /* The length of a string stored in a one-element array must be zero.
+     The result reported by strnlen() for such an array can be non-zero
+     only when the bound is equal to 1 (in which case the result must
+     be one).  */
+  ELIM (strnlen (&c, 0) == 0);
+  ELIM (strnlen (&c, 1) < 2);
+  ELIM (strnlen (&c, 2) == 0);
+  ELIM (strnlen (&c, 9) == 0);
+  ELIM (strnlen (&c, PTRDIFF_MAX) == 0);
+  ELIM (strnlen (&c, SIZE_MAX) == 0);
+  ELIM (strnlen (&c, -1) == 0);
+
+  ELIM (strnlen (a1, 0) == 0);
+  ELIM (strnlen (a1, 1) < 2);
+  ELIM (strnlen (a1, 2) == 0);
+  ELIM (strnlen (a1, 9) == 0);
+  ELIM (strnlen (a1, PTRDIFF_MAX) == 0);
+  ELIM (strnlen (a1, SIZE_MAX) == 0);
+  ELIM (strnlen (a1, -1) == 0);
+
+  ELIM (strnlen (a3, 0) == 0);
+  ELIM (strnlen (a3, 1) < 2);
+  ELIM (strnlen (a3, 2) < 3);
+  ELIM (strnlen (a3, 3) < 4);
+  ELIM (strnlen (a3, 9) < 4);
+  ELIM (strnlen (a3, PTRDIFF_MAX) < 4);
+  ELIM (strnlen (a3, SIZE_MAX) < 4);
+  ELIM (strnlen (a3, -1) < 4);
+
+  ELIM (strnlen (a3_7[0], 0) == 0);
+  ELIM (strnlen (a3_7[0], 1) < 2);
+  ELIM (strnlen (a3_7[0], 2) < 3);
+  ELIM (strnlen (a3_7[0], 3) < 4);
+  ELIM (strnlen (a3_7[0], 9) < 8);
+  ELIM (strnlen (a3_7[0], PTRDIFF_MAX) < 8);
+  ELIM (strnlen (a3_7[0], SIZE_MAX) < 8);
+  ELIM (strnlen (a3_7[0], -1) < 8);
+
+  ELIM (strnlen (a3_7[2], 0) == 0);
+  ELIM (strnlen (a3_7[2], 1) < 2);
+  ELIM (strnlen (a3_7[2], 2) < 3);
+  ELIM (strnlen (a3_7[2], 3) < 4);
+  ELIM (strnlen (a3_7[2], 9) < 8);
+  ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < 8);
+  ELIM (strnlen (a3_7[2], SIZE_MAX) < 8);
+  ELIM (strnlen (a3_7[2], -1) < 8);
+
+  ELIM (strnlen ((char*)a3_7, 0) == 0);
+  ELIM (strnlen ((char*)a3_7, 1) < 2);
+  ELIM (strnlen ((char*)a3_7, 2) < 3);
+  ELIM (strnlen ((char*)a3_7, 3) < 4);
+  ELIM (strnlen ((char*)a3_7, 9) < 10);
+  ELIM (strnlen ((char*)a3_7, 19) < 20);
+  ELIM (strnlen ((char*)a3_7, 21) < 22);
+  ELIM (strnlen ((char*)a3_7, 23) < 22);
+  ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) < 22);
+  ELIM (strnlen ((char*)a3_7, SIZE_MAX) < 22);
+  ELIM (strnlen ((char*)a3_7, -1) < 22);
+
+  ELIM (strnlen (ax, 0) == 0);
+  ELIM (strnlen (ax, 1) < 2);
+  ELIM (strnlen (ax, 2) < 3);
+  ELIM (strnlen (ax, 9) < 10);
+  ELIM (strnlen (a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
+  ELIM (strnlen (a3, SIZE_MAX) < PTRDIFF_MAX);
+  ELIM (strnlen (a3, -1) < PTRDIFF_MAX);
+}
+
+struct MemArrays
+{
+  char c;
+  char a0[0];
+  char a1[1];
+  char a3[3];
+  char a5[5];
+  char a3_7[3][7];
+  char ax[1];
+};
+
+void elim_strnlen_memarr_cst (struct MemArrays *p, int i)
+{
+  ELIM (strnlen (&p->c, 0) == 0);
+  ELIM (strnlen (&p->c, 1) < 2);
+  ELIM (strnlen (&p->c, 9) == 0);
+  ELIM (strnlen (&p->c, PTRDIFF_MAX) == 0);
+  ELIM (strnlen (&p->c, SIZE_MAX) == 0);
+  ELIM (strnlen (&p->c, -1) == 0);
+
+  /* Other accesses to internal zero-length arrays are undefined.  */
+  ELIM (strnlen (p->a0, 0) == 0);
+
+  ELIM (strnlen (p->a1, 0) == 0);
+  ELIM (strnlen (p->a1, 1) < 2);
+  ELIM (strnlen (p->a1, 9) == 0);
+  ELIM (strnlen (p->a1, PTRDIFF_MAX) == 0);
+  ELIM (strnlen (p->a1, SIZE_MAX) == 0);
+  ELIM (strnlen (p->a1, -1) == 0);
+
+  ELIM (strnlen (p->a3, 0) == 0);
+  ELIM (strnlen (p->a3, 1) < 2);
+  ELIM (strnlen (p->a3, 2) < 3);
+  ELIM (strnlen (p->a3, 3) < 4);
+  ELIM (strnlen (p->a3, 9) < 4);
+  ELIM (strnlen (p->a3, PTRDIFF_MAX) < 4);
+  ELIM (strnlen (p->a3, SIZE_MAX) < 4);
+  ELIM (strnlen (p->a3, -1) < 4);
+
+  ELIM (strnlen (p[i].a3, 0) == 0);
+  ELIM (strnlen (p[i].a3, 1) < 2);
+  ELIM (strnlen (p[i].a3, 2) < 3);
+  ELIM (strnlen (p[i].a3, 3) < 4);
+  ELIM (strnlen (p[i].a3, 9) < 4);
+  ELIM (strnlen (p[i].a3, PTRDIFF_MAX) < 4);
+  ELIM (strnlen (p[i].a3, SIZE_MAX) < 4);
+  ELIM (strnlen (p[i].a3, -1) < 4);
+
+  ELIM (strnlen (p->a3_7[0], 0) == 0);
+  ELIM (strnlen (p->a3_7[0], 1) < 2);
+  ELIM (strnlen (p->a3_7[0], 2) < 3);
+  ELIM (strnlen (p->a3_7[0], 3) < 4);
+  ELIM (strnlen (p->a3_7[0], 9) < 8);
+  ELIM (strnlen (p->a3_7[0], PTRDIFF_MAX) < 8);
+  ELIM (strnlen (p->a3_7[0], SIZE_MAX) < 8);
+  ELIM (strnlen (p->a3_7[0], -1) < 8);
+
+  ELIM (strnlen (p->a3_7[2], 0) == 0);
+  ELIM (strnlen (p->a3_7[2], 1) < 2);
+  ELIM (strnlen (p->a3_7[2], 2) < 3);
+  ELIM (strnlen (p->a3_7[2], 3) < 4);
+  ELIM (strnlen (p->a3_7[2], 9) < 8);
+  ELIM (strnlen (p->a3_7[2], PTRDIFF_MAX) < 8);
+  ELIM (strnlen (p->a3_7[2], SIZE_MAX) < 8);
+  ELIM (strnlen (p->a3_7[2], -1) < 8);
+
+  ELIM (strnlen (p->a3_7[i], 0) == 0);
+  ELIM (strnlen (p->a3_7[i], 1) < 2);
+  ELIM (strnlen (p->a3_7[i], 2) < 3);
+  ELIM (strnlen (p->a3_7[i], 3) < 4);
+
+#if 0
+  /* This is tranformed into strnlen ((char*)p + offsetof (a3_7[i]), N)
+     which makes it impssible to determine the size of the array.  */
+  ELIM (strnlen (p->a3_7[i], 9) < 8);
+  ELIM (strnlen (p->a3_7[i], PTRDIFF_MAX) < 8);
+  ELIM (strnlen (p->a3_7[i], SIZE_MAX) < 8);
+  ELIM (strnlen (p->a3_7[i], -1) < 8);
+#else
+  ELIM (strnlen (p->a3_7[i], 9) < 10);
+  ELIM (strnlen (p->a3_7[i], 19) < 20);
+#endif
+
+  ELIM (strnlen ((char*)p->a3_7, 0) == 0);
+  ELIM (strnlen ((char*)p->a3_7, 1) < 2);
+  ELIM (strnlen ((char*)p->a3_7, 2) < 3);
+  ELIM (strnlen ((char*)p->a3_7, 3) < 4);
+  ELIM (strnlen ((char*)p->a3_7, 9) < 10);
+  ELIM (strnlen ((char*)p->a3_7, 19) < 20);
+  ELIM (strnlen ((char*)p->a3_7, 21) < 22);
+  ELIM (strnlen ((char*)p->a3_7, 23) < 22);
+  ELIM (strnlen ((char*)p->a3_7, PTRDIFF_MAX) < 22);
+  ELIM (strnlen ((char*)p->a3_7, SIZE_MAX) < 22);
+  ELIM (strnlen ((char*)p->a3_7, -1) < 22);
+
+  ELIM (strnlen (p->ax, 0) == 0);
+  ELIM (strnlen (p->ax, 1) < 2);
+  ELIM (strnlen (p->ax, 2) < 3);
+  ELIM (strnlen (p->ax, 9) < 10);
+  ELIM (strnlen (p->a3, PTRDIFF_MAX) <= PTRDIFF_MAX);
+  ELIM (strnlen (p->a3, SIZE_MAX) < PTRDIFF_MAX);
+  ELIM (strnlen (p->a3, -1) < PTRDIFF_MAX);
+}
+
+
+void elim_strnlen_str_cst (void)
+{
+  const char *s0 = "";
+  const char *s1 = "1";
+  const char *s3 = "123";
+
+  ELIM (strnlen (s0, 0) == 0);
+  ELIM (strnlen (s0, 1) == 0);
+  ELIM (strnlen (s0, 9) == 0);
+  ELIM (strnlen (s0, PTRDIFF_MAX) == 0);
+  ELIM (strnlen (s0, SIZE_MAX) == 0);
+  ELIM (strnlen (s0, -1) == 0);
+
+  ELIM (strnlen (s1, 0) == 0);
+  ELIM (strnlen (s1, 1) == 1);
+  ELIM (strnlen (s1, 9) == 1);
+  ELIM (strnlen (s1, PTRDIFF_MAX) == 1);
+  ELIM (strnlen (s1, SIZE_MAX) == 1);
+  ELIM (strnlen (s1, -2) == 1);
+
+  ELIM (strnlen (s3, 0) == 0);
+  ELIM (strnlen (s3, 1) == 1);
+  ELIM (strnlen (s3, 2) == 2);
+  ELIM (strnlen (s3, 3) == 3);
+  ELIM (strnlen (s3, 9) == 3);
+  ELIM (strnlen (s3, PTRDIFF_MAX) == 3);
+  ELIM (strnlen (s3, SIZE_MAX) == 3);
+  ELIM (strnlen (s3, -2) == 3);
+}
+
+void elim_strnlen_range (char *s)
+{
+  const char *s0 = "";
+  const char *s1 = "1";
+  const char *s3 = "123";
+
+  size_t n_0_1 = (size_t)s & 1;
+  size_t n_0_2 = ((size_t)s & 3) < 3 ? ((size_t)s & 3) : 2;
+  size_t n_0_3 = (size_t)s & 3;
+  size_t n_1_2 = n_0_1 + 1;
+
+  ELIM (strnlen (s0, n_0_1) == 0);
+  ELIM (strnlen (s0, n_0_2) == 0);
+  ELIM (strnlen (s0, n_1_2) == 0);
+
+  ELIM (strnlen (s1, n_0_1) < 2);
+  ELIM (strnlen (s1, n_0_2) < 2);
+  ELIM (strnlen (s1, n_0_3) < 2);
+
+  ELIM (strnlen (s1, n_1_2) > 0);
+  ELIM (strnlen (s1, n_1_2) < 2);
+
+  ELIM (strnlen (s3, n_0_1) < 2);
+  ELIM (strnlen (s3, n_0_2) < 3);
+  ELIM (strnlen (s3, n_0_3) < 4);
+
+  ELIM (strnlen (s3, n_1_2) > 0);
+  ELIM (strnlen (s3, n_1_2) < 4);
+}
+
+
+#line 1000
+
+void keep_strnlen_arr_cst (void)
+{
+  KEEP (strnlen (&c, 1) == 0);
+  KEEP (strnlen (&c, 1) == 1);
+
+  KEEP (strnlen (a1, 1) == 0);
+  KEEP (strnlen (a1, 1) == 1);
+
+  KEEP (strnlen (ax, 9) < 9);
+}
+
+struct FlexArrays
+{
+  char c;
+  char a0[0];   /* Access to internal zero-length arrays are undefined.  */
+  char a1[1];
+};
+
+void keep_strnlen_memarr_cst (struct FlexArrays *p)
+{
+  KEEP (strnlen (&p->c, 1) == 0);
+  KEEP (strnlen (&p->c, 1) == 1);
+
+#if 0
+  /* Accesses to internal zero-length arrays are undefined so avoid
+     exercising them.  */
+  KEEP (strnlen (p->a0, 1) == 0);
+  KEEP (strnlen (p->a0, 1) == 1);
+  KEEP (strnlen (p->a0, 9) < 9);
+#endif
+
+  KEEP (strnlen (p->a1, 1) == 0);
+  KEEP (strnlen (p->a1, 1) == 1);
+
+  KEEP (strnlen (p->a1, 2) == 0);
+  KEEP (strnlen (p->a1, 2) == 1);
+  KEEP (strnlen (p->a1, 2) == 2);
+
+  KEEP (strnlen (p->a1, 9) < 9);
+}
+
+/* { dg-final { scan-tree-dump-times "call_in_true_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\]" 13 "optimized" } }
+   { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 13 "optimized" } } */
index 8f69940b027735ecd1ffeaa8ec47d28f9617c55f..a4044fd28f56017ee02d33705018fa996acbf621 100644 (file)
@@ -9,6 +9,7 @@ void *malloc (size_t);
 void free (void *);
 char *strdup (const char *);
 size_t strlen (const char *);
+size_t strnlen (const char *, size_t);
 void *memcpy (void *__restrict, const void *__restrict, size_t);
 void *memmove (void *, const void *, size_t);
 char *strcpy (char *__restrict, const char *__restrict);
index a4064a54b2e4aabcbeb377964a9a473e0ac8e02e..693d9d3491a273a3fb9517e167579b65fcaae640 100644 (file)
@@ -1124,16 +1124,17 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
   update_stmt (last.stmt);
 }
 
-/* For an LHS that is an SSA_NAME with integer type 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.  */
+/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument
+   SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to
+   a character array A[N] with unknown length bounded by N, and for
+   strnlen(), by min (N, BOUND).  */
 
-static void
-maybe_set_strlen_range (tree lhs, tree src)
+static tree
+maybe_set_strlen_range (tree lhs, tree src, tree bound)
 {
   if (TREE_CODE (lhs) != SSA_NAME
       || !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
-    return;
+    return NULL_TREE;
 
   if (TREE_CODE (src) == SSA_NAME)
     {
@@ -1143,24 +1144,87 @@ maybe_set_strlen_range (tree lhs, tree src)
        src = gimple_assign_rhs1 (def);
     }
 
-  if (TREE_CODE (src) != ADDR_EXPR)
-    return;
+  wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
+  wide_int min = wi::zero (max.get_precision ());
 
-  /* 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;
+  if (TREE_CODE (src) == ADDR_EXPR)
+    {
+      /* 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);
+      bool src_is_array = TREE_CODE (TREE_TYPE (src)) == ARRAY_TYPE;
+      if (src_is_array && !array_at_struct_end_p (src))
+       {
+         tree type = TREE_TYPE (src);
+         if (tree dom = TYPE_DOMAIN (type))
+           {
+             tree maxval = TYPE_MAX_VALUE (dom);
+             if (maxval)
+               max = wi::to_wide (maxval);
+             else
+               max = wi::zero (min.get_precision ());
+
+             /* For strlen() the upper bound above is equal to
+                the longest string that can be stored in the array
+                (i.e., it accounts for the terminating nul.  For
+                strnlen() bump up the maximum by one since the array
+                need not be nul-terminated.  */
+             if (bound)
+               ++max;
+           }
+       }
+      else
+       {
+         if (TREE_CODE (src) == COMPONENT_REF && !src_is_array)
+           src = TREE_OPERAND (src, 1);
+         if (DECL_P (src))
+           {
+             /* Handle the unlikely case of strlen (&c) where c is some
+                variable.  */
+             if (tree size = DECL_SIZE_UNIT (src))
+               if (TREE_CODE (size) == INTEGER_CST)
+                 max = wi::to_wide (size);
+           }
+       }
+    }
 
-  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);
-      }
+  if (bound)
+    {
+      /* For strnlen, adjust MIN and MAX as necessary.  If the bound
+        is less than the size of the array set MAX to it.  It it's
+        greater than MAX and MAX is non-zero bump MAX down to account
+        for the necessary terminating nul.  Otherwise leave it alone.  */
+      if (TREE_CODE (bound) == INTEGER_CST)
+       {
+         wide_int wibnd = wi::to_wide (bound);
+         int cmp = wi::cmpu (wibnd, max);
+         if (cmp < 0)
+           max = wibnd;
+         else if (cmp && wi::ne_p (max, min))
+           --max;
+       }
+      else if (TREE_CODE (bound) == SSA_NAME)
+       {
+         wide_int minbound, maxbound;
+         value_range_type rng = get_range_info (bound, &minbound, &maxbound);
+         if (rng == VR_RANGE)
+           {
+             /* For a bound in a known range, adjust the range determined
+                above as necessary.  For a bound in some anti-range or
+                in an unknown range, use the range determined above.  */
+             if (wi::ltu_p (minbound, min))
+               min = minbound;
+             if (wi::ltu_p (maxbound, max))
+               max = maxbound;
+           }
+       }
+    }
+
+  if (min == max)
+    return wide_int_to_tree (size_type_node, min);
+
+  set_range_info (lhs, VR_RANGE, min, max);
+  return lhs;
 }
 
 /* Handle a strlen call.  If strlen of the argument is known, replace
@@ -1170,16 +1234,18 @@ maybe_set_strlen_range (tree lhs, tree src)
 static void
 handle_builtin_strlen (gimple_stmt_iterator *gsi)
 {
-  int idx;
-  tree src;
   gimple *stmt = gsi_stmt (*gsi);
   tree lhs = gimple_call_lhs (stmt);
 
   if (lhs == NULL_TREE)
     return;
 
-  src = gimple_call_arg (stmt, 0);
-  idx = get_stridx (src);
+  location_t loc = gimple_location (stmt);
+  tree callee = gimple_call_fndecl (stmt);
+  tree src = gimple_call_arg (stmt, 0);
+  tree bound = (DECL_FUNCTION_CODE (callee) == BUILT_IN_STRNLEN
+               ? gimple_call_arg (stmt, 1) : NULL_TREE);
+  int idx = get_stridx (src);
   if (idx)
     {
       strinfo *si = NULL;
@@ -1203,8 +1269,9 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
            }
          rhs = unshare_expr (rhs);
          if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs)))
-           rhs = fold_convert_loc (gimple_location (stmt),
-                                   TREE_TYPE (lhs), rhs);
+           rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs);
+         if (bound)
+           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);
          stmt = gsi_stmt (*gsi);
@@ -1225,10 +1292,8 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
            }
 
          if (strlen_to_stridx)
-           {
-             location_t loc = gimple_location (stmt);
-             strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc));
-           }
+           strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc));
+
          return;
        }
     }
@@ -1251,7 +1316,6 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
              si->full_string_p = true;
              if (TREE_CODE (old) == INTEGER_CST)
                {
-                 location_t loc = gimple_location (stmt);
                  old = fold_convert_loc (loc, TREE_TYPE (lhs), old);
                  tree adj = fold_build2_loc (loc, MINUS_EXPR,
                                              TREE_TYPE (lhs), lhs, old);
@@ -1274,14 +1338,32 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
       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);
+        to [0, min (N, BOUND)].  A constant return value means
+        the range would have consisted of a single value.  In
+        that case, fold the result into the returned constant.  */
+      if (tree ret = maybe_set_strlen_range (lhs, src, bound))
+       if (TREE_CODE (ret) == INTEGER_CST)
+         {
+           if (dump_file && (dump_flags & TDF_DETAILS) != 0)
+             {
+               fprintf (dump_file, "Optimizing: ");
+               print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
+             }
+           if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (ret)))
+             ret = fold_convert_loc (loc, TREE_TYPE (lhs), ret);
+           if (!update_call_from_tree (gsi, ret))
+             gimplify_and_update_call_from_tree (gsi, ret);
+           stmt = gsi_stmt (*gsi);
+           update_stmt (stmt);
+           if (dump_file && (dump_flags & TDF_DETAILS) != 0)
+             {
+               fprintf (dump_file, "into: ");
+               print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
+             }
+         }
 
       if (strlen_to_stridx)
-       {
-         location_t loc = gimple_location (stmt);
-         strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc));
-       }
+       strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc));
     }
 }
 
@@ -3333,6 +3415,7 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh)
        switch (DECL_FUNCTION_CODE (callee))
          {
          case BUILT_IN_STRLEN:
+         case BUILT_IN_STRNLEN:
            handle_builtin_strlen (gsi);
            break;
          case BUILT_IN_STRCHR: