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

* Re: [PATCH] handle conditionals in -Wstringop-overflow et al. (PR 92936)
  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
  0 siblings, 1 reply; 6+ messages in thread
From: Jeff Law @ 2020-11-13 21:34 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches


On 11/2/20 7:24 PM, Martin Sebor wrote:
> 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
>
> gcc-92936.diff
>
> 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.

So my only significant concern here is the recursive nature and the lack
of a limiter for pathological cases.  We certainly run into cases with
thousands of PHI arguments and deep chains of PHIs feeding other PHIs. 
Can you put in a PARAM to limit the amount of recursion and and PHI
arguments you look at?  With that I think this is fine -- I consider it
unlikely this patch is the root cause of the ICEs I sent you earlier
today from the tester since those failures are in the array bounds
checking bits.


jeff



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

* Re: [PATCH] handle conditionals in -Wstringop-overflow et al. (PR 92936)
  2020-11-13 21:34 ` Jeff Law
@ 2020-11-29 22:27   ` Martin Sebor
  2020-11-30 20:49     ` Jeff Law
  0 siblings, 1 reply; 6+ messages in thread
From: Martin Sebor @ 2020-11-29 22:27 UTC (permalink / raw)
  To: Jeff Law, gcc-patches

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

On 11/13/20 2:34 PM, Jeff Law wrote:
> 
> On 11/2/20 7:24 PM, Martin Sebor wrote:
>> 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
>>
>> gcc-92936.diff
>>
>> 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.
> 
> So my only significant concern here is the recursive nature and the lack 
> of a limiter for pathological cases.  We certainly run into cases with 
> thousands of PHI arguments and deep chains of PHIs feeding other PHIs.  
> Can you put in a PARAM to limit the amount of recursion and and PHI 
> arguments you look at?  With that I think this is fine -- I consider it 
> unlikely this patch is the root cause of the ICEs I sent you earlier 
> today from the tester since those failures are in the array bounds 
> checking bits.

I've reused the same approach/class as in tree-ssa-strlen.c and
after adjusting things that need to be adjusted and retesting
with Binutils/GDB and Glibc committed the attached patch in
r11-5523.

That said, although the recursion hasn't been a problem (there's
still code elsewhere that does this sort of traversal of the use-
def chains that doesn't use the parameter), the subsequent patch
that adds the cache makes it possible to reduce it to just trees
(when the function is called in a GIMPLE pass to cache each
pointer assignment).

Martin

[-- Attachment #2: gcc-92936.diff --]
[-- Type: text/x-patch, Size: 140531 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 (class ssa_name_limit_t): Move class here from
	tree-ssa-strlen.c.
	(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.
	(class ssa_name_limit_t): Move class to builtins.{h,c}.

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.
	* g++.dg/warn/Wstringop-overflow-3.C: Same.
	* g++.dg/warn/Wplacement-new-size.C: Remove a test for a no longer
	issued warning.
	* gcc.dg/Warray-bounds-43.c: Remove unused declarations.
	* 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-7.c: New test.
	* 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.
	* gcc.dg/Wstringop-overflow-63.c: New test.
	* gcc.dg/Wstringop-overflow-64.c: New test.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 40e77c7bf40..7c498e7bb26 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -185,6 +185,8 @@ 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 *, ssa_name_limit_t &,
+			     range_query *);
 
 unsigned HOST_WIDE_INT target_newline;
 unsigned HOST_WIDE_INT target_percent;
@@ -199,7 +201,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 +225,175 @@ 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 *THIS.  If *THIS
+   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 */,
+		     ssa_name_limit_t *psnlim /* = NULL */,
+		     range_query *rvals /* = NULL */) const
+{
+  gphi *phi_stmt = this->phi ();
+  if (!phi_stmt)
+    return ref;
+
+  /* FIXME: Calling get_ref() with a null PSNLIM is dangerous and might
+     cause unbounded recursion.  */
+  ssa_name_limit_t snlim_buf;
+  if (!psnlim)
+    psnlim = &snlim_buf;
+
+  if (!psnlim->visit_phi (ref))
+    return NULL_TREE;
+
+  /* Reflects the range of offsets of all PHI arguments refer to the same
+     object (i.e., have the same REF).  */
+  access_ref same_ref;
+  /* The conservative result of the PHI reflecting the offset and size
+     of the largest PHI argument, regardless of whether or not they all
+     refer to the same object.  */
+  access_ref phi_ref;
+  if (pref)
+    {
+      phi_ref = *pref;
+      same_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 ());
+  /* The offset of the PHI, not reflecting those of its arguments.  */
+  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, *psnlim, rvals)
+	  || phi_arg_ref.sizrng[0] < 0)
+	/* A PHI with all null pointer arguments.  */
+	return NULL_TREE;
+
+      /* 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;
+
+      const bool nullp = integer_zerop (arg) && (i || i + 1 < nargs);
+
+      if (phi_ref.sizrng[0] < 0)
+	{
+	  if (!nullp)
+	    same_ref = phi_arg_ref;
+	  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];
+
+      /* Disregard null pointers in PHIs with two or more arguments.
+	 TODO: Handle this better!  */
+      if (nullp)
+	continue;
+
+      /* 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 (phi_arg_ref.ref != same_ref.ref)
+	same_ref.ref = NULL_TREE;
+
+      if (phirem[1] < argrem[1]
+	  || (phirem[1] == argrem[1]
+	      && phi_ref.sizrng[1] < phi_arg_ref.sizrng[1]))
+	/* Use the argument with the most space remaining as the result,
+	   or the larger one if the space is equal.  */
+	phi_ref = phi_arg_ref;
+
+      /* Set SAME_REF.OFFRNG to the maximum range of all arguments.  */
+      if (phi_arg_ref.offrng[0] < same_ref.offrng[0])
+	same_ref.offrng[0] = phi_arg_ref.offrng[0];
+      if (same_ref.offrng[1] < phi_arg_ref.offrng[1])
+	same_ref.offrng[1] = phi_arg_ref.offrng[1];
+    }
+
+  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).  */
+      psnlim->leave_phi (ref);
+      return NULL_TREE;
+    }
+
+  if (!same_ref.ref && same_ref.offrng[0] != 0)
+    /* Clear BASE0 if not all the arguments refer to the same object and
+       if not all their offsets are zero-based.  This allows the final
+       PHI offset to out of bounds for some arguments but not for others
+       (or negative even of all the arguments are BASE0), which is overly
+       permissive.  */
+    phi_ref.base0 = false;
+
+  if (same_ref.ref)
+    phi_ref = same_ref;
+  else
+    {
+      /* 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;
+    }
+
+  /* Avoid changing *THIS.  */
+  if (pref && pref != this)
+    *pref = phi_ref;
+
+  psnlim->leave_phi (ref);
+
+  return phi_ref.ref;
+}
+
 /* Return the maximum amount of space remaining and if non-null, set
    argument to the minimum.  */
 
@@ -318,7 +490,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)
 	{
@@ -353,6 +524,67 @@ void access_ref::add_offset (const offset_int &min, const offset_int &max)
     }
 }
 
+bool
+ssa_name_limit_t::visit_phi (tree ssa_name)
+{
+  if (!visited)
+    visited = BITMAP_ALLOC (NULL);
+
+  /* Return false if SSA_NAME has already been visited.  */
+  return bitmap_set_bit (visited, SSA_NAME_VERSION (ssa_name));
+}
+
+void
+ssa_name_limit_t::leave_phi (tree ssa_name)
+{
+  /* Return false if SSA_NAME has already been visited.  */
+  bitmap_clear_bit (visited, SSA_NAME_VERSION (ssa_name));
+}
+
+bool
+ssa_name_limit_t::next ()
+{
+  /* Return a negative value to let caller avoid recursing beyond
+     the specified limit.  */
+  if (ssa_def_max == 0)
+    return false;
+
+  --ssa_def_max;
+
+  return true;
+}
+
+/* If the SSA_NAME has already been "seen" return a positive value.
+   Otherwise add it to VISITED.  If the SSA_NAME limit has been
+   reached, return a negative value.  Otherwise return zero.  */
+
+int
+ssa_name_limit_t::next_phi (tree ssa_name)
+{
+  {
+    gimple *def_stmt = SSA_NAME_DEF_STMT (ssa_name);
+    /* Return a positive value if the PHI has already been visited.  */
+    if (gimple_code (def_stmt) == GIMPLE_PHI
+	&& !visit_phi (ssa_name))
+      return 1;
+  }
+
+  /* Return a negative value to let caller avoid recursing beyond
+     the specified limit.  */
+  if (ssa_def_max == 0)
+    return -1;
+
+  --ssa_def_max;
+
+  return 0;
+}
+
+ssa_name_limit_t::~ssa_name_limit_t ()
+{
+  if (visited)
+    BITMAP_FREE (visited);
+}
+
 /* Return true if NAME starts with __builtin_ or __sync_.  */
 
 static bool
@@ -3561,28 +3793,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))
@@ -3590,22 +3836,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)
 	{
@@ -3624,28 +3882,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))
@@ -3653,22 +3924,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)
@@ -3697,7 +3980,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;
 
@@ -3706,40 +3989,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;
@@ -3750,44 +4057,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;
@@ -3799,35 +4131,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)
@@ -3879,28 +4240,61 @@ 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)
+	{
+	  /* Skip any PHIs; those could lead to infinite recursion.  */
+	  if (all_refs[i].phi ())
+	    continue;
+
+	  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
@@ -3910,110 +4304,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
@@ -4233,17 +4644,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.  */
@@ -4326,12 +4738,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;
     }
@@ -4463,11 +4876,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
@@ -4499,6 +4913,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
@@ -4646,6 +5063,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,
+		     ssa_name_limit_t &snlim, 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], snlim, 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], snlim, 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).
@@ -4653,7 +5148,7 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2],
    if it's unique, otherwise to null, PREF->OFFRNG to the range of
    offsets into it, and PREF->SIZRNG to the range of sizes of
    the object(s).
-   VISITED is used to avoid visiting the same PHI operand multiple
+   SNLIM is used to avoid visiting the same PHI operand multiple
    times, and, when nonnull, RVALS to determine range information.
    Returns true on success, false when a meaningful size (or range)
    cannot be determined.
@@ -4662,8 +5157,8 @@ gimple_call_return_array (gimple *stmt, offset_int offrng[2],
    to influence code generation or optimization.  */
 
 static bool
-compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
-		 range_query *rvals)
+compute_objsize (tree ptr, int ostype, access_ref *pref,
+		 ssa_name_limit_t &snlim, range_query *rvals)
 {
   STRIP_NOPS (ptr);
 
@@ -4699,7 +5194,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
   if (code == BIT_FIELD_REF)
     {
       tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize (ref, ostype, pref, visited, rvals))
+      if (!compute_objsize (ref, ostype, pref, snlim, rvals))
 	return false;
 
       offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
@@ -4710,6 +5205,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)
@@ -4717,7 +5216,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 	  /* In OSTYPE zero (for raw memory functions like memcpy), use
 	     the maximum size instead if the identity of the enclosing
 	     object cannot be determined.  */
-	  if (!compute_objsize (ref, ostype, pref, visited, rvals))
+	  if (!compute_objsize (ref, ostype, pref, snlim, rvals))
 	    return false;
 
 	  /* Otherwise, use the size of the enclosing object and add
@@ -4727,9 +5226,17 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 	    pref->add_offset (wi::to_offset (offset));
 	  else
 	    pref->add_max_offset ();
+
+	  if (!pref->ref)
+	    /* REF may have been already set to an SSA_NAME earlier
+	       to provide better context for diagnostics.  In that case,
+	       leave it unchanged.  */
+	    pref->ref = ref;
 	  return true;
 	}
 
+      pref->ref = field;
+
       if (!addr && POINTER_TYPE_P (TREE_TYPE (field)))
 	{
 	  /* Set maximum size if the reference is to the pointer member
@@ -4738,8 +5245,6 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 	  return true;
 	}
 
-      pref->ref = field;
-
       /* SAM is set for array members that might need special treatment.  */
       special_array_member sam;
       tree size = component_ref_size (ptr, &sam);
@@ -4768,7 +5273,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
     {
       tree ref = TREE_OPERAND (ptr, 0);
       tree reftype = TREE_TYPE (ref);
-      if (code == ARRAY_REF
+      if (!addr && code == ARRAY_REF
 	  && TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE)
 	/* Avoid arrays of pointers.  FIXME: Hande pointers to arrays
 	   of known bound.  */
@@ -4786,7 +5291,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 	    return false;
 	}
 
-      if (!compute_objsize (ref, ostype, pref, visited, rvals))
+      if (!compute_objsize (ref, ostype, pref, snlim, rvals))
 	return false;
 
       offset_int orng[2];
@@ -4852,7 +5357,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
   if (code == TARGET_MEM_REF)
     {
       tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize (ref, ostype, pref, visited, rvals))
+      if (!compute_objsize (ref, ostype, pref, snlim, rvals))
 	return false;
 
       /* TODO: Handle remaining operands.  Until then, add maximum offset.  */
@@ -4879,13 +5384,14 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
   if (code == STRING_CST)
     {
       pref->sizrng[0] = pref->sizrng[1] = TREE_STRING_LENGTH (ptr);
+      pref->ref = ptr;
       return true;
     }
 
   if (code == POINTER_PLUS_EXPR)
     {
       tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize (ref, ostype, pref, visited, rvals))
+      if (!compute_objsize (ref, ostype, pref, snlim, rvals))
 	return false;
 
       offset_int orng[2];
@@ -4900,11 +5406,16 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
   if (code == VIEW_CONVERT_EXPR)
     {
       ptr = TREE_OPERAND (ptr, 0);
-      return compute_objsize (ptr, ostype, pref, visited, rvals);
+      return compute_objsize (ptr, ostype, pref, snlim, rvals);
     }
 
-  if (TREE_CODE (ptr) == SSA_NAME)
+  if (code == SSA_NAME)
     {
+      if (!snlim.next ())
+	return false;
+
+      /* Only process an SSA_NAME if the recursion limit has not yet
+	 been reached.  */
       gimple *stmt = SSA_NAME_DEF_STMT (ptr);
       if (is_gimple_call (stmt))
 	{
@@ -4933,7 +5444,7 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 	      offset_int offrng[2];
 	      if (tree ret = gimple_call_return_array (stmt, offrng, rvals))
 		{
-		  if (!compute_objsize (ret, ostype, pref, visited, rvals))
+		  if (!compute_objsize (ret, ostype, pref, snlim, rvals))
 		    return false;
 
 		  /* Cap OFFRNG[1] to at most the remaining size of
@@ -4964,8 +5475,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;
@@ -4975,14 +5488,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, &snlim, rvals))
+	    return false;
+	  *pref = phi_ref;
+	  pref->ref = ptr;
+	  return true;
+	}
 
       if (!is_gimple_assign (stmt))
 	{
@@ -4992,21 +5510,22 @@ 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;
+	  pref->ref = ptr;
 	  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, snlim, rvals);
+
+      tree rhs = gimple_assign_rhs1 (stmt);
+
       if (code == POINTER_PLUS_EXPR
-	  && TREE_CODE (TREE_TYPE (ptr)) == POINTER_TYPE)
+	  && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE)
 	{
 	  /* Compute the size of the object first. */
-	  if (!compute_objsize (ptr, ostype, pref, visited, rvals))
+	  if (!compute_objsize (rhs, ostype, pref, snlim, rvals))
 	    return false;
 
 	  offset_int orng[2];
@@ -5018,17 +5537,22 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 	  return true;
 	}
 
-      if (code == ADDR_EXPR)
-	return compute_objsize (ptr, ostype, pref, visited, rvals);
+      if (code == ADDR_EXPR
+	  || code == SSA_NAME)
+	return compute_objsize (rhs, ostype, pref, snlim, rvals);
 
-      /* This could be an assignment from a nonlocal pointer.  Save PTR
-	 to mention in diagnostics but otherwise treat it as a pointer
-	 to an unknown object.  */
-      pref->ref = ptr;
+      /* (This could also be an assignment from a nonlocal pointer.)  Save
+	 PTR to mention in diagnostics but otherwise treat it as a pointer
+         to an unknown object.  */
+      pref->ref = rhs;
+      pref->base0 = false;
+      pref->set_max_size_range ();
+      return true;
     }
 
   /* Assume all other expressions point into an unknown object
      of the maximum valid size.  */
+  pref->ref = ptr;
   pref->base0 = false;
   pref->set_max_size_range ();
   return true;
@@ -5041,15 +5565,8 @@ tree
 compute_objsize (tree ptr, int ostype, access_ref *pref,
 		 range_query *rvals /* = NULL */)
 {
-  bitmap visited = NULL;
-
-  bool success
-    = compute_objsize (ptr, ostype, pref, &visited, rvals);
-
-  if (visited)
-    BITMAP_FREE (visited);
-
-  if (!success)
+  ssa_name_limit_t snlim;
+  if (!compute_objsize (ptr, ostype, pref, snlim, rvals))
     return NULL_TREE;
 
   offset_int maxsize = pref->size_remaining ();
diff --git a/gcc/builtins.h b/gcc/builtins.h
index c09f36da02b..b00af627223 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -153,6 +153,42 @@ 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);
 
+/* Describes recursion limits used by functions that follow use-def
+   chains of SSA_NAMEs.  */
+
+class ssa_name_limit_t
+{
+  bitmap visited;         /* Bitmap of visited SSA_NAMEs.  */
+  unsigned ssa_def_max;   /* Longest chain of SSA_NAMEs to follow.  */
+
+  /* Not copyable or assignable.  */
+  DISABLE_COPY_AND_ASSIGN (ssa_name_limit_t);
+
+public:
+
+  ssa_name_limit_t ()
+    : visited (),
+      ssa_def_max (param_ssa_name_def_chain_limit) { }
+
+  /* Set a bit for the PHI in VISITED and return true if it wasn't
+     already set.  */
+  bool visit_phi (tree);
+  /* Clear a bit for the PHI in VISITED.  */
+  void leave_phi (tree);
+  /* Return false if the SSA_NAME chain length counter has reached
+     the limit, otherwise increment the counter and return true.  */
+  bool next ();
+
+  /* If the SSA_NAME has already been "seen" return a positive value.
+     Otherwise add it to VISITED.  If the SSA_NAME limit has been
+     reached, return a negative value.  Otherwise return zero.  */
+  int next_phi (tree);
+
+  ~ssa_name_limit_t ();
+};
+
+class range_query;
+
 /* Describes a reference to an object used in an access.  */
 struct access_ref
 {
@@ -162,17 +198,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,
+		ssa_name_limit_t * = NULL, range_query * = NULL) const;
 
   /* Return true if OFFRNG is the constant zero.  */
   bool offset_zero () const
@@ -211,6 +242,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 +266,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 +292,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 63b1a309564..1d79930cd58 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/g++.dg/warn/Wplacement-new-size.C b/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C
index 48d6b15656a..25325b3f839 100644
--- a/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C
+++ b/gcc/testsuite/g++.dg/warn/Wplacement-new-size.C
@@ -332,11 +332,11 @@ void test (void *p, int32_t n)
     new (&uac2.c) int32_t;                  // { dg-warning "placement" }
     new (&uac3.c) int32_t;                  // { dg-warning "placement" }
 
-    // Diagnose the following even though the size of uac4.c could be
-    // expected to extend to the end of the union (as it is by Built-in
-    // Object Size and so isn't diagnosed in calls to functions like
-    // memset(&uac4.c, 0, sizeof(int32_t)) when _FORTIFY_SOURCE is non-zero.  */
-    new (&uac4.c) int32_t;                  // { dg-warning "placement" }
+    /* The following isn't diagnosed (anymore) for consistency with
+       the middle end where members of unions are considered to extend
+       to the end of the enclosing object.
+       See gcc.dg/Wstringop-overflow-60.c for the middle end test.  */
+    new (&uac4.c) int32_t;
 
     new (&uac4.c + 1) int32_t;              // { dg-warning "placement" }
 }
diff --git a/gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
index da9ad6fd6a2..c68e82a4a58 100644
--- a/gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
+++ b/gcc/testsuite/g++.dg/warn/Wstringop-overflow-3.C
@@ -12,7 +12,7 @@ void sink (void*);
 struct Ax
 {
   char n;
-  char a[];                     // { dg-message "at offset \[0-2\] to object 'Ax::a' declared here" "note: flexarray" }
+  char a[];                     // { dg-message "destination object 'Ax::a' of size 0" "note: flexarray" }
 };
 
 // Verify warning for a definition with no initializer.
@@ -93,7 +93,7 @@ NOIPA void gaxx ()
 struct A0
 {
   char n;
-  char a[0];                    // { dg-message "at offset \[0-2\] to object 'A0::a' with size 0 declared here" "note: trailing zero-length array" }
+  char a[0];                    // { dg-message "destination object 'A0::a' of size 0" "note: trailing zero-length array" }
 };
 
 // Verify warning for a definition with no initializer.
@@ -160,7 +160,7 @@ NOIPA void ga0x ()
 struct A1
 {
   char n;
-  char a[1];                    // { dg-message "at offset \[1-9\] to object 'A1::a' with size 1 declared here" "note: trailing one-element array" }
+  char a[1];                    // { dg-message "at offset \[1-2\] into destination object 'A1::a' of size 1" "note: trailing one-element array" }
 };
 
 // Verify warning for a definition with no initializer.
@@ -234,7 +234,7 @@ NOIPA void ga1x ()
 struct A1i
 {
   char n;
-  char a[1];                    // { dg-message "at offset \[1-9\] to object 'A1i::a' with size 1 declared here" "note: interior one-element array" }
+  char a[1];                    // { dg-message "at offset \[1-2\] into destination object 'A1i::a' of size 1" "note: interior one-element array" }
   char x;
 };
 
@@ -307,7 +307,7 @@ NOIPA void ga1ix ()
 struct Bx
 {
   char n;
-  char a[];                     // { dg-message "at offset 0 to object 'Bx::a' declared here" "note: flexarray class member" }
+  char a[];                     // { dg-message "destination object 'Bx::a' of size 0" "note: flexarray class member" }
 
   // Verify the warning for a constant.
   Bx () { a[0] = 0; }           // { dg-warning "\\\[-Wstringop-overflow" }
@@ -332,7 +332,7 @@ NOIPA void gbxi (int i)
 struct B0
 {
   char n;
-  char a[0];                    // { dg-message "at offset 0 to object 'B0::a' with size 0 declared here" "note: zero-length trailing array class member" }
+  char a[0];                    // { dg-message "destination object 'B0::a' of size 0" "note: zero-length trailing array class member" }
 
   B0 () { a[0] = 0; }           // { dg-warning "\\\[-Wstringop-overflow" }
 };
@@ -348,7 +348,7 @@ NOIPA void gb0 (void)
 struct B1
 {
   char n;
-  char a[1];                    // { dg-message "at offset 1 to object 'B1::a' with size 1 declared here" "note: one-element trailing array class member" }
+  char a[1];                    // { dg-message "at offset 1 into destination object 'B1::a' of size 1" "note: one-element trailing array class member" }
 
   B1 () { a[1] = 0; }           // { dg-warning "\\\[-Wstringop-overflow" }
 };
@@ -362,7 +362,7 @@ NOIPA void gb1 (void)
 
 struct B123
 {
-  char a[123];                  // { dg-message "at offset 123 to object 'B123::a' with size 123 declared here" "note: large trailing array class member" }
+  char a[123];                  // { dg-message "at offset 123 into destination object 'B123::a' of size 123" "note: large trailing array class member" }
 
   B123 () { a[123] = 0; }       // { dg-warning "\\\[-Wstringop-overflow" }
 };
@@ -376,7 +376,7 @@ NOIPA void gb123 (void)
 
 struct B234
 {
-  char a[234];                  // { dg-message "at offset 234 to object 'B234::a' with size 234 declared here" "note: large trailing array class member" }
+  char a[234];                  // { dg-message "at offset 234 into destination object 'B234::a' of size 234" "note: large trailing array class member" }
 
   B234 (int i) { a[i] = 0; }    // { dg-warning "\\\[-Wstringop-overflow" }
 };
diff --git a/gcc/testsuite/gcc.dg/Warray-bounds-43.c b/gcc/testsuite/gcc.dg/Warray-bounds-43.c
index 8892921157d..0f521a7250d 100644
--- a/gcc/testsuite/gcc.dg/Warray-bounds-43.c
+++ b/gcc/testsuite/gcc.dg/Warray-bounds-43.c
@@ -5,20 +5,9 @@
 
 #define NOIPA __attribute__ ((noipa))
 
-const char a0[] = "";
-const char a1[] = "1";
-const char a2[] = "12";
-const char a3[] = "123";
-const char a4[] = "1234";
-const char a5[] = "12345";
-const char a6[] = "123456";
-const char a7[] = "1234567";
-const char a8[] = "12345678";
 const char a9[] = "123456789";
 
-void f (const char*, ...);
-
-int i0, i1, i2, i3, i4, i5, i6, i7, i8;
+void sink (const char*, ...);
 
 NOIPA int g2 (int i)
 {
@@ -28,7 +17,7 @@ NOIPA int g2 (int i)
   const char *p1 = p0 + i;
   const char *p2 = p1 + i;
 
-  f (p0, p1, p2);
+  sink (p0, p1, p2);
 
   return p2[8];     // { dg-warning "\\\[-Warray-bounds]" }
 }
@@ -42,7 +31,7 @@ NOIPA int g3 (int i)
   const char *p2 = p1 + i;
   const char *p3 = p2 + i;
 
-  f (p0, p1, p2, p3);
+  sink (p0, p1, p2, p3);
 
   return p3[7];     // { dg-warning "\\\[-Warray-bounds]" }
 }
@@ -57,7 +46,7 @@ NOIPA int g4 (int i)
   const char *p3 = p2 + i;
   const char *p4 = p3 + i;
 
-  f (p0, p1, p2, p3, p4);
+  sink (p0, p1, p2, p3, p4);
 
   return p4[6];     // { dg-warning "\\\[-Warray-bounds]" }
 }
@@ -73,7 +62,7 @@ NOIPA int g5 (int i)
   const char *p4 = p3 + i;
   const char *p5 = p4 + i;
 
-  f (p0, p1, p2, p3, p4, p5);
+  sink (p0, p1, p2, p3, p4, p5);
 
   return p5[5];
 }
@@ -90,7 +79,7 @@ NOIPA int g6 (int i)
   const char *p5 = p4 + i;
   const char *p6 = p5 + i;
 
-  f (p0, p1, p2, p3, p4, p5, p6);
+  sink (p0, p1, p2, p3, p4, p5, p6);
 
   return p6[4];
 }
@@ -108,7 +97,7 @@ NOIPA int g7 (int i)
   const char *p6 = p5 + i;
   const char *p7 = p6 + i;
 
-  f (p0, p1, p2, p3, p4, p5, p6, p7);
+  sink (p0, p1, p2, p3, p4, p5, p6, p7);
 
   return p7[3];
 }
@@ -127,7 +116,7 @@ NOIPA int g8 (int i)
   const char *p7 = p6 + i;
   const char *p8 = p7 + i;
 
-  f (p0, p1, p2, p3, p4, p5, p6, p7, p8);
+  sink (p0, p1, p2, p3, p4, p5, p6, p7, p8);
 
   return p8[2];
 }
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 f91bcbe234c..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" "note" }
+  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-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/Wstringop-overflow-63.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-63.c
new file mode 100644
index 00000000000..c98721dd8c7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-63.c
@@ -0,0 +1,33 @@
+/* PR middle-end/92936 - missing warning on a past-the-end store to a PHI
+   Test case derived from gcc/opts-common.c.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+char* f (const void*, ...);
+
+const char *
+candidates_list_and_hint (const char *arg, char **str, const char *a[])
+{
+  size_t len = 0;
+  int i;
+
+  for (i = 0; a[i]; ++i)
+    len += __builtin_strlen (a[i]) + 1;
+
+  char *p = (char*)__builtin_malloc (len);
+  *str = p;
+
+  for (i = 0; a[i]; ++i)
+    {
+      len = __builtin_strlen (a[i]);
+      __builtin_memcpy (p, a[i], len);
+      p[len] = ' ';
+      p += len + 1;
+    }
+
+  p[-1] = '\0';     // { dg-bogus "\\\[-Wstringop-overflow" }
+
+  return f (arg, &a);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-64.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-64.c
new file mode 100644
index 00000000000..88b9d297525
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-64.c
@@ -0,0 +1,74 @@
+/* PR middle-end/92936 - missing warning on a past-the-end store to a PHI
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+void* memset (void*, int, size_t);
+
+extern char a3[3], a5[5], a9[9];
+
+extern int cnd[];
+
+void* f2 (void)
+{
+  char *p0 = cnd[0] ? a3 : 0;
+  char *p1 = cnd[1] ? a5 : p0;
+
+  return memset (p1, 0, 6);   // { dg-warning "writing 6 bytes into a region of size 5" }
+}
+
+void* f3 (void)
+{
+  char *p0 = cnd[0] ? a3 : 0;
+  char *p1 = cnd[1] ? a5 : 0;
+  char *p2 = cnd[2] ? p0 : p1;
+
+  return memset (p2, 0, 6);   // { dg-warning "writing 6 bytes into a region of size 5" }
+}
+
+void* f3_2 (void)
+{
+  char *p0 = cnd[0] ? a3 : 0;
+  char *p1 = cnd[1] ? a5 : 0;
+  char *p2 = cnd[2] ? p1 : p0;
+
+  return memset (p2, 0, 6);   // { dg-warning "writing 6 bytes into a region of size 5" }
+}
+
+void* f3_3 (void)
+{
+  char *p0 = cnd[0] ? a5 : 0;
+  char *p1 = cnd[1] ? p0 : a5;
+  char *p2 = cnd[2] ? p1 : p0;
+
+  return memset (p2, 0, 6);   // { dg-warning "writing 6 bytes into a region of size 5" }
+}
+
+void* f4 (void)
+{
+  char *p0 = cnd[0] ? a3 : 0;
+  char *p1 = cnd[1] ? a5 : 0;
+  char *p2 = cnd[2] ? p0 : 0;
+  char *p3 = cnd[3] ? p1 : p2;
+
+  return memset (p3, 0, 6);   // { dg-warning "writing 6 bytes into a region of size 5" }
+}
+
+void* f9 (void)
+{
+  char *p0 = cnd[0] ? a5 : 0;
+  char *p1 = cnd[1] ? a5 + 1 : 0;
+  char *p2 = cnd[2] ? a5 + 2 : 0;
+  char *p3 = cnd[3] ? a5 + 3 : 0;
+  char *p4 = cnd[4] ? a5 + 4 : 0;
+
+  char *p5 = cnd[5] ? p0 : p1;
+  char *p6 = cnd[6] ? p5 : p2;
+  char *p7 = cnd[7] ? p6 : p3;
+  char *p8 = cnd[8] ? p7 : p4;
+  char *p9 = cnd[9] ? p8 : p5;
+
+  return memset (p9, 0, 6);   // { dg-warning "writing 6 bytes into a region of size 5" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-7.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-7.c
new file mode 100644
index 00000000000..cb2addf3af4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-7.c
@@ -0,0 +1,124 @@
+/* Test to verify that --param ssa_name_def_chain_limit can be used to
+   limit the maximum number of SSA_NAME assignments the warning follows.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -Wno-array-bounds --param ssa-name-def-chain-limit=5" }  */
+
+#define NOIPA __attribute__ ((noipa))
+
+void* memset (void*, int, __SIZE_TYPE__);
+
+char a9[9];
+
+void sink (const char*, ...);
+
+NOIPA void g2 (int i)
+{
+  if (i < 1) i = 1;
+
+  char *p0 = a9;
+  char *p1 = p0 + i;
+  char *p2 = p1 + i;
+
+  sink (p0, p1, p2);
+
+  memset (p2, 0, 8);          // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+NOIPA void g3 (int i)
+{
+  if (i < 1) i = 1;
+
+  char *p0 = a9;
+  char *p1 = p0 + i;
+  char *p2 = p1 + i;
+  char *p3 = p2 + i;
+
+  sink (p0, p1, p2, p3);
+
+  memset (p3, 0, 7);          // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+NOIPA void g4 (int i)
+{
+  if (i < 1) i = 1;
+
+  char *p0 = a9;
+  char *p1 = p0 + i;
+  char *p2 = p1 + i;
+  char *p3 = p2 + i;
+  char *p4 = p3 + i;
+
+  sink (p0, p1, p2, p3, p4);
+
+  memset (p4, 0, 6);          // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+NOIPA void g5 (int i)
+{
+  if (i < 1) i = 1;
+
+  char *p0 = a9;
+  char *p1 = p0 + i;
+  char *p2 = p1 + i;
+  char *p3 = p2 + i;
+  char *p4 = p3 + i;
+  char *p5 = p4 + i;
+
+  sink (p0, p1, p2, p3, p4, p5);
+
+  memset (p5, 0, 5);          // { dg-warning "\\\[-Wstringop-overflow" }
+}
+
+NOIPA void g6 (int i)
+{
+  if (i < 1) i = 1;
+
+  char *p0 = a9;
+  char *p1 = p0 + i;
+  char *p2 = p1 + i;
+  char *p3 = p2 + i;
+  char *p4 = p3 + i;
+  char *p5 = p4 + i;
+  char *p6 = p5 + i;
+
+  sink (p0, p1, p2, p3, p4, p5, p6);
+
+  memset (p6, 0, 4);
+}
+
+NOIPA void g7 (int i)
+{
+  if (i < 1) i = 1;
+
+  char *p0 = a9;
+  char *p1 = p0 + i;
+  char *p2 = p1 + i;
+  char *p3 = p2 + i;
+  char *p4 = p3 + i;
+  char *p5 = p4 + i;
+  char *p6 = p5 + i;
+  char *p7 = p6 + i;
+
+  sink (p0, p1, p2, p3, p4, p5, p6, p7);
+
+  memset (p7, 0, 4);
+}
+
+NOIPA void g8 (int i)
+{
+  if (i < 1) i = 1;
+
+  char *p0 = a9;
+  char *p1 = p0 + i;
+  char *p2 = p1 + i;
+  char *p3 = p2 + i;
+  char *p4 = p3 + i;
+  char *p5 = p4 + i;
+  char *p6 = p5 + i;
+  char *p7 = p6 + i;
+  char *p8 = p7 + i;
+
+  sink (p0, p1, p2, p3, p4, p5, p6, p7, p8);
+
+  memset (p8, 0, 2);
+}
diff --git a/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c b/gcc/testsuite/gcc.dg/warn-strnlen-no-nul.c
index 02f6f3d5342..2afd2b5feeb 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" { xfail *-*-*} } */
+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 a5e78a89e65..c11ff8f2ff4 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).  */
@@ -4600,55 +4346,6 @@ handle_pointer_plus (gimple_stmt_iterator *gsi)
     }
 }
 
-/* Describes recursion limits used by count_nonzero_bytes.  */
-
-class ssa_name_limit_t
-{
-  bitmap visited;         /* Bitmap of visited SSA_NAMEs.  */
-  unsigned ssa_def_max;   /* Longest chain of SSA_NAMEs to follow.  */
-
-  /* Not copyable or assignable.  */
-  ssa_name_limit_t (ssa_name_limit_t&);
-  void operator= (ssa_name_limit_t&);
-
- public:
-
-  ssa_name_limit_t ()
-    : visited (NULL),
-    ssa_def_max (param_ssa_name_def_chain_limit) { }
-
-  int next_ssa_name (tree);
-
-  ~ssa_name_limit_t ()
-    {
-      if (visited)
-	BITMAP_FREE (visited);
-    }
-};
-
-/* If the SSA_NAME has already been "seen" return a positive value.
-   Otherwise add it to VISITED.  If the SSA_NAME limit has been
-   reached, return a negative value.  Otherwise return zero.  */
-
-int ssa_name_limit_t::next_ssa_name (tree ssa_name)
-{
-  if (!visited)
-    visited = BITMAP_ALLOC (NULL);
-
-  /* Return a positive value if SSA_NAME has already been visited.  */
-  if (!bitmap_set_bit (visited, SSA_NAME_VERSION (ssa_name)))
-    return 1;
-
-  /* Return a negative value to let caller avoid recursing beyond
-     the specified limit.  */
-  if (ssa_def_max == 0)
-    return -1;
-
-  --ssa_def_max;
-
-  return 0;
-}
-
 static bool
 count_nonzero_bytes_addr (tree, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT,
 			  unsigned [3], bool *, bool *, bool *,
@@ -4706,7 +4403,7 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
 	  /* Avoid processing an SSA_NAME that has already been visited
 	     or if an SSA_NAME limit has been reached.  Indicate success
 	     if the former and failure if the latter.  */
-	  if (int res = snlim.next_ssa_name (exp))
+	  if (int res = snlim.next_phi (exp))
 	    return res > 0;
 
 	  /* Determine the minimum and maximum from the PHI arguments.  */
@@ -4941,7 +4638,7 @@ count_nonzero_bytes_addr (tree exp, unsigned HOST_WIDE_INT offset,
 	  /* Avoid processing an SSA_NAME that has already been visited
 	     or if an SSA_NAME limit has been reached.  Indicate success
 	     if the former and failure if the latter.  */
-	  if (int res = snlim.next_ssa_name (exp))
+	  if (int res = snlim.next_phi (exp))
 	    return res > 0;
 
 	  /* Determine the minimum and maximum from the PHI arguments.  */

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

* Re: [PATCH] handle conditionals in -Wstringop-overflow et al. (PR 92936)
  2020-11-29 22:27   ` Martin Sebor
@ 2020-11-30 20:49     ` Jeff Law
  2020-12-01  0:58       ` Martin Sebor
  0 siblings, 1 reply; 6+ messages in thread
From: Jeff Law @ 2020-11-30 20:49 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches



On 11/29/20 3:27 PM, Martin Sebor wrote:
> On 11/13/20 2:34 PM, Jeff Law wrote:
>>
>> On 11/2/20 7:24 PM, Martin Sebor wrote:
>>> 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
>>>
>>> gcc-92936.diff
>>>
>>> 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.
>>
>> So my only significant concern here is the recursive nature and the
>> lack of a limiter for pathological cases.  We certainly run into
>> cases with thousands of PHI arguments and deep chains of PHIs feeding
>> other PHIs.  Can you put in a PARAM to limit the amount of recursion
>> and and PHI arguments you look at?  With that I think this is fine --
>> I consider it unlikely this patch is the root cause of the ICEs I
>> sent you earlier today from the tester since those failures are in
>> the array bounds checking bits.
>
> I've reused the same approach/class as in tree-ssa-strlen.c and
> after adjusting things that need to be adjusted and retesting
> with Binutils/GDB and Glibc committed the attached patch in
> r11-5523.
>
> That said, although the recursion hasn't been a problem (there's
> still code elsewhere that does this sort of traversal of the use-
> def chains that doesn't use the parameter), the subsequent patch
> that adds the cache makes it possible to reduce it to just trees
> (when the function is called in a GIMPLE pass to cache each
> pointer assignment).
>
> Martin
>
> gcc-92936.diff
>
OK.  I didn't necessarily expect that the recursion is causing problems
in our code base or in the testsuite.  It's not terribly unusual for
tests which cause these kinds of problems to be too big to include in
the testsuite.  It's also the case that these problems typically aren't
reported until after a release gets out into the wild and someone tries
there machine generated torturous code on the latest bits :-)

Thanks,
jeff


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

* Re: [PATCH] handle conditionals in -Wstringop-overflow et al. (PR 92936)
  2020-11-30 20:49     ` Jeff Law
@ 2020-12-01  0:58       ` Martin Sebor
  2020-12-01 23:52         ` Jeff Law
  0 siblings, 1 reply; 6+ messages in thread
From: Martin Sebor @ 2020-12-01  0:58 UTC (permalink / raw)
  To: Jeff Law, gcc-patches

On 11/30/20 1:49 PM, Jeff Law wrote:
> 
> 
> On 11/29/20 3:27 PM, Martin Sebor wrote:
>> On 11/13/20 2:34 PM, Jeff Law wrote:
>>>
>>> On 11/2/20 7:24 PM, Martin Sebor wrote:
>>>> 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
>>>>
>>>> gcc-92936.diff
>>>>
>>>> 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.
>>>
>>> So my only significant concern here is the recursive nature and the
>>> lack of a limiter for pathological cases.  We certainly run into
>>> cases with thousands of PHI arguments and deep chains of PHIs feeding
>>> other PHIs.  Can you put in a PARAM to limit the amount of recursion
>>> and and PHI arguments you look at?  With that I think this is fine --
>>> I consider it unlikely this patch is the root cause of the ICEs I
>>> sent you earlier today from the tester since those failures are in
>>> the array bounds checking bits.
>>
>> I've reused the same approach/class as in tree-ssa-strlen.c and
>> after adjusting things that need to be adjusted and retesting
>> with Binutils/GDB and Glibc committed the attached patch in
>> r11-5523.
>>
>> That said, although the recursion hasn't been a problem (there's
>> still code elsewhere that does this sort of traversal of the use-
>> def chains that doesn't use the parameter), the subsequent patch
>> that adds the cache makes it possible to reduce it to just trees
>> (when the function is called in a GIMPLE pass to cache each
>> pointer assignment).
>>
>> Martin
>>
>> gcc-92936.diff
>>
> OK.  I didn't necessarily expect that the recursion is causing problems
> in our code base or in the testsuite.  It's not terribly unusual for
> tests which cause these kinds of problems to be too big to include in
> the testsuite.  It's also the case that these problems typically aren't
> reported until after a release gets out into the wild and someone tries
> there machine generated torturous code on the latest bits :-)

What I meant was that the recursion in compute_objsize has been
there in various forms since at least GCC 8 (it's still in other
parts of GCC today), and it hasn't been a source of reported
problems.

I also never did understand how following a use-def chain of
SSA_NAMEs could have any but linear complexity.  For everything
except PHIs the traversal is strictly linear; for PHIs, setting
and testing the visited bitmap breaks cycles, again guaranteeing
linear complexity.  For reference, here are the numbers I put
together last year (this was for get_range_strlen_dynamic, not
compute_objsize):
https://gcc.gnu.org/pipermail/gcc-patches/2019-July/525000.html

Anyway, whether it's actually needed or not, the limit's there
now (in compute_objsize), and the caching should only make
reaching it that much less likely.

Martin

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

* Re: [PATCH] handle conditionals in -Wstringop-overflow et al. (PR 92936)
  2020-12-01  0:58       ` Martin Sebor
@ 2020-12-01 23:52         ` Jeff Law
  0 siblings, 0 replies; 6+ messages in thread
From: Jeff Law @ 2020-12-01 23:52 UTC (permalink / raw)
  To: Martin Sebor, gcc-patches



On 11/30/20 5:58 PM, Martin Sebor wrote:
>
> What I meant was that the recursion in compute_objsize has been
> there in various forms since at least GCC 8 (it's still in other
> parts of GCC today), and it hasn't been a source of reported
> problems.
Understood.  Could be due to a variety of factors.  Having the clamps is
still useful.

>
> I also never did understand how following a use-def chain of
> SSA_NAMEs could have any but linear complexity.  For everything
> except PHIs the traversal is strictly linear; for PHIs, setting
> and testing the visited bitmap breaks cycles, again guaranteeing
> linear complexity.  For reference, here are the numbers I put
> together last year (this was for get_range_strlen_dynamic, not
> compute_objsize):
> https://gcc.gnu.org/pipermail/gcc-patches/2019-July/525000.html
The worst of these cases tend to be when each block has thousands of
PHIs, each of which have thousands of arguments.   This kind of scenario
happens with computed gotos, setjmp/longjmp, EH and similar
mechansisms.  We've gone to great lengths to try and factor the gotos,
landing pads, etc to keep the complexity of the CFG (and indirectly the
complexity of the PHIs) to a minimum.   Bugzilla is riddled with all
kinds of these issues.  If you were to start wandering through them
you'd see the name Brad Lucier show up often ;-)  His codes were often
the motivating case for various clamps.

So the walk up the use-def chains isn't usually too bad, though in
extreme cases, even the linear walk gets expensive.

>
> Anyway, whether it's actually needed or not, the limit's there
> now (in compute_objsize), and the caching should only make
> reaching it that much less likely.
>
Thanks.

jeff


^ 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).