public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r11-3827] Generalize compute_objsize to return maximum size/offset instead of failing (PR middle-end/97023).
@ 2020-10-12 15:07 Martin Sebor
  0 siblings, 0 replies; only message in thread
From: Martin Sebor @ 2020-10-12 15:07 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:83685efd5fd1623cfc4e4c435ce2773d95d458d1

commit r11-3827-g83685efd5fd1623cfc4e4c435ce2773d95d458d1
Author: Martin Sebor <msebor@redhat.com>
Date:   Fri Oct 9 14:48:43 2020 -0600

    Generalize compute_objsize to return maximum size/offset instead of failing (PR middle-end/97023).
    
    Also resolves:
    PR middle-end/97342 - bogus -Wstringop-overflow with nonzero signed and unsigned offsets
    PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
    PR middle-end/96384 - bogus -Wstringop-overflow= storing into multidimensional array with index in range
    
    gcc/ChangeLog:
    
            PR middle-end/97342
            PR middle-end/97023
            PR middle-end/96384
            * builtins.c (access_ref::access_ref): Initialize new member.  Use
            new enum.
            (access_ref::size_remaining): Define new member function.
            (inform_access): Handle expressions referencing objects.
            (gimple_call_alloc_size): Call get_size_range instead of get_range.
            (gimple_call_return_array): New function.
            (get_range): Rename...
            (get_offset_range): ...to this.  Improve detection of ranges from
            types of expressions.
            (gimple_call_return_array): Adjust calls to get_range per above.
            (compute_objsize): Same.  Set maximum size or offset instead of
            failing for unknown objects and handle more kinds of expressions.
            (compute_objsize): Call access_ref::size_remaining.
            (compute_objsize): Have transitional wrapper fail for pointers
            into unknown objects.
            (expand_builtin_strncmp): Call access_ref::size_remaining and
            handle new cases.
            * builtins.h (access_ref::size_remaining): Declare new member function.
            (access_ref::set_max_size_range): Define new member function.
            (access_ref::add_ofset, access_ref::add_max_ofset): Same.
            (access_ref::add_base0): New data member.
            * calls.c (get_size_range): Change argument type.  Handle new
            condition.
            * calls.h (get_size_range): Adjust signature.
            (enum size_range_flags): Define new type.
            * gimple-ssa-warn-restrict.c (builtin_memref::builtin_memref): Correct
            argument to get_size_range.
            * tree-ssa-strlen.c (get_range): Handle anti-ranges.
            (maybe_warn_overflow): Check DECL_P before assuming it's one.
    
    gcc/testsuite/ChangeLog:
    
            PR middle-end/97342
            PR middle-end/97023
            PR middle-end/96384
            * c-c++-common/Wrestrict.c: Adjust comment.
            * gcc.dg/Wstringop-overflow-34.c: Remove xfail.
            * gcc.dg/Wstringop-overflow-43.c: Remove xfails.  Adjust regex patterns.
            * gcc.dg/pr51683.c: Prune out expected warning.
            * gcc.target/i386/pr60693.c: Same.
            * g++.dg/warn/Wplacement-new-size-8.C: New test.
            * gcc.dg/Wstringop-overflow-41.c: New test.
            * gcc.dg/Wstringop-overflow-44.s: New test.
            * gcc.dg/Wstringop-overflow-45.c: New test.
            * gcc.dg/Wstringop-overflow-46.c: New test.
            * gcc.dg/Wstringop-overflow-47.c: New test.
            * gcc.dg/Wstringop-overflow-49.c: New test.
            * gcc.dg/Wstringop-overflow-50.c: New test.
            * gcc.dg/Wstringop-overflow-51.c: New test.
            * gcc.dg/Wstringop-overflow-52.c: New test.
            * gcc.dg/Wstringop-overflow-53.c: New test.
            * gcc.dg/Wstringop-overflow-54.c: New test.
            * gcc.dg/Wstringop-overflow-55.c: New test.
            * gcc.dg/Wstringop-overread-5.c: New test.

Diff:
---
 gcc/builtins.c                                    | 659 ++++++++++++++++------
 gcc/builtins.h                                    |  41 +-
 gcc/calls.c                                       |  69 ++-
 gcc/calls.h                                       |  10 +-
 gcc/gimple-ssa-warn-restrict.c                    |   2 +-
 gcc/testsuite/c-c++-common/Wrestrict.c            |  13 +-
 gcc/testsuite/g++.dg/warn/Wplacement-new-size-8.C | 147 +++++
 gcc/testsuite/gcc.dg/Wstringop-overflow-34.c      |   2 +-
 gcc/testsuite/gcc.dg/Wstringop-overflow-41.c      | 120 ++++
 gcc/testsuite/gcc.dg/Wstringop-overflow-43.c      |   9 +-
 gcc/testsuite/gcc.dg/Wstringop-overflow-44.s      | 271 +++++++++
 gcc/testsuite/gcc.dg/Wstringop-overflow-45.c      | 255 +++++++++
 gcc/testsuite/gcc.dg/Wstringop-overflow-46.c      |  97 ++++
 gcc/testsuite/gcc.dg/Wstringop-overflow-47.c      |  69 +++
 gcc/testsuite/gcc.dg/Wstringop-overflow-49.c      | 146 +++++
 gcc/testsuite/gcc.dg/Wstringop-overflow-50.c      | 125 ++++
 gcc/testsuite/gcc.dg/Wstringop-overflow-51.c      |  34 ++
 gcc/testsuite/gcc.dg/Wstringop-overflow-52.c      |  62 ++
 gcc/testsuite/gcc.dg/Wstringop-overflow-53.c      | 116 ++++
 gcc/testsuite/gcc.dg/Wstringop-overflow-54.c      | 103 ++++
 gcc/testsuite/gcc.dg/Wstringop-overflow-55.c      |  97 ++++
 gcc/testsuite/gcc.dg/Wstringop-overread-5.c       |  76 +++
 gcc/testsuite/gcc.dg/pr51683.c                    |   3 +
 gcc/testsuite/gcc.target/i386/pr60693.c           |   3 +
 gcc/tree-ssa-strlen.c                             |  14 +-
 25 files changed, 2339 insertions(+), 204 deletions(-)

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 283c1e6674c..3a77da2b10c 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -183,8 +183,6 @@ static void maybe_emit_chk_warning (tree, enum built_in_function);
 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 get_range (tree, gimple *, signop, offset_int[2],
-		       range_query * = NULL);
 static bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
 
 unsigned HOST_WIDE_INT target_newline;
@@ -200,7 +198,7 @@ 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)
+: ref (), eval ([](tree x){ return x; }), trail1special (true), base0 (true)
 {
   /* Set to valid.  */
   offrng[0] = offrng[1] = 0;
@@ -215,7 +213,7 @@ access_ref::access_ref (tree bound /* = NULL_TREE */,
      set the bounds of the access to reflect both it and MINACCESS.
      BNDRNG[0] is the size of the minimum access.  */
   tree rng[2];
-  if (bound && get_size_range (bound, rng, true))
+  if (bound && get_size_range (bound, rng, SR_ALLOW_ZERO))
     {
       bndrng[0] = wi::to_offset (rng[0]);
       bndrng[1] = wi::to_offset (rng[1]);
@@ -223,6 +221,131 @@ access_ref::access_ref (tree bound /* = NULL_TREE */,
     }
 }
 
+/* Return the maximum amount of space remaining and if non-null, set
+   argument to the minimum.  */
+
+offset_int
+access_ref::size_remaining (offset_int *pmin /* = NULL */) const
+{
+  offset_int minbuf;
+  if (!pmin)
+    pmin = &minbuf;
+
+  /* add_offset() ensures the offset range isn't inverted.  */
+  gcc_checking_assert (offrng[0] <= offrng[1]);
+
+  if (base0)
+    {
+      /* The offset into referenced object is zero-based (i.e., it's
+	 not referenced by a pointer into middle of some unknown object).  */
+      if (offrng[0] < 0 && offrng[1] < 0)
+	{
+	  /* If the offset is negative the remaining size is zero.  */
+	  *pmin = 0;
+	  return 0;
+	}
+
+      if (sizrng[1] <= offrng[0])
+	{
+	  /* If the starting offset is greater than or equal to the upper
+	     bound on the size of the object, the space remaining is zero.
+	     As a special case, if it's equal, set *PMIN to -1 to let
+	     the caller know the offset is valid and just past the end.  */
+	  *pmin = sizrng[1] == offrng[0] ? -1 : 0;
+	  return 0;
+	}
+
+      /* Otherwise return the size minus the lower bound of the offset.  */
+      offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
+
+      *pmin = sizrng[0] - or0;
+      return sizrng[1] - or0;
+    }
+
+  /* The offset to the referenced object isn't zero-based (i.e., it may
+     refer to a byte other than the first.  The size of such an object
+     is constrained only by the size of the address space (the result
+     of max_object_size()).  */
+  if (sizrng[1] <= offrng[0])
+    {
+      *pmin = 0;
+      return 0;
+    }
+
+  offset_int or0 = offrng[0] < 0 ? 0 : offrng[0];
+
+  *pmin = sizrng[0] - or0;
+  return sizrng[1] - or0;
+}
+
+/* Add the range [MIN, MAX] to the offset range.  For known objects (with
+   zero-based offsets) at least one of whose offset's bounds is in range,
+   constrain the other (or both) to the bounds of the object (i.e., zero
+   and the upper bound of its size).  This improves the quality of
+   diagnostics.  */
+
+void access_ref::add_offset (const offset_int &min, const offset_int &max)
+{
+  if (min <= max)
+    {
+      /* To add an ordinary range just add it to the bounds.  */
+      offrng[0] += min;
+      offrng[1] += max;
+    }
+  else if (!base0)
+    {
+      /* To add an inverted range to an offset to an unknown object
+	 expand it to the maximum.  */
+      add_max_offset ();
+      return;
+    }
+  else
+    {
+      /* To add an inverted range to an offset to an known object set
+	 the upper bound to the maximum representable offset value
+	 (which may be greater than MAX_OBJECT_SIZE).
+	 The lower bound is either the sum of the current offset and
+	 MIN when abs(MAX) is greater than the former, or zero otherwise.
+	 Zero because then then inverted range includes the negative of
+	 the lower bound.  */
+      offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+      offrng[1] = maxoff;
+
+      if (max >= 0)
+	{
+	  offrng[0] = 0;
+	  return;
+	}
+
+      offrng[1] = maxoff;
+      offset_int absmax = wi::abs (max);
+      if (offrng[0] < absmax)
+	offrng[0] += min;
+      else
+	offrng[0] = 0;
+    }
+
+  if (!base0)
+    return;
+
+  /* When referencing a known object check to see if the offset computed
+     so far is in bounds... */
+  offset_int remrng[2];
+  remrng[1] = size_remaining (remrng);
+  if (remrng[1] > 0 || remrng[0] < 0)
+    {
+      /* ...if so, constrain it so that neither bound exceeds the size of
+	 the object.  Out of bounds offsets are left unchanged, and, for
+	 better or worse, become in bounds later.  They should be detected
+	 and diagnosed at the point they first become invalid by
+	 -Warray-bounds.  */
+      if (offrng[0] < 0)
+	offrng[0] = 0;
+      if (offrng[1] > sizrng[1])
+	offrng[1] = sizrng[1];
+    }
+}
+
 /* Return true if NAME starts with __builtin_ or __sync_.  */
 
 static bool
@@ -3757,15 +3880,24 @@ inform_access (const access_ref &ref, access_mode mode)
   if (!ref.ref)
     return;
 
-  /* Convert offset range and avoid including a zero range since it isn't
-     necessarily meaningful.  */
-  long long minoff = 0, maxoff = 0;
-  if (wi::fits_shwi_p (ref.offrng[0])
-      && wi::fits_shwi_p (ref.offrng[1]))
-    {
-      minoff = ref.offrng[0].to_shwi ();
-      maxoff = ref.offrng[1].to_shwi ();
-    }
+  /* 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 ();
+  else
+    minoff = ref.offrng[0] < 0 ? diff_min : diff_max;
+
+  if (wi::fits_shwi_p (ref.offrng[1]))
+    maxoff = ref.offrng[1].to_shwi ();
+
+  if (maxoff <= diff_min || maxoff >= diff_max)
+    /* Avoid mentioning an upper bound that's equal to or in excess
+       of the maximum of ptrdiff_t.  */
+    maxoff = minoff;
 
   /* Convert size range and always include it since all sizes are
      meaningful. */
@@ -3799,23 +3931,27 @@ inform_access (const access_ref &ref, access_mode mode)
 	sprintf (sizestr, "[%llu, %llu]", minsize, maxsize);
 
     }
-  else
+  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
+    return;
 
   if (mode == access_read_write || mode == access_write_only)
     {
-      if (DECL_P (ref.ref))
+      if (allocfn == NULL_TREE)
 	{
 	  if (minoff == maxoff)
 	    {
 	      if (minoff == 0)
-		inform (loc, "destination object %qD", ref.ref);
+		inform (loc, "destination object %qE", ref.ref);
 	      else
-		inform (loc, "at offset %lli into destination object %qD",
+		inform (loc, "at offset %wi into destination object %qE",
 			minoff, ref.ref);
 	    }
 	  else
-	    inform (loc, "at offset [%lli, %lli] into destination object %qD",
+	    inform (loc, "at offset [%wi, %wi] into destination object %qE",
 		    minoff, maxoff, ref.ref);
 	  return;
 	}
@@ -3827,12 +3963,12 @@ inform_access (const access_ref &ref, access_mode mode)
 		    sizestr, allocfn);
 	  else
 	    inform (loc,
-		    "at offset %lli into destination object of size %s "
+		    "at offset %wi into destination object of size %s "
 		    "allocated by %qE", minoff, sizestr, allocfn);
 	}
       else
 	inform (loc,
-		"at offset [%lli, %lli] into destination object of size %s "
+		"at offset [%wi, %wi] into destination object of size %s "
 		"allocated by %qE",
 		minoff, maxoff, sizestr, allocfn);
 
@@ -3846,11 +3982,11 @@ inform_access (const access_ref &ref, access_mode mode)
 	  if (minoff == 0)
 	    inform (loc, "source object %qD", ref.ref);
 	  else
-	    inform (loc, "at offset %lli into source object %qD",
+	    inform (loc, "at offset %wi into source object %qD",
 		    minoff, ref.ref);
 	}
       else
-	inform (loc, "at offset [%lli, %lli] into source object %qD",
+	inform (loc, "at offset [%wi, %wi] into source object %qD",
 		minoff, maxoff, ref.ref);
       return;
     }
@@ -3862,12 +3998,12 @@ inform_access (const access_ref &ref, access_mode mode)
 		sizestr, allocfn);
       else
 	inform (loc,
-		"at offset %lli into source object of size %s "
+		"at offset %wi into source object of size %s "
 		"allocated by %qE", minoff, sizestr, allocfn);
     }
   else
     inform (loc,
-	    "at offset [%lli, %lli] into source object of size %s "
+	    "at offset [%wi, %wi] into source object of size %s "
 	    "allocated by %qE",
 	    minoff, maxoff, sizestr, allocfn);
 }
@@ -4152,8 +4288,14 @@ check_access (tree exp, tree dstwrite,
 		   && range[0]
 		   && TREE_CODE (slen) == INTEGER_CST
 		   && tree_int_cst_lt (slen, range[0]));
-
-  if (!overread && pad && pad->src.sizrng[1] >= 0 && pad->src.offrng[0] >= 0)
+  /* If none is determined try to get a better answer based on the details
+     in PAD.  */
+  if (!overread
+      && pad
+      && pad->src.sizrng[1] >= 0
+      && pad->src.offrng[0] >= 0
+      && (pad->src.offrng[1] < 0
+	  || pad->src.offrng[0] <= pad->src.offrng[1]))
     {
       /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
 	 PAD is nonnull and BNDRNG is valid.  */
@@ -4215,7 +4357,7 @@ check_read_access (tree exp, tree src, tree bound /* = NULL_TREE */,
 
 tree
 gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
-			range_query *rvals /* = NULL */)
+			range_query * /* = NULL */)
 {
   if (!stmt)
     return NULL_TREE;
@@ -4267,14 +4409,17 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
   if (!rng1)
     rng1 = rng1_buf;
 
+  /* Use maximum precision to avoid overflow below.  */
   const int prec = ADDR_MAX_PRECISION;
-  const tree size_max = TYPE_MAX_VALUE (sizetype);
-  if (!get_range (size, stmt, rng1, rvals))
-    {
-      /* Use the full non-negative range on failure.  */
-      rng1[0] = wi::zero (prec);
-      rng1[1] = wi::to_wide (size_max, prec);
-    }
+
+  {
+    tree r[2];
+    /* Determine the largest valid range size, including zero.  */
+    if (!get_size_range (size, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+      return NULL_TREE;
+    rng1[0] = wi::to_wide (r[0], prec);
+    rng1[1] = wi::to_wide (r[1], prec);
+  }
 
   if (argidx2 > nargs && TREE_CODE (size) == INTEGER_CST)
     return fold_convert (sizetype, size);
@@ -4283,26 +4428,24 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
      of the upper bounds as a constant.  Ignore anti-ranges.  */
   tree n = argidx2 < nargs ? gimple_call_arg (stmt, argidx2) : integer_one_node;
   wide_int rng2[2];
-	  if (!get_range (n, stmt, rng2, rvals))
-    {
+  {
+    tree r[2];
       /* As above, use the full non-negative range on failure.  */
-      rng2[0] = wi::zero (prec);
-      rng2[1] = wi::to_wide (size_max, prec);
-    }
-
-  /* Extend to the maximum precision to avoid overflow.  */
-  rng1[0] = wide_int::from (rng1[0], prec, UNSIGNED);
-  rng1[1] = wide_int::from (rng1[1], prec, UNSIGNED);
-  rng2[0] = wide_int::from (rng2[0], prec, UNSIGNED);
-  rng2[1] = wide_int::from (rng2[1], prec, UNSIGNED);
+    if (!get_size_range (n, r, SR_ALLOW_ZERO | SR_USE_LARGEST))
+      return NULL_TREE;
+    rng2[0] = wi::to_wide (r[0], prec);
+    rng2[1] = wi::to_wide (r[1], prec);
+  }
 
   /* Compute products of both bounds for the caller but return the lesser
      of SIZE_MAX and the product of the upper bounds as a constant.  */
   rng1[0] = rng1[0] * rng2[0];
   rng1[1] = rng1[1] * rng2[1];
+
+  const tree size_max = TYPE_MAX_VALUE (sizetype);
   if (wi::gtu_p (rng1[1], wi::to_wide (size_max, prec)))
     {
-      rng1[1] = wi::to_wide (size_max);
+      rng1[1] = wi::to_wide (size_max, prec);
       return size_max;
     }
 
@@ -4315,7 +4458,8 @@ gimple_call_alloc_size (gimple *stmt, wide_int rng1[2] /* = NULL */,
    Return the function parameter on success and null otherwise.  */
 
 tree
-gimple_parm_array_size (tree ptr, wide_int rng[2], range_query * /* = NULL */)
+gimple_parm_array_size (tree ptr, wide_int rng[2],
+			range_query * /* = NULL */)
 {
   /* For a function argument try to determine the byte size of the array
      from the current function declaratation (e.g., attribute access or
@@ -4363,27 +4507,43 @@ gimple_parm_array_size (tree ptr, wide_int rng[2], range_query * /* = NULL */)
   return var;
 }
 
-/* Wrapper around the wide_int overload of get_range.  Returns the same
-   result but accepts offset_int instead.  */
+/* Wrapper around the wide_int overload of get_range that accepts
+   offset_int instead.  For middle end expressions returns the same
+   result.  For a subset of nonconstamt expressions emitted by the front
+   end determines a more precise range than would be possible otherwise.  */
 
 static bool
-get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
-	   range_query *rvals /* = NULL */)
+get_offset_range (tree x, gimple *stmt, offset_int r[2], range_query *rvals)
 {
-  tree type = TREE_TYPE (x);
-  if (TREE_CODE (x) != INTEGER_CST
-      && TREE_CODE (x) != SSA_NAME)
+  offset_int add = 0;
+  if (TREE_CODE (x) == PLUS_EXPR)
     {
-      if (TYPE_UNSIGNED (type))
+      /* Handle constant offsets in pointer addition expressions seen
+	 n the front end IL.  */
+      tree op = TREE_OPERAND (x, 1);
+      if (TREE_CODE (op) == INTEGER_CST)
 	{
-	  if (sgn == SIGNED)
-	    type = signed_type_for (type);
+	  op = fold_convert (signed_type_for (TREE_TYPE (op)), op);
+	  add = wi::to_offset (op);
+	  x = TREE_OPERAND (x, 0);
 	}
-      else if (sgn == UNSIGNED)
-	type = unsigned_type_for (type);
+    }
+
+  if (TREE_CODE (x) == NOP_EXPR)
+    /* Also handle conversions to sizetype seen in the front end IL.  */
+    x = TREE_OPERAND (x, 0);
+
+  tree type = TREE_TYPE (x);
 
-      r[0] = wi::to_offset (TYPE_MIN_VALUE (type));
-      r[1] = wi::to_offset (TYPE_MAX_VALUE (type));
+   if (TREE_CODE (x) != INTEGER_CST
+      && TREE_CODE (x) != SSA_NAME)
+    {
+      if (TYPE_UNSIGNED (type)
+	  && TYPE_PRECISION (type) == TYPE_PRECISION (sizetype))
+	type = signed_type_for (type);
+
+      r[0] = wi::to_offset (TYPE_MIN_VALUE (type)) + add;
+      r[1] = wi::to_offset (TYPE_MAX_VALUE (type)) + add;
       return x;
     }
 
@@ -4391,6 +4551,7 @@ get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
   if (!get_range (x, stmt, wr, rvals))
     return false;
 
+  signop sgn = SIGNED;
   /* Only convert signed integers or unsigned sizetype to a signed
      offset and avoid converting large positive values in narrower
      types to negative offsets.  */
@@ -4403,6 +4564,83 @@ get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
   return true;
 }
 
+/* Return the argument that the call STMT to a built-in function returns
+   or null if it doesn't.  On success, set OFFRNG[] to the range of offsets
+   from the argument reflected in the value returned by the built-in if it
+   can be determined, otherwise to 0 and HWI_M1U respectively.  */
+
+static tree
+gimple_call_return_array (gimple *stmt, offset_int offrng[2],
+			  range_query *rvals)
+{
+  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL)
+      || gimple_call_num_args (stmt) < 1)
+    return NULL_TREE;
+
+  tree fn = gimple_call_fndecl (stmt);
+  switch (DECL_FUNCTION_CODE (fn))
+    {
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMSET:
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      offrng[0] = offrng[1] = 0;
+      return gimple_call_arg (stmt, 0);
+
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+      {
+	tree off = gimple_call_arg (stmt, 2);
+	if (!get_offset_range (off, stmt, offrng, rvals))
+	  {
+	    offrng[0] = 0;
+	    offrng[1] = HOST_WIDE_INT_M1U;
+	  }
+	return gimple_call_arg (stmt, 0);
+      }
+
+    case BUILT_IN_MEMCHR:
+      {
+	tree off = gimple_call_arg (stmt, 2);
+	if (get_offset_range (off, stmt, offrng, rvals))
+	  offrng[0] = 0;
+	else
+	  {
+	    offrng[0] = 0;
+	    offrng[1] = HOST_WIDE_INT_M1U;
+	  }
+	return gimple_call_arg (stmt, 0);
+      }
+
+    case BUILT_IN_STRCHR:
+    case BUILT_IN_STRRCHR:
+    case BUILT_IN_STRSTR:
+      {
+	offrng[0] = 0;
+	offrng[1] = HOST_WIDE_INT_M1U;
+      }
+      return gimple_call_arg (stmt, 0);
+
+    default:
+      break;
+    }
+
+  return NULL_TREE;
+}
+
 /* 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).
@@ -4412,7 +4650,8 @@ get_range (tree x, gimple *stmt, signop sgn, offset_int r[2],
    the object(s).
    VISITED 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 the size cannot be determined.
+   Returns true on success, false when a meaningful size (or range)
+   cannot be determined.
 
    The function is intended for diagnostics and should not be used
    to influence code generation or optimization.  */
@@ -4429,25 +4668,40 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 
   if (DECL_P (ptr))
     {
-      /* Bail if the reference is to the pointer itself (as opposed
-	 to what it points to).  */
+      pref->ref = ptr;
+
       if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
-	return false;
+	{
+	  /* Set the maximum size if the reference is to the pointer
+	     itself (as opposed to what it points to).  */
+	  pref->set_max_size_range ();
+	  return true;
+	}
 
-      pref->ref = ptr;
       if (tree size = decl_init_size (ptr, false))
 	if (TREE_CODE (size) == INTEGER_CST)
 	  {
 	    pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
 	    return true;
 	  }
-      pref->sizrng[0] = 0;
-      pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+
+      pref->set_max_size_range ();
       return true;
     }
 
   const tree_code code = TREE_CODE (ptr);
 
+  if (code == BIT_FIELD_REF)
+    {
+      tree ref = TREE_OPERAND (ptr, 0);
+      if (!compute_objsize (ref, ostype, pref, visited, rvals))
+	return false;
+
+      offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
+      pref->add_offset (off / BITS_PER_UNIT);
+      return true;
+    }
+
   if (code == COMPONENT_REF)
     {
       tree ref = TREE_OPERAND (ptr, 0);
@@ -4455,27 +4709,29 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 
       if (ostype == 0)
 	{
-	  /* For raw memory functions like memcpy bail if the size
-	     of the enclosing object cannot be determined.  */
-	  if (!compute_objsize (ref, ostype, pref, visited, rvals)
-	      || !pref->ref)
+	  /* 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))
 	    return false;
 
 	  /* Otherwise, use the size of the enclosing object and add
 	     the offset of the member to the offset computed so far.  */
 	  tree offset = byte_position (field);
-	  if (TREE_CODE (offset) != INTEGER_CST)
-	    return false;
-	  offset_int off = wi::to_offset (offset);
-	  pref->offrng[0] += off;
-	  pref->offrng[1] += off;
+	  if (TREE_CODE (offset) == INTEGER_CST)
+	    pref->add_offset (wi::to_offset (offset));
+	  else
+	    pref->add_max_offset ();
 	  return true;
 	}
 
-      /* Bail if the reference is to the pointer itself (as opposed
-	 to what it points to).  */
       if (!addr && POINTER_TYPE_P (TREE_TYPE (field)))
-	return false;
+	{
+	  /* Set maximum size if the reference is to the pointer member
+	     itself (as opposed to what it points to).  */
+	  pref->set_max_size_range ();
+	  return true;
+	}
 
       pref->ref = field;
 
@@ -4530,9 +4786,12 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 
       offset_int orng[2];
       tree off = pref->eval (TREE_OPERAND (ptr, 1));
-      if (!get_range (off, NULL, SIGNED, orng, rvals))
-	/* Fail unless the size of the object is zero.  */
-	return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1];
+      if (!get_offset_range (off, NULL, orng, rvals))
+	{
+	  /* Set ORNG to the maximum offset representable in ptrdiff_t.  */
+	  orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+	  orng[0] = -orng[1] - 1;
+	}
 
       if (TREE_CODE (ptr) == ARRAY_REF)
 	{
@@ -4551,7 +4810,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 	  tree eltype = TREE_TYPE (ptr);
 	  tree tpsize = TYPE_SIZE_UNIT (eltype);
 	  if (!tpsize || TREE_CODE (tpsize) != INTEGER_CST)
-	    return false;
+	    {
+	      pref->add_max_offset ();
+	      return true;
+	    }
 
 	  offset_int sz = wi::to_offset (tpsize);
 	  orng[0] *= sz;
@@ -4578,12 +4840,44 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 	    }
 	}
 
-      pref->offrng[0] += orng[0];
-      pref->offrng[1] += orng[1];
+      pref->add_offset (orng[0], orng[1]);
+      return true;
+    }
 
+  if (code == TARGET_MEM_REF)
+    {
+      tree ref = TREE_OPERAND (ptr, 0);
+      if (!compute_objsize (ref, ostype, pref, visited, rvals))
+	return false;
+
+      /* TODO: Handle remaining operands.  Until then, add maximum offset.  */
+      pref->ref = ptr;
+      pref->add_max_offset ();
+      return true;
+    }
+
+  if (code == INTEGER_CST)
+    {
+      /* Pointer constants other than null are most likely the result
+	 of erroneous null pointer addition/subtraction.  Set size to
+	 zero.  For null pointers, set size to the maximum for now
+	 since those may be the result of jump threading.  */
+      if (integer_zerop (ptr))
+	pref->set_max_size_range ();
+      else
+	pref->sizrng[0] = pref->sizrng[1] = 0;
+      pref->ref = ptr;
+
+      return true;
+    }
+
+  if (code == STRING_CST)
+    {
+      pref->sizrng[0] = pref->sizrng[1] = TREE_STRING_LENGTH (ptr);
       return true;
     }
-  else if (code == POINTER_PLUS_EXPR)
+
+  if (code == POINTER_PLUS_EXPR)
     {
       tree ref = TREE_OPERAND (ptr, 0);
       if (!compute_objsize (ref, ostype, pref, visited, rvals))
@@ -4591,16 +4885,14 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
 
       offset_int orng[2];
       tree off = pref->eval (TREE_OPERAND (ptr, 1));
-      if (!get_range (off, NULL, SIGNED, orng, rvals))
-	/* Fail unless the size of the object is zero.  */
-	return pref->sizrng[0] == 0 && pref->sizrng[0] == pref->sizrng[1];
-
-      pref->offrng[0] += orng[0];
-      pref->offrng[1] += orng[1];
-
+      if (get_offset_range (off, NULL, orng, rvals))
+	pref->add_offset (orng[0], orng[1]);
+      else
+	pref->add_max_offset ();
       return true;
     }
-  else if (code == VIEW_CONVERT_EXPR)
+
+  if (code == VIEW_CONVERT_EXPR)
     {
       ptr = TREE_OPERAND (ptr, 0);
       return compute_objsize (ptr, ostype, pref, visited, rvals);
@@ -4612,17 +4904,53 @@ compute_objsize (tree ptr, int ostype, access_ref *pref, bitmap *visited,
       if (is_gimple_call (stmt))
 	{
 	  /* If STMT is a call to an allocation function get the size
-	     from its argument(s).  If successful, also set *PDECL to
-	     PTR for the caller to include in diagnostics.  */
+	     from its argument(s).  If successful, also set *PREF->REF
+	     to PTR for the caller to include in diagnostics.  */
 	  wide_int wr[2];
 	  if (gimple_call_alloc_size (stmt, wr, rvals))
 	    {
 	      pref->ref = ptr;
 	      pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
 	      pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
-	      return true;
+	      /* Constrain both bounds to a valid size.  */
+	      offset_int maxsize = wi::to_offset (max_object_size ());
+	      if (pref->sizrng[0] > maxsize)
+		pref->sizrng[0] = maxsize;
+	      if (pref->sizrng[1] > maxsize)
+		pref->sizrng[1] = maxsize;
 	    }
-	  return false;
+	  else
+	    {
+	      /* For functions known to return one of their pointer arguments
+		 try to determine what the returned pointer points to, and on
+		 success add OFFRNG which was set to the offset added by
+		 the function (e.g., memchr) to the overall offset.  */
+	      offset_int offrng[2];
+	      if (tree ret = gimple_call_return_array (stmt, offrng, rvals))
+		{
+		  if (!compute_objsize (ret, ostype, pref, visited, rvals))
+		    return false;
+
+		  /* Cap OFFRNG[1] to at most the remaining size of
+		     the object.  */
+		  offset_int remrng[2];
+		  remrng[1] = pref->size_remaining (remrng);
+		  if (remrng[1] < offrng[1])
+		    offrng[1] = remrng[1];
+		  pref->add_offset (offrng[0], offrng[1]);
+		}
+	      else
+		{
+		  /* For other calls that might return arbitrary pointers
+		     including into the middle of objects set the size
+		     range to maximum, clear PREF->BASE0, and also set
+		     PREF->REF to include in diagnostics.  */
+		  pref->set_max_size_range ();
+		  pref->base0 = false;
+		  pref->ref = ptr;
+		}
+	    }
+	  return true;
 	}
 
       if (gimple_nop_p (stmt))
@@ -4631,62 +4959,74 @@ 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];
-	  tree ref = gimple_parm_array_size (ptr, wr, rvals);
-	  if (!ref)
-	    return NULL_TREE;
-	  pref->ref = ref;
-	  pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
-	  pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+	  if (tree ref = gimple_parm_array_size (ptr, wr, rvals))
+	    {
+	      pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
+	      pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+	      pref->ref = ref;
+	      return true;
+	    }
+
+	  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 (!is_gimple_assign (stmt))
-	return false;
+	{
+	  /* Clear BASE0 since the assigned pointer might point into
+	     the middle of the object, set the maximum size range and,
+	     if the SSA_NAME refers to a function argumnent, set
+	     PREF->REF to it.  */
+	  pref->base0 = false;
+	  pref->set_max_size_range ();
+	  if (tree var = SSA_NAME_VAR (ptr))
+	    if (TREE_CODE (var) == PARM_DECL)
+	      pref->ref = var;
+	  return true;
+	}
 
       ptr = gimple_assign_rhs1 (stmt);
 
       tree_code code = gimple_assign_rhs_code (stmt);
-      if (TREE_CODE (TREE_TYPE (ptr)) != POINTER_TYPE)
-	/* Avoid conversions from non-pointers.  */
-	return false;
 
-      if (code == POINTER_PLUS_EXPR)
+      if (code == POINTER_PLUS_EXPR
+	  && TREE_CODE (TREE_TYPE (ptr)) == POINTER_TYPE)
 	{
-	  /* If the the offset in the expression can be determined use
-	     it to adjust the overall offset.  Otherwise, set the overall
-	     offset to the maximum.  */
+	  /* Compute the size of the object first. */
+	  if (!compute_objsize (ptr, ostype, pref, visited, rvals))
+	    return false;
+
 	  offset_int orng[2];
 	  tree off = gimple_assign_rhs2 (stmt);
-	  if (!get_range (off, stmt, SIGNED, orng, rvals))
-	    {
-	      orng[0] = wi::to_offset (TYPE_MIN_VALUE (ptrdiff_type_node));
-	      orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
-	    }
-
-	  pref->offrng[0] += orng[0];
-	  pref->offrng[1] += orng[1];
+	  if (get_offset_range (off, stmt, orng, rvals))
+	    pref->add_offset (orng[0], orng[1]);
+	  else
+	    pref->add_max_offset ();
+	  return true;
 	}
-      else if (code != ADDR_EXPR)
-	return false;
 
-      return compute_objsize (ptr, ostype, pref, visited, rvals);
-    }
-
-  tree type = TREE_TYPE (ptr);
-  type = TYPE_MAIN_VARIANT (type);
-  if (TREE_CODE (ptr) == ADDR_EXPR)
-    ptr = TREE_OPERAND (ptr, 0);
+      if (code == ADDR_EXPR)
+	return compute_objsize (ptr, ostype, pref, visited, rvals);
 
-  if (TREE_CODE (type) == ARRAY_TYPE
-      && !array_at_struct_end_p (ptr))
-    {
-      if (tree size = TYPE_SIZE_UNIT (type))
-	return get_range (size, NULL, UNSIGNED, pref->sizrng, 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;
     }
 
-  return false;
+  /* Assume all other expressions point into an unknown object
+     of the maximum valid size.  */
+  pref->base0 = false;
+  pref->set_max_size_range ();
+  return true;
 }
 
 /* A "public" wrapper around the above.  Clients should use this overload
@@ -4707,27 +5047,10 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
   if (!success)
     return NULL_TREE;
 
-  if (pref->offrng[1] < pref->offrng[0])
-    {
-      if (pref->offrng[1] < 0
-	  && pref->sizrng[1] <= pref->offrng[0])
-	return size_zero_node;
-
-      return wide_int_to_tree (sizetype, pref->sizrng[1]);
-    }
-
-  if (pref->offrng[0] < 0)
-    {
-      if (pref->offrng[1] < 0)
-	return size_zero_node;
-
-      pref->offrng[0] = 0;
-    }
-
-  if (pref->sizrng[1] <= pref->offrng[0])
-    return size_zero_node;
-
-  return wide_int_to_tree (sizetype, pref->sizrng[1] - pref->offrng[0]);
+  offset_int maxsize = pref->size_remaining ();
+  if (pref->base0 && pref->offrng[0] < 0 && pref->offrng[1] >= 0)
+    pref->offrng[0] = 0;
+  return wide_int_to_tree (sizetype, maxsize);
 }
 
 /* Transitional wrapper around the above.  The function should be removed
@@ -4735,13 +5058,13 @@ compute_objsize (tree ptr, int ostype, access_ref *pref,
 
 tree
 compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
-		 tree *poff /* = NULL */, class range_query *rvals /* = NULL */)
+		 tree *poff /* = NULL */, range_query *rvals /* = NULL */)
 {
   /* Set the initial offsets to zero and size to negative to indicate
      none has been computed yet.  */
   access_ref ref;
   tree size = compute_objsize (ptr, ostype, &ref, rvals);
-  if (!size)
+  if (!size || !ref.base0)
     return NULL_TREE;
 
   if (pdecl)
@@ -6020,13 +6343,21 @@ expand_builtin_strncmp (tree exp, ATTRIBUTE_UNUSED rtx target,
 	  tree size2 = compute_objsize (arg2, 1, &ref2);
 	  tree func = get_callee_fndecl (exp);
 
-	  if (size1 && size2)
+	  if (size1 && size2 && bndrng[0] && !integer_zerop (bndrng[0]))
 	    {
-	      tree maxsize = tree_int_cst_le (size1, size2) ? size2 : size1;
-
-	      if (tree_int_cst_lt (maxsize, bndrng[0]))
+	      offset_int rem1 = ref1.size_remaining ();
+	      offset_int rem2 = ref2.size_remaining ();
+	      if (rem1 == 0 || rem2 == 0)
 		maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp, func,
-				      bndrng, maxsize);
+				      bndrng, integer_zero_node);
+	      else
+		{
+		  offset_int maxrem = wi::max (rem1, rem2, UNSIGNED);
+		  if (maxrem < wi::to_offset (bndrng[0]))
+		    maybe_warn_for_bound (OPT_Wstringop_overread, loc, exp,
+					  func, bndrng,
+					  wide_int_to_tree (sizetype, maxrem));
+		}
 	    }
 	  else if (bndrng[0]
 		   && !integer_zerop (bndrng[0])
diff --git a/gcc/builtins.h b/gcc/builtins.h
index f226c63e198..c09f36da02b 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -180,15 +180,45 @@ struct access_ref
     return offrng[0] == 0 && offrng[1] == 0;
   }
 
-  /* Return true if OFFRNG is bounded to a subrange of possible offset
-     values.  */
+  /* Return true if OFFRNG is bounded to a subrange of offset values
+     valid for the largest possible object.  */
   bool offset_bounded () const;
 
+  /* Return the maximum amount of space remaining and if non-null, set
+     argument to the minimum.  */
+  offset_int size_remaining (offset_int * = NULL) const;
+
+  /* Set the size range to the maximum.  */
+  void set_max_size_range ()
+  {
+    sizrng[0] = 0;
+    sizrng[1] = wi::to_offset (max_object_size ());
+  }
+
+  /* Add OFF to the offset range.  */
+  void add_offset (const offset_int &off)
+  {
+    add_offset (off, off);
+  }
+
+  /* Add the range [MIN, MAX] to the offset range.  */
+  void add_offset (const offset_int &, const offset_int &);
+
+  /* Add the maximum representable offset to the offset range.  */
+  void add_max_offset ()
+  {
+    offset_int maxoff = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+    add_offset (-maxoff - 1, maxoff);
+  }
+
   /* Used to fold integer expressions when called from front ends.  */
   tree (*eval)(tree);
   /* Set if trailing one-element arrays should be treated as flexible
      array members.  */
   bool trail1special;
+  /* Set if valid offsets must start at zero (for declared and allocated
+     objects but not for others referenced by pointers).  */
+  bool base0;
 };
 
 /* Describes a pair of references used in an access by built-in
@@ -216,11 +246,10 @@ 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 compute_objsize (tree, int, access_ref *, range_query * = NULL);
 extern tree compute_objsize (tree, int, tree * = NULL, tree * = NULL,
 			     range_query * = NULL);
-extern tree compute_objsize (tree, int, access_ref *, range_query * = NULL);
-
-extern bool check_access (tree, tree, tree, tree, tree, access_mode,
-			  const access_data * = NULL);
+extern bool check_access (tree, tree, tree, tree, tree,
+			  access_mode, const access_data * = NULL);
 
 #endif /* GCC_BUILTINS_H */
diff --git a/gcc/calls.c b/gcc/calls.c
index 93da3d6256e..d3120b23f60 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -1235,14 +1235,16 @@ alloc_max_size (void)
    after adjusting it if necessary to make EXP a represents a valid size
    of object, or a valid size argument to an allocation function declared
    with attribute alloc_size (whose argument may be signed), or to a string
-   manipulation function like memset.  When ALLOW_ZERO is true, allow
-   returning a range of [0, 0] for a size in an anti-range [1, N] where
-   N > PTRDIFF_MAX.  A zero range is a (nearly) invalid argument to
-   allocation functions like malloc but it is a valid argument to
-   functions like memset.  */
+   manipulation function like memset.
+   When ALLOW_ZERO is set in FLAGS, allow returning a range of [0, 0] for
+   a size in an anti-range [1, N] where N > PTRDIFF_MAX.  A zero range is
+   a (nearly) invalid argument to allocation functions like malloc but it
+   is a valid argument to functions like memset.
+   When USE_LARGEST is set in FLAGS set RANGE to the largest valid subrange
+   in a multi-range, otherwise to the smallest valid subrange.  */
 
 bool
-get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
+get_size_range (tree exp, tree range[2], int flags /* = 0 */)
 {
   if (!exp)
     return false;
@@ -1314,25 +1316,50 @@ get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
 	      min = wi::zero (expprec);
 	    }
 	}
-      else if (wi::eq_p (0, min - 1))
+      else
 	{
-	  /* EXP is unsigned and not in the range [1, MAX].  That means
-	     it's either zero or greater than MAX.  Even though 0 would
-	     normally be detected by -Walloc-zero, unless ALLOW_ZERO
-	     is true, set the range to [MAX, TYPE_MAX] so that when MAX
-	     is greater than the limit the whole range is diagnosed.  */
-	  if (allow_zero)
-	    min = max = wi::zero (expprec);
-	  else
+	  wide_int maxsize = wi::to_wide (max_object_size ());
+	  min = wide_int::from (min, maxsize.get_precision (), UNSIGNED);
+	  max = wide_int::from (max, maxsize.get_precision (), UNSIGNED);
+	  if (wi::eq_p (0, min - 1))
 	    {
+	      /* EXP is unsigned and not in the range [1, MAX].  That means
+		 it's either zero or greater than MAX.  Even though 0 would
+		 normally be detected by -Walloc-zero, unless ALLOW_ZERO
+		 is set, set the range to [MAX, TYPE_MAX] so that when MAX
+		 is greater than the limit the whole range is diagnosed.  */
+	      wide_int maxsize = wi::to_wide (max_object_size ());
+	      if (flags & SR_ALLOW_ZERO)
+		{
+		  if (wi::leu_p (maxsize, max + 1)
+		      || !(flags & SR_USE_LARGEST))
+		    min = max = wi::zero (expprec);
+		  else
+		    {
+		      min = max + 1;
+		      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+		    }
+		}
+	      else
+		{
+		  min = max + 1;
+		  max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+		}
+	    }
+	  else if ((flags & SR_USE_LARGEST)
+		   && wi::ltu_p (max + 1, maxsize))
+	    {
+	      /* When USE_LARGEST is set and the larger of the two subranges
+		 is a valid size, use it...  */
 	      min = max + 1;
-	      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	      max = maxsize;
+	    }
+	  else
+	    {
+	      /* ...otherwise use the smaller subrange.  */
+	      max = min - 1;
+	      min = wi::zero (expprec);
 	    }
-	}
-      else
-	{
-	  max = min - 1;
-	  min = wi::zero (expprec);
 	}
     }
 
diff --git a/gcc/calls.h b/gcc/calls.h
index dfb951ca45b..644ec45d92c 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -133,7 +133,15 @@ extern bool reference_callee_copied (CUMULATIVE_ARGS *,
 extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
 extern tree get_attr_nonstring_decl (tree, tree * = NULL);
 extern bool maybe_warn_nonstring_arg (tree, tree);
-extern bool get_size_range (tree, tree[2], bool = false);
+enum size_range_flags
+  {
+   /* Set to consider zero a valid range.  */
+   SR_ALLOW_ZERO = 1,
+   /* Set to use the largest subrange of a set of ranges as opposed
+      to the smallest.  */
+   SR_USE_LARGEST = 2
+  };
+extern bool get_size_range (tree, tree[2], int = 0);
 extern rtx rtx_for_static_chain (const_tree, bool);
 extern bool cxx17_empty_base_field_p (const_tree);
 
diff --git a/gcc/gimple-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c
index 512fc138528..e2734c81456 100644
--- a/gcc/gimple-ssa-warn-restrict.c
+++ b/gcc/gimple-ssa-warn-restrict.c
@@ -264,7 +264,7 @@ builtin_memref::builtin_memref (tree expr, tree size)
       tree range[2];
       /* Determine the size range, allowing for the result to be [0, 0]
 	 for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX.  */
-      get_size_range (size, range, true);
+      get_size_range (size, range, SR_ALLOW_ZERO);
       sizrange[0] = wi::to_offset (range[0]);
       sizrange[1] = wi::to_offset (range[1]);
       /* get_size_range returns SIZE_MAX for the maximum size.
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
index 3b019c8a80e..9eb02bdbfcb 100644
--- a/gcc/testsuite/c-c++-common/Wrestrict.c
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -320,14 +320,17 @@ void test_memcpy_anti_range (char *d, const char *s)
 
   T (d, d + SAR (0, 3), UR (DIFF_MAX - 2, DIFF_MAX));               /* { dg-warning "accessing \[0-9\]+ or more bytes at offsets 0 and \\\[-?\[0-9\]+, -?\[0-9\]+] overlaps \[0-9\]+ bytes at offset 2" "memcpy" } */
 
-  /* Verify that a size in an anti-range ~[0, N] where N >= PTRDIFF_MAX
-     doesn't trigger a warning.  */
+  /* Verify that a size in an anti-range ~[1, N] where N >= PTRDIFF_MAX - 2
+     doesn't trigger a warning.
+     With ~[1, PTRDIFF_MAX - 1] the difference between the just-past-the-end
+     pointer to A and A for char A[PTRDIFF_MAX] wouldn't be representable in
+     ptrdiff_t.  Since such a large object cannot exist, so the size of
+     the region must be zero.  */
+  T (d, s, UAR (1, DIFF_MAX / 2 - 1));
   T (d, s, UAR (1, DIFF_MAX - 1));
   T (d, s, UAR (1, DIFF_MAX));
   T (d, s, UAR (1, SIZE_MAX - 1));
-
-  /* This causes the last dg-warning test to fail for some reason.
-     T (d, s, UAR (1, SIZE_MAX)); */
+  T (d, s, UAR (1, SIZE_MAX));
 }
 
 /* Verify calls to memcpy() where the combination of offsets in some
diff --git a/gcc/testsuite/g++.dg/warn/Wplacement-new-size-8.C b/gcc/testsuite/g++.dg/warn/Wplacement-new-size-8.C
new file mode 100644
index 00000000000..a0d136266ea
--- /dev/null
+++ b/gcc/testsuite/g++.dg/warn/Wplacement-new-size-8.C
@@ -0,0 +1,147 @@
+/* Verify informational notes following the warning.
+   { dg-do compile }
+   { dg-options "-Wall" } */
+
+#define DISS_MAX  __PTRDIFF_MAX__
+#define SIZE_MAX  __SIZE_MAX__
+
+typedef __SIZE_TYPE__  size_t;
+
+template <int N> struct S { char a[N]; };
+
+void* operator new (size_t, void *p) { return p; }
+void* operator new[] (size_t, void *p) { return p; }
+
+
+void test_cst_off ()
+{
+  {
+    char ca0[0];                // { dg-message "'ca0' declared here" "note" }
+    new (ca0 + 0) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[0]' and size '0'" }
+  }
+  {
+    char ca1[1];
+    new (ca1 + 0) S<1>;
+  }
+  {
+    char ca1[1];                // { dg-message "'ca1' declared here" "note" }
+    new (ca1 + 0) S<2>;         // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[1]' and size '1'" }
+  }
+  {
+    char ca1[1];                // { dg-message "at offset 1 from 'ca1' declared here" "note" }
+    new (ca1 + 1) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+  }
+  {
+    char ca1[1];                // { dg-message "at offset 2 from 'ca1' declared here" "note" }
+    new (ca1 + 2) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+  }
+  {
+    char ca1[1];                // { dg-message "at offset -1 from 'ca1' declared here" "note" }
+    new (ca1 - 1) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+  }
+  {
+    /* Offsets are treated as signed so SIZE_MAX is indistinguishable
+       from -1.  */
+    char ca1[1];                // { dg-message "at offset \\d+ from 'ca1' declared here" "note" { xfail *-*-* } }
+                                // { dg-message "at offset -1 from 'ca1' declared here" "note" { target *-*-* } .-1 }
+    new (ca1 + SIZE_MAX) S<1>;  // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[1]' and size '0'" }
+  }
+}
+
+
+/* Verify that the range of the offset included in the note corresponds
+   to the range of its type (plus the optional constant).  */
+
+void test_var_off_uchar (unsigned char i)
+{
+  {
+    // Verify that the nore doesn't mention an offset.
+    char ca0[0];                // { dg-message ": 'ca0' declared here" "note" }
+    new (ca0 + i) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[0]' and size '0'" }
+  }
+  {
+    char ca1[1];
+    new (ca1 + i) S<1>;
+  }
+  {
+    // Verify that the nore doesn't mention an offset.
+    char ca1[1];                // { dg-message ": 'ca1' declared here" "note" }
+    new (ca1 + i) S<2>;         // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[1]' and size at most '1'" }
+  }
+  {
+    char ca2[2];
+    new (ca2 + i) S<2>;
+    new (ca2 + 1 - i) S<2>;
+    new (ca2 - i + 1) S<2>;
+    new (ca2 - 2 + i) S<2>;
+    new (ca2 - i + 2) S<2>;
+    new (ca2 - i + i) S<2>;
+    new (ca2 + i + i) S<2>;
+  }
+  {
+    char ca2[2];                // { dg-message "at offset \\\[1, 2] from 'ca2' declared here" "note" }
+    new (ca2 + i + 1) S<2>;     // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[2]' and size at most '1'" }
+  }
+
+  {
+    char a[65281];
+    new (a + i + 65280) S<1>;
+  }
+  {
+    char a[65281];              // { dg-message "at offset 65281 from 'a' declared here" "note" }
+    new (a + i + 65281) S<1>;   // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[65281]' and size '0'" }
+  }
+  {
+    char a[65281];              // { dg-message "at offset \\\[65154, 65281] from 'a' declared here" "note" }
+    new (a + i + 65154) S<128>; // { dg-warning "constructing an object of type 'S<128>' and size '128' in a region of type 'char \\\[65281]' and size at most '127'" }
+  }
+}
+
+
+/* Same as above but also verify that the signedness of the offset is
+   considered in the issuing the warning.  */
+
+void test_var_off_schar (signed char i)
+{
+  {
+    // Verify that the nore doesn't mention an offset.
+    char ca0[0];                // { dg-message ": 'ca0' declared here" "note" }
+    new (ca0 + i) S<1>;         // { dg-warning "constructing an object of type 'S<1>' and size '1' in a region of type 'char \\\[0]' and size '0'" }
+  }
+  {
+    char ca1[1];
+    new (ca1 + i) S<1>;
+    new (ca1 - i) S<1>;
+    new (ca1 + i + 1) S<1>;
+    new (ca1 - i + 1) S<1>;
+    new (ca1 + i + i) S<1>;
+    new (ca1 - i - i) S<1>;
+  }
+  {
+    // Verify that the nore doesn't mention an offset.
+    char ca1[1];                // { dg-message ": 'ca1' declared here" "note" }
+    new (ca1 + i) S<2>;         // { dg-warning "constructing an object of type 'S<2>' and size '2' in a region of type 'char \\\[1]' and size at most '1'" }
+  }
+  {
+    char ca2[2];
+    new (ca2 + i) S<2>;
+    new (ca2 + 1 - i) S<2>;
+    new (ca2 - i + 1) S<2>;
+    new (ca2 - 2 + i) S<2>;
+    new (ca2 - i + 2) S<2>;
+    new (ca2 - i + i) S<2>;
+    new (ca2 + i + i) S<2>;
+  }
+  {
+    char ca2[2];
+    new (ca2 + i + 1) S<2>;
+  }
+
+  {
+    char a[65281];              // { dg-message "at offset \\\[65153, 65408] from 'a'" }
+    new (a + i + 65280) S<1>;
+    new (a + i + 65281) S<1>;
+    new (a + i + 65281) S<128>;
+    new (a + i + 65281) S<129>; // { dg-warning "constructing an object of type 'S<129>' and size '129' in a region of type 'char \\\[65281]' and size at most '128'" }
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c
index fd43f3afb59..a1b103918cf 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-34.c
@@ -112,7 +112,7 @@ void s2_warn_cstoff_cstidx (struct S2 *p)
 void s2_warn_varoff_cstdix (struct S2 *p, int i)
 {
   char *q = p->a + i;
-  q[2] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" "pr?????" { xfail *-*-* }  }
+  q[2] = __LINE__;            // { dg-warning "\\\[-Wstringop-overflow" }
 }
 
 void s2_warn_cstoff_varidx (struct S2 *p, int i)
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-41.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-41.c
new file mode 100644
index 00000000000..9b2d2cbc501
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-41.c
@@ -0,0 +1,120 @@
+/* Verify that writes at excessive offsets into declared or allocated
+   objects of unknown size are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+void* memcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+
+void sink (void*);
+
+
+void char_array_cst_off_cst_size (void)
+{
+  extern char caxcc[];                  // { dg-message "at offset \\d+ into destination object 'caxcc'" }
+
+  char *p = caxcc;
+  size_t idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 2" }
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 1" "pr?????" { xfail ilp32 } }
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 0" }
+  sink (p);
+}
+
+
+void char_array_var_off_cst_size (size_t idx)
+{
+  /* The offset is a range with a very large lower bound and an upper
+     bound of DIFF_MAX.  There's not point in also mentioning the latter
+     (it wouldn't make the note any more meaningful) so verify it only
+     mentions the lower bound.  */
+  extern char caxvc[];                  // { dg-message "at offset \\d+ into destination object 'caxvc'" "note" }
+
+  char *p = caxvc;
+
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+  sink (p);
+}
+
+
+void char_array_var_off_var_size (size_t idx, size_t n)
+{
+  extern char caxvv[];                  // { dg-message "at offset \\d+ into destination object 'caxvv'" "note" }
+
+  char *p = caxvv;
+
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  if (n < 3 || 7 < n)
+    n = 3;
+
+  memset (p + idx, 0, n);
+  sink (p);
+
+  ++n;
+  memset (p + idx, 0, n);               // { dg-warning "writing between 4 and 8 bytes into a region of size 3" }
+  sink (p);
+}
+
+
+void alloc_array_var_off_cst_size (size_t n, size_t idx)
+{
+  char *p = malloc (n);                 // { dg-message "at offset \\d+ into destination object" "note" }
+
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+  sink (p);
+}
+
+
+void int_array_cst_off_cst_size (void)
+{
+  extern int iaxc[];                    // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'iaxc'" }
+
+  int *p = iaxc;
+  size_t idx = DIFF_MAX / sizeof *iaxc;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+  sink (p);
+}
+
+
+void* nowarn_anti_range_1 (char *p, char *q)
+{
+  size_t n = q - p;
+  if (!n) return 0;
+
+  char *d = __builtin_malloc (n + 1);
+  memcpy (d, p, n + 1);                 // { dg-bogus "-Wstringop-overflow" }
+  return d;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-43.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-43.c
index 3ac5a88e4b0..14ab925afdc 100644
--- a/gcc/testsuite/gcc.dg/Wstringop-overflow-43.c
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-43.c
@@ -159,9 +159,10 @@ void warn_memset_reversed_range (void)
 
   char *p = &a11[11];
 
-  /* The below is represented as a true anti-range as opposed to a range
-     with reversed bounds and the former aren't handled.  */
-  T1 (p, SAR (INT_MIN, -11), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+  /* Since the offset is excessive, either starting before &a11[0]
+     ot just past &a[11], the region size in the warning should
+     probably be zero, but accept other sizes too.  */
+  T1 (p, SAR (INT_MIN, -11), n11);      // { dg-warning "writing 11 or more bytes into a region of size \\d+" }
 
   /* The following are represented as ordinary ranges with reversed bounds
      and those are handled. */
@@ -171,7 +172,7 @@ void warn_memset_reversed_range (void)
   T1 (p, SAR (INT_MIN,   1), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail ilp32 } }
   T1 (p, SAR (INT_MIN,   0), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" }
   /* Also represented as a true anti-range.  */
-  T1 (p, SAR (    -12, -11), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+  T1 (p, SAR (    -12, -11), n11);      // { dg-warning "writing 11 or more bytes into a region of size \\d+" }
   T1 (p, SAR (    -12,  -1), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" }
   T1 (p, SAR (    -11,   0), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" }
   T1 (p, SAR (    -11,  11), n11);      // { dg-warning "writing 11 or more bytes into a region of size 0" }
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-44.s b/gcc/testsuite/gcc.dg/Wstringop-overflow-44.s
new file mode 100644
index 00000000000..0fc73a99078
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-44.s
@@ -0,0 +1,271 @@
+	.file	"Wstringop-overflow-44.c"
+	.text
+	.p2align 4
+	.globl	f0
+	.type	f0, @function
+f0:
+.LFB0:
+	.cfi_startproc
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	f0, .-f0
+	.p2align 4
+	.globl	f1
+	.type	f1, @function
+f1:
+.LFB1:
+	.cfi_startproc
+	ret
+	.cfi_endproc
+.LFE1:
+	.size	f1, .-f1
+	.p2align 4
+	.globl	f2
+	.type	f2, @function
+f2:
+.LFB2:
+	.cfi_startproc
+	movl	n(%rip), %eax
+	testl	%eax, %eax
+	jle	.L12
+.L4:
+	ret
+	.p2align 4,,10
+	.p2align 3
+.L12:
+	movslq	%eax, %rdx
+	movq	d(%rip), %rcx
+	testq	%rdx, %rdx
+	je	.L4
+	xorl	%eax, %eax
+.L6:
+	movb	$0, (%rcx,%rax)
+	addq	$1, %rax
+	cmpq	%rdx, %rax
+	jb	.L6
+	ret
+	.cfi_endproc
+.LFE2:
+	.size	f2, .-f2
+	.p2align 4
+	.globl	f3
+	.type	f3, @function
+f3:
+.LFB3:
+	.cfi_startproc
+	movslq	n(%rip), %rdx
+	testl	%edx, %edx
+	jle	.L15
+	ret
+	.p2align 4,,10
+	.p2align 3
+.L15:
+	movq	%rdi, %rsi
+	movq	d(%rip), %rdi
+	jmp	strncpy
+	.cfi_endproc
+.LFE3:
+	.size	f3, .-f3
+	.p2align 4
+	.globl	f4
+	.type	f4, @function
+f4:
+.LFB4:
+	.cfi_startproc
+	movl	n(%rip), %eax
+	testl	%eax, %eax
+	jle	.L18
+	ret
+	.p2align 4,,10
+	.p2align 3
+.L18:
+	movq	d(%rip), %rax
+	movq	%rdi, %rsi
+	movb	$0, (%rax)
+	movslq	n(%rip), %rdx
+	movq	d(%rip), %rdi
+	jmp	strncat
+	.cfi_endproc
+.LFE4:
+	.size	f4, .-f4
+	.p2align 4
+	.globl	g0
+	.type	g0, @function
+g0:
+.LFB5:
+	.cfi_startproc
+	movl	n(%rip), %eax
+	testl	%eax, %eax
+	jle	.L25
+	ret
+	.p2align 4,,10
+	.p2align 3
+.L25:
+	subq	$24, %rsp
+	.cfi_def_cfa_offset 32
+	leaq	15(%rsp), %rdi
+	call	sink
+	addq	$24, %rsp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE5:
+	.size	g0, .-g0
+	.p2align 4
+	.globl	g1
+	.type	g1, @function
+g1:
+.LFB6:
+	.cfi_startproc
+	movl	n(%rip), %eax
+	testl	%eax, %eax
+	jle	.L32
+	ret
+	.p2align 4,,10
+	.p2align 3
+.L32:
+	subq	$24, %rsp
+	.cfi_def_cfa_offset 32
+	leaq	15(%rsp), %rdi
+	call	sink
+	addq	$24, %rsp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE6:
+	.size	g1, .-g1
+	.p2align 4
+	.globl	g2
+	.type	g2, @function
+g2:
+.LFB7:
+	.cfi_startproc
+	movl	n(%rip), %eax
+	testl	%eax, %eax
+	jle	.L45
+	ret
+	.p2align 4,,10
+	.p2align 3
+.L45:
+	movslq	%eax, %rdx
+	subq	$24, %rsp
+	.cfi_def_cfa_offset 32
+	testq	%rdx, %rdx
+	je	.L36
+	xorl	%eax, %eax
+.L35:
+	movb	$0, 15(%rsp,%rax)
+	addq	$1, %rax
+	cmpq	%rdx, %rax
+	jb	.L35
+.L36:
+	leaq	15(%rsp), %rdi
+	call	sink
+	addq	$24, %rsp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE7:
+	.size	g2, .-g2
+	.p2align 4
+	.globl	g3
+	.type	g3, @function
+g3:
+.LFB8:
+	.cfi_startproc
+	movslq	n(%rip), %rdx
+	testl	%edx, %edx
+	jle	.L52
+	ret
+	.p2align 4,,10
+	.p2align 3
+.L52:
+	subq	$24, %rsp
+	.cfi_def_cfa_offset 32
+	movq	%rdi, %rsi
+	leaq	15(%rsp), %rdi
+	call	strncpy
+	leaq	15(%rsp), %rdi
+	call	sink
+	addq	$24, %rsp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE8:
+	.size	g3, .-g3
+	.p2align 4
+	.globl	g4
+	.type	g4, @function
+g4:
+.LFB9:
+	.cfi_startproc
+	movslq	n(%rip), %rdx
+	testl	%edx, %edx
+	jle	.L59
+	ret
+	.p2align 4,,10
+	.p2align 3
+.L59:
+	subq	$24, %rsp
+	.cfi_def_cfa_offset 32
+	movq	%rdi, %rsi
+	leaq	15(%rsp), %rdi
+	movb	$0, 15(%rsp)
+	call	strncat
+	leaq	15(%rsp), %rdi
+	call	sink
+	addq	$24, %rsp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE9:
+	.size	g4, .-g4
+	.p2align 4
+	.globl	h0
+	.type	h0, @function
+h0:
+.LFB10:
+	.cfi_startproc
+	movl	n(%rip), %eax
+	testl	%eax, %eax
+	jle	.L66
+	ret
+	.p2align 4,,10
+	.p2align 3
+.L66:
+	subq	$8, %rsp
+	.cfi_def_cfa_offset 16
+	movl	$1, %edi
+	call	malloc
+	movq	%rax, d(%rip)
+	addq	$8, %rsp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE10:
+	.size	h0, .-h0
+	.p2align 4
+	.globl	h1
+	.type	h1, @function
+h1:
+.LFB16:
+	.cfi_startproc
+	movl	n(%rip), %eax
+	testl	%eax, %eax
+	jle	.L73
+	ret
+	.p2align 4,,10
+	.p2align 3
+.L73:
+	subq	$8, %rsp
+	.cfi_def_cfa_offset 16
+	movl	$1, %edi
+	call	malloc
+	movq	%rax, d(%rip)
+	addq	$8, %rsp
+	.cfi_def_cfa_offset 8
+	ret
+	.cfi_endproc
+.LFE16:
+	.size	h1, .-h1
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-45.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-45.c
new file mode 100644
index 00000000000..112d79a5ae7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-45.c
@@ -0,0 +1,255 @@
+/* PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
+   Verify that out of bounds writes by built-ins to objects through pointers
+   returned by other built-ins are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#include "range.h"
+
+void* malloc (size_t);
+void* memcpy (void*, const void*, size_t);
+void* memmove (void*, const void*, size_t);
+void* mempcpy (void*, const void*, size_t);
+
+void sink (void*, ...);
+
+
+void nowarn_memcpy (const void *s)
+{
+  extern char cpy_a4[4];
+  unsigned n = sizeof cpy_a4;
+
+  void *p = cpy_a4;
+  p = memcpy (p, s, n);
+  sink (p);
+  memcpy (p, s, n);
+  sink (p);
+
+  p = cpy_a4 + 1;
+  p = memcpy (p, s, n - 1);
+  sink (p);
+  memcpy (p, s, n - 1);
+  sink (p);
+
+  p = cpy_a4 + 2;
+  p = memcpy (p, s, n - 2);
+  sink (p);
+  memcpy (p, s, n - 2);
+  sink (p);
+
+  p = cpy_a4 + 3;
+  p = memcpy (p, s, n - 3);
+  sink (p);
+  memcpy (p, s, n - 3);
+  sink (p);
+
+  p = cpy_a4 + 4;
+  p = memcpy (p, s, n - 4);
+  sink (p);
+  memcpy (p, s, n - 4);
+  sink (p);
+}
+
+
+void nowarn_memcpy_chain (const void *s)
+{
+  extern char cpy_a8[8];
+
+  char *p = cpy_a8;
+
+  p = memcpy (p + 1, s, 7);
+  sink (p);
+
+  p = memcpy (p + 2 , s, 5);
+  sink (p);
+
+  p = memcpy (p + 3 , s, 2);
+  sink (p);
+
+  p = memcpy (p + 1 , s, 1);
+  sink (p);
+
+  p = memcpy (p - 7 , s, 8);
+  sink (p);
+
+  memcpy (p + 1, s, 7);
+}
+
+
+void warn_memcpy (const void *s)
+{
+  extern char cpy_a5[5];                // { dg-message "destination object 'cpy_a5'" "note" }
+
+  unsigned n = sizeof cpy_a5;
+  void *p = cpy_a5;
+
+  p = memcpy (p, s, n);
+  sink (p);
+  memcpy (p, s, n + 1);                 // { dg-warning "writing 6 bytes into a region of size 5" }
+  sink (p);
+
+  p = cpy_a5;
+  p = memcpy (p, s, n);
+  sink (p);
+  memcpy (p, s, n + 1);                 // { dg-warning "writing 6 bytes into a region of size 5" }
+  sink (p);
+
+  p = cpy_a5 + 1;
+  p = memcpy (p, s, n - 1);
+  sink (p);
+  memcpy (p, s, n);                     // { dg-warning "writing 5 bytes into a region of size 4" }
+  sink (p);
+}
+
+
+void warn_memcpy_chain (const void *s)
+{
+  extern char cpy_a8[8];                // { dg-message "destination object 'cpy_a8'" "note" }
+
+  char *p = cpy_a8;
+
+  p = memcpy (p, s, 9);                 // { dg-warning "writing 9 bytes into a region of size 8" }
+  sink (p);
+
+  p = memcpy (p + 2, s, 7);             // { dg-warning "writing 7 bytes into a region of size 6" }
+  sink (p);
+
+  p = memcpy (p + 3, s, 5);             // { dg-warning "writing 5 bytes into a region of size 3" }
+  sink (p);
+
+  p = memcpy (p + 3, s, 3);             // { dg-warning "writing 3 bytes into a region of size 0" }
+  sink (p);
+}
+
+
+void nowarn_mempcpy (const void *s)
+{
+  extern char a4[4];
+  unsigned n = sizeof a4;
+
+  char *p = mempcpy (a4, s, n);
+  sink (p);
+  mempcpy (p - 4, s, n);
+  sink (p);
+
+  p = mempcpy (a4 + 1, s, n - 1);
+  sink (p);
+  mempcpy (p - 4, s, n);
+  sink (p);
+
+  p = mempcpy (a4 + 2, s, n - 2);
+  sink (p);
+  mempcpy (p - 4, s, n);
+  sink (p);
+
+  p = mempcpy (a4 + 3, s, n - 3);
+  sink (p);
+  mempcpy (p - 4, s, n);
+  sink (p);
+
+  p = mempcpy (a4 + 4, s, n - 4);
+  sink (p);
+  mempcpy (p - 4, s, n);
+  sink (p);
+}
+
+
+void nowarn_mempcpy_chain (const void *s)
+{
+  extern char pcpy_a8[8];
+
+  char *p = pcpy_a8;
+
+  p = mempcpy (p + 1, s, 7);
+  sink (p);
+
+  p = mempcpy (p - 7 , s, 7);
+  sink (p);
+
+  p = mempcpy (p - 5 , s, 5);
+  sink (p);
+
+  p = mempcpy (p - 3 , s, 3);
+  sink (p);
+
+  p = mempcpy (p - 2 , s, 2);
+  sink (p);
+
+  mempcpy (p - 1, s, 1);
+  sink (p);
+
+  mempcpy (p - 8, s, 8);
+}
+
+
+void warn_mempcpy (const void *s)
+{
+  extern char pcpy_a5[5];               // { dg-message "destination object 'pcpy_a5'" "note" }
+
+  char *p = pcpy_a5;
+
+  p = mempcpy (p, s, 5);
+  sink (p);
+  mempcpy (p - 5, s, 6);                // { dg-warning "writing 6 bytes into a region of size 5 " }
+  sink (p);
+
+  p = pcpy_a5;
+  p = mempcpy (p, s, 3);
+  sink (p);
+  mempcpy (p, s, 3);                    // { dg-warning "writing 3 bytes into a region of size 2 " }
+  sink (p);
+
+  p = pcpy_a5 + 1;
+  p = mempcpy (p, s, 3);
+  sink (p);
+  mempcpy (p - 1, s, 5);                // { dg-warning "writing 5 bytes into a region of size 2 " }
+  sink (p);
+}
+
+
+void warn_mempcpy_chain_3 (const void *s)
+{
+  char *p = malloc (5);                 // { dg-message "at offset \\\[3, 5] into destination object of size 5" "note" }
+  p = mempcpy (p, s, UR (1, 2));
+  p = mempcpy (p, s, UR (2, 3));
+  p = mempcpy (p, s, UR (3, 4));        // { dg-warning "writing between 3 and 4 bytes into a region of size 2 " }
+
+  sink (p);
+}
+
+void warn_mempcpy_offrng_chain_3 (const void *s)
+{
+  char *p = malloc (11);                 // { dg-message "at offset \\\[9, 11] into destination object of size 11 " "note" }
+  size_t r1_2 = UR (1, 2);
+  size_t r2_3 = r1_2 + 1;
+  size_t r3_4 = r2_3 + 1;
+
+  p = mempcpy (p + r1_2, s, r1_2);
+  p = mempcpy (p + r2_3, s, r2_3);
+  p = mempcpy (p + r3_4, s, r3_4);       // { dg-warning "writing between 3 and 4 bytes into a region of size 2 " }
+
+  sink (p);
+}
+
+void warn_mempcpy_chain_4 (const void *s)
+{
+  char *p = malloc (9);                 // { dg-message "at offset \\\[6, 9] into destination object of size 9 " "note" }
+  p = mempcpy (p, s, UR (1, 2));
+  p = mempcpy (p, s, UR (2, 3));
+  p = mempcpy (p, s, UR (3, 4));
+  p = mempcpy (p, s, UR (4, 5));        // { dg-warning "writing between 4 and 5 bytes into a region of size 3 " }
+
+  sink (p);
+}
+
+void warn_mempcpy_chain_5 (const void *s)
+{
+  char *p = malloc (14);                // { dg-message "at offset \\\[10, 14] into destination object of size 14 " "note" }
+  p = mempcpy (p, s, UR (1, 2));
+  p = mempcpy (p, s, UR (2, 3));
+  p = mempcpy (p, s, UR (3, 4));
+  p = mempcpy (p, s, UR (4, 5));
+  p = mempcpy (p, s, UR (5, 6));        // { dg-warning "writing between 5 and 6 bytes into a region of size 4 " }
+
+  sink (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c
new file mode 100644
index 00000000000..a4d78b21cd1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-46.c
@@ -0,0 +1,97 @@
+/* PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
+   Verify that out of bounds writes by built-ins to objects through pointers
+   returned by memchr() are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#include "range.h"
+
+void* malloc (size_t);
+void* memchr (void*, int, size_t);
+void* memset (void*, int, size_t);
+
+void sink (void*, ...);
+
+void nowarn_memchr_cst_memset_cst (const void *s)
+{
+  char *p = malloc (4);
+  sink (p);
+
+  p = memchr (p, '1', 4);
+  memset (p, 0, 4);
+}
+
+void nowarn_memchr_uint_memset_cst (const void *s, unsigned n)
+{
+  char *p = malloc (4);
+  sink (p);
+
+  p = memchr (p, '1', n);
+  memset (p, 0, 4);
+}
+
+void nowarn_memchr_sz_memset_cst (const void *s, size_t n)
+{
+  char *p = malloc (4);
+  sink (p);
+
+  p = memchr (p, '1', n);
+  memset (p, 0, 4);
+}
+
+void nowarn_memchr_anti_range_memset_cst (const void *s, size_t n)
+{
+  char *p = malloc (4);
+  sink (p);
+
+  if (n == 0)
+    n = 1;
+
+  p = memchr (p, '1', n);
+  memset (p, 0, 4);
+}
+
+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" }
+  sink (p);
+
+  p = memchr (p, '1', 4);
+  memset (p, 0, 5);                     // { dg-warning "writing 5 bytes into a region of size 4 " }
+}
+
+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" }
+  sink (p);
+
+  p = memchr (p, '1', n);
+  memset (p, 0, 5);                     // { dg-warning "writing 5 bytes into a region of size 4 " }
+}
+
+void warn_memchr_var_memset_range (const void *s, unsigned n)
+{
+  /* The offsets in the first two notes are bounded by the size of
+     the allocated object.  The real upper bound of the offset in
+     the last note includes the upper bound f the offset of the pointer
+     returned from the previous memchr() call, but it ends up getting
+     constrained to the bounds of the allocated object so it's the same
+     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 }
+
+  sink (p0);
+  char *p1 = memchr (p0, '1', n);
+  memset (p1, 0, UR (8, 9));            // { dg-warning "writing between 8 and 9 bytes into a region of size 7 " }
+
+  sink (p0);
+  p1 = memchr (p0 + 1, '2', n);
+  memset (p1, 0, UR (7, 9));            // { dg-warning "writing between 7 and 9 bytes into a region of size 6 " }
+
+  sink (p0);
+  char *p2 = memchr (p1 + 1, '3', n);
+  memset (p2, 0, UR (6, 9));            // { dg-warning "writing between 6 and 9 bytes into a region of size 5 " }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c
new file mode 100644
index 00000000000..02b14ee2eda
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-47.c
@@ -0,0 +1,69 @@
+/* Verify that storing a bigger vector into smaller space is diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+typedef __INT16_TYPE__                         int16_t;
+typedef __attribute__ ((__vector_size__ (32))) char C32;
+
+typedef __attribute__ ((__vector_size__ (64))) int16_t I16_64;
+
+void sink (void*);
+
+
+void nowarn_c32 (char c)
+{
+  extern char nowarn_a32[32];
+
+  void *p = nowarn_a32;
+  *(C32*)p = (C32){ c };
+  sink (p);
+
+  char a32[32];
+  p = a32;
+  *(C32*)p = (C32){ c };
+  sink (p);
+}
+
+void warn_c32 (char c)
+{
+  extern char warn_a32[32];   // { dg-message "at offset 32 to object 'warn_a32' with size 32" }
+
+  void *p = warn_a32 + 1;
+  *(C32*)p = (C32){ c };      // { dg-warning "writing 1 byte into a region of size 0" }
+
+  /* Verify a local variable too. */
+  char a32[32];
+  p = a32 + 1;
+  *(C32*)p = (C32){ c };      // { dg-warning "writing 1 byte into a region of size 0" }
+  sink (p);
+}
+
+
+void nowarn_i16_64 (int16_t i)
+{
+  extern char nowarn_a64[64];
+
+  void *p = nowarn_a64;
+  I16_64 *q = (I16_64*)p;
+  *q = (I16_64){ i };
+
+  char a64[64];
+  q = (I16_64*)a64;
+  *q = (I16_64){ i };
+  sink (q);
+}
+
+void warn_i16_64 (int16_t i)
+{
+  extern char warn_a64[64];   // { dg-message "at offset 128 to object 'warn_a64' with size 64" "pr97027" { xfail *-*-* } }
+
+  void *p = warn_a64 + 1;
+  I16_64 *q = (I16_64*)p;
+  *q = (I16_64){ i };         // { dg-warning "writing 1 byte into a region of size 0" "pr97027" { xfail *-*-* } }
+
+  char a64[64];
+  p = a64 + 1;
+  q = (I16_64*)p;
+  *q = (I16_64){ i };         // { dg-warning "writing 1 byte into a region of size 0" "pr97027" { xfail *-*-* } }
+  sink (p);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-49.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-49.c
new file mode 100644
index 00000000000..84b6c94fa7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-49.c
@@ -0,0 +1,146 @@
+/* Verify the handling of anti-ranges/multi-ranges by allocation functions
+   and subsequent accesses.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* malloc (size_t);
+void  bzero (void*, size_t);
+void* memset (void*, int, size_t);
+
+
+/* Exercise size_t (via malloc and memset) and unsigned/signed int.  */
+
+__attribute__ ((alloc_size (1))) void*
+alloc_int (int);
+
+__attribute__ ((access (write_only, 1, 2))) void
+access_int (void*, int);
+
+__attribute__ ((alloc_size (1))) void*
+alloc_uint (unsigned);
+
+__attribute__ ((access (write_only, 1, 2))) void
+access_uint (void*, unsigned);
+
+
+void* nowarn_malloc_memset_same_anti_range (size_t n)
+{
+  /* Set N to the anti-range ~[3, 3].  */
+  if (n == 3)
+    n = 4;
+  void *p = malloc (n);
+
+  /* Verify there is no warning for an access to N bytes at P.
+     This means the warning has to assume the value of N in the call
+     to alloc() is in the larger subrange [4, UINT_MAX], while in
+     the call to access() in [0, 3].  */
+  return memset (p, 0, n);
+}
+
+/* Same as above but with two valid ranges.  */
+
+void* nowarn_malloc_memset_anti_range (size_t n1, size_t n2)
+{
+  /* Set N1 to the anti-range ~[3, 3].  */
+  if (n1 == 3)
+    n1 = 4;
+  void *p = malloc (n1);
+
+  /* Set N2 to the anti-range ~[7, 7].  */
+  if (n2 == 7)
+    n2 = 8;
+
+  return memset (p, 0, n2);
+}
+
+
+void nowarn_alloc_access_same_anti_range_int (int n)
+{
+  /* Set N to the anti-range ~[3, 3].  */
+  if (n == 3)
+    n = 4;
+  void *p = alloc_int (n);
+
+  /* Verify there is no warning for an access to N bytes at P.
+     This means the warning has to assume the value of N in the call
+     to alloc() is in the larger subrange [4, UINT_MAX], while in
+     the call to access() in [0, 3].  */
+  access_int (p, n);
+}
+
+/* Same as above but with two valid ranges.  */
+
+void nowarn_alloc_access_anti_range_int (int n1, int n2)
+{
+  /* Set N1 to the anti-range ~[3, 3].  */
+  if (n1 == 3)
+    n1 = 4;
+  void *p = alloc_int (n1);
+
+  /* Set N2 to the anti-range ~[7, 7].  */
+  if (n2 == 7)
+    n2 = 8;
+
+  access_int (p, n2);
+}
+
+
+void nowarn_alloc_access_same_anti_range_uint (unsigned n)
+{
+  /* Set N to the anti-range ~[3, 3].  */
+  if (n == 3)
+    n = 4;
+  void *p = alloc_uint (n);
+
+  /* Verify there is no warning for an access to N bytes at P.
+     This means the warning has to assume the value of N in the call
+     to alloc() is in the larger subrange [4, UINT_MAX], while in
+     the call to access() in [0, 3].  */
+  access_uint (p, n);
+}
+
+/* Same as above but with two valid ranges.  */
+
+void nowarn_alloc_access_anti_range_uint (unsigned n1, unsigned n2)
+{
+  /* Set N1 to the anti-range ~[3, 3].  */
+  if (n1 == 3)
+    n1 = 4;
+  void *p = alloc_uint (n1);
+
+  /* Set N2 to the anti-range ~[7, 7].  */
+  if (n2 == 7)
+    n2 = 8;
+
+  access_uint (p, n2);
+}
+
+
+void* nowarn_malloc_anti_range_memset_range (size_t n1, size_t n2)
+{
+  /* Set N1 to the anti-range ~[3, 3].  */
+  if (n1 == 3)
+    n1 = 4;
+  void *p = malloc (n1);
+
+  /* Set N2 to the range [5, MAX].  */
+  if (n2 < 5)
+    n2 = 5;
+  return memset (p, 0, n2);
+}
+
+void* nowarn_malloc_range_bzero_anti_range (size_t n1, size_t n2)
+{
+  /* Set N1 to the anti-range ~[3, 3].  */
+  if (n1 > 4)
+    n1 = 4;
+  void *p = malloc (n1);
+
+  /* Set N2 to the range [5, MAX].  */
+  if (n2 <= 3 || 5 <= n2)
+    n2 = 4;
+  bzero (p, n2);
+  return p;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-50.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-50.c
new file mode 100644
index 00000000000..7df58e5209e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-50.c
@@ -0,0 +1,125 @@
+/* Verify that writes at excessive offsets into objects of unknown size
+   pointed to by function arguments are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+void* memset (void*, int, size_t);
+
+void sink (void*);
+
+char* fcall (void);
+
+void char_ptr_cst_off_cst_size (char *p)
+                                        // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'p'" "note" { target *-*-* } .-1 }
+{
+  size_t idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 2" }
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 1" }
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 0" }
+}
+
+
+void char_ptr_var_difoff_cst_size (ptrdiff_t idx)
+{
+  char *p = fcall ();
+  /* The offset is a range with a very large lower bound and an upper
+     bound of DIFF_MAX.  There's not point in also mentioning the latter
+     (it wouldn't make the note any more meaningful) so verify it only
+     mentions the lower bound.
+     { dg-message "at offset \\d+ into destination object of size \\\[0, \\d+] (allocated|returned) by 'fcall'" "note" { target *-*-* } .-5 } */
+
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+}
+
+
+void char_ptr_var_szoff_cst_size (size_t idx)
+{
+  extern char* gptr;
+  // { dg-message "at offset \\d+ into destination object 'gptr'" "note" { target *-*-* } .-1 }
+
+  char *p = gptr;
+
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" "" { xfail *-*-* } }
+
+  if (idx > DIFF_MAX)
+    idx = DIFF_MAX;
+
+  memset (p + idx, 0, 7);               // { dg-warning "writing 7 bytes into a region of size 3" }
+}
+
+
+void char_ptr_var_difoff_var_size (char *p, ptrdiff_t idx, size_t n)
+                                        // { dg-message "at offset \\d+ into destination object 'p'" "note" { target *-*-* } .-1 }
+{
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  if (n < 3 || 7 < n)
+    n = 3;
+
+  memset (p + idx, 0, n);
+  sink (p);
+
+  ++n;
+  memset (p + idx, 0, n);               // { dg-warning "writing between 4 and 8 bytes into a region of size 3" }
+}
+
+
+void char_ptr_var_szoff_var_size (char *p, size_t idx, size_t n)
+                                        // { dg-message "at offset \\\[\[1-9\]\[0-9\]+, \[1-9\]\[0-9\]+] into destination object 'p'" "note" { xfail *-*-* } .-1 }
+{
+  if (idx < DIFF_MAX - 3)
+    idx = DIFF_MAX - 3;
+
+  if (n < 3 || 7 < n)
+    n = 3;
+
+  memset (p + idx, 0, n);
+  sink (p);
+
+  ++n;
+  /* With an unsigned offset large values are interpreted as negative
+     so the addition (p + idx) is effectively treated as subtraction,
+     making an overflow indistinguishable from a valid (if unlikely)
+     store.  */
+  memset (p + idx, 0, n);               // { dg-warning "writing between 4 and 8 bytes into a region of size 3" "pr?????" { xfail *-*-* } }
+}
+
+
+void int_ptr_cst_off_cst_size (int *p)
+                                        // { dg-message "at offset \[1-9\]\[0-9\]+ into destination object 'p'" "note" { target *-*-* } .-1 }
+{
+  size_t idx = DIFF_MAX / sizeof *p;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-51.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-51.c
new file mode 100644
index 00000000000..6f36643c8bb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-51.c
@@ -0,0 +1,34 @@
+/* Test case derived from Binutils/GDB's readline/readline/histexpand.c.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+char *
+get_subst_pattern (char *str, int *iptr, int delimiter, int is_rhs, int *lenptr)
+{
+  int si, i, j, k;
+  char *s;
+
+  s = 0;
+  i = *iptr;
+
+  for (si = i; str[si] && str[si] != delimiter; si++)
+      if (str[si] == '\\' && str[si + 1] == delimiter)
+	si++;
+
+  if (si > i || is_rhs)
+    {
+      s = (char *)__builtin_malloc (si - i + 1);
+      for (j = 0, k = i; k < si; j++, k++)
+	{
+	  /* Remove a backslash quoting the search string delimiter. */
+	  if (str[k] == '\\' && str[k + 1] == delimiter)
+	    k++;
+	  s[j] = str[k];   // { dg-bogus "-Wstringop-overflow" }
+	}
+      s[j] = '\0';
+      if (lenptr)
+	*lenptr = j;
+    }
+
+  return s;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-52.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-52.c
new file mode 100644
index 00000000000..a28965557c3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-52.c
@@ -0,0 +1,62 @@
+
+/* PR middle-end/97023 - missing warning on buffer overflow in chained mempcpy
+   Verify that writes by built-in functions to objects through pointers
+   returned by ordinary (non-built-int) function are assumed to point to
+   the beginning of objects.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#include "range.h"
+
+void* memcpy (void*, const void*, size_t);
+void* memset (void*, int, size_t);
+
+void sink (void*, ...);
+
+extern char* arrptr[];
+extern char* ptr;
+extern char* retptr (void);
+struct S { char *p; };
+extern struct S retstruct (void);
+
+void nowarn_ptr (void)
+{
+  {
+    void *p = arrptr;
+    memset (p - 1, 0, 12345);           // { dg-warning "\\\[-Wstringop-overflow" }
+    memset (p,0, 12345);
+    memset (p,0, DIFF_MAX - 1);
+  }
+
+  {
+    char *p = arrptr[0];
+    memset (p - 1, 0, 12345);
+    memset (p - 12345, 0, 12345);
+    memset (p - 1234, 0, DIFF_MAX - 1);
+    memset (p - DIFF_MAX + 1, 0, 12345);
+  }
+
+  {
+    char *p = ptr;
+    memset (p - 1, 0, 12345);
+    memset (p - 12345, 0, 12345);
+    memset (p - 1234, 0, DIFF_MAX - 1);
+    memset (p - DIFF_MAX + 1, 0, 12345);
+  }
+
+  {
+    char *p = retptr ();
+    memset (p - 1, 0, 12345);
+    memset (p - 12345, 0, 12345);
+    memset (p - 1234, 0, DIFF_MAX - 1);
+    memset (p - DIFF_MAX + 1, 0, 12345);
+  }
+
+  {
+    char *p = retstruct ().p;
+    memset (p - 1, 0, 12345);
+    memset (p - 12345, 0, 12345);
+    memset (p - 1234, 0, DIFF_MAX - 1);
+    memset (p - DIFF_MAX + 1, 0, 12345);
+  }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-53.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-53.c
new file mode 100644
index 00000000000..cd8fa3202eb
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-53.c
@@ -0,0 +1,116 @@
+/* PR middle-end/96384 - bogus -Wstringop-overflow= storing into
+   multidimensional array with index in range
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+#define SHRT_MAX   __SHRT_MAX__
+#define SHRT_MIN   (-SHRT_MAX - 1)
+#define INT_MAX    __INT_MAX__
+#define INT_MIN    (-INT_MAX - 1)
+#define LONG_MAX   __LONG_MAX__
+#define LONG_MIN   (-LONG_MAX - 1)
+
+#define USHRT_MAX  (SHRT_MAX * 2 + 1)
+#define UINT_MAX   ~0U
+#define ULONG_MAX  ~0LU
+
+char ca3_5_7[3][5][7];
+
+void nowarn_ca_3_5_ssi (short i)
+{
+  if (i > SHRT_MAX - 1)
+    i = SHRT_MAX - 1;
+
+  ca3_5_7[i][0][0] = __LINE__;
+  ca3_5_7[i][0][1] = __LINE__;
+  ca3_5_7[i][0][2] = __LINE__;
+  ca3_5_7[i][0][3] = __LINE__;
+  ca3_5_7[i][0][4] = __LINE__;
+  ca3_5_7[i][0][5] = __LINE__;
+  ca3_5_7[i][0][6] = __LINE__;
+
+  ca3_5_7[i][1][0] = __LINE__;
+  ca3_5_7[i][1][1] = __LINE__;
+  ca3_5_7[i][1][2] = __LINE__;
+  ca3_5_7[i][1][3] = __LINE__;
+  ca3_5_7[i][1][4] = __LINE__;
+  ca3_5_7[i][1][5] = __LINE__;
+  ca3_5_7[i][1][6] = __LINE__;
+
+  ca3_5_7[i][2][0] = __LINE__;
+  ca3_5_7[i][2][1] = __LINE__;
+  ca3_5_7[i][2][2] = __LINE__;
+  ca3_5_7[i][2][3] = __LINE__;
+  ca3_5_7[i][2][4] = __LINE__;
+  ca3_5_7[i][2][5] = __LINE__;
+  ca3_5_7[i][2][6] = __LINE__;
+
+  ca3_5_7[i][3][0] = __LINE__;
+  ca3_5_7[i][3][1] = __LINE__;
+  ca3_5_7[i][3][2] = __LINE__;
+  ca3_5_7[i][3][3] = __LINE__;
+  ca3_5_7[i][3][4] = __LINE__;
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[i][3][6] = __LINE__;
+
+  ca3_5_7[i][4][0] = __LINE__;
+  ca3_5_7[i][4][1] = __LINE__;
+  ca3_5_7[i][4][2] = __LINE__;
+  ca3_5_7[i][4][3] = __LINE__;
+  ca3_5_7[i][4][4] = __LINE__;
+  ca3_5_7[i][4][5] = __LINE__;
+  ca3_5_7[i][4][6] = __LINE__;
+
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_usi (unsigned short i)
+{
+  if (i > USHRT_MAX - 1)
+    i = USHRT_MAX - 1;
+
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_si (int i)
+{
+  if (i > INT_MAX - 1)
+    i = INT_MAX - 1;
+
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_ui (unsigned i)
+{
+  if (i > UINT_MAX - 1)
+    i = UINT_MAX - 1;
+
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_li (long i)
+{
+  if (i > LONG_MAX - 1)
+    i = LONG_MAX - 1;
+
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
+
+void nowarn_ca_3_5_uli (unsigned long i)
+{
+  if (i > ULONG_MAX - 1)
+    i = ULONG_MAX - 1;
+
+  ca3_5_7[i][3][5] = __LINE__;
+  ca3_5_7[1][i][5] = __LINE__;
+  ca3_5_7[2][3][i] = __LINE__;
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c
new file mode 100644
index 00000000000..26568f8366d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-54.c
@@ -0,0 +1,103 @@
+/* Verify that writes at excessive offsets into flexible array members
+   of extern or allocated objects of unknow size are diagnosed.
+   { dg-do compile }
+   { dg-options "-O2" } */
+
+#define DIFF_MAX __PTRDIFF_MAX__
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+void* memset (void*, int, size_t);
+
+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'" }
+
+  char *p = caxcc.a;
+  size_t idx = DIFF_MAX - 4;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 2" }
+  sink (p);
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 1" }
+
+  ++idx;
+  memset (p + idx, 0, 3);               // { dg-warning "writing 3 bytes into a region of size 0" }
+}
+
+
+void char_flexarray_var_off_cst_size (ptrdiff_t idx)
+{
+  extern struct { char n, a[]; }
+    caxvc;                              // { dg-message "destination object 'caxvc'" }
+
+  char *p = caxvc.a;
+
+  if (idx < DIFF_MAX - 4)
+    idx = DIFF_MAX - 4;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+}
+
+
+void char_flexarray_var_off_var_size (size_t n, ptrdiff_t idx)
+{
+  extern struct { char n, a[]; }
+    caxvv;                              // { dg-message "destination object 'caxvv'" }
+
+  char *p = caxvv.a;
+
+  if (idx < DIFF_MAX - 4)
+    idx = DIFF_MAX - 4;
+
+  if (n < 3 || 7 < n)
+    n = 3;
+
+  memset (p + idx, 0, n);
+  sink (p);
+
+  ++n;
+  memset (p + idx, 0, n);               // { dg-warning "writing between 4 and 8 bytes into a region of size 3" }
+}
+
+
+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" }
+
+  if (idx < DIFF_MAX - 4)
+    idx = DIFF_MAX - 4;
+
+  memset (p->a + idx, 0, 3);
+  sink (p);
+
+  memset (p->a + idx, 0, 5);            // { dg-warning "writing 5 bytes into a region of size 3" }
+}
+
+
+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'" }
+
+  int *p = iaxc.a;
+  size_t idx = DIFF_MAX / sizeof *p - 1;
+
+  memset (p + idx, 0, 3);
+  sink (p);
+
+  memset (p + idx, 0, 5);               // { dg-warning "writing 5 bytes into a region of size 3" }
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-55.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-55.c
new file mode 100644
index 00000000000..25f5b82d9be
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-55.c
@@ -0,0 +1,97 @@
+/* Verify that offsets in "anti-ranges" are handled correctly.
+   { dg-do compile }
+   { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+void* memset (void*, int, size_t);
+
+void sink (void*, ...);
+#define T(x) sink (x)
+
+
+void int_range_add_sub_ (int i, int j)
+{
+  if (i < 1) i = 1;
+  if (j > -1) j = -1;
+
+  char ca5[5];              // { dg-message "at offset \\\[1, 5]" "note" }
+  char *p0 = ca5;           // offset
+  char *p1 = p0 + i;        //  1-5
+  char *p2 = p1 + i;        //  2-5
+  char *p3 = p2 + j;        //  0-4
+  char *p4 = p3 + j;        //  0-3
+  char *p5 = p4 + j;        //  0-2
+  char *p6 = p5 + j;        //  0-1
+  char *p7 = p6 + i;        //  1-2
+
+  memset (p7, 0, 5);        // { dg-warning "writing 5 bytes into a region of size 4" }
+
+  sink (p0, p1, p2, p3, p4, p5, p6, p7);
+}
+
+
+void ruint_arint_add (unsigned i, int j)
+{
+  i |= 1;   // [1, UINT_MAX]
+  j |= 1;   // [INT_MIN + 1, -1] U [1, INT_MAX]
+
+  char a[5];                // { dg-message "at offset \\\[1, 5]" "note" }
+  char *p0 = a;             // offset
+  char *p1 = p0 + i;        //  1-5
+  T (memset (p1, 0, 4));
+
+  char *p2 = p1 + j;        //  0-5
+  T (memset (p2, 0, 5));
+
+  char *p3 = p2 + i;        //  1-5
+  T (memset (p3, 0, 4));
+
+  char *p4 = p3 + j;        //  0-5
+  T (memset (p4, 0, 5));
+
+  char *p5 = p4 + i;        //  1-5
+  T (memset (p5, 0, 4));
+
+  char *p6 = p5 + j;        //  0-5
+  T (memset (p6, 0, 5));
+
+  char *p7 = p6 + i;        //  1-5
+  T (memset (p7, 0, 5));    // { dg-warning "writing 5 bytes into a region of size 4" "" }
+}
+
+
+void warn_ptrdiff_anti_range_add (ptrdiff_t i)
+{
+  i |= 1;
+
+  char ca5[5];              // { dg-message "at offset \\\[1, 5]" "pr?????" { xfail *-*-* } }
+  char *p0 = ca5;           // offset
+  char *p1 = p0 + i;        //  1-5
+  char *p2 = p1 + i;        //  2-5
+  char *p3 = p2 + i;        //  3-5
+  char *p4 = p3 + i;        //  4-5
+  char *p5 = p4 + i;        //   5
+
+  memset (p5, 0, 5);        // { dg-warning "writing 5 bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+
+  sink (p0, p1, p2, p3, p4, p5);
+}
+
+void warn_int_anti_range (int i)
+{
+  i |= 1;
+
+  char ca5[5];              // { dg-message "at offset \\\[1, 5]" "pr?????" { xfail *-*-* } }
+  char *p0 = ca5;           // offset
+  char *p1 = p0 + i;        //  1-5
+  char *p2 = p1 + i;        //  2-5
+  char *p3 = p2 + i;        //  3-5
+  char *p4 = p3 + i;        //  4-5
+  char *p5 = p4 + i;        //   5
+
+  memset (p5, 0, 5);        // { dg-warning "writing 5 bytes into a region of size 0" "pr?????" { xfail *-*-* } }
+
+  sink (p0, p1, p2, p3, p4, p5);
+}
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overread-5.c b/gcc/testsuite/gcc.dg/Wstringop-overread-5.c
new file mode 100644
index 00000000000..b75002b4295
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overread-5.c
@@ -0,0 +1,76 @@
+/* Verify -Wstringop-overread with a source pointer pointing either
+   before the beginning or past the end of an object.
+   { dg-do compile }
+   { dg-options "-O -Wall -Wno-array-bounds" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+size_t strlen (const char *);
+
+void sink (void*, ...);
+
+void off_sz_or_1 (size_t i)
+{
+  i |= 1;
+
+  /* Verify the offset in the notes only mentions the meaningful lower
+     bound and not a range with the excessive (and meaningless) upper
+     bound like [2, 9223372036854775807].  */
+  extern char a[1];
+  // { dg-message "at offset 1 into source object 'a'" "note" { target *-*-* } .-1 }
+  // { dg-message "at offset 2 " "note" { target *-*-* } .-2 }
+
+  char *p1 = a + i;
+  char *p2 = p1 + 1;
+  char *p3 = p1 - 1;
+
+  size_t n = 0;
+  n += strlen (p1);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p2);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p3);
+
+  sink (p1, p2, p3, n);
+}
+
+
+void off_sz_or_2 (size_t i)
+{
+  i |= 2;
+
+  extern char b[2];
+  // { dg-message "at offset 2 " "note" { target *-*-* } .-1 }
+  // { dg-message "at offset 3 " "note" { target *-*-* } .-2 }
+
+  char *p1 = b + i;
+  char *p2 = p1 + 1;
+  char *p3 = p1 - 1;
+
+  size_t n = 0;
+  n += strlen (p1);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p2);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p3);
+
+  sink (p1, p2, p3, n);
+}
+
+
+void off_sz_or_4 (size_t i)
+{
+  i |= 4;
+
+  extern char c[3];
+  // { dg-message "at offset 4 " "note" { target *-*-* } .-1 }
+  // { dg-message "at offset 5 " "note" { target *-*-* } .-2 }
+  // { dg-message "at offset 3 " "note" { target *-*-* } .-3 }
+
+  char *p1 = c + i;
+  char *p2 = p1 + 1;
+  char *p3 = p1 - 1;
+
+  size_t n = 0;
+  n += strlen (p1);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p2);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+  n += strlen (p3);     // { dg-warning "reading 1 or more bytes from a region of size 0" }
+
+  sink (p1, p2, p3, n);
+}
diff --git a/gcc/testsuite/gcc.dg/pr51683.c b/gcc/testsuite/gcc.dg/pr51683.c
index c477cd8fdd2..7a624e4055d 100644
--- a/gcc/testsuite/gcc.dg/pr51683.c
+++ b/gcc/testsuite/gcc.dg/pr51683.c
@@ -12,6 +12,9 @@ void *
 foo (void *p)
 {
   return bar ((void *) 0x12345000, p, 256);
+  /* Integers converted to pointers are assumed to be the result of
+     (invalid) arithmetic on null pointers.
+     { dg-prune-output "writing 256 bytes into a region of size 0" } */
 }
 
 /* { dg-final { scan-tree-dump "memcpy" "optimized" } } */
diff --git a/gcc/testsuite/gcc.target/i386/pr60693.c b/gcc/testsuite/gcc.target/i386/pr60693.c
index e6033a783f4..962c705f18f 100644
--- a/gcc/testsuite/gcc.target/i386/pr60693.c
+++ b/gcc/testsuite/gcc.target/i386/pr60693.c
@@ -11,3 +11,6 @@ foo (void)
   __builtin_memcpy (buf, (void *) 0x8000, 4096);
   bar (buf);
 }
+
+/* Reading from a constant address might triggers:
+   { dg-prune-output "\\\[-Wstringop-overread" } */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index f4d1c5ca256..ebb17cd852c 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -228,8 +228,20 @@ get_range (tree val, gimple *stmt, wide_int minmax[2],
 
   value_range_kind rng = get_range_info (val, minmax, minmax + 1);
   if (rng == VR_RANGE)
+    /* This may be an inverted range whose MINMAX[1] < MINMAX[0].  */
     return val;
 
+  if (rng == VR_ANTI_RANGE)
+    {
+      /* An anti-range is the same as an ordinary range with inverted
+	 bounds (where MINMAX[1] < MINMAX[0] is true) that may result
+	 from the conversion of a signed anti-range to unsigned.  */
+      wide_int tmp = minmax[0];
+      minmax[0] = minmax[1] + 1;
+      minmax[1] = wi::sub (tmp, 1);
+      return val;
+    }
+
   /* Do not handle anti-ranges and instead make use of the on-demand
      VRP if/when it becomes available (hopefully in GCC 11).  */
   return NULL_TREE;
@@ -2243,7 +2255,7 @@ maybe_warn_overflow (gimple *stmt, tree len,
     sprintf (offstr, "[%lli, %lli]",
 	     (long long) offrng[0].to_shwi (), (long long) offrng[1].to_shwi ());
 
-  if (destdecl)
+  if (destdecl && DECL_P (destdecl))
     {
       if (tree size = DECL_SIZE_UNIT (destdecl))
 	inform (DECL_SOURCE_LOCATION (destdecl),


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2020-10-12 15:07 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-12 15:07 [gcc r11-3827] Generalize compute_objsize to return maximum size/offset instead of failing (PR middle-end/97023) Martin Sebor

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