Make full use of context-sensitive ranges in access warnings.

gcc/ChangeLog:

	* builtins.c (check_strncat_sizes): Pass access_data ctor additional
	arguments.
	(expand_builtin_memcmp): Move code to gimple-ssa-warn-access.cc.
	(expand_builtin_fork_or_exec): Same.
	* gimple-array-bounds.cc (array_bounds_checker::check_mem_ref):
	Pass compute_objsize additional arguments.
	(inbounds_memaccess_p): Same.
	(array_bounds_checker::check_array_bounds): Add an assert.  Stash
	statement in a member.
	(check_array_bounds_dom_walker::before_dom_children):
	* gimple-array-bounds.h (array_bounds_checker::m_stmt): New member.
	* gimple-ssa-sprintf.c (get_destination_size): Add an argument.
	(handle_printf_call): Pass a new argument.
	* gimple-ssa-warn-access.cc (get_size_range): Add an argument.
	(check_access): Add an argument and pass it along to callees.
	(check_read_access): Make a member function.
	(pass_waccess::check_strcat): Pass access_data ctor additional
	arguments.
	(pass_waccess::check_strncat): Same.
	(pass_waccess::check_stxcpy): Same.
	(pass_waccess::check_stxncpy): Same.
	(pass_waccess::check_strncmp): Same.
	(pass_waccess::check_read_access):
	(pass_waccess::check_builtin):
	(pass_waccess::maybe_check_access_sizes):
	(pass_waccess::maybe_check_dealloc_call):
	* gimple-ssa-warn-access.h (check_read_access): Declare a new
	member function.
	* pointer-query.cc (compute_objsize_r): Add an argument.
	(gimple_call_return_array): Same.
	(gimple_call_alloc_size): Same.
	(access_ref::access_ref): Same.
	(access_ref::get_ref): Same.
	(pointer_query::get_ref): Same.
	(handle_min_max_size): Pass an arguments to callees.
	(handle_array_ref): Add an argument.
	(handle_mem_ref): Same.
	(compute_objsize): Same.
	* pointer-query.h (struct access_ref): Adjust signatures.
	(struct access_data): Same.
	(gimple_call_alloc_size): Add an argument.
	(gimple_parm_array_size): Same.
	(compute_objsize): Same.
	* tree-ssa-strlen.c (strlen_pass::adjust_last_stmt): Pass an additional
	argument to compute_objsize.
	(strlen_pass::maybe_warn_overflow): Same.
	(maybe_diag_stxncpy_trunc): Same.

gcc/testsuite/ChangeLog:

	* gcc.dg/Wstringop-overflow-22.c: Correct typos.
	* gcc.dg/Wstringop-overflow-77.c: New test.

libstdc++/testsuite/ChangeLog:

	* 21_strings/basic_string/capacity/1.cc: Also suppress
	-Wstringop-overread.
	* 27_io/filesystem/path/factory/u8path-char8_t.cc: Same.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index f1c3fea3583..7d0f61fc98b 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3600,7 +3600,7 @@ check_strncat_sizes (tree exp, tree objsize)
   /* Try to verify that the destination is big enough for the shortest
      string.  */
 
-  access_data data (exp, access_read_write, maxread, true);
+  access_data data (nullptr, exp, access_read_write, maxread, true);
   if (!objsize && warn_stringop_overflow)
     {
       /* If it hasn't been provided by __strncat_chk, try to determine
@@ -4260,12 +4260,6 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
   tree arg2 = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  /* Diagnose calls where the specified length exceeds the size of either
-     object.  */
-  if (!check_read_access (exp, arg1, len, 0)
-      || !check_read_access (exp, arg2, len, 0))
-    return NULL_RTX;
-
   /* Due to the performance benefit, always inline the calls first
      when result_eq is false.  */
   rtx result = NULL_RTX;
@@ -5486,27 +5480,6 @@ expand_builtin_fork_or_exec (tree fn, tree exp, rtx target, int ignore)
   tree id, decl;
   tree call;
 
-  if (DECL_FUNCTION_CODE (fn) != BUILT_IN_FORK)
-    {
-      tree path = CALL_EXPR_ARG (exp, 0);
-      /* Detect unterminated path.  */
-      if (!check_read_access (exp, path))
-	return NULL_RTX;
-
-      /* Also detect unterminated first argument.  */
-      switch (DECL_FUNCTION_CODE (fn))
-	{
-	case BUILT_IN_EXECL:
-	case BUILT_IN_EXECLE:
-	case BUILT_IN_EXECLP:
-	  if (!check_read_access (exp, path))
-	    return NULL_RTX;
-	default:
-	  break;
-	}
-    }
-
-
   /* If we are not profiling, just call the function.  */
   if (!profile_arc_flag)
     return NULL_RTX;
diff --git a/gcc/gimple-array-bounds.cc b/gcc/gimple-array-bounds.cc
index 0517e5ddd8e..a3535598998 100644
--- a/gcc/gimple-array-bounds.cc
+++ b/gcc/gimple-array-bounds.cc
@@ -426,7 +426,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref,
       axssize = wi::to_offset (access_size);
 
   access_ref aref;
-  if (!compute_objsize (ref, 0, &aref, ranges))
+  if (!compute_objsize (ref, m_stmt, 0, &aref, ranges))
     return false;
 
   if (aref.offset_in_range (axssize))
@@ -667,7 +667,7 @@ array_bounds_checker::check_addr_expr (location_t location, tree t,
    problems discussed in pr98266 and pr97595.  */
 
 static bool
-inbounds_memaccess_p (tree t)
+inbounds_memaccess_p (tree t, gimple *stmt)
 {
   if (TREE_CODE (t) != COMPONENT_REF)
     return false;
@@ -686,7 +686,7 @@ inbounds_memaccess_p (tree t)
      allocated).  */
   access_ref aref;   // unused
   tree refop = TREE_OPERAND (mref, 0);
-  tree refsize = compute_objsize (refop, 1, &aref);
+  tree refsize = compute_objsize (refop, stmt, 1, &aref);
   if (!refsize || TREE_CODE (refsize) != INTEGER_CST)
     return false;
 
@@ -724,6 +724,7 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
 {
   tree t = *tp;
   struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
+
   location_t location;
 
   if (EXPR_HAS_LOCATION (t))
@@ -735,6 +736,8 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
 
   bool warned = false;
   array_bounds_checker *checker = (array_bounds_checker *) wi->info;
+  gcc_assert (checker->m_stmt == wi->stmt);
+
   if (TREE_CODE (t) == ARRAY_REF)
     warned = checker->check_array_ref (location, t, wi->stmt,
 				       false/*ignore_off_by_one*/);
@@ -746,7 +749,7 @@ array_bounds_checker::check_array_bounds (tree *tp, int *walk_subtree,
       checker->check_addr_expr (location, t, wi->stmt);
       *walk_subtree = false;
     }
-  else if (inbounds_memaccess_p (t))
+  else if (inbounds_memaccess_p (t, wi->stmt))
     /* Hack: Skip MEM_REF checks in accesses to a member of a base class
        at an offset that's within the bounds of the enclosing object.
        See pr98266 and pr97595.  */
@@ -794,14 +797,13 @@ check_array_bounds_dom_walker::before_dom_children (basic_block bb)
   for (si = gsi_start_bb (bb); !gsi_end_p (si); gsi_next (&si))
     {
       gimple *stmt = gsi_stmt (si);
-      struct walk_stmt_info wi;
       if (!gimple_has_location (stmt)
 	  || is_gimple_debug (stmt))
 	continue;
 
-      memset (&wi, 0, sizeof (wi));
-
+      struct walk_stmt_info wi{ };
       wi.info = checker;
+      checker->m_stmt = stmt;
 
       walk_gimple_op (stmt, array_bounds_checker::check_array_bounds, &wi);
     }
diff --git a/gcc/gimple-array-bounds.h b/gcc/gimple-array-bounds.h
index d8f7ff7a89f..d0e665eff11 100644
--- a/gcc/gimple-array-bounds.h
+++ b/gcc/gimple-array-bounds.h
@@ -36,8 +36,12 @@ private:
   void check_addr_expr (location_t, tree, gimple *);
   const value_range *get_value_range (const_tree op, gimple *);
 
+  /* Current function.  */
   struct function *fun;
+  /* Ranger instance.  */
   range_query *ranges;
+  /* Current statement.  */
+  gimple *m_stmt;
 };
 
 #endif // GCC_GIMPLE_ARRAY_BOUNDS_H
diff --git a/gcc/gimple-ssa-sprintf.c b/gcc/gimple-ssa-sprintf.c
index 8e90b7cfc43..8f42cb59540 100644
--- a/gcc/gimple-ssa-sprintf.c
+++ b/gcc/gimple-ssa-sprintf.c
@@ -4030,11 +4030,11 @@ compute_format_length (call_info &info, format_result *res, range_query *query)
   return success;
 }
 
-/* Return the size of the object referenced by the expression DEST if
-   available, or the maximum possible size otherwise.  */
+/* Return the size of the object referenced by the expression DEST in
+   statement STMT, if available, or the maximum possible size otherwise.  */
 
 static unsigned HOST_WIDE_INT
-get_destination_size (tree dest, pointer_query &ptr_qry)
+get_destination_size (tree dest, gimple *stmt, pointer_query &ptr_qry)
 {
   /* When there is no destination return the maximum.  */
   if (!dest)
@@ -4042,7 +4042,7 @@ get_destination_size (tree dest, pointer_query &ptr_qry)
 
   /* Use compute_objsize to determine the size of the destination object.  */
   access_ref aref;
-  if (!ptr_qry.get_ref (dest, &aref))
+  if (!ptr_qry.get_ref (dest, stmt, &aref))
     return HOST_WIDE_INT_MAX;
 
   offset_int remsize = aref.size_remaining ();
@@ -4516,7 +4516,7 @@ handle_printf_call (gimple_stmt_iterator *gsi, pointer_query &ptr_qry)
       /* For non-bounded functions like sprintf, determine the size
 	 of the destination from the object or pointer passed to it
 	 as the first argument.  */
-      dstsize = get_destination_size (dstptr, ptr_qry);
+      dstsize = get_destination_size (dstptr, info.callstmt, ptr_qry);
     }
   else if (tree size = gimple_call_arg (info.callstmt, idx_dstsize))
     {
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 00c3ea0f505..d3616ef725f 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -1190,11 +1190,11 @@ warn_for_access (location_t loc, tree func, tree expr, int opt,
    by BNDRNG if nonnull and valid.  */
 
 static void
-get_size_range (range_query *query, tree bound, tree range[2],
+get_size_range (range_query *query, tree bound, gimple *stmt, tree range[2],
 		const offset_int bndrng[2])
 {
   if (bound)
-    get_size_range (query, bound, NULL, range);
+    get_size_range (query, bound, stmt, range);
 
   if (!bndrng || (bndrng[0] == 0 && bndrng[1] == HOST_WIDE_INT_M1U))
     return;
@@ -1251,7 +1251,8 @@ template <class GimpleOrTree>
 static bool
 check_access (GimpleOrTree exp, tree dstwrite,
 	      tree maxread, tree srcstr, tree dstsize,
-	      access_mode mode, const access_data *pad /* = NULL */)
+	      access_mode mode, const access_data *pad,
+	      range_query *rvals)
 {
   /* The size of the largest object is half the address space, or
      PTRDIFF_MAX.  (This is way too permissive.)  */
@@ -1338,7 +1339,8 @@ check_access (GimpleOrTree exp, tree dstwrite,
 
   /* Set RANGE to that of DSTWRITE if non-null, bounded by PAD->DST.BNDRNG
      if valid.  */
-  get_size_range (NULL, dstwrite, range, pad ? pad->dst.bndrng : NULL);
+  gimple *stmt = pad ? pad->stmt : nullptr;
+  get_size_range (rvals, dstwrite, stmt, range, pad ? pad->dst.bndrng : NULL);
 
   tree func = get_callee_fndecl (exp);
   /* Read vs write access by built-ins can be determined from the const
@@ -1432,7 +1434,7 @@ check_access (GimpleOrTree exp, tree dstwrite,
     {
       /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
 	 PAD is nonnull and BNDRNG is valid.  */
-      get_size_range (NULL, maxread, range, pad ? pad->src.bndrng : NULL);
+      get_size_range (rvals, maxread, stmt, range, pad ? pad->src.bndrng : NULL);
 
       location_t loc = get_location (exp);
       tree size = dstsize;
@@ -1479,7 +1481,7 @@ check_access (GimpleOrTree exp, tree dstwrite,
     {
       /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
 	 PAD is nonnull and BNDRNG is valid.  */
-      get_size_range (NULL, maxread, range, pad ? pad->src.bndrng : NULL);
+      get_size_range (rvals, maxread, stmt, range, pad ? pad->src.bndrng : NULL);
       /* Set OVERREAD for reads starting just past the end of an object.  */
       overread = pad->src.sizrng[1] - pad->src.offrng[0] < pad->src.bndrng[0];
       range[0] = wide_int_to_tree (sizetype, pad->src.bndrng[0]);
@@ -1512,13 +1514,14 @@ check_access (GimpleOrTree exp, tree dstwrite,
   return true;
 }
 
-bool
+static bool
 check_access (gimple *stmt, tree dstwrite,
 	      tree maxread, tree srcstr, tree dstsize,
-	      access_mode mode, const access_data *pad /* = NULL */)
+	      access_mode mode, const access_data *pad,
+	      range_query *rvals)
 {
-  return check_access<gimple *>(stmt, dstwrite, maxread, srcstr, dstsize,
-				mode, pad);
+  return check_access<gimple *> (stmt, dstwrite, maxread, srcstr, dstsize,
+				 mode, pad, rvals);
 }
 
 bool
@@ -1526,45 +1529,8 @@ check_access (tree expr, tree dstwrite,
 	      tree maxread, tree srcstr, tree dstsize,
 	      access_mode mode, const access_data *pad /* = NULL */)
 {
-  return check_access<tree>(expr, dstwrite, maxread, srcstr, dstsize,
-			    mode, pad);
-}
-
-/* A convenience wrapper for check_access above to check access
-   by a read-only function like puts.  */
-
-template <class GimpleOrTree>
-static bool
-check_read_access (GimpleOrTree expr, tree src, tree bound, int ost)
-{
-  if (!warn_stringop_overread)
-    return true;
-
-  if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound)))
-    bound = fold_convert (size_type_node, bound);
-
-  tree fndecl = get_callee_fndecl (expr);
-  maybe_warn_nonstring_arg (fndecl, expr);
-
-  access_data data (expr, access_read_only, NULL_TREE, false, bound, true);
-  compute_objsize (src, ost, &data.src);
-  return check_access (expr, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound,
-		       /*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode,
-		       &data);
-}
-
-bool
-check_read_access (gimple *stmt, tree src, tree bound /* = NULL_TREE */,
-		   int ost /* = 1 */)
-{
-  return check_read_access<gimple *>(stmt, src, bound, ost);
-}
-
-bool
-check_read_access (tree expr, tree src, tree bound /* = NULL_TREE */,
-		   int ost /* = 1 */)
-{
-  return check_read_access<tree>(expr, src, bound, ost);
+  return check_access<tree> (expr, dstwrite, maxread, srcstr, dstsize,
+			     mode, pad, nullptr);
 }
 
 /* Return true if STMT is a call to an allocation function.  Unless
@@ -2130,6 +2096,7 @@ private:
   void check_stxncpy (gcall *);
   void check_strncmp (gcall *);
   void check_memop_access (gimple *, tree, tree, tree);
+  void check_read_access (gimple *, tree, tree = NULL_TREE, int = 1);
 
   void maybe_check_dealloc_call (gcall *);
   void maybe_check_access_sizes (rdwr_map *, tree, tree, gimple *);
@@ -2425,14 +2392,14 @@ pass_waccess::check_strcat (gcall *stmt)
      the destination to which the SRC string is being appended so
      just diagnose cases when the souce string is longer than
      the destination object.  */
-  access_data data (stmt, access_read_write, NULL_TREE, true,
-		    NULL_TREE, true);
+  access_data data (m_ptr_qry.rvals, stmt, access_read_write, NULL_TREE,
+		    true, NULL_TREE, true);
   const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-  compute_objsize (src, ost, &data.src, &m_ptr_qry);
-  tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
+  compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
+  tree destsize = compute_objsize (dest, stmt, ost, &data.dst, &m_ptr_qry);
 
   check_access (stmt, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
-		src, destsize, data.mode, &data);
+		src, destsize, data.mode, &data, m_ptr_qry.rvals);
 }
 
 /* Check a call STMT to strcat() for overflow and warn if it does.  */
@@ -2466,12 +2433,12 @@ pass_waccess::check_strncat (gcall *stmt)
       maxlen = lendata.maxbound;
     }
 
-  access_data data (stmt, access_read_write);
+  access_data data (m_ptr_qry.rvals, stmt, access_read_write);
   /* Try to verify that the destination is big enough for the shortest
      string.  First try to determine the size of the destination object
      into which the source is being copied.  */
   const int ost = warn_stringop_overflow - 1;
-  tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
+  tree destsize = compute_objsize (dest, stmt, ost, &data.dst, &m_ptr_qry);
 
   /* Add one for the terminating nul.  */
   tree srclen = (maxlen
@@ -2500,7 +2467,7 @@ pass_waccess::check_strncat (gcall *stmt)
     srclen = maxread;
 
   check_access (stmt, /*dstwrite=*/NULL_TREE, maxread, srclen,
-		destsize, data.mode, &data);
+		destsize, data.mode, &data, m_ptr_qry.rvals);
 }
 
 /* Check a call STMT to stpcpy() or strcpy() for overflow and warn
@@ -2524,14 +2491,14 @@ pass_waccess::check_stxcpy (gcall *stmt)
 
   if (warn_stringop_overflow)
     {
-      access_data data (stmt, access_read_write, NULL_TREE, true,
-			NULL_TREE, true);
+      access_data data (m_ptr_qry.rvals, stmt, access_read_write, NULL_TREE,
+			true, NULL_TREE, true);
       const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-      compute_objsize (src, ost, &data.src, &m_ptr_qry);
-      tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
+      compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
+      tree dstsize = compute_objsize (dst, stmt, ost, &data.dst, &m_ptr_qry);
       check_access (stmt, /*dstwrite=*/ NULL_TREE,
 		    /*maxread=*/ NULL_TREE, /*srcstr=*/ src,
-		    dstsize, data.mode, &data);
+		    dstsize, data.mode, &data, m_ptr_qry.rvals);
     }
 
   /* Check to see if the argument was declared attribute nonstring
@@ -2555,13 +2522,14 @@ pass_waccess::check_stxncpy (gcall *stmt)
   /* The number of bytes to write (not the maximum).  */
   tree len = call_arg (stmt, 2);
 
-  access_data data (stmt, access_read_write, len, true, len, true);
+  access_data data (m_ptr_qry.rvals, stmt, access_read_write, len, true, len,
+		    true);
   const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-  compute_objsize (src, ost, &data.src, &m_ptr_qry);
-  tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
+  compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
+  tree dstsize = compute_objsize (dst, stmt, ost, &data.dst, &m_ptr_qry);
 
-  check_access (stmt, /*dstwrite=*/len,
-		/*maxread=*/len, src, dstsize, data.mode, &data);
+  check_access (stmt, /*dstwrite=*/len, /*maxread=*/len, src, dstsize,
+		data.mode, &data, m_ptr_qry.rvals);
 }
 
 /* Check a call STMT to stpncpy() or strncpy() for overflow and warn
@@ -2594,6 +2562,11 @@ pass_waccess::check_strncmp (gcall *stmt)
   tree len1 = c_strlen (arg1, 1, &lendata1);
   tree len2 = c_strlen (arg2, 1, &lendata2);
 
+  if (len1 && TREE_CODE (len1) != INTEGER_CST)
+    len1 = NULL_TREE;
+  if (len2 && TREE_CODE (len2) != INTEGER_CST)
+    len2 = NULL_TREE;
+
   if (len1 && len2)
     /* If the length of both arguments was computed they must both be
        nul-terminated and no further checking is necessary regardless
@@ -2606,13 +2579,15 @@ pass_waccess::check_strncmp (gcall *stmt)
   if (maybe_warn_nonstring_arg (get_callee_fndecl (stmt), stmt))
     return;
 
-  access_data adata1 (stmt, access_read_only, NULL_TREE, false, bound, true);
-  access_data adata2 (stmt, access_read_only, NULL_TREE, false, bound, true);
+  access_data adata1 (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE, false,
+		      bound, true);
+  access_data adata2 (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE, false,
+		      bound, true);
 
   /* Determine the range of the bound first and bail if it fails; it's
      cheaper than computing the size of the objects.  */
   tree bndrng[2] = { NULL_TREE, NULL_TREE };
-  get_size_range (m_ptr_qry.rvals, bound, bndrng, adata1.src.bndrng);
+  get_size_range (m_ptr_qry.rvals, bound, stmt, bndrng, adata1.src.bndrng);
   if (!bndrng[0] || integer_zerop (bndrng[0]))
     return;
 
@@ -2623,8 +2598,8 @@ pass_waccess::check_strncmp (gcall *stmt)
 
   /* compute_objsize almost never fails (and ultimately should never
      fail).  Don't bother to handle the rare case when it does.  */
-  if (!compute_objsize (arg1, 1, &adata1.src, &m_ptr_qry)
-      || !compute_objsize (arg2, 1, &adata2.src, &m_ptr_qry))
+  if (!compute_objsize (arg1, stmt, 1, &adata1.src, &m_ptr_qry)
+      || !compute_objsize (arg2, stmt, 1, &adata2.src, &m_ptr_qry))
     return;
 
   /* Compute the size of the remaining space in each array after
@@ -2672,15 +2647,41 @@ pass_waccess::check_memop_access (gimple *stmt, tree dest, tree src, tree size)
      try to determine the size of the largest source and destination
      object using type-0 Object Size regardless of the object size
      type specified by the option.  */
-  access_data data (stmt, access_read_write);
+  access_data data (m_ptr_qry.rvals, stmt, access_read_write);
   tree srcsize
-    = src ? compute_objsize (src, 0, &data.src, &m_ptr_qry) : NULL_TREE;
-  tree dstsize = compute_objsize (dest, 0, &data.dst, &m_ptr_qry);
+    = src ? compute_objsize (src, stmt, 0, &data.src, &m_ptr_qry) : NULL_TREE;
+  tree dstsize = compute_objsize (dest, stmt, 0, &data.dst, &m_ptr_qry);
+
+  check_access (stmt, size, /*maxread=*/NULL_TREE, srcsize, dstsize,
+		data.mode, &data, m_ptr_qry.rvals);
+}
+
+/* A convenience wrapper for check_access to check access by a read-only
+   function like puts or strcmp.  */
+
+void
+pass_waccess::check_read_access (gimple *stmt, tree src,
+				 tree bound /* = NULL_TREE */,
+				 int ost /* = 1 */)
+{
+  if (!warn_stringop_overread)
+    return;
+
+  if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound)))
+    bound = fold_convert (size_type_node, bound);
+
+  tree fndecl = get_callee_fndecl (stmt);
+  maybe_warn_nonstring_arg (fndecl, stmt);
 
-  check_access (stmt, size, /*maxread=*/NULL_TREE,
-		srcsize, dstsize, data.mode, &data);
+  access_data data (m_ptr_qry.rvals, stmt, access_read_only, NULL_TREE,
+		    false, bound, true);
+  compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
+  check_access (stmt, /*dstwrite=*/ NULL_TREE, /*maxread=*/ bound,
+		/*srcstr=*/ src, /*dstsize=*/ NULL_TREE, data.mode,
+		&data, m_ptr_qry.rvals);
 }
 
+
 /* Check call STMT to a built-in function for invalid accesses.  Return
    true if a call has been handled.  */
 
@@ -2699,6 +2700,15 @@ pass_waccess::check_builtin (gcall *stmt)
       check_alloca (stmt);
       return true;
 
+    case BUILT_IN_EXECL:
+    case BUILT_IN_EXECLE:
+    case BUILT_IN_EXECLP:
+    case BUILT_IN_EXECV:
+    case BUILT_IN_EXECVE:
+    case BUILT_IN_EXECVP:
+      check_read_access (stmt, call_arg (stmt, 0));
+      return true;
+
     case BUILT_IN_GETTEXT:
     case BUILT_IN_PUTS:
     case BUILT_IN_PUTS_UNLOCKED:
@@ -2721,8 +2731,12 @@ pass_waccess::check_builtin (gcall *stmt)
 
     case BUILT_IN_STRNDUP:
     case BUILT_IN_STRNLEN:
-      check_read_access (stmt, call_arg (stmt, 0), call_arg (stmt, 1));
-      return true;
+      {
+	tree str = call_arg (stmt, 0);
+	tree len = call_arg (stmt, 1);
+	check_read_access (stmt, str, len);
+	return true;
+      }
 
     case BUILT_IN_STRCAT:
       check_strcat (stmt);
@@ -2900,7 +2914,7 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
       /* Format the value or range to avoid an explosion of messages.  */
       char sizstr[80];
       tree sizrng[2] = { size_zero_node, build_all_ones_cst (sizetype) };
-      if (get_size_range (m_ptr_qry.rvals, access_size, NULL, sizrng, 1))
+      if (get_size_range (m_ptr_qry.rvals, access_size, stmt, sizrng, 1))
 	{
 	  char *s0 = print_generic_expr_to_str (sizrng[0]);
 	  if (tree_int_cst_equal (sizrng[0], sizrng[1]))
@@ -3028,11 +3042,11 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
 	    }
 	}
 
-      access_data data (ptr, access.second.mode, NULL_TREE, false,
-			NULL_TREE, false);
+      access_data data (m_ptr_qry.rvals, stmt, access.second.mode,
+			NULL_TREE, false, NULL_TREE, false);
       access_ref* const pobj = (access.second.mode == access_write_only
 				? &data.dst : &data.src);
-      tree objsize = compute_objsize (ptr, 1, pobj, &m_ptr_qry);
+      tree objsize = compute_objsize (ptr, stmt, 1, pobj, &m_ptr_qry);
 
       /* The size of the destination or source object.  */
       tree dstsize = NULL_TREE, srcsize = NULL_TREE;
@@ -3064,7 +3078,7 @@ pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
       if (mode == access_deferred)
 	mode = TYPE_READONLY (argtype) ? access_read_only : access_read_write;
       check_access (stmt, access_size, /*maxread=*/ NULL_TREE, srcsize,
-		    dstsize, mode, &data);
+		    dstsize, mode, &data, m_ptr_qry.rvals);
 
       if (warning_suppressed_p (stmt, OPT_Wstringop_overflow_))
 	opt_warned = OPT_Wstringop_overflow_;
@@ -3187,7 +3201,7 @@ pass_waccess::maybe_check_dealloc_call (gcall *call)
     return;
 
   access_ref aref;
-  if (!compute_objsize (ptr, 0, &aref, &m_ptr_qry))
+  if (!compute_objsize (ptr, call, 0, &aref, &m_ptr_qry))
     return;
 
   tree ref = aref.ref;
diff --git a/gcc/gimple-ssa-warn-access.h b/gcc/gimple-ssa-warn-access.h
index 00f5bb1a7b2..124f3f530d3 100644
--- a/gcc/gimple-ssa-warn-access.h
+++ b/gcc/gimple-ssa-warn-access.h
@@ -45,7 +45,4 @@ class access_data;
 extern bool check_access (tree, tree, tree, tree, tree, access_mode,
 			  const access_data * = NULL);
 
-extern bool check_read_access (gimple *, tree, tree = NULL_TREE, int ost = 1);
-extern bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
-
 #endif   // GCC_GIMPLE_SSA_WARN_ACCESS_H
diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 910f452868e..3b9f970846b 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -43,8 +43,8 @@
 #include "tree-ssanames.h"
 #include "target.h"
 
-static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
-			       pointer_query *);
+static bool compute_objsize_r (tree, gimple *, int, access_ref *,
+			       ssa_name_limit_t &, pointer_query *);
 
 /* Wrapper around the wide_int overload of get_range that accepts
    offset_int instead.  For middle end expressions returns the same
@@ -115,7 +115,7 @@ get_offset_range (tree x, gimple *stmt, offset_int r[2], range_query *rvals)
 
 static tree
 gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
-			  range_query *rvals)
+			  ssa_name_limit_t &snlim, pointer_query *qry)
 {
   /* Clear and set below for the rare function(s) that might return
      a past-the-end pointer.  */
@@ -191,7 +191,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
 	offrng[0] = 0;
 	offrng[1] = HOST_WIDE_INT_M1U;
 	tree off = gimple_call_arg (stmt, 2);
-	bool off_valid = get_offset_range (off, stmt, offrng, rvals);
+	bool off_valid = get_offset_range (off, stmt, offrng, qry->rvals);
 	if (!off_valid || offrng[0] != offrng[1])
 	  {
 	    /* If the offset is either indeterminate or in some range,
@@ -199,7 +199,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
 	       of the source object.  */
 	    access_ref aref;
 	    tree src = gimple_call_arg (stmt, 1);
-	    if (compute_objsize (src, 1, &aref, rvals)
+	    if (compute_objsize (src, stmt, 1, &aref, qry)
 		&& aref.sizrng[1] < offrng[1])
 	      offrng[1] = aref.sizrng[1];
 	  }
@@ -212,7 +212,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
     case BUILT_IN_MEMCHR:
       {
 	tree off = gimple_call_arg (stmt, 2);
-	if (get_offset_range (off, stmt, offrng, rvals))
+	if (get_offset_range (off, stmt, offrng, qry->rvals))
 	  offrng[1] -= 1;
 	else
 	  offrng[1] = HOST_WIDE_INT_M1U;
@@ -233,7 +233,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
       {
 	access_ref aref;
 	tree src = gimple_call_arg (stmt, 1);
-	if (compute_objsize (src, 1, &aref, rvals))
+	if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry))
 	  offrng[1] = aref.sizrng[1] - 1;
 	else
 	  offrng[1] = HOST_WIDE_INT_M1U;
@@ -250,7 +250,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
 	   and the source object size.  */
 	offrng[1] = HOST_WIDE_INT_M1U;
 	tree off = gimple_call_arg (stmt, 2);
-	if (!get_offset_range (off, stmt, offrng, rvals)
+	if (!get_offset_range (off, stmt, offrng, qry->rvals)
 	    || offrng[0] != offrng[1])
 	  {
 	    /* If the offset is either indeterminate or in some range,
@@ -258,7 +258,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
 	       of the source object.  */
 	    access_ref aref;
 	    tree src = gimple_call_arg (stmt, 1);
-	    if (compute_objsize (src, 1, &aref, rvals)
+	    if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry)
 		&& aref.sizrng[1] < offrng[1])
 	      offrng[1] = aref.sizrng[1];
 	  }
@@ -445,7 +445,7 @@ get_size_range (tree exp, tree range[2], int flags /* = 0 */)
 
 tree
 gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
-			range_query * /* = NULL */)
+			range_query *qry /* = NULL */)
 {
   if (!stmt || !is_gimple_call (stmt))
     return NULL_TREE;
@@ -503,7 +503,7 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   {
     tree r[2];
     /* Determine the largest valid range size, including zero.  */
-    if (!get_size_range (size, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+    if (!get_size_range (qry, size, stmt, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
       return NULL_TREE;
     rng1[0] = wi::to_wide (r[0], prec);
     rng1[1] = wi::to_wide (r[1], prec);
@@ -519,7 +519,7 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   {
     tree r[2];
       /* As above, use the full non-negative range on failure.  */
-    if (!get_size_range (n, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+    if (!get_size_range (qry, n, stmt, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
       return NULL_TREE;
     rng2[0] = wi::to_wide (r[0], prec);
     rng2[1] = wi::to_wide (r[1], prec);
@@ -546,7 +546,7 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
    Set STATIC_ARRAY if the array parameter has been declared [static].
    Return the function parameter on success and null otherwise.  */
 
-tree
+static tree
 gimple_parm_array_size (tree ptr, wide_int rng[2],
 			bool *static_array /* = NULL */)
 {
@@ -596,10 +596,17 @@ gimple_parm_array_size (tree ptr, wide_int rng[2],
   return var;
 }
 
-access_ref::access_ref (tree bound /* = NULL_TREE */,
+/* Given a statement STMT, set the bounds of the reference to at most
+   as many bytes as BOUND or unknown when null, and at least one when
+   the MINACCESS is true unless BOUND is a constant zero.  STMT is
+   used for context to get accurate range info.  */
+
+access_ref::access_ref (range_query *qry /* = nullptr */,
+			tree bound /* = NULL_TREE */,
+			gimple *stmt /* = nullptr */,
 			bool minaccess /* = false */)
-: ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
-  base0 (true), parmarray ()
+  : ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
+    base0 (true), parmarray ()
 {
   /* Set to valid.  */
   offrng[0] = offrng[1] = 0;
@@ -615,7 +622,7 @@ access_ref::access_ref (tree bound /* = NULL_TREE */,
      set the bounds of the access to reflect both it and MINACCESS.
      BNDRNG[0] is the size of the minimum access.  */
   tree rng[2];
-  if (bound && get_size_range (bound, rng, SR_ALLOW_ZERO))
+  if (bound && get_size_range (qry, bound, stmt, rng, SR_ALLOW_ZERO))
     {
       bndrng[0] = wi::to_offset (rng[0]);
       bndrng[1] = wi::to_offset (rng[1]);
@@ -696,7 +703,8 @@ access_ref::get_ref (vec<access_ref> *all_refs,
     {
       access_ref phi_arg_ref;
       tree arg = gimple_phi_arg_def (phi_stmt, i);
-      if (!compute_objsize_r (arg, ostype, &phi_arg_ref, *psnlim, qry)
+      if (!compute_objsize_r (arg, phi_stmt, ostype, &phi_arg_ref, *psnlim,
+			      qry)
 	  || phi_arg_ref.sizrng[0] < 0)
 	/* A PHI with all null pointer arguments.  */
 	return NULL_TREE;
@@ -1312,7 +1320,7 @@ pointer_query::get_ref (tree ptr, int ostype /* = 1 */) const
    there or compute it and insert it into the cache if it's nonnonull.  */
 
 bool
-pointer_query::get_ref (tree ptr, access_ref *pref, int ostype /* = 1 */)
+pointer_query::get_ref (tree ptr, gimple *stmt, access_ref *pref, int ostype /* = 1 */)
 {
   const unsigned version
     = TREE_CODE (ptr) == SSA_NAME ? SSA_NAME_VERSION (ptr) : 0;
@@ -1335,7 +1343,7 @@ pointer_query::get_ref (tree ptr, access_ref *pref, int ostype /* = 1 */)
       ++misses;
     }
 
-  if (!compute_objsize (ptr, ostype, pref, this))
+  if (!compute_objsize (ptr, stmt, ostype, pref, this))
     {
       ++failures;
       return false;
@@ -1502,7 +1510,7 @@ static bool
 handle_min_max_size (tree ptr, int ostype, access_ref *pref,
 		     ssa_name_limit_t &snlim, pointer_query *qry)
 {
-  const gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+  gimple *stmt = SSA_NAME_DEF_STMT (ptr);
   const tree_code code = gimple_assign_rhs_code (stmt);
 
   /* In a valid MAX_/MIN_EXPR both operands must refer to the same array.
@@ -1512,7 +1520,7 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref,
      for the expression.  */
   access_ref aref[2] = { *pref, *pref };
   tree arg1 = gimple_assign_rhs1 (stmt);
-  if (!compute_objsize_r (arg1, ostype, &aref[0], snlim, qry))
+  if (!compute_objsize_r (arg1, stmt, ostype, &aref[0], snlim, qry))
     {
       aref[0].base0 = false;
       aref[0].offrng[0] = aref[0].offrng[1] = 0;
@@ -1521,7 +1529,7 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref,
     }
 
   tree arg2 = gimple_assign_rhs2 (stmt);
-  if (!compute_objsize_r (arg2, ostype, &aref[1], snlim, qry))
+  if (!compute_objsize_r (arg2, stmt, ostype, &aref[1], snlim, qry))
     {
       aref[1].base0 = false;
       aref[1].offrng[0] = aref[1].offrng[1] = 0;
@@ -1589,8 +1597,9 @@ handle_min_max_size (tree ptr, int ostype, access_ref *pref,
    on success and false on failure.  */
 
 static bool
-handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref,
-		  ssa_name_limit_t &snlim, pointer_query *qry)
+handle_array_ref (tree aref, gimple *stmt, bool addr, int ostype,
+		  access_ref *pref, ssa_name_limit_t &snlim,
+		  pointer_query *qry)
 {
   gcc_assert (TREE_CODE (aref) == ARRAY_REF);
 
@@ -1603,7 +1612,7 @@ handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref,
        of known bound.  */
     return false;
 
-  if (!compute_objsize_r (arefop, ostype, pref, snlim, qry))
+  if (!compute_objsize_r (arefop, stmt, ostype, pref, snlim, qry))
     return false;
 
   offset_int orng[2];
@@ -1668,7 +1677,7 @@ handle_array_ref (tree aref, bool addr, int ostype, access_ref *pref,
    MREF.  Return true on success and false on failure.  */
 
 static bool
-handle_mem_ref (tree mref, int ostype, access_ref *pref,
+handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref,
 		ssa_name_limit_t &snlim, pointer_query *qry)
 {
   gcc_assert (TREE_CODE (mref) == MEM_REF);
@@ -1690,7 +1699,7 @@ handle_mem_ref (tree mref, int ostype, access_ref *pref,
     }
 
   tree mrefop = TREE_OPERAND (mref, 0);
-  if (!compute_objsize_r (mrefop, ostype, pref, snlim, qry))
+  if (!compute_objsize_r (mrefop, stmt, ostype, pref, snlim, qry))
     return false;
 
   offset_int orng[2];
@@ -1723,7 +1732,7 @@ handle_mem_ref (tree mref, int ostype, access_ref *pref,
    to influence code generation or optimization.  */
 
 static bool
-compute_objsize_r (tree ptr, int ostype, access_ref *pref,
+compute_objsize_r (tree ptr, gimple *stmt, int ostype, access_ref *pref,
 		   ssa_name_limit_t &snlim, pointer_query *qry)
 {
   STRIP_NOPS (ptr);
@@ -1774,7 +1783,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
   if (code == BIT_FIELD_REF)
     {
       tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+      if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
 	return false;
 
       offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
@@ -1796,7 +1805,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 	  /* In OSTYPE zero (for raw memory functions like memcpy), use
 	     the maximum size instead if the identity of the enclosing
 	     object cannot be determined.  */
-	  if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+	  if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
 	    return false;
 
 	  /* Otherwise, use the size of the enclosing object and add
@@ -1850,15 +1859,15 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
     }
 
   if (code == ARRAY_REF)
-    return handle_array_ref (ptr, addr, ostype, pref, snlim, qry);
+    return handle_array_ref (ptr, stmt, addr, ostype, pref, snlim, qry);
 
   if (code == MEM_REF)
-    return handle_mem_ref (ptr, ostype, pref, snlim, qry);
+    return handle_mem_ref (ptr, stmt, ostype, pref, snlim, qry);
 
   if (code == TARGET_MEM_REF)
     {
       tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+      if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
 	return false;
 
       /* TODO: Handle remaining operands.  Until then, add maximum offset.  */
@@ -1903,7 +1912,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
   if (code == POINTER_PLUS_EXPR)
     {
       tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, ostype, pref, snlim, qry))
+      if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
 	return false;
 
       /* Clear DEREF since the offset is being applied to the target
@@ -1922,7 +1931,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
   if (code == VIEW_CONVERT_EXPR)
     {
       ptr = TREE_OPERAND (ptr, 0);
-      return compute_objsize_r (ptr, ostype, pref, snlim, qry);
+      return compute_objsize_r (ptr, stmt, ostype, pref, snlim, qry);
     }
 
   if (code == SSA_NAME)
@@ -1951,7 +1960,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 	    }
 	}
 
-      gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+      stmt = SSA_NAME_DEF_STMT (ptr);
       if (is_gimple_call (stmt))
 	{
 	  /* If STMT is a call to an allocation function get the size
@@ -1979,9 +1988,9 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 	      bool past_end;
 	      offset_int offrng[2];
 	      if (tree ret = gimple_call_return_array (stmt, offrng,
-						       &past_end, rvals))
+						       &past_end, snlim, qry))
 		{
-		  if (!compute_objsize_r (ret, ostype, pref, snlim, qry))
+		  if (!compute_objsize_r (ret, stmt, ostype, pref, snlim, qry))
 		    return false;
 
 		  /* Cap OFFRNG[1] to at most the remaining size of
@@ -2076,14 +2085,14 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
       if (code == ASSERT_EXPR)
 	{
 	  rhs = TREE_OPERAND (rhs, 0);
-	  return compute_objsize_r (rhs, ostype, pref, snlim, qry);
+	  return compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry);
 	}
 
       if (code == POINTER_PLUS_EXPR
 	  && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE)
 	{
 	  /* Compute the size of the object first. */
-	  if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
+	  if (!compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry))
 	    return false;
 
 	  offset_int orng[2];
@@ -2099,7 +2108,7 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 
       if (code == ADDR_EXPR || code == SSA_NAME)
 	{
-	  if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
+	  if (!compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry))
 	    return false;
 	  qry->put_ref (ptr, *pref);
 	  return true;
@@ -2128,18 +2137,21 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
    instead.  */
 
 tree
-compute_objsize (tree ptr, int ostype, access_ref *pref,
-		 range_query *rvals /* = NULL */)
+compute_objsize (tree ptr, gimple *stmt, int ostype, access_ref *pref,
+		 pointer_query *ptr_qry)
 {
   pointer_query qry;
-  qry.rvals = rvals;
+  if (ptr_qry)
+    ptr_qry->depth = 0;
+  else
+    ptr_qry = &qry;
 
   /* Clear and invalidate in case *PREF is being reused.  */
   pref->offrng[0] = pref->offrng[1] = 0;
   pref->sizrng[0] = pref->sizrng[1] = -1;
 
   ssa_name_limit_t snlim;
-  if (!compute_objsize_r (ptr, ostype, pref, snlim, &qry))
+  if (!compute_objsize_r (ptr, stmt, ostype, pref, snlim, ptr_qry))
     return NULL_TREE;
 
   offset_int maxsize = pref->size_remaining ();
@@ -2152,26 +2164,12 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
    transition to the pointer_query API.  */
 
 tree
-compute_objsize (tree ptr, int ostype, access_ref *pref, pointer_query *ptr_qry)
+compute_objsize (tree ptr, gimple *stmt, int ostype, access_ref *pref,
+		 range_query *rvals /* = NULL */)
 {
   pointer_query qry;
-  if (ptr_qry)
-    ptr_qry->depth = 0;
-  else
-    ptr_qry = &qry;
-
-  /* Clear and invalidate in case *PREF is being reused.  */
-  pref->offrng[0] = pref->offrng[1] = 0;
-  pref->sizrng[0] = pref->sizrng[1] = -1;
-
-  ssa_name_limit_t snlim;
-  if (!compute_objsize_r (ptr, ostype, pref, snlim, ptr_qry))
-    return NULL_TREE;
-
-  offset_int maxsize = pref->size_remaining ();
-  if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0)
-    pref->offrng[0] = 0;
-  return wide_int_to_tree (sizetype, maxsize);
+  qry.rvals = rvals;
+  return compute_objsize (ptr, stmt, ostype, pref, &qry);
 }
 
 /* Legacy wrapper around the above.  The function should be removed
@@ -2184,7 +2182,7 @@ compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
   /* Set the initial offsets to zero and size to negative to indicate
      none has been computed yet.  */
   access_ref ref;
-  tree size = compute_objsize (ptr, ostype, &ref, rvals);
+  tree size = compute_objsize (ptr, nullptr, ostype, &ref, rvals);
   if (!size || !ref.base0)
     return NULL_TREE;
 
diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h
index 3c8172c652d..96c500132b6 100644
--- a/gcc/pointer-query.h
+++ b/gcc/pointer-query.h
@@ -60,18 +60,16 @@ class pointer_query;
 /* Describes a reference to an object used in an access.  */
 struct access_ref
 {
-  /* Set the bounds of the reference to at most as many bytes
-     as the first argument or unknown when null, and at least
-     one when the second argument is true unless the first one
-     is a constant zero.  */
-  access_ref (tree = NULL_TREE, bool = false);
+  /* Set the bounds of the reference.  */
+  access_ref (range_query *query = nullptr, tree = NULL_TREE,
+	      gimple * = nullptr, bool = false);
 
   /* Return the PHI node REF refers to or null if it doesn't.  */
   gphi *phi () const;
 
   /* Return the object to which REF refers.  */
-  tree get_ref (vec<access_ref> *, access_ref * = NULL, int = 1,
-		ssa_name_limit_t * = NULL, pointer_query * = NULL) const;
+  tree get_ref (vec<access_ref> *, access_ref * = nullptr, int = 1,
+		ssa_name_limit_t * = nullptr, pointer_query * = nullptr) const;
 
   /* Return true if OFFRNG is the constant zero.  */
   bool offset_zero () const
@@ -85,7 +83,7 @@ struct access_ref
 
   /* Return the maximum amount of space remaining and if non-null, set
      argument to the minimum.  */
-  offset_int size_remaining (offset_int * = NULL) const;
+  offset_int size_remaining (offset_int * = nullptr) const;
 
 /* Return true if the offset and object size are in range for SIZE.  */
   bool offset_in_range (const offset_int &) const;
@@ -172,13 +170,13 @@ public:
   };
 
   /* Construct an object with the given Ranger instance and cache.  */
-  explicit pointer_query (range_query * = NULL, cache_type * = NULL);
+  explicit pointer_query (range_query * = nullptr, cache_type * = nullptr);
 
   /* Retrieve the access_ref for a variable from cache if it's there.  */
   const access_ref* get_ref (tree, int = 1) const;
 
   /* Retrieve the access_ref for a variable from cache or compute it.  */
-  bool get_ref (tree, access_ref*, int = 1);
+  bool get_ref (tree, gimple *, access_ref*, int = 1);
 
   /* Add an access_ref for the SSA_NAME to the cache.  */
   void put_ref (tree, const access_ref&, int = 1);
@@ -208,19 +206,23 @@ struct access_data
 {
   /* Set the access to at most MAXWRITE and MAXREAD bytes, and
      at least 1 when MINWRITE or MINREAD, respectively, is set.  */
-  access_data (gimple *stmt, access_mode mode,
+  access_data (range_query *query, gimple *stmt, access_mode mode,
 	       tree maxwrite = NULL_TREE, bool minwrite = false,
 	       tree maxread = NULL_TREE, bool minread = false)
     : stmt (stmt), call (),
-      dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
+      dst (query, maxwrite, stmt, minwrite),
+      src (query, maxread, stmt, minread),
+      mode (mode) { }
 
   /* Set the access to at most MAXWRITE and MAXREAD bytes, and
      at least 1 when MINWRITE or MINREAD, respectively, is set.  */
-  access_data (tree expr, access_mode mode,
+  access_data (range_query *query, tree expr, access_mode mode,
 	       tree maxwrite = NULL_TREE, bool minwrite = false,
 	       tree maxread = NULL_TREE, bool minread = false)
     : stmt (), call (expr),
-      dst (maxwrite, minwrite), src (maxread, minread), mode (mode) { }
+      dst (query, maxwrite, nullptr, minwrite),
+      src (query, maxread, nullptr, minread),
+      mode (mode) { }
 
   /* Access statement.  */
   gimple *stmt;
@@ -245,14 +247,23 @@ extern bool get_size_range (tree, tree[2], int = 0);
 extern bool get_size_range (range_query *, tree, gimple *, tree[2], int = 0);
 
 class range_query;
-extern tree gimple_call_alloc_size (gimple *, wide_int[2] = NULL,
-				    range_query * = NULL);
-extern tree gimple_parm_array_size (tree, wide_int[2], bool * = NULL);
+extern tree gimple_call_alloc_size (gimple *, wide_int[2] = nullptr,
+				    range_query * = nullptr);
+
+/* Compute the size of an object referenced by the first argument in
+   a statement given by second argument, using Object Size Type given
+   by third argument.  Store result in an access_ref.  */
+extern tree compute_objsize (tree, gimple *, int, access_ref *,
+			     range_query * = nullptr);
+extern tree compute_objsize (tree, gimple *, int, access_ref *,
+			     pointer_query *);
+inline tree compute_objsize (tree ptr, int ostype, access_ref *pref)
+{
+  return compute_objsize (ptr, nullptr, ostype, pref, (range_query *)nullptr);
+}
 
-extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
 /* Legacy/transitional API.  Should not be used in new code.  */
-extern tree compute_objsize (tree, int, access_ref *, pointer_query *);
-extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
-			     range_query * = NULL);
+extern tree compute_objsize (tree, int, tree * = nullptr, tree * = nullptr,
+			     range_query * = nullptr);
 
 #endif   // GCC_POINTER_QUERY_H
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c
index 8eaaa713275..764b1990276 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-22.c
@@ -260,13 +260,12 @@ T (puts_unlocked, a);       // { dg-warning "missing terminating nul" "puts_unlo
 
 // Exerise exec functions.
 T (execl, a, s, NULL);      // { dg-warning "missing terminating nul" "execl" }
-T (execl, a, s, NULL);      // { dg-warning "missing terminating nul" "execl" }
-T (execle, a, s, NULL, NULL);   // { dg-warning "missing terminating nul" "execl" }
-T (execlp, a, s, NULL);     // { dg-warning "missing terminating nul" "execl" }
+T (execle, a, s, NULL, NULL);   // { dg-warning "missing terminating nul" "execle" }
+T (execlp, a, s, NULL);     // { dg-warning "missing terminating nul" "execlp" }
 
-T (execv, a, &d);           // { dg-warning "missing terminating nul" "execl" }
-T (execve, a, &d, &d);      // { dg-warning "missing terminating nul" "execl" }
-T (execvp, a, &d);          // { dg-warning "missing terminating nul" "execl" }
+T (execv, a, &d);           // { dg-warning "missing terminating nul" "execv" }
+T (execve, a, &d, &d);      // { dg-warning "missing terminating nul" "execve" }
+T (execvp, a, &d);          // { dg-warning "missing terminating nul" "execvp" }
 
 T (gettext, a);             // { dg-warning "missing terminating nul" "gettext" }
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c
new file mode 100644
index 00000000000..e8bc327722a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-77.c
@@ -0,0 +1,38 @@
+/* Verify that -Wstringop-overflow uses context-sensitive range info
+   even at -O0.
+   { dg-do compile }
+   { dg-options "-O0 -Wall" } */
+
+extern void* memset (void*, int, __SIZE_TYPE__);
+
+char a[8];
+
+void warn_offset_range (int i)
+{
+  if (i < 4)
+    i = 4;
+  memset (a + i, 0, 5);       // { dg-warning "writing 5 bytes into a region of size 4 " }
+}
+
+void warn_size_range (int i, int n)
+{
+  if (n < 5)
+    n = 5;
+
+  memset (a + 4, 1, n);      // { dg-warning "writing between 5 and \\d+ bytes into a region of size 4 " }
+}
+
+void warn_offset_and_size_range (int i, int n)
+{
+  if (n < 5)
+    n = 5;
+
+  if (i < 4)
+    {
+      if (n < 9)
+	n = 9;
+      memset (a + i, 1, n);   // { dg-warning "writing between 9 and \\d+ bytes into a region of size 8 " }
+    }
+  else
+    memset (a + i, 0, n);     // { dg-warning "writing between 5 and \\d+ bytes into a region of size 4 " }
+}
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 8c39869616f..2de7cb1a6a0 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -1833,7 +1833,7 @@ strlen_pass::adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat)
       tree dst = gimple_call_arg (last.stmt, 0);
 
       access_ref aref;
-      tree size = compute_objsize (dst, 1, &aref, &ptr_qry);
+      tree size = compute_objsize (dst, stmt, 1, &aref, &ptr_qry);
       if (size && tree_int_cst_lt (size, len))
 	return;
     }
@@ -2035,7 +2035,7 @@ strlen_pass::maybe_warn_overflow (gimple *stmt, bool call_lhs, tree len,
   access_ref aref;
   /* The size of the destination region (which is smaller than
      the destination object for stores at a non-zero offset).  */
-  tree destsize = compute_objsize (dest, ostype, &aref, &ptr_qry);
+  tree destsize = compute_objsize (dest, stmt, ostype, &aref, &ptr_qry);
 
   if (!destsize)
     {
@@ -3115,7 +3115,7 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
     }
 
   access_ref aref;
-  if (tree dstsize = compute_objsize (dst, 1, &aref, ptr_qry))
+  if (tree dstsize = compute_objsize (dst, stmt, 1, &aref, ptr_qry))
     {
       /* The source length is unknown.  Try to determine the destination
 	 size and see if it matches the specified bound.  If not, bail.
@@ -3130,7 +3130,7 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
       /* 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, &aref, ptr_qry))
+      if (tree srcsize = compute_objsize (src, stmt, 1, &aref, ptr_qry))
 	if (wi::to_wide (srcsize) == cntrange[1])
 	  return false;
 
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc
index a2320f2b564..28059748ab7 100644
--- a/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/capacity/1.cc
@@ -17,7 +17,7 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// { dg-options "-Wno-stringop-overflow" }
+// { dg-options "-Wno-stringop-overflow -Wstringop-overread" }
 
 // 21.3.3 string capacity
 
diff --git a/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc b/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc
index f52900d7fda..f5d281fd25a 100644
--- a/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc
+++ b/libstdc++-v3/testsuite/27_io/filesystem/path/factory/u8path-char8_t.cc
@@ -15,7 +15,7 @@
 // with this library; see the file COPYING3.  If not see
 // <http://www.gnu.org/licenses/>.
 
-// { dg-options "-fchar8_t" }
+// { dg-options "-fchar8_t -Wstringop-overread" }
 // { dg-do run { target c++17 } }
 
 #include <filesystem>
@@ -36,6 +36,7 @@ test01()
   p = fs::u8path(u8"\xf0\x9d\x84\x9e");
   VERIFY( p.u8string() == u8"\U0001D11E" );
 
+  // The following triggers a -Wstringop-overread.
   std::u8string s1 = u8"filename2";
   p = fs::u8path(s1);
   VERIFY( p.u8string() == u8"filename2" );
