public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] handle conditionals in -Wstringop-overflow et al. (PR 92936)
@ 2020-11-03  2:24 Martin Sebor
  2020-11-13 21:34 ` Jeff Law
  0 siblings, 1 reply; 6+ messages in thread
From: Martin Sebor @ 2020-11-03  2:24 UTC (permalink / raw)
  To: gcc-patches, Jeff Law

[-- Attachment #1: Type: text/plain, Size: 893 bytes --]

The attached patch extends compute_objsize() to handle conditional
expressions represented either as PHIs or MIN_EXPR and MAX_EXPR.

To simplify the handling of the -Wstringop-overflow/-overread
warnings the change factors this code out of tree-ssa-strlen.c
and into inform_access() in builtins.c, making it a member of
access_ref.  Besides eliminating a decent amount of code
duplication this also improves the consistency of the warnings.

Finally, the change introduces a distinction between the definite
kinds of -Wstringop-overflow (and -Wstringop-overread) warnings
and the maybe kind.  The latter are currently only being issued
for function array parameters but I expect to make use of them
more extensively in the future.

Besides the usual GCC bootstrap/regtest I have tested the change
with Binutils/GDB and Glibc and verified that it doesn't introduce
any false positives.

Martin

[-- Attachment #2: gcc-92936.diff --]
[-- Type: text/x-patch, Size: 113519 bytes --]

PR middle-end/92936 - missing warning on a past-the-end store to a PHI
PR middle-end/92940 - incorrect offset and size in -Wstringop-overflow for out-of-bounds store into VLA and two offset ranges
PR middle-end/89428 - missing -Wstringop-overflow on a PHI with variable offset

gcc/ChangeLog:

	PR middle-end/92936
	PR middle-end/92940
	PR middle-end/89428
	* builtins.c (access_ref::access_ref): Initialize member.
	(access_ref::phi): New function.
	(access_ref::get_ref): New function.
	(access_ref::add_offset): Remove duplicate assignment.
	(maybe_warn_for_bound): Add "maybe" kind of warning messages.
	(warn_for_access): Same.
	(inform_access): Rename...
	(access_ref::inform_access): ...to this.  Print PHI arguments.  Format
	offset the same as size and simplify.  Improve printing of allocation
	functions and VLAs.
	(check_access): Adjust to the above.
	(gimple_parm_array_size): Change argument.
	(handle_min_max_size): New function.
	* builtins.h (struct access_ref): Declare new members.
	(gimple_parm_array_size): Change argument.
	* tree-ssa-strlen.c (maybe_warn_overflow): Use access_ref and simplify.
	(handle_builtin_memcpy): Correct argument passed to maybe_warn_overflow.
	(handle_builtin_memset): Same.

gcc/testsuite/ChangeLog:

	PR middle-end/92936
	PR middle-end/92940
	PR middle-end/89428
	* c-c++-common/Wstringop-overflow-2.c: Adjust text of expected
	informational notes.
	* gcc.dg/Wstringop-overflow-11.c: Remove xfails.
	* gcc.dg/Wstringop-overflow-12.c: Same.
	* gcc.dg/Wstringop-overflow-17.c: Adjust text of expected messages.
	* gcc.dg/Wstringop-overflow-27.c: Same.  Remove xfails.
	* gcc.dg/Wstringop-overflow-28.c: Adjust text of expected messages.
	* gcc.dg/Wstringop-overflow-29.c: Same.
	* gcc.dg/Wstringop-overflow-37.c: Same.
	* gcc.dg/Wstringop-overflow-46.c: Same.
	* gcc.dg/Wstringop-overflow-47.c: Same.
	* gcc.dg/Wstringop-overflow-54.c: Same.
	* gcc.dg/warn-strnlen-no-nul.c: Add expected warning.
	* gcc.dg/Wstringop-overflow-58.c: New test.
	* gcc.dg/Wstringop-overflow-59.c: New test.
	* gcc.dg/Wstringop-overflow-60.c: New test.
	* gcc.dg/Wstringop-overflow-61.c: New test.
	* gcc.dg/Wstringop-overflow-62.c: New test.


diff --git a/gcc/builtins.c b/gcc/builtins.c
index da25343beb1..5d60eab6ba2 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -185,6 +185,7 @@ static void maybe_emit_sprintf_chk_warning (tree, enum built_in_function);
 static void maybe_emit_free_warning (tree);
 static tree fold_builtin_object_size (tree, tree);
 static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
+static bool compute_objsize (tree, int, access_ref *, bitmap *, range_query *);
 
 unsigned HOST_WIDE_INT target_newline;
 unsigned HOST_WIDE_INT target_percent;
@@ -199,7 +200,8 @@ static void expand_builtin_sync_synchronize (void);
 
 access_ref::access_ref (tree bound /* = NULL_TREE */,
 			bool minaccess /* = false */)
-: ref (), eval ([](tree x){ return x; }), trail1special (true), base0 (true)
+: ref (), eval ([](tree x){ return x; }), trail1special (true), base0 (true),
+  parmarray ()
 {
   /* Set to valid.  */
   offrng[0] = offrng[1] = 0;
@@ -222,6 +224,131 @@ access_ref::access_ref (tree bound /* = NULL_TREE */,
     }
 }
 
+/* Return the PHI node REF refers to or null if it doesn't.  */
+
+gphi *
+access_ref::phi () const
+{
+  if (!ref || TREE_CODE (ref) != SSA_NAME)
+    return NULL;
+
+  gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
+  if (gimple_code (def_stmt) != GIMPLE_PHI)
+    return NULL;
+
+  return as_a <gphi *> (def_stmt);
+}
+
+/* Determine and return the largest object to which REF refers.  If REF
+   refers to a PHI and PREF is nonnull, fill *PREF with the details of
+   the object determined by compute_objsize(ARG, OSTYPE) for each PHI
+   argument ARG.  */
+
+tree
+access_ref::get_ref (vec<access_ref> *all_refs,
+		     access_ref *pref /* = NULL */,
+		     int ostype /* = 1 */,
+		     bitmap *visited /* = NULL */,
+		     range_query *rvals /* = NULL */) const
+{
+  gphi *phi_stmt = this->phi ();
+  if (!phi_stmt)
+    return ref;
+
+  bitmap visited_phis = NULL;
+  if (!visited)
+    visited = &visited_phis;
+
+  if (!*visited)
+    *visited = BITMAP_ALLOC (NULL);
+
+  if (!bitmap_set_bit (*visited, SSA_NAME_VERSION (ref)))
+    /* Have compute_objsize() fail and propagate the failure below.  */
+    return NULL_TREE;
+
+  access_ref phi_ref;
+  if (pref)
+    phi_ref = *pref;
+
+  /* Set if any argument is a function array (or VLA) parameter not
+     declared [static].  */
+  bool parmarray = false;
+  /* The size of the smallest object referenced by the PHI arguments.  */
+  offset_int minsize = 0;
+  const offset_int maxobjsize = wi::to_offset (max_object_size ());
+  const offset_int orng[2] = { phi_ref.offrng[0], phi_ref.offrng[1] };
+
+  const unsigned nargs = gimple_phi_num_args (phi_stmt);
+  for (unsigned i = 0; i < nargs; ++i)
+    {
+      access_ref phi_arg_ref;
+      tree arg = gimple_phi_arg_def (phi_stmt, i);
+      if (!compute_objsize (arg, ostype, &phi_arg_ref, visited, rvals))
+	return NULL_TREE;
+
+      /* The call to compute_objsize() above will fail when the ARG has
+	 already been VISITED.  */
+      gcc_checking_assert (phi_arg_ref.sizrng[0] >= 0);
+
+      /* Add PREF's offset to that of the argument.  */
+      phi_arg_ref.add_offset (orng[0], orng[1]);
+
+      if (all_refs)
+	all_refs->safe_push (phi_arg_ref);
+
+      const bool arg_known_size = (phi_arg_ref.sizrng[0] != 0
+				   || phi_arg_ref.sizrng[1] != maxobjsize);
+
+      parmarray |= phi_arg_ref.parmarray;
+
+      if (phi_ref.sizrng[0] < 0)
+	{
+	  phi_ref = phi_arg_ref;
+	  if (arg_known_size)
+	    minsize = phi_arg_ref.sizrng[0];
+	  continue;
+	}
+
+      const bool phi_known_size = (phi_ref.sizrng[0] != 0
+				   || phi_ref.sizrng[1] != maxobjsize);
+
+      if (phi_known_size && phi_arg_ref.sizrng[0] < minsize)
+	minsize = phi_arg_ref.sizrng[0];
+
+      /* Determine the amount of remaining space in the argument.  */
+      offset_int argrem[2];
+      argrem[1] = phi_arg_ref.size_remaining (argrem);
+
+      /* Determine the amount of remaining space computed so far and
+	 if the remaining space in the argument is more use it instead.  */
+      offset_int phirem[2];
+      phirem[1] = phi_ref.size_remaining (phirem);
+
+      if (phirem[1] < argrem[1])
+	/* Use the argument with the most space remaining as the result.  */
+	phi_ref = phi_arg_ref;
+    }
+
+  if (phi_ref.sizrng[0] < 0)
+    /* Fail if none of the PHI's arguments resulted in updating PHI_REF
+       (perhaps because they have all been already visited by prior
+       recursive calls).  */
+    return NULL_TREE;
+
+  /* Replace the lower bound of the largest argument with the size of
+     the smallest argument, and set PARMARRAY if any argument was one.  */
+  phi_ref.sizrng[0] = minsize;
+  phi_ref.parmarray = parmarray;
+
+  if (visited == &visited_phis)
+    BITMAP_FREE (visited_phis);
+
+  if (pref && pref != this)
+    *pref = phi_ref;
+
+  return phi_ref.ref;
+}
+
 /* Return the maximum amount of space remaining and if non-null, set
    argument to the minimum.  */
 
@@ -318,7 +445,6 @@ void access_ref::add_offset (const offset_int &min, const offset_int &max)
 	  return;
 	}
 
-      offrng[1] = maxoff;
       offset_int absmax = wi::abs (max);
       if (offrng[0] < absmax)
 	{
@@ -3560,28 +3686,42 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
 
   if (opt == OPT_Wstringop_overread)
     {
+      bool maybe = pad && pad->src.phi ();
+
       if (tree_int_cst_lt (maxobjsize, bndrng[0]))
 	{
 	  if (bndrng[0] == bndrng[1])
 	    warned = (func
 		      ? warning_at (loc, opt,
-				    "%K%qD specified bound %E "
-				    "exceeds maximum object size %E",
+				    (maybe
+				     ? G_("%K%qD specified bound %E may "
+					  "exceed maximum object size %E")
+				     : G_("%K%qD specified bound %E "
+					  "exceeds maximum object size %E")),
 				    exp, func, bndrng[0], maxobjsize)
 		      : warning_at (loc, opt,
-				    "%Kspecified bound %E "
-				    "exceeds maximum object size %E",
+				    (maybe
+				     ? G_("%Kspecified bound %E may "
+					  "exceed maximum object size %E")
+				     : G_("%Kspecified bound %E "
+					  "exceeds maximum object size %E")),
 				    exp, bndrng[0], maxobjsize));
 	  else
 	    warned = (func
 		      ? warning_at (loc, opt,
-				    "%K%qD specified bound [%E, %E] "
-				    "exceeds maximum object size %E",
+				    (maybe
+				     ? G_("%K%qD specified bound [%E, %E] may "
+					  "exceed maximum object size %E")
+				     : G_("%K%qD specified bound [%E, %E] "
+					  "exceeds maximum object size %E")),
 				    exp, func,
 				    bndrng[0], bndrng[1], maxobjsize)
 		      : warning_at (loc, opt,
-				    "%Kspecified bound [%E, %E] "
-				    "exceeds maximum object size %E",
+				    (maybe
+				     ? G_("%Kspecified bound [%E, %E] may "
+					  "exceed maximum object size %E")
+				     : G_("%Kspecified bound [%E, %E] "
+					  "exceeds maximum object size %E")),
 				    exp, bndrng[0], bndrng[1], maxobjsize));
 	}
       else if (!size || tree_int_cst_le (bndrng[0], size))
@@ -3589,22 +3729,34 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
       else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
 	warned = (func
 		  ? warning_at (loc, opt,
-				"%K%qD specified bound %E exceeds "
-				"source size %E",
+				(maybe
+				 ? G_("%K%qD specified bound %E may exceed "
+				      "source size %E")
+				 : G_("%K%qD specified bound %E exceeds "
+				      "source size %E")),
 				exp, func, bndrng[0], size)
 		  : warning_at (loc, opt,
-				"%Kspecified bound %E exceeds "
-				"source size %E",
+				(maybe
+				 ? G_("%Kspecified bound %E may exceed "
+				      "source size %E")
+				 : G_("%Kspecified bound %E exceeds "
+				      "source size %E")),
 				exp, bndrng[0], size));
       else
 	warned = (func
 		  ? warning_at (loc, opt,
-				"%K%qD specified bound [%E, %E] exceeds "
-				"source size %E",
+				(maybe
+				 ? G_("%K%qD specified bound [%E, %E] may "
+				      "exceed source size %E")
+				 : G_("%K%qD specified bound [%E, %E] exceeds "
+				      "source size %E")),
 				exp, func, bndrng[0], bndrng[1], size)
 		  : warning_at (loc, opt,
-				"%Kspecified bound [%E, %E] exceeds "
-				"source size %E",
+				(maybe
+				 ? G_("%Kspecified bound [%E, %E] may exceed "
+				      "source size %E")
+				 : G_("%Kspecified bound [%E, %E] exceeds "
+				      "source size %E")),
 				exp, bndrng[0], bndrng[1], size));
       if (warned)
 	{
@@ -3623,28 +3775,41 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
       return warned;
     }
 
+  bool maybe = pad && pad->dst.phi ();
   if (tree_int_cst_lt (maxobjsize, bndrng[0]))
     {
       if (bndrng[0] == bndrng[1])
 	warned = (func
 		  ? warning_at (loc, opt,
-				"%K%qD specified size %E "
-				"exceeds maximum object size %E",
+				(maybe
+				 ? G_("%K%qD specified size %E may "
+				      "exceed maximum object size %E")
+				 : G_("%K%qD specified size %E "
+				      "exceeds maximum object size %E")),
 				exp, func, bndrng[0], maxobjsize)
 		  : warning_at (loc, opt,
-				"%Kspecified size %E "
-				"exceeds maximum object size %E",
+				(maybe
+				 ? G_("%Kspecified size %E may exceed "
+				      "maximum object size %E")
+				 : G_("%Kspecified size %E exceeds "
+				      "maximum object size %E")),
 				exp, bndrng[0], maxobjsize));
       else
 	warned = (func
 		  ? warning_at (loc, opt,
-				"%K%qD specified size between %E and %E "
-				"exceeds maximum object size %E",
+				(maybe
+				 ? G_("%K%qD specified size between %E and %E "
+				      "may exceed maximum object size %E")
+				 : G_("%K%qD specified size between %E and %E "
+				      "exceeds maximum object size %E")),
 				exp, func,
 				bndrng[0], bndrng[1], maxobjsize)
 		  : warning_at (loc, opt,
-				"%Kspecified size between %E and %E "
-				"exceeds maximum object size %E",
+				(maybe
+				 ? G_("%Kspecified size between %E and %E "
+				      "may exceed maximum object size %E")
+				 : G_("%Kspecified size between %E and %E "
+				      "exceeds maximum object size %E")),
 				exp, bndrng[0], bndrng[1], maxobjsize));
     }
   else if (!size || tree_int_cst_le (bndrng[0], size))
@@ -3652,22 +3817,34 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
   else if (tree_int_cst_equal (bndrng[0], bndrng[1]))
     warned = (func
 	      ? warning_at (loc, OPT_Wstringop_overflow_,
-			    "%K%qD specified bound %E exceeds "
-			    "destination size %E",
+			    (maybe
+			     ? G_("%K%qD specified bound %E may exceed "
+				  "destination size %E")
+			     : G_("%K%qD specified bound %E exceeds "
+				  "destination size %E")),
 			    exp, func, bndrng[0], size)
 	      : warning_at (loc, OPT_Wstringop_overflow_,
-			    "%Kspecified bound %E exceeds "
-			    "destination size %E",
+			    (maybe
+			     ? G_("%Kspecified bound %E may exceed "
+				  "destination size %E")
+			     : G_("%Kspecified bound %E exceeds "
+				  "destination size %E")),
 			    exp, bndrng[0], size));
   else
     warned = (func
 	      ? warning_at (loc, OPT_Wstringop_overflow_,
-			    "%K%qD specified bound [%E, %E] exceeds "
-			    "destination size %E",
+			    (maybe
+			     ? G_("%K%qD specified bound [%E, %E] may exceed "
+				  "destination size %E")
+			     : G_("%K%qD specified bound [%E, %E] exceeds "
+				  "destination size %E")),
 			    exp, func, bndrng[0], bndrng[1], size)
 	      : warning_at (loc, OPT_Wstringop_overflow_,
-			    "%Kspecified bound [%E, %E] exceeds "
-			    "destination size %E",
+			    (maybe
+			     ? G_("%Kspecified bound [%E, %E] exceeds "
+				  "destination size %E")
+			     : G_("%Kspecified bound [%E, %E] exceeds "
+				  "destination size %E")),
 			    exp, bndrng[0], bndrng[1], size));
 
   if (warned)
@@ -3696,7 +3873,7 @@ maybe_warn_for_bound (int opt, location_t loc, tree exp, tree func,
 
 static bool
 warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
-		 tree size, bool write, bool read)
+		 tree size, bool write, bool read, bool maybe)
 {
   bool warned = false;
 
@@ -3705,40 +3882,64 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
       if (tree_int_cst_equal (range[0], range[1]))
 	warned = (func
 		  ? warning_n (loc, opt, tree_to_uhwi (range[0]),
-			       "%K%qD accessing %E byte in a region "
-			       "of size %E",
-			       "%K%qD accessing %E bytes in a region "
-			       "of size %E",
+			       (maybe
+				? G_("%K%qD may access %E byte in a region "
+				     "of size %E")
+				: G_("%K%qD accessing %E byte in a region "
+				     "of size %E")),
+				(maybe
+				 ? G_ ("%K%qD may access %E bytes in a region "
+				       "of size %E")
+				 : G_ ("%K%qD accessing %E bytes in a region "
+				       "of size %E")),
 			       exp, func, range[0], size)
 		  : warning_n (loc, opt, tree_to_uhwi (range[0]),
-			       "%Kaccessing %E byte in a region "
-			       "of size %E",
-			       "%Kaccessing %E bytes in a region "
-			       "of size %E",
+			       (maybe
+				? G_("%Kmay access %E byte in a region "
+				     "of size %E")
+				: G_("%Kaccessing %E byte in a region "
+				     "of size %E")),
+			       (maybe
+				? G_("%Kmay access %E bytes in a region "
+				     "of size %E")
+				: G_("%Kaccessing %E bytes in a region "
+				     "of size %E")),
 			       exp, range[0], size));
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
 	  warned = (func
 		    ? warning_at (loc, opt,
-				  "%K%qD accessing %E or more bytes in "
-				  "a region of size %E",
+				  (maybe
+				   ? G_("%K%qD may access %E or more bytes "
+					"in a region of size %E")
+				   : G_("%K%qD accessing %E or more bytes "
+					"in a region of size %E")),
 				  exp, func, range[0], size)
 		    : warning_at (loc, opt,
-				  "%Kaccessing %E or more bytes in "
-				  "a region of size %E",
+				  (maybe
+				   ? G_("%Kmay access %E or more bytes "
+					"in a region of size %E")
+				   : G_("%Kaccessing %E or more bytes "
+					"in a region of size %E")),
 				  exp, range[0], size));
 	}
       else
 	warned = (func
 		  ? warning_at (loc, opt,
-				"%K%qD accessing between %E and %E bytes "
-				"in a region of size %E",
+				(maybe
+				 ? G_("%K%qD may access between %E and %E "
+				      "bytes in a region of size %E")
+				 : G_("%K%qD accessing between %E and %E "
+				      "bytes in a region of size %E")),
 				exp, func, range[0], range[1],
 				size)
 		  : warning_at (loc, opt,
-				"%Kaccessing between %E and %E bytes "
-				"in a region of size %E",
+				(maybe
+				 ? G_("%Kmay access between %E and %E bytes "
+				      "in a region of size %E")
+				 : G_("%Kaccessing between %E and %E bytes "
+				      "in a region of size %E")),
 				exp, range[0], range[1],
 				size));
       return warned;
@@ -3749,44 +3950,69 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
       if (tree_int_cst_equal (range[0], range[1]))
 	warned = (func
 		  ? warning_n (loc, opt, tree_to_uhwi (range[0]),
-			       "%K%qD writing %E byte into a region "
-			       "of size %E overflows the destination",
-			       "%K%qD writing %E bytes into a region "
-			       "of size %E overflows the destination",
+			       (maybe
+				? G_("%K%qD may write %E byte into a region "
+				     "of size %E")
+				: G_("%K%qD writing %E byte into a region "
+				     "of size %E overflows the destination")),
+			       (maybe
+				? G_("%K%qD may write %E bytes into a region "
+				     "of size %E")
+				: G_("%K%qD writing %E bytes into a region "
+				     "of size %E overflows the destination")),
 			       exp, func, range[0], size)
 		  : warning_n (loc, opt, tree_to_uhwi (range[0]),
-			       "%Kwriting %E byte into a region "
-			       "of size %E overflows the destination",
-			       "%Kwriting %E bytes into a region "
-			       "of size %E overflows the destination",
+			       (maybe
+				? G_("%Kmay write %E byte into a region "
+				     "of size %E")
+				: G_("%Kwriting %E byte into a region "
+				     "of size %E overflows the destination")),
+			       (maybe
+				? G_("%Kmay write %E bytes into a region "
+				     "of size %E")
+				: G_("%Kwriting %E bytes into a region "
+				     "of size %E overflows the destination")),
 			       exp, range[0], size));
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
 	  warned = (func
 		    ? warning_at (loc, opt,
-				  "%K%qD writing %E or more bytes into "
-				  "a region of size %E overflows "
-				  "the destination",
+				  (maybe
+				   ? G_("%K%qD may write %E or more bytes "
+					"into a region of size %E "
+					"the destination")
+				   : G_("%K%qD writing %E or more bytes "
+					"into a region of size %E overflows "
+					"the destination")),
 				  exp, func, range[0], size)
 		    : warning_at (loc, opt,
-				  "%Kwriting %E or more bytes into "
-				  "a region of size %E overflows "
-				  "the destination",
+				  (maybe
+				   ? G_("%Kmay write %E or more bytes into "
+					"a region of size %E")
+				   : G_("%Kwriting %E or more bytes into "
+					"a region of size %E overflows "
+					"the destination")),
 				  exp, range[0], size));
 	}
       else
 	warned = (func
 		  ? warning_at (loc, opt,
-				"%K%qD writing between %E and %E bytes "
-				"into a region of size %E overflows "
-				"the destination",
+				(maybe
+				 ? G_("%K%qD may write between %E and %E bytes "
+				      "into a region of size %E")
+				 : G_("%K%qD writing between %E and %E bytes "
+				      "into a region of size %E overflows "
+				      "the destination")),
 				exp, func, range[0], range[1],
 				size)
 		  : warning_at (loc, opt,
-				"%Kwriting between %E and %E bytes "
-				"into a region of size %E overflows "
-				"the destination",
+				(maybe
+				 ? G_("%Kmay write between %E and %E bytes "
+				      "into a region of size %E")
+				 : G_("%Kwriting between %E and %E bytes "
+				      "into a region of size %E overflows "
+				      "the destination")),
 				exp, range[0], range[1],
 				size));
       return warned;
@@ -3798,35 +4024,64 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
 	warned = (func
 		  ? warning_n (loc, OPT_Wstringop_overread,
 			       tree_to_uhwi (range[0]),
-			       "%K%qD reading %E byte from a region of size %E",
-			       "%K%qD reading %E bytes from a region of size %E",			       exp, func, range[0], size)
+			       (maybe
+				? G_("%K%qD may reade %E byte from a region "
+				     "of size %E")
+				: G_("%K%qD reading %E byte from a region "
+				     "of size %E")),
+			       (maybe
+				? G_("%K%qD may read %E bytes from a region "
+				     "of size %E")
+				: G_("%K%qD reading %E bytes from a region "
+				     "of size %E")),
+			       exp, func, range[0], size)
 		  : warning_n (loc, OPT_Wstringop_overread,
 			       tree_to_uhwi (range[0]),
-			       "%Kreading %E byte from a region of size %E",
-			       "%Kreading %E bytes from a region of size %E",
+			       (maybe
+				? G_("%Kmay read %E byte from a region "
+				     "of size %E")
+				: G_("%Kreading %E byte from a region "
+				     "of size %E")),
+			       (maybe
+				? G_("%Kmay read %E bytes from a region "
+				     "of size %E")
+				: G_("%Kreading %E bytes from a region "
+				     "of size %E")),
 			       exp, range[0], size));
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
 	  warned = (func
 		    ? warning_at (loc, OPT_Wstringop_overread,
-				  "%K%qD reading %E or more bytes from "
-				  "a region of size %E",
+				  (maybe
+				   ? G_("%K%qD may read %E or more bytes "
+					"from a region of size %E")
+				   : G_("%K%qD reading %E or more bytes "
+					"from a region of size %E")),
 				  exp, func, range[0], size)
 		    : warning_at (loc, OPT_Wstringop_overread,
-				  "%Kreading %E or more bytes from a region "
-				  "of size %E",
+				  (maybe
+				   ? G_("%Kmay read %E or more bytes "
+					"from a region of size %E")
+				   : G_("%Kreading %E or more bytes "
+					"from a region of size %E")),
 				  exp, range[0], size));
 	}
       else
 	warned = (func
 		  ? warning_at (loc, OPT_Wstringop_overread,
-				"%K%qD reading between %E and %E bytes from "
-				"a region of size %E",
+				(maybe
+				 ? G_("%K%qD may read between %E and %E bytes "
+				      "from a region of size %E")
+				 : G_("%K%qD reading between %E and %E bytes "
+				      "from a region of size %E")),
 				exp, func, range[0], range[1], size)
 		  : warning_at (loc, opt,
-				"%K reading between %E and %E bytes from "
-				"a region of size %E",
+				(maybe
+				 ? G_("%Kmay read between %E and %E bytes "
+				      "from a region of size %E")
+				 : G_("%Kreading between %E and %E bytes "
+				      "from a region of size %E")),
 				exp, range[0], range[1], size));
 
       if (warned)
@@ -3878,28 +4133,57 @@ warn_for_access (location_t loc, tree func, tree exp, int opt, tree range[2],
   return warned;
 }
 
-/* Issue an inform message describing the target of an access REF.
+/* Issue one inform message describing each target of an access REF.
    WRITE is set for a write access and clear for a read access.  */
 
-static void
-inform_access (const access_ref &ref, access_mode mode)
+void
+access_ref::inform_access (access_mode mode) const
 {
-  if (!ref.ref)
+  const access_ref &aref = *this;
+  if (!aref.ref)
     return;
 
+  if (aref.phi ())
+    {
+      /* Set MAXREF to refer to the largest object and fill ALL_REFS
+	 with data for all objects referenced by the PHI arguments.  */
+      access_ref maxref;
+      auto_vec<access_ref> all_refs;
+      if (!get_ref (&all_refs, &maxref))
+	return;
+
+      /* Except for MAXREF, the rest of the arguments' offsets need not
+	 reflect one added to the PHI itself.  Determine the latter from
+	 MAXREF on which the result is based.  */
+      const offset_int orng[] =
+	{
+	 offrng[0] - maxref.offrng[0],
+	 wi::smax (offrng[1] - maxref.offrng[1], offrng[0]),
+	};
+
+      /* Add the final PHI's offset to that of each of the arguments
+	 and recurse to issue an inform message for it.  */
+      for (unsigned i = 0; i != all_refs.length (); ++i)
+	{
+	  all_refs[i].add_offset (orng[0], orng[1]);
+	  all_refs[i].inform_access (mode);
+	}
+      return;
+    }
+
   /* Convert offset range and avoid including a zero range since it
      isn't necessarily meaningful.  */
   HOST_WIDE_INT diff_min = tree_to_shwi (TYPE_MIN_VALUE (ptrdiff_type_node));
   HOST_WIDE_INT diff_max = tree_to_shwi (TYPE_MAX_VALUE (ptrdiff_type_node));
   HOST_WIDE_INT minoff;
   HOST_WIDE_INT maxoff = diff_max;
-  if (wi::fits_shwi_p (ref.offrng[0]))
-    minoff = ref.offrng[0].to_shwi ();
+  if (wi::fits_shwi_p (aref.offrng[0]))
+    minoff = aref.offrng[0].to_shwi ();
   else
-    minoff = ref.offrng[0] < 0 ? diff_min : diff_max;
+    minoff = aref.offrng[0] < 0 ? diff_min : diff_max;
 
-  if (wi::fits_shwi_p (ref.offrng[1]))
-    maxoff = ref.offrng[1].to_shwi ();
+  if (wi::fits_shwi_p (aref.offrng[1]))
+    maxoff = aref.offrng[1].to_shwi ();
 
   if (maxoff <= diff_min || maxoff >= diff_max)
     /* Avoid mentioning an upper bound that's equal to or in excess
@@ -3909,110 +4193,127 @@ inform_access (const access_ref &ref, access_mode mode)
   /* Convert size range and always include it since all sizes are
      meaningful. */
   unsigned long long minsize = 0, maxsize = 0;
-  if (wi::fits_shwi_p (ref.sizrng[0])
-      && wi::fits_shwi_p (ref.sizrng[1]))
+  if (wi::fits_shwi_p (aref.sizrng[0])
+      && wi::fits_shwi_p (aref.sizrng[1]))
     {
-      minsize = ref.sizrng[0].to_shwi ();
-      maxsize = ref.sizrng[1].to_shwi ();
+      minsize = aref.sizrng[0].to_shwi ();
+      maxsize = aref.sizrng[1].to_shwi ();
     }
 
+  /* SIZRNG doesn't necessarily have the same range as the allocation
+     size determined by gimple_call_alloc_size ().  */
   char sizestr[80];
-  location_t loc;
-  tree allocfn = NULL_TREE;
-  if (TREE_CODE (ref.ref) == SSA_NAME)
-    {
-      gimple *stmt = SSA_NAME_DEF_STMT (ref.ref);
-      gcc_assert (is_gimple_call (stmt));
-      loc = gimple_location (stmt);
-      allocfn = gimple_call_fndecl (stmt);
-      if (!allocfn)
-	/* Handle calls through pointers to functions.  */
-	allocfn = gimple_call_fn (stmt);
-
-      /* SIZRNG doesn't necessarily have the same range as the allocation
-	 size determined by gimple_call_alloc_size ().  */
+  if (minsize == maxsize)
+    sprintf (sizestr, "%llu", minsize);
+  else
+    sprintf (sizestr, "[%llu, %llu]", minsize, maxsize);
+
+  char offstr[80];
+  if (minoff == 0
+      && (maxoff == 0 || aref.sizrng[1] <= maxoff))
+    offstr[0] = '\0';
+  else if (minoff == maxoff)
+    sprintf (offstr, "%lli", (long long) minoff);
+  else
+    sprintf (offstr, "[%lli, %lli]", (long long) minoff, (long long) maxoff);
 
-      if (minsize == maxsize)
-	sprintf (sizestr, "%llu", minsize);
-      else
-	sprintf (sizestr, "[%llu, %llu]", minsize, maxsize);
+  location_t loc = UNKNOWN_LOCATION;
 
+  tree ref = this->ref;
+  tree allocfn = NULL_TREE;
+  if (TREE_CODE (ref) == SSA_NAME)
+    {
+      gimple *stmt = SSA_NAME_DEF_STMT (ref);
+      if (is_gimple_call (stmt))
+	{
+	  loc = gimple_location (stmt);
+	  if (gimple_call_builtin_p (stmt, BUILT_IN_ALLOCA_WITH_ALIGN))
+	    {
+	      /* Strip the SSA_NAME suffix from the variable name and
+		 recreate an identifier with the VLA's original name.  */
+	      ref = gimple_call_lhs (stmt);
+	      ref = SSA_NAME_IDENTIFIER (ref);
+	      const char *id = IDENTIFIER_POINTER (ref);
+	      size_t len = strcspn (id, ".$");
+	      if (!len)
+		len = strlen (id);
+	      ref = get_identifier_with_length (id, len);
+	    }
+	  else
+	    {
+	      /* Except for VLAs, retrieve the allocation function.  */
+	      allocfn = gimple_call_fndecl (stmt);
+	      if (!allocfn)
+		allocfn = gimple_call_fn (stmt);
+	      if (TREE_CODE (allocfn) == SSA_NAME)
+		{
+		  /* For an ALLOC_CALL via a function pointer make a small
+		     effort to determine the destination of the pointer.  */
+		  gimple *def = SSA_NAME_DEF_STMT (allocfn);
+		  if (gimple_assign_single_p (def))
+		    {
+		      tree rhs = gimple_assign_rhs1 (def);
+		      if (DECL_P (rhs))
+			allocfn = rhs;
+		      else if (TREE_CODE (rhs) == COMPONENT_REF)
+			allocfn = TREE_OPERAND (rhs, 1);
+		    }
+		}
+	    }
+	}
+      else if (gimple_nop_p (stmt))
+	/* Handle DECL_PARM below.  */
+	ref = SSA_NAME_VAR (ref);
     }
-  else if (DECL_P (ref.ref))
-    loc = DECL_SOURCE_LOCATION (ref.ref);
-  else if (EXPR_P (ref.ref) && EXPR_HAS_LOCATION (ref.ref))
-    loc = EXPR_LOCATION (ref.ref);
-  else
+
+  if (DECL_P (ref))
+    loc = DECL_SOURCE_LOCATION (ref);
+  else if (EXPR_P (ref) && EXPR_HAS_LOCATION (ref))
+    loc = EXPR_LOCATION (ref);
+  else if (TREE_CODE (ref) != IDENTIFIER_NODE
+	   && TREE_CODE (ref) != SSA_NAME)
     return;
 
   if (mode == access_read_write || mode == access_write_only)
     {
       if (allocfn == NULL_TREE)
 	{
-	  if (minoff == maxoff)
-	    {
-	      if (minoff == 0)
-		inform (loc, "destination object %qE", ref.ref);
-	      else
-		inform (loc, "at offset %wi into destination object %qE",
-			minoff, ref.ref);
-	    }
+	  if (*offstr)
+	    inform (loc, "at offset %s into destination object %qE of size %s",
+		    offstr, ref, sizestr);
 	  else
-	    inform (loc, "at offset [%wi, %wi] into destination object %qE",
-		    minoff, maxoff, ref.ref);
+	    inform (loc, "destination object %qE of size %s", ref, sizestr);
 	  return;
 	}
 
-      if (minoff == maxoff)
-	{
-	  if (minoff == 0)
-	    inform (loc, "destination object of size %s allocated by %qE",
-		    sizestr, allocfn);
-	  else
-	    inform (loc,
-		    "at offset %wi into destination object of size %s "
-		    "allocated by %qE", minoff, sizestr, allocfn);
-	}
-      else
+      if (*offstr)
 	inform (loc,
-		"at offset [%wi, %wi] into destination object of size %s "
-		"allocated by %qE",
-		minoff, maxoff, sizestr, allocfn);
-
+		"at offset %s into destination object of size %s "
+		"allocated by %qE", offstr, sizestr, allocfn);
+      else
+	inform (loc, "destination object of size %s allocated by %qE",
+		sizestr, allocfn);
       return;
     }
 
-  if (DECL_P (ref.ref))
+  if (DECL_P (ref))
     {
-      if (minoff == maxoff)
-	{
-	  if (minoff == 0)
-	    inform (loc, "source object %qD", ref.ref);
-	  else
-	    inform (loc, "at offset %wi into source object %qD",
-		    minoff, ref.ref);
-	}
+      if (*offstr)
+	inform (loc, "at offset %s into source object %qD of size %s",
+		offstr, ref, sizestr);
       else
-	inform (loc, "at offset [%wi, %wi] into source object %qD",
-		minoff, maxoff, ref.ref);
+	inform (loc, "source object %qD of size %s", ref,  sizestr);
+
       return;
     }
 
-  if (minoff == maxoff)
-    {
-      if (minoff == 0)
-	inform (loc, "source object of size %s allocated by %qE",
-		sizestr, allocfn);
-      else
-	inform (loc,
-		"at offset %wi into source object of size %s "
-		"allocated by %qE", minoff, sizestr, allocfn);
-    }
-  else
+  if (*offstr)
     inform (loc,
-	    "at offset [%wi, %wi] into source object of size %s "
-	    "allocated by %qE",
-	    minoff, maxoff, sizestr, allocfn);
+	    "at offset %s into source object of size %s allocated by %qE",
+	    offstr, sizestr, allocfn);
+  else
+    inform (loc, "source object of size %s allocated by %qE",
+	    sizestr, allocfn);
 }
 
 /* Helper to set RANGE to the range of BOUND if it's nonnull, bounded
@@ -4232,17 +4533,18 @@ check_access (tree exp, tree dstwrite,
 		= mode == access_read_only || mode == access_read_write;
 	      const bool write
 		= mode == access_write_only || mode == access_read_write;
+	      const bool maybe = pad && pad->dst.parmarray;
 	      warned = warn_for_access (loc, func, exp,
 					OPT_Wstringop_overflow_,
 					range, dstsize,
-					write, read && !builtin);
+					write, read && !builtin, maybe);
 	    }
 
 	  if (warned)
 	    {
 	      TREE_NO_WARNING (exp) = true;
 	      if (pad)
-		inform_access (pad->dst, pad->mode);
+		pad->dst.inform_access (pad->mode);
 	    }
 
 	  /* Return error when an overflow has been detected.  */
@@ -4325,12 +4627,13 @@ check_access (tree exp, tree dstwrite,
 
       const bool read
 	= mode == access_read_only || mode == access_read_write;
+      const bool maybe = pad && pad->dst.parmarray;
       if (warn_for_access (loc, func, exp, OPT_Wstringop_overread, range,
-			   slen, false, read))
+			   slen, false, read, maybe))
 	{
 	  TREE_NO_WARNING (exp) = true;
 	  if (pad)
-	    inform_access (pad->src, access_read_only);
+	    pad->src.inform_access (access_read_only);
 	}
       return false;
     }
@@ -4462,11 +4765,12 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
 /* For an access to an object referenced to by the function parameter PTR
    of pointer type, and set RNG[] to the range of sizes of the object
    obtainedfrom the attribute access specification for the current function.
+   Set STATIC_ARRAY if the array parameter has been declared [static].
    Return the function parameter on success and null otherwise.  */
 
 tree
 gimple_parm_array_size (tree ptr, wide_int rng[2],
-			range_query * /* = NULL */)
+			bool *static_array /* = NULL */)
 {
   /* For a function argument try to determine the byte size of the array
      from the current function declaratation (e.g., attribute access or
@@ -4498,6 +4802,9 @@ gimple_parm_array_size (tree ptr, wide_int rng[2],
   if (warn_array_parameter < 2 && !access->static_p)
     return NULL_TREE;
 
+  if (static_array)
+    *static_array = access->static_p;
+
   rng[0] = wi::zero (prec);
   rng[1] = wi::uhwi (access->minsize, prec);
   /* Multiply the array bound encoded in the attribute by the size
@@ -4645,6 +4952,84 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2],
   return NULL_TREE;
 }
 
+/* A helper of compute_objsize() to determine the size from an assignment
+   statement STMT with the RHS of either MIN_EXPR or MAX_EXPR.  */
+
+static bool
+handle_min_max_size (gimple *stmt, int ostype, access_ref *pref,
+		     bitmap *visited, range_query *rvals)
+{
+  tree_code code = gimple_assign_rhs_code (stmt);
+
+  tree ptr = gimple_assign_rhs1 (stmt);
+
+  /* In a valid MAX_/MIN_EXPR both operands must refer to the same array.
+     Determine the size/offset of each and use the one with more or less
+     space remaining, respectively.  If either fails, use the information
+     determined from the other instead, adjusted up or down as appropriate
+     for the expression.  */
+  access_ref aref[2] = { *pref, *pref };
+  if (!compute_objsize (ptr, ostype, &aref[0], visited, rvals))
+    {
+      aref[0].base0 = false;
+      aref[0].offrng[0] = aref[0].offrng[1] = 0;
+      aref[0].add_max_offset ();
+      aref[0].set_max_size_range ();
+    }
+
+  ptr = gimple_assign_rhs2 (stmt);
+  if (!compute_objsize (ptr, ostype, &aref[1], visited, rvals))
+    {
+      aref[1].base0 = false;
+      aref[1].offrng[0] = aref[1].offrng[1] = 0;
+      aref[1].add_max_offset ();
+      aref[1].set_max_size_range ();
+    }
+
+  if (!aref[0].ref && !aref[1].ref)
+    /* Fail if the identity of neither argument could be determined.  */
+    return false;
+
+  bool i0 = false;
+  if (aref[0].ref && aref[0].base0)
+    {
+      if (aref[1].ref && aref[1].base0)
+	{
+	  /* If the object referenced by both arguments has been determined
+	     set *PREF to the one with more or less space remainng, whichever
+	     is appopriate for CODE.
+	     TODO: Indicate when the objects are distinct so it can be
+	     diagnosed.  */
+	  i0 = code == MAX_EXPR;
+	  const bool i1 = !i0;
+
+	  if (aref[i0].size_remaining () < aref[i1].size_remaining ())
+	    *pref = aref[i1];
+	  else
+	    *pref = aref[i0];
+	  return true;
+	}
+
+      /* If only the object referenced by one of the arguments could be
+	 determined, use it and...  */
+      *pref = aref[0];
+      i0 = true;
+    }
+  else
+    *pref = aref[1];
+
+  const bool i1 = !i0;
+  /* ...see if the offset obtained from the other pointer can be used
+     to tighten up the bound on the offset obtained from the first.  */
+  if ((code == MAX_EXPR && aref[i1].offrng[1] < aref[i0].offrng[0])
+      || (code == MIN_EXPR && aref[i0].offrng[0] < aref[i1].offrng[1]))
+    {
+      pref->offrng[0] = aref[i0].offrng[0];
+      pref->offrng[1] = aref[i0].offrng[1];
+    }
+  return true;
+}
+
 /* Helper to compute the size of the object referenced by the PTR
    expression which must have pointer type, using Object Size type
    OSTYPE (only the least significant 2 bits are used).
@@ -4709,6 +5094,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
   if (code == COMPONENT_REF)
     {
       tree ref = TREE_OPERAND (ptr, 0);
+      if (TREE_CODE (TREE_TYPE (ref)) == UNION_TYPE)
+	/* In accesses through union types consider the entire unions
+	   rather than just their members.  */
+	ostype = 0;
       tree field = TREE_OPERAND (ptr, 1);
 
       if (ostype == 0)
@@ -4963,8 +5352,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 	     of the array from the current function declaratation
 	     (e.g., attribute access or related).  */
 	  wide_int wr[2];
-	  if (tree ref = gimple_parm_array_size (ptr, wr, rvals))
+	  bool static_array = false;
+	  if (tree ref = gimple_parm_array_size (ptr, wr, &static_array))
 	    {
+	      pref->parmarray = !static_array;
 	      pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
 	      pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
 	      pref->ref = ref;
@@ -4974,14 +5365,19 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 	  pref->set_max_size_range ();
 	  pref->base0 = false;
 	  pref->ref = ptr;
-	  if (tree var = SSA_NAME_VAR (ptr))
-	    if (TREE_CODE (var) == PARM_DECL)
-	      pref->ref = var;
-
 	  return true;
 	}
 
-      /* TODO: Handle PHI.  */
+      if (gimple_code (stmt) == GIMPLE_PHI)
+	{
+	  pref->ref = ptr;
+	  access_ref phi_ref = *pref;
+	  if (!pref->get_ref (NULL, &phi_ref, ostype, visited, rvals))
+	    return false;
+	  *pref = phi_ref;
+	  pref->ref = ptr;
+	  return true;
+	}
 
       if (!is_gimple_assign (stmt))
 	{
@@ -4991,16 +5387,16 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 	     PREF->REF to it.  */
 	  pref->base0 = false;
 	  pref->set_max_size_range ();
-	  if (tree var = SSA_NAME_VAR (ptr))
-	    if (TREE_CODE (var) == PARM_DECL)
-	      pref->ref = var;
 	  return true;
 	}
 
-      ptr = gimple_assign_rhs1 (stmt);
-
       tree_code code = gimple_assign_rhs_code (stmt);
 
+      if (code == MAX_EXPR || code == MIN_EXPR)
+	return handle_min_max_size (stmt, ostype, pref, visited, rvals);
+
+      ptr = gimple_assign_rhs1 (stmt);
+
       if (code == POINTER_PLUS_EXPR
 	  && TREE_CODE (TREE_TYPE (ptr)) == POINTER_TYPE)
 	{
diff --git a/gcc/builtins.h b/gcc/builtins.h
index c09f36da02b..4a2e34351be 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -153,6 +153,8 @@ extern void warn_string_no_nul (location_t, tree, const char *, tree,
 extern tree unterminated_array (tree, tree * = NULL, bool * = NULL);
 extern bool builtin_with_linkage_p (tree);
 
+class range_query;
+
 /* Describes a reference to an object used in an access.  */
 struct access_ref
 {
@@ -162,17 +164,12 @@ struct access_ref
      is a constant zero.  */
   access_ref (tree = NULL_TREE, bool = false);
 
-  /* Reference to the accessed object(s).  */
-  tree ref;
+  /* Return the PHI node REF refers to or null if it doesn't.  */
+  gphi *phi () const;
 
-  /* Range of byte offsets into and sizes of the object(s).  */
-  offset_int offrng[2];
-  offset_int sizrng[2];
-  /* Range of the bound of the access: denotes that the access
-     is at least BNDRNG[0] bytes but no more than BNDRNG[1].
-     For string functions the size of the actual access is
-     further constrained by the length of the string.  */
-  offset_int bndrng[2];
+  /* Return the object to which REF refers.  */
+  tree get_ref (vec<access_ref> *, access_ref * = NULL, int = 1,
+		bitmap * = NULL, range_query * = NULL) const;
 
   /* Return true if OFFRNG is the constant zero.  */
   bool offset_zero () const
@@ -211,6 +208,22 @@ struct access_ref
     add_offset (-maxoff - 1, maxoff);
   }
 
+  /* Issue an informational message describing the target of an access
+     with the given mode.  */
+  void inform_access (access_mode) const;
+
+  /* Reference to the accessed object(s).  */
+  tree ref;
+
+  /* Range of byte offsets into and sizes of the object(s).  */
+  offset_int offrng[2];
+  offset_int sizrng[2];
+  /* Range of the bound of the access: denotes that the access
+     is at least BNDRNG[0] bytes but no more than BNDRNG[1].
+     For string functions the size of the actual access is
+     further constrained by the length of the string.  */
+  offset_int bndrng[2];
+
   /* Used to fold integer expressions when called from front ends.  */
   tree (*eval)(tree);
   /* Set if trailing one-element arrays should be treated as flexible
@@ -219,6 +232,9 @@ struct access_ref
   /* Set if valid offsets must start at zero (for declared and allocated
      objects but not for others referenced by pointers).  */
   bool base0;
+  /* Set if REF refers to a function array parameter not declared
+     static.  */
+  bool parmarray;
 };
 
 /* Describes a pair of references used in an access by built-in
@@ -242,10 +258,9 @@ struct access_data
   access_mode mode;
 };
 
-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], range_query * = NULL);
+extern tree gimple_parm_array_size (tree, wide_int[2], bool * = NULL);
 extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
 extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
 			     range_query * = NULL);
diff --git a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
index 7c7932e3cf0..676643168cc 100644
--- a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
+++ b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
@@ -10,7 +10,7 @@ void sink (void*);
 struct Ax
 {
   char n;
-  char a[];                     // { dg-message "declared here" }
+  char a[];                     // { dg-message "destination object" "note" }
 };
 
 // Verify warning for a definition with no initializer.
@@ -91,7 +91,7 @@ void gaxx (void)
 struct A0
 {
   char n;
-  char a[0];                    // { dg-message "declared here" }
+  char a[0];                    // { dg-message "destination object" "note" }
 };
 
 // Verify warning for a definition with no initializer.
@@ -158,7 +158,7 @@ void ga0x (void)
 struct A1
 {
   char n;
-  char a[1];                    // { dg-message "declared here" }
+  char a[1];                    // { dg-message "destination object" "note" }
 };
 
 // Verify warning for a definition with no initializer.
@@ -256,7 +256,7 @@ void ga1x (void)
 struct A1i
 {
   char n;
-  char a[1];                    // { dg-message "declared here" }
+  char a[1];                    // { dg-message "destination object" }
   char x;
 };
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-11.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-11.c
index f5dac458d1e..ec3c97e8102 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-11.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-11.c
@@ -72,7 +72,7 @@ void test_memset_array_range_cst_off (void)
 {
   T (SR (-7, 7), 1, 7);
   T (SR (-1, 1), 1, 7);
-  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
   T (SR ( 1, 2), 1, 1);
   T (SR ( 1, 2), 1, 5);
 
@@ -147,7 +147,7 @@ void test_memcpy_array_range_cst_off (const void *s)
 {
   T (SR (-7, 7), 1, 7);
   T (SR (-1, 1), 1, 7);
-  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
   T (SR ( 1, 2), 1, 1);
   T (SR ( 1, 2), 1, 5);
 
@@ -224,7 +224,7 @@ void test_strcpy_array_range_cst_off (const char *s)
 {
   T (SR (-7, 7), 1, 6);
   T (SR (-1, 1), 1, 6);
-  T (SR (-1, 1), 1, 8);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR (-1, 1), 1, 8);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
   T (SR ( 1, 2), 1, 0);
   T (SR ( 1, 2), 1, 1);
   T (SR ( 1, 2), 1, 4);
@@ -290,7 +290,7 @@ void test_strncpy_array_range_cst_off (const char *s)
 {
   T (SR (-7, 7), 1, 7);
   T (SR (-1, 1), 1, 7);
-  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" { xfail *-*-*} } */
+  T (SR (-1, 1), 1, 9);       /* { dg-warning "\\\[-Warray-bounds|-Wstringop-overflow" "pr89428" } */
   T (SR ( 1, 2), 1, 1);
   T (SR ( 1, 2), 1, 5);
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c
index 1e67b5fd928..7c3dc8c0544 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-12.c
@@ -25,7 +25,9 @@ void test_memcpy_array_cst_range_off (const void *s)
   T (d + UR (1, 2), 5);
 
   T (d + UR (0, 1), 6);
-  T (d + UR (0, 1), 7);       /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" "pr89428" { xfail *-*-* } } */
+  /* The warning below should be "writing" but the [0, 1] range
+     is somehow lost and get_range_info() returns VR_VARYING.  */
+  T (d + UR (0, 1), 7);       /* { dg-warning ".memcpy. writing 7 bytes into a region of size 6 overflows the destination" "pr89428" { xfail *-*-* } } */
   T (d + UR (1, 2), 6);       /* { dg-warning ".memcpy. writing 6 bytes into a region of size 5 overflows the destination" } */
   T (d + UR (1, 2), 7);       /* { dg-warning "writing 7 bytes into a region of size 5 " } */
 
@@ -48,7 +50,8 @@ void test_memcpy_array_range_range_off (const void *s)
   char *d = ga7 + UR (0, 1);
   T (d + SR (-1, 0), 1);
   T (d + SR (-1, 0), 7);
-  T (d + SR (-1, 0), 9);       /* { dg-warning "writing 1 byte into a region of size 0 " "pr89350" { xfail *-*-* } } */
+  T (d + SR (-1, 0), 8);       /* { dg-warning "writing 8 bytes into a region of size 7 " } */
+  T (d + SR (-1, 0), 9);       /* { dg-warning "writing 9 bytes into a region of size 7 " "pr89350" } */
 }
 
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-17.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-17.c
index fb81420c933..9c05d04f90c 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-17.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-17.c
@@ -13,7 +13,7 @@ void sink (void*);
 
 void call_copy_n (const char *s)
 {
-  char a[7];        // { dg-message "declared here" }
+  char a[7];        // { dg-message "at offset 7 into destination object 'a'" }
   copy_n (a, "1234567", 7);
   sink (a);
 }
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
index 37c1ca29713..607c27989a3 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-27.c
@@ -261,8 +261,7 @@ void test_strcpy_warn (const char *s)
        that the conversion from signed int to size_t doesn't prevent
        the detection.  */
     int n = strlen (a);
-    char *t = (char*)calloc (n, 1);     // { dg-message "at offset 0 to an object with size 3 allocated by 'calloc' here" "calloc note 1" { xfail *-*-* } }
-                                        // { dg-message "at offset 0 to an object with size at most 3 allocated by 'calloc' here" "calloc note 2" { target *-*-* } .-1 }
+    char *t = (char*)calloc (n, 1);     // { dg-message "destination object of size 3 allocated by 'calloc'" "note" }
     strcpy (t, a);                      // { dg-warning "writing 4 bytes into a region of size (between 0 and )?3 " }
 
     sink (t);
@@ -271,8 +270,7 @@ void test_strcpy_warn (const char *s)
   {
     const char a[] = "1234";
     size_t n = strlen (a);
-    char *t = (char*)malloc (n);        // { dg-message "at offset 0 to an object with size 4 allocated by 'malloc' here" "malloc note 1" { xfail *-*-* } }
-                                        // { dg-message "at offset 0 to an object with size at most 4 allocated by 'malloc' here" "malloc note 2" { target *-*-* } .-1 }
+    char *t = (char*)malloc (n);        // { dg-message "destination object of size 4 allocated by 'malloc'" "note" }
     strcpy (t, a);                      // { dg-warning "writing 5 bytes into a region of size (between 0 and )?4 " }
     sink (t);
   }
@@ -280,14 +278,14 @@ void test_strcpy_warn (const char *s)
   // Exercise PR middle-end/85484.
   {
     size_t len = strlen (s);
-    char vla[len];                      // { dg-message "at offset 0 to an object declared here" "vla note" }
+    char vla[len];                      // { dg-message "destination object 'vla'" "vla note" }
     strcpy (vla, s);                    // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
     sink (vla);
   }
 
   {
     size_t n = strlen (s);
-    char *t = (char*)malloc (n);        // { dg-message "at offset 0 to an object allocated by 'malloc' here" "malloc note" }
+    char *t = (char*)malloc (n);        // { dg-message "allocated by 'malloc'" "malloc note" }
     strcpy (t, s);                      // { dg-warning "writing one too many bytes into a region of a size that depends on 'strlen'" }
     sink (t);
   }
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c
index be7f51ad3a5..5009fb5763a 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-28.c
@@ -40,27 +40,27 @@ void same_size_and_offset_idx_cst (void)
     const size_t n = UR (2, 3);
 
     T (n, n, -4);   // { dg-warning "writing 1 byte into a region of size 0" }
-                    // { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+                    // { dg-message "at offset \\\[-2, -1] into destination object of size \\\[2, 3] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
     T (n, n, -3);
     T (n, n, -2);
     T (n, n, -1);
     T (n, n,  0);
     T (n, n,  1);   // { dg-warning "writing 1 byte into a region of size 0" }
-                    // { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+                    // { dg-message "at offset 3 into destination object of size \\\[2, 3] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
   }
 
   {
     const size_t n = UR (3, 4);
 
     T (n, n, -5);   // { dg-warning "writing 1 byte into a region of size 0" }
-                    // { dg-message "at offset \\\[-2, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+                    // { dg-message "at offset \\\[-2, -1] into destination object of size \\\[3, 4] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
     T (n, n, -4);
     T (n, n, -3);
     T (n, n, -2);
     T (n, n, -1);
     T (n, n,  0);
     T (n, n,  1);   // { dg-warning "writing 1 byte into a region of size 0" }
-                    // { dg-message "at offset \\\[4, 5] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+                    // { dg-message "at offset 4 into destination object of size \\\[3, 4] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
   }
 
   {
@@ -84,15 +84,15 @@ void different_size_and_offset_idx_cst (void)
     const size_t i = UR (1, 2);
 
     T (n, i, -4);   // { dg-warning "writing 1 byte into a region of size 0" }
-                    // { dg-message "at offset \\\[-3, -2] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+                    // { dg-message "at offset \\\[-3, -2] into destination object of size \\\[2, 3] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
     T (n, i, -3);   // { dg-warning "writing 1 byte into a region of size 0" }
-                    // { dg-message "at offset \\\[-2, -1] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+                    // { dg-message "at offset \\\[-2, -1] into destination object of size \\\[2, 3] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
     T (n, i, -2);
     T (n, i, -1);
     T (n, i,  0);
     T (n, i,  1);
     T (n, i,  2);   // { dg-warning "writing 1 byte into a region of size 0" }
-                    // { dg-message "at offset \\\[3, 4] to an object with size between 2 and 3 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+                    // { dg-message "at offset 3 into destination object of size \\\[2, 3] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
   }
 
   {
@@ -100,20 +100,20 @@ void different_size_and_offset_idx_cst (void)
     const size_t i = UR (2, 5);
 
     T (n, i, -6);   // { dg-warning "writing 1 byte into a region of size 0" }
-                    // { dg-message "at offset \\\[-4, -1] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+                    // { dg-message "at offset \\\[-4, -2] into destination object of size \\\[3, 4] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
 
-    /* The offsets -5 and -4 are both necessarily invalid even if the sum
-       (i - 5) and (i - 4) are (or could be) in bounds because they imply
-       that the intermediate offset (p + i) is out of bounds.  */
-    T (n, i, -5);   // { dg-warning "" "intermediate offset" { xfail *-*-* } }
-    T (n, i, -4);   // { dg-warning "" "intermediate offset" { xfail *-*-* } }
+    /* The offset -5 is necessarily invalid even if the sum (i - 5) is (or
+       could be) in bounds because it implies that the intermediate offset
+       (p + i) is out of bounds.  */
+    T (n, i, -5);   // { dg-warning "writing 1 byte into a region of size 0 " }
+    T (n, i, -4);
     T (n, i, -3);
     T (n, i, -2);
     T (n, i, -1);
     T (n, i,  0);
     T (n, i,  1);
     T (n, i,  2);   // { dg-warning "writing 1 byte into a region of size 0" }
-                    // { dg-message "at offset \\\[4, 7] to an object with size between 3 and 4 allocated by 'alloc1'" "note" { target *-*-* } .-1 }
+                    // { dg-message "at offset 4 into destination object of size \\\[3, 4] allocated by 'alloc1'" "note" { target *-*-* } .-1 }
   }
 }
 
@@ -133,11 +133,8 @@ void different_size_and_offset_idx_var (void)
     T (n, i, SR (       0, 1));
     T (n, i, SR (       1, 2));
     T (n, i, SR (       2, 3));
-    /* The warning is issued below but the offset and the size in
-       the note are wrong.  See the FIXME in compute_objsize().  */
     T (n, i, SR (       3, 4));    // { dg-warning "\\\[-Wstringop-overflow" }
-                                   // { dg-message "at offset 4 to an object with size between 3 and 4 allocated by 'alloc1'" "pr92940 note: offset addition" { xfail *-*-* } .-1 }
-                                   // { dg-message "at offset . to an object with size . allocated by 'alloc1'" "note: offset addition" { target *-*-* } .-2 }
+                                   // { dg-message "at offset 4 into destination object of size \\\[3, 4] allocated by 'alloc1'" "pr92940 note: offset addition" { target *-*-* } .-1 }
  }
 }
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-29.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-29.c
index c011d05e89f..f13abbd7ca0 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-29.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-29.c
@@ -11,7 +11,7 @@ void sink (void*);
 
 void direct_call (void)
 {
-  char *q = allocfn (0);            // { dg-message "at offset 0 to an object with size 0 allocated by 'allocfn'" }
+  char *q = allocfn (0);            // { dg-message "object of size 0 allocated by 'allocfn'" "note" }
   q[0] = 0;                         // { dg-warning "\\\[-Wstringop-overflow" }
   sink (q);
 }
@@ -20,7 +20,7 @@ void direct_call (void)
 void local_ptr_call (void)
 {
   allocfn_t *ptr = allocfn;
-  char *q = ptr (1);                // { dg-message "at offset -1 to an object with size 1 allocated by 'allocfn'" }
+  char *q = ptr (1);                // { dg-message "at offset -1 into destination object of size 1 allocated by 'allocfn'" "note" }
   q[0] = 0;
   q[-1] = 0;                        // { dg-warning "\\\[-Wstringop-overflow" }
   sink (q);
@@ -32,7 +32,7 @@ void global_ptr_call (void)
   extern allocfn_t *ptralloc;
 
   allocfn_t *ptr = ptralloc;
-  char *q = ptr (2);               // { dg-message "at offset 3 to an object with size 2 allocated by 'ptralloc'" }
+  char *q = ptr (2);               // { dg-message "at offset 3 into destination object of size 2 allocated by 'ptralloc'" "note" }
   q[0] = 0;
   q[1] = 1;
   q[3] = 3;                        // { dg-warning "\\\[-Wstringop-overflow" }
@@ -44,7 +44,7 @@ void global_ptr_array_call (void)
   extern allocfn_t * (arralloc[]);
 
   allocfn_t *ptr = arralloc[0];
-  char *q = ptr (2);               // { dg-message "at offset 3 to an object with size 2 allocated by 'ptr'" }
+  char *q = ptr (2);               // { dg-message "at offset 3 into destination object of size 2 allocated by 'ptr'" "note" }
   q[0] = 1;
   q[1] = 2;
   q[3] = 3;                        // { dg-warning "\\\[-Wstringop-overflow" }
@@ -56,7 +56,7 @@ struct S { allocfn_t *ptralloc; };
 
 void member_ptr_call (struct S *p)
 {
-  char *q = p->ptralloc (3);       // { dg-message "at offset 5 to an object with size 3 allocated by 'ptralloc' here" }
+  char *q = p->ptralloc (3);       // { dg-message "at offset 5 into destination object of size 3 allocated by 'ptralloc'" "note" }
   q[0] = 0;
   q[1] = 1;
   q[2] = 2;
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c
index 46f8fed79f3..d9cf32d8784 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-37.c
@@ -67,7 +67,7 @@ void* warn_malloc_3_5 (const char *s, unsigned n)
 {
   if (n < 3 || 5 < n)
     n = 3;
-  char *p = (char*)malloc (n);          // { dg-message "at offset 1 into destination object of size \\\[3, 5] allocated by 'malloc'" }
+  char *p = (char*)malloc (n);          // { dg-message "at offset 1 into destination object of size \\\[3, 5] allocated by 'malloc'" "note" }
   // The size below should be a range like the one above.
   strncpy (p + 1, s, 5);                // { dg-warning "writing 5 bytes into a region of size 4 " }
   return p;
@@ -89,7 +89,7 @@ void* warn_usr_alloc_3_5 (UsrAlloc *usr_alloc, const char *s, unsigned n)
 {
   if (n < 3 || 5 < n)
     n = 3;
-  char *p = (char*)usr_alloc (n, 3);    // { dg-message "at offset 1 into destination object of size \\\[9, 15] allocated by 'usr_alloc'" }
+  char *p = (char*)usr_alloc (n, 3);    // { dg-message "at offset 1 into destination object of size \\\[9, 15] allocated by 'usr_alloc'" "note" }
   // The size below should be a range like the one above.
   strncpy (p + 1, s, 15);               // { dg-warning "writing 15 bytes into a region of size 14 " }
   return p;
@@ -179,67 +179,67 @@ void test_note (const char *s)
   extern void sink (void*);
 
   {
-    char a[1][1][2];                    // { dg-message "destination object" }
+    char a[1][1][2];                    // { dg-message "destination object" "note" }
     strncpy (a[0][0], s, 3);            // { dg-warning "writing 3 bytes into a region of size 2 " }
     sink (a);
   }
 
   {
-    char a[1][1][2];                    // { dg-message "at offset 2 into " }
+    char a[1][1][2];                    // { dg-message "at offset 2 into " "note" }
     strncpy (a[0][1], s, 3);            // { dg-warning "writing 3 bytes into a region of size 0 " }
     sink (a);
   }
 
   {
-    char a[1][2][2];                    // { dg-message "destination object" }
+    char a[1][2][2];                    // { dg-message "destination object" "note" }
     strncpy (a[0][0], s, 3);            // { dg-warning "writing 3 bytes into a region of size 2 " }
     sink (a);
   }
 
   {
-    char a[1][2][2];                    // { dg-message "at offset 2 into " }
+    char a[1][2][2];                    // { dg-message "at offset 2 into " "note" }
     strncpy (a[0][1], s, 3);            // { dg-warning "writing 3 bytes into a region of size 2 " }
     sink (a);
   }
 
   {
-    char a[1][2][2];                    // { dg-message "at offset 4 into " }
+    char a[1][2][2];                    // { dg-message "at offset 4 into " "note" }
     strncpy (a[1][0], s, 3);            // { dg-warning "writing 3 bytes into a region of size 0 " }
     sink (a);
   }
 
   {
-    char a[2][1][2];                    // { dg-message "at offset 2 into " }
+    char a[2][1][2];                    // { dg-message "at offset 2 into " "note" }
     strncpy (a[0][1], s, 3);            // { dg-warning "writing 3 bytes into a region of size 0 " }
     sink (a);
   }
 
   {
-    char a[2][1][2];                    // { dg-message "at offset 2 into " }
+    char a[2][1][2];                    // { dg-message "at offset 2 into " "note" }
     strncpy (a[1][0], s, 3);            // { dg-warning "writing 3 bytes into a region of size 2 " }
     sink (a);
   }
 
   {
-    char a[2][2][3];                    // { dg-message "at offset 9 into " }
+    char a[2][2][3];                    // { dg-message "at offset 9 into " "note" }
     strncpy (a[1][1], s, 4);            // { dg-warning "writing 4 bytes into a region of size 3 " }
     sink (a);
   }
 
   {
-    char a[2][3][3];                    // { dg-message "at offset 12 into " }
+    char a[2][3][3];                    // { dg-message "at offset 12 into " "note" }
     strncpy (a[1][1], s, 5);            // { dg-warning "writing 5 bytes into a region of size 3 " }
     sink (a);
   }
 
   {
-    char a[2][3][3];                    // { dg-message "at offset 12 into " }
+    char a[2][3][3];                    // { dg-message "at offset 12 into " "note" }
     strncpy (a[1][1], s, 6);            // { dg-warning "writing 6 bytes into a region of size 3 " }
     sink (a);
   }
 
   {
-    char a[2][3][3];                    // { dg-message "at offset 15 into " }
+    char a[2][3][3];                    // { dg-message "at offset 15 into " "note" }
     strncpy (a[1][2], s, 7);            // { dg-warning "writing 7 bytes into a region of size 3 " }
     sink (a);
   }
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c
index a4d78b21cd1..b126fcbdcae 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c
@@ -53,7 +53,7 @@ void nowarn_memchr_anti_range_memset_cst (const void *s, size_t n)
 
 void warn_memchr_cst_memset_cst (const void *s)
 {
-  char *p = malloc (4);                 // { dg-message "at offset \\\[0, 4] into destination object of size 4 " "note" }
+  char *p = malloc (4);                 // { dg-message "destination object of size 4 " "note" }
   sink (p);
 
   p = memchr (p, '1', 4);
@@ -62,7 +62,7 @@ void warn_memchr_cst_memset_cst (const void *s)
 
 void warn_memchr_var_memset_cst (const void *s, unsigned n)
 {
-  char *p = malloc (4);                 // { dg-message "at offset \\\[0, 4] into destination object of size 4 " "note" }
+  char *p = malloc (4);                 // { dg-message "destination object of size 4 " "note" }
   sink (p);
 
   p = memchr (p, '1', n);
@@ -79,9 +79,9 @@ void warn_memchr_var_memset_range (const void *s, unsigned n)
      as in the first two notes.  The exact value probably isn't too
      important. */
   char *p0 = malloc (UR (5, 7));
-  // { dg-message "at offset \\\[0, 7] into destination object of size \\\[5, 7]" "note" { target *-*-* } .-1 }
-  // { dg-message "at offset \\\[1, 7] into destination object of size \\\[5, 7]" "note"  { target *-*-* } .-2 }
-  // { dg-message "at offset \\\[2, 7] into destination object of size \\\[5, 7]" "note"  { target *-*-* } .-3 }
+  // { dg-message ": destination object of size \\\[5, 7]" "note 1" { target *-*-* } .-1 }
+  // { dg-message "at offset \\\[1, 7] into destination object of size \\\[5, 7]" "note 2"  { target *-*-* } .-2 }
+  // { dg-message "at offset \\\[2, 7] into destination object of size \\\[5, 7]" "note 3"  { target *-*-* } .-3 }
 
   sink (p0);
   char *p1 = memchr (p0, '1', n);
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c
index 02b14ee2eda..cb2c329aa84 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c
@@ -26,7 +26,7 @@ void nowarn_c32 (char c)
 
 void warn_c32 (char c)
 {
-  extern char warn_a32[32];   // { dg-message "at offset 32 to object 'warn_a32' with size 32" }
+  extern char warn_a32[32];   // { dg-message "at offset 32 into destination object 'warn_a32' of size 32" "note" }
 
   void *p = warn_a32 + 1;
   *(C32*)p = (C32){ c };      // { dg-warning "writing 1 byte into a region of size 0" }
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-50.s b/gcc/testsuite/gcc.dg/Wstringop-overflow-50.s
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c
index 26568f8366d..f5929c9e7d6 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c
@@ -15,7 +15,7 @@ void sink (void*);
 void char_flexarray_cst_off_cst_size (void)
 {
   extern struct { char n, a[]; }
-    caxcc;                              // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'caxcc'" }
+    caxcc;                              // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'caxcc'" "note" }
 
   char *p = caxcc.a;
   size_t idx = DIFF_MAX - 4;
@@ -38,7 +38,7 @@ void char_flexarray_cst_off_cst_size (void)
 void char_flexarray_var_off_cst_size (ptrdiff_t idx)
 {
   extern struct { char n, a[]; }
-    caxvc;                              // { dg-message "destination object 'caxvc'" }
+    caxvc;                              // { dg-message "destination object 'caxvc'" "note" }
 
   char *p = caxvc.a;
 
@@ -55,7 +55,7 @@ void char_flexarray_var_off_cst_size (ptrdiff_t idx)
 void char_flexarray_var_off_var_size (size_t n, ptrdiff_t idx)
 {
   extern struct { char n, a[]; }
-    caxvv;                              // { dg-message "destination object 'caxvv'" }
+    caxvv;                              // { dg-message "destination object 'caxvv'" "note" }
 
   char *p = caxvv.a;
 
@@ -76,7 +76,7 @@ void char_flexarray_var_off_var_size (size_t n, ptrdiff_t idx)
 void alloc_array_var_off_cst_size (size_t n, ptrdiff_t idx)
 {
   struct { char n, a[]; }
-    *p = __builtin_malloc (n);          // { dg-message "at offset \\d+ into destination object" }
+    *p = __builtin_malloc (n);          // { dg-message "at offset \\d+ into destination object" "note" }
 
   if (idx < DIFF_MAX - 4)
     idx = DIFF_MAX - 4;
@@ -91,7 +91,7 @@ void alloc_array_var_off_cst_size (size_t n, ptrdiff_t idx)
 void int_array_cst_off_cst_size (void)
 {
   extern struct { int n, a[]; }
-    iaxc;                               // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'iaxc'" }
+    iaxc;                               // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'iaxc'" "note" }
 
   int *p = iaxc.a;
   size_t idx = DIFF_MAX / sizeof *p - 1;
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c
new file mode 100644
index 00000000000..b81186cfb94
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-58.c
@@ -0,0 +1,260 @@
+/* PR middle-end/92936 - missing warning on a past-the-end store to a PHI
+   Exercise warnings for writing into one of two or more declared objects.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX __INT_MAX__
+
+extern void* memset (void*, int, size_t);
+#define memset(d, c, n) sink (memset (d, c, n))
+
+void sink (int, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+volatile int cond1, cond2;
+
+extern char ca0[0], ca1[1], ca2[2], ca3[3], ca4[4],
+            ca5[5], ca6[6], ca7[7], ca8[8], ca9[9], cax[];
+
+#define CHOOSE_DECL_2(n1, n2)			\
+  (cond1 ? ca ## n1 : ca ## n2)
+#define CHOOSE_DECL_3(n1, n2, n3)			\
+  (cond1 < 0 ? ca ## n1 : 0 < cond1 ? ca ## n2 : ca ## n3)
+
+
+void memset_decl_2 (void)
+{
+  {
+    char *p0_1 = CHOOSE_DECL_2 (0, 1);
+
+    memset (p0_1, 0, 0);
+    /* Writing more than the smallest destination should trigger a "may
+       write" warning if the access is unconditionally reachable from
+       the block where the pointer to either object is assigned.  */
+    memset (p0_1, 0, 1);
+    memset (p0_1, 0, 2);      // { dg-warning "memset' writing 2 bytes into a region of size 1 " }
+    memset (p0_1, 0, 9);      // { dg-warning "memset' writing 9 bytes into a region of size 1 " }
+  }
+
+  {
+    char *p0_x = CHOOSE_DECL_2 (0, x);
+
+    memset (p0_x, 0, 0);
+    memset (p0_x, 0, 1);
+    memset (p0_x, 0, 2);
+    memset (p0_x, 0, 9);
+  }
+
+  {
+    char *p3_5 = CHOOSE_DECL_2 (3, 5);
+
+    memset (p3_5, 0, 1);
+    memset (p3_5, 0, 3);
+    memset (p3_5, 0, 4);
+    memset (p3_5, 0, 5);
+    memset (p3_5, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    char *p5_3 = CHOOSE_DECL_2 (5, 3);
+
+    memset (p5_3, 0, 3);
+    memset (p5_3, 0, 4);
+    memset (p5_3, 0, 5);
+    memset (p5_3, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    char *px_3 = CHOOSE_DECL_2 (x, 3);
+
+    memset (px_3, 0, 1);
+    memset (px_3, 0, 3);
+    memset (px_3, 0, 4);
+    memset (px_3, 0, 1234);
+  }
+
+  {
+    char *p5_x = CHOOSE_DECL_2 (5, x);
+
+    memset (p5_x, 0, 1);
+    memset (p5_x, 0, 5);
+    memset (p5_x, 0, 6);
+    memset (p5_x, 0, 1234);
+  }
+
+}
+
+
+void memset_decl_3 (void)
+{
+  {
+    char *p0_1_2 = CHOOSE_DECL_3 (0, 1, 2);
+    memset (p0_1_2, 0, 0);
+    memset (p0_1_2, 0, 1);
+    memset (p0_1_2, 0, 2);
+    memset (p0_1_2, 0, 3);    // { dg-warning "memset' writing 3 bytes into a region of size 2 " }
+    memset (p0_1_2, 0, 9);    // { dg-warning "memset' writing 9 bytes into a region of size 2 " }
+  }
+
+  {
+    char *p0_2_x = CHOOSE_DECL_3 (0, 2, x);
+
+    memset (p0_2_x, 0, 0);
+    memset (p0_2_x, 0, 1);
+    memset (p0_2_x, 0, 3);
+    memset (p0_2_x, 0, 9);
+  }
+
+  {
+    char *p3_4_5 = CHOOSE_DECL_3 (3, 4, 5);
+
+    memset (p3_4_5, 0, 3);
+    memset (p3_4_5, 0, 4);
+    memset (p3_4_5, 0, 5);
+    memset (p3_4_5, 0, 6);    // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    char *p5_3_4 = CHOOSE_DECL_3 (5, 3, 4);
+
+    memset (p5_3_4, 0, 3);
+    memset (p5_3_4, 0, 4);
+    memset (p5_3_4, 0, 5);
+    memset (p5_3_4, 0, 6);    // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    char *p9_8_7 = CHOOSE_DECL_3 (9, 8, 7);
+
+    memset (p9_8_7, 0, 7);
+    memset (p9_8_7, 0, 8);
+    memset (p9_8_7, 0, 9);
+    memset (p9_8_7, 0, 10);   // { dg-warning "memset' writing 10 bytes into a region of size 9 " }
+  }
+}
+
+
+/* Verify conditionally writing into one of two objects with the same
+   size.  */
+
+void memset_decl_2_same_size (int i)
+{
+  {
+    char a4_1[4], a4_2[4];
+    char *p4 = cond1 ? a4_1 : a4_2;
+
+    memset (p4, 0, 1);
+    memset (p4, 0, 2);
+    memset (p4, 0, 3);
+    memset (p4, 0, 4);
+    memset (p4, 0, 5);        // { dg-warning "memset' writing 5 bytes into a region of size 4" }
+  }
+
+  {
+    char a4_1[4];             // { dg-message "destination object 'a4_1" "note" }
+    char a4_2[4];             // { dg-message "destination object 'a4_2" "note" }
+    char *p4 = cond1 ? a4_1 : a4_2;
+    char *p4_i = p4 + i;
+
+    memset (p4_i, 0, 5);      // { dg-warning "memset' writing 5 bytes into a region of size 4" }
+  }
+
+  {
+    if (i < 1)
+      i = 1;
+
+    char a4_1[4];             // { dg-message "at offset \\\[1, 4] into destination object 'a4_1" "note" }
+    char a4_2[4];             // { dg-message "at offset \\\[1, 4] into destination object 'a4_2" "note" }
+    char *p4 = cond1 ? a4_1 : a4_2;
+    char *p4_i = p4 + i;
+
+    memset (p4_i, 0, 3);
+    memset (p4_i, 0, 4);      // { dg-warning "memset' writing 4 bytes into a region of size 3 " }
+  }
+}
+
+
+void memset_decl_2_off (void)
+{
+  int i1 = SR (1, INT_MAX);
+  int i2 = SR (2, INT_MAX);
+
+  {
+    char a5[5];               // { dg-warning "at offset [1, 5] into destination object 'a5'
+    char a7[7];               // { dg-warning "at offset [2, 7] into destination object 'a7'
+    char *p5_p1 = a5 + i1;
+    char *p7_p2 = a7 + i2;
+    char *p5_7 = cond1 ? p5_p1 : p7_p2;
+
+    memset (p5_7, 0, 1);
+    memset (p5_7, 0, 2);
+    memset (p5_7, 0, 3);
+    memset (p5_7, 0, 4);
+    memset (p5_7, 0, 5);
+    memset (p5_7, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+  }
+
+  int i3 = SR (3, INT_MAX);
+
+  {
+    char a5[5];
+    // { dg-message "at offset \\\[3, 5] into destination object 'a5'" "note" { target *-*-* } .-1 }
+    // { dg-message "at offset \\\[2, 5] into destination object 'a5'" "note" { target *-*-* } .-2 }
+    // { dg-message "at offset \\\[1, 5] into destination object 'a5'" "note" { target *-*-* } .-3 }
+    // { dg-message ": destination object 'a5'" "note" { target *-*-* } .-4 }
+    char a9[9];
+    // { dg-message "at offset \\\[4, 9] into destination object 'a9'" "note" { target *-*-* } .-1 }
+    // { dg-message "at offset \\\[3, 9] into destination object 'a9'" "note" { target *-*-* } .-2 }
+    // { dg-message "at offset \\\[2, 9] into destination object 'a9'" "note" { target *-*-* } .-3 }
+    // { dg-message ": destination object 'a9'" "note" { target *-*-* } .-4 }
+    char *p5_p2 = a5 + i2;    // 3 bytes left
+    char *p9_p3 = a9 + i3;    // 6 bytes left
+    char *p =
+      cond1 ? p5_p2 : p9_p3;  // [3 - 6] bytes left
+    char *q = p + i1;         // [2 - 5] bytes left
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 4);
+    memset (q, 0, 5);
+    memset (q, 0, 6);         // { dg-warning "memset' writing 6 bytes into a region of size 5" }
+
+    --q;                      // [3 - 6] bytes left
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 4);
+    memset (q, 0, 5);
+    memset (q, 0, 6);
+    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" }
+
+    --q;                      // [4 - 7] bytes left
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 4);
+    memset (q, 0, 5);
+    memset (q, 0, 6);
+    memset (q, 0, 7);
+    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" }
+
+    int m1_x = SR (-1, INT_MAX);
+    int m2_x = SR (-2, INT_MAX);
+
+    q += cond2 ? m1_x : m2_x;   // [5 - 9] bytes left
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 4);
+    memset (q, 0, 5);
+    memset (q, 0, 6);
+    memset (q, 0, 7);
+    memset (q, 0, 8);
+    memset (q, 0, 9);
+    memset (q, 0, 10);        // { dg-warning "memset' writing 10 bytes into a region of size 9" }
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c
new file mode 100644
index 00000000000..c45a92d21e1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-59.c
@@ -0,0 +1,267 @@
+/* PR middle-end/92936 - missing warning on a past-the-end store to a PHI
+   Exercise warnings for writing into one of two or more allocated objects.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX __INT_MAX__
+
+extern void* malloc (size_t);
+extern void* memset (void*, int, size_t);
+#define memset(d, c, n) sink (memset (d, c, n))
+
+void sink (int, ...);
+#define sink(...) sink (0, __VA_ARGS__)
+
+volatile int cond1, cond2, x;
+
+#define CHOOSE_MALLOC_2(n1, n2)			\
+  (cond1 ? malloc (n1) : malloc (n2))
+#define CHOOSE_MALLOC_3(n1, n2, n3)					\
+  (cond1 < 0 ? malloc (n1) : 0 < cond1 ? malloc (n2) : malloc (n3))
+
+
+void memset_malloc_2 (void)
+{
+  {
+    char *p0_1 = CHOOSE_MALLOC_2 (0, 1);
+
+    memset (p0_1, 0, 0);
+    /* Writing more than the smallest destination should trigger a "may
+       write" warning if the access is unconditionally reachable from
+       the block where the pointer to either object is assigned.  */
+    memset (p0_1, 0, 1);
+    memset (p0_1, 0, 2);      // { dg-warning "memset' writing 2 bytes into a region of size 1 " }
+    memset (p0_1, 0, 9);      // { dg-warning "memset' writing 9 bytes into a region of size 1 " }
+  }
+
+  {
+    char *p0_x = CHOOSE_MALLOC_2 (0, x);
+
+    memset (p0_x, 0, 0);
+    memset (p0_x, 0, 1);
+    memset (p0_x, 0, 2);
+    memset (p0_x, 0, 12345);
+  }
+
+  {
+    char *px_x = CHOOSE_MALLOC_2 (x, x);
+
+    memset (px_x, 0, 0);
+    memset (px_x, 0, 1);
+    memset (px_x, 0, 2);
+    memset (px_x, 0, 12345);
+  }
+
+  {
+    char *p3_5 = CHOOSE_MALLOC_2 (3, 5);
+
+    memset (p3_5, 0, 1);
+    memset (p3_5, 0, 3);
+    memset (p3_5, 0, 4);
+    memset (p3_5, 0, 5);
+    memset (p3_5, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    char *p5_3 = CHOOSE_MALLOC_2 (5, 3);
+
+    memset (p5_3, 0, 3);
+    memset (p5_3, 0, 4);
+    memset (p5_3, 0, 5);
+    memset (p5_3, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    char *px_3 = CHOOSE_MALLOC_2 (x, 3);
+
+    memset (px_3, 0, 1);
+    memset (px_3, 0, 3);
+    memset (px_3, 0, 4);
+    memset (px_3, 0, 1234);
+  }
+
+  {
+    char *p5_x = CHOOSE_MALLOC_2 (5, x);
+
+    memset (p5_x, 0, 1);
+    memset (p5_x, 0, 5);
+    memset (p5_x, 0, 6);
+    memset (p5_x, 0, 1234);
+  }
+
+}
+
+
+void memset_malloc_3 (void)
+{
+  {
+    char *p0_1_2 = CHOOSE_MALLOC_3 (0, 1, 2);
+    memset (p0_1_2, 0, 0);
+    memset (p0_1_2, 0, 1);
+    memset (p0_1_2, 0, 2);
+    memset (p0_1_2, 0, 3);    // { dg-warning "memset' writing 3 bytes into a region of size 2 " }
+    memset (p0_1_2, 0, 9);    // { dg-warning "memset' writing 9 bytes into a region of size 2 " }
+  }
+
+  {
+    char *p0_2_x = CHOOSE_MALLOC_3 (0, 2, x);
+
+    memset (p0_2_x, 0, 0);
+    memset (p0_2_x, 0, 1);
+    memset (p0_2_x, 0, 3);
+    memset (p0_2_x, 0, 9);
+  }
+
+  {
+    char *p3_4_5 = CHOOSE_MALLOC_3 (3, 4, 5);
+
+    memset (p3_4_5, 0, 3);
+    memset (p3_4_5, 0, 4);
+    memset (p3_4_5, 0, 5);
+    memset (p3_4_5, 0, 6);    // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    char *p5_3_4 = CHOOSE_MALLOC_3 (5, 3, 4);
+
+    memset (p5_3_4, 0, 3);
+    memset (p5_3_4, 0, 4);
+    memset (p5_3_4, 0, 5);
+    memset (p5_3_4, 0, 6);    // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    char *p9_8_7 = CHOOSE_MALLOC_3 (9, 8, 7);
+
+    memset (p9_8_7, 0, 7);
+    memset (p9_8_7, 0, 8);
+    memset (p9_8_7, 0, 9);
+    memset (p9_8_7, 0, 10);   // { dg-warning "memset' writing 10 bytes into a region of size 9 " }
+  }
+}
+
+
+/* Verify conditionally writing into one of two objects with the same
+   size.  */
+
+void memset_malloc_2_same_size (int i)
+{
+  {
+    char a4_1[4], a4_2[4];
+    char *p4 = cond1 ? a4_1 : a4_2;
+
+    memset (p4, 0, 1);
+    memset (p4, 0, 2);
+    memset (p4, 0, 3);
+    memset (p4, 0, 4);
+    memset (p4, 0, 5);        // { dg-warning "memset' writing 5 bytes into a region of size 4" }
+  }
+
+  {
+    char a4_1[4];             // { dg-message "destination object 'a4_1" "note" }
+    char a4_2[4];             // { dg-message "destination object 'a4_2" "note" }
+    char *p4 = cond1 ? a4_1 : a4_2;
+    char *p4_i = p4 + i;
+
+    memset (p4_i, 0, 5);      // { dg-warning "memset' writing 5 bytes into a region of size 4" }
+  }
+
+  {
+    if (i < 1)
+      i = 1;
+
+    char a4_1[4];             // { dg-message "at offset \\\[1, 4] into destination object 'a4_1" "note" }
+    char a4_2[4];             // { dg-message "at offset \\\[1, 4] into destination object 'a4_2" "note" }
+    char *p4 = cond1 ? a4_1 : a4_2;
+    char *p4_i = p4 + i;
+
+    memset (p4_i, 0, 3);
+    memset (p4_i, 0, 4);      // { dg-warning "memset' writing 4 bytes into a region of size 3 " }
+  }
+}
+
+
+void memset_malloc_2_off (void)
+{
+  int i1 = SR (1, INT_MAX);
+  int i2 = SR (2, INT_MAX);
+
+  {
+    char a5[5];               // { dg-warning "at offset [1, 5] into destination object 'a5'
+    char a7[7];               // { dg-warning "at offset [2, 7] into destination object 'a7'
+    char *p5_p1 = a5 + i1;
+    char *p7_p2 = a7 + i2;
+    char *p5_7 = cond1 ? p5_p1 : p7_p2;
+
+    memset (p5_7, 0, 1);
+    memset (p5_7, 0, 2);
+    memset (p5_7, 0, 3);
+    memset (p5_7, 0, 4);
+    memset (p5_7, 0, 5);
+    memset (p5_7, 0, 6);      // { dg-warning "memset' writing 6 bytes into a region of size 5 " }
+  }
+
+  int i3 = SR (3, INT_MAX);
+
+  {
+    char a5[5];
+    // { dg-message "at offset \\\[3, 5] into destination object 'a5'" "note" { target *-*-* } .-1 }
+    // { dg-message "at offset \\\[2, 5] into destination object 'a5'" "note" { target *-*-* } .-2 }
+    // { dg-message "at offset \\\[1, 5] into destination object 'a5'" "note" { target *-*-* } .-3 }
+    // { dg-message ": destination object 'a5'" "note" { target *-*-* } .-4 }
+    char a9[9];
+    // { dg-message "at offset \\\[4, 9] into destination object 'a9'" "note" { target *-*-* } .-1 }
+    // { dg-message "at offset \\\[3, 9] into destination object 'a9'" "note" { target *-*-* } .-2 }
+    // { dg-message "at offset \\\[2, 9] into destination object 'a9'" "note" { target *-*-* } .-3 }
+    // { dg-message ": destination object 'a9'" "note" { target *-*-* } .-4 }
+    char *p5_p2 = a5 + i2;    // 3 bytes left
+    char *p9_p3 = a9 + i3;    // 6 bytes left
+    char *p =
+      cond1 ? p5_p2 : p9_p3;  // [3 - 6] bytes left
+    char *q = p + i1;         // [2 - 5] bytes left
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 4);
+    memset (q, 0, 5);
+    memset (q, 0, 6);         // { dg-warning "memset' writing 6 bytes into a region of size 5" }
+
+    --q;                      // [3 - 6] bytes left
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 4);
+    memset (q, 0, 5);
+    memset (q, 0, 6);
+    memset (q, 0, 7);         // { dg-warning "memset' writing 7 bytes into a region of size 6" }
+
+    --q;                      // [4 - 7] bytes left
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 4);
+    memset (q, 0, 5);
+    memset (q, 0, 6);
+    memset (q, 0, 7);
+    memset (q, 0, 8);         // { dg-warning "memset' writing 8 bytes into a region of size 7" }
+
+    int m1_x = SR (-1, INT_MAX);
+    int m2_x = SR (-2, INT_MAX);
+
+    q += cond2 ? m1_x : m2_x;   // [5 - 9] bytes left
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 4);
+    memset (q, 0, 5);
+    memset (q, 0, 6);
+    memset (q, 0, 7);
+    memset (q, 0, 8);
+    memset (q, 0, 9);
+    memset (q, 0, 10);        // { dg-warning "memset' writing 10 bytes into a region of size 9" }
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-60.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-60.c
new file mode 100644
index 00000000000..8c9de20e9bc
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-60.c
@@ -0,0 +1,72 @@
+/* Test derived from Glibc's getifaddrs_internal.   The code could be
+   rewritten to avoid the warning for the memcpy call but since unions
+   are designed to have their members treated as interchangeable there
+   isn't a whole lot to be gained from issuing one.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void*, const void*, size_t);
+
+struct sockaddr
+{
+  short sa_family;
+  char sa_data[14];
+};
+
+struct in_addr
+{
+  int s_addr;
+};
+
+struct in6_addr
+{
+  union
+  {
+    char __u6_addr8[16];
+    short __u6_addr16[8];
+    int __u6_addr32[4];
+  } __in6_u;
+};
+
+struct sockaddr_in
+{
+  short sin_family;
+  short sin_port;
+  struct in_addr sin_addr;
+  unsigned char sin_zero[sizeof (struct sockaddr) -
+			 (sizeof (short)) -
+			 sizeof (short) -
+			 sizeof (struct in_addr)];
+};
+
+struct sockaddr_in6
+{
+  short sin6_family;
+  short sin6_port;
+  int sin6_flowinfo;
+  struct in6_addr sin6_addr;
+  int sin6_scope_id;
+};
+
+union
+{
+  struct sockaddr sa;
+  struct sockaddr_in s4;
+  struct sockaddr_in6 s6;
+} u1, u2;
+
+struct sockaddr *sa;
+
+void test_unconditional (void *p)
+{
+  sa = &u1.sa;
+  memcpy (&((struct sockaddr_in6 *) sa)->sin6_addr, p, 16);
+}
+
+void test_conditional (void *p, int i)
+{
+  sa = i ? &u1.sa : &u2.sa;
+  memcpy (&((struct sockaddr_in6 *) sa)->sin6_addr, p, 16);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-61.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-61.c
new file mode 100644
index 00000000000..7601679fac3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-61.c
@@ -0,0 +1,88 @@
+/* { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+void* memcpy (void*, const void*, size_t);
+size_t strlen (const char *);
+
+// Test case reduced from gcc/attribs.c.
+
+char* sorted_attr_string (char *argv[])
+{
+  size_t n = 0;
+  unsigned int i;
+
+  for (i = 0; argv[i]; ++i)
+    n += strlen (argv[i]);
+
+  char *s = (char*)malloc (n);
+  n = 0;
+  for (i = 0; argv[i]; ++i)
+    {
+      const char *str = argv[i];
+      size_t len = strlen (str);
+      memcpy (s + n, str, len);
+      n += len + 1;
+    }
+
+  /* Replace "=,-" with "_".  */
+  for (i = 0; i < strlen (s); i++)
+    if (s[i] == '=')
+      s[i] = '_';             // { dg-bogus "\\\[-Wstringop-overflow" }
+
+  return s;
+}
+
+
+void f (void*);
+
+void nowarn_cond_escape (int c, int *x)
+{
+  extern char a3[3], a5[5];
+
+  char *p;
+  if (c)
+    {
+      p = a3;
+      *x = 2;
+   }
+   else
+     {
+       p = a5;
+       *x = 4;
+     }
+
+  f (p);   // may modify *x
+
+  if (*x == 2)
+    p[2] = 0;
+  else if (*x == 4)
+    p[4] = 0;                 // { dg-bogus "\\\[-Wstringop-overflow" }
+}
+
+void warn_cond_escape (int c, int *x)
+{
+  extern char a3_2[3];
+  extern char a5_2[5];        // { dg-message "at offset 5 into destination object 'a5_2'" }
+
+  char *p;
+  if (c)
+    {
+      p = a3_2;
+      *x = 2;
+   }
+   else
+     {
+       p = a5_2;
+       *x = 5;
+     }
+
+  f (p);   // may modify *x
+
+  if (*x == 2)
+    p[2] = 0;
+  else if (*x == 5)
+    p[5] = 0;                 // { dg-warning "\\\[-Wstringop-overflow" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c
new file mode 100644
index 00000000000..318d9bd1f94
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-62.c
@@ -0,0 +1,363 @@
+/* Test for MIN and MAX expressions involving pointers.
+  { dg-do compile }
+  { dg-options "-O2 -Wall -Wno-array-bounds -ftrack-macro-expansion=0" } */
+
+#include "range.h"
+
+#define INT_MAX __INT_MAX__
+
+#define MIN(x, y) ((x) < (y) ? (x) :  (y))
+#define MAX(x, y) ((x) < (y) ? (y) :  (x))
+
+typedef __SIZE_TYPE__ size_t;
+
+void* memset (void*, int, size_t);
+#define memset(...) sink (memset (__VA_ARGS__))
+
+void sink (void*, ...);
+
+volatile int cond, vi;
+char* volatile ptr;
+
+void test_min (void)
+{
+  const int i1 = SR (1, INT_MAX);
+  const int i2 = SR (2, INT_MAX);
+
+  {
+    /* Exercise both pointers pointing to a different unknown object plus
+       positive constant offset.  Since PTR is volatile P1 and P2 cannot
+       normally be considered to point to the same object.  It can only
+       be inferred from the MIN expression.  */
+    char *p1 = ptr + 1;
+    char *p2 = ptr + 2;
+
+    char *q = MIN (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, INT_MAX);
+    // { dg-warning "writing 2147483647 bytes into a region of size 2147483646" "ilp32" { target ilp32 } .-1 }
+    memset (q, 0, DIFF_MAX - 2);
+    memset (q, 0, DIFF_MAX);
+    // { dg-warning "writing 2147483647 bytes into a region of size 2147483646" "ilp32" { target ilp32 } .-1 }
+    // { dg-warning "writing 9223372036854775807 bytes into a region of size 9223372036854775806" "lp64" { target lp64 } .-2 }
+  }
+
+  {
+    /* Exercise both pointers pointing to a different unknown object plus
+       variable offset.  */
+    char *p1 = ptr + vi;
+    char *p2 = ptr + vi;
+
+    char *q = MIN (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, INT_MAX);
+  }
+
+  {
+    /* Exercise both pointers pointing to the same object plus constant
+       offset.  */
+    char a2[2];               // { dg-message "at offset 1 into destination object 'a2' of size 2" "note" }
+    char *p1 = a2 + 1;
+    char *p2 = a2 + 2;
+
+    char *q = MIN (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);         // { dg-warning "writing 2 bytes into a region of size 1 " }
+  }
+
+  {
+    /* Exercise both pointers pointing to the same object plus offset
+       in a known range.  */
+    char a3[3];               // { dg-message "at offset \\\[1, 3] into destination object 'a3'" "note" }
+    char *pi = a3 + i1;
+    char *pj = a3 + i2;
+
+    char *q = MIN (pi, pj);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);         // { dg-warning "writing 3 bytes into a region of size 2 " }
+  }
+
+  {
+    /* Exercise both pointers pointing to the same object plus variable
+       offset.  Verify that no offset is mentioned in the note (since
+       its unknown, printing its full range is unnecessary).  */
+    char a4[4];               // { dg-message ": destination object 'a4'" "note" }
+    char *pi = a4 + vi;
+    char *pj = a4 + vi;
+
+    char *q = MIN (pi, pj);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 4);
+    memset (q, 0, 5);         // { dg-warning "writing 5 bytes into a region of size 4 " }
+  }
+
+  {
+    /* Exercise a pointer pointing to a known object with one pointing
+       to an unknown object.  */
+    char a5[5];               // { dg-message ": destination object 'a5'" "note" }
+    char *p = ptr;
+    char *q = MIN (p, a5);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 5);
+    memset (q, 0, 6);         // { dg-warning "writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    /* Exercise a pointer pointing to a known object plus constant offset
+       with one pointing to an unknown object.  */
+    char a6[6];               // { dg-message ": destination object 'a6'" "note" }
+    char *p1 = ptr;
+    char *p2 = a6 + 1;
+    char *q = MIN (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 6);
+    memset (q, 0, 7);         // { dg-warning "writing 7 bytes into a region of size 6 " }
+  }
+
+  {
+    /* Exercise a pointer pointing to a known object with one pointing
+       to an unknown object plus constant offset.  */
+    char a7[7];               // { dg-message ": destination object 'a7'" "note" }
+    char *p1 = a7;
+    char *p2 = ptr + 1;
+    /* Since p1 points to a7[0] it must be less than any pointer to a7
+       plus positive offset, and so Q == P1.  */
+    char *q = MIN (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 7);
+    memset (q, 0, 8);         // { dg-warning "writing 8 bytes into a region of size 7 " }
+  }
+
+  {
+    /* Exercise a pointer pointing to a known object plus constant offset
+       with one pointing to an unknown object plus a different constant
+       offset.  */
+    char a8[8];               // { dg-message "at offset 1 into destination object 'a8'" "note" }
+    char *p1 = a8 + 1;
+    char *p2 = ptr + 2;
+    /* Since P1 points to A8[1] it must be less than or equal to any
+       pointer to A8 plus positive offset.  Either way, Q must point
+       to A8[1].  */
+    char *q = MIN (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 7);
+    memset (q, 0, 8);         // { dg-warning "writing 8 bytes into a region of size 7 " }
+  }
+
+  {
+    /* Same as above but with larger offsets.  */
+    char a9[9];               // { dg-message "at offset 3 into destination object 'a9'" "note" }
+    char *p1 = a9 + 3;
+    char *p2 = ptr + 4;
+    /* Since P1 points to A9[3] it must be less than or equal to any
+       pointer anywhere into A9 plus 4, so Q must point to A9[3].  */
+    char *q = MIN (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 6);
+    memset (q, 0, 7);         // { dg-warning "writing 7 bytes into a region of size 6 " }
+  }
+
+  {
+    /* Same as above but with the offsets reversed.  */
+    char a10[10];              // { dg-message "at offset 5 into destination object 'a10'" "note" }
+    char *p1 = a10 + 10;
+    char *p2 = ptr + 5;
+    /* Since P1 points just past the end of A10 it could be either less
+       or equal to another pointer anywhere into A10 plus 3 because
+       the other pointer itself could start at a non-zero offset that's
+       not reflected in the determined offset).  */
+    char *q = MIN (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 5);
+    memset (q, 0, 6);         // { dg-warning "writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    char a3[3];               // { dg-message ": destination object 'a3'" "note" }
+    char *p1 = ptr;
+    char *p2 = a3 + i1;
+    char *q = MIN (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 4);         // { dg-warning "writing 4 bytes into a region of size 3 " }
+  }
+}
+
+
+void test_max (void)
+{
+  const int i1 = SR (1, INT_MAX);
+  const int i2 = SR (2, INT_MAX);
+
+  {
+    /* Exercise both pointers pointing to the same object plus constant
+       offset.  */
+    char a2[2];               // { dg-message "at offset 1 into destination object 'a2' of size 2" "note" }
+    char *pi = a2 + 1;
+    char *pj = a2 + 2;
+
+    char *q = MAX (pi, pj);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);         // { dg-warning "writing 2 bytes into a region of size 1 " }
+  }
+
+  {
+    /* Exercise both pointers pointing to the same object plus offset
+       in a known range.  */
+    char a3[3];               // { dg-message "at offset \\\[1, 3] into destination object 'a3'" "note" }
+    char *pi = a3 + i1;
+    char *pj = a3 + i2;
+
+    char *q = MAX (pi, pj);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);         // { dg-warning "writing 3 bytes into a region of size 2 " }
+  }
+
+  {
+    /* Exercise both pointers pointing to the same object plus variable
+       offset.  Verify that no offset is mentioned in the note (since
+       its unknown, printing its full range is unnecessary).  */
+    char a4[4];               // { dg-message ": destination object 'a4'" "note" }
+    char *pi = a4 + vi;
+    char *pj = a4 + vi;
+
+    char *q = MAX (pi, pj);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 3);
+    memset (q, 0, 4);
+    memset (q, 0, 5);         // { dg-warning "writing 5 bytes into a region of size 4 " }
+  }
+
+  {
+    /* Exercise a pointer pointing to a known object with one pointing
+       to an unknown object.  */
+    char a5[5];               // { dg-message ": destination object 'a5'" "note" }
+    char *p = ptr;
+    char *q = MAX (p, a5);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 5);
+    memset (q, 0, 6);         // { dg-warning "writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    /* Exercise a pointer pointing to a known object plus constant offset
+       with one pointing to an unknown object.  */
+    char a6[6];               // { dg-message "at offset 1 into destination object 'a6'" "note" }
+    char *p1 = ptr;
+    char *p2 = a6 + 1;
+    char *q = MAX (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 5);
+    memset (q, 0, 6);         // { dg-warning "writing 6 bytes into a region of size 5 " }
+    memset (q, 0, 7);         // { dg-warning "writing 7 bytes into a region of size 5 " }
+  }
+
+  {
+    /* Exercise a pointer pointing to a known object with one pointing
+       to an unknown object plus constant offset.  */
+    char a7[7];               // { dg-message "at offset 1 into destination object 'a7'" "note" }
+    char *p1 = a7;
+    char *p2 = ptr + 1;
+    /* Since p1 points to a7[0] it must be less than any pointer to a7
+       plus positive offset, and so Q == P2.  */
+    char *q = MAX (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 6);
+    memset (q, 0, 7);         // { dg-warning "writing 7 bytes into a region of size 6 " }
+    memset (q, 0, 8);         // { dg-warning "writing 8 bytes into a region of size 6 " }
+  }
+
+  {
+    /* Exercise a pointer pointing to a known object plus constant offset
+       with one pointing to an unknown object plus a different constant
+       offset.  */
+    char a8[8];               // { dg-message "at offset 2 into destination object 'a8'" "note" }
+    char *p1 = a8 + 1;
+    char *p2 = ptr + 2;
+    /* Since P1 points to A8[1] it must be less than or equal to any
+       pointer to A8 plus positive offset.  Either way, Q must point
+       to A8[2].  */
+    char *q = MAX (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 6);
+    memset (q, 0, 7);         // { dg-warning "writing 7 bytes into a region of size 6 " }
+    memset (q, 0, 8);         // { dg-warning "writing 8 bytes into a region of size 6 " }
+  }
+
+  {
+    /* Same as above but with larger offsets.  */
+    char a9[9];               // { dg-message "at offset 4 into destination object 'a9'" "note" }
+    char *p1 = a9 + 3;
+    char *p2 = ptr + 4;
+    /* Since P1 points to A9[3] it must be less than or equal to any
+       pointer anywhere into A9 plus 4, so Q must point to A9[4].  */
+    char *q = MAX (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 5);
+    memset (q, 0, 6);         // { dg-warning "writing 6 bytes into a region of size 5 " }
+  }
+
+  {
+    /* Same as above but with the offsets reversed.  */
+    char a10[10];              // { dg-message "at offset 10 into destination object 'a10'" "note" }
+    char *p1 = a10 + 10;
+    char *p2 = ptr + 5;
+    /* Since P1 points just past the end of A10 it could be either less
+       or equal to another pointer anywhere into A10 plus 3 because
+       the other pointer itself could start at a non-zero offset that's
+       not reflected in the determaxed offset).  */
+    char *q = MAX (p1, p2);
+
+    memset (q, 0, 1);         // { dg-warning "writing 1 byte into a region of size 0 " }
+  }
+
+  {
+    char a11[11];             // { dg-message "at offset \\\[1, 11] into destination object 'a11'" "note" }
+    char *p1 = ptr;
+    char *p2 = a11 + i1;
+    char *q = MAX (p1, p2);
+
+    memset (q, 0, 1);
+    memset (q, 0, 2);
+    memset (q, 0, 10);
+    memset (q, 0, 11);        // { dg-warning "writing 11 bytes into a region of size 10 " }
+  }
+}
+
diff --git a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
index 02f6f3d5342..82555b0fbcc 100644
--- a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
+++ b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
@@ -143,11 +143,11 @@ T (v0 ? b[1] : "", bsz);
 T (v0 ? b[2] : "", bsz);
 T (v0 ? b[3] : "", bsz);
 
-T (v0 ? "" : b[0], bsz + 1);
+T (v0 ? "" : b[0], bsz + 1);      /* { dg-warning "bound 6 may exceed source size 5" } */
 T (v0 ? "" : b[1], bsz + 1);
 T (v0 ? "" : b[2], bsz + 1);
 T (v0 ? "" : b[3], bsz + 1);      /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[0] : "", bsz + 1);
+T (v0 ? b[0] : "", bsz + 1);      /* { dg-warning "bound 6 may exceed source size 5" } */
 T (v0 ? b[1] : "", bsz + 1);
 T (v0 ? b[2] : "", bsz + 1);
 T (v0 ? b[3] : "", bsz + 1);      /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
@@ -185,8 +185,8 @@ T (v0 ? "1234" : b[i3], bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfa
 T (v0 ? b[3] : "1234", bsz + 1);  /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
 T (v0 ? b[i3] : "1234", bsz + 1); /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
 
-T (v0 ? a : b[3], bsz + 1);       /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
-T (v0 ? b[0] : b[2], bsz + 1);
+T (v0 ? a : b[3], bsz + 1);       /* { dg-warning "bound 6 may exceed source size 5" "pr86937" } */
+T (v0 ? b[0] : b[2], bsz + 1);    /* { dg-warning "bound 6 may exceed source size 5" "pr86937" } */
 T (v0 ? b[2] : b[3], bsz + 1);    /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
 T (v0 ? b[3] : b[2], bsz + 1);    /* { dg-warning "unterminated" "pr86937" { xfail *-*-* } } */
 
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index ebb17cd852c..7de2daa2b54 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -1933,6 +1933,8 @@ maybe_warn_overflow (gimple *stmt, tree len,
       dest = gimple_call_arg (stmt, 0);
       writefn = gimple_call_fndecl (stmt);
     }
+  else
+    return;
 
   if (TREE_NO_WARNING (dest))
     return;
@@ -1941,148 +1943,22 @@ maybe_warn_overflow (gimple *stmt, tree len,
      Make sure all operands have the same precision to keep wide_int
      from ICE'ing.  */
 
-  /* Convenience constants.  */
-  const widest_int diff_min
-    = wi::to_widest (TYPE_MIN_VALUE (ptrdiff_type_node));
-  const widest_int diff_max
-    = wi::to_widest (TYPE_MAX_VALUE (ptrdiff_type_node));
-  const widest_int size_max
-    = wi::to_widest (TYPE_MAX_VALUE (size_type_node));
-
-  /* The offset into the destination object computed below and not
-     reflected in DESTSIZE.  */
-  widest_int offrng[2] = { 0, 0 };
-
-  if (!si)
-    {
-      /* If no destination STRINFO was provided try to get it from
-	 the DEST argument.  */
-      tree ref = dest;
-      if (TREE_CODE (ref) == ARRAY_REF)
-	{
-	  /* Handle stores to VLAs (represented as
-	     ARRAY_REF (MEM_REF (vlaptr, 0), N].  */
-	  tree off = TREE_OPERAND (ref, 1);
-	  ref = TREE_OPERAND (ref, 0);
-	  wide_int rng[2];
-	  if (get_range (off, stmt, rng, rvals))
-	    {
-	      /* Convert offsets to the maximum precision.  */
-	      offrng[0] = widest_int::from (rng[0], SIGNED);
-	      offrng[1] = widest_int::from (rng[1], SIGNED);
-	    }
-	  else
-	    {
-	      offrng[0] = diff_min;
-	      offrng[1] = diff_max;
-	    }
-	}
-
-      if (TREE_CODE (ref) == MEM_REF)
-	{
-	  tree mem_off = TREE_OPERAND (ref, 1);
-	  ref = TREE_OPERAND (ref, 0);
-	  wide_int rng[2];
-	  if (get_range (mem_off, stmt, rng, rvals))
-	    {
-	      offrng[0] += widest_int::from (rng[0], SIGNED);
-	      offrng[1] += widest_int::from (rng[1], SIGNED);
-	    }
-	  else
-	    {
-	      offrng[0] = diff_min;
-	      offrng[1] = diff_max;
-	    }
-	}
-
-      wide_int rng[2];
-      if (int idx = get_stridx (ref, rng, rvals))
-	{
-	  si = get_strinfo (idx);
-	  offrng[0] += widest_int::from (rng[0], SIGNED);
-	  offrng[1] += widest_int::from (rng[1], SIGNED);
-	}
-    }
-
-  /* The allocation call if the destination object was allocated
-     by one.  */
-  gimple *alloc_call = NULL;
-  /* The DECL of the destination object if known and not dynamically
-     allocated.  */
-  tree destdecl = NULL_TREE;
-  /* The offset into the destination object set by compute_objsize
-     but already reflected in DESTSIZE.  */
-  tree destoff = NULL_TREE;
+  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 = NULL_TREE;
-
-  /* Compute the range of sizes of the destination object.  The range
-     is constant for declared objects but may be a range for allocated
-     objects.  */
-  widest_int sizrng[2] = { 0, 0 };
-  if (si)
-    {
-      wide_int rng[2];
-      destsize = gimple_call_alloc_size (si->alloc, rng, rvals);
-      if (destsize)
-	{
-	  sizrng[0] = widest_int::from (rng[0], UNSIGNED);
-	  sizrng[1] = widest_int::from (rng[1], UNSIGNED);
-	}
-      alloc_call = si->alloc;
-    }
-  else
-    offrng[0] = offrng[1] = 0;
-
+  tree destsize = compute_objsize (dest, rawmem ? 0 : 1, &aref, rvals);
   if (!destsize)
     {
-      /* If there is no STRINFO for DEST, fall back on compute_objsize.  */
-      tree off = NULL_TREE;
-      destsize = compute_objsize (dest, rawmem ? 0 : 1, &destdecl, &off, rvals);
-      if (destsize)
-	{
-	  /* Remember OFF but clear OFFRNG that may have been set above.  */
-	  destoff = off;
-	  offrng[0] = offrng[1] = 0;
-
-	  if (destdecl && TREE_CODE (destdecl) == SSA_NAME)
-	    {
-	      gimple *stmt = SSA_NAME_DEF_STMT (destdecl);
-	      if (is_gimple_call (stmt))
-		alloc_call = stmt;
-	      destdecl = NULL_TREE;
-	    }
-
-	  wide_int rng[2];
-	  if (get_range (destsize, stmt, rng, rvals))
-	    {
-	      sizrng[0] = widest_int::from (rng[0], UNSIGNED);
-	      sizrng[1] = widest_int::from (rng[1], UNSIGNED);
-	    }
-	  else
-	    {
-	      /* On failure, rather than failing, set the maximum range
-		 so that overflow in allocated objects whose size depends
-		 on the strlen of the source can still be diagnosed
-		 below.  */
-	      sizrng[0] = 0;
-	      sizrng[1] = size_max;
-	    }
-	}
+      aref.sizrng[0] = 0;
+      aref.sizrng[1] = wi::to_offset (max_object_size ());
     }
 
-  if (!destsize)
-    {
-      sizrng[0] = 0;
-      sizrng[1] = size_max;
-    };
-
   /* Return early if the DESTSIZE size expression is the same as LEN
      and the offset into the destination is zero.  This might happen
      in the case of a pair of malloc and memset calls to allocate
      an object and clear it as if by calloc.  */
-  if (destsize == len && !plus_one && offrng[0] == 0 && offrng[0] == offrng[1])
+  if (destsize == len && !plus_one
+      && aref.offrng[0] == 0 && aref.offrng[0] == aref.offrng[1])
     return;
 
   wide_int rng[2];
@@ -2100,38 +1976,13 @@ maybe_warn_overflow (gimple *stmt, tree len,
 
   /* The size of the remaining space in the destination computed
      as the size of the latter minus the offset into it.  */
-  widest_int spcrng[2] = { sizrng[0], sizrng[1] };
-  if (wi::neg_p (offrng[0]) && wi::neg_p (offrng[1]))
-    {
-      /* When the offset is negative and the size of the destination
-	 object unknown there is little to do.
-	 FIXME: Detect offsets that are necessarily invalid regardless
-	 of the size of the object.  */
-      if (!destsize)
-	return;
-
-      /* The remaining space is necessarily zero.  */
-      spcrng[0] = spcrng[1] = 0;
-    }
-  else if (wi::neg_p (offrng[0]))
-    {
-      /* When the lower bound of the offset is negative but the upper
-	 bound is not, reduce the upper bound of the remaining space
-	 by the upper bound of the offset but leave the lower bound
-	 unchanged.  If that makes the upper bound of the space less
-	 than the lower bound swap the two.  */
-      spcrng[1] -= wi::ltu_p (offrng[1], spcrng[1]) ? offrng[1] : spcrng[1];
-      if (wi::ltu_p (spcrng[1], spcrng[0]))
-	std::swap (spcrng[1], spcrng[0]);
-    }
-  else
-    {
-      /* When the offset is positive reduce the remaining space by
-	 the lower bound of the offset or clear it if the offset is
-	 greater.  */
-      spcrng[0] -= wi::ltu_p (offrng[0], spcrng[0]) ? offrng[0] : spcrng[0];
-      spcrng[1] -= wi::ltu_p (offrng[0], spcrng[1]) ? offrng[0] : spcrng[1];
-    }
+  widest_int spcrng[2];
+  {
+    offset_int remrng[2];
+    remrng[1] = aref.size_remaining (remrng);
+    spcrng[0] = remrng[0] == -1 ? 0 : widest_int::from (remrng[0], UNSIGNED);
+    spcrng[1] = widest_int::from (remrng[1], UNSIGNED);
+  }
 
   if (wi::leu_p (lenrng[0], spcrng[0])
       && wi::leu_p (lenrng[1], spcrng[1]))
@@ -2233,112 +2084,7 @@ maybe_warn_overflow (gimple *stmt, tree len,
 
   gimple_set_no_warning (stmt, true);
 
-  /* If DESTOFF is not null, use it to format the offset value/range.  */
-  if (destoff)
-    {
-      wide_int rng[2];
-      if (get_range (destoff, stmt, rng))
-	{
-	  offrng[0] = widest_int::from (rng[0], SIGNED);
-	  offrng[1] = widest_int::from (rng[1], SIGNED);
-	}
-      else
-	offrng[0] = offrng[1] = 0;
-    }
-
-  /* Format the offset to keep the number of inform calls from growing
-     out of control.  */
-  char offstr[64];
-  if (offrng[0] == offrng[1])
-    sprintf (offstr, "%lli", (long long) offrng[0].to_shwi ());
-  else
-    sprintf (offstr, "[%lli, %lli]",
-	     (long long) offrng[0].to_shwi (), (long long) offrng[1].to_shwi ());
-
-  if (destdecl && DECL_P (destdecl))
-    {
-      if (tree size = DECL_SIZE_UNIT (destdecl))
-	inform (DECL_SOURCE_LOCATION (destdecl),
-		"at offset %s to object %qD with size %E declared here",
-		offstr, destdecl, size);
-      else
-	inform (DECL_SOURCE_LOCATION (destdecl),
-		"at offset %s to object %qD declared here",
-		offstr, destdecl);
-      return;
-    }
-
-  if (!alloc_call)
-    return;
-
-  tree allocfn = gimple_call_fndecl (alloc_call);
-  if (!allocfn)
-    {
-      /* For an ALLOC_CALL via a function pointer make a small effort
-	 to determine the destination of the pointer.  */
-      allocfn = gimple_call_fn (alloc_call);
-      if (TREE_CODE (allocfn) == SSA_NAME)
-	{
-	  gimple *def = SSA_NAME_DEF_STMT (allocfn);
-	  if (gimple_assign_single_p (def))
-	    {
-	      tree rhs = gimple_assign_rhs1 (def);
-	      if (DECL_P (rhs))
-		allocfn = rhs;
-	      else if (TREE_CODE (rhs) == COMPONENT_REF)
-		allocfn = TREE_OPERAND (rhs, 1);
-	    }
-	}
-    }
-
-  if (gimple_call_builtin_p (alloc_call, BUILT_IN_ALLOCA_WITH_ALIGN))
-    {
-      if (sizrng[0] == sizrng[1])
-	inform (gimple_location (alloc_call),
-		"at offset %s to an object with size %wu declared here",
-		offstr, sizrng[0].to_uhwi ());
-      else if (sizrng[0] == 0)
-	{
-	  /* Avoid printing impossible sizes.  */
-	  if (wi::ltu_p (sizrng[1], diff_max - 2))
-	    inform (gimple_location (alloc_call),
-		    "at offset %s to an object with size at most %wu "
-		    "declared here",
-		    offstr, sizrng[1].to_uhwi ());
-	  else
-	    inform (gimple_location (alloc_call),
-		    "at offset %s to an object declared here", offstr);
-	}
-      else
-	inform (gimple_location (alloc_call),
-		"at offset %s to an object with size between %wu and %wu "
-		"declared here",
-		offstr, sizrng[0].to_uhwi (), sizrng[1].to_uhwi ());
-      return;
-    }
-
-  if (sizrng[0] == sizrng[1])
-    inform (gimple_location (alloc_call),
-	    "at offset %s to an object with size %wu allocated by %qE here",
-	    offstr, sizrng[0].to_uhwi (), allocfn);
-  else if (sizrng[0] == 0)
-    {
-      /* Avoid printing impossible sizes.  */
-      if (wi::ltu_p (sizrng[1], diff_max - 2))
-	inform (gimple_location (alloc_call),
-		"at offset %s to an object with size at most %wu allocated "
-		"by %qD here",
-		offstr, sizrng[1].to_uhwi (), allocfn);
-      else
-	inform (gimple_location (alloc_call),
-		"at offset %s to an object allocated by %qE here",
-		offstr, allocfn);
-    }
-  else
-    inform (gimple_location (alloc_call),
-	    "at offset %s to an object with size between %wu and %wu "
-	    "allocated by %qE here",
-	    offstr, sizrng[0].to_uhwi (), sizrng[1].to_uhwi (), allocfn);
+  aref.inform_access (access_write_only);
 }
 
 /* Convenience wrapper for the above.  */
@@ -3471,7 +3217,7 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
   if (olddsi != NULL
       && !integer_zerop (len))
     {
-      maybe_warn_overflow (stmt, len, rvals, olddsi, false, true);
+      maybe_warn_overflow (stmt, len, rvals, olddsi, false, false);
       adjust_last_stmt (olddsi, stmt, false);
     }
 
@@ -3938,7 +3684,7 @@ handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write,
   tree memset_size = gimple_call_arg (memset_stmt, 2);
 
   /* Check for overflow.  */
-  maybe_warn_overflow (memset_stmt, memset_size, rvals, NULL, false, true);
+  maybe_warn_overflow (memset_stmt, memset_size, rvals, NULL, false, false);
 
   /* Bail when there is no statement associated with the destination
      (the statement may be null even when SI1->ALLOC is not).  */

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-12-01 23:52 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-03  2:24 [PATCH] handle conditionals in -Wstringop-overflow et al. (PR 92936) Martin Sebor
2020-11-13 21:34 ` Jeff Law
2020-11-29 22:27   ` Martin Sebor
2020-11-30 20:49     ` Jeff Law
2020-12-01  0:58       ` Martin Sebor
2020-12-01 23:52         ` Jeff Law

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).