gimple-fold.h (get_range_strlen): Update prototype.
[gcc.git] / gcc / tree-ssa-strlen.c
index cc1f5f48e3411142c30549e175c4e84638904832..f64bc9bea79725b53151c513eb374a89598462fa 100644 (file)
@@ -1,5 +1,5 @@
 /* String length optimization
-   Copyright (C) 2011-2018 Free Software Foundation, Inc.
+   Copyright (C) 2011-2019 Free Software Foundation, Inc.
    Contributed by Jakub Jelinek <jakub@redhat.com>
 
 This file is part of GCC.
@@ -291,8 +291,6 @@ get_addr_stridx (tree exp, tree ptr, unsigned HOST_WIDE_INT *offset_out)
 static int
 get_stridx (tree exp)
 {
-  tree s, o;
-
   if (TREE_CODE (exp) == SSA_NAME)
     {
       if (ssa_ver_to_stridx[SSA_NAME_VERSION (exp)])
@@ -336,18 +334,10 @@ get_stridx (tree exp)
        return idx;
     }
 
-  s = string_constant (exp, &o);
-  if (s != NULL_TREE
-      && (o == NULL_TREE || tree_fits_shwi_p (o))
-      && TREE_STRING_LENGTH (s) > 0)
-    {
-      HOST_WIDE_INT offset = o ? tree_to_shwi (o) : 0;
-      const char *p = TREE_STRING_POINTER (s);
-      int max = TREE_STRING_LENGTH (s) - 1;
+  const char *p = c_getstr (exp);
+  if (p)
+    return ~(int) strlen (p);
 
-      if (p[max] == '\0' && offset >= 0 && offset <= max)
-       return ~(int) strlen (p + offset);
-    }
   return 0;
 }
 
@@ -581,7 +571,7 @@ get_string_length (strinfo *si)
 
       gcc_assert (is_gimple_call (stmt));
       callee = gimple_call_fndecl (stmt);
-      gcc_assert (callee && DECL_BUILT_IN_CLASS (callee) == BUILT_IN_NORMAL);
+      gcc_assert (callee && fndecl_built_in_p (callee, BUILT_IN_NORMAL));
       lhs = gimple_call_lhs (stmt);
       /* unshare_strinfo is intentionally not called here.  The (delayed)
         transformation of strcpy or strcat into stpcpy is done at the place
@@ -1074,7 +1064,7 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
 
       if (!integer_zerop (gimple_assign_rhs1 (last.stmt)))
        return;
-      if (stmt_could_throw_p (last.stmt))
+      if (stmt_could_throw_p (cfun, last.stmt))
        return;
       gsi = gsi_for_stmt (last.stmt);
       unlink_stmt_vdef (last.stmt);
@@ -1107,6 +1097,13 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
         to store the extra '\0' in that case.  */
       if ((tree_to_uhwi (len) & 3) == 0)
        return;
+
+      /* Don't fold away an out of bounds access, as this defeats proper
+        warnings.  */
+      tree dst = gimple_call_arg (last.stmt, 0);
+      tree size = compute_objsize (dst, 0);
+      if (size && tree_int_cst_lt (size, len))
+       return;
     }
   else if (TREE_CODE (len) == SSA_NAME)
     {
@@ -1124,15 +1121,17 @@ 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.  */
+/* 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)
-    return;
+  if (TREE_CODE (lhs) != SSA_NAME
+      || !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+    return NULL_TREE;
 
   if (TREE_CODE (src) == SSA_NAME)
     {
@@ -1142,24 +1141,84 @@ 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
+         && TREE_CODE (src) != MEM_REF
+         && !array_at_struct_end_p (src))
+       {
+         tree type = TREE_TYPE (src);
+         if (tree size = TYPE_SIZE_UNIT (type))
+           if (size && 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);
-      }
+         /* 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 != 0)
+           --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);
+           }
+       }
+    }
+
+  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_kind 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
@@ -1169,16 +1228,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;
@@ -1202,8 +1263,21 @@ 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);
+
+         /* Set for strnlen() calls with a non-constant bound.  */
+         bool noncst_bound = false;
+         if (bound)
+           {
+             tree new_rhs
+               = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (rhs), rhs, bound);
+
+             noncst_bound = (TREE_CODE (new_rhs) != INTEGER_CST
+                             || tree_int_cst_lt (new_rhs, rhs));
+
+             rhs = new_rhs;
+           }
+
          if (!update_call_from_tree (gsi, rhs))
            gimplify_and_update_call_from_tree (gsi, rhs);
          stmt = gsi_stmt (*gsi);
@@ -1213,6 +1287,12 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
              fprintf (dump_file, "into: ");
              print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
            }
+
+         /* Avoid storing the length for calls to strnlen() with
+            a non-constant bound.  */
+         if (noncst_bound)
+           return;
+
          if (si != NULL
              && TREE_CODE (si->nonzero_chars) != SSA_NAME
              && TREE_CODE (si->nonzero_chars) != INTEGER_CST
@@ -1224,15 +1304,14 @@ 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;
        }
     }
   if (SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
     return;
+
   if (idx == 0)
     idx = new_stridx (src);
   else
@@ -1250,7 +1329,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);
@@ -1268,19 +1346,42 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
     }
   if (idx)
     {
-      strinfo *si = new_strinfo (src, idx, lhs, true);
-      set_strinfo (idx, si);
-      find_equal_ptrs (src, idx);
+      if (!bound)
+       {
+         /* Only store the new length information for calls to strlen(),
+            not for those to strnlen().  */
+         strinfo *si = new_strinfo (src, idx, lhs, true);
+         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);
+        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));
-       }
+      if (strlen_to_stridx && !bound)
+       strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc));
     }
 }
 
@@ -1525,8 +1626,7 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
          tree type = TREE_TYPE (oldlen);
          oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
                                build_int_cst (type, 1));
-         check_bounds_or_overlap (as_a <gcall *>(stmt), olddsi->ptr, src,
-                                  oldlen, NULL_TREE);
+         check_bounds_or_overlap (stmt, olddsi->ptr, src, oldlen, NULL_TREE);
        }
 
       return;
@@ -1613,8 +1713,7 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 
   if (const strinfo *chksi = olddsi ? olddsi : dsi)
     if (si
-       && !check_bounds_or_overlap (as_a <gcall *>(stmt), chksi->ptr, si->ptr,
-                                    NULL_TREE, len))
+       && !check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
       {
        gimple_set_no_warning (stmt, true);
        set_no_warning = true;
@@ -1674,7 +1773,7 @@ handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
    assumed to be the argument in some call to strlen() whose relationship
    to SRC is being ascertained.  */
 
-static bool
+bool
 is_strlen_related_p (tree src, tree len)
 {
   if (TREE_CODE (TREE_TYPE (len)) == POINTER_TYPE
@@ -1711,12 +1810,20 @@ is_strlen_related_p (tree src, tree len)
          && (code == BIT_AND_EXPR
              || code == NOP_EXPR)))
     {
-      /* Pointer plus (an integer) and integer cast or truncation are
-        considered among the (potentially) related expressions to strlen.
-        Others are not.  */
+      /* Pointer plus (an integer), and truncation are considered among
+        the (potentially) related expressions to strlen.  */
       return is_strlen_related_p (src, rhs1);
     }
 
+  if (tree rhs2 = gimple_assign_rhs2 (def_stmt))
+    {
+      /* Integer subtraction is considered strlen-related when both
+        arguments are integers and second one is strlen-related.  */
+      rhstype = TREE_TYPE (rhs2);
+      if (INTEGRAL_TYPE_P (rhstype) && code == MINUS_EXPR)
+       return is_strlen_related_p (src, rhs2);
+    }
+
   return false;
 }
 
@@ -1749,7 +1856,7 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
     cntrange[0] = cntrange[1] = wi::to_wide (cnt);
   else if (TREE_CODE (cnt) == SSA_NAME)
     {
-      enum value_range_type rng = get_range_info (cnt, cntrange, cntrange + 1);
+      enum value_range_kind rng = get_range_info (cnt, cntrange, cntrange + 1);
       if (rng == VR_RANGE)
        ;
       else if (rng == VR_ANTI_RANGE)
@@ -1787,9 +1894,22 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
   if (TREE_CODE (dstdecl) == ADDR_EXPR)
     dstdecl = TREE_OPERAND (dstdecl, 0);
 
-  /* If the destination refers to a an array/pointer declared nonstring
-     return early.  */
   tree ref = NULL_TREE;
+
+  if (!sidx)
+    {
+      /* If the source is a non-string return early to avoid warning
+        for possible truncation (if the truncation is certain SIDX
+        is non-zero).  */
+      tree srcdecl = gimple_call_arg (stmt, 1);
+      if (TREE_CODE (srcdecl) == ADDR_EXPR)
+       srcdecl = TREE_OPERAND (srcdecl, 0);
+      if (get_attr_nonstring_decl (srcdecl, &ref))
+       return false;
+    }
+
+  /* Likewise, if the destination refers to a an array/pointer declared
+     nonstring return early.  */
   if (get_attr_nonstring_decl (dstdecl, &ref))
     return false;
 
@@ -1869,15 +1989,18 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
     lenrange[0] = lenrange[1] = wi::shwi (~sidx, prec);
   else
     {
-      tree range[2];
-      get_range_strlen (src, range);
-      if (range[0] != NULL_TREE
-         && TREE_CODE (range[0]) == INTEGER_CST
-         && range[1] != NULL_TREE
-         && TREE_CODE (range[1]) == INTEGER_CST)
+      c_strlen_data lendata = { };
+      get_range_strlen (src, &lendata, /* eltsize = */1);
+      if (TREE_CODE (lendata.minlen) == INTEGER_CST
+         && TREE_CODE (lendata.maxbound) == INTEGER_CST)
        {
-         lenrange[0] = wi::to_wide (range[0], prec);
-         lenrange[1] = wi::to_wide (range[1], prec);
+         /* When LENDATA.MAXLEN is unknown, reset LENDATA.MINLEN
+            which stores the length of the shortest known string.  */
+         if (integer_all_onesp (lendata.maxlen))
+           lenrange[0] = wi::shwi (0, prec);
+         else
+           lenrange[0] = wi::to_wide (lendata.minlen, prec);
+         lenrange[1] = wi::to_wide (lendata.maxbound, prec);
        }
       else
        {
@@ -1886,7 +2009,9 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
        }
     }
 
-  location_t callloc = gimple_location (stmt);
+  location_t callloc = gimple_nonartificial_location (stmt);
+  callloc = expansion_point_location_if_in_system_header (callloc);
+
   tree func = gimple_call_fndecl (stmt);
 
   if (lenrange[0] != 0 || !wi::neg_p (lenrange[1]))
@@ -1906,7 +2031,11 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
          lenrange[0] = wi::shwi (0, prec);
        }
 
-      gcall *call = as_a <gcall *> (stmt);
+      /* Set to true for strncat whose bound is derived from the length
+        of the destination (the expected usage pattern).  */
+      bool cat_dstlen_bounded = false;
+      if (DECL_FUNCTION_CODE (func) == BUILT_IN_STRNCAT)
+       cat_dstlen_bounded = is_strlen_related_p (dst, cnt);
 
       if (lenrange[0] == cntrange[1] && cntrange[0] == cntrange[1])
        return warning_n (callloc, OPT_Wstringop_truncation,
@@ -1917,47 +2046,51 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
                          "%G%qD output truncated before terminating nul "
                          "copying %E bytes from a string of the same "
                          "length",
-                         call, func, cnt);
-      else if (wi::geu_p (lenrange[0], cntrange[1]))
-       {
-         /* The shortest string is longer than the upper bound of
-            the count so the truncation is certain.  */
-         if (cntrange[0] == cntrange[1])
-           return warning_n (callloc, OPT_Wstringop_truncation,
-                             cntrange[0].to_uhwi (),
-                             "%G%qD output truncated copying %E byte "
-                             "from a string of length %wu",
-                             "%G%qD output truncated copying %E bytes "
-                             "from a string of length %wu",
-                             call, func, cnt, lenrange[0].to_uhwi ());
-
-         return warning_at (callloc, OPT_Wstringop_truncation,
-                            "%G%qD output truncated copying between %wu "
-                            "and %wu bytes from a string of length %wu",
-                            call, func, cntrange[0].to_uhwi (),
-                            cntrange[1].to_uhwi (), lenrange[0].to_uhwi ());
-       }
-      else if (wi::geu_p (lenrange[1], cntrange[1]))
+                         stmt, func, cnt);
+      else if (!cat_dstlen_bounded)
        {
-         /* The longest string is longer than the upper bound of
-            the count so the truncation is possible.  */
-         if (cntrange[0] == cntrange[1])
-           return warning_n (callloc, OPT_Wstringop_truncation,
-                             cntrange[0].to_uhwi (),
-                             "%G%qD output may be truncated copying %E "
-                             "byte from a string of length %wu",
-                             "%G%qD output may be truncated copying %E "
-                             "bytes from a string of length %wu",
-                             call, func, cnt, lenrange[1].to_uhwi ());
-
-         return warning_at (callloc, OPT_Wstringop_truncation,
-                            "%G%qD output may be truncated copying between %wu "
-                            "and %wu bytes from a string of length %wu",
-                            call, func, cntrange[0].to_uhwi (),
-                            cntrange[1].to_uhwi (), lenrange[1].to_uhwi ());
+         if (wi::geu_p (lenrange[0], cntrange[1]))
+           {
+             /* The shortest string is longer than the upper bound of
+                the count so the truncation is certain.  */
+             if (cntrange[0] == cntrange[1])
+               return warning_n (callloc, OPT_Wstringop_truncation,
+                                 cntrange[0].to_uhwi (),
+                                 "%G%qD output truncated copying %E byte "
+                                 "from a string of length %wu",
+                                 "%G%qD output truncated copying %E bytes "
+                                 "from a string of length %wu",
+                                 stmt, func, cnt, lenrange[0].to_uhwi ());
+
+             return warning_at (callloc, OPT_Wstringop_truncation,
+                                "%G%qD output truncated copying between %wu "
+                                "and %wu bytes from a string of length %wu",
+                                stmt, func, cntrange[0].to_uhwi (),
+                                cntrange[1].to_uhwi (), lenrange[0].to_uhwi ());
+           }
+         else if (wi::geu_p (lenrange[1], cntrange[1]))
+           {
+             /* The longest string is longer than the upper bound of
+                the count so the truncation is possible.  */
+             if (cntrange[0] == cntrange[1])
+               return warning_n (callloc, OPT_Wstringop_truncation,
+                                 cntrange[0].to_uhwi (),
+                                 "%G%qD output may be truncated copying %E "
+                                 "byte from a string of length %wu",
+                                 "%G%qD output may be truncated copying %E "
+                                 "bytes from a string of length %wu",
+                                 stmt, func, cnt, lenrange[1].to_uhwi ());
+
+             return warning_at (callloc, OPT_Wstringop_truncation,
+                                "%G%qD output may be truncated copying between "
+                                "%wu and %wu bytes from a string of length %wu",
+                                stmt, func, cntrange[0].to_uhwi (),
+                                cntrange[1].to_uhwi (), lenrange[1].to_uhwi ());
+           }
        }
 
-      if (cntrange[0] != cntrange[1]
+      if (!cat_dstlen_bounded
+         && cntrange[0] != cntrange[1]
          && wi::leu_p (cntrange[0], lenrange[0])
          && wi::leu_p (cntrange[1], lenrange[0] + 1))
        {
@@ -1967,7 +2100,7 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
          return warning_at (callloc, OPT_Wstringop_truncation,
                             "%G%qD output may be truncated copying between "
                             "%wu and %wu bytes from a string of length %wu",
-                            call, func, cntrange[0].to_uhwi (),
+                            stmt, func, cntrange[0].to_uhwi (),
                             cntrange[1].to_uhwi (), lenrange[0].to_uhwi ());
        }
     }
@@ -1984,10 +2117,17 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
       if (wi::to_wide (dstsize) != cntrange[1])
        return false;
 
+      /* Avoid warning for strncpy(a, b, N) calls where the following
+        equalities hold:
+          N == sizeof a && N == sizeof b */
+      if (tree srcsize = compute_objsize (src, 1))
+       if (wi::to_wide (srcsize) == cntrange[1])
+         return false;
+
       if (cntrange[0] == cntrange[1])
        return warning_at (callloc, OPT_Wstringop_truncation,
                           "%G%qD specified bound %E equals destination size",
-                          as_a <gcall *> (stmt), func, cnt);
+                          stmt, func, cnt);
     }
 
   return false;
@@ -2045,8 +2185,7 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
   else
     srcsize = NULL_TREE;
 
-  if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, src,
-                               dstsize, srcsize))
+  if (!check_bounds_or_overlap (stmt, dst, src, dstsize, srcsize))
     {
       gimple_set_no_warning (stmt, true);
       return;
@@ -2069,7 +2208,8 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
      to strlen(S)).  */
   strinfo *silen = get_strinfo (pss->first);
 
-  location_t callloc = gimple_location (stmt);
+  location_t callloc = gimple_nonartificial_location (stmt);
+  callloc = expansion_point_location_if_in_system_header (callloc);
 
   tree func = gimple_call_fndecl (stmt);
 
@@ -2086,13 +2226,13 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
       && warning_at (callloc, OPT_Wstringop_truncation,
                     "%G%qD output truncated before terminating nul "
                     "copying as many bytes from a string as its length",
-                    as_a <gcall *>(stmt), func))
+                    stmt, func))
     warned = true;
   else if (silen && is_strlen_related_p (src, silen->ptr))
     warned = warning_at (callloc, OPT_Wstringop_overflow_,
                         "%G%qD specified bound depends on the length "
                         "of the source argument",
-                        as_a <gcall *>(stmt), func);
+                        stmt, func);
   if (warned)
     {
       location_t strlenloc = pss->second;
@@ -2190,6 +2330,18 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       full_string_p = clen > nonzero_chars;
     }
 
+  if (!full_string_p
+      && olddsi
+      && olddsi->nonzero_chars
+      && TREE_CODE (olddsi->nonzero_chars) == INTEGER_CST
+      && tree_int_cst_le (newlen, olddsi->nonzero_chars))
+    {
+      /* The SRC substring being written strictly overlaps
+        a subsequence of the existing string OLDDSI.  */
+      newlen = olddsi->nonzero_chars;
+      full_string_p = olddsi->full_string_p;
+    }
+
   if (olddsi != NULL && TREE_CODE (len) == SSA_NAME)
     adjust_last_stmt (olddsi, stmt, false);
 
@@ -2331,8 +2483,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 
        tree sptr = si && si->ptr ? si->ptr : src;
 
-       if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr,
-                                     NULL_TREE, slen))
+       if (!check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
          {
            gimple_set_no_warning (stmt, true);
            set_no_warning = true;
@@ -2442,8 +2593,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 
       tree sptr = si && si->ptr ? si->ptr : src;
 
-      if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr,
-                                   dstlen, srcsize))
+      if (!check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
        {
          gimple_set_no_warning (stmt, true);
          set_no_warning = true;
@@ -2465,12 +2615,19 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (endptr)
     dst = fold_convert_loc (loc, TREE_TYPE (dst), unshare_expr (endptr));
   else
-    dst = fold_build2_loc (loc, POINTER_PLUS_EXPR,
-                          TREE_TYPE (dst), unshare_expr (dst),
+    dst = fold_build2_loc (loc, POINTER_PLUS_EXPR, TREE_TYPE (dst), dst,
                           fold_convert_loc (loc, sizetype,
                                             unshare_expr (dstlen)));
   dst = force_gimple_operand_gsi (gsi, dst, true, NULL_TREE, true,
                                  GSI_SAME_STMT);
+  if (objsz)
+    {
+      objsz = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (objsz), objsz,
+                              fold_convert_loc (loc, TREE_TYPE (objsz),
+                                                unshare_expr (dstlen)));
+      objsz = force_gimple_operand_gsi (gsi, objsz, true, NULL_TREE, true,
+                                       GSI_SAME_STMT);
+    }
   if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     {
       fprintf (dump_file, "Optimizing: ");
@@ -2995,11 +3152,25 @@ handle_pointer_plus (gimple_stmt_iterator *gsi)
 }
 
 /* If RHS, either directly or indirectly, refers to a string of constant
-   length, return it.  Otherwise return a negative value.  */
+   length, return the length.  Otherwise, if it refers to a character
+   constant, return 1 if the constant is non-zero and 0 if it is nul.
+   Otherwise, return a negative value.  */
 
 static HOST_WIDE_INT
-get_string_cst_length (tree rhs)
+get_min_string_length (tree rhs, bool *full_string_p)
 {
+  if (INTEGRAL_TYPE_P (TREE_TYPE (rhs)))
+    {
+      if (tree_expr_nonzero_p (rhs))
+       {
+         *full_string_p = false;
+         return 1;
+       }
+
+      *full_string_p = true;
+      return 0;
+    }
+
   if (TREE_CODE (rhs) == MEM_REF
       && integer_zerop (TREE_OPERAND (rhs, 1)))
     {
@@ -3016,9 +3187,11 @@ get_string_cst_length (tree rhs)
                {
                  strinfo *si = get_strinfo (idx);
                  if (si
-                     && si->full_string_p
                      && tree_fits_shwi_p (si->nonzero_chars))
-                   return tree_to_shwi (si->nonzero_chars);
+                   {
+                     *full_string_p = si->full_string_p;
+                     return tree_to_shwi (si->nonzero_chars);
+                   }
                }
            }
        }
@@ -3029,12 +3202,17 @@ get_string_cst_length (tree rhs)
     rhs = DECL_INITIAL (rhs);
 
   if (rhs && TREE_CODE (rhs) == STRING_CST)
-    return strlen (TREE_STRING_POINTER (rhs));
+    {
+      *full_string_p = true;
+      return strlen (TREE_STRING_POINTER (rhs));
+    }
 
   return -1;
 }
 
-/* Handle a single character store.  */
+/* Handle a single or multiple character store either by single
+   character assignment or by multi-character assignment from
+   MEM_REF.  */
 
 static bool
 handle_char_store (gimple_stmt_iterator *gsi)
@@ -3072,8 +3250,15 @@ handle_char_store (gimple_stmt_iterator *gsi)
        si = get_strinfo (idx);
     }
 
-  bool storing_zero_p = initializer_zerop (rhs);
-  bool storing_nonzero_p = !storing_zero_p && tree_expr_nonzero_p (rhs);
+  /* STORING_NONZERO_P is true iff not all stored characters are zero.
+     STORING_ALL_ZEROS_P is true iff all stored characters are zero.
+     Both are false when it's impossible to determine which is true.  */
+  bool storing_nonzero_p;
+  bool storing_all_zeros_p = initializer_zerop (rhs, &storing_nonzero_p);
+  if (!storing_nonzero_p)
+    storing_nonzero_p = tree_expr_nonzero_p (rhs);
+  bool full_string_p = storing_all_zeros_p;
+
   /* Set to the length of the string being assigned if known.  */
   HOST_WIDE_INT rhslen;
 
@@ -3081,11 +3266,11 @@ handle_char_store (gimple_stmt_iterator *gsi)
     {
       int cmp = compare_nonzero_chars (si, offset);
       gcc_assert (offset == 0 || cmp >= 0);
-      if (storing_zero_p && cmp == 0 && si->full_string_p)
+      if (storing_all_zeros_p && cmp == 0 && si->full_string_p)
        {
          /* When overwriting a '\0' with a '\0', the store can be removed
             if we know it has been stored in the current function.  */
-         if (!stmt_could_throw_p (stmt) && si->writable)
+         if (!stmt_could_throw_p (cfun, stmt) && si->writable)
            {
              unlink_stmt_vdef (stmt);
              release_defs (stmt);
@@ -3124,17 +3309,32 @@ handle_char_store (gimple_stmt_iterator *gsi)
          gsi_next (gsi);
          return false;
        }
-      else if (storing_zero_p || storing_nonzero_p || (offset != 0 && cmp > 0))
+      else if (storing_all_zeros_p || storing_nonzero_p || (offset != 0 && cmp > 0))
        {
-         /* When storing_nonzero_p, we know that the string now starts
-            with OFFSET + 1 nonzero characters, but don't know whether
-            there's a following nul terminator.
+         /* When STORING_NONZERO_P, we know that the string will start
+            with at least OFFSET + 1 nonzero characters.  If storing
+            a single character, set si->NONZERO_CHARS to the result.
+            If storing multiple characters, try to determine the number
+            of leading non-zero characters and set si->NONZERO_CHARS to
+            the result instead.
 
-            When storing_zero_p, we know that the string is now OFFSET
-            characters long.
+            When STORING_ALL_ZEROS_P, we know that the string is now
+            OFFSET characters long.
 
             Otherwise, we're storing an unknown value at offset OFFSET,
             so need to clip the nonzero_chars to OFFSET.  */
+         bool full_string_p = storing_all_zeros_p;
+         HOST_WIDE_INT len = 1;
+         if (storing_nonzero_p)
+           {
+             /* Try to get the minimum length of the string (or
+                individual character) being stored.  If it fails,
+                STORING_NONZERO_P guarantees it's at least 1.  */
+             len = get_min_string_length (rhs, &full_string_p);
+             if (len < 0)
+               len = 1;
+           }
+
          location_t loc = gimple_location (stmt);
          tree oldlen = si->nonzero_chars;
          if (cmp == 0 && si->full_string_p)
@@ -3144,11 +3344,14 @@ handle_char_store (gimple_stmt_iterator *gsi)
            adjust_last_stmt (si, stmt, false);
          si = unshare_strinfo (si);
          if (storing_nonzero_p)
-           si->nonzero_chars = build_int_cst (size_type_node, offset + 1);
+           {
+             gcc_assert (len >= 0);
+             si->nonzero_chars = build_int_cst (size_type_node, offset + len);
+           }
          else
            si->nonzero_chars = build_int_cst (size_type_node, offset);
-         si->full_string_p = storing_zero_p;
-         if (storing_zero_p
+         si->full_string_p = full_string_p;
+         if (storing_all_zeros_p
              && ssaname
              && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
            si->endptr = ssaname;
@@ -3168,7 +3371,7 @@ handle_char_store (gimple_stmt_iterator *gsi)
            si->prev = 0;
        }
     }
-  else if (idx == 0 && (storing_zero_p || storing_nonzero_p))
+  else if (idx == 0 && (storing_all_zeros_p || storing_nonzero_p))
     {
       if (ssaname)
        idx = new_stridx (ssaname);
@@ -3177,10 +3380,17 @@ handle_char_store (gimple_stmt_iterator *gsi)
       if (idx != 0)
        {
          tree ptr = (ssaname ? ssaname : build_fold_addr_expr (lhs));
-         tree len = storing_nonzero_p ? size_one_node : size_zero_node;
-         si = new_strinfo (ptr, idx, len, storing_zero_p);
+         HOST_WIDE_INT slen = (storing_all_zeros_p
+                               ? 0
+                               : (storing_nonzero_p
+                                  ? get_min_string_length (rhs, &full_string_p)
+                                  : -1));
+         tree len = (slen <= 0
+                     ? size_zero_node
+                     : build_int_cst (size_type_node, slen));
+         si = new_strinfo (ptr, idx, len, slen >= 0 && full_string_p);
          set_strinfo (idx, si);
-         if (storing_zero_p
+         if (storing_all_zeros_p
              && ssaname
              && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
            si->endptr = ssaname;
@@ -3189,7 +3399,7 @@ handle_char_store (gimple_stmt_iterator *gsi)
        }
     }
   else if (idx == 0
-          && (rhslen = get_string_cst_length (gimple_assign_rhs1 (stmt))) >= 0
+          && (rhslen = get_min_string_length (rhs, &full_string_p)) >= 0
           && ssaname == NULL_TREE
           && TREE_CODE (TREE_TYPE (lhs)) == ARRAY_TYPE)
     {
@@ -3200,14 +3410,15 @@ handle_char_store (gimple_stmt_iterator *gsi)
          if (idx != 0)
            {
              si = new_strinfo (build_fold_addr_expr (lhs), idx,
-                               build_int_cst (size_type_node, rhslen), true);
+                               build_int_cst (size_type_node, rhslen),
+                               full_string_p);
              set_strinfo (idx, si);
              si->dont_invalidate = true;
            }
        }
     }
 
-  if (si != NULL && offset == 0 && storing_zero_p)
+  if (si != NULL && offset == 0 && storing_all_zeros_p)
     {
       /* Allow adjust_last_stmt to remove it if the stored '\0'
         is immediately overwritten.  */
@@ -3329,6 +3540,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:
@@ -3487,7 +3699,7 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh)
                           if we don't have anything better.  */
                        wide_int min, max;
                        tree type = TREE_TYPE (lhs);
-                       enum value_range_type vr
+                       enum value_range_kind vr
                          = get_range_info (lhs, &min, &max);
                        if (vr == VR_VARYING
                            || (vr == VR_RANGE