]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - gcc/tree-ssa-strlen.c
PR tree-optimization/92157 - incorrect strcmp() == 0 result for unknown strings
[thirdparty/gcc.git] / gcc / tree-ssa-strlen.c
index be6ab9f1e1b93021319e78320a328e7c25d3da55..43814584a147d8f1236ae541800144393e8097be 100644 (file)
@@ -1,5 +1,5 @@
 /* String length optimization
-   Copyright (C) 2011-2017 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.
@@ -39,13 +39,15 @@ along with GCC; see the file COPYING3.  If not see
 #include "gimple-iterator.h"
 #include "gimplify-me.h"
 #include "expr.h"
+#include "tree-cfg.h"
 #include "tree-dfa.h"
 #include "domwalk.h"
 #include "tree-ssa-alias.h"
 #include "tree-ssa-propagate.h"
+#include "tree-ssa-strlen.h"
 #include "params.h"
-#include "ipa-chkp.h"
 #include "tree-hash-traits.h"
+#include "tree-object-size.h"
 #include "builtins.h"
 #include "target.h"
 #include "diagnostic-core.h"
@@ -53,6 +55,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "attribs.h"
 #include "calls.h"
+#include "cfgloop.h"
+#include "tree-ssa-loop.h"
+#include "tree-scalar-evolution.h"
+
+#include "vr-values.h"
+#include "gimple-ssa-evrp-analyze.h"
 
 /* A vector indexed by SSA_NAME_VERSION.  0 means unknown, positive value
    is an index into strinfo vector, negative value stands for
@@ -62,6 +70,9 @@ static vec<int> ssa_ver_to_stridx;
 /* Number of currently active string indexes plus one.  */
 static int max_stridx;
 
+/* Set to true to optimize, false when just checking.  */
+static bool strlen_optimize;
+
 /* String information record.  */
 struct strinfo
 {
@@ -152,9 +163,11 @@ struct decl_stridxlist_map
 
 /* Hash table for mapping decls to a chained list of offset -> idx
    mappings.  */
-static hash_map<tree_decl_hash, stridxlist> *decl_to_stridxlist_htab;
+typedef hash_map<tree_decl_hash, stridxlist> decl_to_stridxlist_htab_t;
+static decl_to_stridxlist_htab_t *decl_to_stridxlist_htab;
 
-/* Hash table mapping strlen calls to stridx instances describing
+/* Hash table mapping strlen (or strnlen with constant bound and return
+   smaller than bound) calls to stridx instances describing
    the calls' arguments.  Non-null only when warn_stringop_truncation
    is non-zero.  */
 typedef std::pair<int, location_t> stridx_strlenloc;
@@ -178,12 +191,12 @@ static void handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *);
 
 /* Return:
 
-   - 1 if SI is known to start with more than OFF nonzero characters.
+   *  +1  if SI is known to start with more than OFF nonzero characters.
 
-   - 0 if SI is known to start with OFF nonzero characters,
-     but is not known to start with more.
+   *   0  if SI is known to start with OFF nonzero characters,
+         but is not known to start with more.
 
-   - -1 if SI might not start with OFF nonzero characters.  */
+   *  -1  if SI might not start with OFF nonzero characters.  */
 
 static inline int
 compare_nonzero_chars (strinfo *si, unsigned HOST_WIDE_INT off)
@@ -195,6 +208,33 @@ compare_nonzero_chars (strinfo *si, unsigned HOST_WIDE_INT off)
     return -1;
 }
 
+/* Same as above but suitable also for strings with non-constant lengths.
+   Uses RVALS to determine length range.  */
+
+static int
+compare_nonzero_chars (strinfo *si, unsigned HOST_WIDE_INT off,
+                      const vr_values *rvals)
+{
+  if (!si->nonzero_chars)
+    return -1;
+
+  if (TREE_CODE (si->nonzero_chars) == INTEGER_CST)
+    return compare_tree_int (si->nonzero_chars, off);
+
+  if (TREE_CODE (si->nonzero_chars) != SSA_NAME)
+    return -1;
+
+  const value_range *vr
+    = (CONST_CAST (class vr_values *, rvals)
+       ->get_value_range (si->nonzero_chars));
+
+  value_range_kind rng = vr->kind ();
+  if (rng != VR_RANGE || !range_int_cst_p (vr))
+    return -1;
+
+  return compare_tree_int (vr->min (), off);
+}
+
 /* Return true if SI is known to be a zero-length string.  */
 
 static inline bool
@@ -289,40 +329,87 @@ 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)])
        return ssa_ver_to_stridx[SSA_NAME_VERSION (exp)];
-      int i;
+
       tree e = exp;
-      HOST_WIDE_INT off = 0;
-      for (i = 0; i < 5; i++)
+      HOST_WIDE_INT offset = 0;
+      /* Follow a chain of at most 5 assignments.  */
+      for (int i = 0; i < 5; i++)
        {
          gimple *def_stmt = SSA_NAME_DEF_STMT (e);
-         if (!is_gimple_assign (def_stmt)
-             || gimple_assign_rhs_code (def_stmt) != POINTER_PLUS_EXPR)
+         if (!is_gimple_assign (def_stmt))
+           return 0;
+
+         tree_code rhs_code = gimple_assign_rhs_code (def_stmt);
+         tree ptr, off;
+
+         if (rhs_code == ADDR_EXPR)
+           {
+             /* Handle indices/offsets into VLAs which are implemented
+                as pointers to arrays.  */
+             ptr = gimple_assign_rhs1 (def_stmt);
+             ptr = TREE_OPERAND (ptr, 0);
+
+             /* Handle also VLAs of types larger than char.  */
+             if (tree eltsize = TYPE_SIZE_UNIT (TREE_TYPE (ptr)))
+               {
+                 if (TREE_CODE (ptr) == ARRAY_REF)
+                   {
+                     off = TREE_OPERAND (ptr, 1);
+                     ptr = TREE_OPERAND (ptr, 0);
+                     if (!integer_onep (eltsize))
+                       {
+                         /* Scale the array index by the size of the element
+                            type in the rare case that it's greater than
+                            the typical 1 for char, making sure both operands
+                            have the same type.  */
+                         eltsize = fold_convert (ssizetype, eltsize);
+                         off = fold_convert (ssizetype, off);
+                         off = fold_build2 (MULT_EXPR, ssizetype, off, eltsize);
+                       }
+                   }
+                 else
+                   off = integer_zero_node;
+               }
+             else
+               return 0;
+
+             if (TREE_CODE (ptr) != MEM_REF)
+               return 0;
+
+             /* Add the MEM_REF byte offset.  */
+             tree mem_off = TREE_OPERAND (ptr, 1);
+             off = fold_build2 (PLUS_EXPR, TREE_TYPE (off), off, mem_off);
+             ptr = TREE_OPERAND (ptr, 0);
+           }
+         else if (rhs_code == POINTER_PLUS_EXPR)
+           {
+             ptr = gimple_assign_rhs1 (def_stmt);
+             off = gimple_assign_rhs2 (def_stmt);
+           }
+         else
            return 0;
-         tree rhs1 = gimple_assign_rhs1 (def_stmt);
-         tree rhs2 = gimple_assign_rhs2 (def_stmt);
-         if (TREE_CODE (rhs1) != SSA_NAME
-             || !tree_fits_shwi_p (rhs2))
+
+         if (TREE_CODE (ptr) != SSA_NAME
+             || !tree_fits_shwi_p (off))
            return 0;
-         HOST_WIDE_INT this_off = tree_to_shwi (rhs2);
+         HOST_WIDE_INT this_off = tree_to_shwi (off);
          if (this_off < 0)
            return 0;
-         off = (unsigned HOST_WIDE_INT) off + this_off;
-         if (off < 0)
+         offset = (unsigned HOST_WIDE_INT) offset + this_off;
+         if (offset < 0)
            return 0;
-         if (ssa_ver_to_stridx[SSA_NAME_VERSION (rhs1)])
+         if (ssa_ver_to_stridx[SSA_NAME_VERSION (ptr)])
            {
              strinfo *si
-               = get_strinfo (ssa_ver_to_stridx[SSA_NAME_VERSION (rhs1)]);
-             if (si && compare_nonzero_chars (si, off) >= 0)
-               return get_stridx_plus_constant (si, off, exp);
+               = get_strinfo (ssa_ver_to_stridx[SSA_NAME_VERSION (ptr)]);
+             if (si && compare_nonzero_chars (si, offset) >= 0)
+               return get_stridx_plus_constant (si, offset, exp);
            }
-         e = rhs1;
+         e = ptr;
        }
       return 0;
     }
@@ -334,18 +421,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;
 }
 
@@ -562,25 +641,31 @@ set_endptr_and_length (location_t loc, strinfo *si, tree endptr)
   si->full_string_p = true;
 }
 
-/* Return string length, or NULL if it can't be computed.  */
+/* Return the string length, or NULL if it can't be computed.
+   The length may but need not be constant.  Instead, it might be
+   the result of a strlen() call.  */
 
 static tree
 get_string_length (strinfo *si)
 {
+  /* If the length has already been computed return it if it's exact
+     (i.e., the string is nul-terminated at NONZERO_CHARS), or return
+     null if it isn't.  */
   if (si->nonzero_chars)
     return si->full_string_p ? si->nonzero_chars : NULL;
 
+  /* If the string is the result of one of the built-in calls below
+     attempt to compute the length from the call statement.  */
   if (si->stmt)
     {
       gimple *stmt = si->stmt, *lenstmt;
-      bool with_bounds = gimple_call_with_bounds_p (stmt);
       tree callee, lhs, fn, tem;
       location_t loc;
       gimple_stmt_iterator gsi;
 
       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
@@ -592,20 +677,11 @@ get_string_length (strinfo *si)
        {
        case BUILT_IN_STRCAT:
        case BUILT_IN_STRCAT_CHK:
-       case BUILT_IN_STRCAT_CHKP:
-       case BUILT_IN_STRCAT_CHK_CHKP:
          gsi = gsi_for_stmt (stmt);
          fn = builtin_decl_implicit (BUILT_IN_STRLEN);
          gcc_assert (lhs == NULL_TREE);
          tem = unshare_expr (gimple_call_arg (stmt, 0));
-         if (with_bounds)
-           {
-             lenstmt = gimple_build_call (chkp_maybe_create_clone (fn)->decl,
-                                          2, tem, gimple_call_arg (stmt, 1));
-             gimple_call_set_with_bounds (lenstmt, true);
-           }
-         else
-           lenstmt = gimple_build_call (fn, 1, tem);
+         lenstmt = gimple_build_call (fn, 1, tem);
          lhs = make_ssa_name (TREE_TYPE (TREE_TYPE (fn)), lenstmt);
          gimple_call_set_lhs (lenstmt, lhs);
          gimple_set_vuse (lenstmt, gimple_vuse (stmt));
@@ -626,15 +702,11 @@ get_string_length (strinfo *si)
          /* FALLTHRU */
        case BUILT_IN_STRCPY:
        case BUILT_IN_STRCPY_CHK:
-       case BUILT_IN_STRCPY_CHKP:
-       case BUILT_IN_STRCPY_CHK_CHKP:
          gcc_assert (builtin_decl_implicit_p (BUILT_IN_STPCPY));
-         if (gimple_call_num_args (stmt) == (with_bounds ? 4 : 2))
+         if (gimple_call_num_args (stmt) == 2)
            fn = builtin_decl_implicit (BUILT_IN_STPCPY);
          else
            fn = builtin_decl_explicit (BUILT_IN_STPCPY_CHK);
-         if (with_bounds)
-           fn = chkp_maybe_create_clone (fn)->decl;
          gcc_assert (lhs == NULL_TREE);
          if (dump_file && (dump_flags & TDF_DETAILS) != 0)
            {
@@ -653,8 +725,6 @@ get_string_length (strinfo *si)
          /* FALLTHRU */
        case BUILT_IN_STPCPY:
        case BUILT_IN_STPCPY_CHK:
-       case BUILT_IN_STPCPY_CHKP:
-       case BUILT_IN_STPCPY_CHK_CHKP:
          gcc_assert (lhs != NULL_TREE);
          loc = gimple_location (stmt);
          set_endptr_and_length (loc, si, lhs);
@@ -676,12 +746,364 @@ get_string_length (strinfo *si)
   return si->nonzero_chars;
 }
 
+/* Dump strlen data to FP for statement STMT.  When non-null, RVALS
+   points to EVRP info and is used to dump strlen range for non-constant
+   results.  */
+
+DEBUG_FUNCTION void
+dump_strlen_info (FILE *fp, gimple *stmt, const vr_values *rvals)
+{
+  if (stmt)
+    {
+      fprintf (fp, "\nDumping strlen pass data after ");
+      print_gimple_expr (fp, stmt, TDF_LINENO);
+      fputc ('\n', fp);
+    }
+  else
+    fprintf (fp, "\nDumping strlen pass data\n");
+
+  fprintf (fp, "max_stridx = %i\n", max_stridx);
+  fprintf (fp, "ssa_ver_to_stridx has %u elements\n",
+          ssa_ver_to_stridx.length ());
+  fprintf (fp, "stridx_to_strinfo");
+  if (stridx_to_strinfo)
+    {
+      fprintf (fp, " has %u elements\n", stridx_to_strinfo->length ());
+      for (unsigned i = 0; i != stridx_to_strinfo->length (); ++i)
+       {
+         if (strinfo *si = (*stridx_to_strinfo)[i])
+           {
+             if (!si->idx)
+               continue;
+             fprintf (fp, "  idx = %i", si->idx);
+             if (si->ptr)
+               {
+                 fprintf (fp, ", ptr = ");
+                 print_generic_expr (fp, si->ptr);
+               }
+             fprintf (fp, ", nonzero_chars = ");
+             print_generic_expr (fp, si->nonzero_chars);
+             if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
+               {
+                 value_range_kind rng = VR_UNDEFINED;
+                 wide_int min, max;
+                 if (rvals)
+                   {
+                     const value_range *vr
+                       = CONST_CAST (class vr_values *, rvals)
+                       ->get_value_range (si->nonzero_chars);
+                     rng = vr->kind ();
+                     if (range_int_cst_p (vr))
+                       {
+                         min = wi::to_wide (vr->min ());
+                         max = wi::to_wide (vr->max ());
+                       }
+                     else
+                       rng = VR_UNDEFINED;
+                   }
+                 else
+                   rng = get_range_info (si->nonzero_chars, &min, &max);
+
+                 if (rng == VR_RANGE || rng == VR_ANTI_RANGE)
+                   {
+                     fprintf (fp, " %s[%llu, %llu]",
+                              rng == VR_RANGE ? "" : "~",
+                              (long long) min.to_uhwi (),
+                              (long long) max.to_uhwi ());
+                   }
+               }
+             fprintf (fp, " , refcount = %i", si->refcount);
+             if (si->stmt)
+               {
+                 fprintf (fp, ", stmt = ");
+                 print_gimple_expr (fp, si->stmt, 0);
+               }
+             if (si->writable)
+               fprintf (fp, ", writable");
+             if (si->full_string_p)
+               fprintf (fp, ", full_string_p");
+             if (strinfo *next = get_next_strinfo (si))
+               {
+                 fprintf (fp, ", {");
+                 do
+                   fprintf (fp, "%i%s", next->idx, next->first ? ", " : "");
+                 while ((next = get_next_strinfo (next)));
+                 fprintf (fp, "}");
+               }
+             fputs ("\n", fp);
+           }
+       }
+    }
+  else
+    fprintf (fp, " = null\n");
+
+  fprintf (fp, "decl_to_stridxlist_htab");
+  if (decl_to_stridxlist_htab)
+    {
+      fputs ("\n", fp);
+      typedef decl_to_stridxlist_htab_t::iterator iter_t;
+      for (iter_t it = decl_to_stridxlist_htab->begin ();
+          it != decl_to_stridxlist_htab->end (); ++it)
+       {
+         tree decl = (*it).first;
+         stridxlist *list = &(*it).second;
+         fprintf (fp, "  decl = ");
+         print_generic_expr (fp, decl);
+         if (list)
+           {
+             fprintf (fp, ", offsets = {");
+             for (; list; list = list->next)
+               fprintf (fp, "%lli%s", (long long) list->offset,
+                        list->next ? ", " : "");
+             fputs ("}", fp);
+           }
+         fputs ("\n", fp);
+       }
+    }
+  else
+    fprintf (fp, " = null\n");
+
+  if (laststmt.stmt)
+    {
+      fprintf (fp, "laststmt = ");
+      print_gimple_expr (fp, laststmt.stmt, 0);
+      fprintf (fp, ", len = ");
+      print_generic_expr (fp, laststmt.len);
+      fprintf (fp, ", stridx = %i\n", laststmt.stridx);
+    }
+}
+
+/* Attempt to determine the length of the string SRC.  On success, store
+   the length in *PDATA and return true.  Otherwise, return false.
+   VISITED is a bitmap of visited PHI nodes.  RVALS points to EVRP info
+   and PSSA_DEF_MAX to an SSA_NAME assignment limit used to prevent runaway
+   recursion.  */
+
+static bool
+get_range_strlen_dynamic (tree src, c_strlen_data *pdata, bitmap *visited,
+                         const vr_values *rvals, unsigned *pssa_def_max)
+{
+  int idx = get_stridx (src);
+  if (!idx)
+    {
+      if (TREE_CODE (src) == SSA_NAME)
+       {
+         gimple *def_stmt = SSA_NAME_DEF_STMT (src);
+         if (gimple_code (def_stmt) == GIMPLE_PHI)
+           {
+             if (!*visited)
+               *visited = BITMAP_ALLOC (NULL);
+
+             if (!bitmap_set_bit (*visited, SSA_NAME_VERSION (src)))
+               return true;
+
+             if (*pssa_def_max == 0)
+               return false;
+
+             --*pssa_def_max;
+
+             /* Iterate over the PHI arguments and determine the minimum
+                and maximum length/size of each and incorporate them into
+                the overall result.  */
+             gphi *phi = as_a <gphi *> (def_stmt);
+             for (unsigned i = 0; i != gimple_phi_num_args (phi); ++i)
+               {
+                 tree arg = gimple_phi_arg_def (phi, i);
+                 if (arg == gimple_phi_result (def_stmt))
+                   continue;
+
+                 c_strlen_data argdata = { };
+                 if (get_range_strlen_dynamic (arg, &argdata, visited, rvals,
+                                               pssa_def_max))
+                   {
+                     /* Set the DECL of an unterminated array this argument
+                        refers to if one hasn't been found yet.  */
+                     if (!pdata->decl && argdata.decl)
+                       pdata->decl = argdata.decl;
+
+                     if (!argdata.minlen
+                         || (integer_zerop (argdata.minlen)
+                             && (!argdata.maxbound
+                                 || integer_all_onesp (argdata.maxbound))
+                             && integer_all_onesp (argdata.maxlen)))
+                       {
+                         /* Set the upper bound of the length to unbounded.  */
+                         pdata->maxlen = build_all_ones_cst (size_type_node);
+                         continue;
+                       }
+
+                     /* Adjust the minimum and maximum length determined
+                        so far and the upper bound on the array size.  */
+                     if (!pdata->minlen
+                         || tree_int_cst_lt (argdata.minlen, pdata->minlen))
+                       pdata->minlen = argdata.minlen;
+                     if (!pdata->maxlen
+                         || (argdata.maxlen
+                             && tree_int_cst_lt (pdata->maxlen, argdata.maxlen)))
+                       pdata->maxlen = argdata.maxlen;
+                     if (!pdata->maxbound
+                         || TREE_CODE (pdata->maxbound) != INTEGER_CST
+                         || (argdata.maxbound
+                             && tree_int_cst_lt (pdata->maxbound,
+                                                 argdata.maxbound)
+                             && !integer_all_onesp (argdata.maxbound)))
+                       pdata->maxbound = argdata.maxbound;
+                   }
+                 else
+                   pdata->maxlen = build_all_ones_cst (size_type_node);
+               }
+
+             return true;
+           }
+       }
+
+      /* Return success regardless of the result and handle *PDATA
+        in the caller.  */
+      get_range_strlen (src, pdata, 1);
+      return true;
+    }
+
+  if (idx < 0)
+    {
+      /* SRC is a string of constant length.  */
+      pdata->minlen = build_int_cst (size_type_node, ~idx);
+      pdata->maxlen = pdata->minlen;
+      pdata->maxbound = pdata->maxlen;
+      return true;
+    }
+
+  if (strinfo *si = get_strinfo (idx))
+    {
+      pdata->minlen = get_string_length (si);
+      if (!pdata->minlen && si->nonzero_chars)
+       {
+         if (TREE_CODE (si->nonzero_chars) == INTEGER_CST)
+           pdata->minlen = si->nonzero_chars;
+         else if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
+           {
+             const value_range *vr
+               = CONST_CAST (class vr_values *, rvals)
+               ->get_value_range (si->nonzero_chars);
+             if (vr->kind () == VR_RANGE
+                 && range_int_cst_p (vr))
+               {
+                 pdata->minlen = vr->min ();
+                 pdata->maxlen = vr->max ();
+               }
+             else
+               pdata->minlen = build_zero_cst (size_type_node);
+           }
+         else
+           pdata->minlen = build_zero_cst (size_type_node);
+
+         tree base = si->ptr;
+         if (TREE_CODE (base) == ADDR_EXPR)
+           base = TREE_OPERAND (base, 0);
+
+         HOST_WIDE_INT off;
+         poly_int64 poff;
+         base = get_addr_base_and_unit_offset (base, &poff);
+         if (base
+             && DECL_P (base)
+             && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE
+             && TYPE_SIZE_UNIT (TREE_TYPE (base))
+             && poff.is_constant (&off))
+           {
+             tree basetype = TREE_TYPE (base);
+             tree size = TYPE_SIZE_UNIT (basetype);
+             ++off;   /* Increment for the terminating nul.  */
+             pdata->maxlen = fold_build2 (MINUS_EXPR, size_type_node, size,
+                                          build_int_cst (size_type_node, off));
+             pdata->maxbound = pdata->maxlen;
+           }
+         else
+           pdata->maxlen = build_all_ones_cst (size_type_node);
+       }
+      else if (pdata->minlen && TREE_CODE (pdata->minlen) == SSA_NAME)
+       {
+         const value_range *vr
+           = CONST_CAST (class vr_values *, rvals)
+           ->get_value_range (si->nonzero_chars);
+         if (vr->kind () == VR_RANGE
+             && range_int_cst_p (vr))
+           {
+             pdata->minlen = vr->min ();
+             pdata->maxlen = vr->max ();
+             pdata->maxbound = pdata->maxlen;
+           }
+         else
+           {
+             pdata->minlen = build_zero_cst (size_type_node);
+             pdata->maxlen = build_all_ones_cst (size_type_node);
+           }
+       }
+      else if (pdata->minlen && TREE_CODE (pdata->minlen) == INTEGER_CST)
+       {
+         pdata->maxlen = pdata->minlen;
+         pdata->maxbound = pdata->minlen;
+       }
+      else
+       {
+         /* For PDATA->MINLEN that's a non-constant expression such
+            as PLUS_EXPR whose value range is unknown, set the bounds
+            to zero and SIZE_MAX.  */
+         pdata->minlen = build_zero_cst (size_type_node);
+         pdata->maxlen = build_all_ones_cst (size_type_node);
+       }
+
+      return true;
+    }
+
+  return false;
+}
+
+/* Analogous to get_range_strlen but for dynamically created strings,
+   i.e., those created by calls to strcpy as opposed to just string
+   constants.
+   Try to obtain the range of the lengths of the string(s) referenced
+   by SRC, or the size of the largest array SRC refers to if the range
+   of lengths cannot be determined, and store all in *PDATA.  RVALS
+   points to EVRP info.  */
+
+void
+get_range_strlen_dynamic (tree src, c_strlen_data *pdata,
+                         const vr_values *rvals)
+{
+  bitmap visited = NULL;
+  tree maxbound = pdata->maxbound;
+
+  unsigned limit = PARAM_VALUE (PARAM_SSA_NAME_DEF_CHAIN_LIMIT);
+  if (!get_range_strlen_dynamic (src, pdata, &visited, rvals, &limit))
+    {
+      /* On failure extend the length range to an impossible maximum
+        (a valid MAXLEN must be less than PTRDIFF_MAX - 1).  Other
+        members can stay unchanged regardless.  */
+      pdata->minlen = ssize_int (0);
+      pdata->maxlen = build_all_ones_cst (size_type_node);
+    }
+  else if (!pdata->minlen)
+    pdata->minlen = ssize_int (0);
+
+  /* If it's unchanged from it initial non-null value, set the conservative
+     MAXBOUND to SIZE_MAX.  Otherwise leave it null (if it is null).  */
+  if (maxbound && pdata->maxbound == maxbound)
+    pdata->maxbound = build_all_ones_cst (size_type_node);
+
+  if (visited)
+    BITMAP_FREE (visited);
+}
+
 /* Invalidate string length information for strings whose length
-   might change due to stores in stmt.  */
+   might change due to stores in stmt, except those marked DON'T
+   INVALIDATE.  For string-modifying statements, ZERO_WRITE is
+   set when the statement wrote only zeros.  */
 
 static bool
-maybe_invalidate (gimple *stmt)
+maybe_invalidate (gimple *stmt, bool zero_write = false)
 {
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  %s()\n", __func__);
+
   strinfo *si;
   unsigned int i;
   bool nonempty = false;
@@ -692,18 +1114,60 @@ maybe_invalidate (gimple *stmt)
        if (!si->dont_invalidate)
          {
            ao_ref r;
-           /* Do not use si->nonzero_chars.  */
-           ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE);
+           tree size = NULL_TREE;
+           if (si->nonzero_chars)
+             {
+               /* Include the terminating nul in the size of the string
+                  to consider when determining possible clobber.  */
+               tree type = TREE_TYPE (si->nonzero_chars);
+               size = fold_build2 (PLUS_EXPR, type, si->nonzero_chars,
+                                   build_int_cst (type, 1));
+             }
+           ao_ref_init_from_ptr_and_size (&r, si->ptr, size);
            if (stmt_may_clobber_ref_p_1 (stmt, &r))
              {
+               if (dump_file && (dump_flags & TDF_DETAILS))
+                 {
+                   if (size && tree_fits_uhwi_p (size))
+                     fprintf (dump_file,
+                              "  statement may clobber string "
+                              HOST_WIDE_INT_PRINT_UNSIGNED " long\n",
+                              tree_to_uhwi (size));
+                   else
+                     fprintf (dump_file,
+                              "  statement may clobber string\n");
+                 }
+
                set_strinfo (i, NULL);
                free_strinfo (si);
                continue;
              }
+
+           if (size
+               && !zero_write
+               && si->stmt
+               && is_gimple_call (si->stmt)
+               && (DECL_FUNCTION_CODE (gimple_call_fndecl (si->stmt))
+                   == BUILT_IN_CALLOC))
+             {
+               /* If the clobber test above considered the length of
+                  the string (including the nul), then for (potentially)
+                  non-zero writes that might modify storage allocated by
+                  calloc consider the whole object and if it might be
+                  clobbered by the statement reset the allocation
+                  statement.  */
+               ao_ref_init_from_ptr_and_size (&r, si->ptr, NULL_TREE);
+               if (stmt_may_clobber_ref_p_1 (stmt, &r))
+                 si->stmt = NULL;
+             }
          }
        si->dont_invalidate = false;
        nonempty = true;
       }
+
+  if (dump_file && (dump_flags & TDF_DETAILS))
+    fprintf (dump_file, "  %s() ==> %i\n", __func__, nonempty);
+
   return nonempty;
 }
 
@@ -793,9 +1257,9 @@ get_stridx_plus_constant (strinfo *basesi, unsigned HOST_WIDE_INT off,
   si = new_strinfo (ptr, idx, build_int_cst (size_type_node, nonzero_chars),
                    basesi->full_string_p);
   set_strinfo (idx, si);
-  if (chainsi->next)
+  if (strinfo *nextsi = get_strinfo (chainsi->next))
     {
-      strinfo *nextsi = unshare_strinfo (get_strinfo (chainsi->next));
+      nextsi = unshare_strinfo (nextsi);
       si->next = nextsi->idx;
       nextsi->prev = idx;
     }
@@ -914,9 +1378,9 @@ adjust_related_strinfos (location_t loc, strinfo *origsi, tree adj)
          tree tem;
 
          si = unshare_strinfo (si);
-         /* We shouldn't see delayed lengths here; the caller must have
-            calculated the old length in order to calculate the
-            adjustment.  */
+         /* We shouldn't see delayed lengths here; the caller must
+            have calculated the old length in order to calculate
+            the adjustment.  */
          gcc_assert (si->nonzero_chars);
          tem = fold_convert_loc (loc, TREE_TYPE (si->nonzero_chars), adj);
          si->nonzero_chars = fold_build2_loc (loc, PLUS_EXPR,
@@ -994,14 +1458,21 @@ valid_builtin_call (gimple *stmt)
     return false;
 
   tree callee = gimple_call_fndecl (stmt);
+  tree decl = builtin_decl_explicit (DECL_FUNCTION_CODE (callee));
+  if (decl
+      && decl != callee
+      && !gimple_builtin_call_types_compatible_p (stmt, decl))
+    return false;
+
   switch (DECL_FUNCTION_CODE (callee))
     {
     case BUILT_IN_MEMCMP:
     case BUILT_IN_MEMCMP_EQ:
+    case BUILT_IN_STRCMP:
+    case BUILT_IN_STRNCMP:
     case BUILT_IN_STRCHR:
-    case BUILT_IN_STRCHR_CHKP:
     case BUILT_IN_STRLEN:
-    case BUILT_IN_STRLEN_CHKP:
+    case BUILT_IN_STRNLEN:
       /* The above functions should be pure.  Punt if they aren't.  */
       if (gimple_vdef (stmt) || gimple_vuse (stmt) == NULL_TREE)
        return false;
@@ -1011,25 +1482,21 @@ valid_builtin_call (gimple *stmt)
     case BUILT_IN_MALLOC:
     case BUILT_IN_MEMCPY:
     case BUILT_IN_MEMCPY_CHK:
-    case BUILT_IN_MEMCPY_CHKP:
-    case BUILT_IN_MEMCPY_CHK_CHKP:
     case BUILT_IN_MEMPCPY:
     case BUILT_IN_MEMPCPY_CHK:
-    case BUILT_IN_MEMPCPY_CHKP:
-    case BUILT_IN_MEMPCPY_CHK_CHKP:
     case BUILT_IN_MEMSET:
     case BUILT_IN_STPCPY:
     case BUILT_IN_STPCPY_CHK:
-    case BUILT_IN_STPCPY_CHKP:
-    case BUILT_IN_STPCPY_CHK_CHKP:
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
     case BUILT_IN_STRCAT:
     case BUILT_IN_STRCAT_CHK:
-    case BUILT_IN_STRCAT_CHKP:
-    case BUILT_IN_STRCAT_CHK_CHKP:
     case BUILT_IN_STRCPY:
     case BUILT_IN_STRCPY_CHK:
-    case BUILT_IN_STRCPY_CHKP:
-    case BUILT_IN_STRCPY_CHK_CHKP:
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
       /* The above functions should be neither const nor pure.  Punt if they
         aren't.  */
       if (gimple_vdef (stmt) == NULL_TREE || gimple_vuse (stmt) == NULL_TREE)
@@ -1100,7 +1567,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);
@@ -1118,10 +1585,6 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
     case BUILT_IN_MEMCPY:
     case BUILT_IN_MEMCPY_CHK:
       break;
-    case BUILT_IN_MEMCPY_CHKP:
-    case BUILT_IN_MEMCPY_CHK_CHKP:
-      len_arg_no = 4;
-      break;
     default:
       return;
     }
@@ -1137,6 +1600,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)
     {
@@ -1154,15 +1624,72 @@ 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 that is the result of a strlen()
+   call, or when BOUND is non-null, of a strnlen() call, set LHS
+   range info to [0, min (MAX, BOUND)] when the range includes more
+   than one value and return LHS.  Otherwise, when the range
+   [MIN, MAX] is such that MIN == MAX, return the tree representation
+   of (MIN). The latter allows callers to fold suitable strnlen() calls
+   to constants.  */
+
+tree
+set_strlen_range (tree lhs, wide_int min, wide_int max,
+                 tree bound /* = NULL_TREE */)
+{
+  if (TREE_CODE (lhs) != SSA_NAME
+      || !INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+    return NULL_TREE;
 
-static void
-maybe_set_strlen_range (tree lhs, tree src)
+  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 by callers.  */
+             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;
+}
+
+/* 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 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)
     {
@@ -1172,24 +1699,58 @@ maybe_set_strlen_range (tree lhs, tree src)
        src = gimple_assign_rhs1 (def);
     }
 
-  if (TREE_CODE (src) != ADDR_EXPR)
-    return;
+  /* The longest string is PTRDIFF_MAX - 1 bytes including the final
+     NUL so that the difference between a pointer to just past it and
+     one to its beginning is positive.  */
+  wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2;
 
-  /* 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);
+      if (TREE_CODE (src) != MEM_REF
+         && !array_at_struct_end_p (src))
+       {
+         tree type = TREE_TYPE (src);
+         tree size = TYPE_SIZE_UNIT (type);
+         if (size
+             && TREE_CODE (size) == INTEGER_CST
+             && !integer_zerop (size))
+           {
+             /* Even though such uses of strlen would be undefined,
+                avoid relying on arrays of arrays in case some genius
+                decides to call strlen on an unterminated array element
+                that's followed by a terminated one.  Likewise, avoid
+                assuming that a struct array member is necessarily
+                nul-terminated (the nul may be in the member that
+                follows).  In those cases, assume that the length
+                of the string stored in such an array is bounded
+                by the size of the enclosing object if one can be
+                determined.  */
+             tree base = get_base_address (src);
+             if (VAR_P (base))
+               {
+                 if (tree size = DECL_SIZE_UNIT (base))
+                   if (size
+                       && TREE_CODE (size) == INTEGER_CST
+                       && TREE_CODE (TREE_TYPE (base)) != POINTER_TYPE)
+                     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;
+       }
+    }
+
+  wide_int min = wi::zero (max.get_precision ());
+  return set_strlen_range (lhs, min, max, bound);
 }
 
 /* Handle a strlen call.  If strlen of the argument is known, replace
@@ -1199,29 +1760,45 @@ 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);
-  if (idx)
+  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 || (bound && integer_zerop (bound)))
     {
       strinfo *si = NULL;
       tree rhs;
 
       if (idx < 0)
        rhs = build_int_cst (TREE_TYPE (lhs), ~idx);
+      else if (idx == 0)
+       rhs = bound;
       else
        {
          rhs = NULL_TREE;
          si = get_strinfo (idx);
          if (si != NULL)
-           rhs = get_string_length (si);
+           {
+             rhs = get_string_length (si);
+             /* For strnlen, if bound is constant, even if si is not known
+                to be zero terminated, if we know at least bound bytes are
+                not zero, the return value will be bound.  */
+             if (rhs == NULL_TREE
+                 && bound != NULL_TREE
+                 && TREE_CODE (bound) == INTEGER_CST
+                 && si->nonzero_chars != NULL_TREE
+                 && TREE_CODE (si->nonzero_chars) == INTEGER_CST
+                 && tree_int_cst_le (bound, si->nonzero_chars))
+               rhs = bound;
+           }
        }
       if (rhs != NULL_TREE)
        {
@@ -1232,8 +1809,11 @@ 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);
@@ -1243,7 +1823,10 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
              fprintf (dump_file, "into: ");
              print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
            }
+
          if (si != NULL
+             /* Don't update anything for strnlen.  */
+             && bound == NULL_TREE
              && TREE_CODE (si->nonzero_chars) != SSA_NAME
              && TREE_CODE (si->nonzero_chars) != INTEGER_CST
              && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (lhs))
@@ -1253,16 +1836,21 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
              gcc_assert (si->full_string_p);
            }
 
-         if (strlen_to_stridx)
-           {
-             location_t loc = gimple_location (stmt);
-             strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc));
-           }
+         if (strlen_to_stridx
+             && (bound == NULL_TREE
+                 /* For strnlen record this only if the call is proven
+                    to return the same value as strlen would.  */
+                 || (TREE_CODE (bound) == INTEGER_CST
+                     && TREE_CODE (rhs) == INTEGER_CST
+                     && tree_int_cst_lt (rhs, bound))))
+           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
@@ -1280,11 +1868,16 @@ 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);
                  adjust_related_strinfos (loc, si, adj);
+                 /* Use the constant minimim length as the lower bound
+                    of the non-constant length.  */
+                 wide_int min = wi::to_wide (old);
+                 wide_int max
+                   = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)) - 2;
+                 set_strlen_range (lhs, min, max);
                }
              else
                {
@@ -1298,19 +1891,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));
     }
 }
 
@@ -1325,12 +1941,11 @@ handle_builtin_strchr (gimple_stmt_iterator *gsi)
   tree src;
   gimple *stmt = gsi_stmt (*gsi);
   tree lhs = gimple_call_lhs (stmt);
-  bool with_bounds = gimple_call_with_bounds_p (stmt);
 
   if (lhs == NULL_TREE)
     return;
 
-  if (!integer_zerop (gimple_call_arg (stmt, with_bounds ? 2 : 1)))
+  if (!integer_zerop (gimple_call_arg (stmt, 1)))
     return;
 
   src = gimple_call_arg (stmt, 0);
@@ -1437,9 +2052,8 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi, *olddsi, *zsi;
   location_t loc;
-  bool with_bounds = gimple_call_with_bounds_p (stmt);
 
-  src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
+  src = gimple_call_arg (stmt, 1);
   dst = gimple_call_arg (stmt, 0);
   lhs = gimple_call_lhs (stmt);
   idx = get_stridx (src);
@@ -1470,15 +2084,11 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       {
       case BUILT_IN_STRCPY:
       case BUILT_IN_STRCPY_CHK:
-      case BUILT_IN_STRCPY_CHKP:
-      case BUILT_IN_STRCPY_CHK_CHKP:
        if (lhs != NULL_TREE || !builtin_decl_implicit_p (BUILT_IN_STPCPY))
          return;
        break;
       case BUILT_IN_STPCPY:
       case BUILT_IN_STPCPY_CHK:
-      case BUILT_IN_STPCPY_CHKP:
-      case BUILT_IN_STPCPY_CHK_CHKP:
        if (lhs == NULL_TREE)
          return;
        else
@@ -1561,8 +2171,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;
@@ -1596,19 +2205,16 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   switch (bcode)
     {
     case BUILT_IN_STRCPY:
-    case BUILT_IN_STRCPY_CHKP:
       fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
       if (lhs)
        ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx;
       break;
     case BUILT_IN_STRCPY_CHK:
-    case BUILT_IN_STRCPY_CHK_CHKP:
       fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
       if (lhs)
        ssa_ver_to_stridx[SSA_NAME_VERSION (lhs)] = didx;
       break;
     case BUILT_IN_STPCPY:
-    case BUILT_IN_STPCPY_CHKP:
       /* This would need adjustment of the lhs (subtract one),
         or detection that the trailing '\0' doesn't need to be
         written, if it will be immediately overwritten.
@@ -1620,7 +2226,6 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
        }
       break;
     case BUILT_IN_STPCPY_CHK:
-    case BUILT_IN_STPCPY_CHK_CHKP:
       /* This would need adjustment of the lhs (subtract one),
         or detection that the trailing '\0' doesn't need to be
         written, if it will be immediately overwritten.
@@ -1653,8 +2258,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;
@@ -1670,33 +2274,14 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       fprintf (dump_file, "Optimizing: ");
       print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
     }
-  if (with_bounds)
-    {
-      fn = chkp_maybe_create_clone (fn)->decl;
-      if (gimple_call_num_args (stmt) == 4)
-       success = update_gimple_call (gsi, fn, 5, dst,
-                                     gimple_call_arg (stmt, 1),
-                                     src,
-                                     gimple_call_arg (stmt, 3),
-                                     len);
-      else
-       success = update_gimple_call (gsi, fn, 6, dst,
-                                     gimple_call_arg (stmt, 1),
-                                     src,
-                                     gimple_call_arg (stmt, 3),
-                                     len,
-                                     gimple_call_arg (stmt, 4));
-    }
+  if (gimple_call_num_args (stmt) == 2)
+    success = update_gimple_call (gsi, fn, 3, dst, src, len);
   else
-    if (gimple_call_num_args (stmt) == 2)
-      success = update_gimple_call (gsi, fn, 3, dst, src, len);
-    else
-      success = update_gimple_call (gsi, fn, 4, dst, src, len,
-                                   gimple_call_arg (stmt, 2));
+    success = update_gimple_call (gsi, fn, 4, dst, src, len,
+                                 gimple_call_arg (stmt, 2));
   if (success)
     {
       stmt = gsi_stmt (*gsi);
-      gimple_call_set_with_bounds (stmt, with_bounds);
       update_stmt (stmt);
       if (dump_file && (dump_flags & TDF_DETAILS) != 0)
        {
@@ -1733,7 +2318,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
@@ -1770,20 +2355,29 @@ 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;
 }
 
-/* A helper of handle_builtin_stxncpy.  Check to see if the specified
-   bound is a) equal to the size of the destination DST and if so, b)
-   if it's immediately followed by DST[CNT - 1] = '\0'.  If a) holds
-   and b) does not, warn.  Otherwise, do nothing.  Return true if
-   diagnostic has been issued.
+/* Called by handle_builtin_stxncpy and by gimple_fold_builtin_strncpy
+   in gimple-fold.c.
+   Check to see if the specified bound is a) equal to the size of
+   the destination DST and if so, b) if it's immediately followed by
+   DST[CNT - 1] = '\0'.  If a) holds and b) does not, warn.  Otherwise,
+   do nothing.  Return true if diagnostic has been issued.
 
    The purpose is to diagnose calls to strncpy and stpncpy that do
    not nul-terminate the copy while allowing for the idiom where
@@ -1794,10 +2388,12 @@ is_strlen_related_p (tree src, tree len)
      a[sizeof a - 1] = '\0';
 */
 
-static bool
+bool
 maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
 {
   gimple *stmt = gsi_stmt (gsi);
+  if (gimple_no_warning_p (stmt))
+    return false;
 
   wide_int cntrange[2];
 
@@ -1805,7 +2401,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)
@@ -1830,8 +2426,11 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
     return false;
 
   /* Negative value is the constant string length.  If it's less than
-     the lower bound there is no truncation.  */
-  int sidx = get_stridx (src);
+     the lower bound there is no truncation.  Avoid calling get_stridx()
+     when ssa_ver_to_stridx is empty.  That implies the caller isn't
+     running under the control of this pass and ssa_ver_to_stridx hasn't
+     been created yet.  */
+  int sidx = ssa_ver_to_stridx.length () ? get_stridx (src) : 0;
   if (sidx < 0 && wi::gtu_p (cntrange[0], ~sidx))
     return false;
 
@@ -1840,22 +2439,60 @@ 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;
 
   /* Look for dst[i] = '\0'; after the stxncpy() call and if found
      avoid the truncation warning.  */
-  gsi_next (&gsi);
+  gsi_next_nondebug (&gsi);
   gimple *next_stmt = gsi_stmt (gsi);
-
-  if (!gsi_end_p (gsi) && is_gimple_assign (next_stmt))
+  if (!next_stmt)
     {
-      tree lhs = gimple_assign_lhs (next_stmt);
-      tree_code code = TREE_CODE (lhs);
-      if (code == ARRAY_REF || code == MEM_REF)
+      /* When there is no statement in the same basic block check
+        the immediate successor block.  */
+      if (basic_block bb = gimple_bb (stmt))
+       {
+         if (single_succ_p (bb))
+           {
+             /* For simplicity, ignore blocks with multiple outgoing
+                edges for now and only consider successor blocks along
+                normal edges.  */
+             edge e = EDGE_SUCC (bb, 0);
+             if (!(e->flags & EDGE_ABNORMAL))
+               {
+                 gsi = gsi_start_bb (e->dest);
+                 next_stmt = gsi_stmt (gsi);
+                 if (next_stmt && is_gimple_debug (next_stmt))
+                   {
+                     gsi_next_nondebug (&gsi);
+                     next_stmt = gsi_stmt (gsi);
+                   }
+               }
+           }
+       }
+    }
+
+  if (next_stmt && is_gimple_assign (next_stmt))
+    {
+      tree lhs = gimple_assign_lhs (next_stmt);
+      tree_code code = TREE_CODE (lhs);
+      if (code == ARRAY_REF || code == MEM_REF)
        lhs = TREE_OPERAND (lhs, 0);
 
       tree func = gimple_call_fndecl (stmt);
@@ -1877,6 +2514,7 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
       poly_int64 lhsoff;
       tree lhsbase = get_addr_base_and_unit_offset (lhs, &lhsoff);
       if (lhsbase
+         && dstbase
          && known_eq (dstoff, lhsoff)
          && operand_equal_p (dstbase, lhsbase, 0))
        return false;
@@ -1896,12 +2534,21 @@ 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])
+      c_strlen_data lendata = { };
+      /* Set MAXBOUND to an arbitrary non-null non-integer node as a request
+        to have it set to the length of the longest string in a PHI.  */
+      lendata.maxbound = src;
+      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
        {
@@ -1910,7 +2557,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]))
@@ -1930,46 +2579,66 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
          lenrange[0] = wi::shwi (0, prec);
        }
 
-      if (wi::geu_p (lenrange[0], cntrange[1]))
+      /* 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,
+                         cntrange[0].to_uhwi (),
+                         "%G%qD output truncated before terminating "
+                         "nul copying %E byte from a string of the "
+                         "same length",
+                         "%G%qD output truncated before terminating nul "
+                         "copying %E bytes from a string of the same "
+                         "length",
+                         stmt, func, cnt);
+      else if (!cat_dstlen_bounded)
        {
-         /* The shortest string is longer than the upper bound of
-            the count so the truncation is certain.  */
-         if (cntrange[0] == cntrange[1])
-           return warning_at (callloc, OPT_Wstringop_truncation,
-                              integer_onep (cnt)
-                              ? 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"),
-                              func, cnt, lenrange[0].to_uhwi ());
-
-         return warning_at (callloc, OPT_Wstringop_truncation,
-                            "%qD output truncated copying between %wu "
-                            "and %wu bytes from a string of length %wu",
-                            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_at (callloc, OPT_Wstringop_truncation,
-                              integer_onep (cnt)
-                              ? 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"),
-                              func, cnt, lenrange[1].to_uhwi ());
-
-         return warning_at (callloc, OPT_Wstringop_truncation,
-                            "%qD output may be truncated copying between %wu "
-                            "and %wu bytes from a string of length %wu",
-                            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))
        {
@@ -1977,9 +2646,9 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
             the lower bound of the specified count but shorter than the
             upper bound the copy may (but need not) be truncated.  */
          return warning_at (callloc, OPT_Wstringop_truncation,
-                            "%qD output may be truncated copying between %wu "
-                            "and %wu bytes from a string of length %wu",
-                            func, cntrange[0].to_uhwi (),
+                            "%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[0].to_uhwi ());
        }
     }
@@ -1996,10 +2665,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,
-                          "%qD specified bound %E equals destination size",
-                          func, cnt);
+                          "%G%qD specified bound %E equals destination size",
+                          stmt, func, cnt);
     }
 
   return false;
@@ -2018,23 +2694,29 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
 
   gimple *stmt = gsi_stmt (*gsi);
 
-  bool with_bounds = gimple_call_with_bounds_p (stmt);
-
-  tree dst = gimple_call_arg (stmt, with_bounds ? 1 : 0);
-  tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
-  tree len = gimple_call_arg (stmt, with_bounds ? 3 : 2);
+  tree dst = gimple_call_arg (stmt, 0);
+  tree src = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
   tree dstsize = NULL_TREE, srcsize = NULL_TREE;
 
   int didx = get_stridx (dst);
   if (strinfo *sidst = didx > 0 ? get_strinfo (didx) : NULL)
     {
-      /* Compute the size of the destination string including the NUL.  */
+      /* Compute the size of the destination string including the nul
+        if it is known to be nul-terminated.  */
       if (sidst->nonzero_chars)
        {
-         tree type = TREE_TYPE (sidst->nonzero_chars);
-         dstsize = fold_build2 (PLUS_EXPR, type, sidst->nonzero_chars,
-                                build_int_cst (type, 1));
+         if (sidst->full_string_p)
+           {
+             /* String is known to be nul-terminated.  */
+             tree type = TREE_TYPE (sidst->nonzero_chars);
+             dstsize = fold_build2 (PLUS_EXPR, type, sidst->nonzero_chars,
+                                    build_int_cst (type, 1));
+           }
+         else
+           dstsize = sidst->nonzero_chars;
        }
+
       dst = sidst->ptr;
     }
 
@@ -2046,12 +2728,18 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
         over the terminating nul so SISRC->DONT_INVALIDATE must be left
         clear.  */
 
-      /* Compute the size of the source string including the NUL.  */
+      /* Compute the size of the source string including the terminating
+        nul if its known to be nul-terminated.  */
       if (sisrc->nonzero_chars)
        {
-         tree type = TREE_TYPE (sisrc->nonzero_chars);
-         srcsize = fold_build2 (PLUS_EXPR, type, sisrc->nonzero_chars,
-                                build_int_cst (type, 1));
+         if (sisrc->full_string_p)
+           {
+             tree type = TREE_TYPE (sisrc->nonzero_chars);
+             srcsize = fold_build2 (PLUS_EXPR, type, sisrc->nonzero_chars,
+                                    build_int_cst (type, 1));
+           }
+         else
+           srcsize = sisrc->nonzero_chars;
        }
 
        src = sisrc->ptr;
@@ -2059,8 +2747,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;
@@ -2083,7 +2770,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);
 
@@ -2098,14 +2786,15 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
   if (sisrc == silen
       && is_strlen_related_p (src, len)
       && warning_at (callloc, OPT_Wstringop_truncation,
-                    "%qD output truncated before terminating nul "
+                    "%G%qD output truncated before terminating nul "
                     "copying as many bytes from a string as its length",
-                    func))
+                    stmt, func))
     warned = true;
   else if (silen && is_strlen_related_p (src, silen->ptr))
     warned = warning_at (callloc, OPT_Wstringop_overflow_,
-                        "%qD specified bound depends on the length "
-                        "of the source argument", func);
+                        "%G%qD specified bound depends on the length "
+                        "of the source argument",
+                        stmt, func);
   if (warned)
     {
       location_t strlenloc = pss->second;
@@ -2126,10 +2815,9 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   tree src, dst, len, lhs, oldlen, newlen;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi, *olddsi;
-  bool with_bounds = gimple_call_with_bounds_p (stmt);
 
-  len = gimple_call_arg (stmt, with_bounds ? 4 : 2);
-  src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
+  len = gimple_call_arg (stmt, 2);
+  src = gimple_call_arg (stmt, 1);
   dst = gimple_call_arg (stmt, 0);
   idx = get_stridx (src);
   if (idx == 0)
@@ -2204,6 +2892,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);
 
@@ -2264,8 +2964,6 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
        {
        case BUILT_IN_MEMCPY:
        case BUILT_IN_MEMCPY_CHK:
-       case BUILT_IN_MEMCPY_CHKP:
-       case BUILT_IN_MEMCPY_CHK_CHKP:
          /* Allow adjust_last_stmt to decrease this memcpy's size.  */
          laststmt.stmt = stmt;
          laststmt.len = dsi->nonzero_chars;
@@ -2275,8 +2973,6 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
          break;
        case BUILT_IN_MEMPCPY:
        case BUILT_IN_MEMPCPY_CHK:
-       case BUILT_IN_MEMPCPY_CHKP:
-       case BUILT_IN_MEMPCPY_CHK_CHKP:
          break;
        default:
          gcc_unreachable ();
@@ -2299,9 +2995,8 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi;
   location_t loc = gimple_location (stmt);
-  bool with_bounds = gimple_call_with_bounds_p (stmt);
 
-  tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
+  tree src = gimple_call_arg (stmt, 1);
   tree dst = gimple_call_arg (stmt, 0);
 
   /* Bail if the source is the same as destination.  It will be diagnosed
@@ -2350,8 +3045,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;
@@ -2432,19 +3126,17 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   switch (bcode)
     {
     case BUILT_IN_STRCAT:
-    case BUILT_IN_STRCAT_CHKP:
       if (srclen != NULL_TREE)
        fn = builtin_decl_implicit (BUILT_IN_MEMCPY);
       else
        fn = builtin_decl_implicit (BUILT_IN_STRCPY);
       break;
     case BUILT_IN_STRCAT_CHK:
-    case BUILT_IN_STRCAT_CHK_CHKP:
       if (srclen != NULL_TREE)
        fn = builtin_decl_explicit (BUILT_IN_MEMCPY_CHK);
       else
        fn = builtin_decl_explicit (BUILT_IN_STRCPY_CHK);
-      objsz = gimple_call_arg (stmt, with_bounds ? 4 : 2);
+      objsz = gimple_call_arg (stmt, 2);
       break;
     default:
       gcc_unreachable ();
@@ -2459,12 +3151,12 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 
       /* Compute the size of the source sequence, including the nul.  */
       tree srcsize = srclen ? srclen : size_zero_node;
-      srcsize = fold_build2 (PLUS_EXPR, type, srcsize, build_int_cst (type, 1));
-
+      tree one = build_int_cst (type, 1);
+      srcsize = fold_build2 (PLUS_EXPR, type, srcsize, one);
+      tree dstsize = fold_build2 (PLUS_EXPR, type, dstlen, one);
       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, dstsize, srcsize))
        {
          gimple_set_no_warning (stmt, true);
          set_no_warning = true;
@@ -2486,46 +3178,33 @@ 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: ");
       print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
     }
-  if (with_bounds)
-    {
-      fn = chkp_maybe_create_clone (fn)->decl;
-      if (srclen != NULL_TREE)
-       success = update_gimple_call (gsi, fn, 5 + (objsz != NULL_TREE),
-                                     dst,
-                                     gimple_call_arg (stmt, 1),
-                                     src,
-                                     gimple_call_arg (stmt, 3),
-                                     len, objsz);
-      else
-       success = update_gimple_call (gsi, fn, 4 + (objsz != NULL_TREE),
-                                     dst,
-                                     gimple_call_arg (stmt, 1),
-                                     src,
-                                     gimple_call_arg (stmt, 3),
-                                     objsz);
-    }
+  if (srclen != NULL_TREE)
+    success = update_gimple_call (gsi, fn, 3 + (objsz != NULL_TREE),
+                                 dst, src, len, objsz);
   else
-    if (srclen != NULL_TREE)
-      success = update_gimple_call (gsi, fn, 3 + (objsz != NULL_TREE),
-                                   dst, src, len, objsz);
-    else
-      success = update_gimple_call (gsi, fn, 2 + (objsz != NULL_TREE),
-                                   dst, src, objsz);
+    success = update_gimple_call (gsi, fn, 2 + (objsz != NULL_TREE),
+                                 dst, src, objsz);
   if (success)
     {
       stmt = gsi_stmt (*gsi);
-      gimple_call_set_with_bounds (stmt, with_bounds);
       update_stmt (stmt);
       if (dump_file && (dump_flags & TDF_DETAILS) != 0)
        {
@@ -2577,27 +3256,32 @@ handle_builtin_malloc (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 
 /* Handle a call to memset.
    After a call to calloc, memset(,0,) is unnecessary.
-   memset(malloc(n),0,n) is calloc(n,1).  */
+   memset(malloc(n),0,n) is calloc(n,1).
+   return true when the call is transfomred, false otherwise.  */
 
 static bool
-handle_builtin_memset (gimple_stmt_iterator *gsi)
+handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write)
 {
   gimple *stmt2 = gsi_stmt (*gsi);
   if (!integer_zerop (gimple_call_arg (stmt2, 1)))
-    return true;
+    return false;
+
+  /* Let the caller know the memset call cleared the destination.  */
+  *zero_write = true;
+
   tree ptr = gimple_call_arg (stmt2, 0);
   int idx1 = get_stridx (ptr);
   if (idx1 <= 0)
-    return true;
+    return false;
   strinfo *si1 = get_strinfo (idx1);
   if (!si1)
-    return true;
+    return false;
   gimple *stmt1 = si1->stmt;
   if (!stmt1 || !is_gimple_call (stmt1))
-    return true;
+    return false;
   tree callee1 = gimple_call_fndecl (stmt1);
   if (!valid_builtin_call (stmt1))
-    return true;
+    return false;
   enum built_in_function code1 = DECL_FUNCTION_CODE (callee1);
   tree size = gimple_call_arg (stmt2, 2);
   if (code1 == BUILT_IN_CALLOC)
@@ -2613,7 +3297,7 @@ handle_builtin_memset (gimple_stmt_iterator *gsi)
       si1->stmt = gsi_stmt (gsi1);
     }
   else
-    return true;
+    return false;
   tree lhs = gimple_call_lhs (stmt2);
   unlink_stmt_vdef (stmt2);
   if (lhs)
@@ -2627,53 +3311,81 @@ handle_builtin_memset (gimple_stmt_iterator *gsi)
       release_defs (stmt2);
     }
 
-  return false;
+  return true;
 }
 
-/* Handle a call to memcmp.  We try to handle small comparisons by
-   converting them to load and compare, and replacing the call to memcmp
-   with a __builtin_memcmp_eq call where possible.  */
+/* Return a pointer to the first such equality expression if RES is used
+   only in expressions testing its equality to zero, and null otherwise.  */
 
-static bool
-handle_builtin_memcmp (gimple_stmt_iterator *gsi)
+static gimple *
+used_only_for_zero_equality (tree res)
 {
-  gcall *stmt2 = as_a <gcall *> (gsi_stmt (*gsi));
-  tree res = gimple_call_lhs (stmt2);
-  tree arg1 = gimple_call_arg (stmt2, 0);
-  tree arg2 = gimple_call_arg (stmt2, 1);
-  tree len = gimple_call_arg (stmt2, 2);
-  unsigned HOST_WIDE_INT leni;
+  gimple *first_use = NULL;
+
   use_operand_p use_p;
   imm_use_iterator iter;
 
-  if (!res)
-    return true;
-
   FOR_EACH_IMM_USE_FAST (use_p, iter, res)
     {
-      gimple *ustmt = USE_STMT (use_p);
+      gimple *use_stmt = USE_STMT (use_p);
 
-      if (is_gimple_debug (ustmt))
-       continue;
-      if (gimple_code (ustmt) == GIMPLE_ASSIGN)
+      if (is_gimple_debug (use_stmt))
+        continue;
+      if (gimple_code (use_stmt) == GIMPLE_ASSIGN)
        {
-         gassign *asgn = as_a <gassign *> (ustmt);
-         tree_code code = gimple_assign_rhs_code (asgn);
-         if ((code != EQ_EXPR && code != NE_EXPR)
-             || !integer_zerop (gimple_assign_rhs2 (asgn)))
-           return true;
+         tree_code code = gimple_assign_rhs_code (use_stmt);
+         if (code == COND_EXPR)
+           {
+             tree cond_expr = gimple_assign_rhs1 (use_stmt);
+             if ((TREE_CODE (cond_expr) != EQ_EXPR
+                  && (TREE_CODE (cond_expr) != NE_EXPR))
+                 || !integer_zerop (TREE_OPERAND (cond_expr, 1)))
+               return NULL;
+           }
+         else if (code == EQ_EXPR || code == NE_EXPR)
+           {
+             if (!integer_zerop (gimple_assign_rhs2 (use_stmt)))
+               return NULL;
+            }
+         else
+           return NULL;
        }
-      else if (gimple_code (ustmt) == GIMPLE_COND)
+      else if (gimple_code (use_stmt) == GIMPLE_COND)
        {
-         tree_code code = gimple_cond_code (ustmt);
+         tree_code code = gimple_cond_code (use_stmt);
          if ((code != EQ_EXPR && code != NE_EXPR)
-             || !integer_zerop (gimple_cond_rhs (ustmt)))
-           return true;
+             || !integer_zerop (gimple_cond_rhs (use_stmt)))
+           return NULL;
        }
       else
-       return true;
+        return NULL;
+
+      if (!first_use)
+       first_use = use_stmt;
     }
 
+  return first_use;
+}
+
+/* Handle a call to memcmp.  We try to handle small comparisons by
+   converting them to load and compare, and replacing the call to memcmp
+   with a __builtin_memcmp_eq call where possible.
+   return true when call is transformed, return false otherwise.  */
+
+static bool
+handle_builtin_memcmp (gimple_stmt_iterator *gsi)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  tree res = gimple_call_lhs (stmt);
+
+  if (!res || !used_only_for_zero_equality (res))
+    return false;
+
+  tree arg1 = gimple_call_arg (stmt, 0);
+  tree arg2 = gimple_call_arg (stmt, 1);
+  tree len = gimple_call_arg (stmt, 2);
+  unsigned HOST_WIDE_INT leni;
+
   if (tree_fits_uhwi_p (len)
       && (leni = tree_to_uhwi (len)) <= GET_MODE_SIZE (word_mode)
       && pow2p_hwi (leni))
@@ -2686,10 +3398,10 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi)
       if (int_mode_for_size (leni, 1).exists (&mode)
          && (align >= leni || !targetm.slow_unaligned_access (mode, align)))
        {
-         location_t loc = gimple_location (stmt2);
+         location_t loc = gimple_location (stmt);
          tree type, off;
          type = build_nonstandard_integer_type (leni, 1);
-         gcc_assert (GET_MODE_BITSIZE (TYPE_MODE (type)) == leni);
+         gcc_assert (known_eq (GET_MODE_BITSIZE (TYPE_MODE (type)), leni));
          tree ptrtype = build_pointer_type_for_mode (char_type_node,
                                                      ptr_mode, true);
          off = build_int_cst (ptrtype, 0);
@@ -2706,11 +3418,475 @@ handle_builtin_memcmp (gimple_stmt_iterator *gsi)
                                                   boolean_type_node,
                                                   arg1, arg2));
          gimplify_and_update_call_from_tree (gsi, res);
-         return false;
+         return true;
+       }
+    }
+
+  gimple_call_set_fndecl (stmt, builtin_decl_explicit (BUILT_IN_MEMCMP_EQ));
+  return true;
+}
+
+/* Given an index to the strinfo vector, compute the string length
+   for the corresponding string. Return -1 when unknown.  */
+
+static HOST_WIDE_INT
+compute_string_length (int idx)
+{
+  HOST_WIDE_INT string_leni = -1;
+  gcc_assert (idx != 0);
+
+  if (idx < 0)
+   return ~idx;
+
+  strinfo *si = get_strinfo (idx);
+  if (si)
+    {
+      tree const_string_len = get_string_length (si);
+      if (const_string_len && tree_fits_shwi_p (const_string_len))
+       string_leni = tree_to_shwi (const_string_len);
+    }
+
+  if (string_leni < 0)
+    return -1;
+
+  return string_leni;
+}
+
+/* Determine the minimum size of the object referenced by DEST expression
+   which must have a pointer type.
+   Return the minimum size of the object if successful or HWI_M1U when
+   the size cannot be determined.  */
+
+static unsigned HOST_WIDE_INT
+determine_min_objsize (tree dest)
+{
+  unsigned HOST_WIDE_INT size = 0;
+
+  init_object_sizes ();
+
+  if (compute_builtin_object_size (dest, 2, &size))
+    return size;
+
+  /* Try to determine the size of the object through the RHS
+     of the assign statement.  */
+  if (TREE_CODE (dest) == SSA_NAME)
+    {
+      gimple *stmt = SSA_NAME_DEF_STMT (dest);
+      if (!is_gimple_assign (stmt))
+       return HOST_WIDE_INT_M1U;
+
+      if (!gimple_assign_single_p (stmt)
+         && !gimple_assign_unary_nop_p (stmt))
+       return HOST_WIDE_INT_M1U;
+
+      dest = gimple_assign_rhs1 (stmt);
+      return determine_min_objsize (dest);
+    }
+
+  /* Try to determine the size of the object from its type.  */
+  if (TREE_CODE (dest) != ADDR_EXPR)
+    return HOST_WIDE_INT_M1U;
+
+  tree type = TREE_TYPE (dest);
+  if (TREE_CODE (type) == POINTER_TYPE)
+    type = TREE_TYPE (type);
+
+  type = TYPE_MAIN_VARIANT (type);
+
+  /* The size of a flexible array cannot be determined.  Otherwise,
+     for arrays with more than one element, return the size of its
+     type.  GCC itself misuses arrays of both zero and one elements
+     as flexible array members so they are excluded as well.  */
+  if (TREE_CODE (type) != ARRAY_TYPE
+      || !array_at_struct_end_p (dest))
+    {
+      tree type_size = TYPE_SIZE_UNIT (type);
+      if (type_size && TREE_CODE (type_size) == INTEGER_CST
+         && !integer_onep (type_size)
+         && !integer_zerop (type_size))
+        return tree_to_uhwi (type_size);
+    }
+
+  return HOST_WIDE_INT_M1U;
+}
+
+/* Given strinfo IDX for ARG, set LENRNG[] to the range of lengths
+   of  the string(s) referenced by ARG if it can be determined.
+   If the length cannot be determined, set *SIZE to the size of
+   the array the string is stored in, if any.  If no such array is
+   known, set *SIZE to -1.  When the strings are nul-terminated set
+   *NULTERM to true, otherwise to false.  Return true on success.  */
+
+static bool
+get_len_or_size (tree arg, int idx, unsigned HOST_WIDE_INT lenrng[2],
+                unsigned HOST_WIDE_INT *size, bool *nulterm)
+{
+  /* Set so that both LEN and ~LEN are invalid lengths, i.e.,
+     maximum possible length + 1.  */
+  lenrng[0] = lenrng[1] = HOST_WIDE_INT_MAX;
+
+  *size = HOST_WIDE_INT_M1U;
+
+  if (idx < 0)
+    {
+      /* IDX is the inverted constant string length.  */
+      lenrng[0] = ~idx;
+      lenrng[1] = lenrng[0];
+      *nulterm = true;
+    }
+  else if (idx == 0)
+    ; /* Handled below.  */
+  else if (strinfo *si = get_strinfo (idx))
+    {
+      if (!si->nonzero_chars)
+       arg = si->ptr;
+      else if (tree_fits_uhwi_p (si->nonzero_chars))
+       {
+         lenrng[0] = tree_to_uhwi (si->nonzero_chars);
+         *nulterm = si->full_string_p;
+         /* Set the upper bound only if the string is known to be
+            nul-terminated, otherwise leave it at maximum + 1.  */
+         if (*nulterm)
+           lenrng[1] = lenrng[0];
+       }
+      else if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
+       {
+         wide_int min, max;
+         value_range_kind rng = get_range_info (si->nonzero_chars, &min, &max);
+         if (rng == VR_RANGE)
+           {
+             lenrng[0] = min.to_uhwi ();
+             lenrng[1] = max.to_uhwi ();
+             *nulterm = si->full_string_p;
+           }
+       }
+      else if (si->ptr)
+       arg = si->ptr;
+    }
+
+  if (lenrng[0] == HOST_WIDE_INT_MAX)
+    {
+      /* Compute the minimum and maximum real or possible lengths.  */
+      c_strlen_data lendata = { };
+      if (get_range_strlen (arg, &lendata, /* eltsize = */1))
+       {
+         if (tree_fits_shwi_p (lendata.maxlen) && !lendata.maxbound)
+           {
+             lenrng[0] = tree_to_shwi (lendata.minlen);
+             lenrng[1] = tree_to_shwi (lendata.maxlen);
+             *nulterm = true;
+           }
+         else if (lendata.maxbound && tree_fits_shwi_p (lendata.maxbound))
+           {
+             /* Set *SIZE to the conservative LENDATA.MAXBOUND which
+                is a conservative estimate of the longest string based
+                on the sizes of the arrays referenced by ARG.  */
+             *size = tree_to_uhwi (lendata.maxbound) + 1;
+             *nulterm = false;
+           }
+       }
+      else
+       {
+         /* Set *SIZE to the size of the smallest object referenced
+            by ARG if ARG denotes a single object, or to HWI_M1U
+            otherwise.  */
+         *size = determine_min_objsize (arg);
+         *nulterm = false;
+       }
+    }
+
+  return lenrng[0] != HOST_WIDE_INT_MAX || *size != HOST_WIDE_INT_M1U;
+}
+
+/* If IDX1 and IDX2 refer to strings A and B of unequal lengths, return
+   the result of 0 == strncmp (A, B, BOUND) (which is the same as strcmp
+   for a sufficiently large BOUND).  If the result is based on the length
+   of one string being greater than the longest string that would fit in
+   the array pointer to by the argument, set *PLEN and *PSIZE to
+   the corresponding length (or its complement when the string is known
+   to be at least as long and need not be nul-terminated) and size.
+   Otherwise return null.  */
+
+static tree
+strxcmp_eqz_result (tree arg1, int idx1, tree arg2, int idx2,
+                   unsigned HOST_WIDE_INT bound, unsigned HOST_WIDE_INT len[2],
+                   unsigned HOST_WIDE_INT *psize)
+{
+  /* Determine the range the length of each string is in and whether it's
+     known to be nul-terminated, or the size of the array it's stored in.  */
+  bool nul1, nul2;
+  unsigned HOST_WIDE_INT siz1, siz2;
+  unsigned HOST_WIDE_INT len1rng[2], len2rng[2];
+  if (!get_len_or_size (arg1, idx1, len1rng, &siz1, &nul1)
+      || !get_len_or_size (arg2, idx2, len2rng, &siz2, &nul2))
+    return NULL_TREE;
+
+  /* BOUND is set to HWI_M1U for strcmp and less to strncmp, and LENiRNG
+     to HWI_MAX when invalid.  Adjust the length of each string to consider
+     to be no more than BOUND.  */
+  if (len1rng[0] < HOST_WIDE_INT_MAX && len1rng[0] > bound)
+    len1rng[0] = bound;
+  if (len1rng[1] < HOST_WIDE_INT_MAX && len1rng[1] > bound)
+    len1rng[1] = bound;
+  if (len2rng[0] < HOST_WIDE_INT_MAX && len2rng[0] > bound)
+    len2rng[0] = bound;
+  if (len2rng[1] < HOST_WIDE_INT_MAX && len2rng[1] > bound)
+    len2rng[1] = bound;
+
+  /* Two empty strings are equal.  */
+  if (len1rng[1] == 0 && len2rng[1] == 0)
+    return integer_one_node;
+
+  /* The strings are definitely unequal when the lower bound of the length
+     of one of them is greater than the length of the longest string that
+     would fit into the other array.  */
+  if (len1rng[0] == HOST_WIDE_INT_MAX
+      && len2rng[0] != HOST_WIDE_INT_MAX
+      && ((len2rng[0] < bound && len2rng[0] >= siz1)
+         || len2rng[0] > siz1))
+    {
+      *psize = siz1;
+      len[0] = len1rng[0];
+      /* Set LEN[0] to the lower bound of ARG1's length when it's
+        nul-terminated or to the complement of its minimum length
+        otherwise,  */
+      len[1] = nul2 ? len2rng[0] : ~len2rng[0];
+      return integer_zero_node;
+    }
+
+  if (len2rng[0] == HOST_WIDE_INT_MAX
+      && len1rng[0] != HOST_WIDE_INT_MAX
+      && ((len1rng[0] < bound && len1rng[0] >= siz2)
+         || len1rng[0] > siz2))
+    {
+      *psize = siz2;
+      len[0] = nul1 ? len1rng[0] : ~len1rng[0];
+      len[1] = len2rng[0];
+      return integer_zero_node;
+    }
+
+  /* The strings are also definitely unequal when their lengths are unequal
+     and at least one is nul-terminated.  */
+  if (len1rng[0] != HOST_WIDE_INT_MAX
+      && len2rng[0] != HOST_WIDE_INT_MAX
+      && ((len1rng[1] < len2rng[0] && nul1)
+         || (len2rng[1] < len1rng[0] && nul2)))
+    {
+      if (bound <= len1rng[0] || bound <= len2rng[0])
+       *psize = bound;
+      else
+       *psize = HOST_WIDE_INT_M1U;
+
+      len[0] = len1rng[0];
+      len[1] = len2rng[0];
+      return integer_zero_node;
+    }
+
+  /* The string lengths may be equal or unequal.  Even when equal and
+     both strings nul-terminated, without the string contents there's
+     no way to determine whether they are equal.  */
+  return NULL_TREE;
+}
+
+/* Diagnose pointless calls to strcmp or strncmp STMT with string
+   arguments of lengths LEN or size SIZ and (for strncmp) BOUND,
+   whose result is used in equality expressions that evaluate to
+   a constant due to one argument being longer than the size of
+   the other.  */
+
+static void
+maybe_warn_pointless_strcmp (gimple *stmt, HOST_WIDE_INT bound,
+                            unsigned HOST_WIDE_INT len[2],
+                            unsigned HOST_WIDE_INT siz)
+{
+  tree lhs = gimple_call_lhs (stmt);
+  gimple *use = used_only_for_zero_equality (lhs);
+  if (!use)
+    return;
+
+  bool at_least = false;
+
+  /* Excessive LEN[i] indicates a lower bound.  */
+  if (len[0] > HOST_WIDE_INT_MAX)
+    {
+      at_least = true;
+      len[0] = ~len[0];
+    }
+
+  if (len[1] > HOST_WIDE_INT_MAX)
+    {
+      at_least = true;
+      len[1] = ~len[1];
+    }
+
+  unsigned HOST_WIDE_INT minlen = MIN (len[0], len[1]);
+
+  /* FIXME: Include a note pointing to the declaration of the smaller
+     array.  */
+  location_t stmt_loc = gimple_nonartificial_location (stmt);
+  if (stmt_loc == UNKNOWN_LOCATION && EXPR_HAS_LOCATION (lhs))
+    stmt_loc = tree_nonartificial_location (lhs);
+  stmt_loc = expansion_point_location_if_in_system_header (stmt_loc);
+
+  tree callee = gimple_call_fndecl (stmt);
+  bool warned = false;
+  if (siz <= minlen && bound == -1)
+    warned = warning_at (stmt_loc, OPT_Wstring_compare,
+                        (at_least
+                         ? G_("%G%qD of a string of length %wu or more and "
+                              "an array of size %wu evaluates to nonzero")
+                         : G_("%G%qD of a string of length %wu and an array "
+                              "of size %wu evaluates to nonzero")),
+                        stmt, callee, minlen, siz);
+  else if (!at_least && siz <= HOST_WIDE_INT_MAX)
+    {
+      if (len[0] != HOST_WIDE_INT_MAX && len[1] != HOST_WIDE_INT_MAX)
+       warned = warning_at (stmt_loc, OPT_Wstring_compare,
+                            "%G%qD of strings of length %wu and %wu "
+                            "and bound of %wu evaluates to nonzero",
+                            stmt, callee, len[0], len[1], bound);
+      else
+       warned = warning_at (stmt_loc, OPT_Wstring_compare,
+                            "%G%qD of a string of length %wu, an array "
+                            "of size %wu and bound of %wu evaluates to "
+                            "nonzero",
+                            stmt, callee, minlen, siz, bound);
+    }
+
+  if (warned)
+    {
+      location_t use_loc = gimple_location (use);
+      if (LOCATION_LINE (stmt_loc) != LOCATION_LINE (use_loc))
+       inform (use_loc, "in this expression");
+    }
+}
+
+
+/* Optimize a call to strcmp or strncmp either by folding it to a constant
+   when possible or by transforming the latter to the former.  Warn about
+   calls where the length of one argument is greater than the size of
+   the array to which the other argument points if the latter's length
+   is not known.  Return true when the call has been transformed into
+   another and false otherwise.  */
+
+static bool
+handle_builtin_string_cmp (gimple_stmt_iterator *gsi)
+{
+  gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+  tree lhs = gimple_call_lhs (stmt);
+
+  if (!lhs)
+    return false;
+
+  tree arg1 = gimple_call_arg (stmt, 0);
+  tree arg2 = gimple_call_arg (stmt, 1);
+  int idx1 = get_stridx (arg1);
+  int idx2 = get_stridx (arg2);
+
+  /* For strncmp set to the the value of the third argument if known.  */
+  HOST_WIDE_INT bound = -1;
+
+  /* Extract the strncmp bound.  */
+  if (gimple_call_num_args (stmt) == 3)
+    {
+      tree len = gimple_call_arg (stmt, 2);
+      if (tree_fits_shwi_p (len))
+        bound = tree_to_shwi (len);
+
+      /* If the bound argument is NOT known, do nothing.  */
+      if (bound < 0)
+       return false;
+    }
+
+  {
+    /* Set to the length of one argument (or its complement if it's
+       the lower bound of a range) and the size of the array storing
+       the other if the result is based on the former being equal to
+       or greater than the latter.  */
+    unsigned HOST_WIDE_INT len[2] = { HOST_WIDE_INT_MAX, HOST_WIDE_INT_MAX };
+    unsigned HOST_WIDE_INT siz = HOST_WIDE_INT_M1U;
+
+    /* Try to determine if the two strings are either definitely equal
+       or definitely unequal and if so, either fold the result to zero
+       (when equal) or set the range of the result to ~[0, 0] otherwise.  */
+    if (tree eqz = strxcmp_eqz_result (arg1, idx1, arg2, idx2, bound,
+                                      len, &siz))
+      {
+       if (integer_zerop (eqz))
+         {
+           maybe_warn_pointless_strcmp (stmt, bound, len, siz);
+
+           /* When the lengths of the first two string arguments are
+              known to be unequal set the range of the result to non-zero.
+              This allows the call to be eliminated if its result is only
+              used in tests for equality to zero.  */
+           wide_int zero = wi::zero (TYPE_PRECISION (TREE_TYPE (lhs)));
+           set_range_info (lhs, VR_ANTI_RANGE, zero, zero);
+           return false;
+         }
+       /* When the two strings are definitely equal (such as when they
+          are both empty) fold the call to the constant result.  */
+       replace_call_with_value (gsi, integer_zero_node);
+       return true;
+      }
+  }
+
+  /* Return if nothing is known about the strings pointed to by ARG1
+     and ARG2.  */
+  if (idx1 == 0 && idx2 == 0)
+    return false;
+
+  /* Determine either the length or the size of each of the strings,
+     whichever is available.  */
+  HOST_WIDE_INT cstlen1 = -1, cstlen2 = -1;
+  HOST_WIDE_INT arysiz1 = -1, arysiz2 = -1;
+
+  if (idx1)
+    cstlen1 = compute_string_length (idx1);
+  else
+    arysiz1 = determine_min_objsize (arg1);
+
+  /* Bail if neither the string length nor the size of the array
+     it is stored in can be determined.  */
+  if (cstlen1 < 0 && arysiz1 < 0)
+    return false;
+
+  /* Repeat for the second argument.  */
+  if (idx2)
+    cstlen2 = compute_string_length (idx2);
+  else
+    arysiz2 = determine_min_objsize (arg2);
+
+  if (cstlen2 < 0 && arysiz2 < 0)
+    return false;
+
+  if (cstlen1 < 0 && cstlen2 < 0)
+    return false;
+
+  if (cstlen1 >= 0)
+    ++cstlen1;
+  if (cstlen2 >= 0)
+    ++cstlen2;
+
+  /* The exact number of characters to compare.  */
+  HOST_WIDE_INT cmpsiz = bound < 0 ? cstlen1 < 0 ? cstlen2 : cstlen1 : bound;
+  /* The size of the array in which the unknown string is stored.  */
+  HOST_WIDE_INT varsiz = arysiz1 < 0 ? arysiz2 : arysiz1;
+
+  if (cmpsiz < varsiz && used_only_for_zero_equality (lhs))
+    {
+      /* If the known length is less than the size of the other array
+        and the strcmp result is only used to test equality to zero,
+        transform the call to the equivalent _eq call.  */
+      if (tree fn = builtin_decl_implicit (bound < 0 ? BUILT_IN_STRCMP_EQ
+                                          : BUILT_IN_STRNCMP_EQ))
+       {
+         tree n = build_int_cst (size_type_node, cmpsiz);
+         update_gimple_call (gsi, fn, 3, arg1, arg2, n);
+         return true;
        }
     }
 
-  gimple_call_set_fndecl (stmt2, builtin_decl_explicit (BUILT_IN_MEMCMP_EQ));
   return false;
 }
 
@@ -2771,21 +3947,357 @@ handle_pointer_plus (gimple_stmt_iterator *gsi)
     }
 }
 
-/* Handle a single character store.  */
+/* Describes recursion limits used by count_nonzero_bytes.  */
 
-static bool
-handle_char_store (gimple_stmt_iterator *gsi)
+class ssa_name_limit_t
 {
-  int idx = -1;
-  strinfo *si = NULL;
-  gimple *stmt = gsi_stmt (*gsi);
-  tree ssaname = NULL_TREE, lhs = gimple_assign_lhs (stmt);
-  tree rhs = gimple_assign_rhs1 (stmt);
-  unsigned HOST_WIDE_INT offset = 0;
+  bitmap visited;         /* Bitmap of visited SSA_NAMEs.  */
+  unsigned ssa_def_max;   /* Longest chain of SSA_NAMEs to follow.  */
 
-  if (TREE_CODE (lhs) == MEM_REF
-      && TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME)
-    {
+  /* Not copyable or assignable.  */
+  ssa_name_limit_t (ssa_name_limit_t&);
+  void operator= (ssa_name_limit_t&);
+
+ public:
+
+  ssa_name_limit_t ()
+    : visited (NULL),
+    ssa_def_max (PARAM_VALUE (PARAM_SSA_NAME_DEF_CHAIN_LIMIT)) { }
+
+  int next_ssa_name (tree);
+
+  ~ssa_name_limit_t ()
+    {
+      if (visited)
+       BITMAP_FREE (visited);
+    }
+};
+
+/* If the SSA_NAME has already been "seen" return a positive value.
+   Otherwise add it to VISITED.  If the SSA_NAME limit has been
+   reached, return a negative value.  Otherwise return zero.  */
+
+int ssa_name_limit_t::next_ssa_name (tree ssa_name)
+{
+  if (!visited)
+    visited = BITMAP_ALLOC (NULL);
+
+  /* Return a positive value if SSA_NAME has already been visited.  */
+  if (!bitmap_set_bit (visited, SSA_NAME_VERSION (ssa_name)))
+    return 1;
+
+  /* Return a negative value to let caller avoid recursing beyond
+     the specified limit.  */
+  if (ssa_def_max == 0)
+    return -1;
+
+  --ssa_def_max;
+
+  return 0;
+}
+
+/* Determines the minimum and maximum number of leading non-zero bytes
+   in the representation of EXP and set LENRANGE[0] and LENRANGE[1]
+   to each.  Sets LENRANGE[2] to the total number of bytes in
+   the representation.  Sets *NULTREM if the representation contains
+   a zero byte, and sets *ALLNUL if all the bytes are zero.
+   OFFSET and NBYTES are the offset into the representation and
+   the size of the access to it determined from a MEM_REF or zero
+   for other expressions.
+   Avoid recursing deeper than the limits in SNLIM allow.
+   Returns true on success and false otherwise.  */
+
+static bool
+count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
+                    unsigned HOST_WIDE_INT nbytes,
+                    unsigned lenrange[3], bool *nulterm,
+                    bool *allnul, bool *allnonnul, const vr_values *rvals,
+                    ssa_name_limit_t &snlim)
+{
+  int idx = get_stridx (exp);
+  if (idx > 0)
+    {
+      strinfo *si = get_strinfo (idx);
+      if (!si)
+       return false;
+
+      /* Handle both constant lengths as well non-constant lengths
+        in some range.  */
+      unsigned HOST_WIDE_INT minlen, maxlen;
+      if (tree_fits_shwi_p (si->nonzero_chars))
+       minlen = maxlen = tree_to_shwi (si->nonzero_chars);
+      else if (nbytes
+              && si->nonzero_chars
+              && TREE_CODE (si->nonzero_chars) == SSA_NAME)
+       {
+         const value_range *vr
+           = CONST_CAST (class vr_values *, rvals)
+           ->get_value_range (si->nonzero_chars);
+         if (vr->kind () != VR_RANGE
+             || !range_int_cst_p (vr))
+           return false;
+
+        minlen = tree_to_uhwi (vr->min ());
+        maxlen = tree_to_uhwi (vr->max ());
+       }
+      else
+       return false;
+
+      if (maxlen < offset)
+       return false;
+
+      minlen = minlen < offset ? 0 : minlen - offset;
+      maxlen -= offset;
+      if (maxlen + 1 < nbytes)
+       return false;
+
+      if (nbytes <= minlen)
+       *nulterm = false;
+
+      if (nbytes < minlen)
+       {
+         minlen = nbytes;
+         if (nbytes < maxlen)
+           maxlen = nbytes;
+       }
+
+      if (minlen < lenrange[0])
+       lenrange[0] = minlen;
+      if (lenrange[1] < maxlen)
+       lenrange[1] = maxlen;
+
+      if (lenrange[2] < nbytes)
+       (lenrange[2] = nbytes);
+
+      /* Since only the length of the string are known and not its contents,
+        clear ALLNUL and ALLNONNUL purely on the basis of the length.  */
+      *allnul = false;
+      if (minlen < nbytes)
+       *allnonnul = false;
+
+      return true;
+    }
+
+  if (TREE_CODE (exp) == ADDR_EXPR)
+    exp = TREE_OPERAND (exp, 0);
+
+  if (TREE_CODE (exp) == SSA_NAME)
+    {
+      /* Handle non-zero single-character stores specially.  */
+      tree type = TREE_TYPE (exp);
+      if (TREE_CODE (type) == INTEGER_TYPE
+         && TYPE_MODE (type) == TYPE_MODE (char_type_node)
+         && TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node)
+         && tree_expr_nonzero_p (exp))
+       {
+         /* If the character EXP is known to be non-zero (even if its
+            exact value is not known) recurse once to set the range
+            for an arbitrary constant.  */
+         exp = build_int_cst (type, 1);
+         return count_nonzero_bytes (exp, offset, 1, lenrange,
+                                     nulterm, allnul, allnonnul, rvals, snlim);
+       }
+
+      gimple *stmt = SSA_NAME_DEF_STMT (exp);
+      if (gimple_assign_single_p (stmt))
+       {
+         exp = gimple_assign_rhs1 (stmt);
+         if (TREE_CODE (exp) != MEM_REF)
+           return false;
+         /* Handle MEM_REF below.  */
+       }
+      else if (gimple_code (stmt) == GIMPLE_PHI)
+       {
+         /* Avoid processing an SSA_NAME that has already been visited
+            or if an SSA_NAME limit has been reached.  Indicate success
+            if the former and failure if the latter.  */
+         if (int res = snlim.next_ssa_name (exp))
+           return res > 0;
+
+         /* Determine the minimum and maximum from the PHI arguments.  */
+         unsigned int n = gimple_phi_num_args (stmt);
+         for (unsigned i = 0; i != n; i++)
+           {
+             tree def = gimple_phi_arg_def (stmt, i);
+             if (!count_nonzero_bytes (def, offset, nbytes, lenrange, nulterm,
+                                       allnul, allnonnul, rvals, snlim))
+               return false;
+           }
+
+         return true;
+       }
+    }
+
+  if (TREE_CODE (exp) == MEM_REF)
+    {
+      if (nbytes)
+       return false;
+
+      tree arg = TREE_OPERAND (exp, 0);
+      tree off = TREE_OPERAND (exp, 1);
+
+      if (TREE_CODE (off) != INTEGER_CST
+         || !tree_fits_uhwi_p (off))
+       return false;
+
+      unsigned HOST_WIDE_INT wioff = tree_to_uhwi (off);
+      if (INT_MAX < wioff)
+       return false;
+
+      offset += wioff;
+      if (INT_MAX < offset)
+       return false;
+
+      /* The size of the MEM_REF access determines the number of bytes.  */
+      tree type = TREE_TYPE (exp);
+      tree typesize = TYPE_SIZE_UNIT (type);
+      if (!typesize || !tree_fits_uhwi_p (typesize))
+       return false;
+      nbytes = tree_to_uhwi (typesize);
+
+      /* Handle MEM_REF = SSA_NAME types of assignments.  */
+      return count_nonzero_bytes (arg, offset, nbytes, lenrange, nulterm,
+                                 allnul, allnonnul, rvals, snlim);
+    }
+
+  if (TREE_CODE (exp) == VAR_DECL && TREE_READONLY (exp))
+    {
+      exp = DECL_INITIAL (exp);
+      if (!exp)
+       return false;
+    }
+
+  const char *prep = NULL;
+  if (TREE_CODE (exp) == STRING_CST)
+    {
+      unsigned nchars = TREE_STRING_LENGTH (exp);
+      if (nchars < offset)
+       return false;
+
+      if (!nbytes)
+       /* If NBYTES hasn't been determined earlier from MEM_REF,
+          set it here.  It includes all internal nuls, including
+          the terminating one if the string has one.  */
+       nbytes = nchars - offset;
+
+      prep = TREE_STRING_POINTER (exp) + offset;
+    }
+
+  unsigned char buf[256];
+  if (!prep)
+    {
+      /* If the pointer to representation hasn't been set above
+        for STRING_CST point it at the buffer.  */
+      prep = reinterpret_cast <char *>(buf);
+      /* Try to extract the representation of the constant object
+        or expression starting from the offset.  */
+      unsigned repsize = native_encode_expr (exp, buf, sizeof buf, offset);
+      if (repsize < nbytes)
+       {
+         /* This should only happen when REPSIZE is zero because EXP
+            doesn't denote an object with a known initializer, except
+            perhaps when the reference reads past its end.  */
+         lenrange[0] = 0;
+         prep = NULL;
+       }
+      else if (!nbytes)
+       nbytes = repsize;
+      else if (nbytes < repsize)
+       return false;
+    }
+
+  if (!nbytes)
+    return false;
+
+  /* Compute the number of leading nonzero bytes in the representation
+     and update the minimum and maximum.  */
+  unsigned n = prep ? strnlen (prep, nbytes) : nbytes;
+
+  if (n < lenrange[0])
+    lenrange[0] = n;
+  if (lenrange[1] < n)
+    lenrange[1] = n;
+
+  /* Set the size of the representation.  */
+  if (lenrange[2] < nbytes)
+    lenrange[2] = nbytes;
+
+  /* Clear NULTERM if none of the bytes is zero.  */
+  if (n == nbytes)
+    *nulterm = false;
+
+  if (n)
+    {
+      /* When the initial number of non-zero bytes N is non-zero, reset
+       *ALLNUL; if N is less than that the size of the representation
+       also clear *ALLNONNUL.  */
+      *allnul = false;
+      if (n < nbytes)
+       *allnonnul = false;
+    }
+  else if (*allnul || *allnonnul)
+    {
+      *allnonnul = false;
+
+      if (*allnul)
+       {
+         /* When either ALLNUL is set and N is zero, also determine
+            whether all subsequent bytes after the first one (which
+            is nul) are zero or nonzero and clear ALLNUL if not.  */
+         for (const char *p = prep; p != prep + nbytes; ++p)
+           if (*p)
+             {
+               *allnul = false;
+               break;
+             }
+       }
+    }
+
+  return true;
+}
+
+/* Same as above except with an implicit SSA_NAME limit.  RVALS is used
+   to determine ranges of dynamically computed string lengths (the results
+   of strlen).  */
+
+static bool
+count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
+                    bool *allnul, bool *allnonnul, const vr_values *rvals)
+{
+  /* Set to optimistic values so the caller doesn't have to worry about
+     initializing these and to what.  On success, the function will clear
+     these if it determines their values are different but being recursive
+     it never sets either to true.  On failure, their values are
+     unspecified.  */
+  *nulterm = true;
+  *allnul = true;
+  *allnonnul = true;
+
+  ssa_name_limit_t snlim;
+  return count_nonzero_bytes (exp, 0, 0, lenrange, nulterm, allnul, allnonnul,
+                             rvals, snlim);
+}
+
+/* Handle a single or multibyte store other than by a built-in function,
+   either via a single character assignment or by multi-byte assignment
+   either via MEM_REF or via a type other than char (such as in
+   '*(int*)a = 12345').  Return true when handled.  */
+
+static bool
+handle_store (gimple_stmt_iterator *gsi, bool *zero_write, const vr_values *rvals)
+{
+  int idx = -1;
+  strinfo *si = NULL;
+  gimple *stmt = gsi_stmt (*gsi);
+  tree ssaname = NULL_TREE, lhs = gimple_assign_lhs (stmt);
+  tree rhs = gimple_assign_rhs1 (stmt);
+
+  /* The offset of the first byte in LHS modified by the the store.  */
+  unsigned HOST_WIDE_INT offset = 0;
+
+  if (TREE_CODE (lhs) == MEM_REF
+      && TREE_CODE (TREE_OPERAND (lhs, 0)) == SSA_NAME)
+    {
       tree mem_offset = TREE_OPERAND (lhs, 1);
       if (tree_fits_uhwi_p (mem_offset))
        {
@@ -2798,8 +4310,11 @@ handle_char_store (gimple_stmt_iterator *gsi)
            si = get_strinfo (idx);
          if (offset == 0)
            ssaname = TREE_OPERAND (lhs, 0);
-         else if (si == NULL || compare_nonzero_chars (si, offset) < 0)
-           return true;
+         else if (si == NULL || compare_nonzero_chars (si, offset, rvals) < 0)
+           {
+             *zero_write = initializer_zerop (rhs);
+             return true;
+           }
        }
     }
   else
@@ -2809,20 +4324,105 @@ 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_CODE (rhs) == INTEGER_CST
-                           && integer_nonzerop (rhs));
+  /* Minimum and maximum leading non-zero bytes and the size of the store.  */
+  unsigned lenrange[] = { UINT_MAX, 0, 0 };
+
+  /* Set to the minimum length of the string being assigned if known.  */
+  unsigned HOST_WIDE_INT rhs_minlen;
+
+  /* STORING_NONZERO_P is true iff not all stored characters are zero.
+     STORING_ALL_NONZERO_P is true if 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_nonzero_p;
+  bool storing_all_zeros_p;
+  /* FULL_STRING_P is set when the stored sequence of characters form
+     a nul-terminated string.  */
+  bool full_string_p;
+
+  const bool ranges_valid
+    = count_nonzero_bytes (rhs, lenrange, &full_string_p,
+                          &storing_all_zeros_p, &storing_all_nonzero_p,
+                          rvals);
+  if (ranges_valid)
+    {
+      rhs_minlen = lenrange[0];
+      storing_nonzero_p = lenrange[1] > 0;
+      *zero_write = storing_all_zeros_p;
+
+      /* Avoid issuing multiple warnings for the same LHS or statement.
+        For example, -Warray-bounds may have already been issued for
+        an out-of-bounds subscript.  */
+      if (!TREE_NO_WARNING (lhs) && !gimple_no_warning_p (stmt))
+       {
+         /* Set to the declaration referenced by LHS (if known).  */
+         tree decl = NULL_TREE;
+         if (tree dstsize = compute_objsize (lhs, 1, &decl))
+           if (compare_tree_int (dstsize, lenrange[2]) < 0)
+             {
+               /* Fall back on the LHS location if the statement
+                  doesn't have one.  */
+               location_t loc = gimple_nonartificial_location (stmt);
+               if (loc == UNKNOWN_LOCATION && EXPR_HAS_LOCATION (lhs))
+                 loc = tree_nonartificial_location (lhs);
+               loc = expansion_point_location_if_in_system_header (loc);
+               if (warning_n (loc, OPT_Wstringop_overflow_,
+                              lenrange[2],
+                              "%Gwriting %u byte into a region of size %E",
+                              "%Gwriting %u bytes into a region of size %E",
+                              stmt, lenrange[2], dstsize))
+                 {
+                   if (decl)
+                     inform (DECL_SOURCE_LOCATION (decl),
+                             "destination object declared here");
+                   gimple_set_no_warning (stmt, true);
+                 }
+             }
+       }
+    }
+  else
+    {
+      rhs_minlen = HOST_WIDE_INT_M1U;
+      full_string_p = false;
+      storing_nonzero_p = false;
+      storing_all_zeros_p = false;
+      storing_all_nonzero_p = false;
+    }
 
   if (si != NULL)
     {
-      int cmp = compare_nonzero_chars (si, offset);
-      gcc_assert (offset == 0 || cmp >= 0);
-      if (storing_zero_p && cmp == 0 && si->full_string_p)
+      /* The corresponding element is set to 1 if the first and last
+        element, respectively, of the sequence of characters being
+        written over the string described by SI ends before
+        the terminating nul (if it has one), to zero if the nul is
+        being overwritten but not beyond, or negative otherwise.  */
+      int store_before_nul[2];
+      if (ranges_valid)
+       {
+         /* The offset of the last stored byte.  */
+         unsigned HOST_WIDE_INT endoff = offset + lenrange[2] - 1;
+         store_before_nul[0] = compare_nonzero_chars (si, offset, rvals);
+         if (endoff == offset)
+           store_before_nul[1] = store_before_nul[0];
+         else
+           store_before_nul[1] = compare_nonzero_chars (si, endoff, rvals);
+       }
+      else
+       {
+         store_before_nul[0] = compare_nonzero_chars (si, offset, rvals);
+         store_before_nul[1] = store_before_nul[0];
+         gcc_assert (offset == 0 || store_before_nul[0] >= 0);
+       }
+
+      if (storing_all_zeros_p
+         && store_before_nul[0] == 0
+         && store_before_nul[1] == 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);
@@ -2836,56 +4436,85 @@ handle_char_store (gimple_stmt_iterator *gsi)
              return false;
            }
        }
-      /* If si->nonzero_chars > OFFSET, we aren't overwriting '\0',
-        and if we aren't storing '\0', we know that the length of the
-        string and any other zero terminated string in memory remains
-        the same.  In that case we move to the next gimple statement and
-        return to signal the caller that it shouldn't invalidate anything.  
-
-        This is benefical for cases like:
-
-        char p[20];
-        void foo (char *q)
-        {
-          strcpy (p, "foobar");
-          size_t len = strlen (p);        // This can be optimized into 6
-          size_t len2 = strlen (q);        // This has to be computed
-          p[0] = 'X';
-          size_t len3 = strlen (p);        // This can be optimized into 6
-          size_t len4 = strlen (q);        // This can be optimized into len2
-          bar (len, len2, len3, len4);
-        }
-       */ 
-      else if (storing_nonzero_p && cmp > 0)
+
+      if (store_before_nul[1] > 0
+         && storing_nonzero_p
+         && lenrange[0] == lenrange[1]
+         && lenrange[0] == lenrange[2]
+         && TREE_CODE (TREE_TYPE (rhs)) == INTEGER_TYPE)
        {
+         /* Handle a store of one or more non-nul characters that ends
+            before the terminating nul of the destination and so does
+            not affect its length
+            If si->nonzero_chars > OFFSET, we aren't overwriting '\0',
+            and if we aren't storing '\0', we know that the length of
+            the string and any other zero terminated string in memory
+            remains the same.  In that case we move to the next gimple
+            statement and return to signal the caller that it shouldn't
+            invalidate anything.
+
+            This is benefical for cases like:
+
+            char p[20];
+            void foo (char *q)
+            {
+              strcpy (p, "foobar");
+              size_t len = strlen (p);     // can be folded to 6
+              size_t len2 = strlen (q);    // has to be computed
+              p[0] = 'X';
+              size_t len3 = strlen (p);    // can be folded to 6
+              size_t len4 = strlen (q);    // can be folded to len2
+              bar (len, len2, len3, len4);
+              } */
          gsi_next (gsi);
          return false;
        }
-      else if (storing_zero_p || storing_nonzero_p || (offset != 0 && cmp > 0))
+
+      if (storing_all_zeros_p
+         || storing_nonzero_p
+         || (offset != 0 && store_before_nul[1] > 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.  */
+            so need to clip the nonzero_chars to OFFSET.
+            Use the minimum length of the string (or individual character)
+            being stored if it's known.  Otherwise, STORING_NONZERO_P
+            guarantees it's at least 1.  */
+         HOST_WIDE_INT len
+           = storing_nonzero_p && ranges_valid ? lenrange[0] : 1;
          location_t loc = gimple_location (stmt);
          tree oldlen = si->nonzero_chars;
-         if (cmp == 0 && si->full_string_p)
+         if (store_before_nul[1] == 0 && si->full_string_p)
            /* We're overwriting the nul terminator with a nonzero or
               unknown character.  If the previous stmt was a memcpy,
               its length may be decreased.  */
            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
+
+         /* Set FULL_STRING_P only if the length of the strings being
+            written is the same, and clear it if the strings have
+            different lengths.  In the latter case the length stored
+            in si->NONZERO_CHARS becomes the lower bound.
+            FIXME: Handle the upper bound of the length if possible.  */
+         si->full_string_p = full_string_p && lenrange[0] == lenrange[1];
+
+         if (storing_all_zeros_p
              && ssaname
              && !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (ssaname))
            si->endptr = ssaname;
@@ -2905,7 +4534,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);
@@ -2914,10 +4543,29 @@ 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;
+         if (storing_all_zeros_p)
+           slen = 0;
+         else if (storing_nonzero_p && ranges_valid)
+           {
+             /* FIXME: Handle the upper bound of the length when
+                LENRANGE[0] != LENRANGE[1].  */
+             slen = lenrange[0];
+             if (lenrange[0] != lenrange[1])
+               /* Set the minimum length but ignore the maximum
+                  for now.  */
+               full_string_p = false;
+           }
+         else
+           slen = -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;
@@ -2926,26 +4574,26 @@ handle_char_store (gimple_stmt_iterator *gsi)
        }
     }
   else if (idx == 0
-          && TREE_CODE (gimple_assign_rhs1 (stmt)) == STRING_CST
+          && rhs_minlen < HOST_WIDE_INT_M1U
           && ssaname == NULL_TREE
           && TREE_CODE (TREE_TYPE (lhs)) == ARRAY_TYPE)
     {
-      size_t l = strlen (TREE_STRING_POINTER (gimple_assign_rhs1 (stmt)));
       HOST_WIDE_INT a = int_size_in_bytes (TREE_TYPE (lhs));
-      if (a > 0 && (unsigned HOST_WIDE_INT) a > l)
+      if (a > 0 && (unsigned HOST_WIDE_INT) a > rhs_minlen)
        {
          int idx = new_addr_stridx (lhs);
          if (idx != 0)
            {
              si = new_strinfo (build_fold_addr_expr (lhs), idx,
-                               build_int_cst (size_type_node, l), true);
+                               build_int_cst (size_type_node, rhs_minlen),
+                               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.  */
@@ -3050,88 +4698,247 @@ fold_strstr_to_strncmp (tree rhs1, tree rhs2, gimple *stmt)
     }
 }
 
-/* Attempt to check for validity of the performed access a single statement
-   at *GSI using string length knowledge, and to optimize it.  */
+/* Return true if TYPE corresponds to a narrow character type.  */
+
+static bool
+is_char_type (tree type)
+{
+  return (TREE_CODE (type) == INTEGER_TYPE
+         && TYPE_MODE (type) == TYPE_MODE (char_type_node)
+         && TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node));
+}
+
+/* Check the built-in call at GSI for validity and optimize it.
+   Return true to let the caller advance *GSI to the statement
+   in the CFG and false otherwise.  */
 
 static bool
-strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi)
+strlen_check_and_optimize_call (gimple_stmt_iterator *gsi,
+                               bool *zero_write,
+                               const vr_values *rvals)
 {
   gimple *stmt = gsi_stmt (*gsi);
 
-  if (is_gimple_call (stmt))
+  if (!flag_optimize_strlen
+      || !strlen_optimize
+      || !valid_builtin_call (stmt))
     {
-      tree callee = gimple_call_fndecl (stmt);
-      if (valid_builtin_call (stmt))
-       switch (DECL_FUNCTION_CODE (callee))
-         {
-         case BUILT_IN_STRLEN:
-         case BUILT_IN_STRLEN_CHKP:
-           handle_builtin_strlen (gsi);
-           break;
-         case BUILT_IN_STRCHR:
-         case BUILT_IN_STRCHR_CHKP:
-           handle_builtin_strchr (gsi);
-           break;
-         case BUILT_IN_STRCPY:
-         case BUILT_IN_STRCPY_CHK:
-         case BUILT_IN_STPCPY:
-         case BUILT_IN_STPCPY_CHK:
-         case BUILT_IN_STRCPY_CHKP:
-         case BUILT_IN_STRCPY_CHK_CHKP:
-         case BUILT_IN_STPCPY_CHKP:
-         case BUILT_IN_STPCPY_CHK_CHKP:
-           handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi);
-           break;
+      /* When not optimizing we must be checking printf calls which
+        we do even for user-defined functions when they are declared
+        with attribute format.  */
+      handle_printf_call (gsi, rvals);
+      return true;
+    }
 
-         case BUILT_IN_STRNCAT:
-         case BUILT_IN_STRNCAT_CHK:
-           handle_builtin_strncat (DECL_FUNCTION_CODE (callee), gsi);
-           break;
+  tree callee = gimple_call_fndecl (stmt);
+  switch (DECL_FUNCTION_CODE (callee))
+    {
+    case BUILT_IN_STRLEN:
+    case BUILT_IN_STRNLEN:
+      handle_builtin_strlen (gsi);
+      break;
+    case BUILT_IN_STRCHR:
+      handle_builtin_strchr (gsi);
+      break;
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+      handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi);
+      break;
 
-         case BUILT_IN_STPNCPY:
-         case BUILT_IN_STPNCPY_CHK:
-         case BUILT_IN_STRNCPY:
-         case BUILT_IN_STRNCPY_CHK:
-           handle_builtin_stxncpy (DECL_FUNCTION_CODE (callee), gsi);
-           break;
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+      handle_builtin_strncat (DECL_FUNCTION_CODE (callee), gsi);
+      break;
 
-         case BUILT_IN_MEMCPY:
-         case BUILT_IN_MEMCPY_CHK:
-         case BUILT_IN_MEMPCPY:
-         case BUILT_IN_MEMPCPY_CHK:
-         case BUILT_IN_MEMCPY_CHKP:
-         case BUILT_IN_MEMCPY_CHK_CHKP:
-         case BUILT_IN_MEMPCPY_CHKP:
-         case BUILT_IN_MEMPCPY_CHK_CHKP:
-           handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi);
-           break;
-         case BUILT_IN_STRCAT:
-         case BUILT_IN_STRCAT_CHK:
-         case BUILT_IN_STRCAT_CHKP:
-         case BUILT_IN_STRCAT_CHK_CHKP:
-           handle_builtin_strcat (DECL_FUNCTION_CODE (callee), gsi);
-           break;
-         case BUILT_IN_MALLOC:
-         case BUILT_IN_CALLOC:
-           handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi);
-           break;
-         case BUILT_IN_MEMSET:
-           if (!handle_builtin_memset (gsi))
-             return false;
-           break;
-         case BUILT_IN_MEMCMP:
-           if (!handle_builtin_memcmp (gsi))
-             return false;
-           break;
-         default:
-           break;
-         }
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      handle_builtin_stxncpy (DECL_FUNCTION_CODE (callee), gsi);
+      break;
+
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+      handle_builtin_memcpy (DECL_FUNCTION_CODE (callee), gsi);
+      break;
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+      handle_builtin_strcat (DECL_FUNCTION_CODE (callee), gsi);
+      break;
+    case BUILT_IN_MALLOC:
+    case BUILT_IN_CALLOC:
+      handle_builtin_malloc (DECL_FUNCTION_CODE (callee), gsi);
+      break;
+    case BUILT_IN_MEMSET:
+      if (handle_builtin_memset (gsi, zero_write))
+       return false;
+      break;
+    case BUILT_IN_MEMCMP:
+      if (handle_builtin_memcmp (gsi))
+       return false;
+      break;
+    case BUILT_IN_STRCMP:
+    case BUILT_IN_STRNCMP:
+      if (handle_builtin_string_cmp (gsi))
+       return false;
+      break;
+    default:
+      handle_printf_call (gsi, rvals);
+      break;
+    }
+
+  return true;
+}
+
+/* Handle an assignment statement at *GSI to a LHS of integral type.
+   If GSI's basic block needs clean-up of EH, set *CLEANUP_EH to true.  */
+
+static void
+handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  tree lhs = gimple_assign_lhs (stmt);
+  tree lhs_type = TREE_TYPE (lhs);
+
+  enum tree_code code = gimple_assign_rhs_code (stmt);
+  if (code == COND_EXPR)
+    {
+      tree cond = gimple_assign_rhs1 (stmt);
+      enum tree_code cond_code = TREE_CODE (cond);
+
+      if (cond_code == EQ_EXPR || cond_code == NE_EXPR)
+       fold_strstr_to_strncmp (TREE_OPERAND (cond, 0),
+                               TREE_OPERAND (cond, 1), stmt);
     }
+  else if (code == EQ_EXPR || code == NE_EXPR)
+    fold_strstr_to_strncmp (gimple_assign_rhs1 (stmt),
+                           gimple_assign_rhs2 (stmt), stmt);
+  else if (gimple_assign_load_p (stmt)
+          && TREE_CODE (lhs_type) == INTEGER_TYPE
+          && TYPE_MODE (lhs_type) == TYPE_MODE (char_type_node)
+          && (TYPE_PRECISION (lhs_type)
+              == TYPE_PRECISION (char_type_node))
+          && !gimple_has_volatile_ops (stmt))
+    {
+      tree off = integer_zero_node;
+      unsigned HOST_WIDE_INT coff = 0;
+      int idx = 0;
+      tree rhs1 = gimple_assign_rhs1 (stmt);
+      if (code == MEM_REF)
+       {
+         idx = get_stridx (TREE_OPERAND (rhs1, 0));
+         if (idx > 0)
+           {
+             strinfo *si = get_strinfo (idx);
+             if (si
+                 && si->nonzero_chars
+                 && TREE_CODE (si->nonzero_chars) == INTEGER_CST
+                 && (wi::to_widest (si->nonzero_chars)
+                     >= wi::to_widest (off)))
+               off = TREE_OPERAND (rhs1, 1);
+             else
+               /* This case is not useful.  See if get_addr_stridx
+                  returns something usable.  */
+               idx = 0;
+           }
+       }
+      if (idx <= 0)
+       idx = get_addr_stridx (rhs1, NULL_TREE, &coff);
+      if (idx > 0)
+       {
+         strinfo *si = get_strinfo (idx);
+         if (si
+             && si->nonzero_chars
+             && TREE_CODE (si->nonzero_chars) == INTEGER_CST)
+           {
+             widest_int w1 = wi::to_widest (si->nonzero_chars);
+             widest_int w2 = wi::to_widest (off) + coff;
+             if (w1 == w2
+                 && si->full_string_p)
+               {
+                 if (dump_file && (dump_flags & TDF_DETAILS) != 0)
+                   {
+                     fprintf (dump_file, "Optimizing: ");
+                     print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
+                   }
+
+                 /* Reading the final '\0' character.  */
+                 tree zero = build_int_cst (lhs_type, 0);
+                 gimple_set_vuse (stmt, NULL_TREE);
+                 gimple_assign_set_rhs_from_tree (gsi, zero);
+                 *cleanup_eh
+                   |= maybe_clean_or_replace_eh_stmt (stmt,
+                                                      gsi_stmt (*gsi));
+                 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);
+                   }
+               }
+             else if (w1 > w2)
+               {
+                 /* Reading a character before the final '\0'
+                    character.  Just set the value range to ~[0, 0]
+                    if we don't have anything better.  */
+                 wide_int min, max;
+                 signop sign = TYPE_SIGN (lhs_type);
+                 int prec = TYPE_PRECISION (lhs_type);
+                 value_range_kind vr = get_range_info (lhs, &min, &max);
+                 if (vr == VR_VARYING
+                     || (vr == VR_RANGE
+                         && min == wi::min_value (prec, sign)
+                         && max == wi::max_value (prec, sign)))
+                   set_range_info (lhs, VR_ANTI_RANGE,
+                                   wi::zero (prec), wi::zero (prec));
+               }
+           }
+       }
+    }
+
+  if (strlen_to_stridx)
+    {
+      tree rhs1 = gimple_assign_rhs1 (stmt);
+      if (stridx_strlenloc *ps = strlen_to_stridx->get (rhs1))
+       strlen_to_stridx->put (lhs, stridx_strlenloc (*ps));
+    }
+}
+
+/* Attempt to check for validity of the performed access a single statement
+   at *GSI using string length knowledge, and to optimize it.
+   If the given basic block needs clean-up of EH, CLEANUP_EH is set to
+   true.  */
+
+static bool
+check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
+                        const vr_values *rvals)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+
+  /* For statements that modify a string, set to true if the write
+     is only zeros.  */
+  bool zero_write = false;
+
+  if (is_gimple_call (stmt))
+    {
+      if (!strlen_check_and_optimize_call (gsi, &zero_write, rvals))
+       return false;
+    }
+  else if (!flag_optimize_strlen || !strlen_optimize)
+    return true;
   else if (is_gimple_assign (stmt) && !gimple_clobber_p (stmt))
     {
+      /* Handle non-clobbering assignment.  */
       tree lhs = gimple_assign_lhs (stmt);
+      tree lhs_type = TREE_TYPE (lhs);
 
-      if (TREE_CODE (lhs) == SSA_NAME && POINTER_TYPE_P (TREE_TYPE (lhs)))
+      if (TREE_CODE (lhs) == SSA_NAME && POINTER_TYPE_P (lhs_type))
        {
          if (gimple_assign_single_p (stmt)
              || (gimple_assign_cast_p (stmt)
@@ -3143,113 +4950,36 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi)
          else if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
            handle_pointer_plus (gsi);
        }
-    else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (lhs)))
+      else if (TREE_CODE (lhs) == SSA_NAME && INTEGRAL_TYPE_P (lhs_type))
+       /* Handle assignment to a character.  */
+       handle_integral_assign (gsi, cleanup_eh);
+      else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
       {
-       enum tree_code code = gimple_assign_rhs_code (stmt);
-       if (code == COND_EXPR)
-         {
-           tree cond = gimple_assign_rhs1 (stmt);
-           enum tree_code cond_code = TREE_CODE (cond);
+       tree type = TREE_TYPE (lhs);
+       if (TREE_CODE (type) == ARRAY_TYPE)
+         type = TREE_TYPE (type);
 
-           if (cond_code == EQ_EXPR || cond_code == NE_EXPR)
-             fold_strstr_to_strncmp (TREE_OPERAND (cond, 0),
-                                     TREE_OPERAND (cond, 1), stmt);
-         }
-       else if (code == EQ_EXPR || code == NE_EXPR)
-         fold_strstr_to_strncmp (gimple_assign_rhs1 (stmt),
-                                 gimple_assign_rhs2 (stmt), stmt);
-       else if (gimple_assign_load_p (stmt)
-                && TREE_CODE (TREE_TYPE (lhs)) == INTEGER_TYPE
-                && TYPE_MODE (TREE_TYPE (lhs)) == TYPE_MODE (char_type_node)
-                && (TYPE_PRECISION (TREE_TYPE (lhs))
-                    == TYPE_PRECISION (char_type_node))
-                && !gimple_has_volatile_ops (stmt))
+       bool is_char_store = is_char_type (type);
+       if (!is_char_store && TREE_CODE (lhs) == MEM_REF)
          {
-           tree off = integer_zero_node;
-           unsigned HOST_WIDE_INT coff = 0;
-           int idx = 0;
-           tree rhs1 = gimple_assign_rhs1 (stmt);
-           if (code == MEM_REF)
-             {
-               idx = get_stridx (TREE_OPERAND (rhs1, 0));
-               if (idx > 0)
-                 {
-                   strinfo *si = get_strinfo (idx);
-                   if (si
-                       && si->nonzero_chars
-                       && TREE_CODE (si->nonzero_chars) == INTEGER_CST
-                       && (wi::to_widest (si->nonzero_chars)
-                           >= wi::to_widest (off)))
-                     off = TREE_OPERAND (rhs1, 1);
-                   else
-                     /* This case is not useful.  See if get_addr_stridx
-                        returns something usable.  */
-                     idx = 0;
-                 }
-             }
-           if (idx <= 0)
-             idx = get_addr_stridx (rhs1, NULL_TREE, &coff);
-           if (idx > 0)
-             {
-               strinfo *si = get_strinfo (idx);
-               if (si
-                   && si->nonzero_chars
-                   && TREE_CODE (si->nonzero_chars) == INTEGER_CST)
-                 {
-                   widest_int w1 = wi::to_widest (si->nonzero_chars);
-                   widest_int w2 = wi::to_widest (off) + coff;
-                   if (w1 == w2
-                       && si->full_string_p)
-                     {
-                       /* Reading the final '\0' character.  */
-                       tree zero = build_int_cst (TREE_TYPE (lhs), 0);
-                       gimple_set_vuse (stmt, NULL_TREE);
-                       gimple_assign_set_rhs_from_tree (gsi, zero);
-                       update_stmt (gsi_stmt (*gsi));
-                     }
-                   else if (w1 > w2)
-                     {
-                       /* Reading a character before the final '\0'
-                          character.  Just set the value range to ~[0, 0]
-                          if we don't have anything better.  */
-                       wide_int min, max;
-                       tree type = TREE_TYPE (lhs);
-                       enum value_range_type vr
-                         = get_range_info (lhs, &min, &max);
-                       if (vr == VR_VARYING
-                           || (vr == VR_RANGE
-                               && min == wi::min_value (TYPE_PRECISION (type),
-                                                        TYPE_SIGN (type))
-                               && max == wi::max_value (TYPE_PRECISION (type),
-                                                        TYPE_SIGN (type))))
-                         set_range_info (lhs, VR_ANTI_RANGE,
-                                         wi::zero (TYPE_PRECISION (type)),
-                                         wi::zero (TYPE_PRECISION (type)));
-                     }
-                 }
-             }
+           /* To consider stores into char objects via integer types
+              other than char but not those to non-character objects,
+              determine the type of the destination rather than just
+              the type of the access.  */
+           tree ref = TREE_OPERAND (lhs, 0);
+           type = TREE_TYPE (ref);
+           if (TREE_CODE (type) == POINTER_TYPE)
+             type = TREE_TYPE (type);
+           if (TREE_CODE (type) == ARRAY_TYPE)
+             type = TREE_TYPE (type);
+           if (is_char_type (type))
+             is_char_store = true;
          }
 
-       if (strlen_to_stridx)
-         {
-           tree rhs1 = gimple_assign_rhs1 (stmt);
-           if (stridx_strlenloc *ps = strlen_to_stridx->get (rhs1))
-             strlen_to_stridx->put (lhs, stridx_strlenloc (*ps));
-         }
+       /* Handle a single or multibyte assignment.  */
+       if (is_char_store && !handle_store (gsi, &zero_write, rvals))
+         return false;
       }
-    else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
-       {
-         tree type = TREE_TYPE (lhs);
-         if (TREE_CODE (type) == ARRAY_TYPE)
-           type = TREE_TYPE (type);
-         if (TREE_CODE (type) == INTEGER_TYPE
-             && TYPE_MODE (type) == TYPE_MODE (char_type_node)
-             && TYPE_PRECISION (type) == TYPE_PRECISION (char_type_node))
-           {
-             if (! handle_char_store (gsi))
-               return false;
-           }
-       }
     }
   else if (gcond *cond = dyn_cast<gcond *> (stmt))
     {
@@ -3260,7 +4990,7 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi)
     }
 
   if (gimple_vdef (stmt))
-    maybe_invalidate (stmt);
+    maybe_invalidate (stmt, zero_write);
   return true;
 }
 
@@ -3318,10 +5048,22 @@ do_invalidate (basic_block dombb, gimple *phi, bitmap visited, int *count)
 class strlen_dom_walker : public dom_walker
 {
 public:
-  strlen_dom_walker (cdi_direction direction) : dom_walker (direction) {}
+  strlen_dom_walker (cdi_direction direction)
+    : dom_walker (direction),
+    evrp (false),
+    m_cleanup_cfg (false)
+  {}
 
   virtual edge before_dom_children (basic_block);
   virtual void after_dom_children (basic_block);
+
+  /* EVRP analyzer used for printf argument range processing, and
+     to track strlen results across integer variable assignments.  */
+  evrp_range_analyzer evrp;
+
+  /* Flag that will trigger TODO_cleanup_cfg to be returned in strlen
+     execute function.  */
+  bool m_cleanup_cfg;
 };
 
 /* Callback for walk_dominator_tree.  Attempt to optimize various
@@ -3330,6 +5072,8 @@ public:
 edge
 strlen_dom_walker::before_dom_children (basic_block bb)
 {
+  evrp.enter (bb);
+
   basic_block dombb = get_immediate_dominator (CDI_DOMINATORS, bb);
 
   if (dombb == NULL)
@@ -3399,10 +5143,23 @@ strlen_dom_walker::before_dom_children (basic_block bb)
        }
     }
 
+  bool cleanup_eh = false;
+
   /* Attempt to optimize individual statements.  */
   for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
-    if (strlen_check_and_optimize_stmt (&gsi))
-      gsi_next (&gsi);
+    {
+      gimple *stmt = gsi_stmt (gsi);
+
+      /* First record ranges generated by this statement so they
+        can be used by printf argument processing.  */
+      evrp.record_ranges_from_stmt (stmt, false);
+
+      if (check_and_optimize_stmt (&gsi, &cleanup_eh, evrp.get_vr_values ()))
+       gsi_next (&gsi);
+    }
+
+  if (cleanup_eh && gimple_purge_dead_eh_edges (bb))
+      m_cleanup_cfg = true;
 
   bb->aux = stridx_to_strinfo;
   if (vec_safe_length (stridx_to_strinfo) && !strinfo_shared ())
@@ -3416,6 +5173,8 @@ strlen_dom_walker::before_dom_children (basic_block bb)
 void
 strlen_dom_walker::after_dom_children (basic_block bb)
 {
+  evrp.leave (bb);
+
   if (bb->aux)
     {
       stridx_to_strinfo = ((vec<strinfo *, va_heap, vl_embed> *) bb->aux);
@@ -3433,51 +5192,35 @@ strlen_dom_walker::after_dom_children (basic_block bb)
     }
 }
 
-/* Main entry point.  */
-
 namespace {
 
-const pass_data pass_data_strlen =
-{
-  GIMPLE_PASS, /* type */
-  "strlen", /* name */
-  OPTGROUP_NONE, /* optinfo_flags */
-  TV_TREE_STRLEN, /* tv_id */
-  ( PROP_cfg | PROP_ssa ), /* properties_required */
-  0, /* properties_provided */
-  0, /* properties_destroyed */
-  0, /* todo_flags_start */
-  0, /* todo_flags_finish */
-};
-
-class pass_strlen : public gimple_opt_pass
+static unsigned int
+printf_strlen_execute (function *fun, bool warn_only)
 {
-public:
-  pass_strlen (gcc::context *ctxt)
-    : gimple_opt_pass (pass_data_strlen, ctxt)
-  {}
+  strlen_optimize = !warn_only;
 
-  /* opt_pass methods: */
-  virtual bool gate (function *) { return flag_optimize_strlen != 0; }
-  virtual unsigned int execute (function *);
+  calculate_dominance_info (CDI_DOMINATORS);
 
-}; // class pass_strlen
+  bool use_scev = optimize > 0 && flag_printf_return_value;
+  if (use_scev)
+    {
+      loop_optimizer_init (LOOPS_NORMAL);
+      scev_initialize ();
+    }
 
-unsigned int
-pass_strlen::execute (function *fun)
-{
   gcc_assert (!strlen_to_stridx);
   if (warn_stringop_overflow || warn_stringop_truncation)
     strlen_to_stridx = new hash_map<tree, stridx_strlenloc> ();
 
+  /* This has to happen after initializing the loop optimizer
+     and initializing SCEV as they create new SSA_NAMEs.  */
   ssa_ver_to_stridx.safe_grow_cleared (num_ssa_names);
   max_stridx = 1;
 
-  calculate_dominance_info (CDI_DOMINATORS);
-
   /* String length optimization is implemented as a walk of the dominator
      tree and a forward walk of statements within each block.  */
-  strlen_dom_walker (CDI_DOMINATORS).walk (fun->cfg->x_entry_block_ptr);
+  strlen_dom_walker walker (CDI_DOMINATORS);
+  walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
 
   ssa_ver_to_stridx.release ();
   strinfo_pool.release ();
@@ -3498,11 +5241,113 @@ pass_strlen::execute (function *fun)
       strlen_to_stridx = NULL;
     }
 
-  return 0;
+  if (use_scev)
+    {
+      scev_finalize ();
+      loop_optimizer_finalize ();
+    }
+
+  /* Clean up object size info.  */
+  fini_object_sizes ();
+
+  return walker.m_cleanup_cfg ? TODO_cleanup_cfg : 0;
+}
+
+/* This file defines two passes: one for warnings that runs only when
+   optimization is disabled, and another that implements optimizations
+   and also issues warnings.  */
+
+const pass_data pass_data_warn_printf =
+{
+  GIMPLE_PASS, /* type */
+  "warn-printf", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_NONE, /* tv_id */
+  /* Normally an optimization pass would require PROP_ssa but because
+     this pass runs early, with no optimization, to do sprintf format
+     checking, it only requires PROP_cfg.  */
+  PROP_cfg, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_warn_printf : public gimple_opt_pass
+{
+public:
+  pass_warn_printf (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_warn_printf, ctxt)
+  {}
+
+  virtual bool gate (function *);
+  virtual unsigned int execute (function *fun)
+  {
+    return printf_strlen_execute (fun, true);
+  }
+};
+
+
+/* Return true to run the warning pass only when not optimizing and
+   iff either -Wformat-overflow or -Wformat-truncation is specified.  */
+
+bool
+pass_warn_printf::gate (function *)
+{
+  return !optimize && (warn_format_overflow > 0 || warn_format_trunc > 0);
+}
+
+const pass_data pass_data_strlen =
+{
+  GIMPLE_PASS, /* type */
+  "strlen", /* name */
+  OPTGROUP_NONE, /* optinfo_flags */
+  TV_TREE_STRLEN, /* tv_id */
+  PROP_cfg | PROP_ssa, /* properties_required */
+  0, /* properties_provided */
+  0, /* properties_destroyed */
+  0, /* todo_flags_start */
+  0, /* todo_flags_finish */
+};
+
+class pass_strlen : public gimple_opt_pass
+{
+public:
+  pass_strlen (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_strlen, ctxt)
+  {}
+
+  opt_pass * clone () { return new pass_strlen (m_ctxt); }
+
+  virtual bool gate (function *);
+  virtual unsigned int execute (function *fun)
+  {
+    return printf_strlen_execute (fun, false);
+  }
+};
+
+/* Return true to run the pass only when the sprintf and/or strlen
+   optimizations are enabled and -Wformat-overflow or -Wformat-truncation
+   are specified.  */
+
+bool
+pass_strlen::gate (function *)
+{
+  return ((warn_format_overflow > 0
+          || warn_format_trunc > 0
+          || flag_optimize_strlen > 0
+          || flag_printf_return_value)
+         && optimize > 0);
 }
 
 } // anon namespace
 
+gimple_opt_pass *
+make_pass_warn_printf (gcc::context *ctxt)
+{
+  return new pass_warn_printf (ctxt);
+}
+
 gimple_opt_pass *
 make_pass_strlen (gcc::context *ctxt)
 {