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
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.  */

Reply via email to