public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
@ 2017-07-16 23:47 Martin Sebor
  2017-07-20 20:46 ` Martin Sebor
  0 siblings, 1 reply; 39+ messages in thread
From: Martin Sebor @ 2017-07-16 23:47 UTC (permalink / raw)
  To: Gcc Patch List

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

Being implemented in the front end, the -Wrestrict warning
detects only trivial instances of violations.  The attached
patch extends the implementation to the middle-end where
data flow and alias analysis can be combined to detect even
complex cases of overlap.  This work is independent of but
follows on the patch below (waiting for review):

   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00036.html

The changes rely on extending the tree-ssa-{alias,structalias}
machinery in a simple way to answer the "must-alias" kind of
question in addition to the current "may-alias."

The rest of the changes are in gimple-fold.c, tree-ssa-strlen.c,
and builtins.c.

Even though this change makes -Wrestrict a lot more useful, it's
not a complete implementation.  Not all built-ins are handled yet
(e.g., strncat), and support for user-defined functions is still
subject to the limitations of the front end implementation.  To
complete the support, handlers for the missing string built-ins
will need to be added to tree-ssa-strlen.c, and the remaining
bits should be moved from the front end to somewhere in
the middle-end (e.g., calls.c).

Martin

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

PR middle-end/78918 - missing -Wrestrict on memcpy copying over self

gcc/ChangeLog:

	PR middle-end/78918
	* builtins.c (check_sizes): Add argument and handle -Wrestrict.
	Rename function arguments for clarity.
	(check_memop_sizes): Adjust.
	(expand_builtin_memchr): Ditto.
	(expand_builtin_strcat): Ditto.
	(expand_builtin_strcpy): Ditto.
	(expand_builtin_stpcpy): Ditto.
	(expand_builtin_stpncpy): Ditto.
	(expand_builtin_strncpy): Ditto.
	(expand_builtin_memcmp): Ditto.
	(expand_builtin_memory_chk): Ditto.
	(check_strncat_sizes): Ditto.  Rename locals for clarity.
	(expand_builtin_strncat): Ditto.
	(maybe_emit_chk_warning): Ditto.
	(maybe_emit_sprintf_chk_warning): Adjust.
	* cfgexpand.c (expand_call_stmt): Set TREE_NO_WARNING.
	* gimple-fold.c (gimple_fold_builtin_memory_op): Handle -Wrestrict.
	(gimple_fold_builtin_memory_chk): Ditto.
	(gimple_fold_builtin_stxcpy_chk): Ditto.
	* gimple.c (gimple_build_call_from_tree): Set call location.
	* tree-ssa-alias.c (ptr_deref_may_alias_decl_p): Add argument
	and rename...	
	(ptr_deref_alias_decl_p): ...to this.
	(ptr_derefs_may_alias_p): Add argument and rename...
	(ptr_derefs_alias_p): ...to this.
	(ptr_deref_may_alias_ref_p_1): Adjust.
	(aliasing_component_refs_p): Change return type and add argument.
	(decl_refs_may_alias_p): Ditto.
	(indirect_ref_may_alias_decl_p): Ditto.
	(indirect_refs_may_alias_p): Ditto.  Rename to...
	(indirect_refs_alias_p): ...this.
	(refs_alias_p_1): New static function.
	(refs_may_alias_p_1): Call it.
	(refs_must_alias_p_1): New extern function.
	* tree-ssa-alias.h (range_overlap): New inline function.
	(ranges_overlap_p): Call it.
	* tree-ssa-strlen.c (handle_builtin_strcpy): Handle -Wrestrict.
	(handle_builtin_strcat): Ditto.
	* tree-ssa-structalias.c (pt_solution_includes): Add argument.
	(pt_solutions_intersect_1): Ditto.
	(pt_solutions_intersect): Adjust.

gcc/c-family/ChangeLog:

	PR middle-end/78918
	* c-common.c (check_function_restrict): Suppress warning for
	built-in functions.
	* c.opt (-Wrestrict): Include in -Wall.

gcc/testsuite/ChangeLog:

	PR middle-end/78918
	* c-c++-common/Wrestrict.c: New test.
	* gcc.dg/Walloca-1.c: Suppress macro expansion tracking.
	* gcc.dg/pr69172.c: Prune -Wrestrict.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 2deef72..958ed51 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3038,37 +3038,45 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
 
 /* Try to verify that the sizes and lengths of the arguments to a string
    manipulation function given by EXP are within valid bounds and that
-   the operation does not lead to buffer overflow.  Arguments other than
-   EXP may be null.  When non-null, the arguments have the following
-   meaning:
-   SIZE is the user-supplied size argument to the function (such as in
-   memcpy(d, s, SIZE) or strncpy(d, s, SIZE).  It specifies the exact
-   number of bytes to write.
-   MAXLEN is the user-supplied bound on the length of the source sequence
+   the operation does not lead to buffer overflow or read past the end.
+   For copy operations the must not overlap also verify that the source
+   and destination do not overlap one another.
+   Arguments other than EXP may be null.  When non-null, the arguments
+   have the following meaning:
+   DST is the destination of a copy call or null otherwise.
+   SRC is the source of a copy call or null otherwise.
+   DSTWRITE is the number of bytes into the destination obtained from
+   the user-supplied size argument to the function (such as in memcpy(DST,
+   SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+   MAXREAD is the user-supplied bound on the length of the source sequence
    (such as in strncat(d, s, N).  It specifies the upper limit on the number
-   of bytes to write.
-   SRC is the source string (such as in strcpy(d, s)) when the expression
-   EXP is a string function call (as opposed to a memory call like memcpy).
-   As an exception, SRC can also be an integer denoting the precomputed
-   size of the source string or object (for functions like memcpy).
-   OBJSIZE is the size of the destination object specified by the last
+   of bytes to write.  If null it's taken to be the same as DSTWRITE.
+   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+   expression EXP is a string function call (as opposed to a memory call
+   like memcpy).  As an exception, SRCSTR can also be an integer denoting
+   the precomputed size of the source string or object (for functions like
+   memcpy).
+   DSTSIZE is the size of the destination object specified by the last
    argument to the _chk builtins, typically resulting from the expansion
-   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
-   OBJSIZE).
+   of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
+   DSTSIZE).
 
-   When SIZE is null LEN is checked to verify that it doesn't exceed
+   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
    If the call is successfully verified as safe from buffer overflow
-   the function returns true, otherwise false..  */
+   the function returns true, otherwise false.  */
 
 static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_sizes (int opt, tree exp, tree dst, tree src, tree dstwrite,
+	     tree maxread, tree srcstr, tree dstsize)
 {
   /* The size of the largest object is half the address space, or
      SSIZE_MAX.  (This is way too permissive.)  */
   tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
 
+  /* Either the length of the source string for string functions or
+     the size of the source object for raw memory functions.  */
   tree slen = NULL_TREE;
 
   tree range[2] = { NULL_TREE, NULL_TREE };
@@ -3077,28 +3085,28 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
      function like strcpy is not known and the only thing that is
      known is that it must be at least one (for the terminating nul).  */
   bool at_least_one = false;
-  if (src)
+  if (srcstr)
     {
-      /* SRC is normally a pointer to string but as a special case
+      /* SRCSTR is normally a pointer to string but as a special case
 	 it can be an integer denoting the length of a string.  */
-      if (POINTER_TYPE_P (TREE_TYPE (src)))
+      if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
 	{
 	  /* Try to determine the range of lengths the source string
 	     refers to.  If it can be determined and is less than
-	     the upper bound given by MAXLEN add one to it for
+	     the upper bound given by MAXREAD add one to it for
 	     the terminating nul.  Otherwise, set it to one for
-	     the same reason, or to MAXLEN as appropriate.  */
-	  get_range_strlen (src, range);
-	  if (range[0] && (!maxlen || TREE_CODE (maxlen) == INTEGER_CST))
+	     the same reason, or to MAXREAD as appropriate.  */
+	  get_range_strlen (srcstr, range);
+	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
 	    {
-	      if (maxlen && tree_int_cst_le (maxlen, range[0]))
-		range[0] = range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[0]))
+		range[0] = range[1] = maxread;
 	      else
 		range[0] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[0], size_one_node);
 
-	      if (maxlen && tree_int_cst_le (maxlen, range[1]))
-		range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[1]))
+		range[1] = maxread;
 	      else if (!integer_all_onesp (range[1]))
 		range[1] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[1], size_one_node);
@@ -3112,10 +3120,10 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	    }
 	}
       else
-	slen = src;
+	slen = srcstr;
     }
 
-  if (!size && !maxlen)
+  if (!dstwrite && !maxread)
     {
       /* When the only available piece of data is the object size
 	 there is nothing to do.  */
@@ -3123,20 +3131,18 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	return true;
 
       /* Otherwise, when the length of the source sequence is known
-	 (as with with strlen), set SIZE to it.  */
+	 (as with strlen), set DSTWRITE to it.  */
       if (!range[0])
-	size = slen;
+	dstwrite = slen;
     }
 
-  if (!objsize)
-    objsize = maxobjsize;
+  if (!dstsize)
+    dstsize = maxobjsize;
 
-  /* The SIZE is exact if it's non-null, constant, and in range of
-     unsigned HOST_WIDE_INT.  */
-  bool exactsize = size && tree_fits_uhwi_p (size);
+  if (dstwrite)
+    get_size_range (dstwrite, range);
 
-  if (size)
-    get_size_range (size, range);
+  tree func = get_callee_fndecl (exp);
 
   /* First check the number of bytes to be written against the maximum
      object size.  */
@@ -3149,30 +3155,34 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	warning_at (loc, opt,
 		    "%K%qD specified size %E "
 		    "exceeds maximum object size %E",
-		    exp, get_callee_fndecl (exp), range[0], maxobjsize);
+		    exp, func, range[0], maxobjsize);
 	  else
 	    warning_at (loc, opt,
 			"%K%qD specified size between %E and %E "
 			"exceeds maximum object size %E",
-			exp, get_callee_fndecl (exp),
+			exp, func,
 			range[0], range[1], maxobjsize);
       return false;
     }
 
+  /* The number of bytes to write is "exact" if DSTWRITE is non-null,
+     constant, and in range of unsigned HOST_WIDE_INT.  */
+  bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite);
+
   /* Next check the number of bytes to be written against the destination
      object size.  */
-  if (range[0] || !exactsize || integer_all_onesp (size))
+  if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
     {
       if (range[0]
-	  && ((tree_fits_uhwi_p (objsize)
-	       && tree_int_cst_lt (objsize, range[0]))
-	      || (tree_fits_uhwi_p (size)
-		  && tree_int_cst_lt (size, range[0]))))
+	  && ((tree_fits_uhwi_p (dstsize)
+	       && tree_int_cst_lt (dstsize, range[0]))
+	      || (tree_fits_uhwi_p (dstwrite)
+		  && tree_int_cst_lt (dstwrite, range[0]))))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
 
-	  if (size == slen && at_least_one)
+	  if (dstwrite == slen && at_least_one)
 	    {
 	      /* This is a call to strcpy with a destination of 0 size
 		 and a source of unknown length.  The call will write
@@ -3180,7 +3190,7 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else if (tree_int_cst_equal (range[0], range[1]))
 	    warning_at (loc, opt,
@@ -3189,21 +3199,21 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 			      "of size %E overflows the destination")
 			 : G_("%K%qD writing %E bytes into a region "
 			      "of size %E overflows the destination")),
-			exp, get_callee_fndecl (exp), range[0], objsize);
+			exp, func, range[0], dstsize);
 	  else if (tree_int_cst_sign_bit (range[1]))
 	    {
 	      /* Avoid printing the upper bound if it's invalid.  */
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else
 	    warning_at (loc, opt,
 			"%K%qD writing between %E and %E bytes into "
 			"a region of size %E overflows the destination",
-			exp, get_callee_fndecl (exp), range[0],	range[1],
-			objsize);
+			exp, func, range[0], range[1],
+			dstsize);
 
 	  /* Return error when an overflow has been detected.  */
 	  return false;
@@ -3213,11 +3223,15 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
   /* Check the maximum length of the source sequence against the size
      of the destination object if known, or against the maximum size
      of an object.  */
-  if (maxlen)
+  if (maxread)
     {
-      get_size_range (maxlen, range);
+      get_size_range (maxread, range);
+
+      /* Use the lower end for MAXREAD from now on.  */
+      if (range[0])
+	maxread = range[0];
 
-      if (range[0] && objsize && tree_fits_uhwi_p (objsize))
+      if (range[0] && dstsize && tree_fits_uhwi_p (dstsize))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
@@ -3231,40 +3245,41 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], maxobjsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], range[1], maxobjsize);
 
 	      return false;
 	    }
 
-	  if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
+	  if (dstsize != maxobjsize && tree_int_cst_lt (dstsize, range[0]))
 	    {
 	      if (tree_int_cst_equal (range[0], range[1]))
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], objsize);
+			    exp, func,
+			    range[0], dstsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], range[1], objsize);
+			    exp, func,
+			    range[0], range[1], dstsize);
 	      return false;
 	    }
 	}
     }
 
+  /* Check for reading past the end of SRC.  */
   if (slen
-      && slen == src
-      && size && range[0]
+      && slen == srcstr
+      && dstwrite && range[0]
       && tree_int_cst_lt (slen, range[0]))
     {
       location_t loc = tree_nonartificial_location (exp);
@@ -3274,23 +3289,98 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		    (tree_int_cst_equal (range[0], integer_one_node)
 		     ? G_("%K%qD reading %E byte from a region of size %E")
 		     : G_("%K%qD reading %E bytes from a region of size %E")),
-		    exp, get_callee_fndecl (exp), range[0], slen);
+		    exp, func, range[0], slen);
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
 	  warning_at (loc, opt,
 		      "%K%qD reading %E or more bytes from a region "
 		      "of size %E",
-		      exp, get_callee_fndecl (exp), range[0], slen);
+		      exp, func, range[0], slen);
 	}
       else
 	warning_at (loc, opt,
 		    "%K%qD reading between %E and %E bytes from a region "
 		    "of size %E",
-		    exp, get_callee_fndecl (exp), range[0], range[1], slen);
+		    exp, func, range[0], range[1], slen);
       return false;
     }
 
+  /* Check for overlapping copies.  Avoid warning if one has already
+     been issued (unlike -Wstringop-overflow, -Wrestrict is checked
+     in many places and TREE_NO_WARNING applies to it but not to
+     the former (there should be a bit for every kind of warning).  */
+  if (src && dst
+      && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+      && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+      && !TREE_NO_WARNING (exp))
+    {
+      bool have_sizes = dstwrite && tree_fits_uhwi_p (dstwrite);
+
+      if (maxread)
+	have_sizes &= tree_fits_uhwi_p (maxread);
+      else if (slen)
+	{
+	  /* When DSTWRITE is known, set the maximum number of bytes
+	     to access to DSTWRITE either when the length of the source
+	     sequence is unknown, or when DSTWRITE is less than the length.  */
+	  maxread = (have_sizes
+		     && (at_least_one || tree_int_cst_lt (dstwrite, slen))
+		     ? dstwrite : slen);
+	}
+      else
+	{
+	  maxread = dstwrite;
+	  have_sizes = false;
+	}
+
+      /* The exact number bytes copied is known only if the sizes
+	 haven't adjusted earlier on.  */
+      have_sizes &= !at_least_one || (dstwrite && dstwrite != slen);
+
+      /* Initialize the Alias Oracle references for each of the two
+	 accesses.  */
+      ao_ref dstref, srcref;
+      ao_ref_init_from_ptr_and_size (&dstref, dst, dstwrite);
+      ao_ref_init_from_ptr_and_size (&srcref, src, maxread);
+
+      /* This shouldn't happen.  */
+      if (dstref.size == -1)
+	{
+	  dstref.size = BITS_PER_UNIT;
+	  have_sizes = false;
+	}
+
+      /* And neither should this.  */
+      if (srcref.size == -1)
+	{
+	  srcref.size = BITS_PER_UNIT;
+	  have_sizes = false;
+	}
+
+      HOST_WIDE_INT offset;
+      if (unsigned HOST_WIDE_INT overlap
+	  = refs_must_alias_p_1 (&dstref, &srcref, &offset))
+	{
+	  location_t loc = tree_nonartificial_location (exp);
+
+	  warning_at (loc, OPT_Wrestrict,
+		      have_sizes
+		      ? (overlap > 1
+			 ? G_("%K%qD source sequence overlaps %wu bytes "
+			      "of destination at offset %wi")
+			 : G_("%K%qD source sequence overlaps %wu byte "
+			      "of destination at offset %wi"))
+		      : (overlap > 1
+			 ? G_("%K%qD source sequence may overlap %wu bytes "
+			      "of destination at offset %wi")
+			 : G_("%K%qD source sequence may overlap %wu byte "
+			      "of destination at offset %wi")),
+		      exp, func, overlap, offset);
+	  return false;
+	}
+    }
+
   return true;
 }
 
@@ -3330,8 +3420,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size)
   tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
   tree dstsize = compute_objsize (dest, 0);
 
-  return check_sizes (OPT_Wstringop_overflow_, exp,
-		      size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+  return check_sizes (OPT_Wstringop_overflow_, exp, dest, src,
+		      size, /*maxread=*/NULL_TREE, srcsize, dstsize);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -3353,8 +3443,8 @@ expand_builtin_memchr (tree exp, rtx)
     {
       tree size = compute_objsize (arg1, 0);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, len, /*maxlen=*/NULL_TREE,
-		   size, /*objsize=*/NULL_TREE);
+		   exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		   /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
     }
 
   return NULL_RTX;
@@ -3652,7 +3742,8 @@ expand_builtin_strcat (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+	       exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+	       destsize);
 
   return NULL_RTX;
 }
@@ -3671,11 +3762,15 @@ expand_builtin_strcpy (tree exp, rtx target)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
 
-  if (warn_stringop_overflow)
+  if (warn_stringop_overflow || warn_restrict)
     {
-      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+      /* Use Object Size type-1 for -Wrestrict.  */
+      int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 2;
+
+      tree destsize = compute_objsize (dest, ost);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+		   exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		   src, destsize);
     }
 
   return expand_builtin_strcpy_args (dest, src, target);
@@ -3714,7 +3809,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
     {
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+		   exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+		   destsize);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -3799,7 +3895,7 @@ expand_builtin_stpncpy (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+	       exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 
   return NULL_RTX;
 }
@@ -3829,7 +3925,7 @@ check_strncat_sizes (tree exp, tree objsize)
 {
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
 
   /* Try to determine the range of lengths that the source expression
      refers to.  */
@@ -3853,32 +3949,33 @@ check_strncat_sizes (tree exp, tree objsize)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize)
-      && tree_int_cst_equal (objsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return false;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
   /* The number of bytes to write is LEN but check_sizes will also
      check SRCLEN if LEN's value isn't known.  */
   return check_sizes (OPT_Wstringop_overflow_,
-		      exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+		      exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+		      objsize);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -3896,7 +3993,7 @@ expand_builtin_strncat (tree exp, rtx)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
   /* The upper bound on the number of bytes to write.  */
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
   /* The length of the source sequence.  */
   tree slen = c_strlen (src, 1);
 
@@ -3919,32 +4016,31 @@ expand_builtin_strncat (tree exp, rtx)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize)
-      && tree_int_cst_equal (destsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return NULL_RTX;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
-     check SRCLEN if LEN's value isn't known.  */
+  /* The number of bytes to write is SRCLEN.  */
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+	       exp, dest, src, NULL_TREE, maxread, srclen, destsize);
 
   return NULL_RTX;
 }
@@ -3975,7 +4071,7 @@ expand_builtin_strncpy (tree exp, rtx target)
 	  /* The number of bytes to write is LEN but check_sizes will also
 	     check SLEN if LEN's value isn't known.  */
 	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+		       exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 	}
 
       /* We must be passed a constant len and src parameter.  */
@@ -4319,13 +4415,13 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
     {
       tree size = compute_objsize (arg1, 0);
       if (check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE))
+		       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		       /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
 	{
 	  size = compute_objsize (arg2, 0);
 	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE);
+		       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		       /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
 	}
     }
 
@@ -9708,8 +9804,6 @@ static rtx
 expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			   enum built_in_function fcode)
 {
-  tree dest, src, len, size;
-
   if (!validate_arglist (exp,
 			 POINTER_TYPE,
 			 fcode == BUILT_IN_MEMSET_CHK
@@ -9717,13 +9811,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
-  len = CALL_EXPR_ARG (exp, 2);
-  size = CALL_EXPR_ARG (exp, 3);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 3);
 
   bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
-			       exp, len, /*maxlen=*/NULL_TREE,
+			       exp, dest, src, len, /*maxread=*/NULL_TREE,
 			       /*str=*/NULL_TREE, size);
 
   if (!tree_fits_uhwi_p (size))
@@ -9833,7 +9927,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
   /* The maximum length of the source sequence in a bounded operation
      (such as __strncat_chk) or null if the operation isn't bounded
      (such as __strcat_chk).  */
-  tree maxlen = NULL_TREE;
+  tree maxread = NULL_TREE;
 
   switch (fcode)
     {
@@ -9854,27 +9948,27 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
     case BUILT_IN_STRNCAT_CHK:
       catstr = CALL_EXPR_ARG (exp, 0);
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STPNCPY_CHK:
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
-      maxlen = CALL_EXPR_ARG (exp, 1);
+      maxread = CALL_EXPR_ARG (exp, 1);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
     default:
       gcc_unreachable ();
     }
 
-  if (catstr && maxlen)
+  if (catstr && maxread)
     {
       /* Check __strncat_chk.  There is no way to determine the length
 	 of the string to which the source string is being appended so
@@ -9883,8 +9977,11 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       return;
     }
 
-  check_sizes (OPT_Wstringop_overflow_, exp,
-	       /*size=*/NULL_TREE, maxlen, srcstr, objsize);
+  /* The destination argument is the first one for all built-ins above.  */
+  tree dst = CALL_EXPR_ARG (exp, 0);
+
+  check_sizes (OPT_Wstringop_overflow_, exp, dst, srcstr,
+	       /*size=*/NULL_TREE, maxread, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9940,8 +10037,10 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 
   /* Add one for the terminating nul.  */
   len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
+
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
+	       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
+	       /*maxread=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index feb0904..c86a393 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5261,14 +5261,20 @@ check_function_restrict (const_tree fndecl, const_tree fntype,
 			 int nargs, tree *argarray)
 {
   int i;
-  tree parms;
+  tree parms = TYPE_ARG_TYPES (fntype);
 
   if (fndecl
-      && TREE_CODE (fndecl) == FUNCTION_DECL
-      && DECL_ARGUMENTS (fndecl))
-    parms = DECL_ARGUMENTS (fndecl);
-  else
-    parms = TYPE_ARG_TYPES (fntype);
+      && TREE_CODE (fndecl) == FUNCTION_DECL)
+    {
+      /* Skip checking built-ins here.  They are checked in more
+	 detail elsewhere.  */
+      if (DECL_BUILT_IN (fndecl)
+	  && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+	return;
+
+      if (DECL_ARGUMENTS (fndecl))
+	parms = DECL_ARGUMENTS (fndecl);
+    }
 
   for (i = 0; i < nargs; i++)
     TREE_VISITED (argarray[i]) = 0;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index e0ad3ab..01506fd 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1162,7 +1162,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall)
 Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier.
 
 Wrestrict
-C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++)
+C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
 Warn when an argument passed to a restrict-qualified parameter aliases with
 another argument.
 
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 3e1d24d..76b9b15 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2627,6 +2627,9 @@ expand_call_stmt (gcall *stmt)
   if (gimple_call_nothrow_p (stmt))
     TREE_NOTHROW (exp) = 1;
 
+  if (gimple_no_warning_p (stmt))
+    TREE_NO_WARNING (exp) = 1;
+
   CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt);
   CALL_EXPR_MUST_TAIL_CALL (exp) = gimple_call_must_tail_p (stmt);
   CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b9d071b..14205f5 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3852,6 +3852,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
 -Wparentheses  @gol
 -Wpointer-sign  @gol
 -Wreorder   @gol
+-Wrestrict   @gol
 -Wreturn-type  @gol
 -Wsequence-point  @gol
 -Wsign-compare @r{(only in C++)}  @gol
@@ -6535,11 +6536,25 @@ reduce the padding and so make the structure smaller.
 Warn if anything is declared more than once in the same scope, even in
 cases where multiple declaration is valid and changes nothing.
 
-@item -Wrestrict
+@item -Wno-restrict
 @opindex Wrestrict
 @opindex Wno-restrict
-Warn when an argument passed to a restrict-qualified parameter
-aliases with another argument.
+Warn when an object referenced by a @code{restrict}-qualified parameter
+(or, in C++, @code{__restrict}-qualified parameter) is aliased by another
+argument, or when copies between such objects overlap.  For example,
+the call to the @code{strcpy} unction below attempts to truncate the string
+by replacing its initial characters with the last four.  However, because
+he call writes the terminating NUL into @code{a[4]}, the copies overlap and
+the call is diagnosed.
+
+@smallexample
+struct foo
+@{
+  char a[] = "abcd1234";
+  strcpy (a, a + 4);
+@};
+@end smallexample
+The @option{-Wrestrict} is included in @option{-Wall}.
 
 @item -Wnested-externs @r{(C and Objective-C only)}
 @opindex Wnested-externs
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index d94dc9c..f55cffa 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -57,6 +57,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "fold-const-call.h"
 #include "asan.h"
+#include "intl.h"
+#include "diagnostic-core.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -644,7 +646,13 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
   tree destvar, srcvar;
   location_t loc = gimple_location (stmt);
 
-  /* If the LEN parameter is zero, return DEST.  */
+  tree func = gimple_call_fndecl (stmt);
+  bool nowarn = gimple_no_warning_p (stmt);
+  bool check_overlap = (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+			&& DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+			&& !nowarn);
+
+    /* If the LEN parameter is zero, return DEST.  */
   if (integer_zerop (len))
     {
       gimple *repl;
@@ -666,6 +674,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
      DEST{,+LEN,+LEN-1}.  */
   if (operand_equal_p (src, dest, 0))
     {
+      if (check_overlap)
+	warning_at (loc, OPT_Wrestrict,
+		    "%qD source argument is the same as destination",
+		    func);
+
       unlink_stmt_vdef (stmt);
       if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
 	release_ssa_name (gimple_vdef (stmt));
@@ -715,6 +728,40 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	  unsigned ilen = tree_to_uhwi (len);
 	  if (pow2p_hwi (ilen))
 	    {
+	      if (check_overlap)
+		{
+		  ao_ref dstref, srcref;
+		  ao_ref_init_from_ptr_and_size (&dstref, dest, len);
+		  ao_ref_init_from_ptr_and_size (&srcref, src, len);
+
+		  if (refs_may_alias_p_1 (&dstref, &srcref, false))
+		    {
+		      /* Defer folding until both arguments are in an SSA
+			 form so that aliasing can be determined with greater
+			 accuracy.  */
+		      if (TREE_CODE (dest) != SSA_NAME
+			  || TREE_CODE (src) != SSA_NAME)
+			return false;
+
+		      HOST_WIDE_INT offset;
+		      if (unsigned HOST_WIDE_INT size
+			  = refs_must_alias_p_1 (&dstref, &srcref, &offset))
+			{
+			  warning_at (loc, OPT_Wrestrict,
+				      size > 1
+				      ? G_("%qD source sequence overlaps "
+					   "%wu bytes of destination at offset "
+					   "%wi")
+				      : G_("%qD source sequence may overlap "
+					   "%wu byte of destination at offset "
+					   "%wi"),
+				      func, size, offset);
+			  gimple_set_no_warning (stmt, true);
+			  return false;
+			}
+		    }
+		}
+
 	      tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
 	      if (type
 		  && TYPE_MODE (type) != BLKmode
@@ -1028,6 +1075,29 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	    }
 	}
 
+      if (check_overlap)
+	{
+	  ao_ref dstref, srcref;
+	  ao_ref_init_from_ptr_and_size (&dstref, dest, len);
+	  ao_ref_init_from_ptr_and_size (&srcref, src, len);
+
+	  HOST_WIDE_INT offset;
+	  if (unsigned HOST_WIDE_INT overlap
+	      = refs_must_alias_p_1 (&dstref, &srcref, &offset))
+	    {
+	      warning_at (loc, OPT_Wrestrict,
+			  overlap > 1
+			  ? G_("%qD source sequence overlaps %wu bytes of "
+			       "destination at offset %wi")
+			  : G_("%qD source sequence overlaps %wu byte of "
+			       "destination at offset %wi"),
+			  func, overlap, offset);
+
+	      gimple_set_no_warning (stmt, true);
+	      return false;
+	    }
+	}
+
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
 	{
@@ -1484,12 +1554,19 @@ static bool
 gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
 			    tree dest, tree src)
 {
-  location_t loc = gimple_location (gsi_stmt (*gsi));
+  gimple *stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
   tree fn;
 
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -2272,6 +2349,15 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
      (resp. DEST+LEN for __mempcpy_chk).  */
   if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
     {
+      if (fcode != BUILT_IN_MEMMOVE && fcode != BUILT_IN_MEMMOVE_CHK)
+	{
+	  tree func = gimple_call_fndecl (stmt);
+
+	  warning_at (loc, OPT_Wrestrict,
+		      "%qD source argument is the same as destination",
+		      func);
+	}
+
       if (fcode != BUILT_IN_MEMPCPY_CHK)
 	{
 	  replace_call_with_value (gsi, dest);
@@ -2373,6 +2459,12 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 488f8c8..f2dcece 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -359,6 +359,7 @@ gimple_build_call_from_tree (tree t)
     gimple_call_set_arg (call, i, CALL_EXPR_ARG (t, i));
 
   gimple_set_block (call, TREE_BLOCK (t));
+  gimple_set_location (call, EXPR_LOCATION (t));
 
   /* Carry all the CALL_EXPR flags to the new GIMPLE_CALL.  */
   gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t));
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
new file mode 100644
index 0000000..729ced5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -0,0 +1,424 @@
+/* PR 35503 - Warn about restricted pointers
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+int value (void);
+
+int range (int min, int max)
+{
+  int val = value ();
+  return val < min || max < val ? min : val;
+}
+
+void sink (void*);
+
+/* Exercise memcpy with constant or known arguments.  */
+
+void test_memcpy_cst (void)
+{
+  {
+    char d[7];
+    sink (d);
+
+    const char *s = d;
+    memcpy (d, s, 0);
+    sink (d);
+  }
+
+  {
+    char d[7];
+    sink (d);
+
+    const char *s = d;
+    memcpy (d, s, 1);             /* { dg-warning "\\\[-Wrestrict" } */
+    sink (d);
+  }
+
+  {
+    char d[7];
+    sink (d);
+
+    const char *s = d + 1;
+    memcpy (d, s, 1);
+    sink (d);
+  }
+
+  {
+    char d[7];
+    sink (d);
+
+    const char *s = d + 1;
+    memcpy (d, s, 2);             /* { dg-warning "\\\[-Wrestrict" } */
+    sink (d);
+  }
+
+  {
+    char a[3][7];
+    sink (a);
+
+    void *d = a[0];
+    const void *s = a[1];
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0];
+    s = a[1];
+    memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&a);
+  }
+
+  {
+    struct {
+      char a[7];
+      char b[7];
+      char c[7];
+    } x;
+    sink (&x);
+
+    void *d = x.a;
+    const void *s = x.b;
+    memcpy (d, s, sizeof x.a);
+    sink (&x);
+
+    d = x.a;
+    s = x.a;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 4;
+    s = x.b;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 6;
+    s = x.b;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b;
+    memcpy (d, s, 1);             /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b + 1;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 1;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 2;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 3;
+    memcpy (d, s, sizeof x.b);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+  }
+}
+
+/* Exercise memcpy with destination and source of unknown size.  */
+
+void test_memcpy_var (char *d, const char *s)
+{
+  int n = value ();
+
+  memcpy (d, d, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  memcpy (d, &d[0], n);           /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  memcpy (&d[0], d,  n);          /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  /* The following overlaps if n is greater than 1.  */
+  s = d + 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  /* The following only overlaps if strlen (s + i) >= i so it's not
+     diagnosed.  */
+  s = d + n;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 2;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 3;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 4;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  s = d + 5;
+  n = 7;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+
+  n = range (0, 1);
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+/* Exercise the absence of warnings with memmove.  */
+
+void test_memmove (void)
+{
+  {
+    char d[7];
+    sink (d);
+
+    const void *s = d;
+    memmove (d, s, 7);
+    sink (d);
+
+    s = d + 1;
+    memmove (d, s, 6);
+    sink (d);
+
+    s = d + 2;
+    memmove (d + 1, s, 5);
+    sink (d);
+  }
+}
+
+/* Exercise strcat with constant or known arguments.  */
+
+void test_strcat_cst (const char *s)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[9] = init;				\
+    strcat ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2);            /* { dg-warning "\\\[-Wrestrict" } */
+  /* The nul copied from a[3] to a[3] overwrites itself so this is
+     diagnosed.  */
+  T ("123", a, a + 3);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 4);
+  T ("123", a, a + 5);
+  T ("123", a, a + 6);
+  T ("123", a, a + 7);
+  T ("123", a, a + 8);
+
+  T ("123", a + 1, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 2, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 3, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 4, a);            /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+  T ("123", a + 5, a);            /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T ("123",      a, "123");
+  T ("123",      a, s);
+  T ("12345678", a, s);
+}
+
+/* Exercise strcat with destination and source of unknown length.  */
+
+void test_strcat_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    strcat ((dst), (src));			\
+    sink ((dst));				\
+  } while (0)
+
+  T (d, d);                       /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 1);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 2);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 3);
+
+  int n = value ();
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T (d, "123");
+  T (d + 1, "1234");
+  T (d + n, "12345");
+  T (d, s);
+  T (d + 1, s);
+  T (d + n, s);
+
+  /* This one isn't so obvious and might be worth diagnosing with "may
+     overlap," even more so than the analogous strcpy case.  */
+  T (d, d + n);
+}
+
+/* Exercise strcpy with constant or known arguments.  */
+
+void test_strcpy_cst (void)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[8] = init;				\
+    strcpy ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2);
+  T ("123", a, a + 3);
+
+  /* The terminating nul written to d[2] overwrites s[0].  */
+  T ("1234", a, a + 2);           /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("12345", a, a + 2);          /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* It's not 100% clear if this should trigger a warning.  The non-const
+     string case doesn't.  */
+  T ("123", a, a + value ());
+
+  /* This happens to be safe in GCC but it's still wrong.  */
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("123", a + 1, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 2, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 3, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 4, a);
+}
+
+/* Exercise strcpy with destination and source of unknown length.  */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    strcpy ((dst), (src));			\
+    sink (dst);					\
+  } while (0)
+
+  T (d, &d[0]);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (&d[0], d);                   /* { dg-warning "\\\[-Wrestrict" } */
+
+  s = d;
+  T (d, s);                       /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* The following overlaps if *s is not nul.  It arguably should be
+     diagnosed.  */
+  T (d, d + 1);
+
+  /* The following overlaps only if strlen (s + n) >= n so it's not
+     diagnosed.  */
+  s = d + value ();
+  T (d, s);
+}
+
+/* Exercise strncpy with constant or known arguments.  */
+
+void test_strncpy_cst (void)
+{
+#undef T
+#define T(init, dst, src, size) do {		\
+    char a[7] = init;				\
+    strncpy (dst, src, size);			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a, 0);
+  T ("123", a, a, 1);             /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1, 1);
+  T ("123", a, a + 1, 2);         /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1, 3);         /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2, 1);
+  T ("123", a, a + 2, 2);
+  /* The third written byte (nul) overwrites a[2].  */
+  T ("123", a, a + 2, 3);         /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("1234", a, a + 2, 1);
+  T ("1234", a, a + 2, 2);
+  /* The terminating nul written to a[2] overwrites s[0].  */
+  T ("1234", a, a + 2, 3);        /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("12345", a, a + 2, 1);
+  T ("12345", a, a + 2, 2);
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("12345", a, a + 2, 4);       /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+/* Exercise strncpy with destination and source of unknown length.  */
+
+void test_strncpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src, size) do {			\
+    strncpy (dst, src, size);			\
+    sink ((dst));				\
+  } while (0)
+
+  int n = value ();
+
+  T (d, d, 1);                    /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d, n);                    /* { dg-warning "\\\[-Wrestrict" } */
+
+  T (d, s, 1);
+  T (d, s, n);
+
+  T (d,     d + 1, 1);
+  T (d,     d + 1, 2);            /* { dg-warning "\\\[-Wrestrict" } */
+  T (d + 1, d,     1);
+  T (d + 1, d,     2);            /* { dg-warning "\\\[-Wrestrict" } */
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c b/gcc/testsuite/gcc.dg/Walloca-1.c
index ad39373..85e9160 100644
--- a/gcc/testsuite/gcc.dg/Walloca-1.c
+++ b/gcc/testsuite/gcc.dg/Walloca-1.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* { dg-require-effective-target alloca } */
-/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+/* { dg-options "-Walloca-larger-than=2000 -O2 -ftrack-macro-expansion=0" } */
 
 #define alloca __builtin_alloca
 
diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c
index c0e7463..908d5a6 100644
--- a/gcc/testsuite/gcc.dg/pr69172.c
+++ b/gcc/testsuite/gcc.dg/pr69172.c
@@ -1,4 +1,5 @@
-/* PR tree-optimization/69172 */
+/* PR tree-optimization/69172 - ICE in make_ssa_name_fn,
+   at tree-ssanames.c:266 */
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
 
@@ -43,3 +44,7 @@ f6 (int x)
 {
   return __builtin___mempcpy_chk (&a, &a, x, 0);
 }
+
+/* The calls above violate strict aliasing.  Eliminate the -Wrestrict
+   warnings they trigger.
+  { dg-prune-output "\\\[-Wrestrict]" } */
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 74ee2b0..21f6c61 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -149,12 +149,13 @@ ptr_deref_may_alias_global_p (tree ptr)
   return pt_solution_includes_global (&pi->pt);
 }
 
-/* Return true if dereferencing PTR may alias DECL.
+/* Return true if dereferencing PTR may or alias DECL.  When MUST_ALIAS
+   is true, return true only if PTR definitely aliases DECL.
    The caller is responsible for applying TBAA to see if PTR
    may access DECL at all.  */
 
 static bool
-ptr_deref_may_alias_decl_p (tree ptr, tree decl)
+ptr_deref_alias_decl_p (tree ptr, tree decl, bool must_alias = false)
 {
   struct ptr_info_def *pi;
 
@@ -170,7 +171,7 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
       || (!VAR_P (decl)
 	  && TREE_CODE (decl) != PARM_DECL
 	  && TREE_CODE (decl) != RESULT_DECL))
-    return true;
+    return !must_alias;
 
   /* Disregard pointer offsetting.  */
   if (TREE_CODE (ptr) == POINTER_PLUS_EXPR)
@@ -180,7 +181,7 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
 	  ptr = TREE_OPERAND (ptr, 0);
 	}
       while (TREE_CODE (ptr) == POINTER_PLUS_EXPR);
-      return ptr_deref_may_alias_decl_p (ptr, decl);
+      return ptr_deref_alias_decl_p (ptr, decl, must_alias);
     }
 
   /* ADDR_EXPR pointers either just offset another pointer or directly
@@ -215,12 +216,13 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
   return pt_solution_includes (&pi->pt, decl);
 }
 
-/* Return true if dereferenced PTR1 and PTR2 may alias.
+/* Return true if dereferenced PTR1 and PTR2 may alias.  When MUST_ALIAS
+   is true, return true only if PTR1 and PTR2 definitely alias.
    The caller is responsible for applying TBAA to see if accesses
    through PTR1 and PTR2 may conflict at all.  */
 
-bool
-ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
+static bool
+ptr_derefs_alias_p (tree ptr1, tree ptr2, bool must_alias)
 {
   struct ptr_info_def *pi1, *pi2;
 
@@ -237,7 +239,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
 	  ptr1 = TREE_OPERAND (ptr1, 0);
 	}
       while (TREE_CODE (ptr1) == POINTER_PLUS_EXPR);
-      return ptr_derefs_may_alias_p (ptr1, ptr2);
+      return ptr_derefs_alias_p (ptr1, ptr2, must_alias);
     }
   if (TREE_CODE (ptr2) == POINTER_PLUS_EXPR)
     {
@@ -246,7 +248,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
 	  ptr2 = TREE_OPERAND (ptr2, 0);
 	}
       while (TREE_CODE (ptr2) == POINTER_PLUS_EXPR);
-      return ptr_derefs_may_alias_p (ptr1, ptr2);
+      return ptr_derefs_alias_p (ptr1, ptr2, must_alias);
     }
 
   /* ADDR_EXPR pointers either just offset another pointer or directly
@@ -257,12 +259,12 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
       if (base
 	  && (TREE_CODE (base) == MEM_REF
 	      || TREE_CODE (base) == TARGET_MEM_REF))
-	return ptr_derefs_may_alias_p (TREE_OPERAND (base, 0), ptr2);
+	return ptr_derefs_alias_p (TREE_OPERAND (base, 0), ptr2, must_alias);
       else if (base
 	       && DECL_P (base))
-	return ptr_deref_may_alias_decl_p (ptr2, base);
+	return ptr_deref_alias_decl_p (ptr2, base, must_alias);
       else
-	return true;
+	return !must_alias;
     }
   if (TREE_CODE (ptr2) == ADDR_EXPR)
     {
@@ -270,12 +272,12 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
       if (base
 	  && (TREE_CODE (base) == MEM_REF
 	      || TREE_CODE (base) == TARGET_MEM_REF))
-	return ptr_derefs_may_alias_p (ptr1, TREE_OPERAND (base, 0));
+	return ptr_derefs_alias_p (ptr1, TREE_OPERAND (base, 0), must_alias);
       else if (base
 	       && DECL_P (base))
-	return ptr_deref_may_alias_decl_p (ptr1, base);
+	return ptr_deref_alias_decl_p (ptr1, base, must_alias);
       else
-	return true;
+	return !must_alias;
     }
 
   /* From here we require SSA name pointers.  Anything else aliases.  */
@@ -283,7 +285,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
       || TREE_CODE (ptr2) != SSA_NAME
       || !POINTER_TYPE_P (TREE_TYPE (ptr1))
       || !POINTER_TYPE_P (TREE_TYPE (ptr2)))
-    return true;
+    return !must_alias;
 
   /* We may end up with two empty points-to solutions for two same pointers.
      In this case we still want to say both pointers alias, so shortcut
@@ -296,11 +298,21 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
   pi1 = SSA_NAME_PTR_INFO (ptr1);
   pi2 = SSA_NAME_PTR_INFO (ptr2);
   if (!pi1 || !pi2)
-    return true;
+    return !must_alias;
 
   /* ???  This does not use TBAA to prune decls from the intersection
      that not both pointers may access.  */
-  return pt_solutions_intersect (&pi1->pt, &pi2->pt);
+  return pt_solutions_intersect (&pi1->pt, &pi2->pt, must_alias);
+}
+
+/* Return true if dereferenced PTR1 and PTR2 may alias.
+   The caller is responsible for applying TBAA to see if accesses
+   through PTR1 and PTR2 may conflict at all.  */
+
+bool
+ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
+{
+  return ptr_derefs_alias_p (ptr1, ptr2, false);
 }
 
 /* Return true if dereferencing PTR may alias *REF.
@@ -316,7 +328,7 @@ ptr_deref_may_alias_ref_p_1 (tree ptr, ao_ref *ref)
       || TREE_CODE (base) == TARGET_MEM_REF)
     return ptr_derefs_may_alias_p (ptr, TREE_OPERAND (base, 0));
   else if (DECL_P (base))
-    return ptr_deref_may_alias_decl_p (ptr, base);
+    return ptr_deref_alias_decl_p (ptr, base, false);
 
   return true;
 }
@@ -773,9 +785,11 @@ same_type_for_tbaa (tree type1, tree type2)
    on an indirect reference may alias.  REF2 is the only one that can
    be a decl in which case REF2_IS_DECL is true.
    REF1_ALIAS_SET, BASE1_ALIAS_SET, REF2_ALIAS_SET and BASE2_ALIAS_SET
-   are the respective alias sets.  */
+   are the respective alias sets.
+   The returned value is the size of the overlap if it can be determined,
+   otherwise it's HOST_WIDE_INT_M1U.  */
 
-static bool
+static unsigned HOST_WIDE_INT
 aliasing_component_refs_p (tree ref1,
 			   alias_set_type ref1_alias_set,
 			   alias_set_type base1_alias_set,
@@ -784,7 +798,7 @@ aliasing_component_refs_p (tree ref1,
 			   alias_set_type ref2_alias_set,
 			   alias_set_type base2_alias_set,
 			   HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
-			   bool ref2_is_decl)
+			   bool ref2_is_decl, HOST_WIDE_INT *aloff)
 {
   /* If one reference is a component references through pointers try to find a
      common base and apply offset based disambiguation.  This handles
@@ -816,7 +830,7 @@ aliasing_component_refs_p (tree ref1,
   same_p = same_type_for_tbaa (TREE_TYPE (*refp), type1);
   /* If we couldn't compare types we have to bail out.  */
   if (same_p == -1)
-    return true;
+    return HOST_WIDE_INT_M1U;
   else if (same_p == 1)
     {
       HOST_WIDE_INT offadj, sztmp, msztmp;
@@ -825,7 +839,7 @@ aliasing_component_refs_p (tree ref1,
       offset2 -= offadj;
       get_ref_base_and_extent (base1, &offadj, &sztmp, &msztmp, &reverse);
       offset1 -= offadj;
-      return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+      return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
     }
   /* If we didn't find a common base, try the other way around.  */
   refp = &ref1;
@@ -844,7 +858,7 @@ aliasing_component_refs_p (tree ref1,
       offset1 -= offadj;
       get_ref_base_and_extent (base2, &offadj, &sztmp, &msztmp, &reverse);
       offset2 -= offadj;
-      return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+      return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
     }
 
   /* If we have two type access paths B1.path1 and B2.path2 they may
@@ -855,12 +869,12 @@ aliasing_component_refs_p (tree ref1,
      tail of path2.  */
   if (base1_alias_set == ref2_alias_set
       || alias_set_subset_of (base1_alias_set, ref2_alias_set))
-    return true;
+    return HOST_WIDE_INT_M1U;
   /* If this is ptr vs. decl then we know there is no ptr ... decl path.  */
   if (!ref2_is_decl)
     return (base2_alias_set == ref1_alias_set
 	    || alias_set_subset_of (base2_alias_set, ref1_alias_set));
-  return false;
+  return 0;
 }
 
 /* Return true if we can determine that component references REF1 and REF2,
@@ -1088,21 +1102,25 @@ nonoverlapping_component_refs_p (const_tree x, const_tree y)
    [OFFSET2, OFFSET2 + MAX_SIZE2) may alias.  REF1 and REF2
    if non-NULL are the complete memory reference trees.  */
 
-static bool
+static unsigned HOST_WIDE_INT
 decl_refs_may_alias_p (tree ref1, tree base1,
 		       HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1,
 		       tree ref2, tree base2,
-		       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2)
+		       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
+		       HOST_WIDE_INT *aloff)
 {
   gcc_checking_assert (DECL_P (base1) && DECL_P (base2));
 
   /* If both references are based on different variables, they cannot alias.  */
   if (compare_base_decls (base1, base2) == 0)
-    return false;
+    return 0;
 
   /* If both references are based on the same variable, they cannot alias if
      the accesses do not overlap.  */
-  if (!ranges_overlap_p (offset1, max_size1, offset2, max_size2))
+  HOST_WIDE_INT overlap
+    = range_overlap (offset1, max_size1, offset2, max_size2, aloff);
+
+  if (!overlap)
     return false;
 
   /* For components with variable position, the above test isn't sufficient,
@@ -1110,9 +1128,9 @@ decl_refs_may_alias_p (tree ref1, tree base1,
   if (ref1 && ref2
       && handled_component_p (ref1) && handled_component_p (ref2)
       && nonoverlapping_component_refs_of_decl_p (ref1, ref2))
-    return false;
+    return 0;
 
-  return true;     
+  return overlap;
 }
 
 /* Return true if an indirect reference based on *PTR1 constrained
@@ -1122,16 +1140,17 @@ decl_refs_may_alias_p (tree ref1, tree base1,
    in which case they are computed on-demand.  REF1 and REF2
    if non-NULL are the complete memory reference trees.  */
 
-static bool
-indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
-			       HOST_WIDE_INT offset1,
-			       HOST_WIDE_INT max_size1 ATTRIBUTE_UNUSED,
-			       alias_set_type ref1_alias_set,
-			       alias_set_type base1_alias_set,
-			       tree ref2 ATTRIBUTE_UNUSED, tree base2,
-			       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
-			       alias_set_type ref2_alias_set,
-			       alias_set_type base2_alias_set, bool tbaa_p)
+static unsigned HOST_WIDE_INT
+indirect_ref_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
+			   HOST_WIDE_INT offset1,
+			   HOST_WIDE_INT max_size1 ATTRIBUTE_UNUSED,
+			   alias_set_type ref1_alias_set,
+			   alias_set_type base1_alias_set,
+			   tree ref2 ATTRIBUTE_UNUSED, tree base2,
+			   HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
+			   alias_set_type ref2_alias_set,
+			   alias_set_type base2_alias_set, bool tbaa_p,
+			   HOST_WIDE_INT *aloff)
 {
   tree ptr1;
   tree ptrtype1, dbase2;
@@ -1161,20 +1180,20 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
      so do not apply this optimization for TARGET_MEM_REFs.  */
   if (TREE_CODE (base1) != TARGET_MEM_REF
       && !ranges_overlap_p (MAX (0, offset1p), -1, offset2p, max_size2))
-    return false;
+    return 0;
   /* They also cannot alias if the pointer may not point to the decl.  */
-  if (!ptr_deref_may_alias_decl_p (ptr1, base2))
-    return false;
+  if (!ptr_deref_alias_decl_p (ptr1, base2, aloff != 0))
+    return 0;
 
   /* Disambiguations that rely on strict aliasing rules follow.  */
   if (!flag_strict_aliasing || !tbaa_p)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   ptrtype1 = TREE_TYPE (TREE_OPERAND (base1, 1));
 
   /* If the alias set for a pointer access is zero all bets are off.  */
   if (base1_alias_set == 0)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* When we are trying to disambiguate an access with a pointer dereference
      as base versus one with a decl as base we can use both the size
@@ -1190,7 +1209,7 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
      !alias_set_subset_of (base1_alias_set, base2_alias_set) instead.  */
   if (base1_alias_set != base2_alias_set
       && !alias_sets_conflict_p (base1_alias_set, base2_alias_set))
-    return false;
+    return 0;
   /* If the size of the access relevant for TBAA through the pointer
      is bigger than the size of the decl we can't possibly access the
      decl via that pointer.  */
@@ -1203,10 +1222,10 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
       && TREE_CODE (TREE_TYPE (ptrtype1)) != UNION_TYPE
       && TREE_CODE (TREE_TYPE (ptrtype1)) != QUAL_UNION_TYPE
       && tree_int_cst_lt (DECL_SIZE (base2), TYPE_SIZE (TREE_TYPE (ptrtype1))))
-    return false;
+    return 0;
 
   if (!ref2)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* If the decl is accessed via a MEM_REF, reconstruct the base
      we can use for TBAA and an appropriately adjusted offset.  */
@@ -1229,7 +1248,7 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
   /* If either reference is view-converted, give up now.  */
   if (same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (ptrtype1)) != 1
       || same_type_for_tbaa (TREE_TYPE (dbase2), TREE_TYPE (base2)) != 1)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* If both references are through the same type, they do not alias
      if the accesses do not overlap.  This does extra disambiguation
@@ -1241,11 +1260,11 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
   if ((TREE_CODE (base1) != TARGET_MEM_REF
        || (!TMR_INDEX (base1) && !TMR_INDEX2 (base1)))
       && same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (dbase2)) == 1)
-    return ranges_overlap_p (doffset1, max_size1, doffset2, max_size2);
+    return range_overlap (doffset1, max_size1, doffset2, max_size2, aloff);
 
   if (ref1 && ref2
       && nonoverlapping_component_refs_p (ref1, ref2))
-    return false;
+    return 0;
 
   /* Do access-path based disambiguation.  */
   if (ref1 && ref2
@@ -1255,27 +1274,32 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
 				      offset1, max_size1,
 				      ref2,
 				      ref2_alias_set, base2_alias_set,
-				      offset2, max_size2, true);
+				      offset2, max_size2, true, aloff);
 
-  return true;
+  return aloff ? 0 : HOST_WIDE_INT_M1U;
 }
 
-/* Return true if two indirect references based on *PTR1
-   and *PTR2 constrained to [OFFSET1, OFFSET1 + MAX_SIZE1) and
-   [OFFSET2, OFFSET2 + MAX_SIZE2) may alias.  *PTR1 and *PTR2 have
-   the alias sets BASE1_ALIAS_SET and BASE2_ALIAS_SET which can be -1
-   in which case they are computed on-demand.  REF1 and REF2
-   if non-NULL are the complete memory reference trees. */
-
-static bool
-indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
-			   HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1,
-			   alias_set_type ref1_alias_set,
-			   alias_set_type base1_alias_set,
-			   tree ref2 ATTRIBUTE_UNUSED, tree base2,
-			   HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
-			   alias_set_type ref2_alias_set,
-			   alias_set_type base2_alias_set, bool tbaa_p)
+/* Return non-zero if two indirect references based on *PTR1 and *PTR2
+   constrained to [OFFSET1, OFFSET1 + MAX_SIZE1) and  [OFFSET2, OFFSET2
+   + MAX_SIZE2) may alias.  *PTR1 and *PTR2 have the alias sets
+   BASE1_ALIAS_SET and BASE2_ALIAS_SET which can be -1 in which case
+   they are computed on-demand.  If non-NULL, REF1 and REF2 are the
+   complete memory reference trees.
+   The returned value is the size of the overlap if it can be determined,
+   otherwise it's HOST_WIDE_INT_M1U.  When ALOFF is non-null, the returned
+   value is non-zero if and only if REF1 and REF2 definitely overlap, and
+   *ALOFF is set to the offset of the overlap in REF1.  */
+
+static unsigned HOST_WIDE_INT
+indirect_refs_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
+		       HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1,
+		       alias_set_type ref1_alias_set,
+		       alias_set_type base1_alias_set,
+		       tree ref2 ATTRIBUTE_UNUSED, tree base2,
+		       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
+		       alias_set_type ref2_alias_set,
+		       alias_set_type base2_alias_set, bool tbaa_p,
+		       HOST_WIDE_INT *aloff)
 {
   tree ptr1;
   tree ptr2;
@@ -1328,14 +1352,14 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
 	offset1 += (-moff).to_short_addr ();
       else
 	offset2 += moff.to_short_addr ();
-      return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+      return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
     }
-  if (!ptr_derefs_may_alias_p (ptr1, ptr2))
-    return false;
+  if (!ptr_derefs_alias_p (ptr1, ptr2, aloff != NULL))
+    return 0;
 
   /* Disambiguations that rely on strict aliasing rules follow.  */
   if (!flag_strict_aliasing || !tbaa_p)
-    return true;
+    return HOST_WIDE_INT_M1U;
 
   ptrtype1 = TREE_TYPE (TREE_OPERAND (base1, 1));
   ptrtype2 = TREE_TYPE (TREE_OPERAND (base2, 1));
@@ -1343,7 +1367,7 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
   /* If the alias set for a pointer access is zero all bets are off.  */
   if (base1_alias_set == 0
       || base2_alias_set == 0)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* If both references are through the same type, they do not alias
      if the accesses do not overlap.  This does extra disambiguation
@@ -1359,21 +1383,21 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
       /* But avoid treating arrays as "objects", instead assume they
          can overlap by an exact multiple of their element size.  */
       && TREE_CODE (TREE_TYPE (ptrtype1)) != ARRAY_TYPE)
-    return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+    return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
 
   /* Do type-based disambiguation.  */
   if (base1_alias_set != base2_alias_set
       && !alias_sets_conflict_p (base1_alias_set, base2_alias_set))
-    return false;
+    return 0;
 
   /* If either reference is view-converted, give up now.  */
   if (same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (ptrtype1)) != 1
       || same_type_for_tbaa (TREE_TYPE (base2), TREE_TYPE (ptrtype2)) != 1)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   if (ref1 && ref2
       && nonoverlapping_component_refs_p (ref1, ref2))
-    return false;
+    return 0;
 
   /* Do access-path based disambiguation.  */
   if (ref1 && ref2
@@ -1383,21 +1407,20 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
 				      offset1, max_size1,
 				      ref2,
 				      ref2_alias_set, base2_alias_set,
-				      offset2, max_size2, false);
+				      offset2, max_size2, false, aloff);
 
-  return true;
+  return HOST_WIDE_INT_M1U;
 }
 
-/* Return true, if the two memory references REF1 and REF2 may alias.  */
+/* Return non-zero if the two memory references REF1 and REF2 may alias.
+   The returned value is the size of the overlap if it can be determined,
+   otherwise it's HOST_WIDE_INT_M1U.  When ALOFF is non-null, the returned
+   value is non-zero if and only if REF1 and REF2 definitely overlap, and
+   *ALOFF is set to the offset of the overlap in REF1.  */
 
-bool
-refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
+static unsigned HOST_WIDE_INT
+refs_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p, HOST_WIDE_INT *aloff)
 {
-  tree base1, base2;
-  HOST_WIDE_INT offset1 = 0, offset2 = 0;
-  HOST_WIDE_INT max_size1 = -1, max_size2 = -1;
-  bool var1_p, var2_p, ind1_p, ind2_p;
-
   gcc_checking_assert ((!ref1->ref
 			|| TREE_CODE (ref1->ref) == SSA_NAME
 			|| DECL_P (ref1->ref)
@@ -1414,12 +1437,30 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
 			   || TREE_CODE (ref2->ref) == TARGET_MEM_REF));
 
   /* Decompose the references into their base objects and the access.  */
-  base1 = ao_ref_base (ref1);
-  offset1 = ref1->offset;
-  max_size1 = ref1->max_size;
-  base2 = ao_ref_base (ref2);
-  offset2 = ref2->offset;
-  max_size2 = ref2->max_size;
+  tree base1 = ao_ref_base (ref1);
+  tree base2 = ao_ref_base (ref2);
+
+  HOST_WIDE_INT offset1 = ref1->offset;
+  HOST_WIDE_INT offset2 = ref2->offset;
+
+  HOST_WIDE_INT max_size1 = ref1->max_size;
+  HOST_WIDE_INT max_size2 = ref2->max_size;
+
+  bool must_alias = aloff != NULL;
+  if (must_alias)
+    {
+      /* ALOFF being non-null implies a request for a must-alias kind of
+	 analysis.  Consider the minimum size when ALOFF is non-zero.  */
+      max_size1 = ref1->size;
+      max_size2 = ref2->size;
+    }
+  else
+    {
+      /* ALOFF being null implies a request for a may-alias kind of
+	 analysis.  Consider the maximum size.  */
+      max_size1 = ref1->max_size;
+      max_size2 = ref2->max_size;
+    }
 
   /* We can end up with registers or constants as bases for example from
      *D.1663_44 = VIEW_CONVERT_EXPR<struct DB_LSN>(__tmp$B0F64_59);
@@ -1434,7 +1475,7 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       || TREE_CODE (base2) == CONSTRUCTOR
       || TREE_CODE (base2) == ADDR_EXPR
       || CONSTANT_CLASS_P (base2))
-    return false;
+    return 0;
 
   /* We can end up referring to code via function and label decls.
      As we likely do not properly track code aliases conservatively
@@ -1443,22 +1484,23 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       || TREE_CODE (base1) == LABEL_DECL
       || TREE_CODE (base2) == FUNCTION_DECL
       || TREE_CODE (base2) == LABEL_DECL)
-    return true;
+    return must_alias ? 0 : HOST_WIDE_INT_M1U;
 
-  /* Two volatile accesses always conflict.  */
+  /* Two volatile accesses may always conflict.  */
   if (ref1->volatile_p
       && ref2->volatile_p)
-    return true;
+    return must_alias ? 0 : HOST_WIDE_INT_M1U;
 
   /* Defer to simple offset based disambiguation if we have
      references based on two decls.  Do this before defering to
      TBAA to handle must-alias cases in conformance with the
      GCC extension of allowing type-punning through unions.  */
-  var1_p = DECL_P (base1);
-  var2_p = DECL_P (base2);
+  bool var1_p = DECL_P (base1);
+  bool var2_p = DECL_P (base2);
   if (var1_p && var2_p)
     return decl_refs_may_alias_p (ref1->ref, base1, offset1, max_size1,
-				  ref2->ref, base2, offset2, max_size2);
+				  ref2->ref, base2, offset2, max_size2,
+				  aloff);
 
   /* Handle restrict based accesses.
      ???  ao_ref_base strips inner MEM_REF [&decl], recover from that
@@ -1486,12 +1528,12 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       && MR_DEPENDENCE_CLIQUE (base1) == MR_DEPENDENCE_CLIQUE (base2)
       /* But based on different pointers they do not alias.  */
       && MR_DEPENDENCE_BASE (base1) != MR_DEPENDENCE_BASE (base2))
-    return false;
+    return 0;
 
-  ind1_p = (TREE_CODE (base1) == MEM_REF
-	    || TREE_CODE (base1) == TARGET_MEM_REF);
-  ind2_p = (TREE_CODE (base2) == MEM_REF
-	    || TREE_CODE (base2) == TARGET_MEM_REF);
+  bool ind1_p = (TREE_CODE (base1) == MEM_REF
+		 || TREE_CODE (base1) == TARGET_MEM_REF);
+  bool ind2_p = (TREE_CODE (base2) == MEM_REF
+		 || TREE_CODE (base2) == TARGET_MEM_REF);
 
   /* Canonicalize the pointer-vs-decl case.  */
   if (ind1_p && var2_p)
@@ -1511,33 +1553,63 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       && flag_strict_aliasing
       && !alias_sets_conflict_p (ao_ref_alias_set (ref1),
 				 ao_ref_alias_set (ref2)))
-    return false;
+    return 0;
 
   /* Dispatch to the pointer-vs-decl or pointer-vs-pointer disambiguators.  */
   if (var1_p && ind2_p)
-    return indirect_ref_may_alias_decl_p (ref2->ref, base2,
-					  offset2, max_size2,
-					  ao_ref_alias_set (ref2),
-					  ao_ref_base_alias_set (ref2),
-					  ref1->ref, base1,
-					  offset1, max_size1,
-					  ao_ref_alias_set (ref1),
-					  ao_ref_base_alias_set (ref1),
-					  tbaa_p);
-  else if (ind1_p && ind2_p)
-    return indirect_refs_may_alias_p (ref1->ref, base1,
-				      offset1, max_size1,
-				      ao_ref_alias_set (ref1),
-				      ao_ref_base_alias_set (ref1),
-				      ref2->ref, base2,
+    return indirect_ref_alias_decl_p (ref2->ref, base2,
 				      offset2, max_size2,
 				      ao_ref_alias_set (ref2),
 				      ao_ref_base_alias_set (ref2),
-				      tbaa_p);
+				      ref1->ref, base1,
+				      offset1, max_size1,
+				      ao_ref_alias_set (ref1),
+				      ao_ref_base_alias_set (ref1),
+				      tbaa_p, aloff);
+  else if (ind1_p && ind2_p)
+    return indirect_refs_alias_p (ref1->ref, base1,
+				  offset1, max_size1,
+				  ao_ref_alias_set (ref1),
+				  ao_ref_base_alias_set (ref1),
+				  ref2->ref, base2,
+				  offset2, max_size2,
+				  ao_ref_alias_set (ref2),
+				  ao_ref_base_alias_set (ref2),
+				  tbaa_p, aloff);
 
   gcc_unreachable ();
 }
 
+bool
+refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
+{
+  return refs_alias_p_1 (ref1, ref2, tbaa_p, NULL);
+}
+
+/* Return the size of the overlap in bytes if REF1 and REF2 alias one
+   another, and set *ALOFF (alias offset) to the offset of the overlap
+   in REF1.  Return HOST_WIDE_INT_M1U if the size of the overlap cannot
+   be determined.  Otherwise, if REF1 and REF2 do not definitely overlap,
+   return zero.  */
+
+unsigned HOST_WIDE_INT
+refs_must_alias_p_1 (ao_ref *ref1, ao_ref *ref2, HOST_WIDE_INT *aloff)
+{
+  /* ALOFF being non-zero is what distinguishes a must kind of analysis
+     from a may kind.  Set it to &DUMMY when the caller doesn't care
+     about the offset.  */
+  HOST_WIDE_INT dummy;
+  if (!aloff)
+    aloff = &dummy;
+
+  unsigned HOST_WIDE_INT alsz = refs_alias_p_1 (ref1, ref2, true, aloff);
+
+  /* Convert the size and offset of the overlap from bits to bytes.  */
+  if (alsz)
+    *aloff /= BITS_PER_UNIT;
+  return alsz / BITS_PER_UNIT;
+}
+
 static bool
 refs_may_alias_p (tree ref1, ao_ref *ref2)
 {
diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
index 8c89c69..ea4671b 100644
--- a/gcc/tree-ssa-alias.h
+++ b/gcc/tree-ssa-alias.h
@@ -105,11 +105,14 @@ extern alias_set_type ao_ref_alias_set (ao_ref *);
 extern alias_set_type ao_ref_base_alias_set (ao_ref *);
 extern bool ptr_deref_may_alias_global_p (tree);
 extern bool ptr_derefs_may_alias_p (tree, tree);
+extern bool ptr_derefs_must_alias_p (tree, tree);
 extern bool ptrs_compare_unequal (tree, tree);
 extern bool ref_may_alias_global_p (tree);
 extern bool ref_may_alias_global_p (ao_ref *);
 extern bool refs_may_alias_p (tree, tree);
 extern bool refs_may_alias_p_1 (ao_ref *, ao_ref *, bool);
+extern unsigned HOST_WIDE_INT refs_must_alias_p_1 (ao_ref *, ao_ref *,
+						   HOST_WIDE_INT *);
 extern bool refs_anti_dependent_p (tree, tree);
 extern bool refs_output_dependent_p (tree, tree);
 extern bool ref_maybe_used_by_stmt_p (gimple *, tree);
@@ -152,7 +155,8 @@ extern bool pt_solution_empty_p (struct pt_solution *);
 extern bool pt_solution_singleton_or_null_p (struct pt_solution *, unsigned *);
 extern bool pt_solution_includes_global (struct pt_solution *);
 extern bool pt_solution_includes (struct pt_solution *, const_tree);
-extern bool pt_solutions_intersect (struct pt_solution *, struct pt_solution *);
+extern bool pt_solutions_intersect (struct pt_solution *, struct pt_solution *,
+				    bool = false);
 extern void pt_solution_reset (struct pt_solution *);
 extern void pt_solution_set (struct pt_solution *, bitmap, bool);
 extern void pt_solution_set_var (struct pt_solution *, tree);
@@ -161,6 +165,47 @@ extern void dump_pta_stats (FILE *);
 
 extern GTY(()) struct pt_solution ipa_escaped_pt;
 
+/* Return the size of the overlap if the two ranges [POS1, POS1 + SIZE1]
+   and [POS2, POS2 + SIZE2] overlap.  Set *OFFSET to the offset of the
+   overlap in the first range.  When SIZE1 or SIZE2 is -1 the range is
+   half-open.  Otherwise return zero.  */
+
+static inline unsigned HOST_WIDE_INT
+range_overlap (HOST_WIDE_INT pos1,
+	       unsigned HOST_WIDE_INT size1,
+	       HOST_WIDE_INT pos2,
+	       unsigned HOST_WIDE_INT size2,
+	       HOST_WIDE_INT *offset)
+{
+  if (offset)
+    *offset = pos2 - pos1;
+
+  if (pos1 >= pos2)
+    {
+      if (size2 == HOST_WIDE_INT_M1U)
+	/* Overlap is certain but its size is unknown.  */
+	return HOST_WIDE_INT_M1U;
+
+      unsigned HOST_WIDE_INT end2 = pos2 + (HOST_WIDE_INT) size2;
+      if ((unsigned HOST_WIDE_INT) pos1 < end2)
+	return end2 - pos1 + 1;
+    }
+
+  if (pos2 >= pos1)
+    {
+      if (size1 == HOST_WIDE_INT_M1U)
+	/* Overlap is certain but its size is unknown.  */
+	return HOST_WIDE_INT_M1U;
+
+      unsigned HOST_WIDE_INT end1 = pos1 + (HOST_WIDE_INT) size1;
+      if ((unsigned HOST_WIDE_INT) pos2 < end1)
+	return end1 - pos2 + 1;
+    }
+
+  /* No overlap.  */
+  return 0;
+}
+
 /* Return true, if the two ranges [POS1, SIZE1] and [POS2, SIZE2]
    overlap.  SIZE1 and/or SIZE2 can be (unsigned)-1 in which case the
    range is open-ended.  Otherwise return false.  */
@@ -171,18 +216,7 @@ ranges_overlap_p (HOST_WIDE_INT pos1,
 		  HOST_WIDE_INT pos2,
 		  unsigned HOST_WIDE_INT size2)
 {
-  if (pos1 >= pos2
-      && (size2 == (unsigned HOST_WIDE_INT)-1
-	  || pos1 < (pos2 + (HOST_WIDE_INT) size2)))
-    return true;
-  if (pos2 >= pos1
-      && (size1 == (unsigned HOST_WIDE_INT)-1
-	  || pos2 < (pos1 + (HOST_WIDE_INT) size1)))
-    return true;
-
-  return false;
+  return range_overlap (pos1, size1, pos2, size2, NULL) != 0;
 }
 
-
-
 #endif /* TREE_SSA_ALIAS_H  */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index b0563fe..e6b82d2 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -40,11 +40,15 @@ along with GCC; see the file COPYING3.  If not see
 #include "expr.h"
 #include "tree-dfa.h"
 #include "domwalk.h"
+#include "tree-ssa-alias.h"
 #include "tree-ssa-propagate.h"
 #include "params.h"
 #include "ipa-chkp.h"
 #include "tree-hash-traits.h"
 #include "builtins.h"
+#include "intl.h"
+#include "diagnostic.h"
+#include "diagnostic-core.h"
 
 /* A vector indexed by SSA_NAME_VERSION.  0 means unknown, positive value
    is an index into strinfo vector, negative value stands for
@@ -1559,6 +1563,33 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
   len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
 				  GSI_SAME_STMT);
+
+  if (olddsi && si)
+    {
+      ao_ref dstref, srcref;
+      ao_ref_init_from_ptr_and_size (&dstref, olddsi->ptr, len);
+      ao_ref_init_from_ptr_and_size (&srcref, si->ptr, len);
+
+      HOST_WIDE_INT offset;
+      if (unsigned HOST_WIDE_INT overlap
+	  = refs_must_alias_p_1 (&dstref, &srcref, &offset))
+	{
+	  tree func = gimple_call_fndecl (stmt);
+
+	  if (warning_at (loc, OPT_Wrestrict,
+			  overlap > 1
+			  ? G_("%qD source sequence overlaps %wu bytes of "
+			       "destination at offset %wi")
+			  : G_("%qD source sequence overlaps %wu byte of "
+			       "destination at offset %wi"),
+			  func, overlap, offset))
+	    gimple_set_no_warning (stmt, true);
+
+	  /* Avoid transforming overlapping strcpy to memcpy.  */
+	  return;
+	}
+    }
+
   if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     {
       fprintf (dump_file, "Optimizing: ");
@@ -1786,7 +1817,7 @@ static void
 handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr;
+  tree src, dst, srclen, dstlen, lhs, args, type, fn, objsz, endptr;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi;
@@ -1804,10 +1835,65 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   dsi = NULL;
   if (didx > 0)
     dsi = get_strinfo (didx);
+
+  srclen = NULL_TREE;
+  si = NULL;
+  idx = get_stridx (src);
+  if (idx < 0)
+    srclen = build_int_cst (size_type_node, ~idx);
+  else if (idx > 0)
+    {
+      si = get_strinfo (idx);
+      if (si != NULL)
+	srclen = get_string_length (si);
+    }
+
+  loc = gimple_location (stmt);
+
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
+      if (warn_restrict)
+	{
+	  /* The concatenation always involves copying at least one byte
+	     (the terminating nul), even if the source string is empty.
+	     If the source is unknown assume it's one character long and
+	     compute the size of the result.  */
+	  tree ressize = srclen ? srclen : size_one_node;
+	  tree type = TREE_TYPE (ressize);
+	  ressize = fold_build2 (PLUS_EXPR, type, ressize, ressize);
+	  ressize = fold_build2 (PLUS_EXPR, type, ressize,
+				 build_int_cst (type, 1));
+
+	  tree sptr = si && si->ptr ? si->ptr : src;
+
+	  /* Check for overlap between the concatentated string and
+	     the source string.  */
+	  ao_ref dstref, srcref;
+	  ao_ref_init_from_ptr_and_size (&dstref, dst, ressize);
+	  ao_ref_init_from_ptr_and_size (&srcref, sptr, ressize);
+
+	  HOST_WIDE_INT offset;
+	  if (unsigned HOST_WIDE_INT size
+	      = refs_must_alias_p_1 (&dstref, &srcref, &offset))
+	    {
+	      tree func = gimple_call_fndecl (stmt);
+
+	      if (warning_at (loc, OPT_Wrestrict,
+			      size > 1
+			      ? G_("%qD source sequence overlaps %wu bytes of "
+				   "destination at offset %wi")
+			      : G_("%qD source sequence overlaps %wu byte of "
+				   "destination at offset %wi"),
+			      func, size, offset))
+		gimple_set_no_warning (stmt, true);
+
+	      /* Avoid transforming overlapping strcat.  */
+	      return;
+	    }
+	}
+
       /* strcat (p, q) can be transformed into
-	 tmp = p + strlen (p); endptr = strpcpy (tmp, q);
+	 tmp = p + strlen (p); endptr = stpcpy (tmp, q);
 	 with length endptr - p if we need to compute the length
 	 later on.  Don't do this transformation if we don't need
 	 it.  */
@@ -1840,19 +1926,6 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       return;
     }
 
-  srclen = NULL_TREE;
-  si = NULL;
-  idx = get_stridx (src);
-  if (idx < 0)
-    srclen = build_int_cst (size_type_node, ~idx);
-  else if (idx > 0)
-    {
-      si = get_strinfo (idx);
-      if (si != NULL)
-	srclen = get_string_length (si);
-    }
-
-  loc = gimple_location (stmt);
   dstlen = dsi->nonzero_chars;
   endptr = dsi->endptr;
 
@@ -1914,7 +1987,44 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (fn == NULL_TREE)
     return;
 
-  len = NULL_TREE;
+  if (warn_restrict && dsi && dstlen)
+    {
+      tree slen = srclen ? srclen : size_zero_node;
+      /* Compute the size of the concatendated string, including
+	 the terminating nul.  */
+      tree type = TREE_TYPE (dstlen);
+      tree ressize = fold_build2 (PLUS_EXPR, type, dstlen, slen);
+      ressize = fold_build2 (PLUS_EXPR, type, ressize, build_int_cst (type, 1));
+
+      tree sptr = si && si->ptr ? si->ptr : src;
+
+      /* Check for overlap between the concatentated string and
+	 the source string.  */
+      ao_ref dstref, srcref;
+      ao_ref_init_from_ptr_and_size (&dstref, dsi->ptr, ressize);
+      ao_ref_init_from_ptr_and_size (&srcref, sptr, slen);
+
+      HOST_WIDE_INT offset;
+      if (unsigned HOST_WIDE_INT overlap
+	  = refs_must_alias_p_1 (&dstref, &srcref, &offset))
+	{
+	  tree func = gimple_call_fndecl (stmt);
+
+	  if (warning_at (loc, OPT_Wrestrict,
+			  overlap > 1
+			  ? G_("%qD source sequence overlaps %wu bytes of "
+			       "destination at offset %wi")
+			  : G_("%qD source sequence overlaps %wu byte of "
+			       "destination at offset %wi"),
+			  func, overlap, offset))
+	    gimple_set_no_warning (stmt, true);
+
+	  /* Avoid transforming overlapping strcat.  */
+	  return;
+	}
+    }
+
+  tree len = NULL_TREE;
   if (srclen != NULL_TREE)
     {
       args = TYPE_ARG_TYPES (TREE_TYPE (fn));
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index e563e9d..1728260 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -6743,31 +6743,33 @@ pt_solution_includes (struct pt_solution *pt, const_tree decl)
 }
 
 /* Return true if both points-to solutions PT1 and PT2 have a non-empty
-   intersection.  */
+   intersection.  When STRICT is true, interpret only definitive results
+   as positive, and possible results as negative.  */
 
 static bool
-pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2)
+pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2,
+			  bool strict = false)
 {
   if (pt1->anything || pt2->anything)
-    return true;
+    return !strict;
 
   /* If either points to unknown global memory and the other points to
-     any global memory they alias.  */
+     any global memory they may (though need not) alias.  */
   if ((pt1->nonlocal
        && (pt2->nonlocal
 	   || pt2->vars_contains_nonlocal))
       || (pt2->nonlocal
 	  && pt1->vars_contains_nonlocal))
-    return true;
+    return !strict;
 
   /* If either points to all escaped memory and the other points to
-     any escaped memory they alias.  */
+     any escaped memory they may (though need not) alias.  */
   if ((pt1->escaped
        && (pt2->escaped
 	   || pt2->vars_contains_escaped))
       || (pt2->escaped
 	  && pt1->vars_contains_escaped))
-    return true;
+    return !strict;
 
   /* Check the escaped solution if required.
      ???  Do we need to check the local against the IPA escaped sets?  */
@@ -6775,17 +6777,17 @@ pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2)
       && !pt_solution_empty_p (&ipa_escaped_pt))
     {
       /* If both point to escaped memory and that solution
-	 is not empty they alias.  */
+	 is not empty they may alias.  */
       if (pt1->ipa_escaped && pt2->ipa_escaped)
-	return true;
+	return !strict;
 
       /* If either points to escaped memory see if the escaped solution
 	 intersects with the other.  */
       if ((pt1->ipa_escaped
-	   && pt_solutions_intersect_1 (&ipa_escaped_pt, pt2))
+	   && pt_solutions_intersect_1 (&ipa_escaped_pt, pt2, strict))
 	  || (pt2->ipa_escaped
-	      && pt_solutions_intersect_1 (&ipa_escaped_pt, pt1)))
-	return true;
+	      && pt_solutions_intersect_1 (&ipa_escaped_pt, pt1, strict)))
+	return !strict;
     }
 
   /* Now both pointers alias if their points-to solution intersects.  */
@@ -6794,10 +6796,17 @@ pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2)
 	  && bitmap_intersect_p (pt1->vars, pt2->vars));
 }
 
+/* Return true if the solutions *PT1 and *PT2 intersect.  When STRICT
+   is true, interpret only definitive results as positive, and possible
+   results as negative.  The former effectively gives an answer to
+   the qestion of whether two pointers must refer to the same array,
+   while the latter answers whether they may refer to one.  */
+
 bool
-pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2)
+pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2,
+			bool strict /* = false */)
 {
-  bool res = pt_solutions_intersect_1 (pt1, pt2);
+  bool res = pt_solutions_intersect_1 (pt1, pt2, strict);
   if (res)
     ++pta_stats.pt_solutions_intersect_may_alias;
   else
@@ -6805,7 +6814,6 @@ pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2)
   return res;
 }
 
-
 /* Dump points-to information to OUTFILE.  */
 
 static void

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

* Re: [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-07-16 23:47 [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918) Martin Sebor
@ 2017-07-20 20:46 ` Martin Sebor
  2017-07-25  3:13   ` [PING] " Martin Sebor
  0 siblings, 1 reply; 39+ messages in thread
From: Martin Sebor @ 2017-07-20 20:46 UTC (permalink / raw)
  To: Gcc Patch List

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

With more testing (building GDB, Glibc, Busybox, and the Linux
kernel) I found a few bugs and weaknesses in the initial patch.
Attached is version 2 that fixes the uncovered problems and makes
further enhancements to handle more cases.

Martin

On 07/16/2017 05:47 PM, Martin Sebor wrote:
> Being implemented in the front end, the -Wrestrict warning
> detects only trivial instances of violations.  The attached
> patch extends the implementation to the middle-end where
> data flow and alias analysis can be combined to detect even
> complex cases of overlap.  This work is independent of but
> follows on the patch below (waiting for review):
>
>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00036.html
>
> The changes rely on extending the tree-ssa-{alias,structalias}
> machinery in a simple way to answer the "must-alias" kind of
> question in addition to the current "may-alias."
>
> The rest of the changes are in gimple-fold.c, tree-ssa-strlen.c,
> and builtins.c.
>
> Even though this change makes -Wrestrict a lot more useful, it's
> not a complete implementation.  Not all built-ins are handled yet
> (e.g., strncat), and support for user-defined functions is still
> subject to the limitations of the front end implementation.  To
> complete the support, handlers for the missing string built-ins
> will need to be added to tree-ssa-strlen.c, and the remaining
> bits should be moved from the front end to somewhere in
> the middle-end (e.g., calls.c).
>
> Martin


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

PR middle-end/78918 - missing -Wrestrict on memcpy copying over self

gcc/ChangeLog:

	PR middle-end/78918
	* builtins.c (warn_for_overlap, maybe_warn_for_overlap): New.
	(check_sizes): Add argument and call maybe_warn_for_overlap.
	Rename function arguments for clarity.
	(check_memop_sizes): Adjust.
	(expand_builtin_memchr): Ditto.
	(expand_builtin_strcat): Ditto.
	(expand_builtin_strcpy): Ditto.
	(expand_builtin_stpcpy): Ditto.
	(expand_builtin_stpncpy): Ditto.
	(expand_builtin_strncpy): Ditto.
	(expand_builtin_memcmp): Ditto.
	(expand_builtin_memory_chk): Ditto.
	(check_strncat_sizes): Ditto.  Rename locals for clarity.
	(expand_builtin_strncat): Ditto.
	(maybe_emit_chk_warning): Ditto.
	(maybe_emit_sprintf_chk_warning): Adjust.
	* cfgexpand.c (expand_call_stmt): Set TREE_NO_WARNING.
	* gimple-fold.c (gimple_fold_builtin_memory_op): Handle -Wrestrict.
	(gimple_fold_builtin_strcpy): Ditto.
	(gimple_fold_builtin_memory_chk): Ditto.
	(gimple_fold_builtin_stxcpy_chk): Ditto.
	* gimple.c (gimple_build_call_from_tree): Set call location.
	* tree-ssa-alias.c (ptr_deref_may_alias_decl_p): Add argument
	and rename...	
	(ptr_deref_alias_decl_p): ...to this.
	(ptr_derefs_may_alias_p): Add argument and rename...
	(ptr_derefs_alias_p): ...to this.
	(ptr_deref_may_alias_ref_p_1): Adjust.
	(ao_ref_init_from_ptr_and_size): Handle new argument.
	(aliasing_component_refs_p): Change return type and add argument.
	(decl_refs_may_alias_p): Ditto.
	(indirect_ref_may_alias_decl_p): Ditto.
	(indirect_refs_may_alias_p): Ditto.  Rename to...
	(indirect_refs_alias_p): ...this.
	(refs_alias_p_1): New static function.
	(refs_may_alias_p_1): Call it.
	(refs_must_alias_p_1): New extern function.
	(detect_overlap): Define.
	* tree-ssa-alias.h (ao_ref_init_from_ptr_and_size): Add optional
	argument.
	(detect_overlap): New function.
	(range_overlap): New inline function.
	(ranges_overlap_p): Call it.
	* tree-ssa-strlen.c (handle_builtin_strcpy): Handle -Wrestrict.
	(handle_builtin_strcat): Ditto.
	* tree-ssa-structalias.c (pt_solution_includes): Add argument.
	(pt_solutions_intersect_1): Ditto.
	(pt_solutions_intersect): Adjust.

gcc/c-family/ChangeLog:

	PR middle-end/78918
	* c-common.c (check_function_restrict): Suppress warning for
	built-in functions.
	* c.opt (-Wrestrict): Include in -Wall.

gcc/testsuite/ChangeLog:

	PR middle-end/78918
	* c-c++-common/Wrestrict.c: New test.
	* gcc.dg/Walloca-1.c: Suppress macro expansion tracking.
	* gcc.dg/pr69172.c: Prune -Wrestrict.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 2deef72..b2baa03 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3036,39 +3036,291 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
   return dest_addr;
 }
 
+/* Issue a warning for a restricted copy call expression EXP to a built-in
+   function FUNC, with a destination of size DSTSIZE, size of copy in RANGE,
+   and with OVERLAP bytes at OFFSET.  */
+
+static void
+warn_for_overlap (tree exp, tree func, bool must_overlap, tree dstsize,
+		  const tree range[2], unsigned HOST_WIDE_INT overlap,
+		  const unsigned HOST_WIDE_INT offrange[2])
+{
+  location_t loc = tree_nonartificial_location (exp);
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  /* To avoid combinatorial explosion of diagnostics format the offset
+     or its range as a string and use it in the warning calls below.  */
+  char offstr[64];
+  if (offrange[0] == offrange[1] || offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr, "%llu", (long long) offrange[0]);
+  else
+    sprintf (offstr, "%llu - %llu", (long long) offrange[0],
+	     (long long) offrange[1]);
+
+  /* The text uses the term "writing N bytes" even though most operations
+     involve "copying" and the latter might be clearer.  This first term
+     is used because string functions like strncat and strncpy don't
+     necessarily copy all N bytes; they may copy fewer.  */
+
+  if (tree_to_uhwi (dstsize) >= HOST_WIDE_INT_MAX)
+    {
+      if (must_overlap)
+	{
+	  if (tree_int_cst_equal (range[0], range[1]))
+	    warning_at (loc, OPT_Wrestrict,
+			(integer_onep (range[0])
+			 ? G_("%K%qD writing %E byte overlaps %wu byte "
+			      "at offset %s")
+			 : (overlap == 1
+			    ? G_("%K%qD writing %E bytes overlaps %wu byte "
+				 "at offset %s")
+			    : G_("%K%qD writing %E bytes overlaps %wu bytes "
+				 "at offset %s"))),
+			exp, func, range[0], overlap, offstr);
+	  else if (tree_int_cst_sign_bit (range[1]))
+	    {
+	      /* Avoid printing the upper bound if it's invalid.
+		 The overlap can likewise be "or more" in this case.  */
+	      warning_at (loc, OPT_Wrestrict,
+			  "%K%qD writing %E or more bytes overlaps %wu "
+			  "or more bytes at offset %s",
+			  exp, func, range[0], overlap, offstr);
+	    }
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD writing between %E and %E bytes overlaps "
+			"%wu or more bytes at offset %s",
+			exp, func, range[0], range[1], overlap, offstr);
+	}
+      else
+	{
+	  if (tree_int_cst_equal (range[0], range[1]))
+	    warning_at (loc, OPT_Wrestrict,
+			(integer_onep (range[0])
+			 ? G_("%K%qD writing %E byte may overlap %wu bytes "
+			      "at offset %s")
+			 : (overlap == 1
+			    ? G_("%K%qD writing %E bytes may overlap %wu byte "
+				 "at offset %s")
+			    : G_("%K%qD writing %E bytes may overlap %wu bytes "
+				 "at offset %s"))),
+			exp, func, range[0], overlap, offstr);
+	  else if (tree_int_cst_sign_bit (range[1]))
+	    {
+	      /* Avoid printing the upper bound if it's invalid.
+		 The overlap can likewise be "or more" in this case.  */
+	      warning_at (loc, OPT_Wrestrict,
+			  "%K%qD writing %E or more bytes may overlap %wu "
+			  "or more bytes at offset %s",
+			  exp, func, range[0], overlap, offstr);
+	    }
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD writing between %E and %E bytes may overlap "
+			"%wu or more bytes at offset %s",
+			exp, func, range[0], range[1], overlap,
+			offstr);
+	}
+
+      return;
+    }
+
+  if (must_overlap)
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	warning_at (loc, OPT_Wrestrict,
+		    (integer_onep (range[0])
+		     ? G_("%K%qD writing %E byte into a region "
+			  "of size %E overlaps %wu byte "
+			  "at offset %s")
+		     : (overlap == 1
+			? G_("%K%qD writing %E bytes into a region "
+			     "of size %E overlaps %wu byte "
+			     "at offset %s")
+			: G_("%K%qD writing %E bytes into a region "
+			     "of size %E overlaps %wu bytes "
+			     "at offset %s"))),
+		    exp, func, range[0], dstsize, overlap, offstr);
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.
+	     The overlap can likewise be "or more" in this case.  */
+	  warning_at (loc, OPT_Wrestrict,
+		      "%K%qD writing %E or more bytes into "
+		      "a region of size %E overlaps %wu or more bytes "
+		      "at offset %s",
+		      exp, func, range[0], dstsize, overlap, offstr);
+	}
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD writing between %E and %E bytes into "
+		    "a region of size %E overlaps %wu or more bytes "
+		    "at offset %s",
+		    exp, func, range[0], range[1], dstsize, overlap,
+		    offstr);
+    }
+  else
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	warning_at (loc, OPT_Wrestrict,
+		    (integer_onep (range[0])
+		     ? G_("%K%qD writing %E byte into a region "
+			  "of size %E may overlap %wu bytes "
+			  "at offset %s")
+		     : (overlap == 1
+			? G_("%K%qD writing %E bytes into a region "
+			     "of size %E may overlap %wu byte at "
+			     "offset %s")
+			: G_("%K%qD writing %E bytes into a region "
+			     "of size %E may overlap %wu bytes at "
+			     "offset %s"))),
+		    exp, func, range[0], dstsize, overlap, offstr);
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.
+	     The overlap can likewise be "or more" in this case.  */
+	  warning_at (loc, OPT_Wrestrict,
+		      "%K%qD writing %E or more bytes into "
+		      "a region of size %E may overlap %wu or more "
+		      "bytes at offset %s",
+		      exp, func, range[0], dstsize, overlap, offstr);
+	}
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD writing between %E and %E bytes into "
+		    "a region of size %E may overlap %wu or more bytes "
+		    "at offset %s",
+		    exp, func, range[0], range[1], dstsize, overlap,
+		    offstr);
+    }
+}
+
+/* Determine whather an overlap exists in a restricted copy call
+   expression EXP to a built-in function FUNC, with a destination DST
+   of DSTSIZE in size and source SRC, writing DSTWRITE bytes into DST
+   and reading MAXREAD bytes from SRC of SLEN bytes in length copy in
+   RANGE, and with OVERLAP bytes at OFFSET.  If so, diagnose it and
+   return true.  Otherwise do nothing and return false.  */
+
+static bool
+maybe_warn_for_overlap (tree exp, tree func, tree dst, tree src, tree dstwrite,
+			tree range[2], bool at_least_one, tree maxread,
+			tree slen, tree dstsize, bool relaxed)
+{
+  if (!dstwrite)
+    dstwrite = range[0];
+
+  /* If the range of byte counts is [0, N] and N is non-zero, use
+     1 as the conservative minimum to detect at least the most basic
+     cases of overflow.  */
+  if (dstwrite && TREE_CODE (dstwrite) != INTEGER_CST && range[0])
+    dstwrite = (integer_zerop (range[0]) && !integer_zerop (range[1])
+		? size_one_node : range[0]);
+
+  bool must_overlap = dstwrite && tree_fits_uhwi_p (dstwrite);
+
+  if (maxread)
+    must_overlap &= tree_fits_uhwi_p (maxread);
+  else if (slen)
+    {
+      /* When DSTWRITE is known, set the maximum number of bytes
+	 to access to DSTWRITE either when the length of the source
+	 sequence is unknown, or when DSTWRITE is less than the length.  */
+      maxread = (must_overlap
+		 && (at_least_one || tree_int_cst_lt (dstwrite, slen))
+		 ? dstwrite : slen);
+    }
+  else
+    maxread = dstwrite;
+
+  if (!dstwrite)
+    dstwrite = maxread;
+
+  /* The exact number bytes copied is known only if the sizes
+     haven't adjusted earlier on.  */
+  must_overlap &= !at_least_one || (dstwrite && dstwrite != slen);
+
+  /* Initialize and store the lower bound of a constant offset (in
+     bits), disregarding the offset for the destination.  */
+  ao_ref dstref, srcref;
+  unsigned HOST_WIDE_INT offrange[2];
+
+  ao_ref_init_from_ptr_and_size (&dstref, dst, dstwrite, offrange);
+  ao_ref_init_from_ptr_and_size (&srcref, src, maxread, offrange);
+
+  unsigned HOST_WIDE_INT offset;
+  if (unsigned HOST_WIDE_INT overlap
+      = refs_must_alias_p_1 (&dstref, &srcref, &offset))
+    {
+      /*B Convert the offset range from bits to bytes.  */
+      offrange[0] /= BITS_PER_UNIT;
+      offrange[1] /= BITS_PER_UNIT;
+
+      if (overlap <= offrange[1] - offrange[0])
+	must_overlap = false;
+
+      if (offrange[0] != offrange[1])
+	;
+      else if (offrange[0] != HOST_WIDE_INT_M1U)
+	offrange[0] = offrange[1] = offset;
+
+      if (must_overlap || !relaxed)
+	{
+	  if (!range[0])
+	    range[0] = range[1] = dstwrite;
+
+	  warn_for_overlap (exp, func, must_overlap, dstsize, range, overlap,
+			    offrange);
+	}
+
+      /* Return error when an overlap has been detected.  */
+      return must_overlap;
+    }
+
+  return false;
+}
+
 /* Try to verify that the sizes and lengths of the arguments to a string
    manipulation function given by EXP are within valid bounds and that
-   the operation does not lead to buffer overflow.  Arguments other than
-   EXP may be null.  When non-null, the arguments have the following
-   meaning:
-   SIZE is the user-supplied size argument to the function (such as in
-   memcpy(d, s, SIZE) or strncpy(d, s, SIZE).  It specifies the exact
-   number of bytes to write.
-   MAXLEN is the user-supplied bound on the length of the source sequence
+   the operation does not lead to buffer overflow or read past the end.
+   For copy operations the must not overlap also verify that the source
+   and destination do not overlap one another.
+   Arguments other than EXP may be null.  When non-null, the arguments
+   have the following meaning:
+   DST is the destination of a copy call or null otherwise.
+   SRC is the source of a copy call or null otherwise.
+   DSTWRITE is the number of bytes into the destination obtained from
+   the user-supplied size argument to the function (such as in memcpy(DST,
+   SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+   MAXREAD is the user-supplied bound on the length of the source sequence
    (such as in strncat(d, s, N).  It specifies the upper limit on the number
-   of bytes to write.
-   SRC is the source string (such as in strcpy(d, s)) when the expression
-   EXP is a string function call (as opposed to a memory call like memcpy).
-   As an exception, SRC can also be an integer denoting the precomputed
-   size of the source string or object (for functions like memcpy).
-   OBJSIZE is the size of the destination object specified by the last
+   of bytes to write.  If null it's taken to be the same as DSTWRITE.
+   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+   expression EXP is a string function call (as opposed to a memory call
+   like memcpy).  As an exception, SRCSTR can also be an integer denoting
+   the precomputed size of the source string or object (for functions like
+   memcpy).
+   DSTSIZE is the size of the destination object specified by the last
    argument to the _chk builtins, typically resulting from the expansion
-   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
-   OBJSIZE).
+   of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
+   DSTSIZE).
 
-   When SIZE is null LEN is checked to verify that it doesn't exceed
+   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
    If the call is successfully verified as safe from buffer overflow
-   the function returns true, otherwise false..  */
+   the function returns true, otherwise false.  */
 
 static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_sizes (int opt, tree exp, tree dst, tree src, tree dstwrite,
+	     tree maxread, tree srcstr, tree dstsize)
 {
   /* The size of the largest object is half the address space, or
      SSIZE_MAX.  (This is way too permissive.)  */
   tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
 
+  /* Either the length of the source string for string functions or
+     the size of the source object for raw memory functions.  */
   tree slen = NULL_TREE;
 
   tree range[2] = { NULL_TREE, NULL_TREE };
@@ -3077,28 +3329,28 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
      function like strcpy is not known and the only thing that is
      known is that it must be at least one (for the terminating nul).  */
   bool at_least_one = false;
-  if (src)
+  if (srcstr)
     {
-      /* SRC is normally a pointer to string but as a special case
+      /* SRCSTR is normally a pointer to string but as a special case
 	 it can be an integer denoting the length of a string.  */
-      if (POINTER_TYPE_P (TREE_TYPE (src)))
+      if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
 	{
 	  /* Try to determine the range of lengths the source string
 	     refers to.  If it can be determined and is less than
-	     the upper bound given by MAXLEN add one to it for
+	     the upper bound given by MAXREAD add one to it for
 	     the terminating nul.  Otherwise, set it to one for
-	     the same reason, or to MAXLEN as appropriate.  */
-	  get_range_strlen (src, range);
-	  if (range[0] && (!maxlen || TREE_CODE (maxlen) == INTEGER_CST))
+	     the same reason, or to MAXREAD as appropriate.  */
+	  get_range_strlen (srcstr, range);
+	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
 	    {
-	      if (maxlen && tree_int_cst_le (maxlen, range[0]))
-		range[0] = range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[0]))
+		range[0] = range[1] = maxread;
 	      else
 		range[0] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[0], size_one_node);
 
-	      if (maxlen && tree_int_cst_le (maxlen, range[1]))
-		range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[1]))
+		range[1] = maxread;
 	      else if (!integer_all_onesp (range[1]))
 		range[1] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[1], size_one_node);
@@ -3112,10 +3364,10 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	    }
 	}
       else
-	slen = src;
+	slen = srcstr;
     }
 
-  if (!size && !maxlen)
+  if (!dstwrite && !maxread)
     {
       /* When the only available piece of data is the object size
 	 there is nothing to do.  */
@@ -3123,20 +3375,18 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	return true;
 
       /* Otherwise, when the length of the source sequence is known
-	 (as with with strlen), set SIZE to it.  */
+	 (as with strlen), set DSTWRITE to it.  */
       if (!range[0])
-	size = slen;
+	dstwrite = slen;
     }
 
-  if (!objsize)
-    objsize = maxobjsize;
+  if (!dstsize)
+    dstsize = maxobjsize;
 
-  /* The SIZE is exact if it's non-null, constant, and in range of
-     unsigned HOST_WIDE_INT.  */
-  bool exactsize = size && tree_fits_uhwi_p (size);
+  if (dstwrite)
+    get_size_range (dstwrite, range);
 
-  if (size)
-    get_size_range (size, range);
+  tree func = get_callee_fndecl (exp);
 
   /* First check the number of bytes to be written against the maximum
      object size.  */
@@ -3149,30 +3399,34 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	warning_at (loc, opt,
 		    "%K%qD specified size %E "
 		    "exceeds maximum object size %E",
-		    exp, get_callee_fndecl (exp), range[0], maxobjsize);
+		    exp, func, range[0], maxobjsize);
 	  else
 	    warning_at (loc, opt,
 			"%K%qD specified size between %E and %E "
 			"exceeds maximum object size %E",
-			exp, get_callee_fndecl (exp),
+			exp, func,
 			range[0], range[1], maxobjsize);
       return false;
     }
 
+  /* The number of bytes to write is "exact" if DSTWRITE is non-null,
+     constant, and in range of unsigned HOST_WIDE_INT.  */
+  bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite);
+
   /* Next check the number of bytes to be written against the destination
      object size.  */
-  if (range[0] || !exactsize || integer_all_onesp (size))
+  if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
     {
       if (range[0]
-	  && ((tree_fits_uhwi_p (objsize)
-	       && tree_int_cst_lt (objsize, range[0]))
-	      || (tree_fits_uhwi_p (size)
-		  && tree_int_cst_lt (size, range[0]))))
+	  && ((tree_fits_uhwi_p (dstsize)
+	       && tree_int_cst_lt (dstsize, range[0]))
+	      || (tree_fits_uhwi_p (dstwrite)
+		  && tree_int_cst_lt (dstwrite, range[0]))))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
 
-	  if (size == slen && at_least_one)
+	  if (dstwrite == slen && at_least_one)
 	    {
 	      /* This is a call to strcpy with a destination of 0 size
 		 and a source of unknown length.  The call will write
@@ -3180,7 +3434,7 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else if (tree_int_cst_equal (range[0], range[1]))
 	    warning_at (loc, opt,
@@ -3189,21 +3443,21 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 			      "of size %E overflows the destination")
 			 : G_("%K%qD writing %E bytes into a region "
 			      "of size %E overflows the destination")),
-			exp, get_callee_fndecl (exp), range[0], objsize);
+			exp, func, range[0], dstsize);
 	  else if (tree_int_cst_sign_bit (range[1]))
 	    {
 	      /* Avoid printing the upper bound if it's invalid.  */
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else
 	    warning_at (loc, opt,
 			"%K%qD writing between %E and %E bytes into "
 			"a region of size %E overflows the destination",
-			exp, get_callee_fndecl (exp), range[0],	range[1],
-			objsize);
+			exp, func, range[0], range[1],
+			dstsize);
 
 	  /* Return error when an overflow has been detected.  */
 	  return false;
@@ -3213,11 +3467,15 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
   /* Check the maximum length of the source sequence against the size
      of the destination object if known, or against the maximum size
      of an object.  */
-  if (maxlen)
+  if (maxread)
     {
-      get_size_range (maxlen, range);
+      get_size_range (maxread, range);
+
+      /* Use the lower end for MAXREAD from now on.  */
+      if (range[0])
+	maxread = range[0];
 
-      if (range[0] && objsize && tree_fits_uhwi_p (objsize))
+      if (range[0] && dstsize && tree_fits_uhwi_p (dstsize))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
@@ -3231,40 +3489,41 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], maxobjsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], range[1], maxobjsize);
 
 	      return false;
 	    }
 
-	  if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
+	  if (dstsize != maxobjsize && tree_int_cst_lt (dstsize, range[0]))
 	    {
 	      if (tree_int_cst_equal (range[0], range[1]))
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], objsize);
+			    exp, func,
+			    range[0], dstsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], range[1], objsize);
+			    exp, func,
+			    range[0], range[1], dstsize);
 	      return false;
 	    }
 	}
     }
 
+  /* Check for reading past the end of SRC.  */
   if (slen
-      && slen == src
-      && size && range[0]
+      && slen == srcstr
+      && dstwrite && range[0]
       && tree_int_cst_lt (slen, range[0]))
     {
       location_t loc = tree_nonartificial_location (exp);
@@ -3274,23 +3533,36 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		    (tree_int_cst_equal (range[0], integer_one_node)
 		     ? G_("%K%qD reading %E byte from a region of size %E")
 		     : G_("%K%qD reading %E bytes from a region of size %E")),
-		    exp, get_callee_fndecl (exp), range[0], slen);
+		    exp, func, range[0], slen);
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
 	  warning_at (loc, opt,
 		      "%K%qD reading %E or more bytes from a region "
 		      "of size %E",
-		      exp, get_callee_fndecl (exp), range[0], slen);
+		      exp, func, range[0], slen);
 	}
       else
 	warning_at (loc, opt,
 		    "%K%qD reading between %E and %E bytes from a region "
 		    "of size %E",
-		    exp, get_callee_fndecl (exp), range[0], range[1], slen);
+		    exp, func, range[0], range[1], slen);
       return false;
     }
 
+  /* Check for overlapping copies.  Avoid warning if one has already
+     been issued (unlike -Wstringop-overflow, -Wrestrict is checked
+     in many places and TREE_NO_WARNING applies to it but not to
+     the former (there should be a bit for every kind of warning).  */
+  if (src && dst
+      && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+      && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+      && !TREE_NO_WARNING (exp)
+      && maybe_warn_for_overlap (exp, func, dst, src, dstwrite, range,
+				 at_least_one, maxread, slen, dstsize,
+				 srcstr == NULL))
+    return false;
+
   return true;
 }
 
@@ -3330,8 +3602,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size)
   tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
   tree dstsize = compute_objsize (dest, 0);
 
-  return check_sizes (OPT_Wstringop_overflow_, exp,
-		      size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+  return check_sizes (OPT_Wstringop_overflow_, exp, dest, src,
+		      size, /*maxread=*/NULL_TREE, srcsize, dstsize);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -3353,8 +3625,8 @@ expand_builtin_memchr (tree exp, rtx)
     {
       tree size = compute_objsize (arg1, 0);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, len, /*maxlen=*/NULL_TREE,
-		   size, /*objsize=*/NULL_TREE);
+		   exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		   /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
     }
 
   return NULL_RTX;
@@ -3652,7 +3924,8 @@ expand_builtin_strcat (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+	       exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+	       destsize);
 
   return NULL_RTX;
 }
@@ -3671,11 +3944,15 @@ expand_builtin_strcpy (tree exp, rtx target)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
 
-  if (warn_stringop_overflow)
+  if (warn_stringop_overflow || warn_restrict)
     {
-      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+      /* Use Object Size type-1 for -Wrestrict.  */
+      int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 2;
+
+      tree destsize = compute_objsize (dest, ost);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+		   exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		   src, destsize);
     }
 
   return expand_builtin_strcpy_args (dest, src, target);
@@ -3714,7 +3991,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
     {
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+		   exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+		   destsize);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -3799,7 +4077,7 @@ expand_builtin_stpncpy (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+	       exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 
   return NULL_RTX;
 }
@@ -3829,7 +4107,7 @@ check_strncat_sizes (tree exp, tree objsize)
 {
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
 
   /* Try to determine the range of lengths that the source expression
      refers to.  */
@@ -3853,32 +4131,33 @@ check_strncat_sizes (tree exp, tree objsize)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize)
-      && tree_int_cst_equal (objsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return false;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
   /* The number of bytes to write is LEN but check_sizes will also
      check SRCLEN if LEN's value isn't known.  */
   return check_sizes (OPT_Wstringop_overflow_,
-		      exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+		      exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+		      objsize);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -3896,7 +4175,7 @@ expand_builtin_strncat (tree exp, rtx)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
   /* The upper bound on the number of bytes to write.  */
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
   /* The length of the source sequence.  */
   tree slen = c_strlen (src, 1);
 
@@ -3919,32 +4198,31 @@ expand_builtin_strncat (tree exp, rtx)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize)
-      && tree_int_cst_equal (destsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return NULL_RTX;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
-     check SRCLEN if LEN's value isn't known.  */
+  /* The number of bytes to write is SRCLEN.  */
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+	       exp, dest, src, NULL_TREE, maxread, srclen, destsize);
 
   return NULL_RTX;
 }
@@ -3975,7 +4253,7 @@ expand_builtin_strncpy (tree exp, rtx target)
 	  /* The number of bytes to write is LEN but check_sizes will also
 	     check SLEN if LEN's value isn't known.  */
 	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+		       exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 	}
 
       /* We must be passed a constant len and src parameter.  */
@@ -4319,13 +4597,13 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
     {
       tree size = compute_objsize (arg1, 0);
       if (check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE))
+		       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		       /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
 	{
 	  size = compute_objsize (arg2, 0);
 	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE);
+		       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		       /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
 	}
     }
 
@@ -9708,8 +9986,6 @@ static rtx
 expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			   enum built_in_function fcode)
 {
-  tree dest, src, len, size;
-
   if (!validate_arglist (exp,
 			 POINTER_TYPE,
 			 fcode == BUILT_IN_MEMSET_CHK
@@ -9717,13 +9993,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
-  len = CALL_EXPR_ARG (exp, 2);
-  size = CALL_EXPR_ARG (exp, 3);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 3);
 
   bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
-			       exp, len, /*maxlen=*/NULL_TREE,
+			       exp, dest, src, len, /*maxread=*/NULL_TREE,
 			       /*str=*/NULL_TREE, size);
 
   if (!tree_fits_uhwi_p (size))
@@ -9833,7 +10109,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
   /* The maximum length of the source sequence in a bounded operation
      (such as __strncat_chk) or null if the operation isn't bounded
      (such as __strcat_chk).  */
-  tree maxlen = NULL_TREE;
+  tree maxread = NULL_TREE;
 
   switch (fcode)
     {
@@ -9854,27 +10130,27 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
     case BUILT_IN_STRNCAT_CHK:
       catstr = CALL_EXPR_ARG (exp, 0);
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STPNCPY_CHK:
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
-      maxlen = CALL_EXPR_ARG (exp, 1);
+      maxread = CALL_EXPR_ARG (exp, 1);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
     default:
       gcc_unreachable ();
     }
 
-  if (catstr && maxlen)
+  if (catstr && maxread)
     {
       /* Check __strncat_chk.  There is no way to determine the length
 	 of the string to which the source string is being appended so
@@ -9883,8 +10159,11 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       return;
     }
 
-  check_sizes (OPT_Wstringop_overflow_, exp,
-	       /*size=*/NULL_TREE, maxlen, srcstr, objsize);
+  /* The destination argument is the first one for all built-ins above.  */
+  tree dst = CALL_EXPR_ARG (exp, 0);
+
+  check_sizes (OPT_Wstringop_overflow_, exp, dst, srcstr,
+	       /*size=*/NULL_TREE, maxread, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9940,8 +10219,10 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 
   /* Add one for the terminating nul.  */
   len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
+
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
+	       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
+	       /*maxread=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index feb0904..c86a393 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5261,14 +5261,20 @@ check_function_restrict (const_tree fndecl, const_tree fntype,
 			 int nargs, tree *argarray)
 {
   int i;
-  tree parms;
+  tree parms = TYPE_ARG_TYPES (fntype);
 
   if (fndecl
-      && TREE_CODE (fndecl) == FUNCTION_DECL
-      && DECL_ARGUMENTS (fndecl))
-    parms = DECL_ARGUMENTS (fndecl);
-  else
-    parms = TYPE_ARG_TYPES (fntype);
+      && TREE_CODE (fndecl) == FUNCTION_DECL)
+    {
+      /* Skip checking built-ins here.  They are checked in more
+	 detail elsewhere.  */
+      if (DECL_BUILT_IN (fndecl)
+	  && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+	return;
+
+      if (DECL_ARGUMENTS (fndecl))
+	parms = DECL_ARGUMENTS (fndecl);
+    }
 
   for (i = 0; i < nargs; i++)
     TREE_VISITED (argarray[i]) = 0;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index e0ad3ab..01506fd 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1162,7 +1162,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall)
 Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier.
 
 Wrestrict
-C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++)
+C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
 Warn when an argument passed to a restrict-qualified parameter aliases with
 another argument.
 
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 3e1d24d..76b9b15 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2627,6 +2627,9 @@ expand_call_stmt (gcall *stmt)
   if (gimple_call_nothrow_p (stmt))
     TREE_NOTHROW (exp) = 1;
 
+  if (gimple_no_warning_p (stmt))
+    TREE_NO_WARNING (exp) = 1;
+
   CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt);
   CALL_EXPR_MUST_TAIL_CALL (exp) = gimple_call_must_tail_p (stmt);
   CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b9d071b..14205f5 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3852,6 +3852,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
 -Wparentheses  @gol
 -Wpointer-sign  @gol
 -Wreorder   @gol
+-Wrestrict   @gol
 -Wreturn-type  @gol
 -Wsequence-point  @gol
 -Wsign-compare @r{(only in C++)}  @gol
@@ -6535,11 +6536,25 @@ reduce the padding and so make the structure smaller.
 Warn if anything is declared more than once in the same scope, even in
 cases where multiple declaration is valid and changes nothing.
 
-@item -Wrestrict
+@item -Wno-restrict
 @opindex Wrestrict
 @opindex Wno-restrict
-Warn when an argument passed to a restrict-qualified parameter
-aliases with another argument.
+Warn when an object referenced by a @code{restrict}-qualified parameter
+(or, in C++, @code{__restrict}-qualified parameter) is aliased by another
+argument, or when copies between such objects overlap.  For example,
+the call to the @code{strcpy} unction below attempts to truncate the string
+by replacing its initial characters with the last four.  However, because
+he call writes the terminating NUL into @code{a[4]}, the copies overlap and
+the call is diagnosed.
+
+@smallexample
+struct foo
+@{
+  char a[] = "abcd1234";
+  strcpy (a, a + 4);
+@};
+@end smallexample
+The @option{-Wrestrict} is included in @option{-Wall}.
 
 @item -Wnested-externs @r{(C and Objective-C only)}
 @opindex Wnested-externs
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index d94dc9c..eb61383 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -57,6 +57,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "fold-const-call.h"
 #include "asan.h"
+#include "intl.h"
+#include "diagnostic-core.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -644,7 +646,13 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
   tree destvar, srcvar;
   location_t loc = gimple_location (stmt);
 
-  /* If the LEN parameter is zero, return DEST.  */
+  tree func = gimple_call_fndecl (stmt);
+  bool nowarn = gimple_no_warning_p (stmt);
+  bool check_overlap = (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+			&& DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+			&& !nowarn);
+
+    /* If the LEN parameter is zero, return DEST.  */
   if (integer_zerop (len))
     {
       gimple *repl;
@@ -666,6 +674,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
      DEST{,+LEN,+LEN-1}.  */
   if (operand_equal_p (src, dest, 0))
     {
+      if (check_overlap)
+	warning_at (loc, OPT_Wrestrict,
+		    "%qD source argument is the same as destination",
+		    func);
+
       unlink_stmt_vdef (stmt);
       if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
 	release_ssa_name (gimple_vdef (stmt));
@@ -715,6 +728,31 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	  unsigned ilen = tree_to_uhwi (len);
 	  if (pow2p_hwi (ilen))
 	    {
+	      if (check_overlap)
+		{
+		  ao_ref dstref, srcref;
+		  ao_ref_init_from_ptr_and_size (&dstref, dest, len);
+		  ao_ref_init_from_ptr_and_size (&srcref, src, len);
+
+		  if (refs_may_alias_p_1 (&dstref, &srcref, true))
+		    {
+		      /* Defer folding until both arguments are in an SSA
+			 form so that aliasing can be determined with greater
+			 accuracy.  */
+		      if (TREE_CODE (dest) != SSA_NAME
+			  || TREE_CODE (src) != SSA_NAME)
+			return false;
+
+		      /* Check for overlap and diagnose it.  If it's found
+			 avoid folding.  */
+		      if (detect_overlap (loc, stmt, dest, src, len, endp == 2))
+			{
+			  gimple_set_no_warning (stmt, true);
+			  return false;
+			}
+		    }
+		}
+
 	      tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
 	      if (type
 		  && TYPE_MODE (type) != BLKmode
@@ -1028,6 +1066,10 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	    }
 	}
 
+      /* Avoid folding the call if overlap is detected.  */
+      if (check_overlap && detect_overlap (loc, stmt, dest, src, len))
+	return false;
+
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
 	{
@@ -1392,7 +1434,7 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
 	    tree op3 = gimple_assign_rhs3 (def_stmt);
 	    return get_range_strlen (op2, length, visited, type, fuzzy, flexp)
 	      && get_range_strlen (op3, length, visited, type, fuzzy, flexp);
-          }
+	  }
         return false;
 
       case GIMPLE_PHI:
@@ -1484,12 +1526,19 @@ static bool
 gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
 			    tree dest, tree src)
 {
-  location_t loc = gimple_location (gsi_stmt (*gsi));
+  gimple *stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
   tree fn;
 
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -2272,6 +2321,15 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
      (resp. DEST+LEN for __mempcpy_chk).  */
   if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
     {
+      if (fcode != BUILT_IN_MEMMOVE && fcode != BUILT_IN_MEMMOVE_CHK)
+	{
+	  tree func = gimple_call_fndecl (stmt);
+
+	  warning_at (loc, OPT_Wrestrict,
+		      "%qD source argument is the same as destination",
+		      func);
+	}
+
       if (fcode != BUILT_IN_MEMPCPY_CHK)
 	{
 	  replace_call_with_value (gsi, dest);
@@ -2373,6 +2431,12 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 488f8c8..f2dcece 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -359,6 +359,7 @@ gimple_build_call_from_tree (tree t)
     gimple_call_set_arg (call, i, CALL_EXPR_ARG (t, i));
 
   gimple_set_block (call, TREE_BLOCK (t));
+  gimple_set_location (call, EXPR_LOCATION (t));
 
   /* Carry all the CALL_EXPR flags to the new GIMPLE_CALL.  */
   gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t));
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
new file mode 100644
index 0000000..64bc223
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -0,0 +1,589 @@
+/* PR 35503 - Warn about restricted pointers
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+size_t value (void);
+
+size_t range (size_t min, size_t max)
+{
+  size_t val = value ();
+  return val < min || max < val ? min : val;
+}
+
+#define R(min, max) range ((min), (max))
+
+
+void sink (void*, ...);
+
+/* Exercise memcpy with constant or known arguments.  */
+
+void test_memcpy_cst (void)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+    const void *s = 0;				\
+    memcpy ((dst), (src), (n));			\
+    sink (a, (dst), s);				\
+  } while (0)
+
+  T (a, a, 0);
+  T (a, s = a, 1);           /* { dg-warning "\\\[-Wrestrict" } */
+  T (a, a + 1, 1);
+  T (a, a + 1, 2);           /* { dg-warning "\\\[-Wrestrict" } */
+
+  {
+    char a[3][7];
+    sink (a);
+
+    void *d = a[0];
+    const void *s = a[1];
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0];
+    s = a[1];
+    memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&a);
+  }
+
+  {
+    struct {
+      char a[7];
+      char b[7];
+      char c[7];
+    } x;
+    sink (&x);
+
+    void *d = x.a;
+    const void *s = x.b;
+    memcpy (d, s, sizeof x.a);
+    sink (&x);
+
+    d = x.a;
+    s = x.a;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 4;
+    s = x.b;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 6;
+    s = x.b;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b;
+    memcpy (d, s, 1);             /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b + 1;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 1;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 2;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 3;
+    memcpy (d, s, sizeof x.b);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+  }
+
+  {
+#undef T
+#define T(dst, src, n) do {				\
+      char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+      memcpy ((dst), (src), (n));			\
+      sink (a);						\
+    } while (0)
+
+    /* Verify the offset of the overlap is the same regardless of whether
+       the destination is at lower or higher offset than the source.  */
+    T (a, a + 1, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 4 bytes at offset 1" } */
+    T (a, a + 2, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 3 bytes at offset 2" } */
+    T (a, a + 3, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 2 bytes at offset 3" } */
+
+    T (a + 1, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 8 overlaps 4 bytes at offset 1" } */
+    T (a + 2, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 7 overlaps 3 bytes at offset 2" } */
+    T (a + 3, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 6 overlaps 2 bytes at offset 3" } */
+  }
+}
+
+/* Exercise memcpy with destination or source offset or size in
+   a determinate range.  */
+
+void test_memcpy_range (void *d)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+    memcpy ((dst), (src), (n));			\
+    sink (a);					\
+  } while (0)
+
+  int r = R (2, 3);
+  T (a + r, a, 0);
+  T (a + r, a, 1);
+  T (a + r, a, 2);
+  T (a + r, a, 3);                /* { dg-warning "writing 3 bytes overlaps 1 byte at offset 2" } */
+
+  T (d + r, d, 0);
+  T (d + r, d, 1);
+  T (d + r, d, 2);
+  T (d + r, d, 3);                /* { dg-warning "writing 3 bytes overlaps 1 byte at offset 2" } */
+
+  /* Because the size is constant and a power of 2 the following is
+     folded too early to detect the overlap.  */
+  T (d + r, d, 4);                /* { dg-warning "writing 4 bytes overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+  T (d + r, d, 5);                /* { dg-warning "writing 5 bytes overlaps 3 bytes at offset 2" } */
+
+  T (a, a + 1, R (0, 1));
+  T (a, a + 1, R (0, 2));
+  T (a, a + 1, R (1, 2));
+  T (a, a + 1, R (2, 3));         /* { dg-warning "writing between 2 and 3 bytes into a region of size 9 overlaps 1 or more bytes at offset 1" } */
+  T (a, a + 1, R (2, -2));        /* { dg-warning "writing 2 or more bytes into a region of size 9 overlaps 1 or more bytes at offset 1" } */
+  T (a, a + 2, R (2, 3));
+  T (a, a + 2, R (3, 4));         /* { dg-warning "writing between 3 and 4 bytes into a region of size 9 overlaps 1 or more bytes at offset 2" } */
+  T (a, a + 3, R (3, 4));
+  T (a, a + 3, R (4, 5));         /* { dg-warning "writing between 4 and 5 bytes into a region of size 9 overlaps 1 or more bytes at offset 3" } */
+  T (a, a + 3, R (5, 6));         /* { dg-warning "writing between 5 and 6 bytes into a region of size 9 overlaps 2 or more bytes at offset 3" } */
+
+  T (a + 1, a, R (0, 1));
+  T (a + 1, a, R (0, 2));
+  T (a + 1, a, R (1, 2));
+  T (a + 1, a, R (2, 3));         /* { dg-warning "writing between 2 and 3 bytes into a region of size 8 overlaps 1 or more bytes at offset 1" } */
+  T (a + 1, a, R (2, -2));        /* { dg-warning "writing 2 or more bytes into a region of size 8 overlaps 1 or more bytes at offset 1" } */
+  T (a + 2, a, R (2, 3));
+  T (a + 2, a, R (3, 4));         /* { dg-warning "writing between 3 and 4 bytes into a region of size 7 overlaps 1 or more bytes at offset 2" } */
+  T (a + 3, a, R (3, 4));
+  T (a + 3, a, R (4, 5));         /* { dg-warning "writing between 4 and 5 bytes into a region of size 6 overlaps 1 or more bytes at offset 3" } */
+  T (a + 3, a, R (5, 6));         /* { dg-warning "writing between 5 and 6 bytes into a region of size 6 overlaps 2 or more bytes at offset 3" } */
+
+  /* Verify offset and size both in some range.  The memcpy checking
+     is less strict than that of string functions like strncpy and
+     doesn't trigger unless the overlap is certain.  The following
+     overlaps for (r == 3 && n > 3) but not, for example, for
+     (r == 4 && n == 4), and so it's not diagnosed.  */
+  r = R (3, 5);
+  T (a, a + r, R (4, 6));
+  /* Ditto for objects of unknown sizes.  */
+  T (d, d + r, R (4, 6));
+}
+
+/* Exercise memcpy with destination and source of unknown size.  */
+
+void test_memcpy_var (char *d, const char *s)
+{
+  int n = value ();
+
+  memcpy (d, d, 0);
+  sink (d);
+
+  memcpy (d, d, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  memcpy (d, &d[0], n);           /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  memcpy (&d[0], d,  n);          /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  /* The following overlaps if n is greater than 1.  */
+  s = d + 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  /* The following only overlaps if strlen (s + i) >= i so it's not
+     diagnosed.  */
+  s = d + n;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 2;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 3;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 4;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  s = d + 5;
+  n = 7;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+
+  n = range (0, 1);
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+/* Exercise the absence of warnings with memmove.  */
+
+void test_memmove (void)
+{
+  {
+    char d[7];
+    sink (d);
+
+    const void *s = d;
+    memmove (d, s, 7);
+    sink (d);
+
+    s = d + 1;
+    memmove (d, s, 6);
+    sink (d);
+
+    s = d + 2;
+    memmove (d + 1, s, 5);
+    sink (d);
+  }
+}
+
+/* Exercise strcat with constant or known arguments.  */
+
+void test_strcat_cst (const char *s)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[9] = init;				\
+    strcat ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2);            /* { dg-warning "\\\[-Wrestrict" } */
+  /* The nul copied from a[3] to a[3] overwrites itself so this is
+     diagnosed.  */
+  T ("123", a, a + 3);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 4);
+  T ("123", a, a + 5);
+  T ("123", a, a + 6);
+  T ("123", a, a + 7);
+  T ("123", a, a + 8);
+
+  T ("123", a + 1, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 2, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 3, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 4, a);            /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+  T ("123", a + 5, a);            /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T ("123",      a, "123");
+  T ("123",      a, s);
+  T ("12345678", a, s);
+}
+
+/* Exercise strcat with destination and source of unknown length.  */
+
+void test_strcat_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    strcat ((dst), (src));			\
+    sink ((dst));				\
+  } while (0)
+
+  T (d, d);                       /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 1);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 2);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 3);
+
+  int n = value ();
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T (d, "123");
+  T (d + 1, "1234");
+  T (d + n, "12345");
+  T (d, s);
+  T (d + 1, s);
+  T (d + n, s);
+
+  /* This one isn't so obvious and might be worth diagnosing with "may
+     overlap," even more so than the analogous strcpy case.  */
+  T (d, d + n);
+}
+
+/* Exercise strcpy with constant or known arguments.  */
+
+void test_strcpy_cst (void)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[8] = init;				\
+    strcpy ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2);
+  T ("123", a, a + 3);
+
+  /* The terminating nul written to d[2] overwrites s[0].  */
+  T ("1234", a, a + 2);           /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("12345", a, a + 2);          /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* It's not 100% clear if this should trigger a warning.  The non-const
+     string case doesn't.  */
+  T ("123", a, a + value ());
+
+  /* This happens to be safe in GCC but it's still wrong.  */
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("123", a + 1, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 2, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 3, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 4, a);
+}
+
+/* Exercise strcpy with constant or known arguments offset by a range.
+   The tests verify the use of the lower bound of the range which is
+   more restrictive than using the upper bound for positive values.  */
+
+void test_strcpy_range (void)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[8] = init;				\
+    strcpy ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  /* The range needs to be an int variable and not a size_t here,
+     otherwise the range info in the strlen pass is not good enough
+     to detect these cases.  */
+  int r = R (3, 4);
+  T ("12",  a + r, a);
+  T ("123", a + r, a);            /* { dg-warning "writing 4 bytes overlaps 1 byte at offset 3" } */
+
+  T ("1234", a + r, a);           /* { dg-warning "writing 5 bytes overlaps 2 bytes at offset 3" } */
+
+  T ("12",     a, a + r);
+  T ("123",    a, a + r);
+  T ("1234",   a, a + r);
+  T ("12345",  a, a + r);
+
+  /* The final NUL overlaps the '4' at a[3].  */
+  T ("123456", a, a + r);         /* { dg-warning "writing between 3 and 4 bytes may overlap 1 byte at offset 3" } */
+  T ("1234567", a, a + r);        /* { dg-warning "writing between 4 and 5 bytes overlaps 2 bytes at offset 3" } */
+
+  r = R (2, 5);
+  T ("1234", a + r, a);           /* { dg-warning "writing 5 bytes overlaps 3 bytes at offset 2" } */
+
+
+}
+
+
+/* Exercise strcpy with destination and source of unknown length.  */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    strcpy ((dst), (src));			\
+    sink (dst);					\
+  } while (0)
+
+  T (d, s);
+
+  T (d, &d[0]);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (&d[0], d);                   /* { dg-warning "\\\[-Wrestrict" } */
+
+  s = d;
+  T (d, s);                       /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* The following overlaps if *s is not nul.  It arguably should be
+     diagnosed.  */
+  T (d, d + 1);
+
+  /* The following overlaps if strlen (d) is greater than 1.  Like
+     the above, it possibly should be diagnosed too.  */
+  int r = R (2, 3);
+  T (d, d + r);
+
+  /* The following overlaps only if strlen (s + n) >= n so it's not
+     diagnosed.  */
+  s = d + value ();
+  T (d, s);
+}
+
+/* Exercise strncpy with constant or known arguments.  */
+
+void test_strncpy_cst (void)
+{
+#undef T
+#define T(init, dst, src, size) do {		\
+    char a[7] = init;				\
+    strncpy (dst, src, size);			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a, 0);
+  T ("123", a, a, 1);             /* { dg-warning "\\\[-Wrestrict" } */
+  /* The following overlaps except in the unlikely case that value ()
+     is zero, so it's diagnosed.  */
+  T ("123", a, a, value ());      /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1, 1);
+  T ("123", a, a + 1, 2);         /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1, 3);         /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2, 1);
+  T ("123", a, a + 2, 2);
+  /* The third written byte (nul) overwrites a[2].  */
+  T ("123", a, a + 2, 3);         /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("1234", a, a + 2, 1);
+  T ("1234", a, a + 2, 2);
+  /* The terminating nul written to a[2] overwrites s[0].  */
+  T ("1234", a, a + 2, 3);        /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("12345", a, a + 2, 1);
+  T ("12345", a, a + 2, 2);
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("12345", a, a + 2, 4);       /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+
+/* Exercise strncpy with one or more arguments in a determinate range.  */
+
+void test_strncpy_range (char *d)
+{
+#undef T
+#define T(init, dst, src, size) do {		\
+    char a[9] = init;				\
+    strncpy ((dst), (src), (size));		\
+    sink (a);					\
+  } while (0)
+
+  /* Verify offset and size both in some range.  The strncpy checking
+     is more strict than that of memcpy and triggers even when the
+     overlap is possible but not inevitable.  The following overlaps
+     for (r == 3 && n > 3) but not, for example, for (r == 4 && n == 4),
+     but it's still diagnosed.  */
+  int r = R (3, 5);
+  T ("12345678", a, a + r, R (4, 6));   /* { dg-warning "writing between 4 and 6 bytes into a region of size 9 may overlap 1 or more bytes at offset 3 - 5" } */
+
+  /* Ditto for objects of unknown sizes.  Verify also that since it's
+     unknown the size isn't printed.  */
+  T ("12345678", d, d + r, R (4, 6));  /* { dg-warning "writing between 4 and 6 bytes may overlap 1 or more bytes at offset 3 - 5" } */
+
+  T ("12345678", a, a + r, R (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 9 overlaps 3 or more bytes at offset 3 - 5" } */
+}
+
+
+/* Exercise strncpy with destination and source of unknown length.  */
+
+void test_strncpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src, size) do {			\
+    strncpy (dst, src, size);			\
+    sink ((dst));				\
+  } while (0)
+
+  int n = value ();
+
+  T (d, s, 1);
+  T (d, s, n);
+
+  T (d, d, 1);                    /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d, n);                    /* { dg-warning "\\\[-Wrestrict" } */
+
+  T (d,     d + 1, 1);
+  T (d,     d + 1, 2);            /* { dg-warning "\\\[-Wrestrict" } */
+  T (d + 1, d,     1);
+  T (d + 1, d,     2);            /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+struct MemberArrays
+{
+  char a[8];
+  char b[8];
+  char c[9];
+};
+
+void test_strncpy_strcpy_var (struct MemberArrays *ar, const char *s)
+{
+  /* The following is safe and should not trigger a warning.  */
+  strncpy (ar->b, s, sizeof ar->b - 1);
+  ar->b[sizeof ar->b - 1] = '\0';
+  strcpy (ar->a, ar->b);
+  sink (ar);
+
+  /* The following is not as safe (it might overflow ar->a) but there
+     is no overlap so it also shouldn't trigger -Wrestrict.  */
+  strncpy (ar->c, s, sizeof ar->c - 1);
+  ar->c[sizeof ar->c - 1] = '\0';
+  strcpy (ar->a, ar->c);
+  sink (ar);
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c b/gcc/testsuite/gcc.dg/Walloca-1.c
index ad39373..85e9160 100644
--- a/gcc/testsuite/gcc.dg/Walloca-1.c
+++ b/gcc/testsuite/gcc.dg/Walloca-1.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* { dg-require-effective-target alloca } */
-/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+/* { dg-options "-Walloca-larger-than=2000 -O2 -ftrack-macro-expansion=0" } */
 
 #define alloca __builtin_alloca
 
diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c
index c0e7463..908d5a6 100644
--- a/gcc/testsuite/gcc.dg/pr69172.c
+++ b/gcc/testsuite/gcc.dg/pr69172.c
@@ -1,4 +1,5 @@
-/* PR tree-optimization/69172 */
+/* PR tree-optimization/69172 - ICE in make_ssa_name_fn,
+   at tree-ssanames.c:266 */
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
 
@@ -43,3 +44,7 @@ f6 (int x)
 {
   return __builtin___mempcpy_chk (&a, &a, x, 0);
 }
+
+/* The calls above violate strict aliasing.  Eliminate the -Wrestrict
+   warnings they trigger.
+  { dg-prune-output "\\\[-Wrestrict]" } */
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 74ee2b0..f47f1c1 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -38,6 +38,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-dfa.h"
 #include "ipa-reference.h"
 #include "varasm.h"
+#include "intl.h"
+#include "diagnostic-core.h"
 
 /* Broad overview of how alias analysis on gimple works:
 
@@ -149,12 +151,13 @@ ptr_deref_may_alias_global_p (tree ptr)
   return pt_solution_includes_global (&pi->pt);
 }
 
-/* Return true if dereferencing PTR may alias DECL.
+/* Return true if dereferencing PTR may or alias DECL.  When MUST_ALIAS
+   is true, return true only if PTR definitely aliases DECL.
    The caller is responsible for applying TBAA to see if PTR
    may access DECL at all.  */
 
 static bool
-ptr_deref_may_alias_decl_p (tree ptr, tree decl)
+ptr_deref_alias_decl_p (tree ptr, tree decl, bool must_alias = false)
 {
   struct ptr_info_def *pi;
 
@@ -170,7 +173,7 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
       || (!VAR_P (decl)
 	  && TREE_CODE (decl) != PARM_DECL
 	  && TREE_CODE (decl) != RESULT_DECL))
-    return true;
+    return !must_alias;
 
   /* Disregard pointer offsetting.  */
   if (TREE_CODE (ptr) == POINTER_PLUS_EXPR)
@@ -180,7 +183,7 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
 	  ptr = TREE_OPERAND (ptr, 0);
 	}
       while (TREE_CODE (ptr) == POINTER_PLUS_EXPR);
-      return ptr_deref_may_alias_decl_p (ptr, decl);
+      return ptr_deref_alias_decl_p (ptr, decl, must_alias);
     }
 
   /* ADDR_EXPR pointers either just offset another pointer or directly
@@ -215,12 +218,13 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
   return pt_solution_includes (&pi->pt, decl);
 }
 
-/* Return true if dereferenced PTR1 and PTR2 may alias.
+/* Return true if dereferenced PTR1 and PTR2 may alias.  When MUST_ALIAS
+   is true, return true only if PTR1 and PTR2 definitely alias.
    The caller is responsible for applying TBAA to see if accesses
    through PTR1 and PTR2 may conflict at all.  */
 
-bool
-ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
+static bool
+ptr_derefs_alias_p (tree ptr1, tree ptr2, bool must_alias)
 {
   struct ptr_info_def *pi1, *pi2;
 
@@ -237,7 +241,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
 	  ptr1 = TREE_OPERAND (ptr1, 0);
 	}
       while (TREE_CODE (ptr1) == POINTER_PLUS_EXPR);
-      return ptr_derefs_may_alias_p (ptr1, ptr2);
+      return ptr_derefs_alias_p (ptr1, ptr2, must_alias);
     }
   if (TREE_CODE (ptr2) == POINTER_PLUS_EXPR)
     {
@@ -246,7 +250,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
 	  ptr2 = TREE_OPERAND (ptr2, 0);
 	}
       while (TREE_CODE (ptr2) == POINTER_PLUS_EXPR);
-      return ptr_derefs_may_alias_p (ptr1, ptr2);
+      return ptr_derefs_alias_p (ptr1, ptr2, must_alias);
     }
 
   /* ADDR_EXPR pointers either just offset another pointer or directly
@@ -257,12 +261,12 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
       if (base
 	  && (TREE_CODE (base) == MEM_REF
 	      || TREE_CODE (base) == TARGET_MEM_REF))
-	return ptr_derefs_may_alias_p (TREE_OPERAND (base, 0), ptr2);
+	return ptr_derefs_alias_p (TREE_OPERAND (base, 0), ptr2, must_alias);
       else if (base
 	       && DECL_P (base))
-	return ptr_deref_may_alias_decl_p (ptr2, base);
+	return ptr_deref_alias_decl_p (ptr2, base, must_alias);
       else
-	return true;
+	return !must_alias;
     }
   if (TREE_CODE (ptr2) == ADDR_EXPR)
     {
@@ -270,12 +274,12 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
       if (base
 	  && (TREE_CODE (base) == MEM_REF
 	      || TREE_CODE (base) == TARGET_MEM_REF))
-	return ptr_derefs_may_alias_p (ptr1, TREE_OPERAND (base, 0));
+	return ptr_derefs_alias_p (ptr1, TREE_OPERAND (base, 0), must_alias);
       else if (base
 	       && DECL_P (base))
-	return ptr_deref_may_alias_decl_p (ptr1, base);
+	return ptr_deref_alias_decl_p (ptr1, base, must_alias);
       else
-	return true;
+	return !must_alias;
     }
 
   /* From here we require SSA name pointers.  Anything else aliases.  */
@@ -283,7 +287,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
       || TREE_CODE (ptr2) != SSA_NAME
       || !POINTER_TYPE_P (TREE_TYPE (ptr1))
       || !POINTER_TYPE_P (TREE_TYPE (ptr2)))
-    return true;
+    return !must_alias;
 
   /* We may end up with two empty points-to solutions for two same pointers.
      In this case we still want to say both pointers alias, so shortcut
@@ -296,11 +300,21 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
   pi1 = SSA_NAME_PTR_INFO (ptr1);
   pi2 = SSA_NAME_PTR_INFO (ptr2);
   if (!pi1 || !pi2)
-    return true;
+    return !must_alias;
 
   /* ???  This does not use TBAA to prune decls from the intersection
      that not both pointers may access.  */
-  return pt_solutions_intersect (&pi1->pt, &pi2->pt);
+  return pt_solutions_intersect (&pi1->pt, &pi2->pt, must_alias);
+}
+
+/* Return true if dereferenced PTR1 and PTR2 may alias.
+   The caller is responsible for applying TBAA to see if accesses
+   through PTR1 and PTR2 may conflict at all.  */
+
+bool
+ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
+{
+  return ptr_derefs_alias_p (ptr1, ptr2, false);
 }
 
 /* Return true if dereferencing PTR may alias *REF.
@@ -316,7 +330,7 @@ ptr_deref_may_alias_ref_p_1 (tree ptr, ao_ref *ref)
       || TREE_CODE (base) == TARGET_MEM_REF)
     return ptr_derefs_may_alias_p (ptr, TREE_OPERAND (base, 0));
   else if (DECL_P (base))
-    return ptr_deref_may_alias_decl_p (ptr, base);
+    return ptr_deref_alias_decl_p (ptr, base, false);
 
   return true;
 }
@@ -672,15 +686,25 @@ ao_ref_alias_set (ao_ref *ref)
 }
 
 /* Init an alias-oracle reference representation from a gimple pointer
-   PTR and a gimple size SIZE in bytes.  If SIZE is NULL_TREE then the
-   size is assumed to be unknown.  The access is assumed to be only
-   to or after of the pointer target, not before it.  */
+   PTR and a gimple size SIZE in bytes.  The reference stores sizes and
+   offsets in bits.  If SIZE is NULL_TREE then the size is assumed to
+   be unknown.  The access is assumed to be only to or after of
+   the pointer target, not before it.  If OFFRNG is not null, make use
+   of range information associated with non-constant offsets to determine
+   the bounds on the REF->OFFSET member and set the OFFRNG[] array to its
+   range, also in bits.  */
 
 void
-ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size)
+ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size,
+			       unsigned HOST_WIDE_INT offrng[2] /* = NULL */)
 {
   HOST_WIDE_INT t, size_hwi, extra_offset = 0;
+
+  if (offrng)
+    offrng[0] = offrng[1] = 0;
+
   ref->ref = NULL_TREE;
+
   if (TREE_CODE (ptr) == SSA_NAME)
     {
       gimple *stmt = SSA_NAME_DEF_STMT (ptr);
@@ -688,12 +712,30 @@ ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size)
 	  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
 	ptr = gimple_assign_rhs1 (stmt);
       else if (is_gimple_assign (stmt)
-	       && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
-	       && TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST)
+	       && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
 	{
-	  ptr = gimple_assign_rhs1 (stmt);
-	  extra_offset = BITS_PER_UNIT
-			 * int_cst_value (gimple_assign_rhs2 (stmt));
+	  tree offset = gimple_assign_rhs2 (stmt);
+	  if (TREE_CODE (offset) == INTEGER_CST)
+	    {
+	      ptr = gimple_assign_rhs1 (stmt);
+	      extra_offset = BITS_PER_UNIT * int_cst_value (offset);
+	    }
+	  else if (offrng && TREE_CODE (offset) == SSA_NAME)
+	    {
+	      wide_int min, max;
+	      value_range_type rng = get_range_info (offset, &min, &max);
+	      if (rng == VR_RANGE && wi::fits_uhwi_p (min))
+		{
+		  ptr = gimple_assign_rhs1 (stmt);
+		  offrng[0] = BITS_PER_UNIT * min.to_uhwi ();
+		  offrng[1] = BITS_PER_UNIT * max.to_uhwi ();
+
+		  extra_offset = offrng[0];
+		}
+	      else
+		/* Offset range is indeterminate.  */
+		offrng[0] = offrng[1] = HOST_WIDE_INT_M1U;
+	    }
 	}
     }
 
@@ -773,9 +815,11 @@ same_type_for_tbaa (tree type1, tree type2)
    on an indirect reference may alias.  REF2 is the only one that can
    be a decl in which case REF2_IS_DECL is true.
    REF1_ALIAS_SET, BASE1_ALIAS_SET, REF2_ALIAS_SET and BASE2_ALIAS_SET
-   are the respective alias sets.  */
+   are the respective alias sets.
+   The returned value is the size of the overlap if it can be determined,
+   otherwise it's HOST_WIDE_INT_M1U.  */
 
-static bool
+static unsigned HOST_WIDE_INT
 aliasing_component_refs_p (tree ref1,
 			   alias_set_type ref1_alias_set,
 			   alias_set_type base1_alias_set,
@@ -784,7 +828,7 @@ aliasing_component_refs_p (tree ref1,
 			   alias_set_type ref2_alias_set,
 			   alias_set_type base2_alias_set,
 			   HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
-			   bool ref2_is_decl)
+			   bool ref2_is_decl, unsigned HOST_WIDE_INT *aloff)
 {
   /* If one reference is a component references through pointers try to find a
      common base and apply offset based disambiguation.  This handles
@@ -816,7 +860,7 @@ aliasing_component_refs_p (tree ref1,
   same_p = same_type_for_tbaa (TREE_TYPE (*refp), type1);
   /* If we couldn't compare types we have to bail out.  */
   if (same_p == -1)
-    return true;
+    return HOST_WIDE_INT_M1U;
   else if (same_p == 1)
     {
       HOST_WIDE_INT offadj, sztmp, msztmp;
@@ -825,7 +869,7 @@ aliasing_component_refs_p (tree ref1,
       offset2 -= offadj;
       get_ref_base_and_extent (base1, &offadj, &sztmp, &msztmp, &reverse);
       offset1 -= offadj;
-      return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+      return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
     }
   /* If we didn't find a common base, try the other way around.  */
   refp = &ref1;
@@ -844,7 +888,7 @@ aliasing_component_refs_p (tree ref1,
       offset1 -= offadj;
       get_ref_base_and_extent (base2, &offadj, &sztmp, &msztmp, &reverse);
       offset2 -= offadj;
-      return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+      return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
     }
 
   /* If we have two type access paths B1.path1 and B2.path2 they may
@@ -855,12 +899,12 @@ aliasing_component_refs_p (tree ref1,
      tail of path2.  */
   if (base1_alias_set == ref2_alias_set
       || alias_set_subset_of (base1_alias_set, ref2_alias_set))
-    return true;
+    return HOST_WIDE_INT_M1U;
   /* If this is ptr vs. decl then we know there is no ptr ... decl path.  */
   if (!ref2_is_decl)
     return (base2_alias_set == ref1_alias_set
 	    || alias_set_subset_of (base2_alias_set, ref1_alias_set));
-  return false;
+  return 0;
 }
 
 /* Return true if we can determine that component references REF1 and REF2,
@@ -1088,21 +1132,25 @@ nonoverlapping_component_refs_p (const_tree x, const_tree y)
    [OFFSET2, OFFSET2 + MAX_SIZE2) may alias.  REF1 and REF2
    if non-NULL are the complete memory reference trees.  */
 
-static bool
+static unsigned HOST_WIDE_INT
 decl_refs_may_alias_p (tree ref1, tree base1,
 		       HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1,
 		       tree ref2, tree base2,
-		       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2)
+		       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
+		       unsigned HOST_WIDE_INT *aloff)
 {
   gcc_checking_assert (DECL_P (base1) && DECL_P (base2));
 
   /* If both references are based on different variables, they cannot alias.  */
   if (compare_base_decls (base1, base2) == 0)
-    return false;
+    return 0;
 
   /* If both references are based on the same variable, they cannot alias if
      the accesses do not overlap.  */
-  if (!ranges_overlap_p (offset1, max_size1, offset2, max_size2))
+  HOST_WIDE_INT overlap
+    = range_overlap (offset1, max_size1, offset2, max_size2, aloff);
+
+  if (!overlap)
     return false;
 
   /* For components with variable position, the above test isn't sufficient,
@@ -1110,9 +1158,9 @@ decl_refs_may_alias_p (tree ref1, tree base1,
   if (ref1 && ref2
       && handled_component_p (ref1) && handled_component_p (ref2)
       && nonoverlapping_component_refs_of_decl_p (ref1, ref2))
-    return false;
+    return 0;
 
-  return true;     
+  return overlap;
 }
 
 /* Return true if an indirect reference based on *PTR1 constrained
@@ -1122,16 +1170,17 @@ decl_refs_may_alias_p (tree ref1, tree base1,
    in which case they are computed on-demand.  REF1 and REF2
    if non-NULL are the complete memory reference trees.  */
 
-static bool
-indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
-			       HOST_WIDE_INT offset1,
-			       HOST_WIDE_INT max_size1 ATTRIBUTE_UNUSED,
-			       alias_set_type ref1_alias_set,
-			       alias_set_type base1_alias_set,
-			       tree ref2 ATTRIBUTE_UNUSED, tree base2,
-			       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
-			       alias_set_type ref2_alias_set,
-			       alias_set_type base2_alias_set, bool tbaa_p)
+static unsigned HOST_WIDE_INT
+indirect_ref_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
+			   HOST_WIDE_INT offset1,
+			   HOST_WIDE_INT max_size1 ATTRIBUTE_UNUSED,
+			   alias_set_type ref1_alias_set,
+			   alias_set_type base1_alias_set,
+			   tree ref2 ATTRIBUTE_UNUSED, tree base2,
+			   HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
+			   alias_set_type ref2_alias_set,
+			   alias_set_type base2_alias_set, bool tbaa_p,
+			   unsigned HOST_WIDE_INT *aloff)
 {
   tree ptr1;
   tree ptrtype1, dbase2;
@@ -1161,20 +1210,20 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
      so do not apply this optimization for TARGET_MEM_REFs.  */
   if (TREE_CODE (base1) != TARGET_MEM_REF
       && !ranges_overlap_p (MAX (0, offset1p), -1, offset2p, max_size2))
-    return false;
+    return 0;
   /* They also cannot alias if the pointer may not point to the decl.  */
-  if (!ptr_deref_may_alias_decl_p (ptr1, base2))
-    return false;
+  if (!ptr_deref_alias_decl_p (ptr1, base2, aloff != 0))
+    return 0;
 
   /* Disambiguations that rely on strict aliasing rules follow.  */
   if (!flag_strict_aliasing || !tbaa_p)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   ptrtype1 = TREE_TYPE (TREE_OPERAND (base1, 1));
 
   /* If the alias set for a pointer access is zero all bets are off.  */
   if (base1_alias_set == 0)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* When we are trying to disambiguate an access with a pointer dereference
      as base versus one with a decl as base we can use both the size
@@ -1190,7 +1239,7 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
      !alias_set_subset_of (base1_alias_set, base2_alias_set) instead.  */
   if (base1_alias_set != base2_alias_set
       && !alias_sets_conflict_p (base1_alias_set, base2_alias_set))
-    return false;
+    return 0;
   /* If the size of the access relevant for TBAA through the pointer
      is bigger than the size of the decl we can't possibly access the
      decl via that pointer.  */
@@ -1203,10 +1252,10 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
       && TREE_CODE (TREE_TYPE (ptrtype1)) != UNION_TYPE
       && TREE_CODE (TREE_TYPE (ptrtype1)) != QUAL_UNION_TYPE
       && tree_int_cst_lt (DECL_SIZE (base2), TYPE_SIZE (TREE_TYPE (ptrtype1))))
-    return false;
+    return 0;
 
   if (!ref2)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* If the decl is accessed via a MEM_REF, reconstruct the base
      we can use for TBAA and an appropriately adjusted offset.  */
@@ -1229,7 +1278,7 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
   /* If either reference is view-converted, give up now.  */
   if (same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (ptrtype1)) != 1
       || same_type_for_tbaa (TREE_TYPE (dbase2), TREE_TYPE (base2)) != 1)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* If both references are through the same type, they do not alias
      if the accesses do not overlap.  This does extra disambiguation
@@ -1241,11 +1290,11 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
   if ((TREE_CODE (base1) != TARGET_MEM_REF
        || (!TMR_INDEX (base1) && !TMR_INDEX2 (base1)))
       && same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (dbase2)) == 1)
-    return ranges_overlap_p (doffset1, max_size1, doffset2, max_size2);
+    return range_overlap (doffset1, max_size1, doffset2, max_size2, aloff);
 
   if (ref1 && ref2
       && nonoverlapping_component_refs_p (ref1, ref2))
-    return false;
+    return 0;
 
   /* Do access-path based disambiguation.  */
   if (ref1 && ref2
@@ -1255,27 +1304,32 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
 				      offset1, max_size1,
 				      ref2,
 				      ref2_alias_set, base2_alias_set,
-				      offset2, max_size2, true);
+				      offset2, max_size2, true, aloff);
 
-  return true;
+  return aloff ? 0 : HOST_WIDE_INT_M1U;
 }
 
-/* Return true if two indirect references based on *PTR1
-   and *PTR2 constrained to [OFFSET1, OFFSET1 + MAX_SIZE1) and
-   [OFFSET2, OFFSET2 + MAX_SIZE2) may alias.  *PTR1 and *PTR2 have
-   the alias sets BASE1_ALIAS_SET and BASE2_ALIAS_SET which can be -1
-   in which case they are computed on-demand.  REF1 and REF2
-   if non-NULL are the complete memory reference trees. */
-
-static bool
-indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
-			   HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1,
-			   alias_set_type ref1_alias_set,
-			   alias_set_type base1_alias_set,
-			   tree ref2 ATTRIBUTE_UNUSED, tree base2,
-			   HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
-			   alias_set_type ref2_alias_set,
-			   alias_set_type base2_alias_set, bool tbaa_p)
+/* Return non-zero if two indirect references based on *PTR1 and *PTR2
+   constrained to [OFFSET1, OFFSET1 + MAX_SIZE1) and  [OFFSET2, OFFSET2
+   + MAX_SIZE2) may alias.  *PTR1 and *PTR2 have the alias sets
+   BASE1_ALIAS_SET and BASE2_ALIAS_SET which can be -1 in which case
+   they are computed on-demand.  If non-NULL, REF1 and REF2 are the
+   complete memory reference trees.
+   The returned value is the size of the overlap if it can be determined,
+   otherwise it's HOST_WIDE_INT_M1U.  When ALOFF is non-null, the returned
+   value is non-zero if and only if REF1 and REF2 definitely overlap, and
+   *ALOFF is set to the offset of the overlap in REF1.  */
+
+static unsigned HOST_WIDE_INT
+indirect_refs_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
+		       HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1,
+		       alias_set_type ref1_alias_set,
+		       alias_set_type base1_alias_set,
+		       tree ref2 ATTRIBUTE_UNUSED, tree base2,
+		       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
+		       alias_set_type ref2_alias_set,
+		       alias_set_type base2_alias_set, bool tbaa_p,
+		       unsigned HOST_WIDE_INT *aloff)
 {
   tree ptr1;
   tree ptr2;
@@ -1328,14 +1382,14 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
 	offset1 += (-moff).to_short_addr ();
       else
 	offset2 += moff.to_short_addr ();
-      return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+      return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
     }
-  if (!ptr_derefs_may_alias_p (ptr1, ptr2))
-    return false;
+  if (!ptr_derefs_alias_p (ptr1, ptr2, aloff != NULL))
+    return 0;
 
   /* Disambiguations that rely on strict aliasing rules follow.  */
   if (!flag_strict_aliasing || !tbaa_p)
-    return true;
+    return HOST_WIDE_INT_M1U;
 
   ptrtype1 = TREE_TYPE (TREE_OPERAND (base1, 1));
   ptrtype2 = TREE_TYPE (TREE_OPERAND (base2, 1));
@@ -1343,7 +1397,7 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
   /* If the alias set for a pointer access is zero all bets are off.  */
   if (base1_alias_set == 0
       || base2_alias_set == 0)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* If both references are through the same type, they do not alias
      if the accesses do not overlap.  This does extra disambiguation
@@ -1359,21 +1413,21 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
       /* But avoid treating arrays as "objects", instead assume they
          can overlap by an exact multiple of their element size.  */
       && TREE_CODE (TREE_TYPE (ptrtype1)) != ARRAY_TYPE)
-    return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+    return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
 
   /* Do type-based disambiguation.  */
   if (base1_alias_set != base2_alias_set
       && !alias_sets_conflict_p (base1_alias_set, base2_alias_set))
-    return false;
+    return 0;
 
   /* If either reference is view-converted, give up now.  */
   if (same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (ptrtype1)) != 1
       || same_type_for_tbaa (TREE_TYPE (base2), TREE_TYPE (ptrtype2)) != 1)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   if (ref1 && ref2
       && nonoverlapping_component_refs_p (ref1, ref2))
-    return false;
+    return 0;
 
   /* Do access-path based disambiguation.  */
   if (ref1 && ref2
@@ -1383,21 +1437,21 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
 				      offset1, max_size1,
 				      ref2,
 				      ref2_alias_set, base2_alias_set,
-				      offset2, max_size2, false);
+				      offset2, max_size2, false, aloff);
 
-  return true;
+  return HOST_WIDE_INT_M1U;
 }
 
-/* Return true, if the two memory references REF1 and REF2 may alias.  */
+/* Return non-zero if the two memory references REF1 and REF2 may alias.
+   The returned value is the size of the overlap if it can be determined,
+   otherwise it's HOST_WIDE_INT_M1U.  When ALOFF is non-null, the returned
+   value is non-zero if and only if REF1 and REF2 definitely overlap, and
+   *ALOFF is set to the offset of the overlap in REF1.  */
 
-bool
-refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
+static unsigned HOST_WIDE_INT
+refs_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p,
+		unsigned HOST_WIDE_INT *aloff)
 {
-  tree base1, base2;
-  HOST_WIDE_INT offset1 = 0, offset2 = 0;
-  HOST_WIDE_INT max_size1 = -1, max_size2 = -1;
-  bool var1_p, var2_p, ind1_p, ind2_p;
-
   gcc_checking_assert ((!ref1->ref
 			|| TREE_CODE (ref1->ref) == SSA_NAME
 			|| DECL_P (ref1->ref)
@@ -1414,12 +1468,30 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
 			   || TREE_CODE (ref2->ref) == TARGET_MEM_REF));
 
   /* Decompose the references into their base objects and the access.  */
-  base1 = ao_ref_base (ref1);
-  offset1 = ref1->offset;
-  max_size1 = ref1->max_size;
-  base2 = ao_ref_base (ref2);
-  offset2 = ref2->offset;
-  max_size2 = ref2->max_size;
+  tree base1 = ao_ref_base (ref1);
+  tree base2 = ao_ref_base (ref2);
+
+  HOST_WIDE_INT offset1 = ref1->offset;
+  HOST_WIDE_INT offset2 = ref2->offset;
+
+  HOST_WIDE_INT max_size1 = ref1->max_size;
+  HOST_WIDE_INT max_size2 = ref2->max_size;
+
+  bool must_alias = aloff != NULL;
+  if (must_alias)
+    {
+      /* ALOFF being non-null implies a request for a must-alias kind of
+	 analysis.  Consider the minimum size when ALOFF is non-zero.  */
+      max_size1 = ref1->size;
+      max_size2 = ref2->size;
+    }
+  else
+    {
+      /* ALOFF being null implies a request for a may-alias kind of
+	 analysis.  Consider the maximum size.  */
+      max_size1 = ref1->max_size;
+      max_size2 = ref2->max_size;
+    }
 
   /* We can end up with registers or constants as bases for example from
      *D.1663_44 = VIEW_CONVERT_EXPR<struct DB_LSN>(__tmp$B0F64_59);
@@ -1434,7 +1506,7 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       || TREE_CODE (base2) == CONSTRUCTOR
       || TREE_CODE (base2) == ADDR_EXPR
       || CONSTANT_CLASS_P (base2))
-    return false;
+    return 0;
 
   /* We can end up referring to code via function and label decls.
      As we likely do not properly track code aliases conservatively
@@ -1443,22 +1515,23 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       || TREE_CODE (base1) == LABEL_DECL
       || TREE_CODE (base2) == FUNCTION_DECL
       || TREE_CODE (base2) == LABEL_DECL)
-    return true;
+    return must_alias ? 0 : HOST_WIDE_INT_M1U;
 
-  /* Two volatile accesses always conflict.  */
+  /* Two volatile accesses may always conflict.  */
   if (ref1->volatile_p
       && ref2->volatile_p)
-    return true;
+    return must_alias ? 0 : HOST_WIDE_INT_M1U;
 
   /* Defer to simple offset based disambiguation if we have
      references based on two decls.  Do this before defering to
      TBAA to handle must-alias cases in conformance with the
      GCC extension of allowing type-punning through unions.  */
-  var1_p = DECL_P (base1);
-  var2_p = DECL_P (base2);
+  bool var1_p = DECL_P (base1);
+  bool var2_p = DECL_P (base2);
   if (var1_p && var2_p)
     return decl_refs_may_alias_p (ref1->ref, base1, offset1, max_size1,
-				  ref2->ref, base2, offset2, max_size2);
+				  ref2->ref, base2, offset2, max_size2,
+				  aloff);
 
   /* Handle restrict based accesses.
      ???  ao_ref_base strips inner MEM_REF [&decl], recover from that
@@ -1486,12 +1559,12 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       && MR_DEPENDENCE_CLIQUE (base1) == MR_DEPENDENCE_CLIQUE (base2)
       /* But based on different pointers they do not alias.  */
       && MR_DEPENDENCE_BASE (base1) != MR_DEPENDENCE_BASE (base2))
-    return false;
+    return 0;
 
-  ind1_p = (TREE_CODE (base1) == MEM_REF
-	    || TREE_CODE (base1) == TARGET_MEM_REF);
-  ind2_p = (TREE_CODE (base2) == MEM_REF
-	    || TREE_CODE (base2) == TARGET_MEM_REF);
+  bool ind1_p = (TREE_CODE (base1) == MEM_REF
+		 || TREE_CODE (base1) == TARGET_MEM_REF);
+  bool ind2_p = (TREE_CODE (base2) == MEM_REF
+		 || TREE_CODE (base2) == TARGET_MEM_REF);
 
   /* Canonicalize the pointer-vs-decl case.  */
   if (ind1_p && var2_p)
@@ -1511,33 +1584,65 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       && flag_strict_aliasing
       && !alias_sets_conflict_p (ao_ref_alias_set (ref1),
 				 ao_ref_alias_set (ref2)))
-    return false;
+    return 0;
 
   /* Dispatch to the pointer-vs-decl or pointer-vs-pointer disambiguators.  */
   if (var1_p && ind2_p)
-    return indirect_ref_may_alias_decl_p (ref2->ref, base2,
-					  offset2, max_size2,
-					  ao_ref_alias_set (ref2),
-					  ao_ref_base_alias_set (ref2),
-					  ref1->ref, base1,
-					  offset1, max_size1,
-					  ao_ref_alias_set (ref1),
-					  ao_ref_base_alias_set (ref1),
-					  tbaa_p);
-  else if (ind1_p && ind2_p)
-    return indirect_refs_may_alias_p (ref1->ref, base1,
-				      offset1, max_size1,
-				      ao_ref_alias_set (ref1),
-				      ao_ref_base_alias_set (ref1),
-				      ref2->ref, base2,
+    return indirect_ref_alias_decl_p (ref2->ref, base2,
 				      offset2, max_size2,
 				      ao_ref_alias_set (ref2),
 				      ao_ref_base_alias_set (ref2),
-				      tbaa_p);
+				      ref1->ref, base1,
+				      offset1, max_size1,
+				      ao_ref_alias_set (ref1),
+				      ao_ref_base_alias_set (ref1),
+				      tbaa_p, aloff);
+  else if (ind1_p && ind2_p)
+    return indirect_refs_alias_p (ref1->ref, base1,
+				  offset1, max_size1,
+				  ao_ref_alias_set (ref1),
+				  ao_ref_base_alias_set (ref1),
+				  ref2->ref, base2,
+				  offset2, max_size2,
+				  ao_ref_alias_set (ref2),
+				  ao_ref_base_alias_set (ref2),
+				  tbaa_p, aloff);
 
   gcc_unreachable ();
 }
 
+/* Return true, if the two memory references REF1 and REF2 may alias.  */
+
+bool
+refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
+{
+  return refs_alias_p_1 (ref1, ref2, tbaa_p, NULL);
+}
+
+/* Return the size of the overlap in bytes if REF1 and REF2 alias one
+   another, and set *ALOFF (alias offset) to the offset of the overlap
+   in REF1.  Return HOST_WIDE_INT_M1U if the size of the overlap cannot
+   be determined.  Otherwise, if REF1 and REF2 do not definitely overlap,
+   return zero.  */
+
+unsigned HOST_WIDE_INT
+refs_must_alias_p_1 (ao_ref *ref1, ao_ref *ref2, unsigned HOST_WIDE_INT *aloff)
+{
+  /* ALOFF being non-zero is what distinguishes a must kind of analysis
+     from a may kind.  Set it to &DUMMY when the caller doesn't care
+     about the offset.  */
+  unsigned HOST_WIDE_INT dummy;
+  if (!aloff)
+    aloff = &dummy;
+
+  unsigned HOST_WIDE_INT alsz = refs_alias_p_1 (ref1, ref2, true, aloff);
+
+  /* Convert the size and offset of the overlap from bits to bytes.  */
+  if (alsz)
+    *aloff /= BITS_PER_UNIT;
+  return alsz / BITS_PER_UNIT;
+}
+
 static bool
 refs_may_alias_p (tree ref1, ao_ref *ref2)
 {
@@ -2945,3 +3050,90 @@ walk_aliased_vdefs (ao_ref *ref, tree vdef,
   return ret;
 }
 
+/* Attempt to detect and diagnose overlapping copy in a statement STMT
+   from SRC to DST of SIZE bytes.  When ADJUST is set, adjust SIZE to
+   reflect the lower bound of the non-constant SRC offset.  Return true
+   when overlap has been detected, false otherwise.
+   In the case of a non-constant offset, using a positive lower bound
+   is more strict than using the upper bound.  */
+
+bool
+detect_overlap (location_t loc, gimple *stmt, tree dst, tree src, tree size,
+		bool adjust /* = false */)
+{
+  ao_ref dstref, srcref;
+  unsigned HOST_WIDE_INT range[2];
+
+  /* Initialize and store the lower bound of a constant offset (in
+     bits), disregarding the offset for the destination.  */
+  ao_ref_init_from_ptr_and_size (&dstref, dst, size, range);
+  ao_ref_init_from_ptr_and_size (&srcref, src, size, range);
+
+  if (adjust)
+    {
+      /* Adjust the source and destination sizes (in bits) by
+	 the non-constant offset (also in bits).  */
+      dstref.size -= range[0];
+      srcref.size -= range[0];
+    }
+
+  /* Determine the size of the OVERLAP, if any, and its OFFSET, both
+     in bytes.  */
+  unsigned HOST_WIDE_INT offset;
+  unsigned HOST_WIDE_INT overlap
+    = refs_must_alias_p_1 (&dstref, &srcref, &offset);
+
+  if (!overlap)
+    return false;
+
+  /* Convert the size from bits to bytes (OFFSET is in bytes).  */
+
+  range[1] = (dstref.size - (range[1] - range[0])) / BITS_PER_UNIT;
+  range[0] = dstref.size / BITS_PER_UNIT;
+
+  if (range[0] > range[1])
+    std::swap (range[0], range[1]);
+
+  bool warned;
+
+  tree func = gimple_call_fndecl (stmt);
+
+  /* Below distinguish three cases:
+     1) certain constant overlap
+     2) certain overlap in some range
+     3) possible overlap.  */
+  if (range[0] == range[1])
+    warned = warning_at (loc, OPT_Wrestrict,
+			 range[0] == 1
+			 ? (overlap == 1
+			    ? G_("%qD writing %wu byte overlaps %wu byte "
+				 "at offset %wu")
+			    : G_("%qD writing %wu byte overlaps %wu bytes "
+				 "at offset %wu"))
+			 : (overlap == 1
+			    ? G_("%qD writing %wu bytes overlaps %wu byte "
+				 "at offset %wu")
+			    : G_("%qD writing %wu bytes overlaps %wu bytes "
+				 "at offset %wu")),
+			 func, range[0], overlap, offset);
+  else if (range[1] - range[0] < overlap)
+    warned = warning_at (loc, OPT_Wrestrict,
+			 overlap == 1
+			 ? G_("%qD writing between %wu and %wu bytes "
+			      "overlaps %wu byte at offset %wu")
+			 : G_("%qD writing between %wu and %wu bytes "
+			      "overlaps %wu bytes at offset %wu"),
+			 func, range[0], range[1], overlap, offset);
+  else
+    warned = warning_at (loc, OPT_Wrestrict,
+			 overlap == 1
+			 ? G_("%qD writing between %wu and %wu bytes "
+			      "may overlap %wu byte at offset %wu")
+			 : G_("%qD writing %wu byte may overlap %wu bytes "
+			      "at offset %wu"),
+			 func, range[0], range[1], overlap, offset);
+  if (warned)
+    gimple_set_no_warning (stmt, true);
+
+  return true;
+}
diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
index 8c89c69..8b3609d 100644
--- a/gcc/tree-ssa-alias.h
+++ b/gcc/tree-ssa-alias.h
@@ -99,17 +99,21 @@ struct ao_ref
 
 /* In tree-ssa-alias.c  */
 extern void ao_ref_init (ao_ref *, tree);
-extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree);
+extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree,
+					   unsigned HOST_WIDE_INT[2] = NULL);
 extern tree ao_ref_base (ao_ref *);
 extern alias_set_type ao_ref_alias_set (ao_ref *);
 extern alias_set_type ao_ref_base_alias_set (ao_ref *);
 extern bool ptr_deref_may_alias_global_p (tree);
 extern bool ptr_derefs_may_alias_p (tree, tree);
+extern bool ptr_derefs_must_alias_p (tree, tree);
 extern bool ptrs_compare_unequal (tree, tree);
 extern bool ref_may_alias_global_p (tree);
 extern bool ref_may_alias_global_p (ao_ref *);
 extern bool refs_may_alias_p (tree, tree);
 extern bool refs_may_alias_p_1 (ao_ref *, ao_ref *, bool);
+extern unsigned HOST_WIDE_INT refs_must_alias_p_1 (ao_ref *, ao_ref *,
+						   unsigned HOST_WIDE_INT *);
 extern bool refs_anti_dependent_p (tree, tree);
 extern bool refs_output_dependent_p (tree, tree);
 extern bool ref_maybe_used_by_stmt_p (gimple *, tree);
@@ -144,6 +148,8 @@ extern void debug (pt_solution *ptr);
 extern void dump_points_to_info_for (FILE *, tree);
 extern void debug_points_to_info_for (tree);
 extern void dump_alias_stats (FILE *);
+extern bool detect_overlap (location_t, gimple *, tree, tree, tree,
+			    bool = false);
 
 
 /* In tree-ssa-structalias.c  */
@@ -152,7 +158,8 @@ extern bool pt_solution_empty_p (struct pt_solution *);
 extern bool pt_solution_singleton_or_null_p (struct pt_solution *, unsigned *);
 extern bool pt_solution_includes_global (struct pt_solution *);
 extern bool pt_solution_includes (struct pt_solution *, const_tree);
-extern bool pt_solutions_intersect (struct pt_solution *, struct pt_solution *);
+extern bool pt_solutions_intersect (struct pt_solution *, struct pt_solution *,
+				    bool = false);
 extern void pt_solution_reset (struct pt_solution *);
 extern void pt_solution_set (struct pt_solution *, bitmap, bool);
 extern void pt_solution_set_var (struct pt_solution *, tree);
@@ -161,6 +168,50 @@ extern void dump_pta_stats (FILE *);
 
 extern GTY(()) struct pt_solution ipa_escaped_pt;
 
+/* Return the size of the overlap if the two ranges [POS1, POS1 + SIZE1]
+   and [POS2, POS2 + SIZE2] overlap.  Set *OFFSET to the offset of the
+   overlap.  When SIZE1 or SIZE2 is -1 the range is half-open.  Otherwise,
+   return zero.  */
+
+static inline unsigned HOST_WIDE_INT
+range_overlap (HOST_WIDE_INT pos1,
+	       unsigned HOST_WIDE_INT size1,
+	       HOST_WIDE_INT pos2,
+	       unsigned HOST_WIDE_INT size2,
+	       unsigned HOST_WIDE_INT *offset)
+{
+  if (pos1 >= pos2)
+    {
+      if (offset)
+	*offset = pos1 - pos2;
+
+      if (size2 == HOST_WIDE_INT_M1U)
+	/* Overlap is certain but its size is unknown.  */
+	return HOST_WIDE_INT_M1U;
+
+      unsigned HOST_WIDE_INT end2 = pos2 + (HOST_WIDE_INT) size2;
+      if ((unsigned HOST_WIDE_INT) pos1 < end2)
+	return end2 - pos1;
+    }
+
+  if (pos2 >= pos1)
+    {
+      if (offset)
+	*offset = pos2 - pos1;
+
+      if (size1 == HOST_WIDE_INT_M1U)
+	/* Overlap is certain but its size is unknown.  */
+	return HOST_WIDE_INT_M1U;
+
+      unsigned HOST_WIDE_INT end1 = pos1 + (HOST_WIDE_INT) size1;
+      if ((unsigned HOST_WIDE_INT) pos2 < end1)
+	return end1 - pos2;
+    }
+
+  /* No overlap.  */
+  return 0;
+}
+
 /* Return true, if the two ranges [POS1, SIZE1] and [POS2, SIZE2]
    overlap.  SIZE1 and/or SIZE2 can be (unsigned)-1 in which case the
    range is open-ended.  Otherwise return false.  */
@@ -171,18 +222,7 @@ ranges_overlap_p (HOST_WIDE_INT pos1,
 		  HOST_WIDE_INT pos2,
 		  unsigned HOST_WIDE_INT size2)
 {
-  if (pos1 >= pos2
-      && (size2 == (unsigned HOST_WIDE_INT)-1
-	  || pos1 < (pos2 + (HOST_WIDE_INT) size2)))
-    return true;
-  if (pos2 >= pos1
-      && (size1 == (unsigned HOST_WIDE_INT)-1
-	  || pos2 < (pos1 + (HOST_WIDE_INT) size1)))
-    return true;
-
-  return false;
+  return range_overlap (pos1, size1, pos2, size2, NULL) != 0;
 }
 
-
-
 #endif /* TREE_SSA_ALIAS_H  */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index b0563fe..75c01b0 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "expr.h"
 #include "tree-dfa.h"
 #include "domwalk.h"
+#include "tree-ssa-alias.h"
 #include "tree-ssa-propagate.h"
 #include "params.h"
 #include "ipa-chkp.h"
@@ -1477,6 +1478,22 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 	    }
 	}
       dsi->stmt = stmt;
+
+      /* Try to detect overlap before returning.  This catches cases
+	 like strcpy (d, d + n) where n is non-constant whose range
+	 is such that (n <= strlen (d) holds).
+
+	 OLDDSI->NONZERO_chars may have been reset by this point with
+	 oldlen holding it original value.  */
+      if (olddsi && oldlen)
+	{
+	  /* Add 1 for the terminating NUL.  */
+	  tree type = TREE_TYPE (oldlen);
+	  oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
+				build_int_cst (type, 1));
+	  detect_overlap (loc, stmt, olddsi->ptr, src, oldlen, true);
+	}
+
       return;
     }
 
@@ -1559,6 +1576,12 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
   len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
 				  GSI_SAME_STMT);
+
+  if (const strinfo *chksi = olddsi ? olddsi : dsi)
+    if (si && detect_overlap (loc, stmt, chksi->ptr, si->ptr, len, true))
+      /* Avoid transforming strcpy to memcpy when overlap is detected.  */
+      return;
+
   if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     {
       fprintf (dump_file, "Optimizing: ");
@@ -1786,7 +1809,7 @@ static void
 handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr;
+  tree src, dst, srclen, dstlen, lhs, args, type, fn, objsz, endptr;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi;
@@ -1804,10 +1827,43 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   dsi = NULL;
   if (didx > 0)
     dsi = get_strinfo (didx);
+
+  srclen = NULL_TREE;
+  si = NULL;
+  idx = get_stridx (src);
+  if (idx < 0)
+    srclen = build_int_cst (size_type_node, ~idx);
+  else if (idx > 0)
+    {
+      si = get_strinfo (idx);
+      if (si != NULL)
+	srclen = get_string_length (si);
+    }
+
+  loc = gimple_location (stmt);
+
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
+      if (warn_restrict)
+	{
+	  /* The concatenation always involves copying at least one byte
+	     (the terminating nul), even if the source string is empty.
+	     If the source is unknown assume it's one character long and
+	     compute the size of the result.  */
+	  tree ressize = srclen ? srclen : size_one_node;
+	  tree type = TREE_TYPE (ressize);
+	  ressize = fold_build2 (PLUS_EXPR, type, ressize, ressize);
+	  ressize = fold_build2 (PLUS_EXPR, type, ressize,
+				 build_int_cst (type, 1));
+
+	  tree sptr = si && si->ptr ? si->ptr : src;
+	  if (detect_overlap (loc, stmt, dst, sptr, ressize, true))
+	    /* Avoid transforming overlapping strcat.  */
+	    return;
+	}
+
       /* strcat (p, q) can be transformed into
-	 tmp = p + strlen (p); endptr = strpcpy (tmp, q);
+	 tmp = p + strlen (p); endptr = stpcpy (tmp, q);
 	 with length endptr - p if we need to compute the length
 	 later on.  Don't do this transformation if we don't need
 	 it.  */
@@ -1840,19 +1896,6 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       return;
     }
 
-  srclen = NULL_TREE;
-  si = NULL;
-  idx = get_stridx (src);
-  if (idx < 0)
-    srclen = build_int_cst (size_type_node, ~idx);
-  else if (idx > 0)
-    {
-      si = get_strinfo (idx);
-      if (si != NULL)
-	srclen = get_string_length (si);
-    }
-
-  loc = gimple_location (stmt);
   dstlen = dsi->nonzero_chars;
   endptr = dsi->endptr;
 
@@ -1914,7 +1957,22 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (fn == NULL_TREE)
     return;
 
-  len = NULL_TREE;
+  if (warn_restrict && dsi && dstlen)
+    {
+      tree slen = srclen ? srclen : size_zero_node;
+      /* Compute the size of the concatenated string, including
+	 the terminating nul.  */
+      tree type = TREE_TYPE (dstlen);
+      tree ressize = fold_build2 (PLUS_EXPR, type, dstlen, slen);
+      ressize = fold_build2 (PLUS_EXPR, type, ressize, build_int_cst (type, 1));
+
+      tree sptr = si && si->ptr ? si->ptr : src;
+      if (detect_overlap (loc, stmt, dst, sptr, ressize, true))
+	/* Avoid transforming overlapping strcat.  */
+	return;
+    }
+
+  tree len = NULL_TREE;
   if (srclen != NULL_TREE)
     {
       args = TYPE_ARG_TYPES (TREE_TYPE (fn));
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index e563e9d..1728260 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -6743,31 +6743,33 @@ pt_solution_includes (struct pt_solution *pt, const_tree decl)
 }
 
 /* Return true if both points-to solutions PT1 and PT2 have a non-empty
-   intersection.  */
+   intersection.  When STRICT is true, interpret only definitive results
+   as positive, and possible results as negative.  */
 
 static bool
-pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2)
+pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2,
+			  bool strict = false)
 {
   if (pt1->anything || pt2->anything)
-    return true;
+    return !strict;
 
   /* If either points to unknown global memory and the other points to
-     any global memory they alias.  */
+     any global memory they may (though need not) alias.  */
   if ((pt1->nonlocal
        && (pt2->nonlocal
 	   || pt2->vars_contains_nonlocal))
       || (pt2->nonlocal
 	  && pt1->vars_contains_nonlocal))
-    return true;
+    return !strict;
 
   /* If either points to all escaped memory and the other points to
-     any escaped memory they alias.  */
+     any escaped memory they may (though need not) alias.  */
   if ((pt1->escaped
        && (pt2->escaped
 	   || pt2->vars_contains_escaped))
       || (pt2->escaped
 	  && pt1->vars_contains_escaped))
-    return true;
+    return !strict;
 
   /* Check the escaped solution if required.
      ???  Do we need to check the local against the IPA escaped sets?  */
@@ -6775,17 +6777,17 @@ pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2)
       && !pt_solution_empty_p (&ipa_escaped_pt))
     {
       /* If both point to escaped memory and that solution
-	 is not empty they alias.  */
+	 is not empty they may alias.  */
       if (pt1->ipa_escaped && pt2->ipa_escaped)
-	return true;
+	return !strict;
 
       /* If either points to escaped memory see if the escaped solution
 	 intersects with the other.  */
       if ((pt1->ipa_escaped
-	   && pt_solutions_intersect_1 (&ipa_escaped_pt, pt2))
+	   && pt_solutions_intersect_1 (&ipa_escaped_pt, pt2, strict))
 	  || (pt2->ipa_escaped
-	      && pt_solutions_intersect_1 (&ipa_escaped_pt, pt1)))
-	return true;
+	      && pt_solutions_intersect_1 (&ipa_escaped_pt, pt1, strict)))
+	return !strict;
     }
 
   /* Now both pointers alias if their points-to solution intersects.  */
@@ -6794,10 +6796,17 @@ pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2)
 	  && bitmap_intersect_p (pt1->vars, pt2->vars));
 }
 
+/* Return true if the solutions *PT1 and *PT2 intersect.  When STRICT
+   is true, interpret only definitive results as positive, and possible
+   results as negative.  The former effectively gives an answer to
+   the qestion of whether two pointers must refer to the same array,
+   while the latter answers whether they may refer to one.  */
+
 bool
-pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2)
+pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2,
+			bool strict /* = false */)
 {
-  bool res = pt_solutions_intersect_1 (pt1, pt2);
+  bool res = pt_solutions_intersect_1 (pt1, pt2, strict);
   if (res)
     ++pta_stats.pt_solutions_intersect_may_alias;
   else
@@ -6805,7 +6814,6 @@ pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2)
   return res;
 }
 
-
 /* Dump points-to information to OUTFILE.  */
 
 static void
PR middle-end/78918 - missing -Wrestrict on memcpy copying over self

gcc/ChangeLog:

	PR middle-end/78918
	* builtins.c (warn_for_overlap, maybe_warn_for_overlap): New.
	(check_sizes): Add argument and call maybe_warn_for_overlap.
	Rename function arguments for clarity.
	(check_memop_sizes): Adjust.
	(expand_builtin_memchr): Ditto.
	(expand_builtin_strcat): Ditto.
	(expand_builtin_strcpy): Ditto.
	(expand_builtin_stpcpy): Ditto.
	(expand_builtin_stpncpy): Ditto.
	(expand_builtin_strncpy): Ditto.
	(expand_builtin_memcmp): Ditto.
	(expand_builtin_memory_chk): Ditto.
	(check_strncat_sizes): Ditto.  Rename locals for clarity.
	(expand_builtin_strncat): Ditto.
	(maybe_emit_chk_warning): Ditto.
	(maybe_emit_sprintf_chk_warning): Adjust.
	* cfgexpand.c (expand_call_stmt): Set TREE_NO_WARNING.
	* gimple-fold.c (gimple_fold_builtin_memory_op): Handle -Wrestrict.
	(gimple_fold_builtin_strcpy): Ditto.
	(gimple_fold_builtin_memory_chk): Ditto.
	(gimple_fold_builtin_stxcpy_chk): Ditto.
	* gimple.c (gimple_build_call_from_tree): Set call location.
	* tree-ssa-alias.c (ptr_deref_may_alias_decl_p): Add argument
	and rename...	
	(ptr_deref_alias_decl_p): ...to this.
	(ptr_derefs_may_alias_p): Add argument and rename...
	(ptr_derefs_alias_p): ...to this.
	(ptr_deref_may_alias_ref_p_1): Adjust.
	(ao_ref_init_from_ptr_and_size): Handle new argument.
	(aliasing_component_refs_p): Change return type and add argument.
	(decl_refs_may_alias_p): Ditto.
	(indirect_ref_may_alias_decl_p): Ditto.
	(indirect_refs_may_alias_p): Ditto.  Rename to...
	(indirect_refs_alias_p): ...this.
	(refs_alias_p_1): New static function.
	(refs_may_alias_p_1): Call it.
	(refs_must_alias_p_1): New extern function.
	(detect_overlap): Define.
	* tree-ssa-alias.h (ao_ref_init_from_ptr_and_size): Add optional
	argument.
	(detect_overlap): New function.
	(range_overlap): New inline function.
	(ranges_overlap_p): Call it.
	* tree-ssa-strlen.c (handle_builtin_strcpy): Handle -Wrestrict.
	(handle_builtin_strcat): Ditto.
	* tree-ssa-structalias.c (pt_solution_includes): Add argument.
	(pt_solutions_intersect_1): Ditto.
	(pt_solutions_intersect): Adjust.

gcc/c-family/ChangeLog:

	PR middle-end/78918
	* c-common.c (check_function_restrict): Suppress warning for
	built-in functions.
	* c.opt (-Wrestrict): Include in -Wall.

gcc/testsuite/ChangeLog:

	PR middle-end/78918
	* c-c++-common/Wrestrict.c: New test.
	* gcc.dg/Walloca-1.c: Suppress macro expansion tracking.
	* gcc.dg/pr69172.c: Prune -Wrestrict.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 2deef72..b2baa03 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3036,39 +3036,291 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
   return dest_addr;
 }
 
+/* Issue a warning for a restricted copy call expression EXP to a built-in
+   function FUNC, with a destination of size DSTSIZE, size of copy in RANGE,
+   and with OVERLAP bytes at OFFSET.  */
+
+static void
+warn_for_overlap (tree exp, tree func, bool must_overlap, tree dstsize,
+		  const tree range[2], unsigned HOST_WIDE_INT overlap,
+		  const unsigned HOST_WIDE_INT offrange[2])
+{
+  location_t loc = tree_nonartificial_location (exp);
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  /* To avoid combinatorial explosion of diagnostics format the offset
+     or its range as a string and use it in the warning calls below.  */
+  char offstr[64];
+  if (offrange[0] == offrange[1] || offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr, "%llu", (long long) offrange[0]);
+  else
+    sprintf (offstr, "%llu - %llu", (long long) offrange[0],
+	     (long long) offrange[1]);
+
+  /* The text uses the term "writing N bytes" even though most operations
+     involve "copying" and the latter might be clearer.  This first term
+     is used because string functions like strncat and strncpy don't
+     necessarily copy all N bytes; they may copy fewer.  */
+
+  if (tree_to_uhwi (dstsize) >= HOST_WIDE_INT_MAX)
+    {
+      if (must_overlap)
+	{
+	  if (tree_int_cst_equal (range[0], range[1]))
+	    warning_at (loc, OPT_Wrestrict,
+			(integer_onep (range[0])
+			 ? G_("%K%qD writing %E byte overlaps %wu byte "
+			      "at offset %s")
+			 : (overlap == 1
+			    ? G_("%K%qD writing %E bytes overlaps %wu byte "
+				 "at offset %s")
+			    : G_("%K%qD writing %E bytes overlaps %wu bytes "
+				 "at offset %s"))),
+			exp, func, range[0], overlap, offstr);
+	  else if (tree_int_cst_sign_bit (range[1]))
+	    {
+	      /* Avoid printing the upper bound if it's invalid.
+		 The overlap can likewise be "or more" in this case.  */
+	      warning_at (loc, OPT_Wrestrict,
+			  "%K%qD writing %E or more bytes overlaps %wu "
+			  "or more bytes at offset %s",
+			  exp, func, range[0], overlap, offstr);
+	    }
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD writing between %E and %E bytes overlaps "
+			"%wu or more bytes at offset %s",
+			exp, func, range[0], range[1], overlap, offstr);
+	}
+      else
+	{
+	  if (tree_int_cst_equal (range[0], range[1]))
+	    warning_at (loc, OPT_Wrestrict,
+			(integer_onep (range[0])
+			 ? G_("%K%qD writing %E byte may overlap %wu bytes "
+			      "at offset %s")
+			 : (overlap == 1
+			    ? G_("%K%qD writing %E bytes may overlap %wu byte "
+				 "at offset %s")
+			    : G_("%K%qD writing %E bytes may overlap %wu bytes "
+				 "at offset %s"))),
+			exp, func, range[0], overlap, offstr);
+	  else if (tree_int_cst_sign_bit (range[1]))
+	    {
+	      /* Avoid printing the upper bound if it's invalid.
+		 The overlap can likewise be "or more" in this case.  */
+	      warning_at (loc, OPT_Wrestrict,
+			  "%K%qD writing %E or more bytes may overlap %wu "
+			  "or more bytes at offset %s",
+			  exp, func, range[0], overlap, offstr);
+	    }
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD writing between %E and %E bytes may overlap "
+			"%wu or more bytes at offset %s",
+			exp, func, range[0], range[1], overlap,
+			offstr);
+	}
+
+      return;
+    }
+
+  if (must_overlap)
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	warning_at (loc, OPT_Wrestrict,
+		    (integer_onep (range[0])
+		     ? G_("%K%qD writing %E byte into a region "
+			  "of size %E overlaps %wu byte "
+			  "at offset %s")
+		     : (overlap == 1
+			? G_("%K%qD writing %E bytes into a region "
+			     "of size %E overlaps %wu byte "
+			     "at offset %s")
+			: G_("%K%qD writing %E bytes into a region "
+			     "of size %E overlaps %wu bytes "
+			     "at offset %s"))),
+		    exp, func, range[0], dstsize, overlap, offstr);
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.
+	     The overlap can likewise be "or more" in this case.  */
+	  warning_at (loc, OPT_Wrestrict,
+		      "%K%qD writing %E or more bytes into "
+		      "a region of size %E overlaps %wu or more bytes "
+		      "at offset %s",
+		      exp, func, range[0], dstsize, overlap, offstr);
+	}
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD writing between %E and %E bytes into "
+		    "a region of size %E overlaps %wu or more bytes "
+		    "at offset %s",
+		    exp, func, range[0], range[1], dstsize, overlap,
+		    offstr);
+    }
+  else
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	warning_at (loc, OPT_Wrestrict,
+		    (integer_onep (range[0])
+		     ? G_("%K%qD writing %E byte into a region "
+			  "of size %E may overlap %wu bytes "
+			  "at offset %s")
+		     : (overlap == 1
+			? G_("%K%qD writing %E bytes into a region "
+			     "of size %E may overlap %wu byte at "
+			     "offset %s")
+			: G_("%K%qD writing %E bytes into a region "
+			     "of size %E may overlap %wu bytes at "
+			     "offset %s"))),
+		    exp, func, range[0], dstsize, overlap, offstr);
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.
+	     The overlap can likewise be "or more" in this case.  */
+	  warning_at (loc, OPT_Wrestrict,
+		      "%K%qD writing %E or more bytes into "
+		      "a region of size %E may overlap %wu or more "
+		      "bytes at offset %s",
+		      exp, func, range[0], dstsize, overlap, offstr);
+	}
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD writing between %E and %E bytes into "
+		    "a region of size %E may overlap %wu or more bytes "
+		    "at offset %s",
+		    exp, func, range[0], range[1], dstsize, overlap,
+		    offstr);
+    }
+}
+
+/* Determine whather an overlap exists in a restricted copy call
+   expression EXP to a built-in function FUNC, with a destination DST
+   of DSTSIZE in size and source SRC, writing DSTWRITE bytes into DST
+   and reading MAXREAD bytes from SRC of SLEN bytes in length copy in
+   RANGE, and with OVERLAP bytes at OFFSET.  If so, diagnose it and
+   return true.  Otherwise do nothing and return false.  */
+
+static bool
+maybe_warn_for_overlap (tree exp, tree func, tree dst, tree src, tree dstwrite,
+			tree range[2], bool at_least_one, tree maxread,
+			tree slen, tree dstsize, bool relaxed)
+{
+  if (!dstwrite)
+    dstwrite = range[0];
+
+  /* If the range of byte counts is [0, N] and N is non-zero, use
+     1 as the conservative minimum to detect at least the most basic
+     cases of overflow.  */
+  if (dstwrite && TREE_CODE (dstwrite) != INTEGER_CST && range[0])
+    dstwrite = (integer_zerop (range[0]) && !integer_zerop (range[1])
+		? size_one_node : range[0]);
+
+  bool must_overlap = dstwrite && tree_fits_uhwi_p (dstwrite);
+
+  if (maxread)
+    must_overlap &= tree_fits_uhwi_p (maxread);
+  else if (slen)
+    {
+      /* When DSTWRITE is known, set the maximum number of bytes
+	 to access to DSTWRITE either when the length of the source
+	 sequence is unknown, or when DSTWRITE is less than the length.  */
+      maxread = (must_overlap
+		 && (at_least_one || tree_int_cst_lt (dstwrite, slen))
+		 ? dstwrite : slen);
+    }
+  else
+    maxread = dstwrite;
+
+  if (!dstwrite)
+    dstwrite = maxread;
+
+  /* The exact number bytes copied is known only if the sizes
+     haven't adjusted earlier on.  */
+  must_overlap &= !at_least_one || (dstwrite && dstwrite != slen);
+
+  /* Initialize and store the lower bound of a constant offset (in
+     bits), disregarding the offset for the destination.  */
+  ao_ref dstref, srcref;
+  unsigned HOST_WIDE_INT offrange[2];
+
+  ao_ref_init_from_ptr_and_size (&dstref, dst, dstwrite, offrange);
+  ao_ref_init_from_ptr_and_size (&srcref, src, maxread, offrange);
+
+  unsigned HOST_WIDE_INT offset;
+  if (unsigned HOST_WIDE_INT overlap
+      = refs_must_alias_p_1 (&dstref, &srcref, &offset))
+    {
+      /*B Convert the offset range from bits to bytes.  */
+      offrange[0] /= BITS_PER_UNIT;
+      offrange[1] /= BITS_PER_UNIT;
+
+      if (overlap <= offrange[1] - offrange[0])
+	must_overlap = false;
+
+      if (offrange[0] != offrange[1])
+	;
+      else if (offrange[0] != HOST_WIDE_INT_M1U)
+	offrange[0] = offrange[1] = offset;
+
+      if (must_overlap || !relaxed)
+	{
+	  if (!range[0])
+	    range[0] = range[1] = dstwrite;
+
+	  warn_for_overlap (exp, func, must_overlap, dstsize, range, overlap,
+			    offrange);
+	}
+
+      /* Return error when an overlap has been detected.  */
+      return must_overlap;
+    }
+
+  return false;
+}
+
 /* Try to verify that the sizes and lengths of the arguments to a string
    manipulation function given by EXP are within valid bounds and that
-   the operation does not lead to buffer overflow.  Arguments other than
-   EXP may be null.  When non-null, the arguments have the following
-   meaning:
-   SIZE is the user-supplied size argument to the function (such as in
-   memcpy(d, s, SIZE) or strncpy(d, s, SIZE).  It specifies the exact
-   number of bytes to write.
-   MAXLEN is the user-supplied bound on the length of the source sequence
+   the operation does not lead to buffer overflow or read past the end.
+   For copy operations the must not overlap also verify that the source
+   and destination do not overlap one another.
+   Arguments other than EXP may be null.  When non-null, the arguments
+   have the following meaning:
+   DST is the destination of a copy call or null otherwise.
+   SRC is the source of a copy call or null otherwise.
+   DSTWRITE is the number of bytes into the destination obtained from
+   the user-supplied size argument to the function (such as in memcpy(DST,
+   SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+   MAXREAD is the user-supplied bound on the length of the source sequence
    (such as in strncat(d, s, N).  It specifies the upper limit on the number
-   of bytes to write.
-   SRC is the source string (such as in strcpy(d, s)) when the expression
-   EXP is a string function call (as opposed to a memory call like memcpy).
-   As an exception, SRC can also be an integer denoting the precomputed
-   size of the source string or object (for functions like memcpy).
-   OBJSIZE is the size of the destination object specified by the last
+   of bytes to write.  If null it's taken to be the same as DSTWRITE.
+   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+   expression EXP is a string function call (as opposed to a memory call
+   like memcpy).  As an exception, SRCSTR can also be an integer denoting
+   the precomputed size of the source string or object (for functions like
+   memcpy).
+   DSTSIZE is the size of the destination object specified by the last
    argument to the _chk builtins, typically resulting from the expansion
-   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
-   OBJSIZE).
+   of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
+   DSTSIZE).
 
-   When SIZE is null LEN is checked to verify that it doesn't exceed
+   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
    If the call is successfully verified as safe from buffer overflow
-   the function returns true, otherwise false..  */
+   the function returns true, otherwise false.  */
 
 static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_sizes (int opt, tree exp, tree dst, tree src, tree dstwrite,
+	     tree maxread, tree srcstr, tree dstsize)
 {
   /* The size of the largest object is half the address space, or
      SSIZE_MAX.  (This is way too permissive.)  */
   tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
 
+  /* Either the length of the source string for string functions or
+     the size of the source object for raw memory functions.  */
   tree slen = NULL_TREE;
 
   tree range[2] = { NULL_TREE, NULL_TREE };
@@ -3077,28 +3329,28 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
      function like strcpy is not known and the only thing that is
      known is that it must be at least one (for the terminating nul).  */
   bool at_least_one = false;
-  if (src)
+  if (srcstr)
     {
-      /* SRC is normally a pointer to string but as a special case
+      /* SRCSTR is normally a pointer to string but as a special case
 	 it can be an integer denoting the length of a string.  */
-      if (POINTER_TYPE_P (TREE_TYPE (src)))
+      if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
 	{
 	  /* Try to determine the range of lengths the source string
 	     refers to.  If it can be determined and is less than
-	     the upper bound given by MAXLEN add one to it for
+	     the upper bound given by MAXREAD add one to it for
 	     the terminating nul.  Otherwise, set it to one for
-	     the same reason, or to MAXLEN as appropriate.  */
-	  get_range_strlen (src, range);
-	  if (range[0] && (!maxlen || TREE_CODE (maxlen) == INTEGER_CST))
+	     the same reason, or to MAXREAD as appropriate.  */
+	  get_range_strlen (srcstr, range);
+	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
 	    {
-	      if (maxlen && tree_int_cst_le (maxlen, range[0]))
-		range[0] = range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[0]))
+		range[0] = range[1] = maxread;
 	      else
 		range[0] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[0], size_one_node);
 
-	      if (maxlen && tree_int_cst_le (maxlen, range[1]))
-		range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[1]))
+		range[1] = maxread;
 	      else if (!integer_all_onesp (range[1]))
 		range[1] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[1], size_one_node);
@@ -3112,10 +3364,10 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	    }
 	}
       else
-	slen = src;
+	slen = srcstr;
     }
 
-  if (!size && !maxlen)
+  if (!dstwrite && !maxread)
     {
       /* When the only available piece of data is the object size
 	 there is nothing to do.  */
@@ -3123,20 +3375,18 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	return true;
 
       /* Otherwise, when the length of the source sequence is known
-	 (as with with strlen), set SIZE to it.  */
+	 (as with strlen), set DSTWRITE to it.  */
       if (!range[0])
-	size = slen;
+	dstwrite = slen;
     }
 
-  if (!objsize)
-    objsize = maxobjsize;
+  if (!dstsize)
+    dstsize = maxobjsize;
 
-  /* The SIZE is exact if it's non-null, constant, and in range of
-     unsigned HOST_WIDE_INT.  */
-  bool exactsize = size && tree_fits_uhwi_p (size);
+  if (dstwrite)
+    get_size_range (dstwrite, range);
 
-  if (size)
-    get_size_range (size, range);
+  tree func = get_callee_fndecl (exp);
 
   /* First check the number of bytes to be written against the maximum
      object size.  */
@@ -3149,30 +3399,34 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	warning_at (loc, opt,
 		    "%K%qD specified size %E "
 		    "exceeds maximum object size %E",
-		    exp, get_callee_fndecl (exp), range[0], maxobjsize);
+		    exp, func, range[0], maxobjsize);
 	  else
 	    warning_at (loc, opt,
 			"%K%qD specified size between %E and %E "
 			"exceeds maximum object size %E",
-			exp, get_callee_fndecl (exp),
+			exp, func,
 			range[0], range[1], maxobjsize);
       return false;
     }
 
+  /* The number of bytes to write is "exact" if DSTWRITE is non-null,
+     constant, and in range of unsigned HOST_WIDE_INT.  */
+  bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite);
+
   /* Next check the number of bytes to be written against the destination
      object size.  */
-  if (range[0] || !exactsize || integer_all_onesp (size))
+  if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
     {
       if (range[0]
-	  && ((tree_fits_uhwi_p (objsize)
-	       && tree_int_cst_lt (objsize, range[0]))
-	      || (tree_fits_uhwi_p (size)
-		  && tree_int_cst_lt (size, range[0]))))
+	  && ((tree_fits_uhwi_p (dstsize)
+	       && tree_int_cst_lt (dstsize, range[0]))
+	      || (tree_fits_uhwi_p (dstwrite)
+		  && tree_int_cst_lt (dstwrite, range[0]))))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
 
-	  if (size == slen && at_least_one)
+	  if (dstwrite == slen && at_least_one)
 	    {
 	      /* This is a call to strcpy with a destination of 0 size
 		 and a source of unknown length.  The call will write
@@ -3180,7 +3434,7 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else if (tree_int_cst_equal (range[0], range[1]))
 	    warning_at (loc, opt,
@@ -3189,21 +3443,21 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 			      "of size %E overflows the destination")
 			 : G_("%K%qD writing %E bytes into a region "
 			      "of size %E overflows the destination")),
-			exp, get_callee_fndecl (exp), range[0], objsize);
+			exp, func, range[0], dstsize);
 	  else if (tree_int_cst_sign_bit (range[1]))
 	    {
 	      /* Avoid printing the upper bound if it's invalid.  */
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else
 	    warning_at (loc, opt,
 			"%K%qD writing between %E and %E bytes into "
 			"a region of size %E overflows the destination",
-			exp, get_callee_fndecl (exp), range[0],	range[1],
-			objsize);
+			exp, func, range[0], range[1],
+			dstsize);
 
 	  /* Return error when an overflow has been detected.  */
 	  return false;
@@ -3213,11 +3467,15 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
   /* Check the maximum length of the source sequence against the size
      of the destination object if known, or against the maximum size
      of an object.  */
-  if (maxlen)
+  if (maxread)
     {
-      get_size_range (maxlen, range);
+      get_size_range (maxread, range);
+
+      /* Use the lower end for MAXREAD from now on.  */
+      if (range[0])
+	maxread = range[0];
 
-      if (range[0] && objsize && tree_fits_uhwi_p (objsize))
+      if (range[0] && dstsize && tree_fits_uhwi_p (dstsize))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
@@ -3231,40 +3489,41 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], maxobjsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], range[1], maxobjsize);
 
 	      return false;
 	    }
 
-	  if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
+	  if (dstsize != maxobjsize && tree_int_cst_lt (dstsize, range[0]))
 	    {
 	      if (tree_int_cst_equal (range[0], range[1]))
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], objsize);
+			    exp, func,
+			    range[0], dstsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], range[1], objsize);
+			    exp, func,
+			    range[0], range[1], dstsize);
 	      return false;
 	    }
 	}
     }
 
+  /* Check for reading past the end of SRC.  */
   if (slen
-      && slen == src
-      && size && range[0]
+      && slen == srcstr
+      && dstwrite && range[0]
       && tree_int_cst_lt (slen, range[0]))
     {
       location_t loc = tree_nonartificial_location (exp);
@@ -3274,23 +3533,36 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		    (tree_int_cst_equal (range[0], integer_one_node)
 		     ? G_("%K%qD reading %E byte from a region of size %E")
 		     : G_("%K%qD reading %E bytes from a region of size %E")),
-		    exp, get_callee_fndecl (exp), range[0], slen);
+		    exp, func, range[0], slen);
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
 	  warning_at (loc, opt,
 		      "%K%qD reading %E or more bytes from a region "
 		      "of size %E",
-		      exp, get_callee_fndecl (exp), range[0], slen);
+		      exp, func, range[0], slen);
 	}
       else
 	warning_at (loc, opt,
 		    "%K%qD reading between %E and %E bytes from a region "
 		    "of size %E",
-		    exp, get_callee_fndecl (exp), range[0], range[1], slen);
+		    exp, func, range[0], range[1], slen);
       return false;
     }
 
+  /* Check for overlapping copies.  Avoid warning if one has already
+     been issued (unlike -Wstringop-overflow, -Wrestrict is checked
+     in many places and TREE_NO_WARNING applies to it but not to
+     the former (there should be a bit for every kind of warning).  */
+  if (src && dst
+      && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+      && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+      && !TREE_NO_WARNING (exp)
+      && maybe_warn_for_overlap (exp, func, dst, src, dstwrite, range,
+				 at_least_one, maxread, slen, dstsize,
+				 srcstr == NULL))
+    return false;
+
   return true;
 }
 
@@ -3330,8 +3602,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size)
   tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
   tree dstsize = compute_objsize (dest, 0);
 
-  return check_sizes (OPT_Wstringop_overflow_, exp,
-		      size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+  return check_sizes (OPT_Wstringop_overflow_, exp, dest, src,
+		      size, /*maxread=*/NULL_TREE, srcsize, dstsize);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -3353,8 +3625,8 @@ expand_builtin_memchr (tree exp, rtx)
     {
       tree size = compute_objsize (arg1, 0);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, len, /*maxlen=*/NULL_TREE,
-		   size, /*objsize=*/NULL_TREE);
+		   exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		   /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
     }
 
   return NULL_RTX;
@@ -3652,7 +3924,8 @@ expand_builtin_strcat (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+	       exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+	       destsize);
 
   return NULL_RTX;
 }
@@ -3671,11 +3944,15 @@ expand_builtin_strcpy (tree exp, rtx target)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
 
-  if (warn_stringop_overflow)
+  if (warn_stringop_overflow || warn_restrict)
     {
-      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+      /* Use Object Size type-1 for -Wrestrict.  */
+      int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 2;
+
+      tree destsize = compute_objsize (dest, ost);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+		   exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		   src, destsize);
     }
 
   return expand_builtin_strcpy_args (dest, src, target);
@@ -3714,7 +3991,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
     {
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+		   exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+		   destsize);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -3799,7 +4077,7 @@ expand_builtin_stpncpy (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+	       exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 
   return NULL_RTX;
 }
@@ -3829,7 +4107,7 @@ check_strncat_sizes (tree exp, tree objsize)
 {
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
 
   /* Try to determine the range of lengths that the source expression
      refers to.  */
@@ -3853,32 +4131,33 @@ check_strncat_sizes (tree exp, tree objsize)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize)
-      && tree_int_cst_equal (objsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return false;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
   /* The number of bytes to write is LEN but check_sizes will also
      check SRCLEN if LEN's value isn't known.  */
   return check_sizes (OPT_Wstringop_overflow_,
-		      exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+		      exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+		      objsize);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -3896,7 +4175,7 @@ expand_builtin_strncat (tree exp, rtx)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
   /* The upper bound on the number of bytes to write.  */
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
   /* The length of the source sequence.  */
   tree slen = c_strlen (src, 1);
 
@@ -3919,32 +4198,31 @@ expand_builtin_strncat (tree exp, rtx)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize)
-      && tree_int_cst_equal (destsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return NULL_RTX;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
-     check SRCLEN if LEN's value isn't known.  */
+  /* The number of bytes to write is SRCLEN.  */
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+	       exp, dest, src, NULL_TREE, maxread, srclen, destsize);
 
   return NULL_RTX;
 }
@@ -3975,7 +4253,7 @@ expand_builtin_strncpy (tree exp, rtx target)
 	  /* The number of bytes to write is LEN but check_sizes will also
 	     check SLEN if LEN's value isn't known.  */
 	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+		       exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 	}
 
       /* We must be passed a constant len and src parameter.  */
@@ -4319,13 +4597,13 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
     {
       tree size = compute_objsize (arg1, 0);
       if (check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE))
+		       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		       /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
 	{
 	  size = compute_objsize (arg2, 0);
 	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE);
+		       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		       /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
 	}
     }
 
@@ -9708,8 +9986,6 @@ static rtx
 expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			   enum built_in_function fcode)
 {
-  tree dest, src, len, size;
-
   if (!validate_arglist (exp,
 			 POINTER_TYPE,
 			 fcode == BUILT_IN_MEMSET_CHK
@@ -9717,13 +9993,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
-  len = CALL_EXPR_ARG (exp, 2);
-  size = CALL_EXPR_ARG (exp, 3);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 3);
 
   bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
-			       exp, len, /*maxlen=*/NULL_TREE,
+			       exp, dest, src, len, /*maxread=*/NULL_TREE,
 			       /*str=*/NULL_TREE, size);
 
   if (!tree_fits_uhwi_p (size))
@@ -9833,7 +10109,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
   /* The maximum length of the source sequence in a bounded operation
      (such as __strncat_chk) or null if the operation isn't bounded
      (such as __strcat_chk).  */
-  tree maxlen = NULL_TREE;
+  tree maxread = NULL_TREE;
 
   switch (fcode)
     {
@@ -9854,27 +10130,27 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
     case BUILT_IN_STRNCAT_CHK:
       catstr = CALL_EXPR_ARG (exp, 0);
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STPNCPY_CHK:
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
-      maxlen = CALL_EXPR_ARG (exp, 1);
+      maxread = CALL_EXPR_ARG (exp, 1);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
     default:
       gcc_unreachable ();
     }
 
-  if (catstr && maxlen)
+  if (catstr && maxread)
     {
       /* Check __strncat_chk.  There is no way to determine the length
 	 of the string to which the source string is being appended so
@@ -9883,8 +10159,11 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       return;
     }
 
-  check_sizes (OPT_Wstringop_overflow_, exp,
-	       /*size=*/NULL_TREE, maxlen, srcstr, objsize);
+  /* The destination argument is the first one for all built-ins above.  */
+  tree dst = CALL_EXPR_ARG (exp, 0);
+
+  check_sizes (OPT_Wstringop_overflow_, exp, dst, srcstr,
+	       /*size=*/NULL_TREE, maxread, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9940,8 +10219,10 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 
   /* Add one for the terminating nul.  */
   len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
+
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
+	       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
+	       /*maxread=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index feb0904..c86a393 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5261,14 +5261,20 @@ check_function_restrict (const_tree fndecl, const_tree fntype,
 			 int nargs, tree *argarray)
 {
   int i;
-  tree parms;
+  tree parms = TYPE_ARG_TYPES (fntype);
 
   if (fndecl
-      && TREE_CODE (fndecl) == FUNCTION_DECL
-      && DECL_ARGUMENTS (fndecl))
-    parms = DECL_ARGUMENTS (fndecl);
-  else
-    parms = TYPE_ARG_TYPES (fntype);
+      && TREE_CODE (fndecl) == FUNCTION_DECL)
+    {
+      /* Skip checking built-ins here.  They are checked in more
+	 detail elsewhere.  */
+      if (DECL_BUILT_IN (fndecl)
+	  && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+	return;
+
+      if (DECL_ARGUMENTS (fndecl))
+	parms = DECL_ARGUMENTS (fndecl);
+    }
 
   for (i = 0; i < nargs; i++)
     TREE_VISITED (argarray[i]) = 0;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index e0ad3ab..01506fd 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1162,7 +1162,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall)
 Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier.
 
 Wrestrict
-C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++)
+C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
 Warn when an argument passed to a restrict-qualified parameter aliases with
 another argument.
 
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 3e1d24d..76b9b15 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2627,6 +2627,9 @@ expand_call_stmt (gcall *stmt)
   if (gimple_call_nothrow_p (stmt))
     TREE_NOTHROW (exp) = 1;
 
+  if (gimple_no_warning_p (stmt))
+    TREE_NO_WARNING (exp) = 1;
+
   CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt);
   CALL_EXPR_MUST_TAIL_CALL (exp) = gimple_call_must_tail_p (stmt);
   CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b9d071b..14205f5 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3852,6 +3852,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
 -Wparentheses  @gol
 -Wpointer-sign  @gol
 -Wreorder   @gol
+-Wrestrict   @gol
 -Wreturn-type  @gol
 -Wsequence-point  @gol
 -Wsign-compare @r{(only in C++)}  @gol
@@ -6535,11 +6536,25 @@ reduce the padding and so make the structure smaller.
 Warn if anything is declared more than once in the same scope, even in
 cases where multiple declaration is valid and changes nothing.
 
-@item -Wrestrict
+@item -Wno-restrict
 @opindex Wrestrict
 @opindex Wno-restrict
-Warn when an argument passed to a restrict-qualified parameter
-aliases with another argument.
+Warn when an object referenced by a @code{restrict}-qualified parameter
+(or, in C++, @code{__restrict}-qualified parameter) is aliased by another
+argument, or when copies between such objects overlap.  For example,
+the call to the @code{strcpy} unction below attempts to truncate the string
+by replacing its initial characters with the last four.  However, because
+he call writes the terminating NUL into @code{a[4]}, the copies overlap and
+the call is diagnosed.
+
+@smallexample
+struct foo
+@{
+  char a[] = "abcd1234";
+  strcpy (a, a + 4);
+@};
+@end smallexample
+The @option{-Wrestrict} is included in @option{-Wall}.
 
 @item -Wnested-externs @r{(C and Objective-C only)}
 @opindex Wnested-externs
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index d94dc9c..eb61383 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -57,6 +57,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "fold-const-call.h"
 #include "asan.h"
+#include "intl.h"
+#include "diagnostic-core.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -644,7 +646,13 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
   tree destvar, srcvar;
   location_t loc = gimple_location (stmt);
 
-  /* If the LEN parameter is zero, return DEST.  */
+  tree func = gimple_call_fndecl (stmt);
+  bool nowarn = gimple_no_warning_p (stmt);
+  bool check_overlap = (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+			&& DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+			&& !nowarn);
+
+    /* If the LEN parameter is zero, return DEST.  */
   if (integer_zerop (len))
     {
       gimple *repl;
@@ -666,6 +674,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
      DEST{,+LEN,+LEN-1}.  */
   if (operand_equal_p (src, dest, 0))
     {
+      if (check_overlap)
+	warning_at (loc, OPT_Wrestrict,
+		    "%qD source argument is the same as destination",
+		    func);
+
       unlink_stmt_vdef (stmt);
       if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
 	release_ssa_name (gimple_vdef (stmt));
@@ -715,6 +728,31 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	  unsigned ilen = tree_to_uhwi (len);
 	  if (pow2p_hwi (ilen))
 	    {
+	      if (check_overlap)
+		{
+		  ao_ref dstref, srcref;
+		  ao_ref_init_from_ptr_and_size (&dstref, dest, len);
+		  ao_ref_init_from_ptr_and_size (&srcref, src, len);
+
+		  if (refs_may_alias_p_1 (&dstref, &srcref, true))
+		    {
+		      /* Defer folding until both arguments are in an SSA
+			 form so that aliasing can be determined with greater
+			 accuracy.  */
+		      if (TREE_CODE (dest) != SSA_NAME
+			  || TREE_CODE (src) != SSA_NAME)
+			return false;
+
+		      /* Check for overlap and diagnose it.  If it's found
+			 avoid folding.  */
+		      if (detect_overlap (loc, stmt, dest, src, len, endp == 2))
+			{
+			  gimple_set_no_warning (stmt, true);
+			  return false;
+			}
+		    }
+		}
+
 	      tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
 	      if (type
 		  && TYPE_MODE (type) != BLKmode
@@ -1028,6 +1066,10 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	    }
 	}
 
+      /* Avoid folding the call if overlap is detected.  */
+      if (check_overlap && detect_overlap (loc, stmt, dest, src, len))
+	return false;
+
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
 	{
@@ -1392,7 +1434,7 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
 	    tree op3 = gimple_assign_rhs3 (def_stmt);
 	    return get_range_strlen (op2, length, visited, type, fuzzy, flexp)
 	      && get_range_strlen (op3, length, visited, type, fuzzy, flexp);
-          }
+	  }
         return false;
 
       case GIMPLE_PHI:
@@ -1484,12 +1526,19 @@ static bool
 gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
 			    tree dest, tree src)
 {
-  location_t loc = gimple_location (gsi_stmt (*gsi));
+  gimple *stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
   tree fn;
 
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -2272,6 +2321,15 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
      (resp. DEST+LEN for __mempcpy_chk).  */
   if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
     {
+      if (fcode != BUILT_IN_MEMMOVE && fcode != BUILT_IN_MEMMOVE_CHK)
+	{
+	  tree func = gimple_call_fndecl (stmt);
+
+	  warning_at (loc, OPT_Wrestrict,
+		      "%qD source argument is the same as destination",
+		      func);
+	}
+
       if (fcode != BUILT_IN_MEMPCPY_CHK)
 	{
 	  replace_call_with_value (gsi, dest);
@@ -2373,6 +2431,12 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 488f8c8..f2dcece 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -359,6 +359,7 @@ gimple_build_call_from_tree (tree t)
     gimple_call_set_arg (call, i, CALL_EXPR_ARG (t, i));
 
   gimple_set_block (call, TREE_BLOCK (t));
+  gimple_set_location (call, EXPR_LOCATION (t));
 
   /* Carry all the CALL_EXPR flags to the new GIMPLE_CALL.  */
   gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t));
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
new file mode 100644
index 0000000..64bc223
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -0,0 +1,589 @@
+/* PR 35503 - Warn about restricted pointers
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+size_t value (void);
+
+size_t range (size_t min, size_t max)
+{
+  size_t val = value ();
+  return val < min || max < val ? min : val;
+}
+
+#define R(min, max) range ((min), (max))
+
+
+void sink (void*, ...);
+
+/* Exercise memcpy with constant or known arguments.  */
+
+void test_memcpy_cst (void)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+    const void *s = 0;				\
+    memcpy ((dst), (src), (n));			\
+    sink (a, (dst), s);				\
+  } while (0)
+
+  T (a, a, 0);
+  T (a, s = a, 1);           /* { dg-warning "\\\[-Wrestrict" } */
+  T (a, a + 1, 1);
+  T (a, a + 1, 2);           /* { dg-warning "\\\[-Wrestrict" } */
+
+  {
+    char a[3][7];
+    sink (a);
+
+    void *d = a[0];
+    const void *s = a[1];
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0];
+    s = a[1];
+    memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&a);
+  }
+
+  {
+    struct {
+      char a[7];
+      char b[7];
+      char c[7];
+    } x;
+    sink (&x);
+
+    void *d = x.a;
+    const void *s = x.b;
+    memcpy (d, s, sizeof x.a);
+    sink (&x);
+
+    d = x.a;
+    s = x.a;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 4;
+    s = x.b;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 6;
+    s = x.b;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b;
+    memcpy (d, s, 1);             /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b + 1;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 1;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 2;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 3;
+    memcpy (d, s, sizeof x.b);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+  }
+
+  {
+#undef T
+#define T(dst, src, n) do {				\
+      char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+      memcpy ((dst), (src), (n));			\
+      sink (a);						\
+    } while (0)
+
+    /* Verify the offset of the overlap is the same regardless of whether
+       the destination is at lower or higher offset than the source.  */
+    T (a, a + 1, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 4 bytes at offset 1" } */
+    T (a, a + 2, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 3 bytes at offset 2" } */
+    T (a, a + 3, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 2 bytes at offset 3" } */
+
+    T (a + 1, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 8 overlaps 4 bytes at offset 1" } */
+    T (a + 2, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 7 overlaps 3 bytes at offset 2" } */
+    T (a + 3, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 6 overlaps 2 bytes at offset 3" } */
+  }
+}
+
+/* Exercise memcpy with destination or source offset or size in
+   a determinate range.  */
+
+void test_memcpy_range (void *d)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+    memcpy ((dst), (src), (n));			\
+    sink (a);					\
+  } while (0)
+
+  int r = R (2, 3);
+  T (a + r, a, 0);
+  T (a + r, a, 1);
+  T (a + r, a, 2);
+  T (a + r, a, 3);                /* { dg-warning "writing 3 bytes overlaps 1 byte at offset 2" } */
+
+  T (d + r, d, 0);
+  T (d + r, d, 1);
+  T (d + r, d, 2);
+  T (d + r, d, 3);                /* { dg-warning "writing 3 bytes overlaps 1 byte at offset 2" } */
+
+  /* Because the size is constant and a power of 2 the following is
+     folded too early to detect the overlap.  */
+  T (d + r, d, 4);                /* { dg-warning "writing 4 bytes overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+  T (d + r, d, 5);                /* { dg-warning "writing 5 bytes overlaps 3 bytes at offset 2" } */
+
+  T (a, a + 1, R (0, 1));
+  T (a, a + 1, R (0, 2));
+  T (a, a + 1, R (1, 2));
+  T (a, a + 1, R (2, 3));         /* { dg-warning "writing between 2 and 3 bytes into a region of size 9 overlaps 1 or more bytes at offset 1" } */
+  T (a, a + 1, R (2, -2));        /* { dg-warning "writing 2 or more bytes into a region of size 9 overlaps 1 or more bytes at offset 1" } */
+  T (a, a + 2, R (2, 3));
+  T (a, a + 2, R (3, 4));         /* { dg-warning "writing between 3 and 4 bytes into a region of size 9 overlaps 1 or more bytes at offset 2" } */
+  T (a, a + 3, R (3, 4));
+  T (a, a + 3, R (4, 5));         /* { dg-warning "writing between 4 and 5 bytes into a region of size 9 overlaps 1 or more bytes at offset 3" } */
+  T (a, a + 3, R (5, 6));         /* { dg-warning "writing between 5 and 6 bytes into a region of size 9 overlaps 2 or more bytes at offset 3" } */
+
+  T (a + 1, a, R (0, 1));
+  T (a + 1, a, R (0, 2));
+  T (a + 1, a, R (1, 2));
+  T (a + 1, a, R (2, 3));         /* { dg-warning "writing between 2 and 3 bytes into a region of size 8 overlaps 1 or more bytes at offset 1" } */
+  T (a + 1, a, R (2, -2));        /* { dg-warning "writing 2 or more bytes into a region of size 8 overlaps 1 or more bytes at offset 1" } */
+  T (a + 2, a, R (2, 3));
+  T (a + 2, a, R (3, 4));         /* { dg-warning "writing between 3 and 4 bytes into a region of size 7 overlaps 1 or more bytes at offset 2" } */
+  T (a + 3, a, R (3, 4));
+  T (a + 3, a, R (4, 5));         /* { dg-warning "writing between 4 and 5 bytes into a region of size 6 overlaps 1 or more bytes at offset 3" } */
+  T (a + 3, a, R (5, 6));         /* { dg-warning "writing between 5 and 6 bytes into a region of size 6 overlaps 2 or more bytes at offset 3" } */
+
+  /* Verify offset and size both in some range.  The memcpy checking
+     is less strict than that of string functions like strncpy and
+     doesn't trigger unless the overlap is certain.  The following
+     overlaps for (r == 3 && n > 3) but not, for example, for
+     (r == 4 && n == 4), and so it's not diagnosed.  */
+  r = R (3, 5);
+  T (a, a + r, R (4, 6));
+  /* Ditto for objects of unknown sizes.  */
+  T (d, d + r, R (4, 6));
+}
+
+/* Exercise memcpy with destination and source of unknown size.  */
+
+void test_memcpy_var (char *d, const char *s)
+{
+  int n = value ();
+
+  memcpy (d, d, 0);
+  sink (d);
+
+  memcpy (d, d, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  memcpy (d, &d[0], n);           /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  memcpy (&d[0], d,  n);          /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  /* The following overlaps if n is greater than 1.  */
+  s = d + 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  /* The following only overlaps if strlen (s + i) >= i so it's not
+     diagnosed.  */
+  s = d + n;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 2;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 3;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 4;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  s = d + 5;
+  n = 7;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+
+  n = range (0, 1);
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+/* Exercise the absence of warnings with memmove.  */
+
+void test_memmove (void)
+{
+  {
+    char d[7];
+    sink (d);
+
+    const void *s = d;
+    memmove (d, s, 7);
+    sink (d);
+
+    s = d + 1;
+    memmove (d, s, 6);
+    sink (d);
+
+    s = d + 2;
+    memmove (d + 1, s, 5);
+    sink (d);
+  }
+}
+
+/* Exercise strcat with constant or known arguments.  */
+
+void test_strcat_cst (const char *s)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[9] = init;				\
+    strcat ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2);            /* { dg-warning "\\\[-Wrestrict" } */
+  /* The nul copied from a[3] to a[3] overwrites itself so this is
+     diagnosed.  */
+  T ("123", a, a + 3);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 4);
+  T ("123", a, a + 5);
+  T ("123", a, a + 6);
+  T ("123", a, a + 7);
+  T ("123", a, a + 8);
+
+  T ("123", a + 1, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 2, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 3, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 4, a);            /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+  T ("123", a + 5, a);            /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T ("123",      a, "123");
+  T ("123",      a, s);
+  T ("12345678", a, s);
+}
+
+/* Exercise strcat with destination and source of unknown length.  */
+
+void test_strcat_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    strcat ((dst), (src));			\
+    sink ((dst));				\
+  } while (0)
+
+  T (d, d);                       /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 1);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 2);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 3);
+
+  int n = value ();
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T (d, "123");
+  T (d + 1, "1234");
+  T (d + n, "12345");
+  T (d, s);
+  T (d + 1, s);
+  T (d + n, s);
+
+  /* This one isn't so obvious and might be worth diagnosing with "may
+     overlap," even more so than the analogous strcpy case.  */
+  T (d, d + n);
+}
+
+/* Exercise strcpy with constant or known arguments.  */
+
+void test_strcpy_cst (void)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[8] = init;				\
+    strcpy ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2);
+  T ("123", a, a + 3);
+
+  /* The terminating nul written to d[2] overwrites s[0].  */
+  T ("1234", a, a + 2);           /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("12345", a, a + 2);          /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* It's not 100% clear if this should trigger a warning.  The non-const
+     string case doesn't.  */
+  T ("123", a, a + value ());
+
+  /* This happens to be safe in GCC but it's still wrong.  */
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("123", a + 1, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 2, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 3, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 4, a);
+}
+
+/* Exercise strcpy with constant or known arguments offset by a range.
+   The tests verify the use of the lower bound of the range which is
+   more restrictive than using the upper bound for positive values.  */
+
+void test_strcpy_range (void)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[8] = init;				\
+    strcpy ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  /* The range needs to be an int variable and not a size_t here,
+     otherwise the range info in the strlen pass is not good enough
+     to detect these cases.  */
+  int r = R (3, 4);
+  T ("12",  a + r, a);
+  T ("123", a + r, a);            /* { dg-warning "writing 4 bytes overlaps 1 byte at offset 3" } */
+
+  T ("1234", a + r, a);           /* { dg-warning "writing 5 bytes overlaps 2 bytes at offset 3" } */
+
+  T ("12",     a, a + r);
+  T ("123",    a, a + r);
+  T ("1234",   a, a + r);
+  T ("12345",  a, a + r);
+
+  /* The final NUL overlaps the '4' at a[3].  */
+  T ("123456", a, a + r);         /* { dg-warning "writing between 3 and 4 bytes may overlap 1 byte at offset 3" } */
+  T ("1234567", a, a + r);        /* { dg-warning "writing between 4 and 5 bytes overlaps 2 bytes at offset 3" } */
+
+  r = R (2, 5);
+  T ("1234", a + r, a);           /* { dg-warning "writing 5 bytes overlaps 3 bytes at offset 2" } */
+
+
+}
+
+
+/* Exercise strcpy with destination and source of unknown length.  */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    strcpy ((dst), (src));			\
+    sink (dst);					\
+  } while (0)
+
+  T (d, s);
+
+  T (d, &d[0]);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (&d[0], d);                   /* { dg-warning "\\\[-Wrestrict" } */
+
+  s = d;
+  T (d, s);                       /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* The following overlaps if *s is not nul.  It arguably should be
+     diagnosed.  */
+  T (d, d + 1);
+
+  /* The following overlaps if strlen (d) is greater than 1.  Like
+     the above, it possibly should be diagnosed too.  */
+  int r = R (2, 3);
+  T (d, d + r);
+
+  /* The following overlaps only if strlen (s + n) >= n so it's not
+     diagnosed.  */
+  s = d + value ();
+  T (d, s);
+}
+
+/* Exercise strncpy with constant or known arguments.  */
+
+void test_strncpy_cst (void)
+{
+#undef T
+#define T(init, dst, src, size) do {		\
+    char a[7] = init;				\
+    strncpy (dst, src, size);			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a, 0);
+  T ("123", a, a, 1);             /* { dg-warning "\\\[-Wrestrict" } */
+  /* The following overlaps except in the unlikely case that value ()
+     is zero, so it's diagnosed.  */
+  T ("123", a, a, value ());      /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1, 1);
+  T ("123", a, a + 1, 2);         /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1, 3);         /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2, 1);
+  T ("123", a, a + 2, 2);
+  /* The third written byte (nul) overwrites a[2].  */
+  T ("123", a, a + 2, 3);         /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("1234", a, a + 2, 1);
+  T ("1234", a, a + 2, 2);
+  /* The terminating nul written to a[2] overwrites s[0].  */
+  T ("1234", a, a + 2, 3);        /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("12345", a, a + 2, 1);
+  T ("12345", a, a + 2, 2);
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("12345", a, a + 2, 4);       /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+
+/* Exercise strncpy with one or more arguments in a determinate range.  */
+
+void test_strncpy_range (char *d)
+{
+#undef T
+#define T(init, dst, src, size) do {		\
+    char a[9] = init;				\
+    strncpy ((dst), (src), (size));		\
+    sink (a);					\
+  } while (0)
+
+  /* Verify offset and size both in some range.  The strncpy checking
+     is more strict than that of memcpy and triggers even when the
+     overlap is possible but not inevitable.  The following overlaps
+     for (r == 3 && n > 3) but not, for example, for (r == 4 && n == 4),
+     but it's still diagnosed.  */
+  int r = R (3, 5);
+  T ("12345678", a, a + r, R (4, 6));   /* { dg-warning "writing between 4 and 6 bytes into a region of size 9 may overlap 1 or more bytes at offset 3 - 5" } */
+
+  /* Ditto for objects of unknown sizes.  Verify also that since it's
+     unknown the size isn't printed.  */
+  T ("12345678", d, d + r, R (4, 6));  /* { dg-warning "writing between 4 and 6 bytes may overlap 1 or more bytes at offset 3 - 5" } */
+
+  T ("12345678", a, a + r, R (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 9 overlaps 3 or more bytes at offset 3 - 5" } */
+}
+
+
+/* Exercise strncpy with destination and source of unknown length.  */
+
+void test_strncpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src, size) do {			\
+    strncpy (dst, src, size);			\
+    sink ((dst));				\
+  } while (0)
+
+  int n = value ();
+
+  T (d, s, 1);
+  T (d, s, n);
+
+  T (d, d, 1);                    /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d, n);                    /* { dg-warning "\\\[-Wrestrict" } */
+
+  T (d,     d + 1, 1);
+  T (d,     d + 1, 2);            /* { dg-warning "\\\[-Wrestrict" } */
+  T (d + 1, d,     1);
+  T (d + 1, d,     2);            /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+struct MemberArrays
+{
+  char a[8];
+  char b[8];
+  char c[9];
+};
+
+void test_strncpy_strcpy_var (struct MemberArrays *ar, const char *s)
+{
+  /* The following is safe and should not trigger a warning.  */
+  strncpy (ar->b, s, sizeof ar->b - 1);
+  ar->b[sizeof ar->b - 1] = '\0';
+  strcpy (ar->a, ar->b);
+  sink (ar);
+
+  /* The following is not as safe (it might overflow ar->a) but there
+     is no overlap so it also shouldn't trigger -Wrestrict.  */
+  strncpy (ar->c, s, sizeof ar->c - 1);
+  ar->c[sizeof ar->c - 1] = '\0';
+  strcpy (ar->a, ar->c);
+  sink (ar);
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c b/gcc/testsuite/gcc.dg/Walloca-1.c
index ad39373..85e9160 100644
--- a/gcc/testsuite/gcc.dg/Walloca-1.c
+++ b/gcc/testsuite/gcc.dg/Walloca-1.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* { dg-require-effective-target alloca } */
-/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+/* { dg-options "-Walloca-larger-than=2000 -O2 -ftrack-macro-expansion=0" } */
 
 #define alloca __builtin_alloca
 
diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c
index c0e7463..908d5a6 100644
--- a/gcc/testsuite/gcc.dg/pr69172.c
+++ b/gcc/testsuite/gcc.dg/pr69172.c
@@ -1,4 +1,5 @@
-/* PR tree-optimization/69172 */
+/* PR tree-optimization/69172 - ICE in make_ssa_name_fn,
+   at tree-ssanames.c:266 */
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
 
@@ -43,3 +44,7 @@ f6 (int x)
 {
   return __builtin___mempcpy_chk (&a, &a, x, 0);
 }
+
+/* The calls above violate strict aliasing.  Eliminate the -Wrestrict
+   warnings they trigger.
+  { dg-prune-output "\\\[-Wrestrict]" } */
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 74ee2b0..f47f1c1 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -38,6 +38,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-dfa.h"
 #include "ipa-reference.h"
 #include "varasm.h"
+#include "intl.h"
+#include "diagnostic-core.h"
 
 /* Broad overview of how alias analysis on gimple works:
 
@@ -149,12 +151,13 @@ ptr_deref_may_alias_global_p (tree ptr)
   return pt_solution_includes_global (&pi->pt);
 }
 
-/* Return true if dereferencing PTR may alias DECL.
+/* Return true if dereferencing PTR may or alias DECL.  When MUST_ALIAS
+   is true, return true only if PTR definitely aliases DECL.
    The caller is responsible for applying TBAA to see if PTR
    may access DECL at all.  */
 
 static bool
-ptr_deref_may_alias_decl_p (tree ptr, tree decl)
+ptr_deref_alias_decl_p (tree ptr, tree decl, bool must_alias = false)
 {
   struct ptr_info_def *pi;
 
@@ -170,7 +173,7 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
       || (!VAR_P (decl)
 	  && TREE_CODE (decl) != PARM_DECL
 	  && TREE_CODE (decl) != RESULT_DECL))
-    return true;
+    return !must_alias;
 
   /* Disregard pointer offsetting.  */
   if (TREE_CODE (ptr) == POINTER_PLUS_EXPR)
@@ -180,7 +183,7 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
 	  ptr = TREE_OPERAND (ptr, 0);
 	}
       while (TREE_CODE (ptr) == POINTER_PLUS_EXPR);
-      return ptr_deref_may_alias_decl_p (ptr, decl);
+      return ptr_deref_alias_decl_p (ptr, decl, must_alias);
     }
 
   /* ADDR_EXPR pointers either just offset another pointer or directly
@@ -215,12 +218,13 @@ ptr_deref_may_alias_decl_p (tree ptr, tree decl)
   return pt_solution_includes (&pi->pt, decl);
 }
 
-/* Return true if dereferenced PTR1 and PTR2 may alias.
+/* Return true if dereferenced PTR1 and PTR2 may alias.  When MUST_ALIAS
+   is true, return true only if PTR1 and PTR2 definitely alias.
    The caller is responsible for applying TBAA to see if accesses
    through PTR1 and PTR2 may conflict at all.  */
 
-bool
-ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
+static bool
+ptr_derefs_alias_p (tree ptr1, tree ptr2, bool must_alias)
 {
   struct ptr_info_def *pi1, *pi2;
 
@@ -237,7 +241,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
 	  ptr1 = TREE_OPERAND (ptr1, 0);
 	}
       while (TREE_CODE (ptr1) == POINTER_PLUS_EXPR);
-      return ptr_derefs_may_alias_p (ptr1, ptr2);
+      return ptr_derefs_alias_p (ptr1, ptr2, must_alias);
     }
   if (TREE_CODE (ptr2) == POINTER_PLUS_EXPR)
     {
@@ -246,7 +250,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
 	  ptr2 = TREE_OPERAND (ptr2, 0);
 	}
       while (TREE_CODE (ptr2) == POINTER_PLUS_EXPR);
-      return ptr_derefs_may_alias_p (ptr1, ptr2);
+      return ptr_derefs_alias_p (ptr1, ptr2, must_alias);
     }
 
   /* ADDR_EXPR pointers either just offset another pointer or directly
@@ -257,12 +261,12 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
       if (base
 	  && (TREE_CODE (base) == MEM_REF
 	      || TREE_CODE (base) == TARGET_MEM_REF))
-	return ptr_derefs_may_alias_p (TREE_OPERAND (base, 0), ptr2);
+	return ptr_derefs_alias_p (TREE_OPERAND (base, 0), ptr2, must_alias);
       else if (base
 	       && DECL_P (base))
-	return ptr_deref_may_alias_decl_p (ptr2, base);
+	return ptr_deref_alias_decl_p (ptr2, base, must_alias);
       else
-	return true;
+	return !must_alias;
     }
   if (TREE_CODE (ptr2) == ADDR_EXPR)
     {
@@ -270,12 +274,12 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
       if (base
 	  && (TREE_CODE (base) == MEM_REF
 	      || TREE_CODE (base) == TARGET_MEM_REF))
-	return ptr_derefs_may_alias_p (ptr1, TREE_OPERAND (base, 0));
+	return ptr_derefs_alias_p (ptr1, TREE_OPERAND (base, 0), must_alias);
       else if (base
 	       && DECL_P (base))
-	return ptr_deref_may_alias_decl_p (ptr1, base);
+	return ptr_deref_alias_decl_p (ptr1, base, must_alias);
       else
-	return true;
+	return !must_alias;
     }
 
   /* From here we require SSA name pointers.  Anything else aliases.  */
@@ -283,7 +287,7 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
       || TREE_CODE (ptr2) != SSA_NAME
       || !POINTER_TYPE_P (TREE_TYPE (ptr1))
       || !POINTER_TYPE_P (TREE_TYPE (ptr2)))
-    return true;
+    return !must_alias;
 
   /* We may end up with two empty points-to solutions for two same pointers.
      In this case we still want to say both pointers alias, so shortcut
@@ -296,11 +300,21 @@ ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
   pi1 = SSA_NAME_PTR_INFO (ptr1);
   pi2 = SSA_NAME_PTR_INFO (ptr2);
   if (!pi1 || !pi2)
-    return true;
+    return !must_alias;
 
   /* ???  This does not use TBAA to prune decls from the intersection
      that not both pointers may access.  */
-  return pt_solutions_intersect (&pi1->pt, &pi2->pt);
+  return pt_solutions_intersect (&pi1->pt, &pi2->pt, must_alias);
+}
+
+/* Return true if dereferenced PTR1 and PTR2 may alias.
+   The caller is responsible for applying TBAA to see if accesses
+   through PTR1 and PTR2 may conflict at all.  */
+
+bool
+ptr_derefs_may_alias_p (tree ptr1, tree ptr2)
+{
+  return ptr_derefs_alias_p (ptr1, ptr2, false);
 }
 
 /* Return true if dereferencing PTR may alias *REF.
@@ -316,7 +330,7 @@ ptr_deref_may_alias_ref_p_1 (tree ptr, ao_ref *ref)
       || TREE_CODE (base) == TARGET_MEM_REF)
     return ptr_derefs_may_alias_p (ptr, TREE_OPERAND (base, 0));
   else if (DECL_P (base))
-    return ptr_deref_may_alias_decl_p (ptr, base);
+    return ptr_deref_alias_decl_p (ptr, base, false);
 
   return true;
 }
@@ -672,15 +686,25 @@ ao_ref_alias_set (ao_ref *ref)
 }
 
 /* Init an alias-oracle reference representation from a gimple pointer
-   PTR and a gimple size SIZE in bytes.  If SIZE is NULL_TREE then the
-   size is assumed to be unknown.  The access is assumed to be only
-   to or after of the pointer target, not before it.  */
+   PTR and a gimple size SIZE in bytes.  The reference stores sizes and
+   offsets in bits.  If SIZE is NULL_TREE then the size is assumed to
+   be unknown.  The access is assumed to be only to or after of
+   the pointer target, not before it.  If OFFRNG is not null, make use
+   of range information associated with non-constant offsets to determine
+   the bounds on the REF->OFFSET member and set the OFFRNG[] array to its
+   range, also in bits.  */
 
 void
-ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size)
+ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size,
+			       unsigned HOST_WIDE_INT offrng[2] /* = NULL */)
 {
   HOST_WIDE_INT t, size_hwi, extra_offset = 0;
+
+  if (offrng)
+    offrng[0] = offrng[1] = 0;
+
   ref->ref = NULL_TREE;
+
   if (TREE_CODE (ptr) == SSA_NAME)
     {
       gimple *stmt = SSA_NAME_DEF_STMT (ptr);
@@ -688,12 +712,30 @@ ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size)
 	  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
 	ptr = gimple_assign_rhs1 (stmt);
       else if (is_gimple_assign (stmt)
-	       && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
-	       && TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST)
+	       && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
 	{
-	  ptr = gimple_assign_rhs1 (stmt);
-	  extra_offset = BITS_PER_UNIT
-			 * int_cst_value (gimple_assign_rhs2 (stmt));
+	  tree offset = gimple_assign_rhs2 (stmt);
+	  if (TREE_CODE (offset) == INTEGER_CST)
+	    {
+	      ptr = gimple_assign_rhs1 (stmt);
+	      extra_offset = BITS_PER_UNIT * int_cst_value (offset);
+	    }
+	  else if (offrng && TREE_CODE (offset) == SSA_NAME)
+	    {
+	      wide_int min, max;
+	      value_range_type rng = get_range_info (offset, &min, &max);
+	      if (rng == VR_RANGE && wi::fits_uhwi_p (min))
+		{
+		  ptr = gimple_assign_rhs1 (stmt);
+		  offrng[0] = BITS_PER_UNIT * min.to_uhwi ();
+		  offrng[1] = BITS_PER_UNIT * max.to_uhwi ();
+
+		  extra_offset = offrng[0];
+		}
+	      else
+		/* Offset range is indeterminate.  */
+		offrng[0] = offrng[1] = HOST_WIDE_INT_M1U;
+	    }
 	}
     }
 
@@ -773,9 +815,11 @@ same_type_for_tbaa (tree type1, tree type2)
    on an indirect reference may alias.  REF2 is the only one that can
    be a decl in which case REF2_IS_DECL is true.
    REF1_ALIAS_SET, BASE1_ALIAS_SET, REF2_ALIAS_SET and BASE2_ALIAS_SET
-   are the respective alias sets.  */
+   are the respective alias sets.
+   The returned value is the size of the overlap if it can be determined,
+   otherwise it's HOST_WIDE_INT_M1U.  */
 
-static bool
+static unsigned HOST_WIDE_INT
 aliasing_component_refs_p (tree ref1,
 			   alias_set_type ref1_alias_set,
 			   alias_set_type base1_alias_set,
@@ -784,7 +828,7 @@ aliasing_component_refs_p (tree ref1,
 			   alias_set_type ref2_alias_set,
 			   alias_set_type base2_alias_set,
 			   HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
-			   bool ref2_is_decl)
+			   bool ref2_is_decl, unsigned HOST_WIDE_INT *aloff)
 {
   /* If one reference is a component references through pointers try to find a
      common base and apply offset based disambiguation.  This handles
@@ -816,7 +860,7 @@ aliasing_component_refs_p (tree ref1,
   same_p = same_type_for_tbaa (TREE_TYPE (*refp), type1);
   /* If we couldn't compare types we have to bail out.  */
   if (same_p == -1)
-    return true;
+    return HOST_WIDE_INT_M1U;
   else if (same_p == 1)
     {
       HOST_WIDE_INT offadj, sztmp, msztmp;
@@ -825,7 +869,7 @@ aliasing_component_refs_p (tree ref1,
       offset2 -= offadj;
       get_ref_base_and_extent (base1, &offadj, &sztmp, &msztmp, &reverse);
       offset1 -= offadj;
-      return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+      return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
     }
   /* If we didn't find a common base, try the other way around.  */
   refp = &ref1;
@@ -844,7 +888,7 @@ aliasing_component_refs_p (tree ref1,
       offset1 -= offadj;
       get_ref_base_and_extent (base2, &offadj, &sztmp, &msztmp, &reverse);
       offset2 -= offadj;
-      return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+      return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
     }
 
   /* If we have two type access paths B1.path1 and B2.path2 they may
@@ -855,12 +899,12 @@ aliasing_component_refs_p (tree ref1,
      tail of path2.  */
   if (base1_alias_set == ref2_alias_set
       || alias_set_subset_of (base1_alias_set, ref2_alias_set))
-    return true;
+    return HOST_WIDE_INT_M1U;
   /* If this is ptr vs. decl then we know there is no ptr ... decl path.  */
   if (!ref2_is_decl)
     return (base2_alias_set == ref1_alias_set
 	    || alias_set_subset_of (base2_alias_set, ref1_alias_set));
-  return false;
+  return 0;
 }
 
 /* Return true if we can determine that component references REF1 and REF2,
@@ -1088,21 +1132,25 @@ nonoverlapping_component_refs_p (const_tree x, const_tree y)
    [OFFSET2, OFFSET2 + MAX_SIZE2) may alias.  REF1 and REF2
    if non-NULL are the complete memory reference trees.  */
 
-static bool
+static unsigned HOST_WIDE_INT
 decl_refs_may_alias_p (tree ref1, tree base1,
 		       HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1,
 		       tree ref2, tree base2,
-		       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2)
+		       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
+		       unsigned HOST_WIDE_INT *aloff)
 {
   gcc_checking_assert (DECL_P (base1) && DECL_P (base2));
 
   /* If both references are based on different variables, they cannot alias.  */
   if (compare_base_decls (base1, base2) == 0)
-    return false;
+    return 0;
 
   /* If both references are based on the same variable, they cannot alias if
      the accesses do not overlap.  */
-  if (!ranges_overlap_p (offset1, max_size1, offset2, max_size2))
+  HOST_WIDE_INT overlap
+    = range_overlap (offset1, max_size1, offset2, max_size2, aloff);
+
+  if (!overlap)
     return false;
 
   /* For components with variable position, the above test isn't sufficient,
@@ -1110,9 +1158,9 @@ decl_refs_may_alias_p (tree ref1, tree base1,
   if (ref1 && ref2
       && handled_component_p (ref1) && handled_component_p (ref2)
       && nonoverlapping_component_refs_of_decl_p (ref1, ref2))
-    return false;
+    return 0;
 
-  return true;     
+  return overlap;
 }
 
 /* Return true if an indirect reference based on *PTR1 constrained
@@ -1122,16 +1170,17 @@ decl_refs_may_alias_p (tree ref1, tree base1,
    in which case they are computed on-demand.  REF1 and REF2
    if non-NULL are the complete memory reference trees.  */
 
-static bool
-indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
-			       HOST_WIDE_INT offset1,
-			       HOST_WIDE_INT max_size1 ATTRIBUTE_UNUSED,
-			       alias_set_type ref1_alias_set,
-			       alias_set_type base1_alias_set,
-			       tree ref2 ATTRIBUTE_UNUSED, tree base2,
-			       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
-			       alias_set_type ref2_alias_set,
-			       alias_set_type base2_alias_set, bool tbaa_p)
+static unsigned HOST_WIDE_INT
+indirect_ref_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
+			   HOST_WIDE_INT offset1,
+			   HOST_WIDE_INT max_size1 ATTRIBUTE_UNUSED,
+			   alias_set_type ref1_alias_set,
+			   alias_set_type base1_alias_set,
+			   tree ref2 ATTRIBUTE_UNUSED, tree base2,
+			   HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
+			   alias_set_type ref2_alias_set,
+			   alias_set_type base2_alias_set, bool tbaa_p,
+			   unsigned HOST_WIDE_INT *aloff)
 {
   tree ptr1;
   tree ptrtype1, dbase2;
@@ -1161,20 +1210,20 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
      so do not apply this optimization for TARGET_MEM_REFs.  */
   if (TREE_CODE (base1) != TARGET_MEM_REF
       && !ranges_overlap_p (MAX (0, offset1p), -1, offset2p, max_size2))
-    return false;
+    return 0;
   /* They also cannot alias if the pointer may not point to the decl.  */
-  if (!ptr_deref_may_alias_decl_p (ptr1, base2))
-    return false;
+  if (!ptr_deref_alias_decl_p (ptr1, base2, aloff != 0))
+    return 0;
 
   /* Disambiguations that rely on strict aliasing rules follow.  */
   if (!flag_strict_aliasing || !tbaa_p)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   ptrtype1 = TREE_TYPE (TREE_OPERAND (base1, 1));
 
   /* If the alias set for a pointer access is zero all bets are off.  */
   if (base1_alias_set == 0)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* When we are trying to disambiguate an access with a pointer dereference
      as base versus one with a decl as base we can use both the size
@@ -1190,7 +1239,7 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
      !alias_set_subset_of (base1_alias_set, base2_alias_set) instead.  */
   if (base1_alias_set != base2_alias_set
       && !alias_sets_conflict_p (base1_alias_set, base2_alias_set))
-    return false;
+    return 0;
   /* If the size of the access relevant for TBAA through the pointer
      is bigger than the size of the decl we can't possibly access the
      decl via that pointer.  */
@@ -1203,10 +1252,10 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
       && TREE_CODE (TREE_TYPE (ptrtype1)) != UNION_TYPE
       && TREE_CODE (TREE_TYPE (ptrtype1)) != QUAL_UNION_TYPE
       && tree_int_cst_lt (DECL_SIZE (base2), TYPE_SIZE (TREE_TYPE (ptrtype1))))
-    return false;
+    return 0;
 
   if (!ref2)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* If the decl is accessed via a MEM_REF, reconstruct the base
      we can use for TBAA and an appropriately adjusted offset.  */
@@ -1229,7 +1278,7 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
   /* If either reference is view-converted, give up now.  */
   if (same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (ptrtype1)) != 1
       || same_type_for_tbaa (TREE_TYPE (dbase2), TREE_TYPE (base2)) != 1)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* If both references are through the same type, they do not alias
      if the accesses do not overlap.  This does extra disambiguation
@@ -1241,11 +1290,11 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
   if ((TREE_CODE (base1) != TARGET_MEM_REF
        || (!TMR_INDEX (base1) && !TMR_INDEX2 (base1)))
       && same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (dbase2)) == 1)
-    return ranges_overlap_p (doffset1, max_size1, doffset2, max_size2);
+    return range_overlap (doffset1, max_size1, doffset2, max_size2, aloff);
 
   if (ref1 && ref2
       && nonoverlapping_component_refs_p (ref1, ref2))
-    return false;
+    return 0;
 
   /* Do access-path based disambiguation.  */
   if (ref1 && ref2
@@ -1255,27 +1304,32 @@ indirect_ref_may_alias_decl_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
 				      offset1, max_size1,
 				      ref2,
 				      ref2_alias_set, base2_alias_set,
-				      offset2, max_size2, true);
+				      offset2, max_size2, true, aloff);
 
-  return true;
+  return aloff ? 0 : HOST_WIDE_INT_M1U;
 }
 
-/* Return true if two indirect references based on *PTR1
-   and *PTR2 constrained to [OFFSET1, OFFSET1 + MAX_SIZE1) and
-   [OFFSET2, OFFSET2 + MAX_SIZE2) may alias.  *PTR1 and *PTR2 have
-   the alias sets BASE1_ALIAS_SET and BASE2_ALIAS_SET which can be -1
-   in which case they are computed on-demand.  REF1 and REF2
-   if non-NULL are the complete memory reference trees. */
-
-static bool
-indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
-			   HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1,
-			   alias_set_type ref1_alias_set,
-			   alias_set_type base1_alias_set,
-			   tree ref2 ATTRIBUTE_UNUSED, tree base2,
-			   HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
-			   alias_set_type ref2_alias_set,
-			   alias_set_type base2_alias_set, bool tbaa_p)
+/* Return non-zero if two indirect references based on *PTR1 and *PTR2
+   constrained to [OFFSET1, OFFSET1 + MAX_SIZE1) and  [OFFSET2, OFFSET2
+   + MAX_SIZE2) may alias.  *PTR1 and *PTR2 have the alias sets
+   BASE1_ALIAS_SET and BASE2_ALIAS_SET which can be -1 in which case
+   they are computed on-demand.  If non-NULL, REF1 and REF2 are the
+   complete memory reference trees.
+   The returned value is the size of the overlap if it can be determined,
+   otherwise it's HOST_WIDE_INT_M1U.  When ALOFF is non-null, the returned
+   value is non-zero if and only if REF1 and REF2 definitely overlap, and
+   *ALOFF is set to the offset of the overlap in REF1.  */
+
+static unsigned HOST_WIDE_INT
+indirect_refs_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
+		       HOST_WIDE_INT offset1, HOST_WIDE_INT max_size1,
+		       alias_set_type ref1_alias_set,
+		       alias_set_type base1_alias_set,
+		       tree ref2 ATTRIBUTE_UNUSED, tree base2,
+		       HOST_WIDE_INT offset2, HOST_WIDE_INT max_size2,
+		       alias_set_type ref2_alias_set,
+		       alias_set_type base2_alias_set, bool tbaa_p,
+		       unsigned HOST_WIDE_INT *aloff)
 {
   tree ptr1;
   tree ptr2;
@@ -1328,14 +1382,14 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
 	offset1 += (-moff).to_short_addr ();
       else
 	offset2 += moff.to_short_addr ();
-      return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+      return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
     }
-  if (!ptr_derefs_may_alias_p (ptr1, ptr2))
-    return false;
+  if (!ptr_derefs_alias_p (ptr1, ptr2, aloff != NULL))
+    return 0;
 
   /* Disambiguations that rely on strict aliasing rules follow.  */
   if (!flag_strict_aliasing || !tbaa_p)
-    return true;
+    return HOST_WIDE_INT_M1U;
 
   ptrtype1 = TREE_TYPE (TREE_OPERAND (base1, 1));
   ptrtype2 = TREE_TYPE (TREE_OPERAND (base2, 1));
@@ -1343,7 +1397,7 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
   /* If the alias set for a pointer access is zero all bets are off.  */
   if (base1_alias_set == 0
       || base2_alias_set == 0)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   /* If both references are through the same type, they do not alias
      if the accesses do not overlap.  This does extra disambiguation
@@ -1359,21 +1413,21 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
       /* But avoid treating arrays as "objects", instead assume they
          can overlap by an exact multiple of their element size.  */
       && TREE_CODE (TREE_TYPE (ptrtype1)) != ARRAY_TYPE)
-    return ranges_overlap_p (offset1, max_size1, offset2, max_size2);
+    return range_overlap (offset1, max_size1, offset2, max_size2, aloff);
 
   /* Do type-based disambiguation.  */
   if (base1_alias_set != base2_alias_set
       && !alias_sets_conflict_p (base1_alias_set, base2_alias_set))
-    return false;
+    return 0;
 
   /* If either reference is view-converted, give up now.  */
   if (same_type_for_tbaa (TREE_TYPE (base1), TREE_TYPE (ptrtype1)) != 1
       || same_type_for_tbaa (TREE_TYPE (base2), TREE_TYPE (ptrtype2)) != 1)
-    return true;
+    return aloff ? 0 : HOST_WIDE_INT_M1U;
 
   if (ref1 && ref2
       && nonoverlapping_component_refs_p (ref1, ref2))
-    return false;
+    return 0;
 
   /* Do access-path based disambiguation.  */
   if (ref1 && ref2
@@ -1383,21 +1437,21 @@ indirect_refs_may_alias_p (tree ref1 ATTRIBUTE_UNUSED, tree base1,
 				      offset1, max_size1,
 				      ref2,
 				      ref2_alias_set, base2_alias_set,
-				      offset2, max_size2, false);
+				      offset2, max_size2, false, aloff);
 
-  return true;
+  return HOST_WIDE_INT_M1U;
 }
 
-/* Return true, if the two memory references REF1 and REF2 may alias.  */
+/* Return non-zero if the two memory references REF1 and REF2 may alias.
+   The returned value is the size of the overlap if it can be determined,
+   otherwise it's HOST_WIDE_INT_M1U.  When ALOFF is non-null, the returned
+   value is non-zero if and only if REF1 and REF2 definitely overlap, and
+   *ALOFF is set to the offset of the overlap in REF1.  */
 
-bool
-refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
+static unsigned HOST_WIDE_INT
+refs_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p,
+		unsigned HOST_WIDE_INT *aloff)
 {
-  tree base1, base2;
-  HOST_WIDE_INT offset1 = 0, offset2 = 0;
-  HOST_WIDE_INT max_size1 = -1, max_size2 = -1;
-  bool var1_p, var2_p, ind1_p, ind2_p;
-
   gcc_checking_assert ((!ref1->ref
 			|| TREE_CODE (ref1->ref) == SSA_NAME
 			|| DECL_P (ref1->ref)
@@ -1414,12 +1468,30 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
 			   || TREE_CODE (ref2->ref) == TARGET_MEM_REF));
 
   /* Decompose the references into their base objects and the access.  */
-  base1 = ao_ref_base (ref1);
-  offset1 = ref1->offset;
-  max_size1 = ref1->max_size;
-  base2 = ao_ref_base (ref2);
-  offset2 = ref2->offset;
-  max_size2 = ref2->max_size;
+  tree base1 = ao_ref_base (ref1);
+  tree base2 = ao_ref_base (ref2);
+
+  HOST_WIDE_INT offset1 = ref1->offset;
+  HOST_WIDE_INT offset2 = ref2->offset;
+
+  HOST_WIDE_INT max_size1 = ref1->max_size;
+  HOST_WIDE_INT max_size2 = ref2->max_size;
+
+  bool must_alias = aloff != NULL;
+  if (must_alias)
+    {
+      /* ALOFF being non-null implies a request for a must-alias kind of
+	 analysis.  Consider the minimum size when ALOFF is non-zero.  */
+      max_size1 = ref1->size;
+      max_size2 = ref2->size;
+    }
+  else
+    {
+      /* ALOFF being null implies a request for a may-alias kind of
+	 analysis.  Consider the maximum size.  */
+      max_size1 = ref1->max_size;
+      max_size2 = ref2->max_size;
+    }
 
   /* We can end up with registers or constants as bases for example from
      *D.1663_44 = VIEW_CONVERT_EXPR<struct DB_LSN>(__tmp$B0F64_59);
@@ -1434,7 +1506,7 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       || TREE_CODE (base2) == CONSTRUCTOR
       || TREE_CODE (base2) == ADDR_EXPR
       || CONSTANT_CLASS_P (base2))
-    return false;
+    return 0;
 
   /* We can end up referring to code via function and label decls.
      As we likely do not properly track code aliases conservatively
@@ -1443,22 +1515,23 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       || TREE_CODE (base1) == LABEL_DECL
       || TREE_CODE (base2) == FUNCTION_DECL
       || TREE_CODE (base2) == LABEL_DECL)
-    return true;
+    return must_alias ? 0 : HOST_WIDE_INT_M1U;
 
-  /* Two volatile accesses always conflict.  */
+  /* Two volatile accesses may always conflict.  */
   if (ref1->volatile_p
       && ref2->volatile_p)
-    return true;
+    return must_alias ? 0 : HOST_WIDE_INT_M1U;
 
   /* Defer to simple offset based disambiguation if we have
      references based on two decls.  Do this before defering to
      TBAA to handle must-alias cases in conformance with the
      GCC extension of allowing type-punning through unions.  */
-  var1_p = DECL_P (base1);
-  var2_p = DECL_P (base2);
+  bool var1_p = DECL_P (base1);
+  bool var2_p = DECL_P (base2);
   if (var1_p && var2_p)
     return decl_refs_may_alias_p (ref1->ref, base1, offset1, max_size1,
-				  ref2->ref, base2, offset2, max_size2);
+				  ref2->ref, base2, offset2, max_size2,
+				  aloff);
 
   /* Handle restrict based accesses.
      ???  ao_ref_base strips inner MEM_REF [&decl], recover from that
@@ -1486,12 +1559,12 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       && MR_DEPENDENCE_CLIQUE (base1) == MR_DEPENDENCE_CLIQUE (base2)
       /* But based on different pointers they do not alias.  */
       && MR_DEPENDENCE_BASE (base1) != MR_DEPENDENCE_BASE (base2))
-    return false;
+    return 0;
 
-  ind1_p = (TREE_CODE (base1) == MEM_REF
-	    || TREE_CODE (base1) == TARGET_MEM_REF);
-  ind2_p = (TREE_CODE (base2) == MEM_REF
-	    || TREE_CODE (base2) == TARGET_MEM_REF);
+  bool ind1_p = (TREE_CODE (base1) == MEM_REF
+		 || TREE_CODE (base1) == TARGET_MEM_REF);
+  bool ind2_p = (TREE_CODE (base2) == MEM_REF
+		 || TREE_CODE (base2) == TARGET_MEM_REF);
 
   /* Canonicalize the pointer-vs-decl case.  */
   if (ind1_p && var2_p)
@@ -1511,33 +1584,65 @@ refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
       && flag_strict_aliasing
       && !alias_sets_conflict_p (ao_ref_alias_set (ref1),
 				 ao_ref_alias_set (ref2)))
-    return false;
+    return 0;
 
   /* Dispatch to the pointer-vs-decl or pointer-vs-pointer disambiguators.  */
   if (var1_p && ind2_p)
-    return indirect_ref_may_alias_decl_p (ref2->ref, base2,
-					  offset2, max_size2,
-					  ao_ref_alias_set (ref2),
-					  ao_ref_base_alias_set (ref2),
-					  ref1->ref, base1,
-					  offset1, max_size1,
-					  ao_ref_alias_set (ref1),
-					  ao_ref_base_alias_set (ref1),
-					  tbaa_p);
-  else if (ind1_p && ind2_p)
-    return indirect_refs_may_alias_p (ref1->ref, base1,
-				      offset1, max_size1,
-				      ao_ref_alias_set (ref1),
-				      ao_ref_base_alias_set (ref1),
-				      ref2->ref, base2,
+    return indirect_ref_alias_decl_p (ref2->ref, base2,
 				      offset2, max_size2,
 				      ao_ref_alias_set (ref2),
 				      ao_ref_base_alias_set (ref2),
-				      tbaa_p);
+				      ref1->ref, base1,
+				      offset1, max_size1,
+				      ao_ref_alias_set (ref1),
+				      ao_ref_base_alias_set (ref1),
+				      tbaa_p, aloff);
+  else if (ind1_p && ind2_p)
+    return indirect_refs_alias_p (ref1->ref, base1,
+				  offset1, max_size1,
+				  ao_ref_alias_set (ref1),
+				  ao_ref_base_alias_set (ref1),
+				  ref2->ref, base2,
+				  offset2, max_size2,
+				  ao_ref_alias_set (ref2),
+				  ao_ref_base_alias_set (ref2),
+				  tbaa_p, aloff);
 
   gcc_unreachable ();
 }
 
+/* Return true, if the two memory references REF1 and REF2 may alias.  */
+
+bool
+refs_may_alias_p_1 (ao_ref *ref1, ao_ref *ref2, bool tbaa_p)
+{
+  return refs_alias_p_1 (ref1, ref2, tbaa_p, NULL);
+}
+
+/* Return the size of the overlap in bytes if REF1 and REF2 alias one
+   another, and set *ALOFF (alias offset) to the offset of the overlap
+   in REF1.  Return HOST_WIDE_INT_M1U if the size of the overlap cannot
+   be determined.  Otherwise, if REF1 and REF2 do not definitely overlap,
+   return zero.  */
+
+unsigned HOST_WIDE_INT
+refs_must_alias_p_1 (ao_ref *ref1, ao_ref *ref2, unsigned HOST_WIDE_INT *aloff)
+{
+  /* ALOFF being non-zero is what distinguishes a must kind of analysis
+     from a may kind.  Set it to &DUMMY when the caller doesn't care
+     about the offset.  */
+  unsigned HOST_WIDE_INT dummy;
+  if (!aloff)
+    aloff = &dummy;
+
+  unsigned HOST_WIDE_INT alsz = refs_alias_p_1 (ref1, ref2, true, aloff);
+
+  /* Convert the size and offset of the overlap from bits to bytes.  */
+  if (alsz)
+    *aloff /= BITS_PER_UNIT;
+  return alsz / BITS_PER_UNIT;
+}
+
 static bool
 refs_may_alias_p (tree ref1, ao_ref *ref2)
 {
@@ -2945,3 +3050,90 @@ walk_aliased_vdefs (ao_ref *ref, tree vdef,
   return ret;
 }
 
+/* Attempt to detect and diagnose overlapping copy in a statement STMT
+   from SRC to DST of SIZE bytes.  When ADJUST is set, adjust SIZE to
+   reflect the lower bound of the non-constant SRC offset.  Return true
+   when overlap has been detected, false otherwise.
+   In the case of a non-constant offset, using a positive lower bound
+   is more strict than using the upper bound.  */
+
+bool
+detect_overlap (location_t loc, gimple *stmt, tree dst, tree src, tree size,
+		bool adjust /* = false */)
+{
+  ao_ref dstref, srcref;
+  unsigned HOST_WIDE_INT range[2];
+
+  /* Initialize and store the lower bound of a constant offset (in
+     bits), disregarding the offset for the destination.  */
+  ao_ref_init_from_ptr_and_size (&dstref, dst, size, range);
+  ao_ref_init_from_ptr_and_size (&srcref, src, size, range);
+
+  if (adjust)
+    {
+      /* Adjust the source and destination sizes (in bits) by
+	 the non-constant offset (also in bits).  */
+      dstref.size -= range[0];
+      srcref.size -= range[0];
+    }
+
+  /* Determine the size of the OVERLAP, if any, and its OFFSET, both
+     in bytes.  */
+  unsigned HOST_WIDE_INT offset;
+  unsigned HOST_WIDE_INT overlap
+    = refs_must_alias_p_1 (&dstref, &srcref, &offset);
+
+  if (!overlap)
+    return false;
+
+  /* Convert the size from bits to bytes (OFFSET is in bytes).  */
+
+  range[1] = (dstref.size - (range[1] - range[0])) / BITS_PER_UNIT;
+  range[0] = dstref.size / BITS_PER_UNIT;
+
+  if (range[0] > range[1])
+    std::swap (range[0], range[1]);
+
+  bool warned;
+
+  tree func = gimple_call_fndecl (stmt);
+
+  /* Below distinguish three cases:
+     1) certain constant overlap
+     2) certain overlap in some range
+     3) possible overlap.  */
+  if (range[0] == range[1])
+    warned = warning_at (loc, OPT_Wrestrict,
+			 range[0] == 1
+			 ? (overlap == 1
+			    ? G_("%qD writing %wu byte overlaps %wu byte "
+				 "at offset %wu")
+			    : G_("%qD writing %wu byte overlaps %wu bytes "
+				 "at offset %wu"))
+			 : (overlap == 1
+			    ? G_("%qD writing %wu bytes overlaps %wu byte "
+				 "at offset %wu")
+			    : G_("%qD writing %wu bytes overlaps %wu bytes "
+				 "at offset %wu")),
+			 func, range[0], overlap, offset);
+  else if (range[1] - range[0] < overlap)
+    warned = warning_at (loc, OPT_Wrestrict,
+			 overlap == 1
+			 ? G_("%qD writing between %wu and %wu bytes "
+			      "overlaps %wu byte at offset %wu")
+			 : G_("%qD writing between %wu and %wu bytes "
+			      "overlaps %wu bytes at offset %wu"),
+			 func, range[0], range[1], overlap, offset);
+  else
+    warned = warning_at (loc, OPT_Wrestrict,
+			 overlap == 1
+			 ? G_("%qD writing between %wu and %wu bytes "
+			      "may overlap %wu byte at offset %wu")
+			 : G_("%qD writing %wu byte may overlap %wu bytes "
+			      "at offset %wu"),
+			 func, range[0], range[1], overlap, offset);
+  if (warned)
+    gimple_set_no_warning (stmt, true);
+
+  return true;
+}
diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
index 8c89c69..8b3609d 100644
--- a/gcc/tree-ssa-alias.h
+++ b/gcc/tree-ssa-alias.h
@@ -99,17 +99,21 @@ struct ao_ref
 
 /* In tree-ssa-alias.c  */
 extern void ao_ref_init (ao_ref *, tree);
-extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree);
+extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree,
+					   unsigned HOST_WIDE_INT[2] = NULL);
 extern tree ao_ref_base (ao_ref *);
 extern alias_set_type ao_ref_alias_set (ao_ref *);
 extern alias_set_type ao_ref_base_alias_set (ao_ref *);
 extern bool ptr_deref_may_alias_global_p (tree);
 extern bool ptr_derefs_may_alias_p (tree, tree);
+extern bool ptr_derefs_must_alias_p (tree, tree);
 extern bool ptrs_compare_unequal (tree, tree);
 extern bool ref_may_alias_global_p (tree);
 extern bool ref_may_alias_global_p (ao_ref *);
 extern bool refs_may_alias_p (tree, tree);
 extern bool refs_may_alias_p_1 (ao_ref *, ao_ref *, bool);
+extern unsigned HOST_WIDE_INT refs_must_alias_p_1 (ao_ref *, ao_ref *,
+						   unsigned HOST_WIDE_INT *);
 extern bool refs_anti_dependent_p (tree, tree);
 extern bool refs_output_dependent_p (tree, tree);
 extern bool ref_maybe_used_by_stmt_p (gimple *, tree);
@@ -144,6 +148,8 @@ extern void debug (pt_solution *ptr);
 extern void dump_points_to_info_for (FILE *, tree);
 extern void debug_points_to_info_for (tree);
 extern void dump_alias_stats (FILE *);
+extern bool detect_overlap (location_t, gimple *, tree, tree, tree,
+			    bool = false);
 
 
 /* In tree-ssa-structalias.c  */
@@ -152,7 +158,8 @@ extern bool pt_solution_empty_p (struct pt_solution *);
 extern bool pt_solution_singleton_or_null_p (struct pt_solution *, unsigned *);
 extern bool pt_solution_includes_global (struct pt_solution *);
 extern bool pt_solution_includes (struct pt_solution *, const_tree);
-extern bool pt_solutions_intersect (struct pt_solution *, struct pt_solution *);
+extern bool pt_solutions_intersect (struct pt_solution *, struct pt_solution *,
+				    bool = false);
 extern void pt_solution_reset (struct pt_solution *);
 extern void pt_solution_set (struct pt_solution *, bitmap, bool);
 extern void pt_solution_set_var (struct pt_solution *, tree);
@@ -161,6 +168,50 @@ extern void dump_pta_stats (FILE *);
 
 extern GTY(()) struct pt_solution ipa_escaped_pt;
 
+/* Return the size of the overlap if the two ranges [POS1, POS1 + SIZE1]
+   and [POS2, POS2 + SIZE2] overlap.  Set *OFFSET to the offset of the
+   overlap.  When SIZE1 or SIZE2 is -1 the range is half-open.  Otherwise,
+   return zero.  */
+
+static inline unsigned HOST_WIDE_INT
+range_overlap (HOST_WIDE_INT pos1,
+	       unsigned HOST_WIDE_INT size1,
+	       HOST_WIDE_INT pos2,
+	       unsigned HOST_WIDE_INT size2,
+	       unsigned HOST_WIDE_INT *offset)
+{
+  if (pos1 >= pos2)
+    {
+      if (offset)
+	*offset = pos1 - pos2;
+
+      if (size2 == HOST_WIDE_INT_M1U)
+	/* Overlap is certain but its size is unknown.  */
+	return HOST_WIDE_INT_M1U;
+
+      unsigned HOST_WIDE_INT end2 = pos2 + (HOST_WIDE_INT) size2;
+      if ((unsigned HOST_WIDE_INT) pos1 < end2)
+	return end2 - pos1;
+    }
+
+  if (pos2 >= pos1)
+    {
+      if (offset)
+	*offset = pos2 - pos1;
+
+      if (size1 == HOST_WIDE_INT_M1U)
+	/* Overlap is certain but its size is unknown.  */
+	return HOST_WIDE_INT_M1U;
+
+      unsigned HOST_WIDE_INT end1 = pos1 + (HOST_WIDE_INT) size1;
+      if ((unsigned HOST_WIDE_INT) pos2 < end1)
+	return end1 - pos2;
+    }
+
+  /* No overlap.  */
+  return 0;
+}
+
 /* Return true, if the two ranges [POS1, SIZE1] and [POS2, SIZE2]
    overlap.  SIZE1 and/or SIZE2 can be (unsigned)-1 in which case the
    range is open-ended.  Otherwise return false.  */
@@ -171,18 +222,7 @@ ranges_overlap_p (HOST_WIDE_INT pos1,
 		  HOST_WIDE_INT pos2,
 		  unsigned HOST_WIDE_INT size2)
 {
-  if (pos1 >= pos2
-      && (size2 == (unsigned HOST_WIDE_INT)-1
-	  || pos1 < (pos2 + (HOST_WIDE_INT) size2)))
-    return true;
-  if (pos2 >= pos1
-      && (size1 == (unsigned HOST_WIDE_INT)-1
-	  || pos2 < (pos1 + (HOST_WIDE_INT) size1)))
-    return true;
-
-  return false;
+  return range_overlap (pos1, size1, pos2, size2, NULL) != 0;
 }
 
-
-
 #endif /* TREE_SSA_ALIAS_H  */
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index b0563fe..75c01b0 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "expr.h"
 #include "tree-dfa.h"
 #include "domwalk.h"
+#include "tree-ssa-alias.h"
 #include "tree-ssa-propagate.h"
 #include "params.h"
 #include "ipa-chkp.h"
@@ -1477,6 +1478,22 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 	    }
 	}
       dsi->stmt = stmt;
+
+      /* Try to detect overlap before returning.  This catches cases
+	 like strcpy (d, d + n) where n is non-constant whose range
+	 is such that (n <= strlen (d) holds).
+
+	 OLDDSI->NONZERO_chars may have been reset by this point with
+	 oldlen holding it original value.  */
+      if (olddsi && oldlen)
+	{
+	  /* Add 1 for the terminating NUL.  */
+	  tree type = TREE_TYPE (oldlen);
+	  oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
+				build_int_cst (type, 1));
+	  detect_overlap (loc, stmt, olddsi->ptr, src, oldlen, true);
+	}
+
       return;
     }
 
@@ -1559,6 +1576,12 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
   len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
 				  GSI_SAME_STMT);
+
+  if (const strinfo *chksi = olddsi ? olddsi : dsi)
+    if (si && detect_overlap (loc, stmt, chksi->ptr, si->ptr, len, true))
+      /* Avoid transforming strcpy to memcpy when overlap is detected.  */
+      return;
+
   if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     {
       fprintf (dump_file, "Optimizing: ");
@@ -1786,7 +1809,7 @@ static void
 handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr;
+  tree src, dst, srclen, dstlen, lhs, args, type, fn, objsz, endptr;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi;
@@ -1804,10 +1827,43 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   dsi = NULL;
   if (didx > 0)
     dsi = get_strinfo (didx);
+
+  srclen = NULL_TREE;
+  si = NULL;
+  idx = get_stridx (src);
+  if (idx < 0)
+    srclen = build_int_cst (size_type_node, ~idx);
+  else if (idx > 0)
+    {
+      si = get_strinfo (idx);
+      if (si != NULL)
+	srclen = get_string_length (si);
+    }
+
+  loc = gimple_location (stmt);
+
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
+      if (warn_restrict)
+	{
+	  /* The concatenation always involves copying at least one byte
+	     (the terminating nul), even if the source string is empty.
+	     If the source is unknown assume it's one character long and
+	     compute the size of the result.  */
+	  tree ressize = srclen ? srclen : size_one_node;
+	  tree type = TREE_TYPE (ressize);
+	  ressize = fold_build2 (PLUS_EXPR, type, ressize, ressize);
+	  ressize = fold_build2 (PLUS_EXPR, type, ressize,
+				 build_int_cst (type, 1));
+
+	  tree sptr = si && si->ptr ? si->ptr : src;
+	  if (detect_overlap (loc, stmt, dst, sptr, ressize, true))
+	    /* Avoid transforming overlapping strcat.  */
+	    return;
+	}
+
       /* strcat (p, q) can be transformed into
-	 tmp = p + strlen (p); endptr = strpcpy (tmp, q);
+	 tmp = p + strlen (p); endptr = stpcpy (tmp, q);
 	 with length endptr - p if we need to compute the length
 	 later on.  Don't do this transformation if we don't need
 	 it.  */
@@ -1840,19 +1896,6 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       return;
     }
 
-  srclen = NULL_TREE;
-  si = NULL;
-  idx = get_stridx (src);
-  if (idx < 0)
-    srclen = build_int_cst (size_type_node, ~idx);
-  else if (idx > 0)
-    {
-      si = get_strinfo (idx);
-      if (si != NULL)
-	srclen = get_string_length (si);
-    }
-
-  loc = gimple_location (stmt);
   dstlen = dsi->nonzero_chars;
   endptr = dsi->endptr;
 
@@ -1914,7 +1957,22 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (fn == NULL_TREE)
     return;
 
-  len = NULL_TREE;
+  if (warn_restrict && dsi && dstlen)
+    {
+      tree slen = srclen ? srclen : size_zero_node;
+      /* Compute the size of the concatenated string, including
+	 the terminating nul.  */
+      tree type = TREE_TYPE (dstlen);
+      tree ressize = fold_build2 (PLUS_EXPR, type, dstlen, slen);
+      ressize = fold_build2 (PLUS_EXPR, type, ressize, build_int_cst (type, 1));
+
+      tree sptr = si && si->ptr ? si->ptr : src;
+      if (detect_overlap (loc, stmt, dst, sptr, ressize, true))
+	/* Avoid transforming overlapping strcat.  */
+	return;
+    }
+
+  tree len = NULL_TREE;
   if (srclen != NULL_TREE)
     {
       args = TYPE_ARG_TYPES (TREE_TYPE (fn));
diff --git a/gcc/tree-ssa-structalias.c b/gcc/tree-ssa-structalias.c
index e563e9d..1728260 100644
--- a/gcc/tree-ssa-structalias.c
+++ b/gcc/tree-ssa-structalias.c
@@ -6743,31 +6743,33 @@ pt_solution_includes (struct pt_solution *pt, const_tree decl)
 }
 
 /* Return true if both points-to solutions PT1 and PT2 have a non-empty
-   intersection.  */
+   intersection.  When STRICT is true, interpret only definitive results
+   as positive, and possible results as negative.  */
 
 static bool
-pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2)
+pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2,
+			  bool strict = false)
 {
   if (pt1->anything || pt2->anything)
-    return true;
+    return !strict;
 
   /* If either points to unknown global memory and the other points to
-     any global memory they alias.  */
+     any global memory they may (though need not) alias.  */
   if ((pt1->nonlocal
        && (pt2->nonlocal
 	   || pt2->vars_contains_nonlocal))
       || (pt2->nonlocal
 	  && pt1->vars_contains_nonlocal))
-    return true;
+    return !strict;
 
   /* If either points to all escaped memory and the other points to
-     any escaped memory they alias.  */
+     any escaped memory they may (though need not) alias.  */
   if ((pt1->escaped
        && (pt2->escaped
 	   || pt2->vars_contains_escaped))
       || (pt2->escaped
 	  && pt1->vars_contains_escaped))
-    return true;
+    return !strict;
 
   /* Check the escaped solution if required.
      ???  Do we need to check the local against the IPA escaped sets?  */
@@ -6775,17 +6777,17 @@ pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2)
       && !pt_solution_empty_p (&ipa_escaped_pt))
     {
       /* If both point to escaped memory and that solution
-	 is not empty they alias.  */
+	 is not empty they may alias.  */
       if (pt1->ipa_escaped && pt2->ipa_escaped)
-	return true;
+	return !strict;
 
       /* If either points to escaped memory see if the escaped solution
 	 intersects with the other.  */
       if ((pt1->ipa_escaped
-	   && pt_solutions_intersect_1 (&ipa_escaped_pt, pt2))
+	   && pt_solutions_intersect_1 (&ipa_escaped_pt, pt2, strict))
 	  || (pt2->ipa_escaped
-	      && pt_solutions_intersect_1 (&ipa_escaped_pt, pt1)))
-	return true;
+	      && pt_solutions_intersect_1 (&ipa_escaped_pt, pt1, strict)))
+	return !strict;
     }
 
   /* Now both pointers alias if their points-to solution intersects.  */
@@ -6794,10 +6796,17 @@ pt_solutions_intersect_1 (struct pt_solution *pt1, struct pt_solution *pt2)
 	  && bitmap_intersect_p (pt1->vars, pt2->vars));
 }
 
+/* Return true if the solutions *PT1 and *PT2 intersect.  When STRICT
+   is true, interpret only definitive results as positive, and possible
+   results as negative.  The former effectively gives an answer to
+   the qestion of whether two pointers must refer to the same array,
+   while the latter answers whether they may refer to one.  */
+
 bool
-pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2)
+pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2,
+			bool strict /* = false */)
 {
-  bool res = pt_solutions_intersect_1 (pt1, pt2);
+  bool res = pt_solutions_intersect_1 (pt1, pt2, strict);
   if (res)
     ++pta_stats.pt_solutions_intersect_may_alias;
   else
@@ -6805,7 +6814,6 @@ pt_solutions_intersect (struct pt_solution *pt1, struct pt_solution *pt2)
   return res;
 }
 
-
 /* Dump points-to information to OUTFILE.  */
 
 static void

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

* [PING] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-07-20 20:46 ` Martin Sebor
@ 2017-07-25  3:13   ` Martin Sebor
  2017-08-01  2:27     ` [PING #2] " Martin Sebor
  0 siblings, 1 reply; 39+ messages in thread
From: Martin Sebor @ 2017-07-25  3:13 UTC (permalink / raw)
  To: Gcc Patch List

Ping:

   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01264.html

This change is related to

   [PATCH] enhance -Wrestrict for sprintf %s arguments
   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01176.html

On 07/20/2017 02:45 PM, Martin Sebor wrote:
> With more testing (building GDB, Glibc, Busybox, and the Linux
> kernel) I found a few bugs and weaknesses in the initial patch.
> Attached is version 2 that fixes the uncovered problems and makes
> further enhancements to handle more cases.
>
> Martin
>
> On 07/16/2017 05:47 PM, Martin Sebor wrote:
>> Being implemented in the front end, the -Wrestrict warning
>> detects only trivial instances of violations.  The attached
>> patch extends the implementation to the middle-end where
>> data flow and alias analysis can be combined to detect even
>> complex cases of overlap.  This work is independent of but
>> follows on the patch below (waiting for review):
>>
>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00036.html
>>
>> The changes rely on extending the tree-ssa-{alias,structalias}
>> machinery in a simple way to answer the "must-alias" kind of
>> question in addition to the current "may-alias."
>>
>> The rest of the changes are in gimple-fold.c, tree-ssa-strlen.c,
>> and builtins.c.
>>
>> Even though this change makes -Wrestrict a lot more useful, it's
>> not a complete implementation.  Not all built-ins are handled yet
>> (e.g., strncat), and support for user-defined functions is still
>> subject to the limitations of the front end implementation.  To
>> complete the support, handlers for the missing string built-ins
>> will need to be added to tree-ssa-strlen.c, and the remaining
>> bits should be moved from the front end to somewhere in
>> the middle-end (e.g., calls.c).
>>
>> Martin
>

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

* [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-07-25  3:13   ` [PING] " Martin Sebor
@ 2017-08-01  2:27     ` Martin Sebor
  2017-08-01  9:23       ` Richard Biener
  0 siblings, 1 reply; 39+ messages in thread
From: Martin Sebor @ 2017-08-01  2:27 UTC (permalink / raw)
  To: Gcc Patch List, Richard Biener; +Cc: Jeff Law

Richard,

in discussing this work Jeff mentioned that your comments on
the tree-ssa-alias.c parts would be helpful.  When you have
a chance could you please give it a once over and let me know
if you have any suggestions or concerns?  There are no visible
changes to existing clients of the pass, just extensions that
are relied on only by the new diagnostics.

   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01264.html

I expect to revisit the sprintf %s patch mentioned below and
see how to simplify it by taking advantage of the changes
implemented here.

Thanks
Martin

On 07/24/2017 09:13 PM, Martin Sebor wrote:
> Ping:
>
>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01264.html
>
> This change is related to
>
>   [PATCH] enhance -Wrestrict for sprintf %s arguments
>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01176.html
>
> On 07/20/2017 02:45 PM, Martin Sebor wrote:
>> With more testing (building GDB, Glibc, Busybox, and the Linux
>> kernel) I found a few bugs and weaknesses in the initial patch.
>> Attached is version 2 that fixes the uncovered problems and makes
>> further enhancements to handle more cases.
>>
>> Martin
>>
>> On 07/16/2017 05:47 PM, Martin Sebor wrote:
>>> Being implemented in the front end, the -Wrestrict warning
>>> detects only trivial instances of violations.  The attached
>>> patch extends the implementation to the middle-end where
>>> data flow and alias analysis can be combined to detect even
>>> complex cases of overlap.  This work is independent of but
>>> follows on the patch below (waiting for review):
>>>
>>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00036.html
>>>
>>> The changes rely on extending the tree-ssa-{alias,structalias}
>>> machinery in a simple way to answer the "must-alias" kind of
>>> question in addition to the current "may-alias."
>>>
>>> The rest of the changes are in gimple-fold.c, tree-ssa-strlen.c,
>>> and builtins.c.
>>>
>>> Even though this change makes -Wrestrict a lot more useful, it's
>>> not a complete implementation.  Not all built-ins are handled yet
>>> (e.g., strncat), and support for user-defined functions is still
>>> subject to the limitations of the front end implementation.  To
>>> complete the support, handlers for the missing string built-ins
>>> will need to be added to tree-ssa-strlen.c, and the remaining
>>> bits should be moved from the front end to somewhere in
>>> the middle-end (e.g., calls.c).
>>>
>>> Martin
>>
>

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-01  2:27     ` [PING #2] " Martin Sebor
@ 2017-08-01  9:23       ` Richard Biener
  2017-08-01  9:25         ` Richard Biener
  0 siblings, 1 reply; 39+ messages in thread
From: Richard Biener @ 2017-08-01  9:23 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List, Jeff Law

On Tue, Aug 1, 2017 at 4:27 AM, Martin Sebor <msebor@gmail.com> wrote:
> Richard,
>
> in discussing this work Jeff mentioned that your comments on
> the tree-ssa-alias.c parts would be helpful.  When you have
> a chance could you please give it a once over and let me know
> if you have any suggestions or concerns?  There are no visible
> changes to existing clients of the pass, just extensions that
> are relied on only by the new diagnostics.
>
>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01264.html
>
> I expect to revisit the sprintf %s patch mentioned below and
> see how to simplify it by taking advantage of the changes
> implemented here.

Your ptr_deref_alias_decl_p returns true when must_alias is true
but there is no must-alias relationship.

The ao_ref_init_from_ptr_and_size doesn't belong there.

I dislike all of the changes related to returning an alias "size".

Please keep away from the alias machinery.

Warning during folding is similarly bad design.  Please don't add
more such things.

Thanks for putting this on my radar though.
Richard.


> Thanks
> Martin
>
>
> On 07/24/2017 09:13 PM, Martin Sebor wrote:
>>
>> Ping:
>>
>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01264.html
>>
>> This change is related to
>>
>>   [PATCH] enhance -Wrestrict for sprintf %s arguments
>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01176.html
>>
>> On 07/20/2017 02:45 PM, Martin Sebor wrote:
>>>
>>> With more testing (building GDB, Glibc, Busybox, and the Linux
>>> kernel) I found a few bugs and weaknesses in the initial patch.
>>> Attached is version 2 that fixes the uncovered problems and makes
>>> further enhancements to handle more cases.
>>>
>>> Martin
>>>
>>> On 07/16/2017 05:47 PM, Martin Sebor wrote:
>>>>
>>>> Being implemented in the front end, the -Wrestrict warning
>>>> detects only trivial instances of violations.  The attached
>>>> patch extends the implementation to the middle-end where
>>>> data flow and alias analysis can be combined to detect even
>>>> complex cases of overlap.  This work is independent of but
>>>> follows on the patch below (waiting for review):
>>>>
>>>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00036.html
>>>>
>>>> The changes rely on extending the tree-ssa-{alias,structalias}
>>>> machinery in a simple way to answer the "must-alias" kind of
>>>> question in addition to the current "may-alias."
>>>>
>>>> The rest of the changes are in gimple-fold.c, tree-ssa-strlen.c,
>>>> and builtins.c.
>>>>
>>>> Even though this change makes -Wrestrict a lot more useful, it's
>>>> not a complete implementation.  Not all built-ins are handled yet
>>>> (e.g., strncat), and support for user-defined functions is still
>>>> subject to the limitations of the front end implementation.  To
>>>> complete the support, handlers for the missing string built-ins
>>>> will need to be added to tree-ssa-strlen.c, and the remaining
>>>> bits should be moved from the front end to somewhere in
>>>> the middle-end (e.g., calls.c).
>>>>
>>>> Martin
>>>
>>>
>>
>

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-01  9:23       ` Richard Biener
@ 2017-08-01  9:25         ` Richard Biener
  2017-08-02 17:10           ` Jeff Law
  0 siblings, 1 reply; 39+ messages in thread
From: Richard Biener @ 2017-08-01  9:25 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Gcc Patch List, Jeff Law

On Tue, Aug 1, 2017 at 11:23 AM, Richard Biener
<richard.guenther@gmail.com> wrote:
> On Tue, Aug 1, 2017 at 4:27 AM, Martin Sebor <msebor@gmail.com> wrote:
>> Richard,
>>
>> in discussing this work Jeff mentioned that your comments on
>> the tree-ssa-alias.c parts would be helpful.  When you have
>> a chance could you please give it a once over and let me know
>> if you have any suggestions or concerns?  There are no visible
>> changes to existing clients of the pass, just extensions that
>> are relied on only by the new diagnostics.
>>
>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01264.html
>>
>> I expect to revisit the sprintf %s patch mentioned below and
>> see how to simplify it by taking advantage of the changes
>> implemented here.
>
> Your ptr_deref_alias_decl_p returns true when must_alias is true
> but there is no must-alias relationship.
>
> The ao_ref_init_from_ptr_and_size doesn't belong there.
>
> I dislike all of the changes related to returning an alias "size".
>
> Please keep away from the alias machinery.
>
> Warning during folding is similarly bad design.  Please don't add
> more such things.
>
> Thanks for putting this on my radar though.
> Richard.

Oh, for a constructive comment this all feels like sth for either
sanitizers or a proper static analysis tool.  As I outlined repeatedly
I belive GCC could host a static analysis tool but it surely should
not be interwinded into it's optimization passes or tools.

Richard.

>
>> Thanks
>> Martin
>>
>>
>> On 07/24/2017 09:13 PM, Martin Sebor wrote:
>>>
>>> Ping:
>>>
>>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01264.html
>>>
>>> This change is related to
>>>
>>>   [PATCH] enhance -Wrestrict for sprintf %s arguments
>>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01176.html
>>>
>>> On 07/20/2017 02:45 PM, Martin Sebor wrote:
>>>>
>>>> With more testing (building GDB, Glibc, Busybox, and the Linux
>>>> kernel) I found a few bugs and weaknesses in the initial patch.
>>>> Attached is version 2 that fixes the uncovered problems and makes
>>>> further enhancements to handle more cases.
>>>>
>>>> Martin
>>>>
>>>> On 07/16/2017 05:47 PM, Martin Sebor wrote:
>>>>>
>>>>> Being implemented in the front end, the -Wrestrict warning
>>>>> detects only trivial instances of violations.  The attached
>>>>> patch extends the implementation to the middle-end where
>>>>> data flow and alias analysis can be combined to detect even
>>>>> complex cases of overlap.  This work is independent of but
>>>>> follows on the patch below (waiting for review):
>>>>>
>>>>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg00036.html
>>>>>
>>>>> The changes rely on extending the tree-ssa-{alias,structalias}
>>>>> machinery in a simple way to answer the "must-alias" kind of
>>>>> question in addition to the current "may-alias."
>>>>>
>>>>> The rest of the changes are in gimple-fold.c, tree-ssa-strlen.c,
>>>>> and builtins.c.
>>>>>
>>>>> Even though this change makes -Wrestrict a lot more useful, it's
>>>>> not a complete implementation.  Not all built-ins are handled yet
>>>>> (e.g., strncat), and support for user-defined functions is still
>>>>> subject to the limitations of the front end implementation.  To
>>>>> complete the support, handlers for the missing string built-ins
>>>>> will need to be added to tree-ssa-strlen.c, and the remaining
>>>>> bits should be moved from the front end to somewhere in
>>>>> the middle-end (e.g., calls.c).
>>>>>
>>>>> Martin
>>>>
>>>>
>>>
>>

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-01  9:25         ` Richard Biener
@ 2017-08-02 17:10           ` Jeff Law
  2017-08-03  8:46             ` Richard Biener
  0 siblings, 1 reply; 39+ messages in thread
From: Jeff Law @ 2017-08-02 17:10 UTC (permalink / raw)
  To: Richard Biener, Martin Sebor; +Cc: Gcc Patch List

On 08/01/2017 03:25 AM, Richard Biener wrote:
> On Tue, Aug 1, 2017 at 11:23 AM, Richard Biener
> <richard.guenther@gmail.com> wrote:
>> On Tue, Aug 1, 2017 at 4:27 AM, Martin Sebor <msebor@gmail.com> wrote:
>>> Richard,
>>>
>>> in discussing this work Jeff mentioned that your comments on
>>> the tree-ssa-alias.c parts would be helpful.  When you have
>>> a chance could you please give it a once over and let me know
>>> if you have any suggestions or concerns?  There are no visible
>>> changes to existing clients of the pass, just extensions that
>>> are relied on only by the new diagnostics.
>>>
>>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01264.html
>>>
>>> I expect to revisit the sprintf %s patch mentioned below and
>>> see how to simplify it by taking advantage of the changes
>>> implemented here.
>>
>> Your ptr_deref_alias_decl_p returns true when must_alias is true
>> but there is no must-alias relationship.
>>
>> The ao_ref_init_from_ptr_and_size doesn't belong there.
>>
>> I dislike all of the changes related to returning an alias "size".
>>
>> Please keep away from the alias machinery.
>>
>> Warning during folding is similarly bad design.  Please don't add
>> more such things.
>>
>> Thanks for putting this on my radar though.
>> Richard.
> 
> Oh, for a constructive comment this all feels like sth for either
> sanitizers or a proper static analysis tool.  As I outlined repeatedly
> I belive GCC could host a static analysis tool but it surely should
> not be interwinded into it's optimization passes or tools.
I disagree strongly here.

Sanitiers catch problems after the fact and only if you've compiled your
code with sanitization and manage to find a way to trigger the problem path.

Other static analysis tools are useful, but they aren't an integral part
of the edit, compile, debug cycle and due to their cost are often run
long after code was committed.

Finding useful warnings that can be issued as part of the compile, edit,
debug cycle is, IMHO, far more useful than sanitizers or independent
static checkers.

--


I think finding a way to exploit information that our various analyzers
can provide to give precise warnings is a good thing.  So it seemed
natural that if we wanted to ask a must-alias question that we should be
querying the alias oracle rather than implementing that kind of query
within the sprintf warnings.  I'm not sure why you'd say "Please keep
away from the alias machinery".

I'm a little confused here...


Jeff

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-02 17:10           ` Jeff Law
@ 2017-08-03  8:46             ` Richard Biener
  2017-08-06 23:08               ` Martin Sebor
  2017-08-09 16:09               ` [PING #2] [PATCH] " Jeff Law
  0 siblings, 2 replies; 39+ messages in thread
From: Richard Biener @ 2017-08-03  8:46 UTC (permalink / raw)
  To: Jeff Law; +Cc: Martin Sebor, Gcc Patch List

On Wed, Aug 2, 2017 at 7:10 PM, Jeff Law <law@redhat.com> wrote:
> On 08/01/2017 03:25 AM, Richard Biener wrote:
>> On Tue, Aug 1, 2017 at 11:23 AM, Richard Biener
>> <richard.guenther@gmail.com> wrote:
>>> On Tue, Aug 1, 2017 at 4:27 AM, Martin Sebor <msebor@gmail.com> wrote:
>>>> Richard,
>>>>
>>>> in discussing this work Jeff mentioned that your comments on
>>>> the tree-ssa-alias.c parts would be helpful.  When you have
>>>> a chance could you please give it a once over and let me know
>>>> if you have any suggestions or concerns?  There are no visible
>>>> changes to existing clients of the pass, just extensions that
>>>> are relied on only by the new diagnostics.
>>>>
>>>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01264.html
>>>>
>>>> I expect to revisit the sprintf %s patch mentioned below and
>>>> see how to simplify it by taking advantage of the changes
>>>> implemented here.
>>>
>>> Your ptr_deref_alias_decl_p returns true when must_alias is true
>>> but there is no must-alias relationship.
>>>
>>> The ao_ref_init_from_ptr_and_size doesn't belong there.
>>>
>>> I dislike all of the changes related to returning an alias "size".
>>>
>>> Please keep away from the alias machinery.
>>>
>>> Warning during folding is similarly bad design.  Please don't add
>>> more such things.
>>>
>>> Thanks for putting this on my radar though.
>>> Richard.
>>
>> Oh, for a constructive comment this all feels like sth for either
>> sanitizers or a proper static analysis tool.  As I outlined repeatedly
>> I belive GCC could host a static analysis tool but it surely should
>> not be interwinded into it's optimization passes or tools.
> I disagree strongly here.
>
> Sanitiers catch problems after the fact and only if you've compiled your
> code with sanitization and manage to find a way to trigger the problem path.
>
> Other static analysis tools are useful, but they aren't an integral part
> of the edit, compile, debug cycle and due to their cost are often run
> long after code was committed.
>
> Finding useful warnings that can be issued as part of the compile, edit,
> debug cycle is, IMHO, far more useful than sanitizers or independent
> static checkers.
>
> --
>
>
> I think finding a way to exploit information that our various analyzers
> can provide to give precise warnings is a good thing.  So it seemed
> natural that if we wanted to ask a must-alias question that we should be
> querying the alias oracle rather than implementing that kind of query
> within the sprintf warnings.  I'm not sure why you'd say "Please keep
> away from the alias machinery".
>
> I'm a little confused here...

Well, simply because the way as implemented isn't a must-alias query
but maybe one that's good enough for warnings (reduces false positives
but surely doesn't eliminate them).

There's a must alias query already, in stmt_kills_ref_p.  It's a matter
of refactoring to make a refs_must_alias_p.

Then propose that "overlap range" stuff separately.

But I'm against lumping this all in as an innocent change suggesting
the machinery can do sth (must alias) when it really can't.  I also
do not like adding a "must alias" bool to the may-alias APIs as
the implementation is fundamentally may-alias, must-alias really
is very different.

And to repeat, no, I do not want a "good-enough-for-warnings" must-alias
in an API that's supposed to be used by optimizations where "good enough"
is not good enough.

Richard.

>
>
> Jeff

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-03  8:46             ` Richard Biener
@ 2017-08-06 23:08               ` Martin Sebor
  2017-08-08 13:08                 ` Richard Biener
  2017-08-09 16:14                 ` Jeff Law
  2017-08-09 16:09               ` [PING #2] [PATCH] " Jeff Law
  1 sibling, 2 replies; 39+ messages in thread
From: Martin Sebor @ 2017-08-06 23:08 UTC (permalink / raw)
  To: Richard Biener, Jeff Law; +Cc: Gcc Patch List

On 08/03/2017 02:45 AM, Richard Biener wrote:
> On Wed, Aug 2, 2017 at 7:10 PM, Jeff Law <law@redhat.com> wrote:
>> On 08/01/2017 03:25 AM, Richard Biener wrote:
>>> On Tue, Aug 1, 2017 at 11:23 AM, Richard Biener
>>> <richard.guenther@gmail.com> wrote:
>>>> On Tue, Aug 1, 2017 at 4:27 AM, Martin Sebor <msebor@gmail.com> wrote:
>>>>> Richard,
>>>>>
>>>>> in discussing this work Jeff mentioned that your comments on
>>>>> the tree-ssa-alias.c parts would be helpful.  When you have
>>>>> a chance could you please give it a once over and let me know
>>>>> if you have any suggestions or concerns?  There are no visible
>>>>> changes to existing clients of the pass, just extensions that
>>>>> are relied on only by the new diagnostics.
>>>>>
>>>>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01264.html
>>>>>
>>>>> I expect to revisit the sprintf %s patch mentioned below and
>>>>> see how to simplify it by taking advantage of the changes
>>>>> implemented here.
>>>>
>>>> Your ptr_deref_alias_decl_p returns true when must_alias is true
>>>> but there is no must-alias relationship.
>>>>
>>>> The ao_ref_init_from_ptr_and_size doesn't belong there.
>>>>
>>>> I dislike all of the changes related to returning an alias "size".
>>>>
>>>> Please keep away from the alias machinery.
>>>>
>>>> Warning during folding is similarly bad design.  Please don't add
>>>> more such things.
>>>>
>>>> Thanks for putting this on my radar though.
>>>> Richard.
>>>
>>> Oh, for a constructive comment this all feels like sth for either
>>> sanitizers or a proper static analysis tool.  As I outlined repeatedly
>>> I belive GCC could host a static analysis tool but it surely should
>>> not be interwinded into it's optimization passes or tools.
>> I disagree strongly here.
>>
>> Sanitiers catch problems after the fact and only if you've compiled your
>> code with sanitization and manage to find a way to trigger the problem path.
>>
>> Other static analysis tools are useful, but they aren't an integral part
>> of the edit, compile, debug cycle and due to their cost are often run
>> long after code was committed.
>>
>> Finding useful warnings that can be issued as part of the compile, edit,
>> debug cycle is, IMHO, far more useful than sanitizers or independent
>> static checkers.
>>
>> --
>>
>>
>> I think finding a way to exploit information that our various analyzers
>> can provide to give precise warnings is a good thing.  So it seemed
>> natural that if we wanted to ask a must-alias question that we should be
>> querying the alias oracle rather than implementing that kind of query
>> within the sprintf warnings.  I'm not sure why you'd say "Please keep
>> away from the alias machinery".
>>
>> I'm a little confused here...
>
> Well, simply because the way as implemented isn't a must-alias query
> but maybe one that's good enough for warnings (reduces false positives
> but surely doesn't eliminate them).

I'm very interested in reducing the rate of false positives in
these and all other warnings.  As I mentioned in my comments,
I did my best to weed them out of the implementation by building
GDB, Glibc, Busybox, and the Linux kernel.  That of course isn't
a guarantee that there aren't any.  But the first implementation
of any non-trivial feature is never perfect, and hardly any
warning of sufficient complexity is free of false positives, no
matter here it's implemented (the middle-end, front-end, or
a standalone static analysis tool).  If you spotted some cases
I had missed I'd certainly be grateful for examples.  Otherwise,
they will undoubtedly be reported as more software is exposed
to the warning and, if possible, fixed, as happens with all
other warnings.

> There's a must alias query already, in stmt_kills_ref_p.  It's a matter
> of refactoring to make a refs_must_alias_p.
>
> Then propose that "overlap range" stuff separately.

I appreciate constructive suggestions for improvements  and I will
look into the stmt_kills_ref_p suggestion.  But since my work
elicited such an strong response from you I feel I should explain:
I tried to make my changes as unintrusive as possible.  The alias
oracle is a new area for me and I didn't want to make mistakes in
the process of making overly extensive modifications to it.

> But I'm against lumping this all in as an innocent change suggesting
> the machinery can do sth (must alias) when it really can't.  I also
> do not like adding a "must alias" bool to the may-alias APIs as
> the implementation is fundamentally may-alias, must-alias really
> is very different.

I certainly want to do the right thing and implement the warning
in a way that makes the most sense.  As I said, I'll look into
the refactoring, but since my testing shows the current code to
work well as is, it would be helpful if you could provide more
details about what it is that concerns you with it and what cases
of false positives you are worried about.  (Examples of code that
demonstrate the false positives would be especially valuable.)

That being said, after much thought, I do have to let you know
that I take offense at both your tone and your insinuation that
I tried to sneak in some subversive changes.  I did what I thought
was right.  If it can be done better I'm glad to hear the details
of what's wrong with it and in what ways the approach you prefer
is better.  But I would be grateful for a more respectful reply
in the future.

Martin

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-06 23:08               ` Martin Sebor
@ 2017-08-08 13:08                 ` Richard Biener
  2017-08-09 16:14                 ` Jeff Law
  1 sibling, 0 replies; 39+ messages in thread
From: Richard Biener @ 2017-08-08 13:08 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jeff Law, Gcc Patch List

On Mon, Aug 7, 2017 at 1:08 AM, Martin Sebor <msebor@gmail.com> wrote:
> On 08/03/2017 02:45 AM, Richard Biener wrote:
>>
>> On Wed, Aug 2, 2017 at 7:10 PM, Jeff Law <law@redhat.com> wrote:
>>>
>>> On 08/01/2017 03:25 AM, Richard Biener wrote:
>>>>
>>>> On Tue, Aug 1, 2017 at 11:23 AM, Richard Biener
>>>> <richard.guenther@gmail.com> wrote:
>>>>>
>>>>> On Tue, Aug 1, 2017 at 4:27 AM, Martin Sebor <msebor@gmail.com> wrote:
>>>>>>
>>>>>> Richard,
>>>>>>
>>>>>> in discussing this work Jeff mentioned that your comments on
>>>>>> the tree-ssa-alias.c parts would be helpful.  When you have
>>>>>> a chance could you please give it a once over and let me know
>>>>>> if you have any suggestions or concerns?  There are no visible
>>>>>> changes to existing clients of the pass, just extensions that
>>>>>> are relied on only by the new diagnostics.
>>>>>>
>>>>>>   https://gcc.gnu.org/ml/gcc-patches/2017-07/msg01264.html
>>>>>>
>>>>>> I expect to revisit the sprintf %s patch mentioned below and
>>>>>> see how to simplify it by taking advantage of the changes
>>>>>> implemented here.
>>>>>
>>>>>
>>>>> Your ptr_deref_alias_decl_p returns true when must_alias is true
>>>>> but there is no must-alias relationship.
>>>>>
>>>>> The ao_ref_init_from_ptr_and_size doesn't belong there.
>>>>>
>>>>> I dislike all of the changes related to returning an alias "size".
>>>>>
>>>>> Please keep away from the alias machinery.
>>>>>
>>>>> Warning during folding is similarly bad design.  Please don't add
>>>>> more such things.
>>>>>
>>>>> Thanks for putting this on my radar though.
>>>>> Richard.
>>>>
>>>>
>>>> Oh, for a constructive comment this all feels like sth for either
>>>> sanitizers or a proper static analysis tool.  As I outlined repeatedly
>>>> I belive GCC could host a static analysis tool but it surely should
>>>> not be interwinded into it's optimization passes or tools.
>>>
>>> I disagree strongly here.
>>>
>>> Sanitiers catch problems after the fact and only if you've compiled your
>>> code with sanitization and manage to find a way to trigger the problem
>>> path.
>>>
>>> Other static analysis tools are useful, but they aren't an integral part
>>> of the edit, compile, debug cycle and due to their cost are often run
>>> long after code was committed.
>>>
>>> Finding useful warnings that can be issued as part of the compile, edit,
>>> debug cycle is, IMHO, far more useful than sanitizers or independent
>>> static checkers.
>>>
>>> --
>>>
>>>
>>> I think finding a way to exploit information that our various analyzers
>>> can provide to give precise warnings is a good thing.  So it seemed
>>> natural that if we wanted to ask a must-alias question that we should be
>>> querying the alias oracle rather than implementing that kind of query
>>> within the sprintf warnings.  I'm not sure why you'd say "Please keep
>>> away from the alias machinery".
>>>
>>> I'm a little confused here...
>>
>>
>> Well, simply because the way as implemented isn't a must-alias query
>> but maybe one that's good enough for warnings (reduces false positives
>> but surely doesn't eliminate them).
>
>
> I'm very interested in reducing the rate of false positives in
> these and all other warnings.  As I mentioned in my comments,
> I did my best to weed them out of the implementation by building
> GDB, Glibc, Busybox, and the Linux kernel.  That of course isn't
> a guarantee that there aren't any.  But the first implementation
> of any non-trivial feature is never perfect, and hardly any
> warning of sufficient complexity is free of false positives, no
> matter here it's implemented (the middle-end, front-end, or
> a standalone static analysis tool).  If you spotted some cases
> I had missed I'd certainly be grateful for examples.  Otherwise,
> they will undoubtedly be reported as more software is exposed
> to the warning and, if possible, fixed, as happens with all
> other warnings.
>
>> There's a must alias query already, in stmt_kills_ref_p.  It's a matter
>> of refactoring to make a refs_must_alias_p.
>>
>> Then propose that "overlap range" stuff separately.
>
>
> I appreciate constructive suggestions for improvements  and I will
> look into the stmt_kills_ref_p suggestion.  But since my work
> elicited such an strong response from you I feel I should explain:
> I tried to make my changes as unintrusive as possible.  The alias
> oracle is a new area for me and I didn't want to make mistakes in
> the process of making overly extensive modifications to it.
>
>> But I'm against lumping this all in as an innocent change suggesting
>> the machinery can do sth (must alias) when it really can't.  I also
>> do not like adding a "must alias" bool to the may-alias APIs as
>> the implementation is fundamentally may-alias, must-alias really
>> is very different.
>
>
> I certainly want to do the right thing and implement the warning
> in a way that makes the most sense.  As I said, I'll look into
> the refactoring, but since my testing shows the current code to
> work well as is, it would be helpful if you could provide more
> details about what it is that concerns you with it and what cases
> of false positives you are worried about.  (Examples of code that
> demonstrate the false positives would be especially valuable.)

I am not worried about false positive warnings.  I am worried about
an API used by optimization passes that advertises an ability
(must-alias query, whoo!) that may end up producing wrong-code.

I believe must-alias is so fundamentally different from may-alias
that re-using the same API (with a mere change in one argument)
is wrong.

We _do_ have a must-alias API as I said.

_Any_ change to the alias API needs to give conservatively correct
answers.

> That being said, after much thought, I do have to let you know
> that I take offense at both your tone and your insinuation that
> I tried to sneak in some subversive changes.  I did what I thought
> was right.  If it can be done better I'm glad to hear the details
> of what's wrong with it and in what ways the approach you prefer
> is better.  But I would be grateful for a more respectful reply
> in the future.

Sorry, but the Subject of the mails with the patches say
"enhance -Wrestrict" which causes me to look away quickly.
But most of the patch was actually changes to the alias API
and implementation.  You should have sumbitted those
separately or more prominetly advertise them.

I took an offense on this tactic, if that wasn't your intention
then take this reaction as a reason to better separate changes
to separate parts of the compiler.

Thanks,
Richard.

> Martin

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-03  8:46             ` Richard Biener
  2017-08-06 23:08               ` Martin Sebor
@ 2017-08-09 16:09               ` Jeff Law
  1 sibling, 0 replies; 39+ messages in thread
From: Jeff Law @ 2017-08-09 16:09 UTC (permalink / raw)
  To: Richard Biener; +Cc: Martin Sebor, Gcc Patch List

On 08/03/2017 02:45 AM, Richard Biener wrote:
> 
> Well, simply because the way as implemented isn't a must-alias query
> but maybe one that's good enough for warnings (reduces false positives
> but surely doesn't eliminate them).
OK.  So it's more about building a proper must-alias query and less
about exploiting the alias oracle to give us more precise information.
That was the source of my confusion.

> 
> There's a must alias query already, in stmt_kills_ref_p.  It's a matter
> of refactoring to make a refs_must_alias_p.
Funny you should mention that -- stmt_kills_ref_p is what I had in mind
when Martin first posted this work.  DSE essentially raises a must-alias
query via that interface and I'd fully support refactoring
stmt_kills_ref_p so that it would be used for DSE as well as Martin's work.

> 
> Then propose that "overlap range" stuff separately.
That works for me.

> 
> But I'm against lumping this all in as an innocent change suggesting
> the machinery can do sth (must alias) when it really can't.  I also
> do not like adding a "must alias" bool to the may-alias APIs as
> the implementation is fundamentally may-alias, must-alias really
> is very different.
That works for me.


> 
> And to repeat, no, I do not want a "good-enough-for-warnings" must-alias
> in an API that's supposed to be used by optimizations where "good enough"
> is not good enough.
Understood and agreed.

I'll note that I suspect Martin's desire to reduce false positives and
potentially weaken things here is driven by the pushback he's had on
implementing warnings with a "too high" false positive rate in the past.

jeff

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-06 23:08               ` Martin Sebor
  2017-08-08 13:08                 ` Richard Biener
@ 2017-08-09 16:14                 ` Jeff Law
  2017-08-22  2:04                   ` Martin Sebor
  1 sibling, 1 reply; 39+ messages in thread
From: Jeff Law @ 2017-08-09 16:14 UTC (permalink / raw)
  To: Martin Sebor, Richard Biener; +Cc: Gcc Patch List

On 08/06/2017 05:08 PM, Martin Sebor wrote:

>>
>> Well, simply because the way as implemented isn't a must-alias query
>> but maybe one that's good enough for warnings (reduces false positives
>> but surely doesn't eliminate them).
> 
> I'm very interested in reducing the rate of false positives in
> these and all other warnings.  As I mentioned in my comments,
> I did my best to weed them out of the implementation by building
> GDB, Glibc, Busybox, and the Linux kernel.  That of course isn't
> a guarantee that there aren't any.  But the first implementation
> of any non-trivial feature is never perfect, and hardly any
> warning of sufficient complexity is free of false positives, no
> matter here it's implemented (the middle-end, front-end, or
> a standalone static analysis tool).  If you spotted some cases
> I had missed I'd certainly be grateful for examples.  Otherwise,
> they will undoubtedly be reported as more software is exposed
> to the warning and, if possible, fixed, as happens with all
> other warnings.
I think Richi is saying that the must alias query you've built isn't
proper/correct.  It's less about false positives for Richi and more
about building a proper must alias query if I understand him correctly.

I suspect he's also saying that you can't reasonably build must-alias on
top of a may-alias query framework.  They're pretty different queries.

If you need something that is close to, but not quite a must alias
query, then you're going to have to make a argument for that and you
can't call it a must alias query.


> 
>> There's a must alias query already, in stmt_kills_ref_p.  It's a matter
>> of refactoring to make a refs_must_alias_p.
>>
>> Then propose that "overlap range" stuff separately.
> 
> I appreciate constructive suggestions for improvements  and I will
> look into the stmt_kills_ref_p suggestion.  But since my work
> elicited such an strong response from you I feel I should explain:
> I tried to make my changes as unintrusive as possible.  The alias
> oracle is a new area for me and I didn't want to make mistakes in
> the process of making overly extensive modifications to it.
Note that I've got a reasonably good handle on how we use
stmt_kills_ref_p.  So I can help with questions on that side.

Jeff

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-09 16:14                 ` Jeff Law
@ 2017-08-22  2:04                   ` Martin Sebor
  2017-08-22  9:17                     ` Richard Biener
  0 siblings, 1 reply; 39+ messages in thread
From: Martin Sebor @ 2017-08-22  2:04 UTC (permalink / raw)
  To: Jeff Law, Richard Biener; +Cc: Gcc Patch List

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

On 08/09/2017 10:14 AM, Jeff Law wrote:
> On 08/06/2017 05:08 PM, Martin Sebor wrote:
>
>>>
>>> Well, simply because the way as implemented isn't a must-alias query
>>> but maybe one that's good enough for warnings (reduces false positives
>>> but surely doesn't eliminate them).
>>
>> I'm very interested in reducing the rate of false positives in
>> these and all other warnings.  As I mentioned in my comments,
>> I did my best to weed them out of the implementation by building
>> GDB, Glibc, Busybox, and the Linux kernel.  That of course isn't
>> a guarantee that there aren't any.  But the first implementation
>> of any non-trivial feature is never perfect, and hardly any
>> warning of sufficient complexity is free of false positives, no
>> matter here it's implemented (the middle-end, front-end, or
>> a standalone static analysis tool).  If you spotted some cases
>> I had missed I'd certainly be grateful for examples.  Otherwise,
>> they will undoubtedly be reported as more software is exposed
>> to the warning and, if possible, fixed, as happens with all
>> other warnings.
> I think Richi is saying that the must alias query you've built isn't
> proper/correct.  It's less about false positives for Richi and more
> about building a proper must alias query if I understand him correctly.
>
> I suspect he's also saying that you can't reasonably build must-alias on
> top of a may-alias query framework.  They're pretty different queries.
>
> If you need something that is close to, but not quite a must alias
> query, then you're going to have to make a argument for that and you
> can't call it a must alias query.

Attached is an updated and simplified patch that avoids making
changes to any of the may-alias functions.  It turns out that
all the information the logic needs to determine the overlap
is already in the ao_ref structures populated by
ao_ref_init_from_ptr_and_size.  The only changes to the pass
are the enhancement to ao_ref_init_from_ptr_and_size to make
use of range info and the addition of the two new functions
used by the -Wrestrict clients outside the pass.

Martin

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

PR middle-end/78918 - missing -Wrestrict on memcpy copying over self

gcc/ChangeLog:

	PR middle-end/78918
	* builtins.c (warn_for_overlap, maybe_warn_for_overlap): New.
	(check_sizes): Add argument and call maybe_warn_for_overlap.
	Rename function arguments for clarity.
	(check_memop_sizes): Adjust.
	(expand_builtin_memchr): Ditto.
	(expand_builtin_strcat): Ditto.
	(expand_builtin_strcpy): Ditto.
	(expand_builtin_stpcpy): Ditto.
	(expand_builtin_stpncpy): Ditto.
	(expand_builtin_strncpy): Ditto.
	(expand_builtin_memcmp): Ditto.
	(expand_builtin_memory_chk): Ditto.
	(check_strncat_sizes): Ditto.  Rename locals for clarity.
	(expand_builtin_strncat): Ditto.
	(maybe_emit_chk_warning): Ditto.
	(maybe_emit_sprintf_chk_warning): Adjust.
	* cfgexpand.c (expand_call_stmt): Set TREE_NO_WARNING.
	* gimple-fold.c (gimple_fold_builtin_memory_op): Handle -Wrestrict.
	(gimple_fold_builtin_strcpy): Ditto.
	(gimple_fold_builtin_memory_chk): Ditto.
	(gimple_fold_builtin_stxcpy_chk): Ditto.
	* gimple.c (gimple_build_call_from_tree): Set call location.
	* tree-ssa-alias.h (refs_overlap, detect_overlap): New functions.
	* tree-ssa-alias.c (refs_overlap, detect_overlap): Define.
	* tree-ssa-strlen.c (handle_builtin_strcpy): Handle -Wrestrict.
	(handle_builtin_strcat): Ditto.

gcc/c-family/ChangeLog:

	PR middle-end/78918
	* c-common.c (check_function_restrict): Suppress warning for
	built-in functions.
	* c.opt (-Wrestrict): Include in -Wall.

gcc/testsuite/ChangeLog:

	PR middle-end/78918
	* c-c++-common/Wrestrict.c: New test.
	* gcc.dg/Walloca-1.c: Suppress macro expansion tracking.
	* gcc.dg/pr69172.c: Prune -Wrestrict.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index 2deef72..e0fd9a7 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -3036,39 +3036,303 @@ expand_builtin_memcpy_args (tree dest, tree src, tree len, rtx target, tree exp)
   return dest_addr;
 }
 
+/* Issue a warning for a restricted copy call expression EXP to a built-in
+   function FUNC, with a destination of size DSTSIZE, size of copy in RANGE,
+   and with OVERLAP bytes at offset OFFRANGE.  MUST_OVERLAP is true when
+   the overlap is certain, false when it is likely.  */
+
+static void
+warn_for_overlap (tree exp, tree func, bool must_overlap, tree dstsize,
+		  const tree range[2], unsigned HOST_WIDE_INT overlap,
+		  const unsigned HOST_WIDE_INT offrange[2])
+{
+  location_t loc = tree_nonartificial_location (exp);
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  /* To avoid combinatorial explosion of diagnostics format the offset
+     or its range as a string and use it in the warning calls below.  */
+  char offstr[64];
+  if (offrange[0] == offrange[1] || offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr, "%llu", (long long) offrange[0]);
+  else
+    sprintf (offstr, "%llu - %llu", (long long) offrange[0],
+	     (long long) offrange[1]);
+
+  /* The text uses the term "writing N bytes" even though most operations
+     involve "copying" and the latter might be clearer.  This first term
+     is used because string functions like strncat and strncpy don't
+     necessarily copy all N bytes; they may copy fewer.  */
+
+  if (tree_to_uhwi (dstsize) >= HOST_WIDE_INT_MAX)
+    {
+      if (must_overlap)
+	{
+	  if (tree_int_cst_equal (range[0], range[1]))
+	    warning_at (loc, OPT_Wrestrict,
+			(integer_onep (range[0])
+			 ? G_("%K%qD writing %E byte overlaps %wu byte "
+			      "at offset %s")
+			 : (overlap == 1
+			    ? G_("%K%qD writing %E bytes overlaps %wu byte "
+				 "at offset %s")
+			    : G_("%K%qD writing %E bytes overlaps %wu bytes "
+				 "at offset %s"))),
+			exp, func, range[0], overlap, offstr);
+	  else if (tree_int_cst_sign_bit (range[1]))
+	    {
+	      /* Avoid printing the upper bound if it's invalid.
+		 The overlap can likewise be "or more" in this case.  */
+	      warning_at (loc, OPT_Wrestrict,
+			  "%K%qD writing %E or more bytes overlaps %wu "
+			  "or more bytes at offset %s",
+			  exp, func, range[0], overlap, offstr);
+	    }
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD writing between %E and %E bytes overlaps "
+			"%wu or more bytes at offset %s",
+			exp, func, range[0], range[1], overlap, offstr);
+	}
+      else
+	{
+	  if (tree_int_cst_equal (range[0], range[1]))
+	    warning_at (loc, OPT_Wrestrict,
+			(integer_onep (range[0])
+			 ? G_("%K%qD writing %E byte may overlap %wu bytes "
+			      "at offset %s")
+			 : (overlap == 1
+			    ? G_("%K%qD writing %E bytes may overlap %wu byte "
+				 "at offset %s")
+			    : G_("%K%qD writing %E bytes may overlap %wu bytes "
+				 "at offset %s"))),
+			exp, func, range[0], overlap, offstr);
+	  else if (tree_int_cst_sign_bit (range[1]))
+	    {
+	      /* Avoid printing the upper bound if it's invalid.
+		 The overlap can likewise be "or more" in this case.  */
+	      warning_at (loc, OPT_Wrestrict,
+			  "%K%qD writing %E or more bytes may overlap %wu "
+			  "or more bytes at offset %s",
+			  exp, func, range[0], overlap, offstr);
+	    }
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD writing between %E and %E bytes may overlap "
+			"%wu or more bytes at offset %s",
+			exp, func, range[0], range[1], overlap,
+			offstr);
+	}
+
+      return;
+    }
+
+  if (must_overlap)
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	warning_at (loc, OPT_Wrestrict,
+		    (integer_onep (range[0])
+		     ? G_("%K%qD writing %E byte into a region "
+			  "of size %E overlaps %wu byte "
+			  "at offset %s")
+		     : (overlap == 1
+			? G_("%K%qD writing %E bytes into a region "
+			     "of size %E overlaps %wu byte "
+			     "at offset %s")
+			: G_("%K%qD writing %E bytes into a region "
+			     "of size %E overlaps %wu bytes "
+			     "at offset %s"))),
+		    exp, func, range[0], dstsize, overlap, offstr);
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.
+	     The overlap can likewise be "or more" in this case.  */
+	  warning_at (loc, OPT_Wrestrict,
+		      "%K%qD writing %E or more bytes into "
+		      "a region of size %E overlaps %wu or more bytes "
+		      "at offset %s",
+		      exp, func, range[0], dstsize, overlap, offstr);
+	}
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD writing between %E and %E bytes into "
+		    "a region of size %E overlaps %wu or more bytes "
+		    "at offset %s",
+		    exp, func, range[0], range[1], dstsize, overlap,
+		    offstr);
+    }
+  else
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	{
+	  if (overlap < HOST_WIDE_INT_MAX)
+	    warning_at (loc, OPT_Wrestrict,
+			(integer_onep (range[0])
+			 ? G_("%K%qD writing %E byte into a region "
+			      "of size %E may overlap %wu bytes "
+			      "at offset %s")
+			 : (overlap == 1
+			    ? G_("%K%qD writing %E bytes into a region "
+				 "of size %E may overlap %wu byte at "
+				 "offset %s")
+			    : G_("%K%qD writing %E bytes into a region "
+				 "of size %E may overlap %wu bytes at "
+				 "offset %s"))),
+			exp, func, range[0], dstsize, overlap, offstr);
+	  else
+	    /* Non-constant overlap due to an unknown range.  */
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD writing into a region of size %E may overlap "
+			"at offset %s",
+			exp, func, dstsize, offstr);
+	}
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.
+	     The overlap can likewise be "or more" in this case.  */
+	  warning_at (loc, OPT_Wrestrict,
+		      "%K%qD writing %E or more bytes into "
+		      "a region of size %E may overlap %wu or more "
+		      "bytes at offset %s",
+		      exp, func, range[0], dstsize, overlap, offstr);
+	}
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD writing between %E and %E bytes into "
+		    "a region of size %E may overlap %wu or more bytes "
+		    "at offset %s",
+		    exp, func, range[0], range[1], dstsize, overlap,
+		    offstr);
+    }
+}
+
+/* Determine whether an overlap exists in a restricted copy call
+   expression EXP to a built-in function FUNC, with a destination DST
+   of DSTSIZE in size and source SRC, writing DSTWRITE bytes into DST
+   and reading MAXREAD bytes from SRC of SLEN bytes in length copy in
+   RANGE.  If so, diagnose it and return true.  If RELAXED is true
+   diagnose even uncertain (but likely) overlaps.  Otherwise do nothing
+   and return false.  */
+
+static bool
+maybe_warn_for_overlap (tree exp, tree func, tree dst, tree src, tree dstwrite,
+			tree range[2], bool at_least_one, tree maxread,
+			tree slen, tree dstsize, bool relaxed)
+{
+  if (!dstwrite)
+    dstwrite = range[0];
+
+  /* If the range of byte counts is [0, N] and N is non-zero, use
+     1 as the conservative minimum to detect at least the most basic
+     cases of overflow.  */
+  if (dstwrite && TREE_CODE (dstwrite) != INTEGER_CST && range[0])
+    dstwrite = (integer_zerop (range[0]) && !integer_zerop (range[1])
+		? size_one_node : range[0]);
+
+  bool must_overlap = dstwrite && tree_fits_uhwi_p (dstwrite);
+
+  if (maxread)
+    must_overlap &= tree_fits_uhwi_p (maxread);
+  else if (slen)
+    {
+      /* When DSTWRITE is known, set the maximum number of bytes
+	 to access to DSTWRITE either when the length of the source
+	 sequence is unknown, or when DSTWRITE is less than the length.  */
+      maxread = (must_overlap
+		 && (at_least_one || tree_int_cst_lt (dstwrite, slen))
+		 ? dstwrite : slen);
+    }
+  else
+    maxread = dstwrite;
+
+  if (!dstwrite)
+    dstwrite = maxread;
+
+  /* The exact number of bytes copied is known only if the sizes
+     weren't adjusted earlier on.  */
+  must_overlap &= !at_least_one || (dstwrite && dstwrite != slen);
+
+  /* Initialize and store the lower bound of a constant offset (in
+     bits), disregarding the offset for the destination.  */
+  ao_ref dstref, srcref;
+  unsigned HOST_WIDE_INT offrange[2];
+
+  ao_ref_init_from_ptr_and_size (&dstref, dst, dstwrite, offrange);
+  ao_ref_init_from_ptr_and_size (&srcref, src, maxread, offrange);
+
+  unsigned HOST_WIDE_INT offset;
+  if (unsigned HOST_WIDE_INT overlap
+      = refs_overlap (&dstref, &srcref, &offset))
+    {
+
+      /* Convert the offset range from bits to bytes.  */
+      offrange[0] /= BITS_PER_UNIT;
+      offrange[1] /= BITS_PER_UNIT;
+
+      if (overlap <= offrange[1] - offrange[0])
+	must_overlap = false;
+
+      if (offrange[0] != offrange[1])
+	;
+      else if (offrange[0] != HOST_WIDE_INT_M1U)
+	offrange[0] = offrange[1] = offset;
+
+      if (must_overlap || !relaxed)
+	{
+	  if (!range[0])
+	    range[0] = range[1] = dstwrite;
+
+	  warn_for_overlap (exp, func, must_overlap, dstsize, range, overlap,
+			    offrange);
+	}
+
+      /* Return error when an overlap has been detected.  */
+      return must_overlap;
+    }
+
+  return false;
+}
+
 /* Try to verify that the sizes and lengths of the arguments to a string
    manipulation function given by EXP are within valid bounds and that
-   the operation does not lead to buffer overflow.  Arguments other than
-   EXP may be null.  When non-null, the arguments have the following
-   meaning:
-   SIZE is the user-supplied size argument to the function (such as in
-   memcpy(d, s, SIZE) or strncpy(d, s, SIZE).  It specifies the exact
-   number of bytes to write.
-   MAXLEN is the user-supplied bound on the length of the source sequence
+   the operation does not lead to buffer overflow or read past the end.
+   For copy operations that must not overlap also verify that the source
+   and destination do not overlap one another.
+   Arguments other than EXP may be null.  When non-null, the arguments
+   have the following meaning:
+   DST is the destination of a copy call or null otherwise.
+   SRC is the source of a copy call or null otherwise.
+   DSTWRITE is the number of bytes written into the destination obtained
+   from the user-supplied size argument to the function (such as in
+   memcpy(DST, SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+   MAXREAD is the user-supplied bound on the length of the source sequence
    (such as in strncat(d, s, N).  It specifies the upper limit on the number
-   of bytes to write.
-   SRC is the source string (such as in strcpy(d, s)) when the expression
-   EXP is a string function call (as opposed to a memory call like memcpy).
-   As an exception, SRC can also be an integer denoting the precomputed
-   size of the source string or object (for functions like memcpy).
-   OBJSIZE is the size of the destination object specified by the last
+   of bytes to write.  If null it's taken to be the same as DSTWRITE.
+   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+   expression EXP is a string function call (as opposed to a memory call
+   like memcpy).  As an exception, SRCSTR can also be an integer denoting
+   the precomputed size of the source string or object (for functions like
+   memcpy).
+   DSTSIZE is the size of the destination object specified by the last
    argument to the _chk builtins, typically resulting from the expansion
-   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
-   OBJSIZE).
+   of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
+   DSTSIZE).
 
-   When SIZE is null LEN is checked to verify that it doesn't exceed
+   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
    If the call is successfully verified as safe from buffer overflow
-   the function returns true, otherwise false..  */
+   the function returns true, otherwise false.  */
 
 static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_sizes (int opt, tree exp, tree dst, tree src, tree dstwrite,
+	     tree maxread, tree srcstr, tree dstsize)
 {
   /* The size of the largest object is half the address space, or
      SSIZE_MAX.  (This is way too permissive.)  */
   tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
 
+  /* Either the length of the source string for string functions or
+     the size of the source object for raw memory functions.  */
   tree slen = NULL_TREE;
 
   tree range[2] = { NULL_TREE, NULL_TREE };
@@ -3077,28 +3341,28 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
      function like strcpy is not known and the only thing that is
      known is that it must be at least one (for the terminating nul).  */
   bool at_least_one = false;
-  if (src)
+  if (srcstr)
     {
-      /* SRC is normally a pointer to string but as a special case
+      /* SRCSTR is normally a pointer to string but as a special case
 	 it can be an integer denoting the length of a string.  */
-      if (POINTER_TYPE_P (TREE_TYPE (src)))
+      if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
 	{
 	  /* Try to determine the range of lengths the source string
 	     refers to.  If it can be determined and is less than
-	     the upper bound given by MAXLEN add one to it for
+	     the upper bound given by MAXREAD add one to it for
 	     the terminating nul.  Otherwise, set it to one for
-	     the same reason, or to MAXLEN as appropriate.  */
-	  get_range_strlen (src, range);
-	  if (range[0] && (!maxlen || TREE_CODE (maxlen) == INTEGER_CST))
+	     the same reason, or to MAXREAD as appropriate.  */
+	  get_range_strlen (srcstr, range);
+	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
 	    {
-	      if (maxlen && tree_int_cst_le (maxlen, range[0]))
-		range[0] = range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[0]))
+		range[0] = range[1] = maxread;
 	      else
 		range[0] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[0], size_one_node);
 
-	      if (maxlen && tree_int_cst_le (maxlen, range[1]))
-		range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[1]))
+		range[1] = maxread;
 	      else if (!integer_all_onesp (range[1]))
 		range[1] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[1], size_one_node);
@@ -3112,10 +3376,10 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	    }
 	}
       else
-	slen = src;
+	slen = srcstr;
     }
 
-  if (!size && !maxlen)
+  if (!dstwrite && !maxread)
     {
       /* When the only available piece of data is the object size
 	 there is nothing to do.  */
@@ -3123,20 +3387,18 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	return true;
 
       /* Otherwise, when the length of the source sequence is known
-	 (as with with strlen), set SIZE to it.  */
+	 (as with strlen), set DSTWRITE to it.  */
       if (!range[0])
-	size = slen;
+	dstwrite = slen;
     }
 
-  if (!objsize)
-    objsize = maxobjsize;
+  if (!dstsize)
+    dstsize = maxobjsize;
 
-  /* The SIZE is exact if it's non-null, constant, and in range of
-     unsigned HOST_WIDE_INT.  */
-  bool exactsize = size && tree_fits_uhwi_p (size);
+  if (dstwrite)
+    get_size_range (dstwrite, range);
 
-  if (size)
-    get_size_range (size, range);
+  tree func = get_callee_fndecl (exp);
 
   /* First check the number of bytes to be written against the maximum
      object size.  */
@@ -3149,30 +3411,34 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	warning_at (loc, opt,
 		    "%K%qD specified size %E "
 		    "exceeds maximum object size %E",
-		    exp, get_callee_fndecl (exp), range[0], maxobjsize);
+		    exp, func, range[0], maxobjsize);
 	  else
 	    warning_at (loc, opt,
 			"%K%qD specified size between %E and %E "
 			"exceeds maximum object size %E",
-			exp, get_callee_fndecl (exp),
+			exp, func,
 			range[0], range[1], maxobjsize);
       return false;
     }
 
+  /* The number of bytes to write is "exact" if DSTWRITE is non-null,
+     constant, and in range of unsigned HOST_WIDE_INT.  */
+  bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite);
+
   /* Next check the number of bytes to be written against the destination
      object size.  */
-  if (range[0] || !exactsize || integer_all_onesp (size))
+  if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
     {
       if (range[0]
-	  && ((tree_fits_uhwi_p (objsize)
-	       && tree_int_cst_lt (objsize, range[0]))
-	      || (tree_fits_uhwi_p (size)
-		  && tree_int_cst_lt (size, range[0]))))
+	  && ((tree_fits_uhwi_p (dstsize)
+	       && tree_int_cst_lt (dstsize, range[0]))
+	      || (tree_fits_uhwi_p (dstwrite)
+		  && tree_int_cst_lt (dstwrite, range[0]))))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
 
-	  if (size == slen && at_least_one)
+	  if (dstwrite == slen && at_least_one)
 	    {
 	      /* This is a call to strcpy with a destination of 0 size
 		 and a source of unknown length.  The call will write
@@ -3180,7 +3446,7 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else if (tree_int_cst_equal (range[0], range[1]))
 	    warning_at (loc, opt,
@@ -3189,21 +3455,21 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 			      "of size %E overflows the destination")
 			 : G_("%K%qD writing %E bytes into a region "
 			      "of size %E overflows the destination")),
-			exp, get_callee_fndecl (exp), range[0], objsize);
+			exp, func, range[0], dstsize);
 	  else if (tree_int_cst_sign_bit (range[1]))
 	    {
 	      /* Avoid printing the upper bound if it's invalid.  */
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else
 	    warning_at (loc, opt,
 			"%K%qD writing between %E and %E bytes into "
 			"a region of size %E overflows the destination",
-			exp, get_callee_fndecl (exp), range[0],	range[1],
-			objsize);
+			exp, func, range[0], range[1],
+			dstsize);
 
 	  /* Return error when an overflow has been detected.  */
 	  return false;
@@ -3213,11 +3479,15 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
   /* Check the maximum length of the source sequence against the size
      of the destination object if known, or against the maximum size
      of an object.  */
-  if (maxlen)
+  if (maxread)
     {
-      get_size_range (maxlen, range);
+      get_size_range (maxread, range);
+
+      /* Use the lower end for MAXREAD from now on.  */
+      if (range[0])
+	maxread = range[0];
 
-      if (range[0] && objsize && tree_fits_uhwi_p (objsize))
+      if (range[0] && dstsize && tree_fits_uhwi_p (dstsize))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
@@ -3231,40 +3501,41 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], maxobjsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], range[1], maxobjsize);
 
 	      return false;
 	    }
 
-	  if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
+	  if (dstsize != maxobjsize && tree_int_cst_lt (dstsize, range[0]))
 	    {
 	      if (tree_int_cst_equal (range[0], range[1]))
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], objsize);
+			    exp, func,
+			    range[0], dstsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], range[1], objsize);
+			    exp, func,
+			    range[0], range[1], dstsize);
 	      return false;
 	    }
 	}
     }
 
+  /* Check for reading past the end of SRC.  */
   if (slen
-      && slen == src
-      && size && range[0]
+      && slen == srcstr
+      && dstwrite && range[0]
       && tree_int_cst_lt (slen, range[0]))
     {
       location_t loc = tree_nonartificial_location (exp);
@@ -3274,23 +3545,36 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		    (tree_int_cst_equal (range[0], integer_one_node)
 		     ? G_("%K%qD reading %E byte from a region of size %E")
 		     : G_("%K%qD reading %E bytes from a region of size %E")),
-		    exp, get_callee_fndecl (exp), range[0], slen);
+		    exp, func, range[0], slen);
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
 	  warning_at (loc, opt,
 		      "%K%qD reading %E or more bytes from a region "
 		      "of size %E",
-		      exp, get_callee_fndecl (exp), range[0], slen);
+		      exp, func, range[0], slen);
 	}
       else
 	warning_at (loc, opt,
 		    "%K%qD reading between %E and %E bytes from a region "
 		    "of size %E",
-		    exp, get_callee_fndecl (exp), range[0], range[1], slen);
+		    exp, func, range[0], range[1], slen);
       return false;
     }
 
+  /* Check for overlapping copies.  Avoid warning if one has already
+     been issued (unlike -Wstringop-overflow, -Wrestrict is checked
+     in many places and TREE_NO_WARNING applies to it but not to
+     the former (there should be a bit for every kind of warning).  */
+  if (src && dst
+      && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+      && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+      && !TREE_NO_WARNING (exp)
+      && maybe_warn_for_overlap (exp, func, dst, src, dstwrite, range,
+				 at_least_one, maxread, slen, dstsize,
+				 srcstr == NULL))
+    return false;
+
   return true;
 }
 
@@ -3330,8 +3614,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size)
   tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
   tree dstsize = compute_objsize (dest, 0);
 
-  return check_sizes (OPT_Wstringop_overflow_, exp,
-		      size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+  return check_sizes (OPT_Wstringop_overflow_, exp, dest, src,
+		      size, /*maxread=*/NULL_TREE, srcsize, dstsize);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -3353,8 +3637,8 @@ expand_builtin_memchr (tree exp, rtx)
     {
       tree size = compute_objsize (arg1, 0);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, len, /*maxlen=*/NULL_TREE,
-		   size, /*objsize=*/NULL_TREE);
+		   exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		   /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
     }
 
   return NULL_RTX;
@@ -3652,7 +3936,8 @@ expand_builtin_strcat (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+	       exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+	       destsize);
 
   return NULL_RTX;
 }
@@ -3671,11 +3956,15 @@ expand_builtin_strcpy (tree exp, rtx target)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
 
-  if (warn_stringop_overflow)
+  if (warn_stringop_overflow || warn_restrict)
     {
-      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+      /* Use Object Size type-1 for -Wrestrict.  */
+      int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 2;
+
+      tree destsize = compute_objsize (dest, ost);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+		   exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		   src, destsize);
     }
 
   return expand_builtin_strcpy_args (dest, src, target);
@@ -3714,7 +4003,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
     {
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+		   exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+		   destsize);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -3799,7 +4089,7 @@ expand_builtin_stpncpy (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+	       exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 
   return NULL_RTX;
 }
@@ -3829,7 +4119,7 @@ check_strncat_sizes (tree exp, tree objsize)
 {
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
 
   /* Try to determine the range of lengths that the source expression
      refers to.  */
@@ -3853,32 +4143,33 @@ check_strncat_sizes (tree exp, tree objsize)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize)
-      && tree_int_cst_equal (objsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return false;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
   /* The number of bytes to write is LEN but check_sizes will also
      check SRCLEN if LEN's value isn't known.  */
   return check_sizes (OPT_Wstringop_overflow_,
-		      exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+		      exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+		      objsize);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -3896,7 +4187,7 @@ expand_builtin_strncat (tree exp, rtx)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
   /* The upper bound on the number of bytes to write.  */
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
   /* The length of the source sequence.  */
   tree slen = c_strlen (src, 1);
 
@@ -3919,32 +4210,31 @@ expand_builtin_strncat (tree exp, rtx)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize)
-      && tree_int_cst_equal (destsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return NULL_RTX;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
-     check SRCLEN if LEN's value isn't known.  */
+  /* The number of bytes to write is SRCLEN.  */
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+	       exp, dest, src, NULL_TREE, maxread, srclen, destsize);
 
   return NULL_RTX;
 }
@@ -3975,7 +4265,7 @@ expand_builtin_strncpy (tree exp, rtx target)
 	  /* The number of bytes to write is LEN but check_sizes will also
 	     check SLEN if LEN's value isn't known.  */
 	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+		       exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 	}
 
       /* We must be passed a constant len and src parameter.  */
@@ -4319,13 +4609,13 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
     {
       tree size = compute_objsize (arg1, 0);
       if (check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE))
+		       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		       /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
 	{
 	  size = compute_objsize (arg2, 0);
 	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE);
+		       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		       /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
 	}
     }
 
@@ -9708,8 +9998,6 @@ static rtx
 expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			   enum built_in_function fcode)
 {
-  tree dest, src, len, size;
-
   if (!validate_arglist (exp,
 			 POINTER_TYPE,
 			 fcode == BUILT_IN_MEMSET_CHK
@@ -9717,13 +10005,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
-  len = CALL_EXPR_ARG (exp, 2);
-  size = CALL_EXPR_ARG (exp, 3);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 3);
 
   bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
-			       exp, len, /*maxlen=*/NULL_TREE,
+			       exp, dest, src, len, /*maxread=*/NULL_TREE,
 			       /*str=*/NULL_TREE, size);
 
   if (!tree_fits_uhwi_p (size))
@@ -9833,7 +10121,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
   /* The maximum length of the source sequence in a bounded operation
      (such as __strncat_chk) or null if the operation isn't bounded
      (such as __strcat_chk).  */
-  tree maxlen = NULL_TREE;
+  tree maxread = NULL_TREE;
 
   switch (fcode)
     {
@@ -9854,27 +10142,27 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
     case BUILT_IN_STRNCAT_CHK:
       catstr = CALL_EXPR_ARG (exp, 0);
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STPNCPY_CHK:
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
-      maxlen = CALL_EXPR_ARG (exp, 1);
+      maxread = CALL_EXPR_ARG (exp, 1);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
     default:
       gcc_unreachable ();
     }
 
-  if (catstr && maxlen)
+  if (catstr && maxread)
     {
       /* Check __strncat_chk.  There is no way to determine the length
 	 of the string to which the source string is being appended so
@@ -9883,8 +10171,11 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       return;
     }
 
-  check_sizes (OPT_Wstringop_overflow_, exp,
-	       /*size=*/NULL_TREE, maxlen, srcstr, objsize);
+  /* The destination argument is the first one for all built-ins above.  */
+  tree dst = CALL_EXPR_ARG (exp, 0);
+
+  check_sizes (OPT_Wstringop_overflow_, exp, dst, srcstr,
+	       /*size=*/NULL_TREE, maxread, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9940,8 +10231,10 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 
   /* Add one for the terminating nul.  */
   len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
+
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
+	       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
+	       /*maxread=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index feb0904..c86a393 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5261,14 +5261,20 @@ check_function_restrict (const_tree fndecl, const_tree fntype,
 			 int nargs, tree *argarray)
 {
   int i;
-  tree parms;
+  tree parms = TYPE_ARG_TYPES (fntype);
 
   if (fndecl
-      && TREE_CODE (fndecl) == FUNCTION_DECL
-      && DECL_ARGUMENTS (fndecl))
-    parms = DECL_ARGUMENTS (fndecl);
-  else
-    parms = TYPE_ARG_TYPES (fntype);
+      && TREE_CODE (fndecl) == FUNCTION_DECL)
+    {
+      /* Skip checking built-ins here.  They are checked in more
+	 detail elsewhere.  */
+      if (DECL_BUILT_IN (fndecl)
+	  && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+	return;
+
+      if (DECL_ARGUMENTS (fndecl))
+	parms = DECL_ARGUMENTS (fndecl);
+    }
 
   for (i = 0; i < nargs; i++)
     TREE_VISITED (argarray[i]) = 0;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index e0ad3ab..01506fd 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1162,7 +1162,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall)
 Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier.
 
 Wrestrict
-C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++)
+C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
 Warn when an argument passed to a restrict-qualified parameter aliases with
 another argument.
 
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 3e1d24d..76b9b15 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2627,6 +2627,9 @@ expand_call_stmt (gcall *stmt)
   if (gimple_call_nothrow_p (stmt))
     TREE_NOTHROW (exp) = 1;
 
+  if (gimple_no_warning_p (stmt))
+    TREE_NO_WARNING (exp) = 1;
+
   CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt);
   CALL_EXPR_MUST_TAIL_CALL (exp) = gimple_call_must_tail_p (stmt);
   CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index b9d071b..5c3b14d 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3852,6 +3852,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
 -Wparentheses  @gol
 -Wpointer-sign  @gol
 -Wreorder   @gol
+-Wrestrict   @gol
 -Wreturn-type  @gol
 -Wsequence-point  @gol
 -Wsign-compare @r{(only in C++)}  @gol
@@ -6535,11 +6536,25 @@ reduce the padding and so make the structure smaller.
 Warn if anything is declared more than once in the same scope, even in
 cases where multiple declaration is valid and changes nothing.
 
-@item -Wrestrict
+@item -Wno-restrict
 @opindex Wrestrict
 @opindex Wno-restrict
-Warn when an argument passed to a restrict-qualified parameter
-aliases with another argument.
+Warn when an object referenced by a @code{restrict}-qualified parameter
+(or, in C++, @code{__restrict}-qualified parameter) is aliased by another
+argument, or when copies between such objects overlap.  For example,
+the call to the @code{strcpy} function below attempts to truncate the string
+by replacing its initial characters with the last four.  However, because
+the call writes the terminating NUL into @code{a[4]}, the copies overlap and
+the call is diagnosed.
+
+@smallexample
+struct foo
+@{
+  char a[] = "abcd1234";
+  strcpy (a, a + 4);
+@};
+@end smallexample
+The @option{-Wrestrict} is included in @option{-Wall}.
 
 @item -Wnested-externs @r{(C and Objective-C only)}
 @opindex Wnested-externs
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index d94dc9c..eb61383 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -57,6 +57,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-cfg.h"
 #include "fold-const-call.h"
 #include "asan.h"
+#include "intl.h"
+#include "diagnostic-core.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -644,7 +646,13 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
   tree destvar, srcvar;
   location_t loc = gimple_location (stmt);
 
-  /* If the LEN parameter is zero, return DEST.  */
+  tree func = gimple_call_fndecl (stmt);
+  bool nowarn = gimple_no_warning_p (stmt);
+  bool check_overlap = (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+			&& DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+			&& !nowarn);
+
+    /* If the LEN parameter is zero, return DEST.  */
   if (integer_zerop (len))
     {
       gimple *repl;
@@ -666,6 +674,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
      DEST{,+LEN,+LEN-1}.  */
   if (operand_equal_p (src, dest, 0))
     {
+      if (check_overlap)
+	warning_at (loc, OPT_Wrestrict,
+		    "%qD source argument is the same as destination",
+		    func);
+
       unlink_stmt_vdef (stmt);
       if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
 	release_ssa_name (gimple_vdef (stmt));
@@ -715,6 +728,31 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	  unsigned ilen = tree_to_uhwi (len);
 	  if (pow2p_hwi (ilen))
 	    {
+	      if (check_overlap)
+		{
+		  ao_ref dstref, srcref;
+		  ao_ref_init_from_ptr_and_size (&dstref, dest, len);
+		  ao_ref_init_from_ptr_and_size (&srcref, src, len);
+
+		  if (refs_may_alias_p_1 (&dstref, &srcref, true))
+		    {
+		      /* Defer folding until both arguments are in an SSA
+			 form so that aliasing can be determined with greater
+			 accuracy.  */
+		      if (TREE_CODE (dest) != SSA_NAME
+			  || TREE_CODE (src) != SSA_NAME)
+			return false;
+
+		      /* Check for overlap and diagnose it.  If it's found
+			 avoid folding.  */
+		      if (detect_overlap (loc, stmt, dest, src, len, endp == 2))
+			{
+			  gimple_set_no_warning (stmt, true);
+			  return false;
+			}
+		    }
+		}
+
 	      tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
 	      if (type
 		  && TYPE_MODE (type) != BLKmode
@@ -1028,6 +1066,10 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	    }
 	}
 
+      /* Avoid folding the call if overlap is detected.  */
+      if (check_overlap && detect_overlap (loc, stmt, dest, src, len))
+	return false;
+
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
 	{
@@ -1392,7 +1434,7 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
 	    tree op3 = gimple_assign_rhs3 (def_stmt);
 	    return get_range_strlen (op2, length, visited, type, fuzzy, flexp)
 	      && get_range_strlen (op3, length, visited, type, fuzzy, flexp);
-          }
+	  }
         return false;
 
       case GIMPLE_PHI:
@@ -1484,12 +1526,19 @@ static bool
 gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
 			    tree dest, tree src)
 {
-  location_t loc = gimple_location (gsi_stmt (*gsi));
+  gimple *stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
   tree fn;
 
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -2272,6 +2321,15 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
      (resp. DEST+LEN for __mempcpy_chk).  */
   if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
     {
+      if (fcode != BUILT_IN_MEMMOVE && fcode != BUILT_IN_MEMMOVE_CHK)
+	{
+	  tree func = gimple_call_fndecl (stmt);
+
+	  warning_at (loc, OPT_Wrestrict,
+		      "%qD source argument is the same as destination",
+		      func);
+	}
+
       if (fcode != BUILT_IN_MEMPCPY_CHK)
 	{
 	  replace_call_with_value (gsi, dest);
@@ -2373,6 +2431,12 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 488f8c8..f2dcece 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -359,6 +359,7 @@ gimple_build_call_from_tree (tree t)
     gimple_call_set_arg (call, i, CALL_EXPR_ARG (t, i));
 
   gimple_set_block (call, TREE_BLOCK (t));
+  gimple_set_location (call, EXPR_LOCATION (t));
 
   /* Carry all the CALL_EXPR flags to the new GIMPLE_CALL.  */
   gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t));
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
new file mode 100644
index 0000000..8e36bea
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -0,0 +1,589 @@
+/* PR 35503 - Warn about restricted pointers
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+size_t value (void);
+
+size_t range (size_t min, size_t max)
+{
+  size_t val = value ();
+  return val < min || max < val ? min : val;
+}
+
+#define R(min, max) range ((min), (max))
+
+
+void sink (void*, ...);
+
+/* Exercise memcpy with constant or known arguments.  */
+
+void test_memcpy_cst (void)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+    const void *s = 0;				\
+    memcpy ((dst), (src), (n));			\
+    sink (a, (dst), s);				\
+  } while (0)
+
+  T (a, a, 0);
+  T (a, s = a, 1);           /* { dg-warning "\\\[-Wrestrict" } */
+  T (a, a + 1, 1);
+  T (a, a + 1, 2);           /* { dg-warning "\\\[-Wrestrict" } */
+
+  {
+    char a[3][7];
+    sink (a);
+
+    void *d = a[0];
+    const void *s = a[1];
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0];
+    s = a[1];
+    memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&a);
+  }
+
+  {
+    struct {
+      char a[7];
+      char b[7];
+      char c[7];
+    } x;
+    sink (&x);
+
+    void *d = x.a;
+    const void *s = x.b;
+    memcpy (d, s, sizeof x.a);
+    sink (&x);
+
+    d = x.a;
+    s = x.a;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 4;
+    s = x.b;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 6;
+    s = x.b;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b;
+    memcpy (d, s, 1);             /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b + 1;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 1;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 2;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 3;
+    memcpy (d, s, sizeof x.b);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+  }
+
+  {
+#undef T
+#define T(dst, src, n) do {				\
+      char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+      memcpy ((dst), (src), (n));			\
+      sink (a);						\
+    } while (0)
+
+    /* Verify the offset of the overlap is the same regardless of whether
+       the destination is at lower or higher offset than the source.  */
+    T (a, a + 1, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 4 bytes at offset 1" } */
+    T (a, a + 2, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 3 bytes at offset 2" } */
+    T (a, a + 3, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 2 bytes at offset 3" } */
+
+    T (a + 1, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 8 overlaps 4 bytes at offset 1" } */
+    T (a + 2, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 7 overlaps 3 bytes at offset 2" } */
+    T (a + 3, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 6 overlaps 2 bytes at offset 3" } */
+  }
+}
+
+/* Exercise memcpy with destination or source offset or size in
+   a determinate range.  */
+
+void test_memcpy_range (void *d)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+    memcpy ((dst), (src), (n));			\
+    sink (a);					\
+  } while (0)
+
+  int r = R (2, 3);
+  T (a + r, a, 0);
+  T (a + r, a, 1);
+  T (a + r, a, 2);
+  T (a + r, a, 3);                /* { dg-warning "writing 3 bytes overlaps 1 byte at offset 2" } */
+
+  T (d + r, d, 0);
+  T (d + r, d, 1);
+  T (d + r, d, 2);
+  T (d + r, d, 3);                /* { dg-warning "writing 3 bytes overlaps 1 byte at offset 2" } */
+
+  /* Because the size is constant and a power of 2 the following is
+     folded too early to detect the overlap.  */
+  T (d + r, d, 4);                /* { dg-warning "writing 4 bytes overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+  T (d + r, d, 5);                /* { dg-warning "writing 5 bytes overlaps 3 bytes at offset 2" } */
+
+  T (a, a + 1, R (0, 1));
+  T (a, a + 1, R (0, 2));
+  T (a, a + 1, R (1, 2));
+  T (a, a + 1, R (2, 3));         /* { dg-warning "writing between 2 and 3 bytes into a region of size 9 overlaps 1 or more bytes at offset 1" } */
+  T (a, a + 1, R (2, -2));        /* { dg-warning "writing 2 or more bytes into a region of size 9 overlaps 1 or more bytes at offset 1" } */
+  T (a, a + 2, R (2, 3));
+  T (a, a + 2, R (3, 4));         /* { dg-warning "writing between 3 and 4 bytes into a region of size 9 overlaps 1 or more bytes at offset 2" } */
+  T (a, a + 3, R (3, 4));
+  T (a, a + 3, R (4, 5));         /* { dg-warning "writing between 4 and 5 bytes into a region of size 9 overlaps 1 or more bytes at offset 3" } */
+  T (a, a + 3, R (5, 6));         /* { dg-warning "writing between 5 and 6 bytes into a region of size 9 overlaps 2 or more bytes at offset 3" } */
+
+  T (a + 1, a, R (0, 1));
+  T (a + 1, a, R (0, 2));
+  T (a + 1, a, R (1, 2));
+  T (a + 1, a, R (2, 3));         /* { dg-warning "writing between 2 and 3 bytes into a region of size 8 overlaps 1 or more bytes at offset 1" } */
+  T (a + 1, a, R (2, -2));        /* { dg-warning "writing 2 or more bytes into a region of size 8 overlaps 1 or more bytes at offset 1" } */
+  T (a + 2, a, R (2, 3));
+  T (a + 2, a, R (3, 4));         /* { dg-warning "writing between 3 and 4 bytes into a region of size 7 overlaps 1 or more bytes at offset 2" } */
+  T (a + 3, a, R (3, 4));
+  T (a + 3, a, R (4, 5));         /* { dg-warning "writing between 4 and 5 bytes into a region of size 6 overlaps 1 or more bytes at offset 3" } */
+  T (a + 3, a, R (5, 6));         /* { dg-warning "writing between 5 and 6 bytes into a region of size 6 overlaps 2 or more bytes at offset 3" } */
+
+  /* Verify offset and size both in some range.  The memcpy checking
+     is less strict than that of string functions like strncpy and
+     doesn't trigger unless the overlap is certain.  The following
+     overlaps for (r == 3 && n > 3) but not, for example, for
+     (r == 4 && n == 4), and so it's not diagnosed.  */
+  r = R (3, 5);
+  T (a, a + r, R (4, 6));
+  /* Ditto for objects of unknown sizes.  */
+  T (d, d + r, R (4, 6));
+}
+
+/* Exercise memcpy with destination and source of unknown size.  */
+
+void test_memcpy_var (char *d, const char *s)
+{
+  int n = value ();
+
+  memcpy (d, d, 0);
+  sink (d);
+
+  memcpy (d, d, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  memcpy (d, &d[0], n);           /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  memcpy (&d[0], d,  n);          /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  /* The following overlaps if n is greater than 1.  */
+  s = d + 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  /* The following only overlaps if strlen (s + i) >= i so it's not
+     diagnosed.  */
+  s = d + n;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 2;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 3;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 4;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  s = d + 5;
+  n = 7;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+
+  n = range (0, 1);
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+/* Exercise the absence of warnings with memmove.  */
+
+void test_memmove (void)
+{
+  {
+    char d[7];
+    sink (d);
+
+    const void *s = d;
+    memmove (d, s, 7);
+    sink (d);
+
+    s = d + 1;
+    memmove (d, s, 6);
+    sink (d);
+
+    s = d + 2;
+    memmove (d + 1, s, 5);
+    sink (d);
+  }
+}
+
+/* Exercise strcat with constant or known arguments.  */
+
+void test_strcat_cst (const char *s)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[9] = init;				\
+    strcat ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2);            /* { dg-warning "\\\[-Wrestrict" } */
+  /* The nul copied from a[3] to a[3] overwrites itself so this is
+     diagnosed.  */
+  T ("123", a, a + 3);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 4);
+  T ("123", a, a + 5);
+  T ("123", a, a + 6);
+  T ("123", a, a + 7);
+  T ("123", a, a + 8);
+
+  T ("123", a + 1, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 2, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 3, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 4, a);            /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+  T ("123", a + 5, a);            /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T ("123",      a, "123");
+  T ("123",      a, s);
+  T ("12345678", a, s);
+}
+
+/* Exercise strcat with destination and source of unknown length.  */
+
+void test_strcat_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    strcat ((dst), (src));			\
+    sink ((dst));				\
+  } while (0)
+
+  T (d, d);                       /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 1);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 2);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 3);
+
+  int n = value ();
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T (d, "123");
+  T (d + 1, "1234");
+  T (d + n, "12345");
+  T (d, s);
+  T (d + 1, s);
+  T (d + n, s);
+
+  /* This one isn't so obvious and might be worth diagnosing with "may
+     overlap," even more so than the analogous strcpy case.  */
+  T (d, d + n);
+}
+
+/* Exercise strcpy with constant or known arguments.  */
+
+void test_strcpy_cst (void)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[8] = init;				\
+    strcpy ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2);
+  T ("123", a, a + 3);
+
+  /* The terminating nul written to d[2] overwrites s[0].  */
+  T ("1234", a, a + 2);           /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("12345", a, a + 2);          /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* It's not 100% clear if this should trigger a warning.  The non-const
+     string case doesn't.  */
+  T ("123", a, a + value ());
+
+  /* This happens to be safe in GCC but it's still wrong.  */
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("123", a + 1, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 2, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 3, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 4, a);
+}
+
+/* Exercise strcpy with constant or known arguments offset by a range.
+   The tests verify the use of the lower bound of the range which is
+   more restrictive than using the upper bound for positive values.  */
+
+void test_strcpy_range (void)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[8] = init;				\
+    strcpy ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  /* The range needs to be an int variable and not a size_t here,
+     otherwise the range info in the strlen pass is not good enough
+     to detect these cases.  */
+  int r = R (3, 4);
+  T ("12",  a + r, a);
+  T ("123", a + r, a);            /* { dg-warning "writing 4 bytes overlaps 1 byte at offset 3" } */
+
+  T ("1234", a + r, a);           /* { dg-warning "writing 5 bytes overlaps 2 bytes at offset 3" } */
+
+  T ("12",     a, a + r);
+  T ("123",    a, a + r);
+  T ("1234",   a, a + r);
+  T ("12345",  a, a + r);
+
+  /* The final NUL overlaps the '4' at a[3].  */
+  T ("123456", a, a + r);         /* { dg-warning "writing between 3 and 4 bytes may overlap 1 byte at offset 3" } */
+  T ("1234567", a, a + r);        /* { dg-warning "writing between 4 and 5 bytes overlaps 2 bytes at offset 3" } */
+
+  r = R (2, 5);
+  T ("1234", a + r, a);           /* { dg-warning "writing 5 bytes overlaps 3 bytes at offset 2" } */
+
+
+}
+
+
+/* Exercise strcpy with destination and source of unknown length.  */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    strcpy ((dst), (src));			\
+    sink (dst);					\
+  } while (0)
+
+  T (d, s);
+
+  T (d, &d[0]);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (&d[0], d);                   /* { dg-warning "\\\[-Wrestrict" } */
+
+  s = d;
+  T (d, s);                       /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* The following overlaps if *s is not nul.  It arguably should be
+     diagnosed.  */
+  T (d, d + 1);
+
+  /* The following overlaps if strlen (d) is greater than 1.  Like
+     the above, it possibly should be diagnosed too.  */
+  int r = R (2, 3);
+  T (d, d + r);
+
+  /* The following overlaps only if strlen (s + n) >= n so it's not
+     diagnosed.  */
+  s = d + value ();
+  T (d, s);
+}
+
+/* Exercise strncpy with constant or known arguments.  */
+
+void test_strncpy_cst (void)
+{
+#undef T
+#define T(init, dst, src, size) do {		\
+    char a[7] = init;				\
+    strncpy (dst, src, size);			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a, 0);
+  T ("123", a, a, 1);             /* { dg-warning "\\\[-Wrestrict" } */
+  /* The following overlaps except in the unlikely case that value ()
+     is zero, so it's diagnosed.  */
+  T ("123", a, a, value ());      /* { dg-warning "writing into a region of size 7 may overlap at offset 0" } */
+  T ("123", a, a + 1, 1);
+  T ("123", a, a + 1, 2);         /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1, 3);         /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2, 1);
+  T ("123", a, a + 2, 2);
+  /* The third written byte (nul) overwrites a[2].  */
+  T ("123", a, a + 2, 3);         /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("1234", a, a + 2, 1);
+  T ("1234", a, a + 2, 2);
+  /* The terminating nul written to a[2] overwrites s[0].  */
+  T ("1234", a, a + 2, 3);        /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("12345", a, a + 2, 1);
+  T ("12345", a, a + 2, 2);
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("12345", a, a + 2, 4);       /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+
+/* Exercise strncpy with one or more arguments in a determinate range.  */
+
+void test_strncpy_range (char *d)
+{
+#undef T
+#define T(init, dst, src, size) do {		\
+    char a[9] = init;				\
+    strncpy ((dst), (src), (size));		\
+    sink (a);					\
+  } while (0)
+
+  /* Verify offset and size both in some range.  The strncpy checking
+     is more strict than that of memcpy and triggers even when the
+     overlap is possible but not inevitable.  The following overlaps
+     for (r == 3 && n > 3) but not, for example, for (r == 4 && n == 4),
+     but it's still diagnosed.  */
+  int r = R (3, 5);
+  T ("12345678", a, a + r, R (4, 6));   /* { dg-warning "writing between 4 and 6 bytes into a region of size 9 may overlap 1 or more bytes at offset 3 - 5" } */
+
+  /* Ditto for objects of unknown sizes.  Verify also that since it's
+     unknown the size isn't printed.  */
+  T ("12345678", d, d + r, R (4, 6));  /* { dg-warning "writing between 4 and 6 bytes may overlap 1 or more bytes at offset 3 - 5" } */
+
+  T ("12345678", a, a + r, R (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 9 overlaps 3 or more bytes at offset 3 - 5" } */
+}
+
+
+/* Exercise strncpy with destination and source of unknown length.  */
+
+void test_strncpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src, size) do {			\
+    strncpy (dst, src, size);			\
+    sink ((dst));				\
+  } while (0)
+
+  int n = value ();
+
+  T (d, s, 1);
+  T (d, s, n);
+
+  T (d, d, 1);                    /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d, n);                    /* { dg-warning "\\\[-Wrestrict" } */
+
+  T (d,     d + 1, 1);
+  T (d,     d + 1, 2);            /* { dg-warning "\\\[-Wrestrict" } */
+  T (d + 1, d,     1);
+  T (d + 1, d,     2);            /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+struct MemberArrays
+{
+  char a[8];
+  char b[8];
+  char c[9];
+};
+
+void test_strncpy_strcpy_var (struct MemberArrays *ar, const char *s)
+{
+  /* The following is safe and should not trigger a warning.  */
+  strncpy (ar->b, s, sizeof ar->b - 1);
+  ar->b[sizeof ar->b - 1] = '\0';
+  strcpy (ar->a, ar->b);
+  sink (ar);
+
+  /* The following is not as safe (it might overflow ar->a) but there
+     is no overlap so it also shouldn't trigger -Wrestrict.  */
+  strncpy (ar->c, s, sizeof ar->c - 1);
+  ar->c[sizeof ar->c - 1] = '\0';
+  strcpy (ar->a, ar->c);
+  sink (ar);
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c b/gcc/testsuite/gcc.dg/Walloca-1.c
index ad39373..85e9160 100644
--- a/gcc/testsuite/gcc.dg/Walloca-1.c
+++ b/gcc/testsuite/gcc.dg/Walloca-1.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* { dg-require-effective-target alloca } */
-/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+/* { dg-options "-Walloca-larger-than=2000 -O2 -ftrack-macro-expansion=0" } */
 
 #define alloca __builtin_alloca
 
diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c
index c0e7463..908d5a6 100644
--- a/gcc/testsuite/gcc.dg/pr69172.c
+++ b/gcc/testsuite/gcc.dg/pr69172.c
@@ -1,4 +1,5 @@
-/* PR tree-optimization/69172 */
+/* PR tree-optimization/69172 - ICE in make_ssa_name_fn,
+   at tree-ssanames.c:266 */
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
 
@@ -43,3 +44,7 @@ f6 (int x)
 {
   return __builtin___mempcpy_chk (&a, &a, x, 0);
 }
+
+/* The calls above violate strict aliasing.  Eliminate the -Wrestrict
+   warnings they trigger.
+  { dg-prune-output "\\\[-Wrestrict]" } */
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 74ee2b0..1b46581 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -38,6 +38,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-dfa.h"
 #include "ipa-reference.h"
 #include "varasm.h"
+#include "diagnostic-core.h"
+#include "intl.h"
 
 /* Broad overview of how alias analysis on gimple works:
 
@@ -677,10 +679,15 @@ ao_ref_alias_set (ao_ref *ref)
    to or after of the pointer target, not before it.  */
 
 void
-ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size)
+ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size,
+			       unsigned HOST_WIDE_INT offrng[2] /* = NULL */)
 {
   HOST_WIDE_INT t, size_hwi, extra_offset = 0;
-  ref->ref = NULL_TREE;
+
+  if (offrng)
+    offrng[0] = offrng[1] = 0;
+
+ ref->ref = NULL_TREE;
   if (TREE_CODE (ptr) == SSA_NAME)
     {
       gimple *stmt = SSA_NAME_DEF_STMT (ptr);
@@ -688,12 +695,30 @@ ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size)
 	  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
 	ptr = gimple_assign_rhs1 (stmt);
       else if (is_gimple_assign (stmt)
-	       && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
-	       && TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST)
+	       && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
 	{
-	  ptr = gimple_assign_rhs1 (stmt);
-	  extra_offset = BITS_PER_UNIT
-			 * int_cst_value (gimple_assign_rhs2 (stmt));
+	  tree offset = gimple_assign_rhs2 (stmt);
+	  if (TREE_CODE (offset) == INTEGER_CST)
+	    {
+	      ptr = gimple_assign_rhs1 (stmt);
+	      extra_offset = BITS_PER_UNIT * int_cst_value (offset);
+	    }
+	  else if (offrng && TREE_CODE (offset) == SSA_NAME)
+	    {
+	      wide_int min, max;
+	      value_range_type rng = get_range_info (offset, &min, &max);
+	      if (rng == VR_RANGE && wi::fits_uhwi_p (min))
+		{
+		  ptr = gimple_assign_rhs1 (stmt);
+		  offrng[0] = BITS_PER_UNIT * min.to_uhwi ();
+		  offrng[1] = BITS_PER_UNIT * max.to_uhwi ();
+
+		  extra_offset = offrng[0];
+		}
+	      else
+		/* Offset range is indeterminate.  */
+		offrng[0] = offrng[1] = HOST_WIDE_INT_M1U;
+	    }
 	}
     }
 
@@ -2945,3 +2970,145 @@ walk_aliased_vdefs (ao_ref *ref, tree vdef,
   return ret;
 }
 
+/* Return the size of the overlap in bytes if REF1 and REF2 alias one
+   another, and set *ALOFF (alias offset) to the offset of the overlap.
+   in REF1.  Otherwise, if REF1 and REF2 do not definitely overlap,
+   return zero.  Used for -Wrestrict warnings.  */
+
+unsigned HOST_WIDE_INT
+refs_overlap (ao_ref *ref1, ao_ref *ref2, unsigned HOST_WIDE_INT *aloff)
+{
+  tree base1 = ao_ref_base (ref1);
+  tree base2 = ao_ref_base (ref2);
+
+  if (!base1 || !base2)
+    return 0;
+
+  offset_int offset1 = ref1->offset;
+  offset_int offset2 = ref2->offset;
+
+  if (TREE_CODE (base1) == MEM_REF)
+    {
+      if (TREE_CODE (base2) != MEM_REF)
+	return 0;
+
+      // Compare pointers.
+      offset1 += mem_ref_offset (base1) << LOG2_BITS_PER_UNIT;
+      offset2 += mem_ref_offset (base2) << LOG2_BITS_PER_UNIT;
+      base1 = TREE_OPERAND (base1, 0);
+      base2 = TREE_OPERAND (base2, 0);
+    }
+
+  if (base1 != base2)
+    return 0;
+
+  if (offset1 <= offset2)
+    {
+      *aloff = (offset2 - offset1).to_uhwi () >> LOG2_BITS_PER_UNIT;
+      if (ref1->size == -1)
+	return -1;
+
+      offset_int end1 = offset1 + ref1->size;
+      if (end1 > offset2)
+	return (end1 - offset2).to_uhwi () >> LOG2_BITS_PER_UNIT;
+    }
+  if (offset2 <= offset1)
+    {
+      *aloff = (offset1 - offset2).to_uhwi () >> LOG2_BITS_PER_UNIT;
+      if (ref2->size == -1)
+	return -1;
+
+      offset_int end2 = offset2 + ref2->size;
+      if (end2 > offset1)
+	return (end2 - offset1).to_uhwi () >> LOG2_BITS_PER_UNIT;
+    }
+
+  return 0;
+}
+
+/* Attempt to detect and diagnose overlapping copy in a statement STMT
+   from SRC to DST of SIZE bytes.  When ADJUST is set, adjust SIZE to
+   reflect the lower bound of the non-constant SRC offset.  Return true
+   when overlap has been detected, false otherwise.
+   In the case of a non-constant offset, using a positive lower bound
+   is more strict than using the upper bound.  */
+
+bool
+detect_overlap (location_t loc, gimple *stmt, tree dst, tree src, tree size,
+		bool adjust /* = false */)
+{
+  ao_ref dstref, srcref;
+  unsigned HOST_WIDE_INT range[2];
+
+  /* Initialize and store the lower bound of a constant offset (in
+     bits), disregarding the offset for the destination.  */
+  ao_ref_init_from_ptr_and_size (&dstref, dst, size, range);
+  ao_ref_init_from_ptr_and_size (&srcref, src, size, range);
+
+  if (adjust)
+    {
+      /* Adjust the source and destination sizes (in bits) by
+	 the non-constant offset (also in bits).  */
+      dstref.size -= range[0];
+      srcref.size -= range[0];
+    }
+
+  /* Determine the size of the OVERLAP, if any, and its OFFSET, both
+     in bytes.  */
+  unsigned HOST_WIDE_INT offset;
+  unsigned HOST_WIDE_INT overlap = refs_overlap (&dstref, &srcref, &offset);
+
+  if (!overlap)
+    return false;
+
+  /* Convert the size from bits to bytes (OFFSET is in bytes).  */
+
+  range[1] = (dstref.size - (range[1] - range[0])) / BITS_PER_UNIT;
+  range[0] = dstref.size / BITS_PER_UNIT;
+
+  if (range[0] > range[1])
+    std::swap (range[0], range[1]);
+
+  bool warned;
+
+  tree func = gimple_call_fndecl (stmt);
+
+  /* Below distinguish three cases:
+     1) certain constant overlap
+     2) certain overlap in some range
+     3) possible overlap.  */
+  if (range[0] == range[1])
+    warned = warning_at (loc, OPT_Wrestrict,
+			 range[0] == 1
+			 ? (overlap == 1
+			    ? G_("%qD writing %wu byte overlaps %wu byte "
+				 "at offset %wu")
+			    : G_("%qD writing %wu byte overlaps %wu bytes "
+				 "at offset %wu"))
+			 : (overlap == 1
+			    ? G_("%qD writing %wu bytes overlaps %wu byte "
+				 "at offset %wu")
+			    : G_("%qD writing %wu bytes overlaps %wu bytes "
+				 "at offset %wu")),
+			 func, range[0], overlap, offset);
+  else if (range[1] - range[0] < overlap)
+    warned = warning_at (loc, OPT_Wrestrict,
+			 overlap == 1
+			 ? G_("%qD writing between %wu and %wu bytes "
+			      "overlaps %wu byte at offset %wu")
+			 : G_("%qD writing between %wu and %wu bytes "
+			      "overlaps %wu bytes at offset %wu"),
+			 func, range[0], range[1], overlap, offset);
+  else
+    warned = warning_at (loc, OPT_Wrestrict,
+			 overlap == 1
+			 ? G_("%qD writing between %wu and %wu bytes "
+			      "may overlap %wu byte at offset %wu")
+			 : G_("%qD writing %wu byte may overlap %wu bytes "
+			      "at offset %wu"),
+			 func, range[0], range[1], overlap, offset);
+  if (warned)
+    gimple_set_no_warning (stmt, true);
+
+  return true;
+}
diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
index 8c89c69..f15dd17 100644
--- a/gcc/tree-ssa-alias.h
+++ b/gcc/tree-ssa-alias.h
@@ -99,7 +99,8 @@ struct ao_ref
 
 /* In tree-ssa-alias.c  */
 extern void ao_ref_init (ao_ref *, tree);
-extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree);
+extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree,
+					   unsigned HOST_WIDE_INT[2] = NULL);
 extern tree ao_ref_base (ao_ref *);
 extern alias_set_type ao_ref_alias_set (ao_ref *);
 extern alias_set_type ao_ref_base_alias_set (ao_ref *);
@@ -145,6 +146,10 @@ extern void dump_points_to_info_for (FILE *, tree);
 extern void debug_points_to_info_for (tree);
 extern void dump_alias_stats (FILE *);
 
+extern unsigned HOST_WIDE_INT refs_overlap (ao_ref *, ao_ref *,
+					    unsigned HOST_WIDE_INT *);
+extern bool detect_overlap (location_t, gimple *, tree, tree, tree,
+			    bool = false);
 
 /* In tree-ssa-structalias.c  */
 extern unsigned int compute_may_aliases (void);
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index b0563fe..75c01b0 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "expr.h"
 #include "tree-dfa.h"
 #include "domwalk.h"
+#include "tree-ssa-alias.h"
 #include "tree-ssa-propagate.h"
 #include "params.h"
 #include "ipa-chkp.h"
@@ -1477,6 +1478,22 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 	    }
 	}
       dsi->stmt = stmt;
+
+      /* Try to detect overlap before returning.  This catches cases
+	 like strcpy (d, d + n) where n is non-constant whose range
+	 is such that (n <= strlen (d) holds).
+
+	 OLDDSI->NONZERO_chars may have been reset by this point with
+	 oldlen holding it original value.  */
+      if (olddsi && oldlen)
+	{
+	  /* Add 1 for the terminating NUL.  */
+	  tree type = TREE_TYPE (oldlen);
+	  oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
+				build_int_cst (type, 1));
+	  detect_overlap (loc, stmt, olddsi->ptr, src, oldlen, true);
+	}
+
       return;
     }
 
@@ -1559,6 +1576,12 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
   len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
 				  GSI_SAME_STMT);
+
+  if (const strinfo *chksi = olddsi ? olddsi : dsi)
+    if (si && detect_overlap (loc, stmt, chksi->ptr, si->ptr, len, true))
+      /* Avoid transforming strcpy to memcpy when overlap is detected.  */
+      return;
+
   if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     {
       fprintf (dump_file, "Optimizing: ");
@@ -1786,7 +1809,7 @@ static void
 handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr;
+  tree src, dst, srclen, dstlen, lhs, args, type, fn, objsz, endptr;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi;
@@ -1804,10 +1827,43 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   dsi = NULL;
   if (didx > 0)
     dsi = get_strinfo (didx);
+
+  srclen = NULL_TREE;
+  si = NULL;
+  idx = get_stridx (src);
+  if (idx < 0)
+    srclen = build_int_cst (size_type_node, ~idx);
+  else if (idx > 0)
+    {
+      si = get_strinfo (idx);
+      if (si != NULL)
+	srclen = get_string_length (si);
+    }
+
+  loc = gimple_location (stmt);
+
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
+      if (warn_restrict)
+	{
+	  /* The concatenation always involves copying at least one byte
+	     (the terminating nul), even if the source string is empty.
+	     If the source is unknown assume it's one character long and
+	     compute the size of the result.  */
+	  tree ressize = srclen ? srclen : size_one_node;
+	  tree type = TREE_TYPE (ressize);
+	  ressize = fold_build2 (PLUS_EXPR, type, ressize, ressize);
+	  ressize = fold_build2 (PLUS_EXPR, type, ressize,
+				 build_int_cst (type, 1));
+
+	  tree sptr = si && si->ptr ? si->ptr : src;
+	  if (detect_overlap (loc, stmt, dst, sptr, ressize, true))
+	    /* Avoid transforming overlapping strcat.  */
+	    return;
+	}
+
       /* strcat (p, q) can be transformed into
-	 tmp = p + strlen (p); endptr = strpcpy (tmp, q);
+	 tmp = p + strlen (p); endptr = stpcpy (tmp, q);
 	 with length endptr - p if we need to compute the length
 	 later on.  Don't do this transformation if we don't need
 	 it.  */
@@ -1840,19 +1896,6 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       return;
     }
 
-  srclen = NULL_TREE;
-  si = NULL;
-  idx = get_stridx (src);
-  if (idx < 0)
-    srclen = build_int_cst (size_type_node, ~idx);
-  else if (idx > 0)
-    {
-      si = get_strinfo (idx);
-      if (si != NULL)
-	srclen = get_string_length (si);
-    }
-
-  loc = gimple_location (stmt);
   dstlen = dsi->nonzero_chars;
   endptr = dsi->endptr;
 
@@ -1914,7 +1957,22 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (fn == NULL_TREE)
     return;
 
-  len = NULL_TREE;
+  if (warn_restrict && dsi && dstlen)
+    {
+      tree slen = srclen ? srclen : size_zero_node;
+      /* Compute the size of the concatenated string, including
+	 the terminating nul.  */
+      tree type = TREE_TYPE (dstlen);
+      tree ressize = fold_build2 (PLUS_EXPR, type, dstlen, slen);
+      ressize = fold_build2 (PLUS_EXPR, type, ressize, build_int_cst (type, 1));
+
+      tree sptr = si && si->ptr ? si->ptr : src;
+      if (detect_overlap (loc, stmt, dst, sptr, ressize, true))
+	/* Avoid transforming overlapping strcat.  */
+	return;
+    }
+
+  tree len = NULL_TREE;
   if (srclen != NULL_TREE)
     {
       args = TYPE_ARG_TYPES (TREE_TYPE (fn));

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-22  2:04                   ` Martin Sebor
@ 2017-08-22  9:17                     ` Richard Biener
  2017-08-24 22:36                       ` Jeff Law
  2017-08-29  2:35                       ` Martin Sebor
  0 siblings, 2 replies; 39+ messages in thread
From: Richard Biener @ 2017-08-22  9:17 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jeff Law, Gcc Patch List

On Mon, Aug 21, 2017 at 10:10 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 08/09/2017 10:14 AM, Jeff Law wrote:
>>
>> On 08/06/2017 05:08 PM, Martin Sebor wrote:
>>
>>>>
>>>> Well, simply because the way as implemented isn't a must-alias query
>>>> but maybe one that's good enough for warnings (reduces false positives
>>>> but surely doesn't eliminate them).
>>>
>>>
>>> I'm very interested in reducing the rate of false positives in
>>> these and all other warnings.  As I mentioned in my comments,
>>> I did my best to weed them out of the implementation by building
>>> GDB, Glibc, Busybox, and the Linux kernel.  That of course isn't
>>> a guarantee that there aren't any.  But the first implementation
>>> of any non-trivial feature is never perfect, and hardly any
>>> warning of sufficient complexity is free of false positives, no
>>> matter here it's implemented (the middle-end, front-end, or
>>> a standalone static analysis tool).  If you spotted some cases
>>> I had missed I'd certainly be grateful for examples.  Otherwise,
>>> they will undoubtedly be reported as more software is exposed
>>> to the warning and, if possible, fixed, as happens with all
>>> other warnings.
>>
>> I think Richi is saying that the must alias query you've built isn't
>> proper/correct.  It's less about false positives for Richi and more
>> about building a proper must alias query if I understand him correctly.
>>
>> I suspect he's also saying that you can't reasonably build must-alias on
>> top of a may-alias query framework.  They're pretty different queries.
>>
>> If you need something that is close to, but not quite a must alias
>> query, then you're going to have to make a argument for that and you
>> can't call it a must alias query.
>
>
> Attached is an updated and simplified patch that avoids making
> changes to any of the may-alias functions.  It turns out that
> all the information the logic needs to determine the overlap
> is already in the ao_ref structures populated by
> ao_ref_init_from_ptr_and_size.  The only changes to the pass
> are the enhancement to ao_ref_init_from_ptr_and_size to make
> use of range info and the addition of the two new functions
> used by the -Wrestrict clients outside the pass.

Warning for memcpy (p, p, ...) is going to fire false positives all around
given the C++ FE emits those in some cases and optimization can
expose that we are dealing with self-assignments.  And *p = *p is
valid.

@@ -1028,6 +1066,10 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
            }
        }

+      /* Avoid folding the call if overlap is detected.  */
+      if (check_overlap && detect_overlap (loc, stmt, dest, src, len))
+       return false;
+

no, please not.  You diagnosed the call (which might be a false positive)
so why keep it undefined?  The folded stmt will either have the same
semantics (aggregate copies expanded as memcpy) or have all reads
performed before writes.

The ao_ref_init_from_ptr_and_size change misses a changelog entry.

+detect_overlap (location_t loc, gimple *stmt, tree dst, tree src, tree size,
+               bool adjust /* = false */)
+{
+  ao_ref dstref, srcref;
+  unsigned HOST_WIDE_INT range[2];
+
+  /* Initialize and store the lower bound of a constant offset (in
+     bits), disregarding the offset for the destination.  */
+  ao_ref_init_from_ptr_and_size (&dstref, dst, size, range);
+  ao_ref_init_from_ptr_and_size (&srcref, src, size, range);

just pass NULL range to the first call?

-  ref->ref = NULL_TREE;
+
+  if (offrng)
+    offrng[0] = offrng[1] = 0;
+
+ ref->ref = NULL_TREE;

bogus indent

+         else if (offrng && TREE_CODE (offset) == SSA_NAME)
+           {
+             wide_int min, max;
+             value_range_type rng = get_range_info (offset, &min, &max);
+             if (rng == VR_RANGE && wi::fits_uhwi_p (min))
+               {
+                 ptr = gimple_assign_rhs1 (stmt);
+                 offrng[0] = BITS_PER_UNIT * min.to_uhwi ();
+                 offrng[1] = BITS_PER_UNIT * max.to_uhwi ();
+
+                 extra_offset = offrng[0];

you didnt' check whether max fits uhwi.  The effect of passing offrng
is not documented.

+             else
+               /* Offset range is indeterminate.  */
+               offrng[0] = offrng[1] = HOST_WIDE_INT_M1U;

I believe a cleaner interface would be to do

void
ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size, tree
*var_byte_offset)

and set *var_byte_offset to your 'offset' above, leaving 'ref'
unchanged.  The caller
can then get at the range info of var_byte_offset and adjust
ref->offset if desired.
The indeterminate state is then much cleaner - NULL_TREE.

+unsigned HOST_WIDE_INT
+refs_overlap (ao_ref *ref1, ao_ref *ref2, unsigned HOST_WIDE_INT *aloff)
+{

bool
refs_must_overlap_p (ao_ref *, ao_ref *, unsigned HOST_WIDE_INT *off,
unsinged HOST_WIDE_INT *size)

your return values are in bytes thus

+
+      // Compare pointers.
+      offset1 += mem_ref_offset (base1) << LOG2_BITS_PER_UNIT;
+      offset2 += mem_ref_offset (base2) << LOG2_BITS_PER_UNIT;
+      base1 = TREE_OPERAND (base1, 0);

just do the intermediate computations in bytes as well.

It looks like it is unspecified relative to which ref the offset is given,
how's that useful information then -- the diagnostic doesn't seem
too helpful?  Why not keep it relative to the first ref and thus make
aloff signed?

detect_overlap doesn't belong to tree-ssa-alias.[ch].

(didn't look at the diagnostic parts)

Thanks,
Richard.


> Martin

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-22  9:17                     ` Richard Biener
@ 2017-08-24 22:36                       ` Jeff Law
  2017-08-29  3:42                         ` Martin Sebor
  2017-08-29  2:35                       ` Martin Sebor
  1 sibling, 1 reply; 39+ messages in thread
From: Jeff Law @ 2017-08-24 22:36 UTC (permalink / raw)
  To: Richard Biener, Martin Sebor; +Cc: Gcc Patch List

On 08/22/2017 02:45 AM, Richard Biener wrote:
> On Mon, Aug 21, 2017 at 10:10 PM, Martin Sebor <msebor@gmail.com> wrote:
>> On 08/09/2017 10:14 AM, Jeff Law wrote:
>>>
>>> On 08/06/2017 05:08 PM, Martin Sebor wrote:
>>>
>>>>>
>>>>> Well, simply because the way as implemented isn't a must-alias query
>>>>> but maybe one that's good enough for warnings (reduces false positives
>>>>> but surely doesn't eliminate them).
>>>>
>>>>
>>>> I'm very interested in reducing the rate of false positives in
>>>> these and all other warnings.  As I mentioned in my comments,
>>>> I did my best to weed them out of the implementation by building
>>>> GDB, Glibc, Busybox, and the Linux kernel.  That of course isn't
>>>> a guarantee that there aren't any.  But the first implementation
>>>> of any non-trivial feature is never perfect, and hardly any
>>>> warning of sufficient complexity is free of false positives, no
>>>> matter here it's implemented (the middle-end, front-end, or
>>>> a standalone static analysis tool).  If you spotted some cases
>>>> I had missed I'd certainly be grateful for examples.  Otherwise,
>>>> they will undoubtedly be reported as more software is exposed
>>>> to the warning and, if possible, fixed, as happens with all
>>>> other warnings.
>>>
>>> I think Richi is saying that the must alias query you've built isn't
>>> proper/correct.  It's less about false positives for Richi and more
>>> about building a proper must alias query if I understand him correctly.
>>>
>>> I suspect he's also saying that you can't reasonably build must-alias on
>>> top of a may-alias query framework.  They're pretty different queries.
>>>
>>> If you need something that is close to, but not quite a must alias
>>> query, then you're going to have to make a argument for that and you
>>> can't call it a must alias query.
>>
>>
>> Attached is an updated and simplified patch that avoids making
>> changes to any of the may-alias functions.  It turns out that
>> all the information the logic needs to determine the overlap
>> is already in the ao_ref structures populated by
>> ao_ref_init_from_ptr_and_size.  The only changes to the pass
>> are the enhancement to ao_ref_init_from_ptr_and_size to make
>> use of range info and the addition of the two new functions
>> used by the -Wrestrict clients outside the pass.
> 
> Warning for memcpy (p, p, ...) is going to fire false positives all around
> given the C++ FE emits those in some cases and optimization can
> expose that we are dealing with self-assignments.  And *p = *p is
> valid.
Correct.  I wound my way through this mess a while back.  Essentially
Red Hat had a customer with code that had overlapped memcpy arguments.
We had them use the memstomp interposition library to start tracking
these problems down.

One of the things that popped up was structure/class copies which were
implemented via calls to memcpy.    In the case of self assignment, the
interposition library would note the overlap and (rightly IMHO) complain.


One could argue that GCC should emit memmove by default for structure
assignments, only using memcpy when it knows its not doing self
assignment (which may be hard to determine).  Furthermore, GCC should
eliminate self structure/class assignment.


> 
> @@ -1028,6 +1066,10 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
>             }
>         }
> 
> +      /* Avoid folding the call if overlap is detected.  */
> +      if (check_overlap && detect_overlap (loc, stmt, dest, src, len))
> +       return false;
> +
> 
> no, please not.  You diagnosed the call (which might be a false positive)
> so why keep it undefined?  The folded stmt will either have the same
> semantics (aggregate copies expanded as memcpy) or have all reads
> performed before writes.
So can we distinguish here between overlap and the self-copy case?

A self-copy should just be folded away.  It's no different than x = x on
scalars except that we've dropped it to a memcpy in the IL.  Doing so
makes the code more efficient and removes false positives from tools
like the memstomp interposition library, making those tools more useful.




Jeff

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-22  9:17                     ` Richard Biener
  2017-08-24 22:36                       ` Jeff Law
@ 2017-08-29  2:35                       ` Martin Sebor
  2017-08-29 11:32                         ` Richard Biener
  1 sibling, 1 reply; 39+ messages in thread
From: Martin Sebor @ 2017-08-29  2:35 UTC (permalink / raw)
  To: Richard Biener; +Cc: Jeff Law, Gcc Patch List

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

On 08/22/2017 02:45 AM, Richard Biener wrote:
> On Mon, Aug 21, 2017 at 10:10 PM, Martin Sebor <msebor@gmail.com> wrote:
>> On 08/09/2017 10:14 AM, Jeff Law wrote:
>>>
>>> On 08/06/2017 05:08 PM, Martin Sebor wrote:
>>>
>>>>>
>>>>> Well, simply because the way as implemented isn't a must-alias query
>>>>> but maybe one that's good enough for warnings (reduces false positives
>>>>> but surely doesn't eliminate them).
>>>>
>>>>
>>>> I'm very interested in reducing the rate of false positives in
>>>> these and all other warnings.  As I mentioned in my comments,
>>>> I did my best to weed them out of the implementation by building
>>>> GDB, Glibc, Busybox, and the Linux kernel.  That of course isn't
>>>> a guarantee that there aren't any.  But the first implementation
>>>> of any non-trivial feature is never perfect, and hardly any
>>>> warning of sufficient complexity is free of false positives, no
>>>> matter here it's implemented (the middle-end, front-end, or
>>>> a standalone static analysis tool).  If you spotted some cases
>>>> I had missed I'd certainly be grateful for examples.  Otherwise,
>>>> they will undoubtedly be reported as more software is exposed
>>>> to the warning and, if possible, fixed, as happens with all
>>>> other warnings.
>>>
>>> I think Richi is saying that the must alias query you've built isn't
>>> proper/correct.  It's less about false positives for Richi and more
>>> about building a proper must alias query if I understand him correctly.
>>>
>>> I suspect he's also saying that you can't reasonably build must-alias on
>>> top of a may-alias query framework.  They're pretty different queries.
>>>
>>> If you need something that is close to, but not quite a must alias
>>> query, then you're going to have to make a argument for that and you
>>> can't call it a must alias query.
>>
>>
>> Attached is an updated and simplified patch that avoids making
>> changes to any of the may-alias functions.  It turns out that
>> all the information the logic needs to determine the overlap
>> is already in the ao_ref structures populated by
>> ao_ref_init_from_ptr_and_size.  The only changes to the pass
>> are the enhancement to ao_ref_init_from_ptr_and_size to make
>> use of range info and the addition of the two new functions
>> used by the -Wrestrict clients outside the pass.
>
> Warning for memcpy (p, p, ...) is going to fire false positives all around
> given the C++ FE emits those in some cases and optimization can
> expose that we are dealing with self-assignments.  And *p = *p is
> valid.

I changed it to only warn for calls to the library function and
not to the __builtin_xxx.  Though I haven't been able to reproduce
the problem you referring to (bug 32667) which makes me wonder if
it's been fixed.

>
> @@ -1028,6 +1066,10 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
>             }
>         }
>
> +      /* Avoid folding the call if overlap is detected.  */
> +      if (check_overlap && detect_overlap (loc, stmt, dest, src, len))
> +       return false;
> +
>
> no, please not.  You diagnosed the call (which might be a false positive)
> so why keep it undefined?  The folded stmt will either have the same
> semantics (aggregate copies expanded as memcpy) or have all reads
> performed before writes.

My goal was to follow the approach reflected in the comments
elsewhere in the file:

     /* Out of bound array access.  Value is undefined,
        but don't fold.  */

While gimple_fold_builtin_memory_op may be able to provide well-
defined behavior for the otherwise undefined semantics in a small
subset of cases, it doesn't attempt to fold many more that it
otherwise could (it only folds calls with sizes that are powers
of 2).  So it seems of dubious value to make an effort in this
relatively small subset of cases.

In my experience, users also don't appreciate optimizations that
"rely on" undefined behavior one way or the other.  What they would
like to see instead is that when their compiler detects undefined
behavior it diagnoses it but either doesn't use it to make
optimization decisions, or uses it to disable them.  For calls to
library functions, that in my view means making the call and not
folding it.  (Btw., do we have some sort of a policy or guideline
for how to handle such cases in general?)

With all that said, I don't see a big problem with proceeding with
the folding as you suggest either, so I did and added a comment
documenting it and a test to verify this guarantee.

I should also acknowledge that in my approach I forgot that once
the overlap has been diagnosed and the no-warning bit set, the
next call to gimple_fold_builtin_memory_op() with the same
statement would just go ahead and fold it anyway.  So the tests
were ineffective regardless.

> The ao_ref_init_from_ptr_and_size change misses a changelog entry.
>
> +detect_overlap (location_t loc, gimple *stmt, tree dst, tree src, tree size,
> +               bool adjust /* = false */)
> +{
> +  ao_ref dstref, srcref;
> +  unsigned HOST_WIDE_INT range[2];
> +
> +  /* Initialize and store the lower bound of a constant offset (in
> +     bits), disregarding the offset for the destination.  */
> +  ao_ref_init_from_ptr_and_size (&dstref, dst, size, range);
> +  ao_ref_init_from_ptr_and_size (&srcref, src, size, range);
>
> just pass NULL range to the first call?

The argument needs to be non-null for the base to be determined
the same way between the two references.  (It's not obvious so
I've added a comment to the description of the function.)

>
> -  ref->ref = NULL_TREE;
> +
> +  if (offrng)
> +    offrng[0] = offrng[1] = 0;
> +
> + ref->ref = NULL_TREE;
>
> bogus indent
>
> +         else if (offrng && TREE_CODE (offset) == SSA_NAME)
> +           {
> +             wide_int min, max;
> +             value_range_type rng = get_range_info (offset, &min, &max);
> +             if (rng == VR_RANGE && wi::fits_uhwi_p (min))
> +               {
> +                 ptr = gimple_assign_rhs1 (stmt);
> +                 offrng[0] = BITS_PER_UNIT * min.to_uhwi ();
> +                 offrng[1] = BITS_PER_UNIT * max.to_uhwi ();
> +
> +                 extra_offset = offrng[0];
>
> you didnt' check whether max fits uhwi.  The effect of passing offrng
> is not documented.

Done.

>
> +             else
> +               /* Offset range is indeterminate.  */
> +               offrng[0] = offrng[1] = HOST_WIDE_INT_M1U;
>
> I believe a cleaner interface would be to do
>
> void
> ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size, tree
> *var_byte_offset)
>
> and set *var_byte_offset to your 'offset' above, leaving 'ref'
> unchanged.  The caller
> can then get at the range info of var_byte_offset and adjust
> ref->offset if desired.
> The indeterminate state is then much cleaner - NULL_TREE.

It would make the interface cleaner and the function simpler at
the expense of all the callers having to extract the range and
deal with the additional complexity.  There are six call sites
so that would not be an overall improvement.  I'd need to
introduce a wrapper function and do the work there.  I don't
see the advantage of that approach but I also don't have
a problem with introducing the overload if you consider
the change above necessary.

> +unsigned HOST_WIDE_INT
> +refs_overlap (ao_ref *ref1, ao_ref *ref2, unsigned HOST_WIDE_INT *aloff)
> +{
>
> bool
> refs_must_overlap_p (ao_ref *, ao_ref *, unsigned HOST_WIDE_INT *off,
> unsinged HOST_WIDE_INT *size)

I avoided the words "must" and "alias" on account of your concern
that the function could be misunderstood to be intended to gate
optimizations.  It also wasn't meant to be used as a predicate
(even though it could be) but I have changed its signature as you
suggest.

> your return values are in bytes thus
>
> +
> +      // Compare pointers.
> +      offset1 += mem_ref_offset (base1) << LOG2_BITS_PER_UNIT;
> +      offset2 += mem_ref_offset (base2) << LOG2_BITS_PER_UNIT;
> +      base1 = TREE_OPERAND (base1, 0);
>
> just do the intermediate computations in bytes as well.

That is (slightly) simpler, thank you.

>
> It looks like it is unspecified relative to which ref the offset is given,
> how's that useful information then -- the diagnostic doesn't seem
> too helpful?  Why not keep it relative to the first ref and thus make
> aloff signed?

If I understand your suggestion, an offset relative to the first
reference (which I think is the destination pointer at all call
sites) would be zero whenever the destination pointer were greater
than the source, and positive(?) otherwise.  E.g., like so:

   memcpy (d, d + 1, 2);   // offset is +1

   memcpy (d + 1, d, 2);   // offset is 0.

The second case doesn't seem helpful.

As it is, the offset is the absolute value of the distance between
the source and the destination pointers:

   memcpy (d, d + 1, 2);   // offset is 1

   memcpy (d + 1, d, 2);   // offset is also 1

This seems more meaningful to me but I welcome suggestions for
improvements.

> detect_overlap doesn't belong to tree-ssa-alias.[ch].

I moved it to builtins.c.   If you find that unsuitable there as
well then please suggest an appropriate home for the function.

Attached is an updated patch retested on x86_64-linux.

Martin

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

PR middle-end/78918 - missing -Wrestrict on memcpy copying over self

gcc/ChangeLog:

	PR middle-end/78918
	* builtins.c (warn_for_overlap, maybe_warn_for_overlap): New.
	(detect_overlap): Ditto.
	(check_sizes): Add argument and call maybe_warn_for_overlap.
	Rename function arguments for clarity.
	(check_memop_sizes): Adjust.
	(expand_builtin_memchr): Ditto.
	(expand_builtin_strcat): Ditto.
	(expand_builtin_strcpy): Ditto.
	(expand_builtin_stpcpy): Ditto.
	(expand_builtin_stpncpy): Ditto.
	(expand_builtin_strncpy): Ditto.
	(expand_builtin_memcmp): Ditto.
	(expand_builtin_memory_chk): Ditto.
	(check_strncat_sizes): Ditto.  Rename locals for clarity.
	(expand_builtin_strncat): Ditto.
	(maybe_emit_chk_warning): Ditto.
	(maybe_emit_sprintf_chk_warning): Adjust.
	* builtins.h (detect_overlap): Declare.
	* cfgexpand.c (expand_call_stmt): Set TREE_NO_WARNING.
	* gimple-fold.c (gimple_fold_builtin_memory_op): Handle -Wrestrict.
	(gimple_fold_builtin_strcpy): Ditto.
	(gimple_fold_builtin_memory_chk): Ditto.
	(gimple_fold_builtin_stxcpy_chk): Ditto.
	* gimple.c (gimple_build_call_from_tree): Set call location.
	* tree-ssa-alias.h (refs_overlap_p): New function.
	* tree-ssa-alias.c (refs_overlap_p): Define.
	* tree-ssa-strlen.c (handle_builtin_strcpy): Handle -Wrestrict.
	(handle_builtin_strcat): Ditto.
	* doc/invoke.texi (-Wrestrict): Update.

gcc/c-family/ChangeLog:

	PR middle-end/78918
	* c-common.c (check_function_restrict): Suppress warning for
	built-in functions.
	* c.opt (-Wrestrict): Include in -Wall.

gcc/testsuite/ChangeLog:

	PR middle-end/78918
	* c-c++-common/Wrestrict.c: New test.
	* gcc.dg/memcpy-6.c: New test.
	* gcc.dg/Walloca-1.c: Suppress macro expansion tracking.
	* gcc.dg/pr69172.c: Prune -Wrestrict.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index fa0f89c..d870c51 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -2963,39 +2963,311 @@ determine_block_size (tree len, rtx len_rtx,
 			  GET_MODE_MASK (GET_MODE (len_rtx)));
 }
 
+/* Issue a warning for a restricted copy call expression EXP to a built-in
+   function FUNC, with a destination of size DSTSIZE, size of copy in RANGE,
+   and with OVERLAP bytes at offset OFFRANGE.  MUST_OVERLAP is true when
+   the overlap is certain, false when it is likely.  */
+
+static void
+warn_for_overlap (tree exp, tree func, bool must_overlap, tree dstsize,
+		  const tree range[2], unsigned HOST_WIDE_INT overlap,
+		  const unsigned HOST_WIDE_INT offrange[2])
+{
+  location_t loc = tree_nonartificial_location (exp);
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  /* To avoid combinatorial explosion of diagnostics format the offset
+     or its range as a string and use it in the warning calls below.  */
+  char offstr[64];
+  if (offrange[0] == offrange[1] || offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr, "%llu", (long long) offrange[0]);
+  else
+    sprintf (offstr, "%llu - %llu", (long long) offrange[0],
+	     (long long) offrange[1]);
+
+  /* The text uses the term "writing N bytes" even though most operations
+     involve "copying" and the latter might be clearer.  This first term
+     is used because string functions like strncat and strncpy don't
+     necessarily copy all N bytes; they may copy fewer.  */
+
+  if (tree_to_uhwi (dstsize) >= HOST_WIDE_INT_MAX)
+    {
+      if (must_overlap)
+	{
+	  if (tree_int_cst_equal (range[0], range[1]))
+	    warning_at (loc, OPT_Wrestrict,
+			(integer_onep (range[0])
+			 ? G_("%K%qD writing %E byte overlaps %wu byte "
+			      "at offset %s")
+			 : (overlap == 1
+			    ? G_("%K%qD writing %E bytes overlaps %wu byte "
+				 "at offset %s")
+			    : G_("%K%qD writing %E bytes overlaps %wu bytes "
+				 "at offset %s"))),
+			exp, func, range[0], overlap, offstr);
+	  else if (tree_int_cst_sign_bit (range[1]))
+	    {
+	      /* Avoid printing the upper bound if it's invalid.
+		 The overlap can likewise be "or more" in this case.  */
+	      warning_at (loc, OPT_Wrestrict,
+			  "%K%qD writing %E or more bytes overlaps %wu "
+			  "or more bytes at offset %s",
+			  exp, func, range[0], overlap, offstr);
+	    }
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD writing between %E and %E bytes overlaps "
+			"%wu or more bytes at offset %s",
+			exp, func, range[0], range[1], overlap, offstr);
+	}
+      else
+	{
+	  if (tree_int_cst_equal (range[0], range[1]))
+	    warning_at (loc, OPT_Wrestrict,
+			(integer_onep (range[0])
+			 ? G_("%K%qD writing %E byte may overlap %wu bytes "
+			      "at offset %s")
+			 : (overlap == 1
+			    ? G_("%K%qD writing %E bytes may overlap %wu byte "
+				 "at offset %s")
+			    : G_("%K%qD writing %E bytes may overlap %wu bytes "
+				 "at offset %s"))),
+			exp, func, range[0], overlap, offstr);
+	  else if (tree_int_cst_sign_bit (range[1]))
+	    {
+	      /* Avoid printing the upper bound if it's invalid.
+		 The overlap can likewise be "or more" in this case.  */
+	      warning_at (loc, OPT_Wrestrict,
+			  "%K%qD writing %E or more bytes may overlap %wu "
+			  "or more bytes at offset %s",
+			  exp, func, range[0], overlap, offstr);
+	    }
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD writing between %E and %E bytes may overlap "
+			"%wu or more bytes at offset %s",
+			exp, func, range[0], range[1], overlap,
+			offstr);
+	}
+
+      return;
+    }
+
+  if (must_overlap)
+    {
+      if (TREE_CODE (range[0]) != INTEGER_CST)
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD writing into a region of size %E overlaps "
+		    "at offset %s",
+		    exp, func, dstsize, offstr);
+      else if (tree_int_cst_equal (range[0], range[1]))
+	warning_at (loc, OPT_Wrestrict,
+		    (integer_onep (range[0])
+		     ? G_("%K%qD writing %E byte into a region "
+			  "of size %E overlaps %wu byte "
+			  "at offset %s")
+		     : (overlap == 1
+			? G_("%K%qD writing %E bytes into a region "
+			     "of size %E overlaps %wu byte "
+			     "at offset %s")
+			: G_("%K%qD writing %E bytes into a region "
+			     "of size %E overlaps %wu bytes "
+			     "at offset %s"))),
+		    exp, func, range[0], dstsize, overlap, offstr);
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.
+	     The overlap can likewise be "or more" in this case.  */
+	  warning_at (loc, OPT_Wrestrict,
+		      "%K%qD writing %E or more bytes into "
+		      "a region of size %E overlaps %wu or more bytes "
+		      "at offset %s",
+		      exp, func, range[0], dstsize, overlap, offstr);
+	}
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD writing between %E and %E bytes into "
+		    "a region of size %E overlaps %wu or more bytes "
+		    "at offset %s",
+		    exp, func, range[0], range[1], dstsize, overlap,
+		    offstr);
+    }
+  else
+    {
+      if (tree_int_cst_equal (range[0], range[1]))
+	{
+	  if (overlap < HOST_WIDE_INT_MAX)
+	    warning_at (loc, OPT_Wrestrict,
+			(integer_onep (range[0])
+			 ? G_("%K%qD writing %E byte into a region "
+			      "of size %E may overlap %wu bytes "
+			      "at offset %s")
+			 : (overlap == 1
+			    ? G_("%K%qD writing %E bytes into a region "
+				 "of size %E may overlap %wu byte at "
+				 "offset %s")
+			    : G_("%K%qD writing %E bytes into a region "
+				 "of size %E may overlap %wu bytes at "
+				 "offset %s"))),
+			exp, func, range[0], dstsize, overlap, offstr);
+	  else
+	    /* Non-constant overlap due to an unknown range.  */
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD writing into a region of size %E may overlap "
+			"at offset %s",
+			exp, func, dstsize, offstr);
+	}
+      else if (tree_int_cst_sign_bit (range[1]))
+	{
+	  /* Avoid printing the upper bound if it's invalid.
+	     The overlap can likewise be "or more" in this case.  */
+	  warning_at (loc, OPT_Wrestrict,
+		      "%K%qD writing %E or more bytes into "
+		      "a region of size %E may overlap %wu or more "
+		      "bytes at offset %s",
+		      exp, func, range[0], dstsize, overlap, offstr);
+	}
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD writing between %E and %E bytes into "
+		    "a region of size %E may overlap %wu or more bytes "
+		    "at offset %s",
+		    exp, func, range[0], range[1], dstsize, overlap,
+		    offstr);
+    }
+}
+
+/* Determine whether an overlap exists in a restricted copy call
+   expression EXP to a built-in function FUNC, with a destination DST
+   of DSTSIZE in size and source SRC, writing DSTWRITE bytes into DST
+   and reading MAXREAD bytes from SRC of SLEN bytes in length copy in
+   RANGE.  If so, diagnose it and return true.  If RELAXED is true
+   diagnose even uncertain (but likely) overlaps.  Otherwise do nothing
+   and return false.  */
+
+static bool
+maybe_warn_for_overlap (tree exp, tree func, tree dst, tree src, tree dstwrite,
+			tree range[2], bool at_least_one, tree maxread,
+			tree slen, tree dstsize, bool relaxed)
+{
+  if (!dstwrite)
+    dstwrite = range[0];
+
+  bool must_overlap;
+
+  /* If the range of byte counts is [0, N] and N is non-zero, use
+     1 as the conservative minimum to detect at least the most basic
+     cases of overflow.  */
+  if (dstwrite && TREE_CODE (dstwrite) != INTEGER_CST && range[0])
+    {
+      if (integer_zerop (range[0]) && !integer_zerop (range[1]))
+	{
+	  dstwrite = size_one_node;
+	  must_overlap = false;
+	}
+      else
+	{
+	  dstwrite = range[0];
+	  must_overlap = !integer_zerop (dstwrite);
+	}
+    }
+  else
+    must_overlap = (dstwrite
+		    && TREE_CODE (dstwrite) == INTEGER_CST
+		    && !integer_zerop (dstwrite));
+
+  if (maxread)
+    {
+      must_overlap &= TREE_CODE (maxread) == INTEGER_CST;
+      if (!dstwrite)
+	dstwrite = maxread;
+    }
+  else
+    maxread = dstwrite;
+
+  /* The exact number of bytes copied is known only if the sizes
+     weren't adjusted earlier on.  */
+  must_overlap &= !at_least_one || (dstwrite && dstwrite != slen);
+
+  /* Initialize and store the lower bound of a constant offset (in
+     bits), disregarding the offset for the destination.  */
+  ao_ref dstref, srcref;
+  unsigned HOST_WIDE_INT offrange[2];
+
+  ao_ref_init_from_ptr_and_size (&dstref, dst, dstwrite, offrange);
+  ao_ref_init_from_ptr_and_size (&srcref, src, maxread, offrange);
+
+  unsigned HOST_WIDE_INT offset;
+  unsigned HOST_WIDE_INT overlap;
+  if (!refs_overlap_p (&dstref, &srcref, &overlap, &offset))
+    return false;
+
+  /* Convert the offset range from bits to bytes.  */
+  offrange[0] /= BITS_PER_UNIT;
+  offrange[1] /= BITS_PER_UNIT;
+
+  if (overlap <= offrange[1] - offrange[0])
+    must_overlap = false;
+
+  if (offrange[0] != offrange[1])
+    ;
+  else if (offrange[0] != HOST_WIDE_INT_M1U)
+    offrange[0] = offrange[1] = offset;
+
+  if (must_overlap || !relaxed)
+    {
+      if (!range[0])
+	range[0] = range[1] = dstwrite;
+
+      warn_for_overlap (exp, func, must_overlap, dstsize, range, overlap,
+			offrange);
+    }
+
+  /* Return error when an overlap has been detected.  */
+  return must_overlap;
+}
+
 /* Try to verify that the sizes and lengths of the arguments to a string
    manipulation function given by EXP are within valid bounds and that
-   the operation does not lead to buffer overflow.  Arguments other than
-   EXP may be null.  When non-null, the arguments have the following
-   meaning:
-   SIZE is the user-supplied size argument to the function (such as in
-   memcpy(d, s, SIZE) or strncpy(d, s, SIZE).  It specifies the exact
-   number of bytes to write.
-   MAXLEN is the user-supplied bound on the length of the source sequence
+   the operation does not lead to buffer overflow or read past the end.
+   For copy operations that must not overlap also verify that the source
+   and destination do not overlap one another.
+   Arguments other than EXP may be null.  When non-null, the arguments
+   have the following meaning:
+   DST is the destination of a copy call or null otherwise.
+   SRC is the source of a copy call or null otherwise.
+   DSTWRITE is the number of bytes written into the destination obtained
+   from the user-supplied size argument to the function (such as in
+   memcpy(DST, SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+   MAXREAD is the user-supplied bound on the length of the source sequence
    (such as in strncat(d, s, N).  It specifies the upper limit on the number
-   of bytes to write.
-   SRC is the source string (such as in strcpy(d, s)) when the expression
-   EXP is a string function call (as opposed to a memory call like memcpy).
-   As an exception, SRC can also be an integer denoting the precomputed
-   size of the source string or object (for functions like memcpy).
-   OBJSIZE is the size of the destination object specified by the last
+   of bytes to write.  If null it's taken to be the same as DSTWRITE.
+   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+   expression EXP is a string function call (as opposed to a memory call
+   like memcpy).  As an exception, SRCSTR can also be an integer denoting
+   the precomputed size of the source string or object (for functions like
+   memcpy).
+   DSTSIZE is the size of the destination object specified by the last
    argument to the _chk builtins, typically resulting from the expansion
-   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
-   OBJSIZE).
+   of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
+   DSTSIZE).
 
-   When SIZE is null LEN is checked to verify that it doesn't exceed
+   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
    If the call is successfully verified as safe from buffer overflow
-   the function returns true, otherwise false..  */
+   the function returns true, otherwise false.  */
 
 static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_sizes (int opt, tree exp, tree dst, tree src, tree dstwrite,
+	     tree maxread, tree srcstr, tree dstsize)
 {
   /* The size of the largest object is half the address space, or
      SSIZE_MAX.  (This is way too permissive.)  */
   tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
 
+  /* Either the length of the source string for string functions or
+     the size of the source object for raw memory functions.  */
   tree slen = NULL_TREE;
 
   tree range[2] = { NULL_TREE, NULL_TREE };
@@ -3004,28 +3276,28 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
      function like strcpy is not known and the only thing that is
      known is that it must be at least one (for the terminating nul).  */
   bool at_least_one = false;
-  if (src)
+  if (srcstr)
     {
-      /* SRC is normally a pointer to string but as a special case
+      /* SRCSTR is normally a pointer to string but as a special case
 	 it can be an integer denoting the length of a string.  */
-      if (POINTER_TYPE_P (TREE_TYPE (src)))
+      if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
 	{
 	  /* Try to determine the range of lengths the source string
 	     refers to.  If it can be determined and is less than
-	     the upper bound given by MAXLEN add one to it for
+	     the upper bound given by MAXREAD add one to it for
 	     the terminating nul.  Otherwise, set it to one for
-	     the same reason, or to MAXLEN as appropriate.  */
-	  get_range_strlen (src, range);
-	  if (range[0] && (!maxlen || TREE_CODE (maxlen) == INTEGER_CST))
+	     the same reason, or to MAXREAD as appropriate.  */
+	  get_range_strlen (srcstr, range);
+	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
 	    {
-	      if (maxlen && tree_int_cst_le (maxlen, range[0]))
-		range[0] = range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[0]))
+		range[0] = range[1] = maxread;
 	      else
 		range[0] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[0], size_one_node);
 
-	      if (maxlen && tree_int_cst_le (maxlen, range[1]))
-		range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[1]))
+		range[1] = maxread;
 	      else if (!integer_all_onesp (range[1]))
 		range[1] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[1], size_one_node);
@@ -3039,10 +3311,10 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	    }
 	}
       else
-	slen = src;
+	slen = srcstr;
     }
 
-  if (!size && !maxlen)
+  if (!dstwrite && !maxread)
     {
       /* When the only available piece of data is the object size
 	 there is nothing to do.  */
@@ -3050,20 +3322,18 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	return true;
 
       /* Otherwise, when the length of the source sequence is known
-	 (as with with strlen), set SIZE to it.  */
+	 (as with strlen), set DSTWRITE to it.  */
       if (!range[0])
-	size = slen;
+	dstwrite = slen;
     }
 
-  if (!objsize)
-    objsize = maxobjsize;
+  if (!dstsize)
+    dstsize = maxobjsize;
 
-  /* The SIZE is exact if it's non-null, constant, and in range of
-     unsigned HOST_WIDE_INT.  */
-  bool exactsize = size && tree_fits_uhwi_p (size);
+  if (dstwrite)
+    get_size_range (dstwrite, range);
 
-  if (size)
-    get_size_range (size, range);
+  tree func = get_callee_fndecl (exp);
 
   /* First check the number of bytes to be written against the maximum
      object size.  */
@@ -3076,30 +3346,34 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	warning_at (loc, opt,
 		    "%K%qD specified size %E "
 		    "exceeds maximum object size %E",
-		    exp, get_callee_fndecl (exp), range[0], maxobjsize);
+		    exp, func, range[0], maxobjsize);
 	  else
 	    warning_at (loc, opt,
 			"%K%qD specified size between %E and %E "
 			"exceeds maximum object size %E",
-			exp, get_callee_fndecl (exp),
+			exp, func,
 			range[0], range[1], maxobjsize);
       return false;
     }
 
+  /* The number of bytes to write is "exact" if DSTWRITE is non-null,
+     constant, and in range of unsigned HOST_WIDE_INT.  */
+  bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite);
+
   /* Next check the number of bytes to be written against the destination
      object size.  */
-  if (range[0] || !exactsize || integer_all_onesp (size))
+  if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
     {
       if (range[0]
-	  && ((tree_fits_uhwi_p (objsize)
-	       && tree_int_cst_lt (objsize, range[0]))
-	      || (tree_fits_uhwi_p (size)
-		  && tree_int_cst_lt (size, range[0]))))
+	  && ((tree_fits_uhwi_p (dstsize)
+	       && tree_int_cst_lt (dstsize, range[0]))
+	      || (tree_fits_uhwi_p (dstwrite)
+		  && tree_int_cst_lt (dstwrite, range[0]))))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
 
-	  if (size == slen && at_least_one)
+	  if (dstwrite == slen && at_least_one)
 	    {
 	      /* This is a call to strcpy with a destination of 0 size
 		 and a source of unknown length.  The call will write
@@ -3107,7 +3381,7 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else if (tree_int_cst_equal (range[0], range[1]))
 	    warning_at (loc, opt,
@@ -3116,21 +3390,21 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 			      "of size %E overflows the destination")
 			 : G_("%K%qD writing %E bytes into a region "
 			      "of size %E overflows the destination")),
-			exp, get_callee_fndecl (exp), range[0], objsize);
+			exp, func, range[0], dstsize);
 	  else if (tree_int_cst_sign_bit (range[1]))
 	    {
 	      /* Avoid printing the upper bound if it's invalid.  */
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else
 	    warning_at (loc, opt,
 			"%K%qD writing between %E and %E bytes into "
 			"a region of size %E overflows the destination",
-			exp, get_callee_fndecl (exp), range[0],	range[1],
-			objsize);
+			exp, func, range[0], range[1],
+			dstsize);
 
 	  /* Return error when an overflow has been detected.  */
 	  return false;
@@ -3140,11 +3414,15 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
   /* Check the maximum length of the source sequence against the size
      of the destination object if known, or against the maximum size
      of an object.  */
-  if (maxlen)
+  if (maxread)
     {
-      get_size_range (maxlen, range);
+      get_size_range (maxread, range);
+
+      /* Use the lower end for MAXREAD from now on.  */
+      if (range[0])
+	maxread = range[0];
 
-      if (range[0] && objsize && tree_fits_uhwi_p (objsize))
+      if (range[0] && dstsize && tree_fits_uhwi_p (dstsize))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
@@ -3158,40 +3436,41 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], maxobjsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], range[1], maxobjsize);
 
 	      return false;
 	    }
 
-	  if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
+	  if (dstsize != maxobjsize && tree_int_cst_lt (dstsize, range[0]))
 	    {
 	      if (tree_int_cst_equal (range[0], range[1]))
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], objsize);
+			    exp, func,
+			    range[0], dstsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], range[1], objsize);
+			    exp, func,
+			    range[0], range[1], dstsize);
 	      return false;
 	    }
 	}
     }
 
+  /* Check for reading past the end of SRC.  */
   if (slen
-      && slen == src
-      && size && range[0]
+      && slen == srcstr
+      && dstwrite && range[0]
       && tree_int_cst_lt (slen, range[0]))
     {
       location_t loc = tree_nonartificial_location (exp);
@@ -3201,23 +3480,36 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		    (tree_int_cst_equal (range[0], integer_one_node)
 		     ? G_("%K%qD reading %E byte from a region of size %E")
 		     : G_("%K%qD reading %E bytes from a region of size %E")),
-		    exp, get_callee_fndecl (exp), range[0], slen);
+		    exp, func, range[0], slen);
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
 	  warning_at (loc, opt,
 		      "%K%qD reading %E or more bytes from a region "
 		      "of size %E",
-		      exp, get_callee_fndecl (exp), range[0], slen);
+		      exp, func, range[0], slen);
 	}
       else
 	warning_at (loc, opt,
 		    "%K%qD reading between %E and %E bytes from a region "
 		    "of size %E",
-		    exp, get_callee_fndecl (exp), range[0], range[1], slen);
+		    exp, func, range[0], range[1], slen);
       return false;
     }
 
+  /* Check for overlapping copies.  Avoid warning if one has already
+     been issued (unlike -Wstringop-overflow, -Wrestrict is checked
+     in many places and TREE_NO_WARNING applies to it but not to
+     the former (there should be a bit for every kind of warning).  */
+  if (src && dst
+      && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+      && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+      && !TREE_NO_WARNING (exp)
+      && maybe_warn_for_overlap (exp, func, dst, src, dstwrite, range,
+				 at_least_one, maxread, slen, dstsize,
+				 srcstr == NULL))
+    return false;
+
   return true;
 }
 
@@ -3257,8 +3549,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size)
   tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
   tree dstsize = compute_objsize (dest, 0);
 
-  return check_sizes (OPT_Wstringop_overflow_, exp,
-		      size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+  return check_sizes (OPT_Wstringop_overflow_, exp, dest, src,
+		      size, /*maxread=*/NULL_TREE, srcsize, dstsize);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -3280,8 +3572,8 @@ expand_builtin_memchr (tree exp, rtx)
     {
       tree size = compute_objsize (arg1, 0);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, len, /*maxlen=*/NULL_TREE,
-		   size, /*objsize=*/NULL_TREE);
+		   exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		   /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
     }
 
   return NULL_RTX;
@@ -3589,7 +3881,8 @@ expand_builtin_strcat (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+	       exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+	       destsize);
 
   return NULL_RTX;
 }
@@ -3608,11 +3901,15 @@ expand_builtin_strcpy (tree exp, rtx target)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
 
-  if (warn_stringop_overflow)
+  if (warn_stringop_overflow || warn_restrict)
     {
-      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
+      /* Use Object Size type-1 for -Wrestrict.  */
+      int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 2;
+
+      tree destsize = compute_objsize (dest, ost);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+		   exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		   src, destsize);
     }
 
   return expand_builtin_strcpy_args (dest, src, target);
@@ -3651,7 +3948,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
     {
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
       check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+		   exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+		   destsize);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -3735,7 +4033,7 @@ expand_builtin_stpncpy (tree exp, rtx)
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+	       exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 
   return NULL_RTX;
 }
@@ -3765,7 +4063,7 @@ check_strncat_sizes (tree exp, tree objsize)
 {
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
 
   /* Try to determine the range of lengths that the source expression
      refers to.  */
@@ -3789,32 +4087,33 @@ check_strncat_sizes (tree exp, tree objsize)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize)
-      && tree_int_cst_equal (objsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return false;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
   /* The number of bytes to write is LEN but check_sizes will also
      check SRCLEN if LEN's value isn't known.  */
   return check_sizes (OPT_Wstringop_overflow_,
-		      exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+		      exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+		      objsize);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -3832,7 +4131,7 @@ expand_builtin_strncat (tree exp, rtx)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
   /* The upper bound on the number of bytes to write.  */
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
   /* The length of the source sequence.  */
   tree slen = c_strlen (src, 1);
 
@@ -3855,32 +4154,31 @@ expand_builtin_strncat (tree exp, rtx)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize)
-      && tree_int_cst_equal (destsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return NULL_RTX;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
-     check SRCLEN if LEN's value isn't known.  */
+  /* The number of bytes to write is SRCLEN.  */
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+	       exp, dest, src, NULL_TREE, maxread, srclen, destsize);
 
   return NULL_RTX;
 }
@@ -3911,7 +4209,7 @@ expand_builtin_strncpy (tree exp, rtx target)
 	  /* The number of bytes to write is LEN but check_sizes will also
 	     check SLEN if LEN's value isn't known.  */
 	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+		       exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 	}
 
       /* We must be passed a constant len and src parameter.  */
@@ -4255,13 +4553,13 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
     {
       tree size = compute_objsize (arg1, 0);
       if (check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE))
+		       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		       /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
 	{
 	  size = compute_objsize (arg2, 0);
 	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE);
+		       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		       /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
 	}
     }
 
@@ -9644,8 +9942,6 @@ static rtx
 expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			   enum built_in_function fcode)
 {
-  tree dest, src, len, size;
-
   if (!validate_arglist (exp,
 			 POINTER_TYPE,
 			 fcode == BUILT_IN_MEMSET_CHK
@@ -9653,13 +9949,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
-  len = CALL_EXPR_ARG (exp, 2);
-  size = CALL_EXPR_ARG (exp, 3);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 3);
 
   bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
-			       exp, len, /*maxlen=*/NULL_TREE,
+			       exp, dest, src, len, /*maxread=*/NULL_TREE,
 			       /*str=*/NULL_TREE, size);
 
   if (!tree_fits_uhwi_p (size))
@@ -9769,7 +10065,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
   /* The maximum length of the source sequence in a bounded operation
      (such as __strncat_chk) or null if the operation isn't bounded
      (such as __strcat_chk).  */
-  tree maxlen = NULL_TREE;
+  tree maxread = NULL_TREE;
 
   switch (fcode)
     {
@@ -9790,27 +10086,27 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
     case BUILT_IN_STRNCAT_CHK:
       catstr = CALL_EXPR_ARG (exp, 0);
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STPNCPY_CHK:
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
-      maxlen = CALL_EXPR_ARG (exp, 1);
+      maxread = CALL_EXPR_ARG (exp, 1);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
     default:
       gcc_unreachable ();
     }
 
-  if (catstr && maxlen)
+  if (catstr && maxread)
     {
       /* Check __strncat_chk.  There is no way to determine the length
 	 of the string to which the source string is being appended so
@@ -9819,8 +10115,11 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       return;
     }
 
-  check_sizes (OPT_Wstringop_overflow_, exp,
-	       /*size=*/NULL_TREE, maxlen, srcstr, objsize);
+  /* The destination argument is the first one for all built-ins above.  */
+  tree dst = CALL_EXPR_ARG (exp, 0);
+
+  check_sizes (OPT_Wstringop_overflow_, exp, dst, srcstr,
+	       /*size=*/NULL_TREE, maxread, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9876,8 +10175,10 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 
   /* Add one for the terminating nul.  */
   len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
+
   check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
+	       exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
+	       /*maxread=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
@@ -10508,3 +10809,92 @@ target_char_cst_p (tree t, char *p)
   *p = (char)tree_to_uhwi (t);
   return true;
 }
+
+/* Attempt to detect and diagnose overlapping copy in a statement STMT
+   from SRC to DST of SIZE bytes.  When ADJUST is set, adjust SIZE to
+   reflect the lower bound of the non-constant SRC offset.  Return true
+   when overlap has been detected, false otherwise.
+   In the case of a non-constant offset, using a positive lower bound
+   is more strict than using the upper bound.  */
+
+bool
+detect_overlap (location_t loc, gimple *stmt, tree dst, tree src, tree size,
+		bool adjust /* = false */, bool diagnose /* = true */)
+{
+  ao_ref dstref, srcref;
+  unsigned HOST_WIDE_INT range[2];
+
+  /* Initialize and store the lower bound of a constant offset (in
+     bits), disregarding the offset for the destination.  */
+  ao_ref_init_from_ptr_and_size (&dstref, dst, size, range);
+  ao_ref_init_from_ptr_and_size (&srcref, src, size, range);
+
+  if (adjust)
+    {
+      /* Adjust the source and destination sizes (in bits) by
+	 the non-constant offset (also in bits).  */
+      dstref.size -= range[0];
+      srcref.size -= range[0];
+    }
+
+  /* Determine the size of the OVERLAP, if any, and its OFFSET, both
+     in bytes.  */
+  unsigned HOST_WIDE_INT offset;
+  unsigned HOST_WIDE_INT overlap;
+  if (!refs_overlap_p (&dstref, &srcref, &overlap, &offset))
+    return false;
+
+  if (!diagnose)
+    return true;
+
+  /* Convert the size from bits to bytes (OFFSET is in bytes).  */
+
+  range[1] = (dstref.size - (range[1] - range[0])) / BITS_PER_UNIT;
+  range[0] = dstref.size / BITS_PER_UNIT;
+
+  if (range[0] > range[1])
+    std::swap (range[0], range[1]);
+
+  bool warned;
+
+  tree func = gimple_call_fndecl (stmt);
+
+  /* Below distinguish three cases:
+     1) certain constant overlap
+     2) certain overlap in some range
+     3) possible overlap.  */
+  if (range[0] == range[1])
+    warned = warning_at (loc, OPT_Wrestrict,
+			 range[0] == 1
+			 ? (overlap == 1
+			    ? G_("%qD writing %wu byte overlaps %wu byte "
+				 "at offset %wu")
+			    : G_("%qD writing %wu byte overlaps %wu bytes "
+				 "at offset %wu"))
+			 : (overlap == 1
+			    ? G_("%qD writing %wu bytes overlaps %wu byte "
+				 "at offset %wu")
+			    : G_("%qD writing %wu bytes overlaps %wu bytes "
+				 "at offset %wu")),
+			 func, range[0], overlap, offset);
+  else if (range[1] - range[0] < overlap)
+    warned = warning_at (loc, OPT_Wrestrict,
+			 overlap == 1
+			 ? G_("%qD writing between %wu and %wu bytes "
+			      "overlaps %wu byte at offset %wu")
+			 : G_("%qD writing between %wu and %wu bytes "
+			      "overlaps %wu bytes at offset %wu"),
+			 func, range[0], range[1], overlap, offset);
+  else
+    warned = warning_at (loc, OPT_Wrestrict,
+			 overlap == 1
+			 ? G_("%qD writing between %wu and %wu bytes "
+			      "may overlap %wu byte at offset %wu")
+			 : G_("%qD writing %wu byte may overlap %wu bytes "
+			      "at offset %wu"),
+			 func, range[0], range[1], overlap, offset);
+  if (warned)
+    gimple_set_no_warning (stmt, true);
+
+  return true;
+}
diff --git a/gcc/builtins.h b/gcc/builtins.h
index f590f61..c1402d8 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -102,4 +102,7 @@ extern bool target_char_cst_p (tree t, char *p);
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
 
+extern bool detect_overlap (location_t, gimple *, tree, tree, tree,
+			    bool = false, bool = true);
+
 #endif
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 156c89d..43ec1dd 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5262,14 +5262,20 @@ check_function_restrict (const_tree fndecl, const_tree fntype,
 			 int nargs, tree *argarray)
 {
   int i;
-  tree parms;
+  tree parms = TYPE_ARG_TYPES (fntype);
 
   if (fndecl
-      && TREE_CODE (fndecl) == FUNCTION_DECL
-      && DECL_ARGUMENTS (fndecl))
-    parms = DECL_ARGUMENTS (fndecl);
-  else
-    parms = TYPE_ARG_TYPES (fntype);
+      && TREE_CODE (fndecl) == FUNCTION_DECL)
+    {
+      /* Skip checking built-ins here.  They are checked in more
+	 detail elsewhere.  */
+      if (DECL_BUILT_IN (fndecl)
+	  && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+	return;
+
+      if (DECL_ARGUMENTS (fndecl))
+	parms = DECL_ARGUMENTS (fndecl);
+    }
 
   for (i = 0; i < nargs; i++)
     TREE_VISITED (argarray[i]) = 0;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 3435fe9..6cbd140 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1170,7 +1170,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall)
 Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier.
 
 Wrestrict
-C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++)
+C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
 Warn when an argument passed to a restrict-qualified parameter aliases with
 another argument.
 
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index 573f0c7..237160d 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2629,6 +2629,9 @@ expand_call_stmt (gcall *stmt)
   if (gimple_call_nothrow_p (stmt))
     TREE_NOTHROW (exp) = 1;
 
+  if (gimple_no_warning_p (stmt))
+    TREE_NO_WARNING (exp) = 1;
+
   CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt);
   CALL_EXPR_MUST_TAIL_CALL (exp) = gimple_call_must_tail_p (stmt);
   CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 64363e5..13af531 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3852,6 +3852,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
 -Wparentheses  @gol
 -Wpointer-sign  @gol
 -Wreorder   @gol
+-Wrestrict   @gol
 -Wreturn-type  @gol
 -Wsequence-point  @gol
 -Wsign-compare @r{(only in C++)}  @gol
@@ -6561,11 +6562,25 @@ reduce the padding and so make the structure smaller.
 Warn if anything is declared more than once in the same scope, even in
 cases where multiple declaration is valid and changes nothing.
 
-@item -Wrestrict
+@item -Wno-restrict
 @opindex Wrestrict
 @opindex Wno-restrict
-Warn when an argument passed to a restrict-qualified parameter
-aliases with another argument.
+Warn when an object referenced by a @code{restrict}-qualified parameter
+(or, in C++, @code{__restrict}-qualified parameter) is aliased by another
+argument, or when copies between such objects overlap.  For example,
+the call to the @code{strcpy} function below attempts to truncate the string
+by replacing its initial characters with the last four.  However, because
+the call writes the terminating NUL into @code{a[4]}, the copies overlap and
+the call is diagnosed.
+
+@smallexample
+struct foo
+@{
+  char a[] = "abcd1234";
+  strcpy (a, a + 4);
+@};
+@end smallexample
+The @option{-Wrestrict} is included in @option{-Wall}.
 
 @item -Wnested-externs @r{(C and Objective-C only)}
 @opindex Wnested-externs
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index bf39f28..19ce821 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -59,6 +59,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "intl.h"
+#include "diagnostic-core.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -658,13 +660,12 @@ size_must_be_zero_p (tree size)
   return wi::eq_p (min, wone) && wi::geu_p (max, ssize_max);
 }
 
-/* Fold function call to builtin mem{{,p}cpy,move}.  Return
-   false if no simplification can be made.
-   If ENDP is 0, return DEST (like memcpy).
-   If ENDP is 1, return DEST+LEN (like mempcpy).
-   If ENDP is 2, return DEST+LEN-1 (like stpcpy).
-   If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
-   (memmove).   */
+/* Fold function call to builtin mem{{,p}cpy,move}.  Try to detect and
+   diagnose (otherwise undefined) overlapping copies without preventing
+   folding.  When folded, GCC guarantees that overlapping memcpy has
+   the same semantics as memmove.  Call to the library memcpy need not
+   provide the same guarantee.  Return false if no simplification can
+   be made.  */
 
 static bool
 gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
@@ -676,6 +677,12 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
   tree destvar, srcvar;
   location_t loc = gimple_location (stmt);
 
+  tree func = gimple_call_fndecl (stmt);
+  bool nowarn = gimple_no_warning_p (stmt);
+  bool check_overlap = (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+			&& DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+			&& !nowarn);
+
   /* If the LEN parameter is a constant zero or in range where
      the only valid value is zero, return DEST.  */
   if (size_must_be_zero_p (len))
@@ -699,6 +706,15 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
      DEST{,+LEN,+LEN-1}.  */
   if (operand_equal_p (src, dest, 0))
     {
+      /* Avoid diagnosing exact overlap in calls to __builtin_memcpy.
+	 It's safe and may even be emitted by GCC itself (see bug
+	 32667).  However, diagnose it in explicit calls to the memcpy
+	 function.  */
+      if (check_overlap && *IDENTIFIER_POINTER (DECL_NAME (func)) != '_')
+	warning_at (loc, OPT_Wrestrict,
+		    "%qD source argument is the same as destination",
+		    func);
+
       unlink_stmt_vdef (stmt);
       if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
 	release_ssa_name (gimple_vdef (stmt));
@@ -748,6 +764,27 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	  unsigned ilen = tree_to_uhwi (len);
 	  if (pow2p_hwi (ilen))
 	    {
+	      if (check_overlap)
+		{
+		  /* Detect overlapping copies and issue -Wrestrict.  */
+		  if (detect_overlap (loc, stmt, dest, src, len, endp == 2))
+		    gimple_set_no_warning (stmt, true);
+		  else if (TREE_CODE (dest) != SSA_NAME
+			   || TREE_CODE (src) != SSA_NAME)
+		    {
+		      /* If no overlap is detected and at least one of
+			 the arguments is not in an SSA form defer folding
+			 until they both are so that aliasing can be
+			 determined with greater accuracy.  */
+		      ao_ref dstref;
+		      ao_ref srcref;
+		      ao_ref_init_from_ptr_and_size (&dstref, dest, len);
+		      ao_ref_init_from_ptr_and_size (&srcref, src, len);
+		      if (refs_may_alias_p_1 (&dstref, &srcref, true))
+			return false;
+		    }
+		}
+
 	      tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
 	      if (type
 		  && TYPE_MODE (type) != BLKmode
@@ -1061,6 +1098,9 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	    }
 	}
 
+      /* Detect overlapping copies and issue -Wrestrict.  */
+      detect_overlap (loc, stmt, dest, src, len);
+
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
 	{
@@ -1425,7 +1465,7 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
 	    tree op3 = gimple_assign_rhs3 (def_stmt);
 	    return get_range_strlen (op2, length, visited, type, fuzzy, flexp)
 	      && get_range_strlen (op3, length, visited, type, fuzzy, flexp);
-          }
+	  }
         return false;
 
       case GIMPLE_PHI:
@@ -1517,12 +1557,19 @@ static bool
 gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
 			    tree dest, tree src)
 {
-  location_t loc = gimple_location (gsi_stmt (*gsi));
+  gimple *stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
   tree fn;
 
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -2305,6 +2352,15 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
      (resp. DEST+LEN for __mempcpy_chk).  */
   if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
     {
+      if (fcode != BUILT_IN_MEMMOVE && fcode != BUILT_IN_MEMMOVE_CHK)
+	{
+	  tree func = gimple_call_fndecl (stmt);
+
+	  warning_at (loc, OPT_Wrestrict,
+		      "%qD source argument is the same as destination",
+		      func);
+	}
+
       if (fcode != BUILT_IN_MEMPCPY_CHK)
 	{
 	  replace_call_with_value (gsi, dest);
@@ -2406,6 +2462,12 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -7226,3 +7288,4 @@ gimple_stmt_integer_valued_real_p (gimple *stmt, int depth)
       return false;
     }
 }
+
diff --git a/gcc/gimple.c b/gcc/gimple.c
index c4e6f81..d70552d 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -361,6 +361,7 @@ gimple_build_call_from_tree (tree t)
     gimple_call_set_arg (call, i, CALL_EXPR_ARG (t, i));
 
   gimple_set_block (call, TREE_BLOCK (t));
+  gimple_set_location (call, EXPR_LOCATION (t));
 
   /* Carry all the CALL_EXPR flags to the new GIMPLE_CALL.  */
   gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t));
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
new file mode 100644
index 0000000..643a89a
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -0,0 +1,604 @@
+/* PR 35503 - Warn about restricted pointers
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+typedef __SIZE_TYPE__ size_t;
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+size_t value (void);
+
+size_t range (size_t min, size_t max)
+{
+  size_t val = value ();
+  return val < min || max < val ? min : val;
+}
+
+#define R(min, max) range ((min), (max))
+
+
+void sink (void*, ...);
+
+/* Exercise memcpy with constant or known arguments.  */
+
+void test_memcpy_cst (void)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+    const void *s = 0;				\
+    memcpy ((dst), (src), (n));			\
+    sink (a, (dst), s);				\
+  } while (0)
+
+  T (a, a, 0);
+  T (a, s = a, 1);           /* { dg-warning "\\\[-Wrestrict" } */
+  T (a, a + 1, 1);
+  T (a, a + 1, 2);           /* { dg-warning "\\\[-Wrestrict" } */
+
+  {
+    char a[3] = { 1, 2, 3 };
+
+    /* Verify that a call to memcpy with an exact overlap is diagnosed
+       (also tested above) but an excplicit one to __builtin_memcpy is
+       not.  See bug 32667 for the rationale.  */
+    (memcpy)(a, a, sizeof a);   /* { dg-warning "source argument is the same as destination" } */
+    sink (a);
+
+    __builtin_memcpy (a, a, sizeof a);
+    sink (a);
+  }
+
+  {
+    char a[3][7];
+    sink (a);
+
+    void *d = a[0];
+    const void *s = a[1];
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0];
+    s = a[1];
+    memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0] + 1); /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&a);
+  }
+
+  {
+    struct {
+      char a[7];
+      char b[7];
+      char c[7];
+    } x;
+    sink (&x);
+
+    void *d = x.a;
+    const void *s = x.b;
+    memcpy (d, s, sizeof x.a);
+    sink (&x);
+
+    d = x.a;
+    s = x.a;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 4;
+    s = x.b;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 6;
+    s = x.b;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b;
+    memcpy (d, s, 1);             /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b + 1;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 1;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 2;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 3;
+    memcpy (d, s, sizeof x.b);    /* { dg-warning "\\\[-Wrestrict" } */
+    sink (&x);
+  }
+
+  {
+#undef T
+#define T(dst, src, n) do {				\
+      char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+      memcpy ((dst), (src), (n));			\
+      sink (a);						\
+    } while (0)
+
+    /* Verify the offset of the overlap is the same regardless of whether
+       the destination is at lower or higher offset than the source.  */
+    T (a, a + 1, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 4 bytes at offset 1" } */
+    T (a, a + 2, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 3 bytes at offset 2" } */
+    T (a, a + 3, 5);             /* { dg-warning "writing 5 bytes into a region of size 9 overlaps 2 bytes at offset 3" } */
+
+    T (a + 1, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 8 overlaps 4 bytes at offset 1" } */
+    T (a + 2, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 7 overlaps 3 bytes at offset 2" } */
+    T (a + 3, a, 5);             /* { dg-warning "writing 5 bytes into a region of size 6 overlaps 2 bytes at offset 3" } */
+  }
+}
+
+/* Exercise memcpy with destination or source offset or size in
+   a determinate range.  */
+
+void test_memcpy_range (void *d)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+    memcpy ((dst), (src), (n));			\
+    sink (a);					\
+  } while (0)
+
+  int r = R (2, 3);
+  T (a + r, a, 0);
+  T (a + r, a, 1);
+  T (a + r, a, 2);
+  T (a + r, a, 3);                /* { dg-warning "writing 3 bytes overlaps 1 byte at offset 2" } */
+
+  T (d + r, d, 0);
+  T (d + r, d, 1);
+  T (d + r, d, 2);
+  T (d + r, d, 3);                /* { dg-warning "writing 3 bytes overlaps 1 byte at offset 2" } */
+
+  /* Because the size is constant and a power of 2 the following is
+     folded too early to detect the overlap.  */
+  T (d + r, d, 4);                /* { dg-warning "writing 4 bytes overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+  T (d + r, d, 5);                /* { dg-warning "writing 5 bytes overlaps 3 bytes at offset 2" } */
+
+  T (a, a + 1, R (0, 1));
+  T (a, a + 1, R (0, 2));
+  T (a, a + 1, R (1, 2));
+  T (a, a + 1, R (2, 3));         /* { dg-warning "writing between 2 and 3 bytes into a region of size 9 overlaps 1 or more bytes at offset 1" } */
+  T (a, a + 1, R (2, -2));        /* { dg-warning "writing 2 or more bytes into a region of size 9 overlaps 1 or more bytes at offset 1" } */
+  T (a, a + 2, R (2, 3));
+  T (a, a + 2, R (3, 4));         /* { dg-warning "writing between 3 and 4 bytes into a region of size 9 overlaps 1 or more bytes at offset 2" } */
+  T (a, a + 3, R (3, 4));
+  T (a, a + 3, R (4, 5));         /* { dg-warning "writing between 4 and 5 bytes into a region of size 9 overlaps 1 or more bytes at offset 3" } */
+  T (a, a + 3, R (5, 6));         /* { dg-warning "writing between 5 and 6 bytes into a region of size 9 overlaps 2 or more bytes at offset 3" } */
+
+  T (a + 1, a, R (0, 1));
+  T (a + 1, a, R (0, 2));
+  T (a + 1, a, R (1, 2));
+  T (a + 1, a, R (2, 3));         /* { dg-warning "writing between 2 and 3 bytes into a region of size 8 overlaps 1 or more bytes at offset 1" } */
+  T (a + 1, a, R (2, -2));        /* { dg-warning "writing 2 or more bytes into a region of size 8 overlaps 1 or more bytes at offset 1" } */
+  T (a + 2, a, R (2, 3));
+  T (a + 2, a, R (3, 4));         /* { dg-warning "writing between 3 and 4 bytes into a region of size 7 overlaps 1 or more bytes at offset 2" } */
+  T (a + 3, a, R (3, 4));
+  T (a + 3, a, R (4, 5));         /* { dg-warning "writing between 4 and 5 bytes into a region of size 6 overlaps 1 or more bytes at offset 3" } */
+  T (a + 3, a, R (5, 6));         /* { dg-warning "writing between 5 and 6 bytes into a region of size 6 overlaps 2 or more bytes at offset 3" } */
+
+  /* Verify offset and size both in some range.  The memcpy checking
+     is less strict than that of string functions like strncpy and
+     doesn't trigger unless the overlap is certain.  The following
+     overlaps for (r == 3 && n > 3) but not, for example, for
+     (r == 4 && n == 4), and so it's not diagnosed.  */
+  r = R (3, 5);
+  T (a, a + r, R (4, 6));
+  /* Ditto for objects of unknown sizes.  */
+  T (d, d + r, R (4, 6));
+}
+
+/* Exercise memcpy with destination and source of unknown size.  */
+
+void test_memcpy_var (char *d, const char *s)
+{
+  int n = value ();
+
+  memcpy (d, d, 0);
+  sink (d);
+
+  memcpy (d, d, n);               /* { dg-warning "source argument is the same as destination" } */
+  sink (d);
+
+  memcpy (d, &d[0], n);           /* { dg-warning "source argument is the same as destination" } */
+  sink (d);
+
+  memcpy (&d[0], d,  n);          /* { dg-warning "source argument is the same as destination" } */
+  sink (d);
+
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "source argument is the same as destination" } */
+  sink (d);
+
+  /* The following overlaps if n is greater than 1.  */
+  s = d + 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + n;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + value ();
+  memcpy (d, s, value ());
+  sink (d);
+
+  s = d + 3;
+  n = 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 2;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 3;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 4;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+  sink (d);
+
+  s = d + 5;
+  n = 7;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+
+  n = range (0, 1);
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+/* Exercise the absence of warnings with memmove.  */
+
+void test_memmove (void)
+{
+  {
+    char d[7];
+    sink (d);
+
+    const void *s = d;
+    memmove (d, s, 7);
+    sink (d);
+
+    s = d + 1;
+    memmove (d, s, 6);
+    sink (d);
+
+    s = d + 2;
+    memmove (d + 1, s, 5);
+    sink (d);
+  }
+}
+
+/* Exercise strcat with constant or known arguments.  */
+
+void test_strcat_cst (const char *s)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[9] = init;				\
+    strcat ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2);            /* { dg-warning "\\\[-Wrestrict" } */
+  /* The nul copied from a[3] to a[3] overwrites itself so this is
+     diagnosed.  */
+  T ("123", a, a + 3);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 4);
+  T ("123", a, a + 5);
+  T ("123", a, a + 6);
+  T ("123", a, a + 7);
+  T ("123", a, a + 8);
+
+  T ("123", a + 1, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 2, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 3, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 4, a);            /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+  T ("123", a + 5, a);            /* { dg-bogus "\\\[-Wrestrict" "bug 81435" { xfail *-*-* } } */
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T ("123",      a, "123");
+  T ("123",      a, s);
+  T ("12345678", a, s);
+}
+
+/* Exercise strcat with destination and source of unknown length.  */
+
+void test_strcat_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    strcat ((dst), (src));			\
+    sink ((dst));				\
+  } while (0)
+
+  T (d, d);                       /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 1);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 2);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d + 3);
+
+  int n = value ();
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T (d, "123");
+  T (d + 1, "1234");
+  T (d + n, "12345");
+  T (d, s);
+  T (d + 1, s);
+  T (d + n, s);
+
+  /* This one isn't so obvious and might be worth diagnosing with "may
+     overlap," even more so than the analogous strcpy case.  */
+  T (d, d + n);
+}
+
+/* Exercise strcpy with constant or known arguments.  */
+
+void test_strcpy_cst (void)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[8] = init;				\
+    strcpy ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2);
+  T ("123", a, a + 3);
+
+  /* The terminating nul written to d[2] overwrites s[0].  */
+  T ("1234", a, a + 2);           /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("12345", a, a + 2);          /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* It's not 100% clear if this should trigger a warning.  The non-const
+     string case doesn't.  */
+  T ("123", a, a + value ());
+
+  /* This happens to be safe in GCC but it's still wrong.  */
+  T ("123", a, a);                /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("123", a + 1, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 2, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 3, a);            /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a + 4, a);
+}
+
+/* Exercise strcpy with constant or known arguments offset by a range.
+   The tests verify the use of the lower bound of the range which is
+   more restrictive than using the upper bound for positive values.  */
+
+void test_strcpy_range (void)
+{
+#undef T
+#define T(init, dst, src) do {			\
+    char a[8] = init;				\
+    strcpy ((dst), (src));			\
+    sink (a);					\
+  } while (0)
+
+  /* The range needs to be an int variable and not a size_t here,
+     otherwise the range info in the strlen pass is not good enough
+     to detect these cases.  */
+  int r = R (3, 4);
+  T ("12",  a + r, a);
+  T ("123", a + r, a);            /* { dg-warning "writing 4 bytes overlaps 1 byte at offset 3" } */
+
+  T ("1234", a + r, a);           /* { dg-warning "writing 5 bytes overlaps 2 bytes at offset 3" } */
+
+  T ("12",     a, a + r);
+  T ("123",    a, a + r);
+  T ("1234",   a, a + r);
+  T ("12345",  a, a + r);
+
+  /* The final NUL overlaps the '4' at a[3].  */
+  T ("123456", a, a + r);         /* { dg-warning "writing between 3 and 4 bytes may overlap 1 byte at offset 3" } */
+  T ("1234567", a, a + r);        /* { dg-warning "writing between 4 and 5 bytes overlaps 2 bytes at offset 3" } */
+
+  r = R (2, 5);
+  T ("1234", a + r, a);           /* { dg-warning "writing 5 bytes overlaps 3 bytes at offset 2" } */
+
+
+}
+
+
+/* Exercise strcpy with destination and source of unknown length.  */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    strcpy ((dst), (src));			\
+    sink (dst);					\
+  } while (0)
+
+  T (d, s);
+
+  T (d, &d[0]);                   /* { dg-warning "\\\[-Wrestrict" } */
+  T (&d[0], d);                   /* { dg-warning "\\\[-Wrestrict" } */
+
+  s = d;
+  T (d, s);                       /* { dg-warning "\\\[-Wrestrict" } */
+
+  /* The following overlaps if *s is not nul.  It arguably should be
+     diagnosed.  */
+  T (d, d + 1);
+
+  /* The following overlaps if strlen (d) is greater than 1.  Like
+     the above, it possibly should be diagnosed too.  */
+  int r = R (2, 3);
+  T (d, d + r);
+
+  /* The following overlaps only if strlen (s + n) >= n so it's not
+     diagnosed.  */
+  s = d + value ();
+  T (d, s);
+}
+
+/* Exercise strncpy with constant or known arguments.  */
+
+void test_strncpy_cst (void)
+{
+#undef T
+#define T(init, dst, src, size) do {		\
+    char a[7] = init;				\
+    strncpy (dst, src, size);			\
+    sink (a);					\
+  } while (0)
+
+  T ("123", a, a, 0);
+  T ("123", a, a, 1);             /* { dg-warning "\\\[-Wrestrict" } */
+  /* The following overlaps except in the unlikely case that value ()
+     is zero, so it's diagnosed.  */
+  T ("123", a, a, value ());      /* { dg-warning "writing into a region of size 7 may overlap at offset 0" } */
+  T ("123", a, a + 1, 1);
+  T ("123", a, a + 1, 2);         /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 1, 3);         /* { dg-warning "\\\[-Wrestrict" } */
+  T ("123", a, a + 2, 1);
+  T ("123", a, a + 2, 2);
+  /* The third written byte (nul) overwrites a[2].  */
+  T ("123", a, a + 2, 3);         /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("1234", a, a + 2, 1);
+  T ("1234", a, a + 2, 2);
+  /* The terminating nul written to a[2] overwrites s[0].  */
+  T ("1234", a, a + 2, 3);        /* { dg-warning "\\\[-Wrestrict" } */
+
+  T ("12345", a, a + 2, 1);
+  T ("12345", a, a + 2, 2);
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("12345", a, a + 2, 4);       /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+
+/* Exercise strncpy with one or more arguments in a determinate range.  */
+
+void test_strncpy_range (char *d)
+{
+#undef T
+#define T(init, dst, src, size) do {		\
+    char a[9] = init;				\
+    strncpy ((dst), (src), (size));		\
+    sink (a);					\
+  } while (0)
+
+  /* Verify offset and size both in some range.  The strncpy checking
+     is more strict than that of memcpy and triggers even when the
+     overlap is possible but not inevitable.  The following overlaps
+     for (r == 3 && n > 3) but not, for example, for (r == 4 && n == 4),
+     but it's still diagnosed.  */
+  int r = R (3, 5);
+  T ("12345678", a, a + r, R (4, 6));   /* { dg-warning "writing between 4 and 6 bytes into a region of size 9 may overlap 1 or more bytes at offset 3 - 5" } */
+
+  /* Ditto for objects of unknown sizes.  Verify also that since it's
+     unknown the size isn't printed.  */
+  T ("12345678", d, d + r, R (4, 6));  /* { dg-warning "writing between 4 and 6 bytes may overlap 1 or more bytes at offset 3 - 5" } */
+
+  T ("12345678", a, a + r, R (6, 7));  /* { dg-warning "writing between 6 and 7 bytes into a region of size 9 overlaps 3 or more bytes at offset 3 - 5" } */
+}
+
+
+/* Exercise strncpy with destination and source of unknown length.  */
+
+void test_strncpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src, size) do {			\
+    strncpy (dst, src, size);			\
+    sink ((dst));				\
+  } while (0)
+
+  int n = value ();
+
+  T (d, s, 1);
+  T (d, s, n);
+
+  T (d, d, 1);                    /* { dg-warning "\\\[-Wrestrict" } */
+  T (d, d, n);                    /* { dg-warning "\\\[-Wrestrict" } */
+
+  T (d,     d + 1, 1);
+  T (d,     d + 1, 2);            /* { dg-warning "\\\[-Wrestrict" } */
+  T (d + 1, d,     1);
+  T (d + 1, d,     2);            /* { dg-warning "\\\[-Wrestrict" } */
+}
+
+struct MemberArrays
+{
+  char a[8];
+  char b[8];
+  char c[9];
+};
+
+void test_strncpy_strcpy_var (struct MemberArrays *ar, const char *s)
+{
+  /* The following is safe and should not trigger a warning.  */
+  strncpy (ar->b, s, sizeof ar->b - 1);
+  ar->b[sizeof ar->b - 1] = '\0';
+  strcpy (ar->a, ar->b);
+  sink (ar);
+
+  /* The following is not as safe (it might overflow ar->a) but there
+     is no overlap so it also shouldn't trigger -Wrestrict.  */
+  strncpy (ar->c, s, sizeof ar->c - 1);
+  ar->c[sizeof ar->c - 1] = '\0';
+  strcpy (ar->a, ar->c);
+  sink (ar);
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c b/gcc/testsuite/gcc.dg/Walloca-1.c
index ad39373..85e9160 100644
--- a/gcc/testsuite/gcc.dg/Walloca-1.c
+++ b/gcc/testsuite/gcc.dg/Walloca-1.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* { dg-require-effective-target alloca } */
-/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+/* { dg-options "-Walloca-larger-than=2000 -O2 -ftrack-macro-expansion=0" } */
 
 #define alloca __builtin_alloca
 
diff --git a/gcc/testsuite/gcc.dg/memcpy-6.c b/gcc/testsuite/gcc.dg/memcpy-6.c
new file mode 100644
index 0000000..1d76e7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/memcpy-6.c
@@ -0,0 +1,40 @@
+/* Test to verify that overlapping memcpy with const sizes that are powers
+   of two are folded into into the same code as memmove, but that they
+   are diagnosed nonetheless.
+   { dg-do compile }
+   { dg-options "-O0 -Wrestrict -fdump-tree-optimized" } */
+
+char a[32];
+
+void fold_copy_2 (void)
+{
+  __builtin_memcpy (a + 1, a, 2);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_4 (void)
+{
+  __builtin_memcpy (a + 2, a, 4);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_8 (void)
+{
+  __builtin_memcpy (a + 3, a, 8);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_move_2 (void)
+{
+  __builtin_memmove (a + 1, a, 2);
+}
+
+void fold_move_4 (void)
+{
+  __builtin_memmove (a + 2, a, 4);
+}
+
+void fold_move_8 (void)
+{
+  __builtin_memmove (a + 3, a, 8);
+}
+
+/* { dg-final { scan-tree-dump-not "memcpy" "optimized" } }
+   { dg-final { scan-tree-dump-not "memmove" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/memcpy-6.s b/gcc/testsuite/gcc.dg/memcpy-6.s
new file mode 100644
index 0000000..e69de29
diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c
index c0e7463..908d5a6 100644
--- a/gcc/testsuite/gcc.dg/pr69172.c
+++ b/gcc/testsuite/gcc.dg/pr69172.c
@@ -1,4 +1,5 @@
-/* PR tree-optimization/69172 */
+/* PR tree-optimization/69172 - ICE in make_ssa_name_fn,
+   at tree-ssanames.c:266 */
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
 
@@ -43,3 +44,7 @@ f6 (int x)
 {
   return __builtin___mempcpy_chk (&a, &a, x, 0);
 }
+
+/* The calls above violate strict aliasing.  Eliminate the -Wrestrict
+   warnings they trigger.
+  { dg-prune-output "\\\[-Wrestrict]" } */
diff --git a/gcc/tree-ssa-alias.c b/gcc/tree-ssa-alias.c
index 9bbc163..e0fcdec 100644
--- a/gcc/tree-ssa-alias.c
+++ b/gcc/tree-ssa-alias.c
@@ -674,12 +674,23 @@ ao_ref_alias_set (ao_ref *ref)
 /* Init an alias-oracle reference representation from a gimple pointer
    PTR and a gimple size SIZE in bytes.  If SIZE is NULL_TREE then the
    size is assumed to be unknown.  The access is assumed to be only
-   to or after of the pointer target, not before it.  */
+   to or after of the pointer target, not before it.
+   When non-null, both elements of the OFFRNG array are set to the
+   range of offsets that PTR includes if it's POINTER_PLUS_EXPR.
+   This is used for -Wrestrict.  To make sure their BASE member can
+   be used to detemine mutual overlap, calls to initialize such
+   references need to pass a non-null OFFRNG even if the result is
+   not used.  */
 
 void
-ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size)
+ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size,
+			       unsigned HOST_WIDE_INT offrng[2] /* = NULL */)
 {
   HOST_WIDE_INT t, size_hwi, extra_offset = 0;
+
+  if (offrng)
+    offrng[0] = offrng[1] = 0;
+
   ref->ref = NULL_TREE;
   if (TREE_CODE (ptr) == SSA_NAME)
     {
@@ -688,12 +699,41 @@ ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size)
 	  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
 	ptr = gimple_assign_rhs1 (stmt);
       else if (is_gimple_assign (stmt)
-	       && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR
-	       && TREE_CODE (gimple_assign_rhs2 (stmt)) == INTEGER_CST)
+	       && gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
 	{
-	  ptr = gimple_assign_rhs1 (stmt);
-	  extra_offset = BITS_PER_UNIT
-			 * int_cst_value (gimple_assign_rhs2 (stmt));
+	  tree offset = gimple_assign_rhs2 (stmt);
+	  if (TREE_CODE (offset) == INTEGER_CST)
+	    {
+	      ptr = gimple_assign_rhs1 (stmt);
+	      extra_offset = BITS_PER_UNIT * int_cst_value (offset);
+
+	      if (TREE_CODE (ptr) == SSA_NAME)
+		{
+		  gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+		  if (gimple_assign_single_p (stmt)
+		      && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+		    ptr = gimple_assign_rhs1 (stmt);
+		}
+	    }
+	  else if (offrng && TREE_CODE (offset) == SSA_NAME)
+	    {
+	      wide_int min, max;
+	      value_range_type rng = get_range_info (offset, &min, &max);
+	      if (rng == VR_RANGE && wi::fits_uhwi_p (min))
+		{
+		  ptr = gimple_assign_rhs1 (stmt);
+		  offrng[0] = BITS_PER_UNIT * min.to_uhwi ();
+		  if (wi::fits_uhwi_p (max))
+		    offrng[1] = BITS_PER_UNIT * max.to_uhwi ();
+		  else
+		    offrng[1] = HOST_WIDE_INT_M1U;
+
+		  extra_offset = offrng[0];
+		}
+	      else
+		/* Offset range is indeterminate.  */
+		offrng[0] = offrng[1] = HOST_WIDE_INT_M1U;
+	    }
 	}
     }
 
@@ -2959,3 +2999,71 @@ walk_aliased_vdefs (ao_ref *ref, tree vdef,
   return ret;
 }
 
+/* Return true if REF1 and REF2 overlap one another and set *OVERLAP
+   and *ALOFF (alias offset) to the size and offset of the overlap
+   in bytes.  Otherwise, if REF1 and REF2 do not definitely overlap,
+   return false.  Used for -Wrestrict warnings.  */
+
+bool
+refs_overlap_p (ao_ref *ref1, ao_ref *ref2, unsigned HOST_WIDE_INT *overlap,
+		unsigned HOST_WIDE_INT *aloff)
+{
+  tree base1 = ao_ref_base (ref1);
+  tree base2 = ao_ref_base (ref2);
+
+  if (!base1 || !base2)
+    return false;
+
+  offset_int offset1 = ref1->offset >> LOG2_BITS_PER_UNIT;
+  offset_int offset2 = ref2->offset >> LOG2_BITS_PER_UNIT;
+
+  if (TREE_CODE (base1) == MEM_REF)
+    {
+      offset1 += mem_ref_offset (base1);
+      base1 = TREE_OPERAND (base1, 0);
+    }
+
+  if (TREE_CODE (base2) == MEM_REF)
+    {
+      offset2 += mem_ref_offset (base2);
+      base2 = TREE_OPERAND (base2, 0);
+    }
+
+  if (base1 != base2)
+    return false;
+
+  if (offset1 <= offset2)
+    {
+      *aloff = (offset2 - offset1).to_uhwi ();
+      if (ref1->size == -1)
+	{
+	  *overlap = HOST_WIDE_INT_M1U;
+	  return true;
+	}
+
+      offset_int end1 = offset1 + (ref1->size >> LOG2_BITS_PER_UNIT);
+      if (end1 > offset2)
+	{
+	  *overlap = (end1 - offset2).to_uhwi ();
+	  return true;
+	}
+    }
+  if (offset2 <= offset1)
+    {
+      *aloff = (offset1 - offset2).to_uhwi ();
+      if (ref2->size == -1)
+	{
+	  *overlap = HOST_WIDE_INT_M1U;
+	  return true;
+	}
+
+      offset_int end2 = offset2 + (ref2->size >> LOG2_BITS_PER_UNIT);
+      if (end2 > offset1)
+	{
+	  *overlap = (end2 - offset1).to_uhwi ();
+	  return true;
+	}
+    }
+
+  return false;
+}
diff --git a/gcc/tree-ssa-alias.h b/gcc/tree-ssa-alias.h
index 8c89c69..02a2be0 100644
--- a/gcc/tree-ssa-alias.h
+++ b/gcc/tree-ssa-alias.h
@@ -99,7 +99,8 @@ struct ao_ref
 
 /* In tree-ssa-alias.c  */
 extern void ao_ref_init (ao_ref *, tree);
-extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree);
+extern void ao_ref_init_from_ptr_and_size (ao_ref *, tree, tree,
+					   unsigned HOST_WIDE_INT[2] = NULL);
 extern tree ao_ref_base (ao_ref *);
 extern alias_set_type ao_ref_alias_set (ao_ref *);
 extern alias_set_type ao_ref_base_alias_set (ao_ref *);
@@ -145,6 +146,8 @@ extern void dump_points_to_info_for (FILE *, tree);
 extern void debug_points_to_info_for (tree);
 extern void dump_alias_stats (FILE *);
 
+extern bool refs_overlap_p (ao_ref *, ao_ref *, unsigned HOST_WIDE_INT *,
+			    unsigned HOST_WIDE_INT *);
 
 /* In tree-ssa-structalias.c  */
 extern unsigned int compute_may_aliases (void);
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index b0563fe..75c01b0 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "expr.h"
 #include "tree-dfa.h"
 #include "domwalk.h"
+#include "tree-ssa-alias.h"
 #include "tree-ssa-propagate.h"
 #include "params.h"
 #include "ipa-chkp.h"
@@ -1477,6 +1478,22 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 	    }
 	}
       dsi->stmt = stmt;
+
+      /* Try to detect overlap before returning.  This catches cases
+	 like strcpy (d, d + n) where n is non-constant whose range
+	 is such that (n <= strlen (d) holds).
+
+	 OLDDSI->NONZERO_chars may have been reset by this point with
+	 oldlen holding it original value.  */
+      if (olddsi && oldlen)
+	{
+	  /* Add 1 for the terminating NUL.  */
+	  tree type = TREE_TYPE (oldlen);
+	  oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
+				build_int_cst (type, 1));
+	  detect_overlap (loc, stmt, olddsi->ptr, src, oldlen, true);
+	}
+
       return;
     }
 
@@ -1559,6 +1576,12 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
   len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
 				  GSI_SAME_STMT);
+
+  if (const strinfo *chksi = olddsi ? olddsi : dsi)
+    if (si && detect_overlap (loc, stmt, chksi->ptr, si->ptr, len, true))
+      /* Avoid transforming strcpy to memcpy when overlap is detected.  */
+      return;
+
   if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     {
       fprintf (dump_file, "Optimizing: ");
@@ -1786,7 +1809,7 @@ static void
 handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr;
+  tree src, dst, srclen, dstlen, lhs, args, type, fn, objsz, endptr;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi;
@@ -1804,10 +1827,43 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   dsi = NULL;
   if (didx > 0)
     dsi = get_strinfo (didx);
+
+  srclen = NULL_TREE;
+  si = NULL;
+  idx = get_stridx (src);
+  if (idx < 0)
+    srclen = build_int_cst (size_type_node, ~idx);
+  else if (idx > 0)
+    {
+      si = get_strinfo (idx);
+      if (si != NULL)
+	srclen = get_string_length (si);
+    }
+
+  loc = gimple_location (stmt);
+
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
+      if (warn_restrict)
+	{
+	  /* The concatenation always involves copying at least one byte
+	     (the terminating nul), even if the source string is empty.
+	     If the source is unknown assume it's one character long and
+	     compute the size of the result.  */
+	  tree ressize = srclen ? srclen : size_one_node;
+	  tree type = TREE_TYPE (ressize);
+	  ressize = fold_build2 (PLUS_EXPR, type, ressize, ressize);
+	  ressize = fold_build2 (PLUS_EXPR, type, ressize,
+				 build_int_cst (type, 1));
+
+	  tree sptr = si && si->ptr ? si->ptr : src;
+	  if (detect_overlap (loc, stmt, dst, sptr, ressize, true))
+	    /* Avoid transforming overlapping strcat.  */
+	    return;
+	}
+
       /* strcat (p, q) can be transformed into
-	 tmp = p + strlen (p); endptr = strpcpy (tmp, q);
+	 tmp = p + strlen (p); endptr = stpcpy (tmp, q);
 	 with length endptr - p if we need to compute the length
 	 later on.  Don't do this transformation if we don't need
 	 it.  */
@@ -1840,19 +1896,6 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       return;
     }
 
-  srclen = NULL_TREE;
-  si = NULL;
-  idx = get_stridx (src);
-  if (idx < 0)
-    srclen = build_int_cst (size_type_node, ~idx);
-  else if (idx > 0)
-    {
-      si = get_strinfo (idx);
-      if (si != NULL)
-	srclen = get_string_length (si);
-    }
-
-  loc = gimple_location (stmt);
   dstlen = dsi->nonzero_chars;
   endptr = dsi->endptr;
 
@@ -1914,7 +1957,22 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (fn == NULL_TREE)
     return;
 
-  len = NULL_TREE;
+  if (warn_restrict && dsi && dstlen)
+    {
+      tree slen = srclen ? srclen : size_zero_node;
+      /* Compute the size of the concatenated string, including
+	 the terminating nul.  */
+      tree type = TREE_TYPE (dstlen);
+      tree ressize = fold_build2 (PLUS_EXPR, type, dstlen, slen);
+      ressize = fold_build2 (PLUS_EXPR, type, ressize, build_int_cst (type, 1));
+
+      tree sptr = si && si->ptr ? si->ptr : src;
+      if (detect_overlap (loc, stmt, dst, sptr, ressize, true))
+	/* Avoid transforming overlapping strcat.  */
+	return;
+    }
+
+  tree len = NULL_TREE;
   if (srclen != NULL_TREE)
     {
       args = TYPE_ARG_TYPES (TREE_TYPE (fn));

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-24 22:36                       ` Jeff Law
@ 2017-08-29  3:42                         ` Martin Sebor
  2017-09-01 15:15                           ` Jeff Law
  0 siblings, 1 reply; 39+ messages in thread
From: Martin Sebor @ 2017-08-29  3:42 UTC (permalink / raw)
  To: Jeff Law, Richard Biener; +Cc: Gcc Patch List

On 08/24/2017 04:09 PM, Jeff Law wrote:
> On 08/22/2017 02:45 AM, Richard Biener wrote:
>> On Mon, Aug 21, 2017 at 10:10 PM, Martin Sebor <msebor@gmail.com> wrote:
>>> On 08/09/2017 10:14 AM, Jeff Law wrote:
>>>>
>>>> On 08/06/2017 05:08 PM, Martin Sebor wrote:
>>>>
>>>>>>
>>>>>> Well, simply because the way as implemented isn't a must-alias query
>>>>>> but maybe one that's good enough for warnings (reduces false positives
>>>>>> but surely doesn't eliminate them).
>>>>>
>>>>>
>>>>> I'm very interested in reducing the rate of false positives in
>>>>> these and all other warnings.  As I mentioned in my comments,
>>>>> I did my best to weed them out of the implementation by building
>>>>> GDB, Glibc, Busybox, and the Linux kernel.  That of course isn't
>>>>> a guarantee that there aren't any.  But the first implementation
>>>>> of any non-trivial feature is never perfect, and hardly any
>>>>> warning of sufficient complexity is free of false positives, no
>>>>> matter here it's implemented (the middle-end, front-end, or
>>>>> a standalone static analysis tool).  If you spotted some cases
>>>>> I had missed I'd certainly be grateful for examples.  Otherwise,
>>>>> they will undoubtedly be reported as more software is exposed
>>>>> to the warning and, if possible, fixed, as happens with all
>>>>> other warnings.
>>>>
>>>> I think Richi is saying that the must alias query you've built isn't
>>>> proper/correct.  It's less about false positives for Richi and more
>>>> about building a proper must alias query if I understand him correctly.
>>>>
>>>> I suspect he's also saying that you can't reasonably build must-alias on
>>>> top of a may-alias query framework.  They're pretty different queries.
>>>>
>>>> If you need something that is close to, but not quite a must alias
>>>> query, then you're going to have to make a argument for that and you
>>>> can't call it a must alias query.
>>>
>>>
>>> Attached is an updated and simplified patch that avoids making
>>> changes to any of the may-alias functions.  It turns out that
>>> all the information the logic needs to determine the overlap
>>> is already in the ao_ref structures populated by
>>> ao_ref_init_from_ptr_and_size.  The only changes to the pass
>>> are the enhancement to ao_ref_init_from_ptr_and_size to make
>>> use of range info and the addition of the two new functions
>>> used by the -Wrestrict clients outside the pass.
>>
>> Warning for memcpy (p, p, ...) is going to fire false positives all around
>> given the C++ FE emits those in some cases and optimization can
>> expose that we are dealing with self-assignments.  And *p = *p is
>> valid.
> Correct.  I wound my way through this mess a while back.  Essentially
> Red Hat had a customer with code that had overlapped memcpy arguments.
> We had them use the memstomp interposition library to start tracking
> these problems down.
>
> One of the things that popped up was structure/class copies which were
> implemented via calls to memcpy.    In the case of self assignment, the
> interposition library would note the overlap and (rightly IMHO) complain.

Is this bug 32667?  I'm not having any luck reproducing it with
any of the test cases there and varying struct sizes, or with
the test case in the duplicate bug 65029 I filed for the same
thing last year.  It would be nice to have a test case.

> One could argue that GCC should emit memmove by default for structure
> assignments, only using memcpy when it knows its not doing self
> assignment (which may be hard to determine).  Furthermore, GCC should
> eliminate self structure/class assignment.

If it's still a problem emitting memmove seems like the right
thing to do.  From what I've read the performance advantage of
memcpy over memmove seems debatable at best.  Most performance
sensitive code avoids making copies of very large objects so
the only code that can be impacted doesn't care about efficiency
quite so much.  For small enough objects, inlining the copy as
GCC already does would obviate the efficiency concern altogether.

>
>
>>
>> @@ -1028,6 +1066,10 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
>>             }
>>         }
>>
>> +      /* Avoid folding the call if overlap is detected.  */
>> +      if (check_overlap && detect_overlap (loc, stmt, dest, src, len))
>> +       return false;
>> +
>>
>> no, please not.  You diagnosed the call (which might be a false positive)
>> so why keep it undefined?  The folded stmt will either have the same
>> semantics (aggregate copies expanded as memcpy) or have all reads
>> performed before writes.
> So can we distinguish here between overlap and the self-copy case?

Yes, but only in a limited subset of cases.

>
> A self-copy should just be folded away.  It's no different than x = x on
> scalars except that we've dropped it to a memcpy in the IL.  Doing so
> makes the code more efficient and removes false positives from tools
> like the memstomp interposition library, making those tools more useful.

It's possible to do in simple cases but not in general.  I agree
that in the general case when overlap is possible the only safe
solution, short of actually testing for it at runtime, is to call
memmove.

Martin

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-29  2:35                       ` Martin Sebor
@ 2017-08-29 11:32                         ` Richard Biener
       [not found]                           ` <40984eff-b156-3315-7bb5-558e9e83bf6c@gmail.com>
  0 siblings, 1 reply; 39+ messages in thread
From: Richard Biener @ 2017-08-29 11:32 UTC (permalink / raw)
  To: Martin Sebor; +Cc: Jeff Law, Gcc Patch List

On Tue, Aug 29, 2017 at 2:20 AM, Martin Sebor <msebor@gmail.com> wrote:
> On 08/22/2017 02:45 AM, Richard Biener wrote:
>>
>> On Mon, Aug 21, 2017 at 10:10 PM, Martin Sebor <msebor@gmail.com> wrote:
>>>
>>> On 08/09/2017 10:14 AM, Jeff Law wrote:
>>>>
>>>>
>>>> On 08/06/2017 05:08 PM, Martin Sebor wrote:
>>>>
>>>>>>
>>>>>> Well, simply because the way as implemented isn't a must-alias query
>>>>>> but maybe one that's good enough for warnings (reduces false positives
>>>>>> but surely doesn't eliminate them).
>>>>>
>>>>>
>>>>>
>>>>> I'm very interested in reducing the rate of false positives in
>>>>> these and all other warnings.  As I mentioned in my comments,
>>>>> I did my best to weed them out of the implementation by building
>>>>> GDB, Glibc, Busybox, and the Linux kernel.  That of course isn't
>>>>> a guarantee that there aren't any.  But the first implementation
>>>>> of any non-trivial feature is never perfect, and hardly any
>>>>> warning of sufficient complexity is free of false positives, no
>>>>> matter here it's implemented (the middle-end, front-end, or
>>>>> a standalone static analysis tool).  If you spotted some cases
>>>>> I had missed I'd certainly be grateful for examples.  Otherwise,
>>>>> they will undoubtedly be reported as more software is exposed
>>>>> to the warning and, if possible, fixed, as happens with all
>>>>> other warnings.
>>>>
>>>>
>>>> I think Richi is saying that the must alias query you've built isn't
>>>> proper/correct.  It's less about false positives for Richi and more
>>>> about building a proper must alias query if I understand him correctly.
>>>>
>>>> I suspect he's also saying that you can't reasonably build must-alias on
>>>> top of a may-alias query framework.  They're pretty different queries.
>>>>
>>>> If you need something that is close to, but not quite a must alias
>>>> query, then you're going to have to make a argument for that and you
>>>> can't call it a must alias query.
>>>
>>>
>>>
>>> Attached is an updated and simplified patch that avoids making
>>> changes to any of the may-alias functions.  It turns out that
>>> all the information the logic needs to determine the overlap
>>> is already in the ao_ref structures populated by
>>> ao_ref_init_from_ptr_and_size.  The only changes to the pass
>>> are the enhancement to ao_ref_init_from_ptr_and_size to make
>>> use of range info and the addition of the two new functions
>>> used by the -Wrestrict clients outside the pass.
>>
>>
>> Warning for memcpy (p, p, ...) is going to fire false positives all around
>> given the C++ FE emits those in some cases and optimization can
>> expose that we are dealing with self-assignments.  And *p = *p is
>> valid.
>
>
> I changed it to only warn for calls to the library function and
> not to the __builtin_xxx.  Though I haven't been able to reproduce
> the problem you referring to (bug 32667) which makes me wonder if
> it's been fixed.

@@ -699,6 +706,15 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
      DEST{,+LEN,+LEN-1}.  */
   if (operand_equal_p (src, dest, 0))
     {
+      /* Avoid diagnosing exact overlap in calls to __builtin_memcpy.
+        It's safe and may even be emitted by GCC itself (see bug
+        32667).  However, diagnose it in explicit calls to the memcpy
+        function.  */
+      if (check_overlap && *IDENTIFIER_POINTER (DECL_NAME (func)) != '_')
+       warning_at (loc, OPT_Wrestrict,

You can have explicit calls to __builtin_memcpy so that check looks bogus to me.
I think there's no way to distinguish the cases and I'd simply remove the check
and set TREE_NO_WARNING on the implicit calls generated by frontends.

>>
>> @@ -1028,6 +1066,10 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator
>> *gsi,
>>             }
>>         }
>>
>> +      /* Avoid folding the call if overlap is detected.  */
>> +      if (check_overlap && detect_overlap (loc, stmt, dest, src, len))
>> +       return false;
>> +
>>
>> no, please not.  You diagnosed the call (which might be a false positive)
>> so why keep it undefined?  The folded stmt will either have the same
>> semantics (aggregate copies expanded as memcpy) or have all reads
>> performed before writes.
>
>
> My goal was to follow the approach reflected in the comments
> elsewhere in the file:
>
>     /* Out of bound array access.  Value is undefined,
>        but don't fold.  */

This is done to keep -Warray-bounds triggering IIRC.  Also there's no
good value to fold to (well, we used zero).  I also vetoed emitting
warnings from folding code given that's inherently fragile ... (I think
I already said I don't like warning from gimple_fold_builtin_memory_op
too much either).

> While gimple_fold_builtin_memory_op may be able to provide well-
> defined behavior for the otherwise undefined semantics in a small
> subset of cases, it doesn't attempt to fold many more that it
> otherwise could (it only folds calls with sizes that are powers
> of 2).  So it seems of dubious value to make an effort in this
> relatively small subset of cases.
>
> In my experience, users also don't appreciate optimizations that
> "rely on" undefined behavior one way or the other.  What they would
> like to see instead is that when their compiler detects undefined
> behavior it diagnoses it but either doesn't use it to make
> optimization decisions, or uses it to disable them.  For calls to
> library functions, that in my view means making the call and not
> folding it.  (Btw., do we have some sort of a policy or guideline
> for how to handle such cases in general?)
>
> With all that said, I don't see a big problem with proceeding with
> the folding as you suggest either, so I did and added a comment
> documenting it and a test to verify this guarantee.
>
> I should also acknowledge that in my approach I forgot that once
> the overlap has been diagnosed and the no-warning bit set, the
> next call to gimple_fold_builtin_memory_op() with the same
> statement would just go ahead and fold it anyway.  So the tests
> were ineffective regardless.

Yeah.

@@ -748,6 +764,27 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
          unsigned ilen = tree_to_uhwi (len);
          if (pow2p_hwi (ilen))
            {
+             if (check_overlap)
+               {
+                 /* Detect overlapping copies and issue -Wrestrict.  */
+                 if (detect_overlap (loc, stmt, dest, src, len, endp == 2))
+                   gimple_set_no_warning (stmt, true);
+                 else if (TREE_CODE (dest) != SSA_NAME
+                          || TREE_CODE (src) != SSA_NAME)
+                   {
+                     /* If no overlap is detected and at least one of
+                        the arguments is not in an SSA form defer folding
+                        until they both are so that aliasing can be
+                        determined with greater accuracy.  */
+                     ao_ref dstref;
+                     ao_ref srcref;
+                     ao_ref_init_from_ptr_and_size (&dstref, dest, len);
+                     ao_ref_init_from_ptr_and_size (&srcref, src, len);
+                     if (refs_may_alias_p_1 (&dstref, &srcref, true))
+                       return false;

please not -- folding these early is very important for inlining decisions
and early simplifications.  IMHO diagnostics should _never_ disable
optimizations.  Even more so code generation should not change
with -W options (similar to -g).

   if (operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+                 "%qD source argument is the same as destination",
+                 func);
+

syle issue - you use func once so please remove it and put
the gimple_call_fndecl call in the warning_at argument.  No reason
for so much extra vertical space either.

@@ -7226,3 +7288,4 @@ gimple_stmt_integer_valued_real_p (gimple *stmt,
int depth)
       return false;
     }
 }
+

spurious blank line added.

+         tree offset = gimple_assign_rhs2 (stmt);
+         if (TREE_CODE (offset) == INTEGER_CST)
+           {
+             ptr = gimple_assign_rhs1 (stmt);
+             extra_offset = BITS_PER_UNIT * int_cst_value (offset);
+
+             if (TREE_CODE (ptr) == SSA_NAME)

IMHO spurious vertical space.


>> The ao_ref_init_from_ptr_and_size change misses a changelog entry.
>>
>> +detect_overlap (location_t loc, gimple *stmt, tree dst, tree src, tree
>> size,
>> +               bool adjust /* = false */)
>> +{
>> +  ao_ref dstref, srcref;
>> +  unsigned HOST_WIDE_INT range[2];
>> +
>> +  /* Initialize and store the lower bound of a constant offset (in
>> +     bits), disregarding the offset for the destination.  */
>> +  ao_ref_init_from_ptr_and_size (&dstref, dst, size, range);
>> +  ao_ref_init_from_ptr_and_size (&srcref, src, size, range);
>>
>> just pass NULL range to the first call?
>
>
> The argument needs to be non-null for the base to be determined
> the same way between the two references.  (It's not obvious so
> I've added a comment to the description of the function.)
>
>>
>> -  ref->ref = NULL_TREE;
>> +
>> +  if (offrng)
>> +    offrng[0] = offrng[1] = 0;
>> +
>> + ref->ref = NULL_TREE;
>>
>> bogus indent
>>
>> +         else if (offrng && TREE_CODE (offset) == SSA_NAME)
>> +           {
>> +             wide_int min, max;
>> +             value_range_type rng = get_range_info (offset, &min, &max);
>> +             if (rng == VR_RANGE && wi::fits_uhwi_p (min))
>> +               {
>> +                 ptr = gimple_assign_rhs1 (stmt);
>> +                 offrng[0] = BITS_PER_UNIT * min.to_uhwi ();
>> +                 offrng[1] = BITS_PER_UNIT * max.to_uhwi ();
>> +
>> +                 extra_offset = offrng[0];
>>
>> you didnt' check whether max fits uhwi.  The effect of passing offrng
>> is not documented.
>
>
> Done.

+         else if (offrng && TREE_CODE (offset) == SSA_NAME)
+           {
+             wide_int min, max;
+             value_range_type rng = get_range_info (offset, &min, &max);
+             if (rng == VR_RANGE && wi::fits_uhwi_p (min))
+               {
+                 ptr = gimple_assign_rhs1 (stmt);
+                 offrng[0] = BITS_PER_UNIT * min.to_uhwi ();

why store those as bits?  The fits_uhwi_p check is bogus that way
given the multiplication may still overflow.

+                 if (wi::fits_uhwi_p (max))
+                   offrng[1] = BITS_PER_UNIT * max.to_uhwi ();
+                 else
+                   offrng[1] = HOST_WIDE_INT_M1U;
+
+                 extra_offset = offrng[0];
+               }
+             else
+               /* Offset range is indeterminate.  */
+               offrng[0] = offrng[1] = HOST_WIDE_INT_M1U;

note the ao_ref in principle already has enough info to capture such
variable range
via offset, size and max_size.  Indeterminate in that case would be
offset = 0 and max_size = -1.  Given you adjust size by offrng[0] a good value
for "indeterminate" is zero, no?  You initialize it to that at the start of the
function anyway.  And you do the adjustment unconditionally in detect_overlap.

So what I'm saying is that, why not just add a bool to
ao_ref_init_from_ptr_and_size
to say "use ranges" and then appropriately adjust offset and max_size instead of
adding the offrange[] output parameter?  Eventually it's even worth doing this
unconditionally.

_note_ that offsets of POINTER_PLUS_EXPR can be negative, sth that
the unsignedness of offrng doesn't seem to consider?  That is,
the offset argument of POINTER_PLUS_EXPR has to be interpreted
as a signed entity (yeah, stupid IL enforcement of _un_signed sizetype
here...) which means literally using get_range_info on the offset argument
is broken.  You have to convert the range/anti-range to the appropriate
signed variant.

>>
>> +             else
>> +               /* Offset range is indeterminate.  */
>> +               offrng[0] = offrng[1] = HOST_WIDE_INT_M1U;
>>
>> I believe a cleaner interface would be to do
>>
>> void
>> ao_ref_init_from_ptr_and_size (ao_ref *ref, tree ptr, tree size, tree
>> *var_byte_offset)
>>
>> and set *var_byte_offset to your 'offset' above, leaving 'ref'
>> unchanged.  The caller
>> can then get at the range info of var_byte_offset and adjust
>> ref->offset if desired.
>> The indeterminate state is then much cleaner - NULL_TREE.
>
>
> It would make the interface cleaner and the function simpler at
> the expense of all the callers having to extract the range and
> deal with the additional complexity.  There are six call sites
> so that would not be an overall improvement.  I'd need to
> introduce a wrapper function and do the work there.  I don't
> see the advantage of that approach but I also don't have
> a problem with introducing the overload if you consider
> the change above necessary.

Please investigate the possibility to go away with offrange[] and instead
use a flag.

>> +unsigned HOST_WIDE_INT
>> +refs_overlap (ao_ref *ref1, ao_ref *ref2, unsigned HOST_WIDE_INT *aloff)
>> +{
>>
>> bool
>> refs_must_overlap_p (ao_ref *, ao_ref *, unsigned HOST_WIDE_INT *off,
>> unsinged HOST_WIDE_INT *size)
>
>
> I avoided the words "must" and "alias" on account of your concern
> that the function could be misunderstood to be intended to gate
> optimizations.  It also wasn't meant to be used as a predicate
> (even though it could be) but I have changed its signature as you
> suggest.

Hmm, ok.  So the function should be called refs_known_overlap_p
then to document that when it returns false we don't know whether
the refs overlap.  If it returns true the computed overlap should be
correct so it can be used for optimization purposes (otherwise
it doesn't belong in tree-ssa-alias.c).

I think the function isn't conservative given it uses ->size rather
than ->max_size or doesn't bail out if size == -1 || size != max_size.

>> your return values are in bytes thus
>>
>> +
>> +      // Compare pointers.
>> +      offset1 += mem_ref_offset (base1) << LOG2_BITS_PER_UNIT;
>> +      offset2 += mem_ref_offset (base2) << LOG2_BITS_PER_UNIT;
>> +      base1 = TREE_OPERAND (base1, 0);
>>
>> just do the intermediate computations in bytes as well.
>
>
> That is (slightly) simpler, thank you.
>
>>
>> It looks like it is unspecified relative to which ref the offset is given,
>> how's that useful information then -- the diagnostic doesn't seem
>> too helpful?  Why not keep it relative to the first ref and thus make
>> aloff signed?
>
>
> If I understand your suggestion, an offset relative to the first
> reference (which I think is the destination pointer at all call
> sites) would be zero whenever the destination pointer were greater
> than the source, and positive(?) otherwise.  E.g., like so:

No, it would be negative.

>
>   memcpy (d, d + 1, 2);   // offset is +1
>
>   memcpy (d + 1, d, 2);   // offset is 0.

offset is -1 (and aloff should be HOST_WIDE_INT *)

> The second case doesn't seem helpful.
>
> As it is, the offset is the absolute value of the distance between
> the source and the destination pointers:
>
>   memcpy (d, d + 1, 2);   // offset is 1
>
>   memcpy (d + 1, d, 2);   // offset is also 1
>
> This seems more meaningful to me but I welcome suggestions for
> improvements.
>
>> detect_overlap doesn't belong to tree-ssa-alias.[ch].
>
>
> I moved it to builtins.c.   If you find that unsuitable there as
> well then please suggest an appropriate home for the function.

builtins.c works for me.

> Attached is an updated patch retested on x86_64-linux.
>
> Martin

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

* Re: [PING #2] [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-08-29  3:42                         ` Martin Sebor
@ 2017-09-01 15:15                           ` Jeff Law
  0 siblings, 0 replies; 39+ messages in thread
From: Jeff Law @ 2017-09-01 15:15 UTC (permalink / raw)
  To: Martin Sebor, Richard Biener; +Cc: Gcc Patch List

On 08/28/2017 06:27 PM, Martin Sebor wrote:
>> Correct.  I wound my way through this mess a while back.  Essentially
>> Red Hat had a customer with code that had overlapped memcpy arguments.
>> We had them use the memstomp interposition library to start tracking
>> these problems down.
>>
>> One of the things that popped up was structure/class copies which were
>> implemented via calls to memcpy.    In the case of self assignment, the
>> interposition library would note the overlap and (rightly IMHO) complain.
> 
> Is this bug 32667?  I'm not having any luck reproducing it with
> any of the test cases there and varying struct sizes, or with
> the test case in the duplicate bug 65029 I filed for the same
> thing last year.  It would be nice to have a test case.
It certainly seems to be in the same class as 32667.  I'm not sure if
it's exactly the same root cause in terms of the front end interactions,
but the net result is the same.

I just reviewed the discussion in my outbox -- I didn't see sample code
or a reference to 32667, but I'm only looking at one side of the
conversation.


> 
>> One could argue that GCC should emit memmove by default for structure
>> assignments, only using memcpy when it knows its not doing self
>> assignment (which may be hard to determine).  Furthermore, GCC should
>> eliminate self structure/class assignment.
> 
> If it's still a problem emitting memmove seems like the right
> thing to do.  From what I've read the performance advantage of
> memcpy over memmove seems debatable at best.  Most performance
> sensitive code avoids making copies of very large objects so
> the only code that can be impacted doesn't care about efficiency
> quite so much.  For small enough objects, inlining the copy as
> GCC already does would obviate the efficiency concern altogether.
I personally feel we reached the point of diminishing returns years ago
--  in attempts to inline/unroll the copies in GCC, in terms of glibc's
copier implementations and in the gain of using memcpy over memmove vs
the pain of folks that can't be bothered to do it right WRT overlapped
copies.

But that's a bit of a digression.  THe issue at handle is structure
assignments that GCC is turning into memcpys.  That's a  much smaller
subset of calls to memcpy and more likely to not be a performance issue
to change.


>> A self-copy should just be folded away.  It's no different than x = x on
>> scalars except that we've dropped it to a memcpy in the IL.  Doing so
>> makes the code more efficient and removes false positives from tools
>> like the memstomp interposition library, making those tools more useful.
> 
> It's possible to do in simple cases but not in general.  I agree
> that in the general case when overlap is possible the only safe
> solution, short of actually testing for it at runtime, is to call
> memmove.
So let's zap those where we know its a self copy.  I'd look favorably on
a patch that use memmove on the others, but I won't immediately approve
it without wider buy-in as I expect it could well be controversial.
These should be separate patches as the former should go forward
immediately.

Jeff

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

* Re: [PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
       [not found]                           ` <40984eff-b156-3315-7bb5-558e9e83bf6c@gmail.com>
@ 2017-10-24  6:40                             ` Martin Sebor
  2017-11-10  0:28                               ` [PING][PATCH] " Martin Sebor
  0 siblings, 1 reply; 39+ messages in thread
From: Martin Sebor @ 2017-10-24  6:40 UTC (permalink / raw)
  To: Richard Biener, Jeff Law; +Cc: Gcc Patch List

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

Attached is a reworked solution to enhance -Wrestrict while
avoiding changing tree-vrp.c or any other VRP machinery.  Richard,
in considering you suggestions I realized that the ao_ref struct
isn't general enough to detect the kinds of problems I needed to
etect (storing bit-offsets in HOST_WIDE_INT means out-of-bounds
offsets cannot be represented or detected, leading to either false
positives or false negatives).

Instead, the solution adds a couple of small classes to builtins.c
to overcome this limitation (I'm contemplating moving them along
with -Wstringop-overflow to a separate file to keep builtins.c
from getting too much bigger).

The detection of out-of-bounds offsets and overlapping accesses
is relatively simple but the rest of the changes are somewhat
involved because of the computation of the offsets and sizes of
the overlaps.

Jeff, as per your suggestion/request in an earlier review (bug
81117) I've renamed some of the existing functions to better
reflect their new function (including renaming strlen_optimize_stmt
in tree-ssa-strlen.c to strlen_check_and_optimize_stmt).  There's
quite a bit of churn due to some of this renaming.  I don't think
this renaming makes the review too difficult but if you feel
differently I can [be persuaded to] split it out into a separate
patch.

To validate the patch I compiled the Linux kernel and Binutils/GDB.
There's one false positive I'm working on resolving that's caused
by an incorrect interpretation of an offset in a range whose lower
bound is greater than its upper bound.  This it so say that while
I'm aware the patch isn't perfect it already works reasonably well
in practice and I think it's close enough to review.

Thanks
Martin

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

PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over self

gcc/c-family/ChangeLog:

	PR tree-optimization/78918
	* c-common.c (check_function_restrict): Avoid checking built-ins.
	* c.opt (-Wrestrict): Include in -Wall.

gcc/ChangeLog:

	PR tree-optimization/78918
	* builtins.c (builtin_memref, builtin_access): New classes.
	(check_bounds_or_overlap, maybe_diag_overlap): New static functions.
	(maybe_diag_offset_bounds): Same.
	(check_sizes): Rename...
	(check_access): ...to this.  Rename function arguments for clarity.
	(check_memop_sizes): Adjust names.
	(expand_builtin_memchr, expand_builtin_memcpy): Same.
	(expand_builtin_memmove, expand_builtin_mempcpy): Same.
	(expand_builtin_strcat, expand_builtin_stpncpy): Same.
	(check_strncat_sizes, expand_builtin_strncat): Same.
	(expand_builtin_strncpy, expand_builtin_memset): Same.
	(expand_builtin_bzero, expand_builtin_memcmp): Same.
	(expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
	(maybe_emit_sprintf_chk_warning): Same.
	(expand_builtin_strcpy): Adjust.
	(expand_builtin_stpcpy): Same.
	(expand_builtin_with_bounds): Detect out-of-bounds/overlapping
	accesses in pointer-checking forms of memcpy, memmove, and mempcpy.
	(gcall_to_tree_minimal, check_bounds_or_overlap, max_object_size):
	Define new functions.
	* builtins.h (check_bounds_or_overlap, max_object_size): Declare.
	* calls.c (alloc_max_size): Call max_object_size instead of
	hardcoding ssizetype limit.  Fix a typo.
	(get_size_range): Handle new argument.
	* calls.h (get_size_range): Add a new argument.
	* cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
	* doc/invoke.texi (-Wrestrict): Adjust, add example.
	* gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
	operations.
	(gimple_fold_builtin_memory_chk): Same.
	(gimple_fold_builtin_stxcpy_chk): New function.
	* gimple.c (gimple_build_call_from_tree): Propagate location.
	* tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
	operations.
	(handle_builtin_strcat): Same.
	(strlen_optimize_stmt): Rename...
	(strlen_check_and_optimize_stmt): ...to this.  Handle strncat,
	stpncpy, strncpy, and their checking forms.

gcc/testsuite/ChangeLog:

	PR tree-optimization/78918
	* c-c++-common/Warray-bounds.c: New test.
	* c-c++-common/Wrestrict-2.c: New test.
	* c-c++-common/Wrestrict.c: New test.
	* c-c++-common/Wrestrict.s: New test.
	* gcc.dg/Walloca-1.c: Adjust/
	* gcc.dg/memcpy-6.c: New test.
	* gcc.dg/pr69172.c: Adjust.
	* gcc.target/i386/chkp-stropt-17.c: New test.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index d3498bb..8054d50 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "calls.h"
 #include "varasm.h"
+#include "tree-dfa.h"
 #include "tree-object-size.h"
 #include "realmpfr.h"
 #include "cfgrtl.h"
@@ -192,6 +193,91 @@ static tree do_mpfr_remquo (tree, tree, tree);
 static tree do_mpfr_lgamma_r (tree, tree, tree);
 static void expand_builtin_sync_synchronize (void);
 
+struct builtin_memref;
+
+/* Description of a memory access by a raw memory or string built-in
+   function.  */
+struct builtin_access
+{
+private:
+  /* Temporaries used to compute the final result.  */
+  offset_int dstoff[2];
+  offset_int srcoff[2];
+  offset_int dstsiz[2];
+  offset_int srcsiz[2];
+
+  /* Member function to call to determine overlap.  */
+  bool (builtin_access::*detect_overlap) ();
+
+public:
+  /* Destination and source memory reference.  */
+  builtin_memref* const dstref;
+  builtin_memref* const srcref;
+  /* The minimum and maximum size of the access. */
+  HOST_WIDE_INT sizrange[2];
+
+  /* The minimum and maximum offset of an overlap of the access
+     (if it does, in fact, overlap), and the size of the overlap.  */
+  HOST_WIDE_INT ovloff[2];
+  HOST_WIDE_INT ovlsiz[2];
+
+  builtin_access (tree, builtin_memref &, builtin_memref &);
+
+  /* Entry point to determine overlap.  */
+  bool overlap ();
+
+private:
+  /* Implementation functions used to determine overlap.  */
+  bool generic_overlap ();
+  bool strcat_overlap ();
+  bool strcpy_overlap ();
+
+  bool no_overlap ()
+  {
+    return false;
+  }
+};
+
+/* Description of a memory reference by a built-in function.  */
+struct builtin_memref
+{
+  /* The original pointer argument to the built-in function.  */
+  tree ptr;
+  /* The referenced subobject or NULL if not available, and the base
+     object of the memory reference or NULL.  */
+  tree ref;
+  tree base;
+
+  /* The size of the BASE object, PTRDIFF_MAX if indeterminate, and
+     -1 until (lazily) initialized.  */
+  offset_int objsize;
+
+  /* The offset of the referenced subobject.  Used to avoid warnings for
+     (apparently) possibly but not definitively overlapping accesses to
+     member arrays.  */
+  offset_int refoff;
+
+  /* The offset range relative to the base.  */
+  offset_int offrange[2];
+  /* The size range of the access.  */
+  offset_int sizrange[2];
+
+  /* True for "bounded" string functions like strncat, and strncpy
+     and their variants that specify either an exact or upper bound
+     on the size of the accesses they perform.  For strncat both
+     the source and destination references are bounded.  For strncpy
+     only the destination reference is.  */
+  bool strbounded_p;
+
+  builtin_memref (tree, tree);
+
+  tree offset_out_of_bounds () const;
+};
+
+static bool check_bounds_or_overlap (tree, tree, tree, tree, tree,
+				      bool = false);
+static bool maybe_diag_overlap (location_t, tree, builtin_access &);
+
 /* Return true if NAME starts with __builtin_ or __sync_.  */
 
 static bool
@@ -2962,39 +3048,157 @@ determine_block_size (tree len, rtx len_rtx,
 			  GET_MODE_MASK (GET_MODE (len_rtx)));
 }
 
+/* Validate REF offsets in an EXPRession passed as an argument to a CALL
+   to a built-in function FUNC to make sure they are within the bounds
+   of the referenced object if its size is known, or PTRDIFF_MAX otherwise.
+   Both initial values of the offsets and their final value computed by
+   the function by incrementing the initial value by the size are
+   validated.  Return true if the offsets are not valid and a diagnostic
+   has been isssued.  */
+
+static bool
+maybe_diag_offset_bounds (location_t loc, tree call, tree func, tree expr,
+			  const builtin_memref &ref)
+{
+  if (!warn_array_bounds)
+    return false;
+
+  tree oobref = ref.offset_out_of_bounds ();
+  if (!oobref)
+    return false;
+
+  if (EXPR_HAS_LOCATION (expr))
+    loc = EXPR_LOCATION (expr);
+
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  tree type;
+
+  char rangestr[2][64];
+  if (ref.offrange[0] == ref.offrange[1])
+    sprintf (rangestr[0], "%lli", (long long) ref.offrange[0].to_shwi ());
+  else
+    sprintf (rangestr[0], "[%lli, %lli]",
+	     (long long) ref.offrange[0].to_shwi (),
+	     (long long) ref.offrange[1].to_shwi ());
+
+  if (oobref == error_mark_node)
+    {
+      if (ref.sizrange[0] == ref.sizrange[1])
+	sprintf (rangestr[1], "%lli", (long long) ref.sizrange[0].to_shwi ());
+      else
+	sprintf (rangestr[1], "[%lli, %lli]",
+		 (long long) ref.sizrange[0].to_shwi (),
+		 (long long) ref.sizrange[1].to_shwi ());
+
+      if (DECL_P (ref.base)
+	  && TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
+	{
+	  if (warning_at (loc, OPT_Warray_bounds,
+			  "%K%qD pointer overflow between offset %s "
+			  "and size %s accessing array %qD with type %qT",
+			  call, func, rangestr[0], rangestr[1], ref.base, type))
+	    inform (DECL_SOURCE_LOCATION (ref.base),
+		    "array %qD declared here", ref.base);
+	  else
+	    warning_at (loc, OPT_Warray_bounds,
+			"%K%qD pointer overflow between offset %s "
+			"and size %s",
+			call, func, rangestr[0], rangestr[1]);
+	}
+      else
+	warning_at (loc, OPT_Warray_bounds,
+		    "%K%qD pointer overflow between offset %s "
+		    "and size %s",
+		    call, func, rangestr[0], rangestr[1]);
+    }
+  else if (oobref == ref.base)
+    {
+      if (DECL_P (ref.base)
+	  && TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
+	{
+	  if (warning_at (loc, OPT_Warray_bounds,
+			  "%K%qD offset %s is out of the bounds of array "
+			  "%qD with type %qT",
+			  call, func, rangestr[0], ref.base, type))
+	    inform (DECL_SOURCE_LOCATION (ref.base),
+		    "array %qD declared here", ref.base);
+	}
+      else
+	warning_at (loc, OPT_Warray_bounds,
+		    "%K%qD offset %s is out of the bounds [0, %wu]",
+		    call, func, rangestr[0], ref.objsize.to_uhwi ());
+    }
+  else if (TREE_CODE (ref.ref) == MEM_REF)
+    {
+      tree type = TREE_TYPE (TREE_OPERAND (ref.ref, 0));
+      if (POINTER_TYPE_P (type))
+	type = TREE_TYPE (type);
+      type = TYPE_MAIN_VARIANT (type);
+
+      warning_at (loc, OPT_Warray_bounds,
+		  "%K%qD offset %s from the object at %qE is out "
+		  "of the bounds of %qT",
+		  call, func, rangestr[0], ref.base, type);
+    }
+  else
+    {
+      type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
+
+      warning_at (loc, OPT_Warray_bounds,
+		"%K%qD offset %s from the object at %qE is out "
+		"of the bounds of referenced subobject %qD with type %qT "
+		"at offset %wu",
+		call, func, rangestr[0], ref.base, TREE_OPERAND (ref.ref, 1),
+		type, ref.refoff.to_uhwi ());
+    }
+
+  return true;
+}
+
 /* Try to verify that the sizes and lengths of the arguments to a string
    manipulation function given by EXP are within valid bounds and that
-   the operation does not lead to buffer overflow.  Arguments other than
-   EXP may be null.  When non-null, the arguments have the following
-   meaning:
-   SIZE is the user-supplied size argument to the function (such as in
-   memcpy(d, s, SIZE) or strncpy(d, s, SIZE).  It specifies the exact
-   number of bytes to write.
-   MAXLEN is the user-supplied bound on the length of the source sequence
+   the operation does not lead to buffer overflow or read past the end.
+   For copy operations that must not overlap also verify that the source
+   and destination do not overlap one another.
+   Arguments other than EXP may be null.  When non-null, the arguments
+   have the following meaning:
+   DST is the destination of a copy call or null otherwise.
+   SRC is the source of a copy call or null otherwise.
+   DSTWRITE is the number of bytes written into the destination obtained
+   from the user-supplied size argument to the function (such as in
+   memcpy(DST, SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+   MAXREAD is the user-supplied bound on the length of the source sequence
    (such as in strncat(d, s, N).  It specifies the upper limit on the number
-   of bytes to write.
-   SRC is the source string (such as in strcpy(d, s)) when the expression
-   EXP is a string function call (as opposed to a memory call like memcpy).
-   As an exception, SRC can also be an integer denoting the precomputed
-   size of the source string or object (for functions like memcpy).
-   OBJSIZE is the size of the destination object specified by the last
+   of bytes to write.  If null it's taken to be the same as DSTWRITE.
+   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+   expression EXP is a string function call (as opposed to a memory call
+   like memcpy).  As an exception, SRCSTR can also be an integer denoting
+   the precomputed size of the source string or object (for functions like
+   memcpy).
+   DSTSIZE is the size of the destination object specified by the last
    argument to the _chk builtins, typically resulting from the expansion
-   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
-   OBJSIZE).
+   of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
+   DSTSIZE).
 
-   When SIZE is null LEN is checked to verify that it doesn't exceed
+   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
-   If the call is successfully verified as safe from buffer overflow
-   the function returns true, otherwise false..  */
+   If the call is successfully verified as safe return true, otherwise
+   return false.  */
 
 static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_access (tree exp, tree dst, tree src, tree dstwrite,
+	      tree maxread, tree srcstr, tree dstsize)
 {
+  int opt = OPT_Wstringop_overflow_;
+
   /* The size of the largest object is half the address space, or
-     SSIZE_MAX.  (This is way too permissive.)  */
-  tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
+     PTRDIFF_MAX.  (This is way too permissive.)  */
+  tree maxobjsize = max_object_size ();
 
+  /* Either the length of the source string for string functions or
+     the size of the source object for raw memory functions.  */
   tree slen = NULL_TREE;
 
   tree range[2] = { NULL_TREE, NULL_TREE };
@@ -3003,28 +3207,28 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
      function like strcpy is not known and the only thing that is
      known is that it must be at least one (for the terminating nul).  */
   bool at_least_one = false;
-  if (src)
+  if (srcstr)
     {
-      /* SRC is normally a pointer to string but as a special case
+      /* SRCSTR is normally a pointer to string but as a special case
 	 it can be an integer denoting the length of a string.  */
-      if (POINTER_TYPE_P (TREE_TYPE (src)))
+      if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
 	{
 	  /* Try to determine the range of lengths the source string
 	     refers to.  If it can be determined and is less than
-	     the upper bound given by MAXLEN add one to it for
+	     the upper bound given by MAXREAD add one to it for
 	     the terminating nul.  Otherwise, set it to one for
-	     the same reason, or to MAXLEN as appropriate.  */
-	  get_range_strlen (src, range);
-	  if (range[0] && (!maxlen || TREE_CODE (maxlen) == INTEGER_CST))
+	     the same reason, or to MAXREAD as appropriate.  */
+	  get_range_strlen (srcstr, range);
+	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
 	    {
-	      if (maxlen && tree_int_cst_le (maxlen, range[0]))
-		range[0] = range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[0]))
+		range[0] = range[1] = maxread;
 	      else
 		range[0] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[0], size_one_node);
 
-	      if (maxlen && tree_int_cst_le (maxlen, range[1]))
-		range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[1]))
+		range[1] = maxread;
 	      else if (!integer_all_onesp (range[1]))
 		range[1] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[1], size_one_node);
@@ -3038,10 +3242,10 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	    }
 	}
       else
-	slen = src;
+	slen = srcstr;
     }
 
-  if (!size && !maxlen)
+  if (!dstwrite && !maxread)
     {
       /* When the only available piece of data is the object size
 	 there is nothing to do.  */
@@ -3049,20 +3253,18 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	return true;
 
       /* Otherwise, when the length of the source sequence is known
-	 (as with with strlen), set SIZE to it.  */
+	 (as with strlen), set DSTWRITE to it.  */
       if (!range[0])
-	size = slen;
+	dstwrite = slen;
     }
 
-  if (!objsize)
-    objsize = maxobjsize;
+  if (!dstsize)
+    dstsize = maxobjsize;
 
-  /* The SIZE is exact if it's non-null, constant, and in range of
-     unsigned HOST_WIDE_INT.  */
-  bool exactsize = size && tree_fits_uhwi_p (size);
+  if (dstwrite)
+    get_size_range (dstwrite, range);
 
-  if (size)
-    get_size_range (size, range);
+  tree func = get_callee_fndecl (exp);
 
   /* First check the number of bytes to be written against the maximum
      object size.  */
@@ -3075,30 +3277,34 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	warning_at (loc, opt,
 		    "%K%qD specified size %E "
 		    "exceeds maximum object size %E",
-		    exp, get_callee_fndecl (exp), range[0], maxobjsize);
+		    exp, func, range[0], maxobjsize);
 	  else
 	    warning_at (loc, opt,
 			"%K%qD specified size between %E and %E "
 			"exceeds maximum object size %E",
-			exp, get_callee_fndecl (exp),
+			exp, func,
 			range[0], range[1], maxobjsize);
       return false;
     }
 
+  /* The number of bytes to write is "exact" if DSTWRITE is non-null,
+     constant, and in range of unsigned HOST_WIDE_INT.  */
+  bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite);
+
   /* Next check the number of bytes to be written against the destination
      object size.  */
-  if (range[0] || !exactsize || integer_all_onesp (size))
+  if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
     {
       if (range[0]
-	  && ((tree_fits_uhwi_p (objsize)
-	       && tree_int_cst_lt (objsize, range[0]))
-	      || (tree_fits_uhwi_p (size)
-		  && tree_int_cst_lt (size, range[0]))))
+	  && ((tree_fits_uhwi_p (dstsize)
+	       && tree_int_cst_lt (dstsize, range[0]))
+	      || (tree_fits_uhwi_p (dstwrite)
+		  && tree_int_cst_lt (dstwrite, range[0]))))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
 
-	  if (size == slen && at_least_one)
+	  if (dstwrite == slen && at_least_one)
 	    {
 	      /* This is a call to strcpy with a destination of 0 size
 		 and a source of unknown length.  The call will write
@@ -3106,7 +3312,7 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else if (tree_int_cst_equal (range[0], range[1]))
 	    warning_at (loc, opt,
@@ -3115,21 +3321,21 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 			      "of size %E overflows the destination")
 			 : G_("%K%qD writing %E bytes into a region "
 			      "of size %E overflows the destination")),
-			exp, get_callee_fndecl (exp), range[0], objsize);
+			exp, func, range[0], dstsize);
 	  else if (tree_int_cst_sign_bit (range[1]))
 	    {
 	      /* Avoid printing the upper bound if it's invalid.  */
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else
 	    warning_at (loc, opt,
 			"%K%qD writing between %E and %E bytes into "
 			"a region of size %E overflows the destination",
-			exp, get_callee_fndecl (exp), range[0],	range[1],
-			objsize);
+			exp, func, range[0], range[1],
+			dstsize);
 
 	  /* Return error when an overflow has been detected.  */
 	  return false;
@@ -3139,11 +3345,15 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
   /* Check the maximum length of the source sequence against the size
      of the destination object if known, or against the maximum size
      of an object.  */
-  if (maxlen)
+  if (maxread)
     {
-      get_size_range (maxlen, range);
+      get_size_range (maxread, range);
 
-      if (range[0] && objsize && tree_fits_uhwi_p (objsize))
+      /* Use the lower end for MAXREAD from now on.  */
+      if (range[0])
+	maxread = range[0];
+
+      if (range[0] && dstsize && tree_fits_uhwi_p (dstsize))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
@@ -3157,40 +3367,41 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], maxobjsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], range[1], maxobjsize);
 
 	      return false;
 	    }
 
-	  if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
+	  if (dstsize != maxobjsize && tree_int_cst_lt (dstsize, range[0]))
 	    {
 	      if (tree_int_cst_equal (range[0], range[1]))
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], objsize);
+			    exp, func,
+			    range[0], dstsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], range[1], objsize);
+			    exp, func,
+			    range[0], range[1], dstsize);
 	      return false;
 	    }
 	}
     }
 
+  /* Check for reading past the end of SRC.  */
   if (slen
-      && slen == src
-      && size && range[0]
+      && slen == srcstr
+      && dstwrite && range[0]
       && tree_int_cst_lt (slen, range[0]))
     {
       location_t loc = tree_nonartificial_location (exp);
@@ -3200,23 +3411,32 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		    (tree_int_cst_equal (range[0], integer_one_node)
 		     ? G_("%K%qD reading %E byte from a region of size %E")
 		     : G_("%K%qD reading %E bytes from a region of size %E")),
-		    exp, get_callee_fndecl (exp), range[0], slen);
+		    exp, func, range[0], slen);
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
 	  warning_at (loc, opt,
 		      "%K%qD reading %E or more bytes from a region "
 		      "of size %E",
-		      exp, get_callee_fndecl (exp), range[0], slen);
+		      exp, func, range[0], slen);
 	}
       else
 	warning_at (loc, opt,
 		    "%K%qD reading between %E and %E bytes from a region "
 		    "of size %E",
-		    exp, get_callee_fndecl (exp), range[0], range[1], slen);
+		    exp, func, range[0], range[1], slen);
       return false;
     }
 
+  /* Check for out-of-bounds offsets and (except for memmove) overlapping
+     copies.  Avoid warning if one has already been issued (unlike
+     -Wstringop-overflow, -Wrestrict is checked in several places and
+     TREE_NO_WARNING applies to it but not to the former (there should
+     be a bit for every kind of warning)).  */
+  if (dst && src && !TREE_NO_WARNING (exp)
+      && !check_bounds_or_overlap (exp, dst, src, dstwrite, maxread))
+    return false;
+
   return true;
 }
 
@@ -3244,11 +3464,8 @@ compute_objsize (tree dest, int ostype)
    (no overflow or invalid sizes), false otherwise.  */
 
 static bool
-check_memop_sizes (tree exp, tree dest, tree src, tree size)
+check_memop_access (tree exp, tree dest, tree src, tree size)
 {
-  if (!warn_stringop_overflow)
-    return true;
-
   /* For functions like memset and memcpy that operate on raw memory
      try to determine the size of the largest source and destination
      object using type-0 Object Size regardless of the object size
@@ -3256,8 +3473,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size)
   tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
   tree dstsize = compute_objsize (dest, 0);
 
-  return check_sizes (OPT_Wstringop_overflow_, exp,
-		      size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+  return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
+		       srcsize, dstsize);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -3278,9 +3495,8 @@ expand_builtin_memchr (tree exp, rtx)
   if (warn_stringop_overflow)
     {
       tree size = compute_objsize (arg1, 0);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, len, /*maxlen=*/NULL_TREE,
-		   size, /*objsize=*/NULL_TREE);
+      check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		    /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
     }
 
   return NULL_RTX;
@@ -3302,7 +3518,7 @@ expand_builtin_memcpy (tree exp, rtx target)
   tree src = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, src, len);
+  check_memop_access (exp, dest, src, len);
 
   return expand_builtin_memory_copy_args (dest, src, len, target, exp,
 					  /*endp=*/ 0);
@@ -3322,7 +3538,7 @@ expand_builtin_memmove (tree exp, rtx)
   tree src = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, src, len);
+  check_memop_access (exp, dest, src, len);
 
   return NULL_RTX;
 }
@@ -3381,7 +3597,7 @@ expand_builtin_mempcpy (tree exp, rtx target)
   /* Avoid expanding mempcpy into memcpy when the call is determined
      to overflow the buffer.  This also prevents the same overflow
      from being diagnosed again when expanding memcpy.  */
-  if (!check_memop_sizes (exp, dest, src, len))
+  if (!check_memop_access (exp, dest, src, len))
     return NULL_RTX;
 
   return expand_builtin_mempcpy_args (dest, src, len,
@@ -3587,8 +3803,8 @@ expand_builtin_strcat (tree exp, rtx)
 
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+  check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+		destsize);
 
   return NULL_RTX;
 }
@@ -3607,11 +3823,14 @@ expand_builtin_strcpy (tree exp, rtx target)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
 
-  if (warn_stringop_overflow)
+  if (warn_stringop_overflow || warn_restrict || warn_array_bounds)
     {
-      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+      /* Use Object Size type-1 for -Wrestrict.  */
+      int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 2;
+
+      tree destsize = compute_objsize (dest, ost);
+      check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		    src, destsize);
     }
 
   return expand_builtin_strcpy_args (dest, src, target);
@@ -3646,11 +3865,11 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
   dst = CALL_EXPR_ARG (exp, 0);
   src = CALL_EXPR_ARG (exp, 1);
 
-  if (warn_stringop_overflow)
+  if (warn_stringop_overflow || warn_restrict || warn_array_bounds)
     {
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+      check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		    src, destsize);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -3733,8 +3952,7 @@ expand_builtin_stpncpy (tree exp, rtx)
   /* The size of the destination object.  */
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 
   return NULL_RTX;
 }
@@ -3764,7 +3982,7 @@ check_strncat_sizes (tree exp, tree objsize)
 {
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
 
   /* Try to determine the range of lengths that the source expression
      refers to.  */
@@ -3788,32 +4006,32 @@ check_strncat_sizes (tree exp, tree objsize)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize)
-      && tree_int_cst_equal (objsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return false;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
+  /* The number of bytes to write is LEN but check_access will also
      check SRCLEN if LEN's value isn't known.  */
-  return check_sizes (OPT_Wstringop_overflow_,
-		      exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+  return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+		       objsize);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -3831,7 +4049,7 @@ expand_builtin_strncat (tree exp, rtx)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
   /* The upper bound on the number of bytes to write.  */
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
   /* The length of the source sequence.  */
   tree slen = c_strlen (src, 1);
 
@@ -3854,32 +4072,30 @@ expand_builtin_strncat (tree exp, rtx)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize)
-      && tree_int_cst_equal (destsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return NULL_RTX;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
-     check SRCLEN if LEN's value isn't known.  */
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+  /* The number of bytes to write is SRCLEN.  */
+  check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize);
 
   return NULL_RTX;
 }
@@ -3907,10 +4123,10 @@ expand_builtin_strncpy (tree exp, rtx target)
 	  tree destsize = compute_objsize (dest,
 					   warn_stringop_overflow - 1);
 
-	  /* The number of bytes to write is LEN but check_sizes will also
+	  /* The number of bytes to write is LEN but check_access will also
 	     check SLEN if LEN's value isn't known.  */
-	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+	  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
+			destsize);
 	}
 
       /* We must be passed a constant len and src parameter.  */
@@ -4005,7 +4221,7 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
   tree val = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, NULL_TREE, len);
+  check_memop_access (exp, dest, NULL_TREE, len);
 
   return expand_builtin_memset_args (dest, val, len, target, mode, exp);
 }
@@ -4194,7 +4410,7 @@ expand_builtin_bzero (tree exp)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree size = CALL_EXPR_ARG (exp, 1);
 
-  check_memop_sizes (exp, dest, NULL_TREE, size);
+  check_memop_access (exp, dest, NULL_TREE, size);
 
   /* New argument list transforming bzero(ptr x, int y) to
      memset(ptr x, int 0, size_t y).   This is done this way
@@ -4253,14 +4469,12 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
   if (warn_stringop_overflow)
     {
       tree size = compute_objsize (arg1, 0);
-      if (check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE))
+      if (check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+			/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
 	{
 	  size = compute_objsize (arg2, 0);
-	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE);
+	  check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+			/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
 	}
     }
 
@@ -7637,6 +7851,23 @@ expand_builtin_with_bounds (tree exp, rtx target,
 	return target;
       break;
 
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMPCPY_CHKP:
+      if (call_expr_nargs (exp) > 3)
+	{
+	  /* memcpy_chkp (void *dst, size_t dstbnd,
+	                  const void *src, size_t srcbnd, size_t n)
+  	     and others take a pointer bound argument just after each
+	     pointer argument.  */
+	  tree dest = CALL_EXPR_ARG (exp, 0);
+	  tree src = CALL_EXPR_ARG (exp, 2);
+	  tree len = CALL_EXPR_ARG (exp, 4);
+
+	  check_memop_access (exp, dest, src, len);
+	  break;
+	}
+
     default:
       break;
     }
@@ -9653,8 +9884,6 @@ static rtx
 expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			   enum built_in_function fcode)
 {
-  tree dest, src, len, size;
-
   if (!validate_arglist (exp,
 			 POINTER_TYPE,
 			 fcode == BUILT_IN_MEMSET_CHK
@@ -9662,14 +9891,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
-  len = CALL_EXPR_ARG (exp, 2);
-  size = CALL_EXPR_ARG (exp, 3);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 3);
 
-  bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
-			       exp, len, /*maxlen=*/NULL_TREE,
-			       /*str=*/NULL_TREE, size);
+  bool sizes_ok = check_access (exp, dest, src, len, /*maxread=*/NULL_TREE,
+				/*str=*/NULL_TREE, size);
 
   if (!tree_fits_uhwi_p (size))
     return NULL_RTX;
@@ -9778,7 +10006,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
   /* The maximum length of the source sequence in a bounded operation
      (such as __strncat_chk) or null if the operation isn't bounded
      (such as __strcat_chk).  */
-  tree maxlen = NULL_TREE;
+  tree maxread = NULL_TREE;
 
   switch (fcode)
     {
@@ -9799,27 +10027,27 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
     case BUILT_IN_STRNCAT_CHK:
       catstr = CALL_EXPR_ARG (exp, 0);
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STPNCPY_CHK:
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
-      maxlen = CALL_EXPR_ARG (exp, 1);
+      maxread = CALL_EXPR_ARG (exp, 1);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
     default:
       gcc_unreachable ();
     }
 
-  if (catstr && maxlen)
+  if (catstr && maxread)
     {
       /* Check __strncat_chk.  There is no way to determine the length
 	 of the string to which the source string is being appended so
@@ -9828,8 +10056,10 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       return;
     }
 
-  check_sizes (OPT_Wstringop_overflow_, exp,
-	       /*size=*/NULL_TREE, maxlen, srcstr, objsize);
+  /* The destination argument is the first one for all built-ins above.  */
+  tree dst = CALL_EXPR_ARG (exp, 0);
+
+  check_access (exp, dst, srcstr, /*size=*/NULL_TREE, maxread, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9885,8 +10115,9 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 
   /* Add one for the terminating nul.  */
   len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
+
+  check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
+		/*maxread=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
@@ -10516,3 +10747,1268 @@ target_char_cst_p (tree t, char *p)
   *p = (char)tree_to_uhwi (t);
   return true;
 }
+
+/* Initialize a memory reference representation from a pointer PTR and
+   a size SIZE in bytes.  If SIZE is NULL_TREE then the size is assumed
+   to be unknown.  */
+
+builtin_memref::builtin_memref (tree expr, tree size)
+: ptr (expr),
+  ref (),
+  base (),
+  objsize (-1),
+  refoff (HOST_WIDE_INT_MIN),
+  offrange (),
+  sizrange (),
+  strbounded_p ()
+{
+  /* Unfortunately, wide_int default ctor is a no-op so array members
+     of the type must be set individually.  */
+  offrange[0] = offrange[1] = 0;
+  sizrange[0] = sizrange[1] = 0;
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  if (TREE_CODE (expr) == SSA_NAME)
+    {
+      /* Try to tease the offset out of the pointer.  */
+      gimple *stmt = SSA_NAME_DEF_STMT (expr);
+      if (gimple_assign_single_p (stmt)
+	  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+	expr = gimple_assign_rhs1 (stmt);
+      else if (is_gimple_assign (stmt))
+	{
+	  tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == NOP_EXPR)
+	    expr = gimple_assign_rhs1 (stmt);
+	  else if (code == POINTER_PLUS_EXPR)
+	    {
+	      expr = gimple_assign_rhs1 (stmt);
+
+	      tree offset = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (offset) == INTEGER_CST)
+		{
+		  offset_int off = int_cst_value (offset);
+		  offrange[0] = off;
+		  offrange[1] = off;
+
+		  if (TREE_CODE (expr) == SSA_NAME)
+		    {
+		      gimple *stmt = SSA_NAME_DEF_STMT (expr);
+		      if (gimple_assign_single_p (stmt)
+			  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+			expr = gimple_assign_rhs1 (stmt);
+		    }
+		}
+	      else if (TREE_CODE (offset) == SSA_NAME)
+		{
+		  wide_int min, max;
+		  value_range_type rng = get_range_info (offset, &min, &max);
+		  if (rng == VR_RANGE)
+		    {
+		      offrange[0] = min.to_shwi ();
+		      offrange[1] = max.to_shwi ();
+		    }
+		  else if (rng == VR_ANTI_RANGE)
+		    {
+		      offrange[0] = (max + 1).to_shwi ();
+		      offrange[1] = (min - 1).to_shwi ();
+		    }
+		  else
+		    {
+		      gimple *stmt = SSA_NAME_DEF_STMT (offset);
+		      if (is_gimple_assign (stmt)
+			  && gimple_assign_rhs_code (stmt) == NOP_EXPR)
+			{
+			  /* Use the bounds of the type of the NOP_EXPR operand
+			     even if it's signed.  The result doesn't trigger
+			     warnings but makes their output more readable.  */
+			  tree type = TREE_TYPE (gimple_assign_rhs1 (stmt));
+			  offrange[0] = wi::to_offset (TYPE_MIN_VALUE (type));
+			  offrange[1] = wi::to_offset (TYPE_MAX_VALUE (type));
+			}
+		      else
+			offrange[1] = maxobjsize;
+		    }
+		}
+	      else
+		offrange[1] = maxobjsize;
+	    }
+	}
+    }
+
+  if (TREE_CODE (expr) == ADDR_EXPR)
+    {
+      HOST_WIDE_INT off;
+      tree oper = TREE_OPERAND (expr, 0);
+
+      /* Determine the base object or pointer of the reference
+	 and its constant offset from the beginning of the base.  */
+      base = get_addr_base_and_unit_offset (oper, &off);
+
+      if (base)
+	{
+	  offrange[0] += off;
+	  offrange[1] += off;
+
+	  /* Stash the reference for offset validation. */
+	  ref = oper;
+
+	  /* Also stash the constant offset for offset validation.  */
+	  tree_code code = TREE_CODE (oper);
+	  if (code == ARRAY_REF || code == COMPONENT_REF)
+	    {
+	      if (code == COMPONENT_REF)
+		{
+		  tree field = TREE_OPERAND (ref, 1);
+		  refoff = (wi::to_offset (DECL_FIELD_OFFSET (field))
+			    + (wi::to_offset (DECL_FIELD_BIT_OFFSET (field))
+			       >> LOG2_BITS_PER_UNIT));
+		}
+	    }
+	  else if (code == MEM_REF)
+	    {
+	      oper = TREE_OPERAND (ref, 0);
+	      tree_code code = TREE_CODE (oper);
+	      if (code == ADDR_EXPR || code == SSA_NAME)
+		{
+		  tree type = TREE_TYPE (oper);
+		  if (POINTER_TYPE_P (type))
+		    {
+		      /* Take the size of the referenced type as
+			 the referenced object's size.  Set its offset
+			 to HWI_MAX here as a flag so it can be reset
+			 to zero at the end of this function.  */
+		      refoff = HOST_WIDE_INT_MAX;
+		    }
+		}
+	    }
+	}
+      else
+	{
+	  size = NULL_TREE;
+	  base = get_base_address (TREE_OPERAND (expr, 0));
+	}
+    }
+
+  if (!base)
+    base = build2 (MEM_REF, char_type_node, expr, null_pointer_node);
+
+  if (TREE_CODE (base) == MEM_REF)
+    {
+      offset_int off = mem_ref_offset (base);
+      refoff += off;
+      offrange[0] += off;
+      offrange[1] += off;
+      base = TREE_OPERAND (base, 0);
+    }
+
+  if (TREE_CODE (base) == SSA_NAME)
+    if (gimple *stmt = SSA_NAME_DEF_STMT (base))
+      {
+	enum gimple_code code = gimple_code (stmt);
+	if (code == GIMPLE_ASSIGN)
+	  if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+	    {
+	      base = gimple_assign_rhs1 (stmt);
+
+	      tree offset = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (offset) == INTEGER_CST)
+		{
+		  offset_int off = int_cst_value (offset);
+		  refoff += off;
+		  offrange[0] += off;
+		  offrange[1] += off;
+		}
+	    }
+
+	if (TREE_CODE (base) == SSA_NAME && SSA_NAME_VAR (base))
+	  base = SSA_NAME_VAR (base);
+      }
+
+  if (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);
+      sizrange[0] = wi::to_offset (range[0]);
+      sizrange[1] = wi::to_offset (range[1]);
+      /* get_size_range returns SIZE_MAX for the maximum size.
+	 Constrain it to the real maximum of PTRDIFF_MAX.  */
+      if (sizrange[1] > maxobjsize)
+	sizrange[1] = maxobjsize;
+    }
+  else
+    sizrange[1] = maxobjsize;
+
+  /* Reset the reference offset to zero (see above).  */
+  if (refoff >= HOST_WIDE_INT_MAX)
+    refoff = 0;
+}
+
+/* Return error_mark_node if the signed offset is exceeds the bounds
+   of the address space (PTRDIFF_MAX).  Otherwise, return BASE when
+   the offset exceeds the bounds of the BASE object. Otherwise return
+   NULL to inidctae the offset is in bounds.  */
+
+tree
+builtin_memref::offset_out_of_bounds () const
+{
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  /* A temporary, possibly adjusted, copy of the offset range.  */
+  offset_int offrng[2] = { offrange[0], offrange[1] };
+
+  if (DECL_P (base) && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE)
+    {
+      if (offrng[1] < offrng[0])
+  	offrng[1] = maxobjsize;
+    }
+
+  /* Conservative offset of the last byte of the referenced object.  */
+  offset_int endoff;
+
+  if (objsize < 0)
+    {
+      endoff = offrng[0] + sizrange[0];
+
+      /* For a reference through a pointer to an object of unknown size
+	 all initial offsets are considered valid, positive as well as
+	 negative, since the pointer itself can point past the beginning
+	 of the object.  However, the sum of the lower bound of the offset
+	 and that of the size must be less than or equal than PTRDIFF_MAX.  */
+      if (endoff > maxobjsize)
+	return error_mark_node;
+
+      return NULL_TREE;
+    }
+
+  /* A reference to an object of known size must be within the bounds
+     of the base object.  */
+  if (offrng[1] < 0 || offrng[0] > objsize)
+    return base;
+
+  /* The extent of the reference must also be within the bounds of
+     the base object (if known) or the maximum object size otherwise.  */
+  endoff = wi::smax (offrng[0], 0) + sizrange[0];
+  if (endoff > maxobjsize)
+    return error_mark_node;
+
+  if (endoff > objsize)
+    return base;
+
+  return NULL_TREE;
+}
+
+/* Create an association between DSTREF and SRCREF for access by
+   a call EXP to a memory or string built-in funtion.  */
+
+builtin_access::builtin_access (tree exp, builtin_memref &dst,
+				builtin_memref &src)
+: dstoff (), srcoff (), dstsiz (), srcsiz (),
+  dstref (&dst), srcref (&src), sizrange (), ovloff (), ovlsiz ()
+{
+  dstoff[0] = dstoff[1] = 0;
+  srcoff[0] = srcoff[1] = 0;
+  dstsiz[0] = dstsiz[1] = 0;
+  srcsiz[0] = srcsiz[1] = 0;
+
+  /* Object Size Type to use to determine the size of the destination
+     and source objects.  Overridden below for raw memory functions.  */
+  int ostype = 1;
+
+  /* True when the size of one reference depends on the offset of
+     itself or the other.  */
+  bool depends_p;
+
+  /* True when the size of the destination reference (THIS) has been
+     determined from SRCREF and so needs to be adjusted by the latter's
+     offset.  Only meaningful for bounded string functions like strncpy.  */
+  bool dstadjust_p = false;
+
+  /* The size argument number (depends on the built-in).  */
+  unsigned sizeargno = 2;
+  if (CALL_WITH_BOUNDS_P (exp))
+    sizeargno += 2;
+
+  tree func = get_callee_fndecl (exp);
+  switch (DECL_FUNCTION_CODE (func))
+    {
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMCPY_CHK_CHKP:
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHKP:
+    case BUILT_IN_MEMPCPY_CHK_CHKP:
+      /* Both the destination and the source accesses are considered
+	 bounded.  */
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::generic_overlap;
+      break;
+
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMMOVE_CHK_CHKP:
+      /* Like for memmcpy both the destination and the source accesses
+	 are considered bounded.  There is never any overlap.  */
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::no_overlap;
+      break;
+
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      /* Only the destination access is considered bounded in the same
+	 was as for memcpy.  */
+      depends_p = true;
+      dstref->strbounded_p = true;
+      detect_overlap = &builtin_access::generic_overlap;
+      break;
+
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STPCPY_CHKP:
+    case BUILT_IN_STPCPY_CHK_CHKP:
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STRCPY_CHKP:
+    case BUILT_IN_STRCPY_CHK_CHKP:
+    depends_p = true;
+      detect_overlap = &builtin_access::strcpy_overlap;
+      break;
+
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRCAT_CHKP:
+    case BUILT_IN_STRCAT_CHK_CHKP:
+      depends_p = true;
+      detect_overlap = &builtin_access::strcat_overlap;
+      break;
+
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+      /* Both the destination and the source accesses are considered
+	 bounded, although in a different way than for memcpy and
+	 strncpy.  */
+      depends_p = true;
+      dstref->strbounded_p = true;
+      srcref->strbounded_p = true;
+      detect_overlap = &builtin_access::strcat_overlap;
+      break;
+
+    default:
+      /* Handle other string functions here whose access may need
+	 to be validated for in-bounds offsets and non-overlapping
+	 copies.  (Not all _chkp functions have BUILT_IN_XXX_CHKP
+	 macros so they need to be handled here.)  */
+      return;
+    }
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  unsigned HOST_WIDE_INT wisize;
+  if (compute_builtin_object_size (dst.base, ostype, &wisize))
+    dst.objsize = wisize;
+  else if (VAR_P (dst.base) && TREE_CODE (TREE_TYPE (dst.base)) == ARRAY_TYPE)
+    {
+      if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (dst.base)))
+	dst.objsize = wi::to_offset (size);
+      else
+	dst.objsize = maxobjsize;
+    }
+  else
+    dst.objsize = HOST_WIDE_INT_MIN;
+
+  if (compute_builtin_object_size (src.base, ostype, &wisize))
+    src.objsize = wisize;
+  else if (VAR_P (src.base) && TREE_CODE (TREE_TYPE (src.base)) == ARRAY_TYPE)
+    {
+      if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (src.base)))
+	src.objsize = wi::to_offset (size);
+      else
+	src.objsize = maxobjsize;
+    }
+  else
+    src.objsize = HOST_WIDE_INT_MIN;
+
+  /* If there is no dependency between the references or the base
+     objects of the two references aren't the same there's nothing
+     else to do.  */
+  if (depends_p && dstref->base != srcref->base)
+    return;
+
+  /* ...otherwise, make adjustments for references to the same object
+     by string functions to reflect the constraints imposed by the called
+     built-in function.  */
+
+  /* For bounded string functions determine the range of the bound
+     on the access.  */
+  offset_int bounds[2] = { 0, 0 };
+  if (dstref->strbounded_p)
+    {
+      tree size = CALL_EXPR_ARG (exp, sizeargno);
+      tree range[2];
+      if (get_size_range (size, range, true))
+	{
+	  bounds[0] = wi::to_offset (range[0]);
+	  bounds[1] = wi::to_offset (range[1]);
+	}
+
+      /* If both references' size ranges are indeterminate use the last
+	 (size) argument from the function call as a substitute.  This
+	 may only be necessary for strncpy (but not for memcpy where
+	 the size range would have been already determined this way).  */
+      if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize
+	  && srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+	{
+	  dstref->sizrange[0] = bounds[0];
+	  dstref->sizrange[1] = bounds[1];
+	}
+    }
+
+  /* The size range of a references involving the same base object
+     can be determined from the size range of the other reference.
+     This makes it possible to compute accurate offsets for warnings
+     involving functions like strcpy where the length of just one of
+     the two arguments is known (determined by tree-ssa-strlen).  */
+  if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize)
+    {
+      /* When the destination size is unknown set it to the size of
+	 the source.  */
+      dstref->sizrange[0] = srcref->sizrange[0];
+      dstref->sizrange[1] = srcref->sizrange[1];
+    }
+  else if (srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+    {
+      /* When the source size is unknown set it to the size of
+	 the destination.  */
+      srcref->sizrange[0] = dstref->sizrange[0];
+      srcref->sizrange[1] = dstref->sizrange[1];
+
+      if (depends_p)
+	{
+	  if (dstref->strbounded_p)
+	    {
+	      /* Strncpy read access is bounded.  */
+	      if (bounds[0] < srcref->sizrange[0])
+		srcref->sizrange[0] = bounds[0];
+	      if (bounds[1] < srcref->sizrange[1])
+		srcref->sizrange[1] = bounds[1];
+	    }
+
+	  /* For string functions, adjust the size range of the source
+	     reference by the inverse boundaries of the offset (because
+	     the higher  the offset into the string the shorter its
+	     length).  */
+	  if (srcref->offrange[1] < srcref->sizrange[0])
+	    srcref->sizrange[0] -= srcref->offrange[1];
+	  else
+	    srcref->sizrange[0] = 0;
+
+	  if (srcref->offrange[0] > 0)
+	    {
+	      if (srcref->offrange[0] < srcref->sizrange[1])
+		srcref->sizrange[1] -= srcref->offrange[0];
+	      else
+		srcref->sizrange[1] = 0;
+	    }
+
+	  dstadjust_p = true;
+	}
+    }
+
+  if (detect_overlap == &builtin_access::generic_overlap)
+    {
+      if (dstref->strbounded_p)
+	{
+	  dstref->sizrange[0] = bounds[0];
+	  dstref->sizrange[1] = bounds[1];
+
+	  if (dstref->sizrange[0] < srcref->sizrange[0])
+	    srcref->sizrange[0] = dstref->sizrange[0];
+
+	  if (dstref->sizrange[1] < srcref->sizrange[1])
+	    srcref->sizrange[1] = dstref->sizrange[1];
+	}
+    }
+  else if (detect_overlap == &builtin_access::strcpy_overlap)
+    {
+      /* For strcpy, adjust the destination size range to match that
+	 of the source computed above.  */
+      if (depends_p && dstadjust_p)
+	{
+	  dstref->sizrange[0] = srcref->sizrange[0];
+	  dstref->sizrange[1] = srcref->sizrange[1];
+	}
+    }
+}
+
+/* Return true if the bounded mempry (memcpy amd similar) or string function
+   access (strncpy and similar) ACS overlaps.  */
+
+bool
+builtin_access::generic_overlap ()
+{
+  builtin_access &acs = *this;
+  const builtin_memref *dstref = acs.dstref;
+  const builtin_memref *srcref = acs.srcref;
+
+  gcc_assert (dstref->base == srcref->base);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  offset_int maxsize = dstref->objsize < 0 ? maxobjsize : dstref->objsize;
+  gcc_assert (maxsize <= maxobjsize);
+
+  /* Adjust the upper bounds of the offsets to make them valid for
+     the smallest access (if possible) but no smaller than the lower
+     bounds.  */
+  if (maxsize < acs.dstoff[1] + acs.dstsiz[0])
+    acs.dstoff[1] = maxsize - acs.dstsiz[0];
+  if (acs.dstoff[1] < acs.dstoff[0])
+    acs.dstoff[1] = acs.dstoff[0];
+
+  if (maxsize < acs.srcoff[1] + acs.srcsiz[0])
+    acs.srcoff[1] = maxsize - acs.srcsiz[0];
+  if (acs.srcoff[1] < acs.srcoff[0])
+    acs.srcoff[1] = acs.srcoff[0];
+
+  /* Determine the minimum and maximum space for the access given
+     the offsets.  */
+  offset_int space[2];
+  space[0] = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+  space[1] = space[0];
+
+  offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+  if (acs.srcsiz[0] > 0)
+    {
+      if (d < space[0])
+	space[0] = d;
+
+      if (space[1] < d)
+	space[1] = d;
+    }
+  else
+    space[1] = acs.dstsiz[1];
+
+  d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+  if (d < space[0])
+    space[0] = d;
+
+  if (space[1] < d)
+    space[1] = d;
+
+  /* Treat raw memory functions both of whose references are bounded
+     as special and permit uncertain overlaps to go undetected.  For
+     all kinds of constant offset and constant size accesses, if
+     overlap isn't certain it is not possible.  */
+  bool overlap_possible = space[0] < acs.dstsiz[1];
+  if (!overlap_possible)
+    return false;
+
+  bool overlap_certain = space[1] < acs.dstsiz[0];
+
+  if (!overlap_certain
+      && !dstref->strbounded_p
+      && detect_overlap == &builtin_access::generic_overlap)
+    return false;
+
+  /* True for stpcpy and strcpy.  */
+  bool stxcpy_p = detect_overlap == &builtin_access::strcpy_overlap;
+
+  /* True when the size of the source sequence is inveresely proportional
+     to the offset into it, such as in strcpy (DST, SRC + OFF).  */
+  bool sizdep_p = (stxcpy_p
+		   && acs.srcoff[0] != acs.srcoff[1]
+		   && acs.sizrange[0] != acs.sizrange[1]);
+
+  if (dstref->refoff >= 0
+      && srcref->refoff >= 0
+      && dstref->refoff != srcref->refoff
+      && (stxcpy_p || dstref->strbounded_p || srcref->strbounded_p))
+    return false;
+
+  /* The number of bytes written.  */
+  offset_int write_min = acs.dstsiz[0];
+  offset_int write_max = acs.dstsiz[1];
+
+  if (acs.dstoff[0] <= acs.srcoff[0])
+    {
+      /* The lower bound of the offset of the first reference is less
+	 than the lower bound of the second.  */
+      if (acs.dstoff[1] <= acs.srcoff[1])
+	{
+	  /* The upper bound of the offset of the first reference is less
+	     than the upper bound of the second.
+	     The size of the overlap is the size of the access minus
+	     the difference between the upper bound of the second offset
+	     and the lower bound of the first offset.  */
+	  if (overlap_certain)
+	    acs.ovlsiz[0]
+	      = (write_min - (acs.srcoff[1] - acs.dstoff[0])).to_shwi ();
+	  acs.ovlsiz[1]
+	    = (write_max - (acs.srcoff[0] - acs.dstoff[0])).to_shwi ();
+
+	  if (acs.ovlsiz[1] > acs.srcsiz[1])
+	    acs.ovlsiz[1] = acs.srcsiz[1].to_shwi ();
+	}
+      else
+	{
+	  /* The offset of the overlap is the lower bound of the first
+	     offset plus the initial non-overlapping size of the access.  */
+	  if (overlap_certain)
+	    acs.ovlsiz[0]
+	      = (write_min - (acs.dstoff[1] - acs.srcoff[0])).to_shwi ();
+
+	  acs.ovlsiz[1]
+	    = (write_max - (acs.dstoff[0] - acs.srcoff[0])).to_shwi ();
+	}
+
+      if (acs.ovlsiz[1])
+	acs.ovloff[1] = (acs.dstoff[0] + write_max).to_shwi () - acs.ovlsiz[1];
+      else
+	acs.ovloff[1] = acs.srcoff[0].to_shwi ();
+
+      if (acs.ovloff[1] >= acs.srcoff[0] + acs.srcsiz[0])
+	acs.ovloff[1] = (acs.srcoff[0] + acs.srcsiz[0]).to_shwi ();
+
+      if (acs.ovlsiz[0] < 0)
+	{
+	  acs.ovlsiz[0] = 0;
+	  acs.ovloff[0] = acs.ovloff[1];
+	}
+      else if (acs.ovlsiz[0] > 0)
+	acs.ovloff[0] = (acs.dstoff[0] + write_min).to_shwi () - acs.ovlsiz[0];
+      else if (acs.ovloff[1] && acs.ovlsiz[1] > 1)
+	{
+	  /* When the minimum overlap is zero then for strcpy the upper
+	     bound of the overlap offset (which is actually the smaller
+	     of the two offset bounds to correspond to the lager size
+	     of the overlap) is two less than the upper bound of the end
+	     offset of the overlap.  For other functions it's just one
+	     less.  */
+	  acs.ovloff[0] = (acs.ovloff[1] + acs.ovlsiz[1] - 1 - stxcpy_p);
+	}
+      else
+	acs.ovloff[0] = acs.ovloff[1];
+    }
+  else
+    {
+      /* The lower bound of the offset of the first reference is
+	 the same or greater that the lower bound of the second.
+	 The size of the overlap is the size of the access minus
+	 the difference between the upper bound of the first offset
+	 and the lower bound of the first offset.  */
+      acs.ovlsiz[0] = (write_min - (acs.dstoff[1] - acs.srcoff[0])).to_shwi ();
+      acs.ovlsiz[1] = (write_max - (acs.dstoff[0] - acs.srcoff[0])).to_shwi ();
+
+      if (acs.ovlsiz[1])
+	acs.ovloff[1] = (acs.srcoff[1] + write_max).to_shwi () - acs.ovlsiz[1];
+      else
+	acs.ovloff[1] = acs.dstoff[1].to_shwi ();
+
+      if (acs.ovlsiz[0] < 0)
+	{
+	  acs.ovlsiz[0] = 0;
+	  acs.ovloff[0] = acs.ovloff[1];
+	}
+      else if (acs.ovlsiz[0] > 0)
+	/* The offset of the overlap is the upper bound of the first
+	   offset plus the initial non-overlapping size of the access.  */
+	acs.ovloff[0] = (acs.srcoff[0] + write_min).to_shwi () - acs.ovlsiz[0];
+      else
+	acs.ovloff[0] = (acs.ovloff[1] + acs.ovlsiz[1] - 1 - sizdep_p);
+    }
+
+  return true;
+}
+
+/* Return true if the strcat-like access ACS overlaps.  */
+
+bool
+builtin_access::strcat_overlap ()
+{
+  builtin_access &acs = *this;
+  const builtin_memref *dstref = acs.dstref;
+  const builtin_memref *srcref = acs.srcref;
+
+  gcc_assert (dstref->base == srcref->base);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  gcc_assert (dstref->base && dstref->base == srcref->base);
+
+  /* Adjust for strcat-like accesses.  */
+
+  /* As a special case for strcat, set the DSTREF offsets to the length
+     of the source string since the function starts writing at the first
+     nul, and set the size to 1 for the length of the nul.  */
+  acs.dstoff[0] += acs.dstsiz[0];
+  acs.dstoff[1] += acs.dstsiz[1];
+
+  bool strfunc_unknown_args = acs.dstsiz[0] == 0 && acs.dstsiz[1] != 0;
+
+  /* The lower bound is zero when the size is unknown because then
+     overlap is not certain. */
+  acs.dstsiz[0] = strfunc_unknown_args ? 0 : 1;
+  acs.dstsiz[1] = 1;
+
+  offset_int maxsize = dstref->objsize < 0 ? maxobjsize : dstref->objsize;
+  gcc_assert (maxsize <= maxobjsize);
+
+  /* For references to the same base object, determine if there's a pair
+     of valid offsets into the two references such that access between
+     them doesn't overlap.  Adjust both upper bounds to be valid for
+     the smaller size (i.e., at most MAXSIZE - SIZE).  */
+
+  if (maxsize < acs.dstoff[1] + acs.dstsiz[0])
+    acs.dstoff[1] = maxsize - acs.dstsiz[0];
+
+  if (maxsize < acs.srcoff[1] + acs.srcsiz[0])
+    acs.srcoff[1] = maxsize - acs.srcsiz[0];
+
+  /* Check to see if there's enough space for both accesses without
+     overlap.  Determine the optimistic (maximum) amount of available
+     space.  */
+  offset_int space;
+  if (acs.dstoff[0] <= acs.srcoff[0])
+    {
+      if (acs.dstoff[1] < acs.srcoff[1])
+	space = acs.srcoff[1] + acs.srcsiz[0] - acs.dstoff[0];
+      else
+	space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+    }
+  else
+    space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+
+  /* Overlap is certain if the distance between the farthest offsets
+     of the opposite accesses is less than the sum of the lower bounds
+     of the sizes of the two accesses.  */
+  bool overlap_certain = space < acs.dstsiz[0] + acs.srcsiz[0];
+
+  /* For a constant-offset, constant size access, consider the largest
+     distance between the offset bounds and the lower bound of the access
+     size.  If the overlap isn't certain return success.  */
+  if (!overlap_certain
+      && acs.dstoff[0] == acs.dstoff[1]
+      && acs.srcoff[0] == acs.srcoff[1]
+      && acs.dstsiz[0] == acs.dstsiz[1]
+      && acs.srcsiz[0] == acs.srcsiz[1])
+    return false;
+
+  /* Overlap is not certain but may be possible.  */
+
+  offset_int access_min = acs.dstsiz[0] + acs.srcsiz[0];
+
+  /* Determine the conservative (minimum) amount of space.  */
+  space = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+  offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+  if (d < space)
+    space = d;
+  d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+  if (d < space)
+    space = d;
+
+  /* For a strict test (used for strcpy and similar with unknown or
+     variable bounds or sizes), consider the smallest distance between
+     the offset bounds and either the upper bound of the access size
+     if known, or the lower bound otherwise.  */
+  if (access_min <= space && (access_min != 0 || !strfunc_unknown_args))
+    return false;
+
+  /* When strcat overlap is certain it is always a single byte:
+     the terminatinn NUL, regardless of offsets and sizes.  When
+     overlap is only possible its range is [0, 1].  */
+  acs.ovlsiz[0] = dstref->sizrange[0] == dstref->sizrange[1] ? 1 : 0;
+  acs.ovlsiz[1] = 1;
+  acs.ovloff[0] = (dstref->sizrange[0] + dstref->offrange[0]).to_shwi ();
+  acs.ovloff[1] = (dstref->sizrange[1] + dstref->offrange[1]).to_shwi ();
+
+  acs.sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+  acs.sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+  return true;
+}
+
+/* Return true if the strcpy-like access ACS overlaps.  */
+
+bool
+builtin_access::strcpy_overlap ()
+{
+  return generic_overlap ();
+}
+
+
+/* Return true if THIS (AKA DSTREF) and SRCREF describe accesses that
+   either overlap one another or that, in order not to overlap, would
+   imply that the size of the referenced object(s) exceeds the maximum
+   size of an object.  Set SIZRANGE to the range of access sizes,
+   OVLOFF to the offsets where the overlap occurs (the offsets must
+   be valid so that their sum with the low bound of the access size
+   is no greater than PTRDIFF_MAX), and OVLSIZ to the rane of the overlap
+   sizes.
+   Otherwise, if THIS and SRCREF do not definitely overlap (even though
+   they may overlap in a way that's not apparent from the available data),
+   return false.
+   Used for -Wrestrict warnings.  */
+
+bool
+builtin_access::overlap ()
+{
+  builtin_access &acs = *this;
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  acs.sizrange[0] = wi::smax (dstref->sizrange[0],
+			      srcref->sizrange[0]).to_shwi ();
+  acs.sizrange[1] = wi::smax (dstref->sizrange[1],
+			      srcref->sizrange[1]).to_shwi ();
+
+  /* Check to see if the two references refer to regions that are
+     too large not to overlap in the address space (whose maximum
+     size is PTRDIFF_MAX).  */
+  offset_int size = dstref->sizrange[0] + srcref->sizrange[0];
+  if (maxobjsize < size)
+    {
+      acs.ovloff[0] = (maxobjsize - dstref->sizrange[0]).to_shwi ();
+      acs.ovlsiz[0] = (size - maxobjsize).to_shwi ();
+      return true;
+    }
+
+  /* If both base objects aren't known return the maximum possible
+     offset that would make them not overlap.  */
+  if (!dstref->base || !srcref->base)
+    return false;
+
+  /* If the base object is an array adjust the the lower bound of
+     the offset to be non-negative.  */
+  if (dstref->base
+      && TREE_CODE (TREE_TYPE (dstref->base)) == ARRAY_TYPE)
+    acs.dstoff[0] = wi::smax (dstref->offrange[0], 0);
+  else
+    acs.dstoff[0] = dstref->offrange[0];
+
+  acs.dstoff[1] = dstref->offrange[1];
+
+  if (srcref->base
+      && TREE_CODE (TREE_TYPE (srcref->base)) == ARRAY_TYPE)
+    acs.srcoff[0] = wi::smax (srcref->offrange[0], 0);
+  else
+    acs.srcoff[0] = srcref->offrange[0];
+
+  acs.srcoff[1] = srcref->offrange[1];
+
+  /* Validate the offset and size of each reference on its own first.
+     This is independent of whether or not the base objects are the
+     same.  Normally, this would have already been detected and
+     diagnosed by -Warray-bounds, unless it has been disabled.  */
+  offset_int maxoff = acs.dstoff[0] + dstref->sizrange[0];
+  if (maxobjsize < maxoff)
+    {
+      acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+      acs.ovloff[0] = acs.dstoff[0].to_shwi () - acs.ovlsiz[0];
+      return true;
+    }
+
+  maxoff = acs.srcoff[0] + srcref->sizrange[0];
+  if (maxobjsize < maxoff)
+    {
+      acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+      acs.ovlsiz[1] = (acs.srcoff[0] + srcref->sizrange[1]
+		      - maxobjsize).to_shwi ();
+      acs.ovloff[0] = acs.srcoff[0].to_shwi () - acs.ovlsiz[0];
+      return true;
+    }
+
+  if (dstref->base != srcref->base)
+    return false;
+
+  acs.dstsiz[0] = dstref->sizrange[0];
+  acs.dstsiz[1] = dstref->sizrange[1];
+
+  acs.srcsiz[0] = srcref->sizrange[0];
+  acs.srcsiz[1] = srcref->sizrange[1];
+
+  /* Call the appropriate function to determine the overlap.  */
+  if ((this->*detect_overlap) ())
+    {
+      sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+      sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+      return true;
+    }
+
+  return false;
+}
+
+/* Attempt to detect and diagnose an overlapping copy in a call expression
+   EXPR involving an an access ACS to a built-in memory or string function.
+   Return true when one has been detected, false otherwise.  */
+
+static bool
+maybe_diag_overlap (location_t loc, tree exp, builtin_access &acs)
+{
+  const builtin_memref &dstref = *acs.dstref;
+  const builtin_memref &srcref = *acs.srcref;
+
+  /* Determine the range of offsets and sizes of the overlap if it
+     exists and issue diagnostics.  */
+  HOST_WIDE_INT *ovloff = acs.ovloff;
+  HOST_WIDE_INT *ovlsiz = acs.ovlsiz;
+  HOST_WIDE_INT *sizrange = acs.sizrange;
+
+  tree func = get_callee_fndecl (exp);
+
+  if (!acs.overlap ())
+    return false;
+
+  /* To avoid a combinatorial explosion of diagnostics format the offsets
+     or their ranges as strings and use them in the warning calls below.  */
+  char offstr[3][64];
+
+  if (dstref.offrange[0] == dstref.offrange[1]
+      || dstref.offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr[0], "%lli", (long long) dstref.offrange[0].to_shwi ());
+  else
+    sprintf (offstr[0], "[%lli, %lli]",
+             (long long) dstref.offrange[0].to_shwi (),
+             (long long) dstref.offrange[1].to_shwi ());
+
+  if (srcref.offrange[0] == srcref.offrange[1]
+      || srcref.offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr[1], "%lli", (long long) srcref.offrange[0].to_shwi ());
+  else
+    sprintf (offstr[1], "[%lli, %lli]",
+	     (long long) srcref.offrange[0].to_shwi (),
+             (long long) srcref.offrange[1].to_shwi ());
+
+  if (ovloff[0] == ovloff[1] || !ovloff[1])
+    sprintf (offstr[2], "%lli", (long long) ovloff[0]);
+  else
+    sprintf (offstr[2], "[%lli, %lli]",
+	     (long long) ovloff[0], (long long) ovloff[1]);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  bool must_overlap = ovlsiz[0] > 0;
+
+  if (ovlsiz[1] == 0)
+    ovlsiz[1] = ovlsiz[0];
+
+  if (must_overlap)
+    {
+      /* Issue definitive "overlaps" diagnostic in this block.  */
+
+      if (sizrange[0] == sizrange[1])
+	{
+	  if (ovlsiz[0] == ovlsiz[1])
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? (ovlsiz[0] == 1
+			   ? G_("%K%qD accessing %wu byte at offsets %s "
+				"and %s overlaps %wu byte at offset %s")
+			   :  G_("%K%qD accessing %wu byte at offsets %s "
+				 "and %s overlaps %wu bytes at offset "
+				 "%s"))
+			: (ovlsiz[0] == 1
+			   ? G_("%K%qD accessing %wu bytes at offsets %s "
+				"and %s overlaps %wu byte at offset %s")
+			   : G_("%K%qD accessing %wu bytes at offsets %s "
+				"and %s overlaps %wu bytes at offset "
+				"%s")),
+			exp, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? G_("%K%qD accessing %wu byte at offsets %s "
+			     "and %s overlaps between %wu and %wu bytes "
+			     "at offset %s")
+			: G_("%K%qD accessing %wu bytes at offsets %s "
+			     "and %s overlaps between %wu and %wu bytes "
+			     "at offset %s"),
+			exp, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+			offstr[2]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? G_("%K%qD accessing %wu byte at offsets %s and "
+			     "%s overlaps %wu or more bytes at offset %s")
+			: G_("%K%qD accessing %wu bytes at offsets %s and "
+			     "%s overlaps %wu or more bytes at offset %s"),
+			exp, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  return true;
+	}
+
+      if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+	{
+	  if (ovlsiz[0] == ovlsiz[1])
+	    warning_at (loc, OPT_Wrestrict,
+			ovlsiz[0] == 1
+			? G_("%K%qD accessing between %wu and %wu bytes "
+			     "at offsets %s and %s overlaps %wu byte at "
+			     "offset %s")
+			: G_("%K%qD accessing between %wu and %wu bytes "
+			     "at offsets %s and %s overlaps %wu bytes "
+			     "at offset %s"),
+			exp, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD accessing between %wu and %wu bytes at "
+			"offsets %s and %s overlaps between %wu and %wu "
+			"bytes at offset %s",
+			exp, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+			offstr[2]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD accessing between %wu and %wu bytes at "
+			"offsets %s and %s overlaps %wu or more bytes "
+			"at offset %s",
+			exp, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  return true;
+	}
+
+      if (ovlsiz[0] != ovlsiz[1])
+	ovlsiz[1] = maxobjsize.to_shwi ();
+
+      if (ovlsiz[0] == ovlsiz[1])
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[0] == 1
+		    ? G_("%K%qD accessing %wu or more bytes at offsets "
+			 "%s and %s overlaps %wu byte at offset %s")
+		    :  G_("%K%qD accessing %wu or more bytes at offsets "
+			  "%s and %s overlaps %wu bytes at offset %s"),
+		    exp, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], offstr[2]);
+      else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD accessing %wu or more bytes at offsets %s "
+		    "and %s overlaps between %wu and %wu bytes "
+		    "at offset %s",
+		    exp, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], ovlsiz[1], offstr[2]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD accessing %wu or more bytes at offsets %s "
+		    "and %s overlaps %wu or more bytes at offset %s",
+		    exp, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], offstr[2]);
+      return true;
+    }
+
+  /* Issue "may overlap" diagnostics below.  */
+  gcc_assert (ovlsiz[0] == 0
+	      && ovlsiz[1] > 0
+	      && ovlsiz[1] < maxobjsize.to_shwi ());
+
+  /* Use more concise wording when one of the offsets is unbounded
+     to avoid confusing the user with large and mostly meaningless
+     numbers.  */
+  bool open_range = ((dstref.offrange[0] == -maxobjsize - 1
+		      && dstref.offrange[1] == maxobjsize)
+		     || (srcref.offrange[0] == -maxobjsize - 1
+			 && srcref.offrange[1] == maxobjsize));
+
+  if (sizrange[0] == sizrange[1] || sizrange[1] == 1)
+    {
+      if (ovlsiz[1] == 1)
+	{
+	  if (open_range)
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[1] == 1
+			? G_("%K%qD accessing %wu byte may overlap "
+			     "%wu byte")
+			: G_("%K%qD accessing %wu bytes may overlap "
+			     "%wu byte"),
+			exp, func, sizrange[1], ovlsiz[1]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[1] == 1
+			? G_("%K%qD accessing %wu byte at offsets %s "
+			     "and %s may overlap %wu byte at offset %s")
+			: G_("%K%qD accessing %wu bytes at offsets %s "
+			     "and %s may overlap %wu byte at offset %s"),
+			exp, func, sizrange[1], offstr[0], offstr[1],
+			ovlsiz[1], offstr[2]);
+	  return true;
+	}
+
+      if (open_range)
+	warning_at (loc, OPT_Wrestrict,
+		    sizrange[1] == 1
+		    ? G_("%K%qD accessing %wu byte may overlap "
+			 "up to %wu bytes")
+		    : G_("%K%qD accessing %wu bytes may overlap "
+			 "up to %wu bytes"),
+		    exp, func, sizrange[1], ovlsiz[1]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    sizrange[1] == 1
+		    ? G_("%K%qD accessing %wu byte at offsets %s and "
+			 "%s may overlap up to %wu bytes at offset %s")
+		    : G_("%K%qD accessing %wu bytes at offsets %s and "
+			 "%s may overlap up to %wu bytes at offset %s"),
+		    exp, func, sizrange[1], offstr[0], offstr[1],
+		    ovlsiz[1], offstr[2]);
+      return true;
+    }
+
+  if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+    {
+      if (open_range)
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[1] == 1
+		    ? G_("%K%qD accessing between %wu and %wu bytes "
+			 "may overlap %wu byte")
+		    : G_("%K%qD accessing between %wu and %wu bytes "
+			 "may overlap up to %wu bytes"),
+		    exp, func, sizrange[0], sizrange[1], ovlsiz[1]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[1] == 1
+		    ? G_("%K%qD accessing between %wu and %wu bytes "
+			 "at offsets %s and %s may overlap %wu byte "
+			 "at offset %s")
+		    : G_("%K%qD accessing between %wu and %wu bytes "
+			 "at offsets %s and %s may overlap up to %wu "
+			 "bytes at offset %s"),
+		    exp, func, sizrange[0], sizrange[1],
+		    offstr[0], offstr[1], ovlsiz[1], offstr[2]);
+      return true;
+    }
+
+  warning_at (loc, OPT_Wrestrict,
+	      ovlsiz[1] == 1
+	      ? G_("%K%qD accessing %wu or more bytes at offsets %s "
+		   "and %s may overlap %wu byte at offset %s")
+	      : G_("%K%qD accessing %wu or more bytes at offsets %s "
+		   "and %s may overlap up to %wu bytes at offset %s"),
+	      exp, func, sizrange[0], offstr[0], offstr[1],
+	      ovlsiz[1], offstr[2]);
+
+  return true;
+}
+
+/* Helper to create a minimal CALL_EXPR tree from a GIMPLE call STMT.
+   The tree is minimally populated to be be usable in a %K directive
+   with the same result as the corresponding STMT with %G. See
+   expand_call_stmt() in cfgexpand.c.  */
+
+static tree
+gcall_to_tree_minimal (gcall *stmt)
+{
+  tree exp = build_vl_exp (CALL_EXPR, gimple_call_num_args (stmt) + 3);
+  CALL_EXPR_FN (exp) = gimple_call_fn (stmt);
+  TREE_TYPE (exp) = gimple_call_return_type (stmt);
+  CALL_EXPR_STATIC_CHAIN (exp) = gimple_call_chain (stmt);
+  CALL_WITH_BOUNDS_P (exp) = gimple_call_with_bounds_p (stmt);
+ 
+  for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
+    {
+      tree arg = gimple_call_arg (stmt, i);
+      CALL_EXPR_ARG (exp, i) = arg;
+    }
+
+  SET_EXPR_LOCATION (exp, gimple_location (stmt));
+  return exp;
+}
+
+/* Attempt to detect and diagnose invalid offset bounds and (exept for
+   memmove) overlapping copy in a call expression EXP from SRC to DST
+   and DSTSIZE and SRCSIZE bytes, respectively.  Return false when one
+   or the other has been detected, true otherwise.  */
+
+static bool
+check_bounds_or_overlap (tree exp, tree dst, tree src, tree dstsize,
+			  tree srcsize, bool bounds_only /* = false */)
+{
+  location_t loc = tree_nonartificial_location (exp);
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  tree func = get_callee_fndecl (exp);
+
+  bool check_overlap
+    = (bounds_only
+       || (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+	   && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK));
+
+  builtin_memref dstref (dst, dstsize);
+  builtin_memref srcref (src, srcsize);
+
+  builtin_access acs (exp, dstref, srcref);
+
+  /* Validate offsets first to make sure they are within the bounds
+     of the destination object if its size is known, or PTRDIFF_MAX
+     otherwise.  */
+  if (maybe_diag_offset_bounds (loc, exp, func, dst, dstref)
+      || maybe_diag_offset_bounds (loc, exp, func, src, srcref))
+    {
+      TREE_NO_WARNING (exp) = 1;
+      return false;
+    }
+
+  if (check_overlap)
+    {
+      if (operand_equal_p (dst, src, 0))
+	{
+	  warning_at (loc, OPT_Wrestrict,
+		      "%K%qD source argument is the same as destination",
+		      exp, func);
+	  TREE_NO_WARNING (exp) = 1;
+	  return false;
+	}
+
+      /* Return false when overlap has been detected.  */
+      if (maybe_diag_overlap (loc, exp, acs))
+	{
+	  TREE_NO_WARNING (exp) = 1;
+	  return false;
+	}
+    }
+
+  return true;
+}
+
+/* Attempt to detect and diagnose invalid offset bounds and (except for
+   memmove) overlapping copy in a statement STMT from SRC to DST and
+   DSTSIZE and SRCIZE bytes, respectively.  Return false when one or
+   the other has been detected, true otherwise.  */
+
+bool
+check_bounds_or_overlap (gimple *stmt, tree dst, tree src,
+			  tree dstsize, tree srcsize,
+			  bool bounds_only /*= false */)
+{
+  if (gimple_no_warning_p (stmt))
+    return false;
+
+  tree exp = gcall_to_tree_minimal (as_a <gcall *> (stmt));
+
+  if (check_bounds_or_overlap (exp, dst, src, dstsize, srcsize, bounds_only))
+    return true;
+
+  /* If a warning has been issued (and NO_WARNING set) set the GIMPLE
+     no warning bit.  */
+  if (TREE_NO_WARNING (exp))
+    gimple_set_no_warning (stmt, true);
+
+  return false;
+}
+
+/* Return the maximum object size.  */
+
+tree
+max_object_size (void)
+{
+  /* To do: Make this a configurable parameter.  */
+  return TYPE_MAX_VALUE (ptrdiff_type_node);
+}
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 4ae7056..52c7ed0 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -102,4 +102,8 @@ extern bool target_char_cst_p (tree t, char *p);
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
 
+extern bool check_bounds_or_overlap (gimple *, tree, tree, tree, tree,
+				     bool = false);
+extern tree max_object_size ();
+
 #endif
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 8f36c77..019ae0c 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5266,14 +5266,20 @@ check_function_restrict (const_tree fndecl, const_tree fntype,
 			 int nargs, tree *argarray)
 {
   int i;
-  tree parms;
+  tree parms = TYPE_ARG_TYPES (fntype);
 
   if (fndecl
-      && TREE_CODE (fndecl) == FUNCTION_DECL
-      && DECL_ARGUMENTS (fndecl))
-    parms = DECL_ARGUMENTS (fndecl);
-  else
-    parms = TYPE_ARG_TYPES (fntype);
+      && TREE_CODE (fndecl) == FUNCTION_DECL)
+    {
+      /* Skip checking built-ins here.  They are checked in more
+	 detail elsewhere.  */
+      if (DECL_BUILT_IN (fndecl)
+	  && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+	return;
+
+      if (DECL_ARGUMENTS (fndecl))
+	parms = DECL_ARGUMENTS (fndecl);
+    }
 
   for (i = 0; i < nargs; i++)
     TREE_VISITED (argarray[i]) = 0;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 13d2a59..09b2374 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1170,7 +1170,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall)
 Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier.
 
 Wrestrict
-C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++)
+C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
 Warn when an argument passed to a restrict-qualified parameter aliases with
 another argument.
 
diff --git a/gcc/calls.c b/gcc/calls.c
index 3730f43..364f2c0 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "builtins.h"
 
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
 #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -1194,7 +1195,7 @@ alloc_max_size (void)
 {
   if (!alloc_object_size_limit)
     {
-      alloc_object_size_limit = TYPE_MAX_VALUE (ssizetype);
+      alloc_object_size_limit = max_object_size ();
 
       if (warn_alloc_size_limit)
 	{
@@ -1213,7 +1214,7 @@ alloc_max_size (void)
 		     suffixes.  */
 		  if (!strcmp (end, "kB"))
 		    unit = 1000;
-		  else if (!strcasecmp (end, "KiB") || strcmp (end, "KB"))
+		  else if (!strcasecmp (end, "KiB") || !strcmp (end, "KB"))
 		    unit = 1024;
 		  else if (!strcmp (end, "MB"))
 		    unit = HOST_WIDE_INT_UC (1000) * 1000;
@@ -1245,7 +1246,8 @@ alloc_max_size (void)
 		{
 		  widest_int w = wi::mul (limit, unit);
 		  if (w < wi::to_widest (alloc_object_size_limit))
-		    alloc_object_size_limit = wide_int_to_tree (ssizetype, w);
+		    alloc_object_size_limit
+		      = wide_int_to_tree (ptrdiff_type_node, w);
 		}
 	    }
 	}
@@ -1254,13 +1256,17 @@ alloc_max_size (void)
 }
 
 /* Return true when EXP's range can be determined and set RANGE[] to it
-   after adjusting it if necessary to make EXP 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.  */
+   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.  */
 
 bool
-get_size_range (tree exp, tree range[2])
+get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
 {
   if (tree_fits_uhwi_p (exp))
     {
@@ -1269,20 +1275,33 @@ get_size_range (tree exp, tree range[2])
       return true;
     }
 
+  tree exptype = TREE_TYPE (exp);
+  bool integral = INTEGRAL_TYPE_P (exptype);
+
   wide_int min, max;
-  enum value_range_type range_type
-    = ((TREE_CODE (exp) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (exp)))
-       ? get_range_info (exp, &min, &max) : VR_VARYING);
+  enum value_range_type range_type;
+
+  if (TREE_CODE (exp) == SSA_NAME && integral)
+    range_type = get_range_info (exp, &min, &max);
+  else
+    range_type = VR_VARYING;
 
   if (range_type == VR_VARYING)
     {
-      /* No range information available.  */
+      if (integral)
+	{
+	  /* Use the full range of the type of the expression when
+	     no value range information is available.  */
+	  range[0] = TYPE_MIN_VALUE (exptype);
+	  range[1] = TYPE_MAX_VALUE (exptype);
+	  return true;
+	}
+
       range[0] = NULL_TREE;
       range[1] = NULL_TREE;
       return false;
     }
 
-  tree exptype = TREE_TYPE (exp);
   unsigned expprec = TYPE_PRECISION (exptype);
 
   bool signed_p = !TYPE_UNSIGNED (exptype);
@@ -1320,11 +1339,16 @@ get_size_range (tree exp, tree range[2])
 	{
 	  /* 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 set the range to
-	     [MAX, TYPE_MAX] so that when MAX is greater than the limit
-	     the whole range is diagnosed.  */
-	  min = max + 1;
-	  max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	     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
+	    {
+	      min = max + 1;
+	      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	    }
 	}
       else
 	{
diff --git a/gcc/calls.h b/gcc/calls.h
index df5817f..cd3d58d 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -38,6 +38,6 @@ extern bool pass_by_reference (CUMULATIVE_ARGS *, machine_mode,
 extern bool reference_callee_copied (CUMULATIVE_ARGS *, machine_mode,
 				     tree, bool);
 extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
-extern bool get_size_range (tree, tree[2]);
+extern bool get_size_range (tree, tree[2], bool = false);
 
 #endif // GCC_CALLS_H
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index d0e0782..9afe742 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2629,6 +2629,9 @@ expand_call_stmt (gcall *stmt)
   if (gimple_call_nothrow_p (stmt))
     TREE_NOTHROW (exp) = 1;
 
+  if (gimple_no_warning_p (stmt))
+    TREE_NO_WARNING (exp) = 1;
+
   CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt);
   CALL_EXPR_MUST_TAIL_CALL (exp) = gimple_call_must_tail_p (stmt);
   CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 7b1a697..9c8ebd7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3864,6 +3864,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
 -Wparentheses  @gol
 -Wpointer-sign  @gol
 -Wreorder   @gol
+-Wrestrict   @gol
 -Wreturn-type  @gol
 -Wsequence-point  @gol
 -Wsign-compare @r{(only in C++)}  @gol
@@ -6612,11 +6613,25 @@ reduce the padding and so make the structure smaller.
 Warn if anything is declared more than once in the same scope, even in
 cases where multiple declaration is valid and changes nothing.
 
-@item -Wrestrict
+@item -Wno-restrict
 @opindex Wrestrict
 @opindex Wno-restrict
-Warn when an argument passed to a restrict-qualified parameter
-aliases with another argument.
+Warn when an object referenced by a @code{restrict}-qualified parameter
+(or, in C++, @code{__restrict}-qualified parameter) is aliased by another
+argument, or when copies between such objects overlap.  For example,
+the call to the @code{strcpy} function below attempts to truncate the string
+by replacing its initial characters with the last four.  However, because
+the call writes the terminating NUL into @code{a[4]}, the copies overlap and
+the call is diagnosed.
+
+@smallexample
+struct foo
+@{
+  char a[] = "abcd1234";
+  strcpy (a, a + 4);
+@};
+@end smallexample
+The @option{-Wrestrict} is included in @option{-Wall}.
 
 @item -Wnested-externs @r{(C and Objective-C only)}
 @opindex Wnested-externs
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index cb33c1e..048a2e4 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -59,6 +59,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "intl.h"
+#include "diagnostic-core.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -658,13 +660,12 @@ size_must_be_zero_p (tree size)
   return wi::eq_p (min, wone) && wi::geu_p (max, ssize_max);
 }
 
-/* Fold function call to builtin mem{{,p}cpy,move}.  Return
-   false if no simplification can be made.
-   If ENDP is 0, return DEST (like memcpy).
-   If ENDP is 1, return DEST+LEN (like mempcpy).
-   If ENDP is 2, return DEST+LEN-1 (like stpcpy).
-   If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
-   (memmove).   */
+/* Fold function call to builtin mem{{,p}cpy,move}.  Try to detect and
+   diagnose (otherwise undefined) overlapping copies without preventing
+   folding.  When folded, GCC guarantees that overlapping memcpy has
+   the same semantics as memmove.  Call to the library memcpy need not
+   provide the same guarantee.  Return false if no simplification can
+   be made.  */
 
 static bool
 gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
@@ -676,6 +677,12 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
   tree destvar, srcvar;
   location_t loc = gimple_location (stmt);
 
+  tree func = gimple_call_fndecl (stmt);
+  bool nowarn = gimple_no_warning_p (stmt);
+  bool check_overlap = (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+			&& DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+			&& !nowarn);
+
   /* If the LEN parameter is a constant zero or in range where
      the only valid value is zero, return DEST.  */
   if (size_must_be_zero_p (len))
@@ -699,6 +706,15 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
      DEST{,+LEN,+LEN-1}.  */
   if (operand_equal_p (src, dest, 0))
     {
+      /* Avoid diagnosing exact overlap in calls to __builtin_memcpy.
+	 It's safe and may even be emitted by GCC itself (see bug
+	 32667).  However, diagnose it in explicit calls to the memcpy
+	 function.  */
+      if (check_overlap && *IDENTIFIER_POINTER (DECL_NAME (func)) != '_')
+	warning_at (loc, OPT_Wrestrict,
+		    "%qD source argument is the same as destination",
+		    func);
+
       unlink_stmt_vdef (stmt);
       if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
 	release_ssa_name (gimple_vdef (stmt));
@@ -748,6 +764,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	  unsigned ilen = tree_to_uhwi (len);
 	  if (pow2p_hwi (ilen))
 	    {
+	      /* Detect invalid bounds and overlapping copies and issue
+		 either -Warray-bounds or -Wrestrict.  */
+	      if (check_bounds_or_overlap (stmt, dest, src, len, len))
+		gimple_set_no_warning (stmt, true);
+
 	      scalar_int_mode mode;
 	      tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
 	      if (type
@@ -1059,6 +1080,10 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	    }
 	}
 
+      /* Detect invalid bounds and overlapping copies and issue either
+	 -Warray-bounds or -Wrestrict.  */
+      check_bounds_or_overlap (stmt, dest, src, len, len);
+
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
 	{
@@ -1423,7 +1448,7 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
 	    tree op3 = gimple_assign_rhs3 (def_stmt);
 	    return get_range_strlen (op2, length, visited, type, fuzzy, flexp)
 	      && get_range_strlen (op3, length, visited, type, fuzzy, flexp);
-          }
+	  }
         return false;
 
       case GIMPLE_PHI:
@@ -1515,12 +1540,19 @@ static bool
 gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
 			    tree dest, tree src)
 {
-  location_t loc = gimple_location (gsi_stmt (*gsi));
+  gimple *stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
   tree fn;
 
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -2303,6 +2335,15 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
      (resp. DEST+LEN for __mempcpy_chk).  */
   if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
     {
+      if (fcode != BUILT_IN_MEMMOVE && fcode != BUILT_IN_MEMMOVE_CHK)
+	{
+	  tree func = gimple_call_fndecl (stmt);
+
+	  warning_at (loc, OPT_Wrestrict,
+		      "%qD source argument is the same as destination",
+		      func);
+	}
+
       if (fcode != BUILT_IN_MEMPCPY_CHK)
 	{
 	  replace_call_with_value (gsi, dest);
@@ -2404,6 +2445,12 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -7295,3 +7342,4 @@ gimple_stmt_integer_valued_real_p (gimple *stmt, int depth)
       return false;
     }
 }
+
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 1f291e1..b81edf9 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -361,6 +361,7 @@ gimple_build_call_from_tree (tree t, tree fnptrtype)
     gimple_call_set_arg (call, i, CALL_EXPR_ARG (t, i));
 
   gimple_set_block (call, TREE_BLOCK (t));
+  gimple_set_location (call, EXPR_LOCATION (t));
 
   /* Carry all the CALL_EXPR flags to the new GIMPLE_CALL.  */
   gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t));
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds.c b/gcc/testsuite/c-c++-common/Warray-bounds.c
new file mode 100644
index 0000000..bbea846
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-bounds.c
@@ -0,0 +1,400 @@
+/* Exercise that -Warray-bounds is issued for out-of-bounds offsets
+   in calls to built-in functions.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" }  */
+
+#define SIZE_MAX __SIZE_MAX__
+#define SSIZE_MAX __PTRDIFF_MAX__
+#define SSIZE_MIN (-SSIZE_MAX - 1)
+
+typedef __PTRDIFF_TYPE__ ssize_t;
+typedef __SIZE_TYPE__    size_t;
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+extern ssize_t signed_value (void)
+{
+  extern volatile ssize_t signed_value_source;
+  return signed_value_source;
+}
+
+extern size_t unsigned_value (void)
+{
+  extern volatile size_t unsigned_value_source;
+  return unsigned_value_source;
+}
+
+ssize_t signed_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return val < min || max < val ? min : val;
+}
+
+ssize_t signed_anti_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return min <= val && val <= max ? min == SSIZE_MIN ? max + 1 : min - 1 : val;
+}
+
+size_t unsigned_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+size_t unsigned_anti_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return min <= val && val <= max ? min == 0 ? max + 1 : min - 1 : val;
+}
+
+#define SR(min, max) signed_range ((min), (max))
+#define UR(min, max) unsigned_range ((min), (max))
+
+#define SAR(min, max) signed_anti_range ((min), (max))
+#define UAR(min, max) unsigned_anti_range ((min), (max))
+
+void sink (void*, ...);
+
+#define CAT(x, y)      x ## y
+#define CONCAT(x, y)   CAT (x, y)
+#define UNIQUE_NAME(x) CONCAT(x, __LINE__)
+
+#define T(type, N, dst, src, n) do {		\
+    extern type UNIQUE_NAME (a)[N];		\
+    type *a = UNIQUE_NAME (a);			\
+    type *pd = (dst);				\
+    const type *ps = (src);			\
+    FUNC (pd, ps, n);				\
+    sink (a, pd, ps, s);			\
+  } while (0)
+
+
+void test_memcpy_bounds (char *d, const char *s, size_t n)
+{
+#define FUNC memcpy
+
+  /* Verify that invalid offsets into an array of known size are
+     detected.  */
+
+  T (char, 1, a + SR (SSIZE_MIN, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of array \[^\n\r]* with type .char ?\\\[1]" } */
+  T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds of array" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  T (char, 1, a + UR (0, 1), s, n);
+  T (char, 1, a + UR (0, 2), s, n);
+  T (char, 1, a + UR (1, 2), s, n);
+  T (char, 1, a + UR (2, 3), s, n);       /* { dg-warning "offset \\\[2, 3] is out of the bounds of array " } */
+  T (char, 1, a + UR (2, SSIZE_MAX), s, n);  /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds of array " } */
+
+  /* Offsets in excess of SSIZE_MAX are treated as negative even if
+     they appear as large positive in the source.  It would be nice
+     if they retained their type but unfortunately that's not how
+     it works so be prepared for both in case it even gets fixed.  */
+  T (char, 1, a + UR (3, SIZE_MAX), s, n);   /* { dg-warning "offset \\\[3, -1] is out of the bounds of array" } */
+
+  /* Verify that invalid offsets into an array of unknown size are
+     detected.  */
+  extern char arr[];
+  T (char, 1, arr + SR (SSIZE_MIN,         0), s, n);
+  T (char, 1, arr + SR (SSIZE_MIN + 1,    -1), s, n);   /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of array " } */
+  T (char, 1, arr + SR (SSIZE_MIN,         1), s, n);
+  T (char, 1, arr + SR (SSIZE_MIN, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (       -2,        -1), s, n);   /* { dg-warning "offset \\\[-2, -1] is out of the bounds of array " } */
+  T (char, 1, arr + SR (       -1,         0), s, n);
+  T (char, 1, arr + SR (       -1,         1), s, n);
+  T (char, 1, arr + SR (       -1, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (        0,         1), s, n);
+  T (char, 1, arr + SR (        0, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (        1,         2), s, n);
+  T (char, 1, arr + SR (        1, SSIZE_MAX), s, n);
+
+  /* Verify that all offsets via a pointer to an uknown object are
+     accepted.  */
+
+  /* Negative indices between [SSIZE_MIN, SSIZE_MAX] are valid since
+     the pointer to which the offset is applied can be at a positive
+     offset from the beginning of an object.  */
+  T (char, 1, d + SR (SSIZE_MIN,         0), s, n);
+  T (char, 1, d + SR (SSIZE_MIN,        -1), s, n);
+  T (char, 1, d + SR (SSIZE_MIN,         1), s, n);
+  T (char, 1, d + SR (SSIZE_MIN, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (       -2,        -1), s, n);
+  T (char, 1, d + SR (       -1,         0), s, n);
+  T (char, 1, d + SR (       -1,         1), s, n);
+  T (char, 1, d + SR (       -1, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (        0,         1), s, n);
+  T (char, 1, d + SR (        0, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (        1,         2), s, n);
+  T (char, 1, d + SR (        1, SSIZE_MAX), s, n);
+}
+
+/* Verify offsets in an anti-range.  */
+
+void test_memcpy_bounds_anti_range (char *d, const char *s, size_t n)
+{
+  T (char, 9, a, a + SAR (-2, -1), 3);
+  T (char, 9, a, a + SAR (-1,  1), 3);
+  T (char, 9, a, a + SAR ( 0,  1), 3);
+  T (char, 9, a, a + SAR ( 0,  2), 3);
+  T (char, 9, a, a + SAR ( 0,  3), 3);
+  T (char, 9, a, a + SAR ( 0,  4), 3);
+  T (char, 9, a, a + SAR ( 0,  5), 3);
+  /* The initial source range is valid but the final range after the access
+     has complete cannot be.  */
+  T (char, 9, a, a + SAR ( 0,  6), 3);   /* { dg-warning "offset \\\[7, -1] is out of the bounds of array " } */
+
+  /* This fails because the offset isn't represented as an SSA_NAME
+     but rather as a GIMPLE_PHI (offset, 0).  With some effort it is
+     possible to extract the range from the PHI but it's not implemented
+     (yet).  */
+  T (char, 9, a, a + SAR ( 1,  6), 3);   /* { dg-warning "offset \\\[7, 0] is out of the bounds of array " "memcpy" { xfail *-*-* } } */
+
+  T (char, 9, a, a + SAR ( 2,  6), 3);   /* { dg-warning "offset \\\[7, 1] is out of the bounds of array " } */
+  T (char, 9, a, a + SAR ( 3,  6), 3);   /* { dg-warning "offset \\\[7, 2] is out of the bounds of array " } */
+
+  T (char, 9, a, a + SAR (-1,  7), 3);   /* { dg-warning "offset \\\[8, -2] is out of the bounds of array " } */
+  T (char, 9, a, a + SAR (-2,  8), 3);   /* { dg-warning "offset \\\[9, -3] is out of the bounds of array " } */
+
+  T (char, 9, a + SAR (-2, -1), a, 3);
+  T (char, 9, a + SAR (-1,  1), a, 3);
+  T (char, 9, a + SAR ( 0,  1), a, 3);
+  T (char, 9, a + SAR ( 0,  2), a, 3);
+  T (char, 9, a + SAR ( 0,  3), a, 3);
+  T (char, 9, a + SAR ( 0,  6), a, 3);   /* { dg-warning "offset \\\[7, -1] is out of the bounds of array " } */
+  T (char, 9, a + SAR (-1,  7), a, 3);   /* { dg-warning "offset \\\[8, -2] is out of the bounds of array " } */
+  T (char, 9, a + SAR (-2,  8), a, 3);   /* { dg-warning "offset \\\[9, -3] is out of the bounds of array " } */
+
+
+}
+
+
+/* Verify that pointer overflow in the computation done by memcpy
+   (i.e., offset + size) is detected and diagnosed.  */
+
+void test_memcpy_overflow (char *d, const char *s, size_t n)
+{
+  extern char arr[];
+
+  /* Verify that offset overflow involving an array of unknown size
+     but known access size is detected.  This works except with small
+     sizes that are powers of 2 due to bug .  */
+  T (char, 1, arr + SR (SSIZE_MAX - 1, SSIZE_MAX), s, 1);
+  T (char, 1, arr + SR (SSIZE_MAX - 1, SSIZE_MAX), s, 2);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 2 accessing array " "bug " { xfail *-*-* } } */
+  T (char, 1, arr + SR (SSIZE_MAX - 2, SSIZE_MAX), s, 3);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 3 accessing array " } */
+  T (char, 1, arr + SR (SSIZE_MAX - 4, SSIZE_MAX), s, 5);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 5 accessing array " } */
+}
+
+void test_memmove_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC memmove
+
+    T (char, 1, a + SR (SSIZE_MIN + 1, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of array \[^\n\r]+ with type .char ?\\\[1]" } */
+  T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds of array" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  const int *pi = (const int*)s;
+  T (int,  2, a + SR (-1, 1), pi, n);
+  T (int,  2, a + SR (-1, 2), pi, n);
+  T (int,  2, a + SR ( 0, 2), pi, n);
+  T (int,  2, a + SR ( 0, 3), pi, n);
+  T (int,  2, a + SR ( 1, 3), pi, n);
+  T (int,  2, a + SR ( 2, 3), pi, n);
+  T (int,  2, a + SR ( 3, 4), pi, n);      /* { dg-warning "offset \\\[12, 16] is out of the bounds of array .\[^\n\r]+. with type .int ?\\\[2]." } */
+}
+
+
+void test_mempcpy_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC mempcpy
+
+  /* Verify that invalid offsets into an array of known size are
+     detected.  */
+
+  T (char, 1, a + SR (SSIZE_MIN, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds" } */
+T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  T (char, 1, a + UR (0, 1), s, n);
+  T (char, 1, a + UR (0, 2), s, n);
+  T (char, 1, a + UR (1, 2), s, n);
+  T (char, 1, a + UR (2, 3), s, n);       /* { dg-warning "offset \\\[2, 3] is out of the bounds of array " } */
+  T (char, 1, a + UR (2, SSIZE_MAX), s, n);  /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds of array" } */
+
+  /* Offsets in excess of SSIZE_MAX are treated as negative even if
+     they appear as large positive in the source.  It would be nice
+     if they retained their type but unfortunately that's not how
+     it works so be prepared for both in case it ever gets fixed.  */
+  T (char, 1, a + UR (3, SIZE_MAX), s, n);   /* { dg-warning "offset \\\[3, -1] is out of the bounds of array " } */
+
+  /* Verify that invalid offsets into an array of unknown size are
+     detected.  */
+  extern char arr[];
+  T (char, 1, arr + SR (SSIZE_MIN,         0), s, n);
+  T (char, 1, arr + SR (SSIZE_MIN,        -1), s, n);   /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of array" } */
+  T (char, 1, arr + SR (SSIZE_MIN,         1), s, n);
+  T (char, 1, arr + SR (SSIZE_MIN, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (       -2,        -1), s, n);   /* { dg-warning "offset \\\[-2, -1] is out of the bounds of array" } */
+  T (char, 1, arr + SR (       -1,         0), s, n);
+  T (char, 1, arr + SR (       -1,         1), s, n);
+  T (char, 1, arr + SR (       -1, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (        0,         1), s, n);
+  T (char, 1, arr + SR (        0, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (        1,         2), s, n);
+  T (char, 1, arr + SR (        1, SSIZE_MAX), s, n);
+
+  /* Verify that all offsets via a pointer to an uknown object are
+     accepted.  */
+
+  /* Negative indices between [SSIZE_MIN, SSIZE_MAX] are valid since
+     the pointer to which the offset is applied can be at a positive
+     offset from the beginning of an object.  */
+  T (char, 1, d + SR (SSIZE_MIN,         0), s, n);
+  T (char, 1, d + SR (SSIZE_MIN,        -1), s, n);
+  T (char, 1, d + SR (SSIZE_MIN,         1), s, n);
+  T (char, 1, d + SR (SSIZE_MIN, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (       -2,        -1), s, n);
+  T (char, 1, d + SR (       -1,         0), s, n);
+  T (char, 1, d + SR (       -1,         1), s, n);
+  T (char, 1, d + SR (       -1, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (        0,         1), s, n);
+  T (char, 1, d + SR (        0, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (        1,         2), s, n);
+  T (char, 1, d + SR (        1, SSIZE_MAX), s, n);
+}
+
+#define TI(type, N, init, dst, src) do {	\
+    type UNIQUE_NAME (a)[N] = init;		\
+    type *a = UNIQUE_NAME (a);			\
+    type *pd = (dst);				\
+    const type *ps = (src);			\
+    FUNC (pd, ps);				\
+    sink (a, pd, ps, s);			\
+  } while (0)
+
+void test_strcpy_bounds (char *d, const char *s)
+{
+#undef FUNC
+#define FUNC strcpy
+
+  ssize_t i;
+
+  TI (char, 1, "",   a, a + SR (SSIZE_MIN, 0));
+  TI (char, 1, "",   a, a + SR (-1, 0));
+  TI (char, 1, "",   a, a + SR (-1, 1));
+  TI (char, 1, "",   a, a + SR (0, 1));
+  TI (char, 1, "",   a, a + SR (0, SSIZE_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (0, SSIZE_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (1, SSIZE_MAX - 1));
+  /* The following needs a warning for reading past the end.  */
+  TI (char, 2, "0",  a, a + SR (2, SSIZE_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (3, SSIZE_MAX - 1));   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[2\\\]." } */
+
+  TI (char, 3, "01", a, a + SR (0, SSIZE_MAX - 1));
+  TI (char, 3, "01", a, a + SR (1, SSIZE_MAX - 1));
+  TI (char, 3, "01", a, a + SR (2, SSIZE_MAX - 1));
+  /* The following needs a warning for reading past the end.  */
+  TI (char, 3, "01", a, a + SR (3, SSIZE_MAX - 1));
+  TI (char, 3, "01", a, a + SR (4, SSIZE_MAX - 1));   /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[3\\\]." } */
+
+  TI (char, 4, "012", a, a + SR (SSIZE_MAX - 2, SSIZE_MAX - 1));   /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[4\\\]." } */
+
+
+  TI (char, 1, "", a + SR (SSIZE_MIN, 0), s);
+  TI (char, 1, "", a + SR (-1, 0), s);
+  TI (char, 1, "", a + SR (-1, 1), s);
+  TI (char, 1, "", a + SR (0, 1), s);
+  TI (char, 1, "", a + SR (0, SSIZE_MAX - 1), s);
+  TI (char, 2, "", a + SR (0, SSIZE_MAX - 1), s);
+  TI (char, 2, "", a + SR (1, SSIZE_MAX - 1), s);
+  /* The following is diagnosed not because the initial source offset
+     it out of bounds (it isn't) but because the final source offset
+     after the access has completed, is.  It would be clearer if
+     the warning mentioned the final offset.  */
+  TI (char, 2, "", a + SR (2, SSIZE_MAX - 1), s);   /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[2\\\]." } */
+  TI (char, 2, "", a + SR (3, SSIZE_MAX - 1), s);   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[2\\\]." } */
+
+  TI (char, 3, "", a + SR (0, SSIZE_MAX - 1), s);
+  TI (char, 3, "", a + SR (1, SSIZE_MAX - 1), s);
+  TI (char, 3, "", a + SR (2, SSIZE_MAX - 1), s);
+  TI (char, 3, "", a + SR (3, SSIZE_MAX - 1), s);   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[3\\\]." } */
+  TI (char, 3, "", a + SR (4, SSIZE_MAX - 1), s);   /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[3\\\]." } */
+
+  TI (char, 4, "", a + SR (SSIZE_MAX - 2, SSIZE_MAX - 1), s);   /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[4\\\]." } */
+}
+
+struct MA
+{
+  int i;
+  char a1_5[5];
+  char a2_11[11];
+};
+
+struct MA2
+{
+  struct MA ma1_3[3];
+  struct MA ma2_5[5];
+};
+
+struct MA3
+{
+  struct MA2 ma1_5[3];
+  struct MA2 ma1_7[7];
+};
+
+void test_strcpy_bounds_member_array (struct MA *pma,
+				      struct MA2 *pma2,
+				      struct MA3 *pma3,
+				      const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC(d, s, ignore) strcpy (d, s)
+
+  T (char, 1, pma->a1_5, s, 0);
+  T (char, 1, pma->a1_5 + 0, s, 0);
+  T (char, 1, pma->a1_5 + 1, s, 0);
+  T (char, 1, pma->a1_5 + 4, s, 0);
+
+  /* The following forms a pointer during the call that's outside
+     the bounds of the array it was derived from (pma->a1_5) so
+     it should be diagnosed but the representation of the pointer
+     addition doesn't contain information to distinguish it from
+     the valid pma->a2_11 + 1 so this is an XFAIL.  */
+  T (char, 1, pma->a1_5 + 5, s, 0);                 /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+  /* The following also forms an out-of-bounds pointer but similar
+     to the above, there is no reliable way to distinguish it from
+     (char*)&pma[1].i + 1 so this too is not diagnosed.  */
+  T (char, 1, pma->a1_5 + sizeof *pma + 1, s, 0);   /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+  T (char, 1, pma->a1_5 - 1, s, 0);   /* { dg-warning "offset -1 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+
+  T (char, 1, pma[1].a1_5, s, 0);
+  T (char, 1, pma[2].a1_5 + 0, s, 0);
+  T (char, 1, pma[3].a1_5 + 1, s, 0);
+  T (char, 1, pma[4].a1_5 + 4, s, 0);
+
+
+  extern struct MA3 ma3[3];
+  T (char, 1, ma3[0].ma1_5[0].ma1_3[0].a1_5 + 6, s, 0);
+}
diff --git a/gcc/testsuite/c-c++-common/Wrestrict-2.c b/gcc/testsuite/c-c++-common/Wrestrict-2.c
new file mode 100644
index 0000000..f440e7b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict-2.c
@@ -0,0 +1,70 @@
+/* PR 35503 - Warn about restricted pointers
+   Test to exercise that -Wrestrict warnings are issued for memory and
+   sring functions when they are declared in system headers (i.e., not
+   just when they are explicitly declared in the source file.)
+   Also verify that the warnings are issued even for calls where the
+   source of the aliasing violation is in a different function than
+   the restricted call.
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict" } */
+
+#include <string.h>
+
+void wrap_memcpy (void *d, const void *s, size_t n)
+{
+  memcpy (d, s, n);   /* { dg-warning "source argument is the same as destination" "memcpy" } */
+}
+
+void call_memcpy (void *d, size_t n)
+{
+  const void *s = d;
+  wrap_memcpy (d, s, n);
+}
+
+
+void wrap_strcat (char *d, const char *s)
+{
+  strcat (d, s);   /* { dg-warning "source argument is the same as destination" "strcat" } */
+}
+
+void call_strcat (char *d)
+{
+  const char *s = d;
+  wrap_strcat (d, s);
+}
+
+
+void wrap_strcpy (char *d, const char *s)
+{
+  strcpy (d, s);   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+}
+
+void call_strcpy (char *d)
+{
+  const char *s = d;
+  wrap_strcpy (d, s);
+}
+
+
+void wrap_strncat (char *d, const char *s, size_t n)
+{
+  strncat (d, s, n);   /* { dg-warning "source argument is the same as destination" "strncat" } */
+}
+
+void call_strncat (char *d, size_t n)
+{
+  const char *s = d;
+  wrap_strncat (d, s, n);
+}
+
+
+void wrap_strncpy (char *d, const char *s, size_t n)
+{
+  strncpy (d, s, n);   /* { dg-warning "source argument is the same as destination" "strncpy" } */
+}
+
+void call_strncpy (char *d, size_t n)
+{
+  const char *s = d;
+  wrap_strncpy (d, s, n);
+}
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
new file mode 100644
index 0000000..011aeaf
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -0,0 +1,958 @@
+/* PR 35503 - Warn about restricted pointers
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#define SIZE_MAX __SIZE_MAX__
+#define SSIZE_MAX (__SIZE_MAX__ / 2)
+#define SSIZE_MIN (-SSIZE_MAX - 1)
+
+#if !defined LINE
+# define LINE 0
+#endif
+
+typedef __PTRDIFF_TYPE__ ssize_t;
+typedef __SIZE_TYPE__    size_t;
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+extern ssize_t signed_value (void)
+{
+  extern volatile ssize_t signed_value_source;
+  return signed_value_source;
+}
+
+extern size_t unsigned_value (void)
+{
+  extern volatile size_t unsigned_value_source;
+  return unsigned_value_source;
+}
+
+ssize_t signed_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return val < min || max < val ? min : val;
+}
+
+ssize_t signed_anti_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return min <= val && val <= max ? min == 0 ? max + 1 : min - 1 : val;
+}
+
+size_t unsigned_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+size_t unsigned_anti_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return min <= val && val <= max ? min == SSIZE_MIN ? max + 1 : min - 1 : val;
+}
+
+#define SR(min, max) signed_range ((min), (max))
+#define UR(min, max) unsigned_range ((min), (max))
+
+#define SAR(min, max) signed_anti_range ((min), (max))
+#define UAR(min, max) unsigned_anti_range ((min), (max))
+
+
+void sink (void*, ...);
+
+/* Exercise memcpy with constant or known arguments.  */
+
+void test_memcpy_cst (void *d, const void *s)
+{
+#undef T
+#define T(dst, src, n) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+      void *pd = (dst);					\
+      const void *ps = (src);				\
+      memcpy (pd, ps, (n));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T (a, a, 0);
+  T (a, s = a, 3);           /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  /* This isn't detected because memcpy calls with small power-of-2 sizes
+     are intentionally folded into safe copies equivalent to memmove.
+     It's marked xfail only because there is value in detecting such
+     invalid calls for portability, and as a reminder of why it isn't
+     diagnosed.  */
+  T (a, a + 1, 1);           /* { dg-warning "\\\[-Wrestrict" "memcpy with a small power of 2 size" { xfail *-*-* } } */
+  T (a, a + 3, 3);
+  T (a, a + 3, 5);           /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  {
+    char a[3] = { 1, 2, 3 };
+
+    /* Verify that a call to memcpy with an exact overlap is diagnosed
+       (also tested above) but an excplicit one to __builtin_memcpy is
+       not.  See bug 32667 for the rationale.  */
+    (memcpy)(a, a, sizeof a);   /* { dg-warning "source argument is the same as destination" "memcpy" } */
+    sink (a);
+
+    __builtin_memcpy (a, a, sizeof a);
+    sink (a);
+  }
+
+  {
+    char a[3][7];
+    sink (a);
+
+    void *d = a[0];
+    const void *s = a[1];
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0];
+    s = a[1];
+    /* The following is only diagnosed for sizes that aren't small
+       powers of 2.  */
+    memcpy (d, s, sizeof a[0] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&a);
+  }
+
+  {
+    struct {
+      char a[7];
+      char b[7];
+      char c[7];
+    } x;
+    sink (&x);
+
+    void *d = x.a;
+    const void *s = x.b;
+    memcpy (d, s, sizeof x.a);
+    sink (&x);
+
+    d = x.a;
+    s = x.a;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 4;
+    s = x.b;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 6;
+    s = x.b;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b;
+    memcpy (d, s, 3);             /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b + 1;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 1;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 2;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 3;
+    memcpy (d, s, sizeof x.b);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+  }
+
+  {
+#undef T
+#define T(dst, src, n) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };	\
+      memcpy ((dst), (src), (n));			\
+      sink (a);						\
+    }							\
+    } while (0)
+
+    /* Verify the offset of the overlap is the same regardless of whether
+       the destination is at lower or higher offset than the source.  */
+    T (a, a + 1, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 1 overlaps 4 bytes at offset 1" "memcpy" } */
+    T (a, a + 2, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "memcpy" } */
+    T (a, a + 3, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 3 overlaps 2 bytes at offset 3" "memcpy" } */
+
+    T (a + 1, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 1 and 0 overlaps 4 bytes at offset 1" "memcpy" } */
+    T (a + 2, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 2 and 0 overlaps 3 bytes at offset 2" "memcpy" } */
+    T (a + 3, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 3 and 0 overlaps 2 bytes at offset 3" "memcpy" } */
+  }
+}
+
+/* Exercise memcpy with destination or source offset or size in
+   a determinate range.  */
+
+void test_memcpy_range (char *d, size_t sz)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    if (!LINE || LINE == __LINE__) {		\
+      char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };	\
+      void *pd = (dst);					\
+      const void *ps = (src);				\
+      memcpy (pd, ps, (n));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  ssize_t ir = SR (2, 3);
+  T (a + ir, a, 0);
+  T (a + ir, a, 1);
+  T (a + ir, a, 2);
+  T (a + ir, a, 3);
+  /* The following fails because the size is a small power of 2.  */
+  T (a + ir, a, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3\\\] and 0 overlaps between 1 and 2 bytes at offset \\\[3, 2]" "memcpy" { xfail *-*-*} } */
+  T (a + ir, a, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3\\\] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  T (d + ir, d, 0);
+  T (d + ir, d, 1);
+  T (d + ir, d, 2);
+  T (d + ir, d, 3);
+  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3\\\] and 0 overlaps 1 byte at offset 3" "bug 79220" { xfail *-*-* } } */
+  T (d + ir, d, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3\\\] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  /* Because the size is constant and a power of 2 the following is
+     folded too early to detect the overlap.  */
+  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3\\\] and 0 overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+  T (d + ir, d, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3\\\] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  /* Exercise the full range of size_t.  */
+  T (d + sz, d, 0);
+  T (d + sz, d, 1);
+  T (d + sz, d, 9);
+
+  T (a, a + 1, SR (0, 1));
+  T (a, a + 1, SR (0, 2));
+  T (a, a + 1, SR (1, 2));
+  T (a, a + 1, SR (2, 3));         /* { dg-warning "accessing between 2 and 3 bytes at offsets 0 and 1 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+  T (a, a + 1, UR (2, SSIZE_MAX + (size_t)1));  /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a, a + 1, UR (2, SIZE_MAX - 1));        /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a, a + 2, SR (2, 3));
+  T (a, a + 2, SR (3, 4));         /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and 2 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+  T (a, a + 3, SR (3, 4));
+  T (a, a + 3, SR (4, 5));         /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and 3 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+  T (a, a + 3, SR (5, 6));         /* { dg-warning "accessing between 5 and 6 bytes at offsets 0 and 3 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+  T (a + 1, a, SR (0, 1));
+  T (a + 1, a, SR (0, 2));
+  T (a + 1, a, SR (1, 2));
+  T (a + 1, a, SR (2, 3));         /* { dg-warning "accessing between 2 and 3 bytes at offsets 1 and 0 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+  T (a + 1, a, UR (2, SSIZE_MAX + (size_t)1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a + 1, a, UR (2, SIZE_MAX - 1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a + 2, a, SR (2, 3));
+  T (a + 2, a, SR (3, 4));         /* { dg-warning "accessing between 3 and 4 bytes at offsets 2 and 0 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+  T (a + 3, a, SR (3, 4));
+  T (a + 3, a, SR (4, 5));         /* { dg-warning "accessing between 4 and 5 bytes at offsets 3 and 0 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+  T (a + 3, a, SR (5, 6));         /* { dg-warning "accessing between 5 and 6 bytes at offsets 3 and 0 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+  ir = SR (2, 5);
+  T (a, a + ir, 4);
+  T (a, a + ir, 5);                /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] overlaps between 1 and 3 bytes at offset \\\[4, 2]" "memcpy" } */
+  T (a, a + ir, 6);                /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] overlaps between 3 and 4 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  size_t n = UR (3, 4);
+  T (a + SR (1, 3), a + SR (1, 3), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3\\\] and \\\[1, 3\\\] overlaps between 1 and 4 bytes at offset \\\[3, 1]" "memcpy" } */
+  T (a + SR (1, 3), a + SR (2, 3), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3\\\] and \\\[2, 3\\\] overlaps between 1 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+  T (a + SR (1, 3), a + SR (3, 4), n);
+
+  T (a + SR (2, 3), a + SR (3, 4), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[2, 3\\\] and \\\[3, 4\\\] overlaps between 1 and 3 bytes at offset \\\[4, 3]" "memcpy" } */
+
+  T (a + SR (1, 3), a + SR (4, 5), n);
+  T (a + SR (2, 3), a + SR (4, 5), n);
+  T (a + SR (3, 4), a + SR (4, 5), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[3, 4\\\] and \\\[4, 5\\\] overlaps between 1 and 3 bytes at offset \\\[5, 4]" "memcpy" } */
+
+  /* Exercise the full range of size_t.  */
+  T (d, d + sz, 0);
+  T (d, d + sz, 1);
+  T (d, d + sz, 9);
+}
+
+/* Exercise memcpy with offset and/or size in a determinate anti-range.  */
+
+void test_memcpy_anti_range (char *d, const char *s)
+{
+  T (d, d + SAR (0, 3), 1);
+  T (d, d + SAR (0, 3), 2);
+  T (d, d + SAR (0, 3), 3);
+  T (d, d + SAR (0, 3), SSIZE_MAX - 2);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 2" } */
+  T (d, d + SAR (0, 3), SSIZE_MAX - 1);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 1" } */
+  T (d, d + SAR (0, 3), SSIZE_MAX);       /* { dg-warning "overlaps \[0-9\]+ bytes at offset 0" } */
+
+  T (d, d + SAR (0, 3), UR (SSIZE_MAX - 2, SSIZE_MAX));               /* { dg-warning "accessing \[0-9\]+ or more bytes at offsets 0 and \\\[-?\[0-9\]+, -?\[0-9\]+] overlaps \[0-9\]+ bytes at offset 2" } */
+
+  /* Verify that a size in an anti-range ~[0, N] where N >= PTRDIFF_MAX
+     doesn't trigger a warning.  */
+  T (d, s, UAR (1, SSIZE_MAX - 1));
+  T (d, s, UAR (1, SSIZE_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)); */
+}
+
+/* Verify calls to memcpy() where the combination of offsets in some
+   range and size is such that either overlap is unavoidable or one
+   or both offsets would exceed the maximum size of an object
+   (SSIZE_MAX).  */
+
+void test_memcpy_range_exceed (char *d, const char *s)
+{
+  /* Verify offset and size both in some range.  The memcpy checking
+     is less strict than that of string functions like strncpy and
+     doesn't trigger unless the overlap is certain.  The following
+     overlaps for (r == 3 && n > 3) but not, for example, for
+     (r == 4 && n == 4), and so it's not diagnosed.  */
+  ssize_t i, j;
+  size_t n;
+
+  i = SR (3, 5);
+  n = UR (4, 6);
+
+  T (a, a + i, n);
+  T (a + i, a, n);
+  /* Ditto for objects of unknown sizes.  */
+  T (d, d + i, n);
+  T (d + i, d, n);
+
+  /* Verify that a warning is issued for a copy between two regions
+     whose aggregate size would exceed SSIZE_MAX if it were to not
+     overlap.  */
+  T (d, s, SSIZE_MAX / 2);
+  T (d, s, SSIZE_MAX / 2 + 1);   /* { dg-warning "overlaps 1 byte" "memcpy" } */
+  T (d, s, SSIZE_MAX / 2 + 2);   /* { dg-warning "overlaps 3 bytes" "memcpy" } */
+  T (d, s, SSIZE_MAX / 2 + 3);   /* { dg-warning "overlaps 5 bytes" "memcpy" } */
+
+  i = SR (SSIZE_MAX - 2, SSIZE_MAX);
+
+  /* Verify a warning for an out-of-bounds offset range and constant
+     size addition.  */
+  T (d, d + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, d, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+  T (d + 1, d + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, d + 1, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+  /* Verify that the warnings above are independent of whether the source
+     and destination are known to be based on the same object.  */
+  T (d, s + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, s, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+  T (d + 1, s + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, s + 1, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+#if __SIZEOF_SIZE_T__ == 8
+  /* Verfiy the offset and size computation is correct.  The overlap
+     offset mentioned in the warning plus sthe size of the access must
+     not exceed SSIZE_MAX.  */
+  T (d, d + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+
+  T (d, s + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+  T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+#elif __SIZEOF_SIZE_T__ == 4
+  T (d, d + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+
+  T (d, s + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+  T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+#endif
+
+  i = SR (SSIZE_MAX - 5, SSIZE_MAX);
+  j = SR (SSIZE_MAX - 9, SSIZE_MAX);
+  n = UR (4, 5);
+  T (d + i, d + j, n);
+
+  n = UR (4, SSIZE_MAX);
+  T (d + i, d + j, n);
+
+  n = UR (4, SIZE_MAX);
+  T (d + i, d + j, n);
+
+  j = SR (SSIZE_MAX - 8, SSIZE_MAX);
+  T (d + i, d + j, n);
+
+  j = SR (SSIZE_MAX - 7, SSIZE_MAX);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+  j = SR (SSIZE_MAX - 6, SSIZE_MAX);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+  n = UR (3, SSIZE_MAX);
+  T (d + i, d + j, n);
+
+  j = SR (SSIZE_MAX - 6, SSIZE_MAX);
+  T (d + i, d + j, n);
+
+  j = SR (SSIZE_MAX - 5, SSIZE_MAX);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+  j = SR (SSIZE_MAX - 4, SSIZE_MAX);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+  j = SR (SSIZE_MAX - 2, SSIZE_MAX);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+}
+
+/* Exercise memcpy with destination and source of unknown size.  */
+
+void test_memcpy_var (char *d, const char *s)
+{
+  size_t n = unsigned_value ();
+
+  memcpy (d, d, 0);
+  sink (d);
+
+  memcpy (d, d, n);               /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  memcpy (d, &d[0], n);           /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  memcpy (&d[0], d,  n);          /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  /* The following overlaps if n is greater than 1.  */
+  s = d + 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + n;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + signed_value ();
+  memcpy (d, s, unsigned_value ());
+  sink (d);
+
+  s = d + 3;
+  n = 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 2;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 3;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 4;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+  sink (d);
+
+  s = d + 5;
+  n = 7;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  n = UR (0, 1);
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+}
+
+/* Exercise the absence of warnings with memmove.  */
+
+void test_memmove (void)
+{
+  {
+    char d[7];
+    sink (d);
+
+    const void *s = d;
+    memmove (d, s, 7);
+    sink (d);
+
+    s = d + 1;
+    memmove (d, s, 6);
+    sink (d);
+
+    s = d + 2;
+    memmove (d + 1, s, 5);
+    sink (d);
+  }
+}
+
+/* Exercise strcat with constant or known arguments.  */
+
+void test_strcat_cst (const char *s)
+{
+#undef T
+#define T(init, dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcat (pd, ps);					\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("0",   a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T ("01",  a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  /* The 3 bytes "12\0" being appended to "012" overwrite the final NUL.  */
+  T ("012", a, a + 1);            /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a, a + 2);            /* { dg-warning "accessing 2 bytes at offsets 0 and 2 overlaps 1 byte at offset 3" "strcat" } */
+  /* The nul copied from a[3] to a[3] overwrites itself so this is
+     diagnosed.  */
+  T ("012", a, a + 3);            /* { dg-warning "accessing 1 byte at offsets 0 and 3 overlaps 1 byte at offset 3" "strcat" } */
+
+  T ("012", a, a + 4);
+  T ("012", a, a + 5);
+  T ("012", a, a + 6);
+  T ("012", a, a + 7);
+  T ("012", a, a + 8);
+
+  T ("0",   a + 1, a);            /* { dg-warning "accessing 2 bytes at offsets 1 and 0 overlaps 1 byte at offset 1" "strcat" } */
+  T ("0",   a + 2, a);
+
+  /* The first of the two offsets in the diagnostic for strcat is that
+     of the first write into the destination, not that of the initial
+     read from it to compute its length.  */
+  T ("01",  a + 1, a);            /* { dg-warning "accessing 3 bytes at offsets 1 and 0 overlaps 1 byte at offset 2" "strcat" } */
+  T ("01",  a + 2, a);            /* { dg-warning "accessing 3 bytes at offsets 2 and 0 overlaps 1 byte at offset 2" "strcat" } */
+  T ("01",  a + 3, a);
+
+  T ("012", a + 1, a);            /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3 " "strcat" } */
+  T ("012", a + 4, a);
+  T ("012", a + 5, a);
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T ("012",      a, "012");
+  T ("012",      a, s);
+  T ("01234567", a, s);
+}
+
+/* Exercise strcat with destination and source of unknown length.  */
+
+void test_strcat_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcat (pd, ps);					\
+      sink (pd, ps);					\
+    }							\
+  } while (0)
+
+  T (d, d);                       /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T (d, d + 1);                   /* { dg-warning "accessing 0 or more bytes at offsets 0 and 1 may overlap 1 byte" "strcat" } */
+  T (d, d + 2);                   /* { dg-warning "accessing 0 or more bytes at offsets 0 and 2 may overlap 1 byte" "strcat" } */
+  T (d, d + 999);                 /* { dg-warning "accessing 0 or more bytes at offsets 0 and 999 may overlap 1 byte" "strcat" } */
+  T (d, d + -99);                 /* { dg-warning "accessing 0 or more bytes at offsets 0 and -99 may overlap 1 byte" "strcat" } */
+
+  size_t n = unsigned_value ();
+
+  T (d + n, d + n);                       /* { dg-warning "\\\[-Wrestrict" "strcat" } */
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T (d, "012");
+  T (d + 1, "0123");
+  T (d + n, "01234");
+  T (d, s);
+  T (d + 1, s);
+  T (d + n, s);
+
+  /* Since the offset is unknown the overlap in the call below, while
+     possible, is certainly not inevitable.  Conservatively, it should
+     not be diagnosed.  For safety, an argument for diagnosing can be
+     made.  It's a judgment call, partly determined by the effort and
+     complexity of treating this case differently from other similar
+     to it.   */
+  T (d, d + n); /* { dg-warning "may overlap" "strcat" } */
+}
+
+/* Exercise strcpy with constant or known arguments.  */
+
+void test_strcpy_cst (ssize_t i)
+{
+#undef T
+#define T(init, dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[8] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcpy (pd, ps);					\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcpy" } */
+  T ("012", a, a + 1);            /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strcpy" } */
+  T ("012", a, a + 2);
+  T ("012", a, a + 3);
+  /* The following doesn't overlap but it should trigger -Wstringop-overflow
+     for reading past the end.  */
+  T ("012", a, a + sizeof a);
+
+  /* The terminating nul written to d[2] overwrites s[0].  */
+  T ("0123", a, a + 2);           /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
+
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("01234", a, a + 2);          /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" } */
+
+  /* This happens to be safe in GCC but it's still wrong.  */
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  T ("012", a + 1, a);            /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 3 bytes at offset 1" "strcpy" } */
+  T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 2 bytes at offset 2" "strcpy" } */
+  T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3" "strcpy" } */
+  T ("012", a + 4, a);
+  /* The following doesn't overlap but it should trigger -Wstrinop-ovewrflow
+     for writing past the end.  */
+  T ("012", a + sizeof a, a);
+}
+
+/* Exercise strcpy with constant or known arguments offset by a range.
+   The tests verify the use of the lower bound of the range which is
+   more restrictive than using the upper bound for positive values.  */
+
+void test_strcpy_range (void)
+{
+#undef T
+#define T(N, init, dst, src)			\
+  do {						\
+    if (!LINE || LINE == __LINE__) {		\
+      char a[N] = init;				\
+      char *pd = (dst);				\
+      const char *ps = (src);			\
+      strcpy (pd, ps);				\
+      sink (a, pd, ps);				\
+    }						\
+  } while (0)
+
+  ssize_t r;
+
+  r = SR (0, 1);
+  T (8, "0", a + r, a);   /* { dg-warning "accessing between 1 and 2 bytes at offsets \\\[0, 1] and 0 overlaps up to 2 bytes at offset \\\[0, 1]" "strcpy" { xfail *-*-*} } */
+
+  r = SR (2, 5);
+  T (8, "01",  a + r, a);            /* { dg-warning "accessing 3 bytes at offsets \\\[2, 5] and 0 may overlap 1 byte at offset 2" } */
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[2, 5] and 0 may overlap up to 2 bytes at offset \\\[3, 2]" "strcpy" } */
+
+  /* The highest offset to which to copy without overflowing the 8-byte
+     destination is 3 and that overlaps 2 bytes.  */
+  T (8, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "strcpy" } */
+
+  /* With a 9-byte destination the highest offset is 4 and that still
+     overlaps 1 byte (the final NUL).  */
+  T (9, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 1 and 3 bytes at offset \\\[4, 2]" "strcpy" } */
+
+  /* With a 10-byte buffer it's possible to copy all 5 bytes without
+     overlap at (a + 5).  Copying at offsets 2 through 4 overflows
+     between 3 and 1 bytes, respectively.  */
+  T (10, "0123", a + r, a);          /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 may overlap up to 3 bytes at offset \\\[4, 2]" "strcpy" } */
+
+
+  r  = SR (3, 4);
+  T (8, "01",  a + r, a);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[3, 4] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+  /* The highest offset to which to copy without overflowing the 8-byte
+     destination is 3 and that overlaps 2 bytes.  */
+  T (8, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps 2 bytes at offset 3" "strcpy" } */
+
+  /* With a 9-byte destination the highest offset is 4 and that still
+     overlaps 1 byte (the final NUL).  */
+  T (9, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[4, 3]" "strcpy" } */
+
+  /* With a 10-byte buffer it's possible to copy all 5 bytes without
+     overlap at (a + 5).  Copying at offsets 2 through 4 overflows
+     between 3 and 1 bytes, respectively.  */
+  T (10, "0123", a + r, a);          /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[4, 3]" "strcpy" } */
+
+  T (8, "01",     a, a + r);
+  T (8, "012",    a, a + r);
+  T (8, "0123",   a, a + r);
+  T (8, "01234",  a, a + r);
+
+  /* With the smaller offset of 3 the final NUL definitely overlaps
+     the '4' at a[3], but with the larger offset of 4 there is no
+     overlap, so the warning is a "may overlap" and the size of
+     the overlap is 1 byte.  */
+  T (8, "012345", a, a + r);         /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and \\\[3, 4] may overlap 1 byte at offset 3" "strcpy" } */
+  T (8, "0123456", a, a + r);        /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and \\\[3, 4] may overlap up to 2 bytes at offset 3" "strcpy" } */
+
+  r = SR (3, SSIZE_MAX - 3);
+  T (8, "01",  a + r, a);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[3, \[0-9\]+] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+  r = SR (SSIZE_MAX - 2, SSIZE_MAX - 1);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps" "strcpy" } */
+
+  /* Exercise the full range of ssize_t.  */
+  r = signed_value ();
+
+  /* The overlap in the cases below isn't inevitable but it is diagnosed
+     because it is possible and so the code is considered unsafe.  */
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap 1 byte" "strcpy" } */
+  T (8, "0", a + r, a);              /* { dg-warning "accessing 2 bytes may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes may overlap up to 4 bytes" "strcpy" } */
+
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap" "strcpy" } */
+  T (8, "0", a, a + r);              /* { dg-warning "accessing between 0 and 2 bytes may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a, a + r);            /* { dg-warning "accessing between 0 and 4 bytes may overlap up to 4 bytes" "strcpy" } */
+}
+
+/* Exercise strcpy with destination and/or source of unknown lengthu.  */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcpy (pd, ps);					\
+      sink (pd, ps);					\
+    }							\
+  } while (0)
+
+  T (d, s);
+
+  T (d, &d[0]);                   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+  T (&d[0], d);                   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  s = d;
+  T (d, s);                       /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  /* The following overlaps if *s is not nul.  It arguably should be
+     diagnosed.  */
+  T (d, d + 1);
+
+  /* The following overlaps if strlen (d) is greater than 1.  Like
+     the above, it possibly should be diagnosed too.  */
+  int r = SR (2, 3);
+  T (d, d + r);
+
+  /* The following overlaps only if strlen (s + n) >= n so it's not
+     diagnosed.  */
+  s = d + signed_value ();
+  T (d, s);
+}
+
+/* Exercise strncpy with constant or known arguments.  */
+
+void test_strncpy_cst (void)
+{
+#undef T
+#define T(init, dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[7] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strncpy (pd, ps, (size));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("012", a, a, 0);
+  T ("012", a, a, 1);             /* { dg-warning "source argument is the same as destination " "strncpy" } */
+  T ("012", a, a + 1, 1);
+  T ("012", a, a + 1, 2);         /* { dg-warning "accessing 2 bytes at offsets 0 and 1 overlaps 1 byte at offset 1" "strncpy" } */
+  T ("012", a, a + 1, 3);         /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strncpy" } */
+  T ("012", a, a + 2, 1);
+  T ("012", a, a + 2, 2);
+  /* The third written byte (nul) overwrites a[2].  */
+  T ("012", a, a + 2, 3);         /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+
+  T ("0123", a, a + 2, 1);
+  T ("0123", a, a + 2, 2);
+  /* The terminating nul written to a[2] overwrites s[0].  */
+  T ("0123", a, a + 2, 3);        /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+
+  T ("01234", a, a + 2, 1);
+  T ("01234", a, a + 2, 2);
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("01234", a, a + 2, 4);       /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+}
+
+
+/* Exercise strncpy with one or more arguments in a determinate range.  */
+
+void test_strncpy_range (char *d, size_t n)
+{
+#undef T
+#define T(init, dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = init;					\
+      strncpy ((dst), (src), (size));			\
+      sink (a, (dst), (src));				\
+    }							\
+  } while (0)
+
+  ssize_t i;
+
+  i = SR (0, 1);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);   /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[0, 1] may overlap 1 byte at offset 1" "strncpy" { xfail *-*-* } } */
+
+  i = SR (1, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);   /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[1, 5] may overlap 1 byte at offset 1" "strncpy" } */
+  T ("0123", a, a + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[1, 5] may overlap up to 2 bytes at offset \\\[2, 1]" "strncpy" } */
+
+  i = SR (2, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[2, 5] may overlap 1 byte at offset 2" "strncpy" } */
+  T ("0123", a, a + i, 4);   /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[2, 5] may overlap up to 2 bytes at offset \\\[3, 2]" "strncpy" } */
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+
+  i = SR (3, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);
+  T ("0123", a, a + i, 4);   /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[3, 5] may overlap 1 byte at offset 3" "strncpy" } */
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+
+  i = SR (4, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);
+  T ("0123", a, a + i, 4);
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+
+  /* Verify offset and size both in some range.  The strncpy checking
+     is more strict than that of memcpy and triggers even when the
+     overlap is possible but not inevitable.  The following overlaps
+     like so ('*' denotes the terminating NUL, '.' the appended NUL
+     that's not copied from the source):
+        a:        01234567*  (also indicates offset)
+        i = 4:    4567       none
+                  4567*      overlaps 1 at offset 4
+                  4567*.     overlaps 2 at offset 4
+        i = 5:    567*       none
+                  567*.      none
+                  567*..     overlaps 1 at offset 5  */
+  T ("01234567", a, a + i, UR (4, 6));   /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  /* Ditto for objects of unknown sizes.  */
+  T ("01234567", d, d + i, UR (4, 6));  /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  T ("01234567", a, a + i, UR (6, 7));  /* { dg-warning "accessing between 6 and 7 bytes at offsets 0 and \\\[4, 5] overlaps between 1 and 3 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  /* The following overlaps except in the unlikely case that value ()
+     is zero, so it's diagnosed.  */
+  T ("012", a, a, n);             /* { dg-warning "source argument is the same as destination " "strncpy" } */
+}
+
+
+/* Exercise strncpy with destination and source of unknown length.  */
+
+void test_strncpy_var (char *d, const char *s, size_t n)
+{
+#undef T
+#define T(dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {		\
+      char *pd = (dst);				\
+      const char *ps = (src);			\
+      strncpy (pd, ps, (size));			\
+      sink (pd, ps);				\
+    }						\
+  } while (0)
+
+  T (d, s, 1);
+  T (d, s, n);
+
+  T (d, d, 1);                    /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+  T (d, d, n);                    /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+
+  T (d,     d + 1, 1);
+  T (d,     d + 1, 2);            /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+  T (d + 1, d,     1);
+  T (d + 1, d,     2);            /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+}
+
+struct MemberArrays
+{
+  char a[7];
+  char b[8];
+  char c[9];
+};
+
+void test_strncpy_strcpy_var (struct MemberArrays *ar, const char *s)
+{
+  /* The following is safe and should not trigger a warning.  */
+  strncpy (ar->b, s, sizeof ar->b - 1);
+  ar->b[sizeof ar->b - 1] = '\0';
+  strcpy (ar->a, ar->b);
+  sink (ar);
+
+  /* The following is not as safe (it might overflow ar->a) but there
+     is no overlap so it also shouldn't trigger -Wrestrict.  */
+  strncpy (ar->c, s, sizeof ar->c - 1);
+  ar->c[sizeof ar->c - 1] = '\0';
+  strcpy (ar->a, ar->c);
+  sink (ar);
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c b/gcc/testsuite/gcc.dg/Walloca-1.c
index ad39373..85e9160 100644
--- a/gcc/testsuite/gcc.dg/Walloca-1.c
+++ b/gcc/testsuite/gcc.dg/Walloca-1.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* { dg-require-effective-target alloca } */
-/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+/* { dg-options "-Walloca-larger-than=2000 -O2 -ftrack-macro-expansion=0" } */
 
 #define alloca __builtin_alloca
 
diff --git a/gcc/testsuite/gcc.dg/memcpy-6.c b/gcc/testsuite/gcc.dg/memcpy-6.c
new file mode 100644
index 0000000..1d76e7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/memcpy-6.c
@@ -0,0 +1,40 @@
+/* Test to verify that overlapping memcpy with const sizes that are powers
+   of two are folded into into the same code as memmove, but that they
+   are diagnosed nonetheless.
+   { dg-do compile }
+   { dg-options "-O0 -Wrestrict -fdump-tree-optimized" } */
+
+char a[32];
+
+void fold_copy_2 (void)
+{
+  __builtin_memcpy (a + 1, a, 2);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_4 (void)
+{
+  __builtin_memcpy (a + 2, a, 4);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_8 (void)
+{
+  __builtin_memcpy (a + 3, a, 8);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_move_2 (void)
+{
+  __builtin_memmove (a + 1, a, 2);
+}
+
+void fold_move_4 (void)
+{
+  __builtin_memmove (a + 2, a, 4);
+}
+
+void fold_move_8 (void)
+{
+  __builtin_memmove (a + 3, a, 8);
+}
+
+/* { dg-final { scan-tree-dump-not "memcpy" "optimized" } }
+   { dg-final { scan-tree-dump-not "memmove" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c
index c0e7463..908d5a6 100644
--- a/gcc/testsuite/gcc.dg/pr69172.c
+++ b/gcc/testsuite/gcc.dg/pr69172.c
@@ -1,4 +1,5 @@
-/* PR tree-optimization/69172 */
+/* PR tree-optimization/69172 - ICE in make_ssa_name_fn,
+   at tree-ssanames.c:266 */
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
 
@@ -43,3 +44,7 @@ f6 (int x)
 {
   return __builtin___mempcpy_chk (&a, &a, x, 0);
 }
+
+/* The calls above violate strict aliasing.  Eliminate the -Wrestrict
+   warnings they trigger.
+  { dg-prune-output "\\\[-Wrestrict]" } */
diff --git a/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c b/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c
new file mode 100644
index 0000000..1be4922
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c
@@ -0,0 +1,68 @@
+/* { dg-do compile { target { ! x32 } } }
+   { dg-require-effective-target mempcpy }
+   { dg-options "-O2 -Wrestrict -fcheck-pointer-bounds -mmpx" } */
+
+#define USE_GNU
+#include "../../gcc.dg/strlenopt.h"
+
+/* There is no BUILT_IN_ST{P,R}NCPY_CHKP or BUILT_IN_STRNCAT_CHKP
+   so the test for them below are XFAIL.  */
+char *stpncpy (char *__restrict, const char *__restrict, size_t);
+char *strncpy (char *__restrict, const char *__restrict, size_t);
+char *strncat (char *__restrict, const char *__restrict, size_t);
+
+
+char a[8];
+
+void test_memcpy (void)
+{
+  memcpy (a, a + 1, 3);   /* { dg-warning ".memcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+void test_memmove (void)
+{
+  memmove (a, a + 1, 3);
+}
+
+void* test_mempcpy (void)
+{
+  return mempcpy (a, a + 1, 3);   /* { dg-warning ".mempcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+char* test_stpcpy (void)
+{
+  strcpy (a, "0123456");
+  return stpcpy (a, a + 2);   /* { dg-warning ".stpcpy\.chkp. accessing 6 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" } */
+}
+
+char* test_stpncpy (void)
+{
+  strcpy (a, "0123456");
+
+  /* There is no BUILT_IN_STPNCPY_CHKP so this isn't handled.  */
+  return stpncpy (a, a + 2, sizeof a);   /* { dg-warning ".stpcpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strcpy (void)
+{
+  strcpy (a, "0123456");
+  strcpy (a, a + 1);   /* { dg-warning ".strcpy\.chkp. accessing 7 bytes at offsets 0 and 1 overlaps 6 bytes at offset 1" } */
+}
+
+void test_strcat (int n)
+{
+  strcat (a, a + 3);   /* { dg-warning ".strcat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" } */
+}
+
+void test_strncat (int n)
+{
+  strncat (a, a + 3, sizeof a);   /* { dg-warning ".strncat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strncpy (int n)
+{
+  strcpy (a, "0123456");
+
+  /* There is no BUILT_IN_STRNCPY_CHKP so this isn't handled.  */
+  strncpy (a, a + 2, sizeof a);   /* { dg-warning ".strncpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 5 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 4ec0dac..3193c31 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -40,12 +40,17 @@ along with GCC; see the file COPYING3.  If not see
 #include "expr.h"
 #include "tree-dfa.h"
 #include "domwalk.h"
+#include "tree-ssa-alias.h"
 #include "tree-ssa-propagate.h"
 #include "params.h"
 #include "ipa-chkp.h"
 #include "tree-hash-traits.h"
 #include "builtins.h"
 #include "target.h"
+#include "diagnostic-core.h"
+#include "diagnostic.h"
+#include "intl.h"
+#include "attribs.h"
 
 /* A vector indexed by SSA_NAME_VERSION.  0 means unknown, positive value
    is an index into strinfo vector, negative value stands for
@@ -1362,7 +1367,7 @@ static void
 handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, len, lhs, args, type, fn, oldlen;
+  tree src, dst, srclen, len, lhs, type, fn, oldlen;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi, *olddsi, *zsi;
@@ -1478,6 +1483,22 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 	    }
 	}
       dsi->stmt = stmt;
+
+      /* Try to detect overlap before returning.  This catches cases
+	 like strcpy (d, d + n) where n is non-constant whose range
+	 is such that (n <= strlen (d) holds).
+
+	 OLDDSI->NONZERO_chars may have been reset by this point with
+	 oldlen holding it original value.  */
+      if (olddsi && oldlen)
+	{
+	  /* Add 1 for the terminating NUL.  */
+	  tree type = TREE_TYPE (oldlen);
+	  oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
+				build_int_cst (type, 1));
+	  check_bounds_or_overlap (stmt, olddsi->ptr, src, oldlen, NULL_TREE);
+	}
+
       return;
     }
 
@@ -1550,16 +1571,30 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (zsi != NULL)
     zsi->dont_invalidate = true;
 
-  if (fn == NULL_TREE)
-    return;
-
-  args = TYPE_ARG_TYPES (TREE_TYPE (fn));
-  type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+  if (fn)
+    {
+      tree args = TYPE_ARG_TYPES (TREE_TYPE (fn));
+      type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+    }
+  else
+    type = size_type_node;
 
   len = fold_convert_loc (loc, type, unshare_expr (srclen));
   len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
+
+  if (const strinfo *chksi = olddsi ? olddsi : dsi)
+    if (si
+	&& !check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
+      /* Avoid transforming strcpy when out-of-bounds offsets or
+	 overlapping acceess is detected..  */
+      return;
+
+  if (fn == NULL_TREE)
+    return;
+
   len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
 				  GSI_SAME_STMT);
+
   if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     {
       fprintf (dump_file, "Optimizing: ");
@@ -1607,6 +1642,68 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
     fprintf (dump_file, "not possible.\n");
 }
 
+/* Check the size argument to the built-in forms of stpncpy and strncpy
+   to see if it's derived from calling strlen() on the source argument
+   and if so, issue a warning.  */
+
+static void
+handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+
+  bool with_bounds = gimple_call_with_bounds_p (stmt);
+
+  tree dst = gimple_call_arg (stmt, with_bounds ? 1 : 0);
+  tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
+  tree dstsize = NULL_TREE, srcsize = NULL_TREE;
+
+  int didx = get_stridx (dst);
+  if (strinfo *sidst = didx > 0 ? get_strinfo (didx) : NULL)
+    {
+      /* Compute the size of the destination string including the NUL.  */
+      if (sidst->nonzero_chars)
+	{
+	  tree type = TREE_TYPE (sidst->nonzero_chars);
+	  dstsize = fold_build2 (PLUS_EXPR, type, sidst->nonzero_chars,
+				 build_int_cst (type, 1));
+	}
+      dst = sidst->ptr;
+    }
+
+  int sidx = get_stridx (src);
+  if (strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL)
+    {
+      /* Strncpy() et al. cannot modify the source string.  Prevent
+	 the rest of the pass from invalidating the strinfo data.  */
+      sisrc->dont_invalidate = true;
+
+      /* Compute the size of the source string including the NUL.  */
+      if (sisrc->nonzero_chars)
+	{
+	  tree type = TREE_TYPE (sisrc->nonzero_chars);
+	  srcsize = fold_build2 (PLUS_EXPR, type, sisrc->nonzero_chars,
+				 build_int_cst (type, 1));
+	}
+
+	src = sisrc->ptr;
+    }
+  else
+    srcsize = NULL_TREE;
+
+  check_bounds_or_overlap (stmt, dst, src, dstsize, srcsize);
+}
+
+/* Check the size argument to the built-in forms of strncat to see if
+   it's derived from calling strlen() on the source argument and if so,
+   issue a warning.  */
+
+static void
+handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
+{
+  /* Same as stxncpy().  */
+  handle_builtin_stxncpy (bcode, gsi);
+}
+
 /* Handle a memcpy-like ({mem{,p}cpy,__mem{,p}cpy_chk}) call.
    If strlen of the second argument is known and length of the third argument
    is that plus one, strlen of the first argument is the same after this
@@ -1787,16 +1884,22 @@ static void
 handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr;
+  tree srclen, args, type, fn, objsz, endptr;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi;
-  location_t loc;
+  location_t loc = gimple_location (stmt);
   bool with_bounds = gimple_call_with_bounds_p (stmt);
 
-  src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
-  dst = gimple_call_arg (stmt, 0);
-  lhs = gimple_call_lhs (stmt);
+  tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
+  tree dst = gimple_call_arg (stmt, 0);
+
+  /* Bail if the source is the same as destination.  It will be diagnosed
+     elsewhere.  */
+  if (operand_equal_p (src, dst, 0))
+    return;
+
+  tree lhs = gimple_call_lhs (stmt);
 
   didx = get_stridx (dst);
   if (didx < 0)
@@ -1805,10 +1908,43 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   dsi = NULL;
   if (didx > 0)
     dsi = get_strinfo (didx);
+
+  srclen = NULL_TREE;
+  si = NULL;
+  idx = get_stridx (src);
+  if (idx < 0)
+    srclen = build_int_cst (size_type_node, ~idx);
+  else if (idx > 0)
+    {
+      si = get_strinfo (idx);
+      if (si != NULL)
+	srclen = get_string_length (si);
+    }
+
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
+      {
+	  /* The concatenation always involves copying at least one byte
+	     (the terminating nul), even if the source string is empty.
+	     If the source is unknown assume it's one character long and
+	     used that as both sizes.  */
+	tree slen = srclen;
+	if (slen)
+	  {
+	    tree type = TREE_TYPE (slen);
+	    slen = fold_build2 (PLUS_EXPR, type, slen, build_int_cst (type, 1));
+	  }
+
+	tree sptr = si && si->ptr ? si->ptr : src;
+
+	if (!check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
+	  /* Avoid transforming strcat when out-of-bounds offsets or
+	     overlapping acceess is detected..  */
+	  return;
+      }
+
       /* strcat (p, q) can be transformed into
-	 tmp = p + strlen (p); endptr = strpcpy (tmp, q);
+	 tmp = p + strlen (p); endptr = stpcpy (tmp, q);
 	 with length endptr - p if we need to compute the length
 	 later on.  Don't do this transformation if we don't need
 	 it.  */
@@ -1841,20 +1977,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       return;
     }
 
-  srclen = NULL_TREE;
-  si = NULL;
-  idx = get_stridx (src);
-  if (idx < 0)
-    srclen = build_int_cst (size_type_node, ~idx);
-  else if (idx > 0)
-    {
-      si = get_strinfo (idx);
-      if (si != NULL)
-	srclen = get_string_length (si);
-    }
-
-  loc = gimple_location (stmt);
-  dstlen = dsi->nonzero_chars;
+  tree dstlen = dsi->nonzero_chars;
   endptr = dsi->endptr;
 
   dsi = unshare_strinfo (dsi);
@@ -1915,7 +2038,23 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (fn == NULL_TREE)
     return;
 
-  len = NULL_TREE;
+  if (dsi && dstlen)
+    {
+      tree type = TREE_TYPE (dstlen);
+
+      /* Compute the size of the source sequence, including the nul.  */
+      tree srcsize = srclen ? srclen : size_zero_node;
+      srcsize = fold_build2 (PLUS_EXPR, type, srcsize, build_int_cst (type, 1));
+
+      tree sptr = si && si->ptr ? si->ptr : src;
+
+      if (!check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
+	  /* Avoid transforming strcat when out-of-bounds offsets or
+	     overlapping acceess is detected..  */
+	return;
+    }
+
+  tree len = NULL_TREE;
   if (srclen != NULL_TREE)
     {
       args = TYPE_ARG_TYPES (TREE_TYPE (fn));
@@ -2481,11 +2620,11 @@ fold_strstr_to_strncmp (tree rhs1, tree rhs2, gimple *stmt)
     }
 }
 
-/* Attempt to optimize a single statement at *GSI using string length
-   knowledge.  */
+/* Attempt to check for validity of the performed access a single statement
+   at *GSI using string length knowledge, and to optimize it.  */
 
 static bool
-strlen_optimize_stmt (gimple_stmt_iterator *gsi)
+strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi)
 {
   gimple *stmt = gsi_stmt (*gsi);
 
@@ -2513,6 +2652,19 @@ strlen_optimize_stmt (gimple_stmt_iterator *gsi)
 	  case BUILT_IN_STPCPY_CHK_CHKP:
 	    handle_builtin_strcpy (DECL_FUNCTION_CODE (callee), gsi);
 	    break;
+
+	  case BUILT_IN_STRNCAT:
+	  case BUILT_IN_STRNCAT_CHK:
+	    handle_builtin_strncat (DECL_FUNCTION_CODE (callee), gsi);
+	    break;
+
+	  case BUILT_IN_STPNCPY:
+	  case BUILT_IN_STPNCPY_CHK:
+	  case BUILT_IN_STRNCPY:
+	  case BUILT_IN_STRNCPY_CHK:
+	    handle_builtin_stxncpy (DECL_FUNCTION_CODE (callee), gsi);
+	    break;
+
 	  case BUILT_IN_MEMCPY:
 	  case BUILT_IN_MEMCPY_CHK:
 	  case BUILT_IN_MEMPCPY:
@@ -2741,7 +2893,7 @@ strlen_dom_walker::before_dom_children (basic_block bb)
 
   /* Attempt to optimize individual statements.  */
   for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
-    if (strlen_optimize_stmt (&gsi))
+    if (strlen_check_and_optimize_stmt (&gsi))
       gsi_next (&gsi);
 
   bb->aux = stridx_to_strinfo;

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

* [PING][PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-10-24  6:40                             ` Martin Sebor
@ 2017-11-10  0:28                               ` Martin Sebor
  2017-11-16 22:24                                 ` [PING 2][PATCH] " Martin Sebor
  0 siblings, 1 reply; 39+ messages in thread
From: Martin Sebor @ 2017-11-10  0:28 UTC (permalink / raw)
  To: Richard Biener, Jeff Law; +Cc: Gcc Patch List

Ping:

   https://gcc.gnu.org/ml/gcc-patches/2017-10/msg01642.html

On 10/23/2017 08:42 PM, Martin Sebor wrote:
> Attached is a reworked solution to enhance -Wrestrict while
> avoiding changing tree-vrp.c or any other VRP machinery.  Richard,
> in considering you suggestions I realized that the ao_ref struct
> isn't general enough to detect the kinds of problems I needed to
> etect (storing bit-offsets in HOST_WIDE_INT means out-of-bounds
> offsets cannot be represented or detected, leading to either false
> positives or false negatives).
>
> Instead, the solution adds a couple of small classes to builtins.c
> to overcome this limitation (I'm contemplating moving them along
> with -Wstringop-overflow to a separate file to keep builtins.c
> from getting too much bigger).
>
> The detection of out-of-bounds offsets and overlapping accesses
> is relatively simple but the rest of the changes are somewhat
> involved because of the computation of the offsets and sizes of
> the overlaps.
>
> Jeff, as per your suggestion/request in an earlier review (bug
> 81117) I've renamed some of the existing functions to better
> reflect their new function (including renaming strlen_optimize_stmt
> in tree-ssa-strlen.c to strlen_check_and_optimize_stmt).  There's
> quite a bit of churn due to some of this renaming.  I don't think
> this renaming makes the review too difficult but if you feel
> differently I can [be persuaded to] split it out into a separate
> patch.
>
> To validate the patch I compiled the Linux kernel and Binutils/GDB.
> There's one false positive I'm working on resolving that's caused
> by an incorrect interpretation of an offset in a range whose lower
> bound is greater than its upper bound.  This it so say that while
> I'm aware the patch isn't perfect it already works reasonably well
> in practice and I think it's close enough to review.
>
> Thanks
> Martin

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

* [PING 2][PATCH] enhance -Wrestrict to handle string built-ins (PR 78918)
  2017-11-10  0:28                               ` [PING][PATCH] " Martin Sebor
@ 2017-11-16 22:24                                 ` Martin Sebor
  2017-11-23  0:16                                   ` Jeff Law
  2017-11-27 12:45                                   ` Richard Biener
  0 siblings, 2 replies; 39+ messages in thread
From: Martin Sebor @ 2017-11-16 22:24 UTC (permalink / raw)
  To: Richard Biener, Jeff Law; +Cc: Gcc Patch List

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

Ping.

I've fixed the outstanding false positive exposed by the Linux
kernel.  The kernel builds with four instances of the warning,
all of them valid (perfect overlap in memcpy).

I also built Glibc.  It shows one instance of the warning, also
a true positive (cause by calling a restrict-qualified function
with two copies of the same argument).

Finally, I built Binutils and GDB with no warnings.

The attached patch includes just that one fix, with everything
else being the same.

On 11/09/2017 04:57 PM, Martin Sebor wrote:
> Ping:
>
>   https://gcc.gnu.org/ml/gcc-patches/2017-10/msg01642.html
>
> On 10/23/2017 08:42 PM, Martin Sebor wrote:
>> Attached is a reworked solution to enhance -Wrestrict while
>> avoiding changing tree-vrp.c or any other VRP machinery.  Richard,
>> in considering you suggestions I realized that the ao_ref struct
>> isn't general enough to detect the kinds of problems I needed to
>> etect (storing bit-offsets in HOST_WIDE_INT means out-of-bounds
>> offsets cannot be represented or detected, leading to either false
>> positives or false negatives).
>>
>> Instead, the solution adds a couple of small classes to builtins.c
>> to overcome this limitation (I'm contemplating moving them along
>> with -Wstringop-overflow to a separate file to keep builtins.c
>> from getting too much bigger).
>>
>> The detection of out-of-bounds offsets and overlapping accesses
>> is relatively simple but the rest of the changes are somewhat
>> involved because of the computation of the offsets and sizes of
>> the overlaps.
>>
>> Jeff, as per your suggestion/request in an earlier review (bug
>> 81117) I've renamed some of the existing functions to better
>> reflect their new function (including renaming strlen_optimize_stmt
>> in tree-ssa-strlen.c to strlen_check_and_optimize_stmt).  There's
>> quite a bit of churn due to some of this renaming.  I don't think
>> this renaming makes the review too difficult but if you feel
>> differently I can [be persuaded to] split it out into a separate
>> patch.
>>
>> To validate the patch I compiled the Linux kernel and Binutils/GDB.
>> There's one false positive I'm working on resolving that's caused
>> by an incorrect interpretation of an offset in a range whose lower
>> bound is greater than its upper bound.  This it so say that while
>> I'm aware the patch isn't perfect it already works reasonably well
>> in practice and I think it's close enough to review.
>>
>> Thanks
>> Martin
>


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

PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over self

gcc/c-family/ChangeLog:

	PR tree-optimization/78918
	* c-common.c (check_function_restrict): Avoid checking built-ins.
	* c.opt (-Wrestrict): Include in -Wall.

gcc/ChangeLog:

	PR tree-optimization/78918
	* builtins.c (builtin_memref, builtin_access): New classes.
	(check_bounds_or_overlap, maybe_diag_overlap): New static functions.
	(maybe_diag_offset_bounds): Same.
	(check_sizes): Rename...
	(check_access): ...to this.  Rename function arguments for clarity.
	(check_memop_sizes): Adjust names.
	(expand_builtin_memchr, expand_builtin_memcpy): Same.
	(expand_builtin_memmove, expand_builtin_mempcpy): Same.
	(expand_builtin_strcat, expand_builtin_stpncpy): Same.
	(check_strncat_sizes, expand_builtin_strncat): Same.
	(expand_builtin_strncpy, expand_builtin_memset): Same.
	(expand_builtin_bzero, expand_builtin_memcmp): Same.
	(expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
	(maybe_emit_sprintf_chk_warning): Same.
	(expand_builtin_strcpy): Adjust.
	(expand_builtin_stpcpy): Same.
	(expand_builtin_with_bounds): Detect out-of-bounds/overlapping
	accesses in pointer-checking forms of memcpy, memmove, and mempcpy.
	(gcall_to_tree_minimal, check_bounds_or_overlap, max_object_size):
	Define new functions.
	* builtins.h (check_bounds_or_overlap, max_object_size): Declare.
	* calls.c (alloc_max_size): Call max_object_size instead of
	hardcoding ssizetype limit.  Fix a typo.
	(get_size_range): Handle new argument.
	* calls.h (get_size_range): Add a new argument.
	* cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
	* doc/invoke.texi (-Wrestrict): Adjust, add example.
	* gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
	operations.
	(gimple_fold_builtin_memory_chk): Same.
	(gimple_fold_builtin_stxcpy_chk): New function.
	* gimple.c (gimple_build_call_from_tree): Propagate location.
	* tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
	operations.
	(handle_builtin_strcat): Same.
	(strlen_optimize_stmt): Rename...
	(strlen_check_and_optimize_stmt): ...to this.  Handle strncat,
	stpncpy, strncpy, and their checking forms.

gcc/testsuite/ChangeLog:

	PR tree-optimization/78918
	* c-c++-common/Warray-bounds.c: New test.
	* c-c++-common/Wrestrict-2.c: New test.
	* c-c++-common/Wrestrict.c: New test.
	* c-c++-common/Wrestrict.s: New test.
	* gcc.dg/Walloca-1.c: Adjust/
	* gcc.dg/memcpy-6.c: New test.
	* gcc.dg/pr69172.c: Adjust.
	* gcc.target/i386/chkp-stropt-17.c: New test.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index d3498bb..2bc1c09 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -46,6 +46,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "stor-layout.h"
 #include "calls.h"
 #include "varasm.h"
+#include "tree-dfa.h"
 #include "tree-object-size.h"
 #include "realmpfr.h"
 #include "cfgrtl.h"
@@ -192,6 +193,91 @@ static tree do_mpfr_remquo (tree, tree, tree);
 static tree do_mpfr_lgamma_r (tree, tree, tree);
 static void expand_builtin_sync_synchronize (void);
 
+struct builtin_memref;
+
+/* Description of a memory access by a raw memory or string built-in
+   function.  */
+struct builtin_access
+{
+private:
+  /* Temporaries used to compute the final result.  */
+  offset_int dstoff[2];
+  offset_int srcoff[2];
+  offset_int dstsiz[2];
+  offset_int srcsiz[2];
+
+  /* Member function to call to determine overlap.  */
+  bool (builtin_access::*detect_overlap) ();
+
+public:
+  /* Destination and source memory reference.  */
+  builtin_memref* const dstref;
+  builtin_memref* const srcref;
+  /* The minimum and maximum size of the access. */
+  HOST_WIDE_INT sizrange[2];
+
+  /* The minimum and maximum offset of an overlap of the access
+     (if it does, in fact, overlap), and the size of the overlap.  */
+  HOST_WIDE_INT ovloff[2];
+  HOST_WIDE_INT ovlsiz[2];
+
+  builtin_access (tree, builtin_memref &, builtin_memref &);
+
+  /* Entry point to determine overlap.  */
+  bool overlap ();
+
+private:
+  /* Implementation functions used to determine overlap.  */
+  bool generic_overlap ();
+  bool strcat_overlap ();
+  bool strcpy_overlap ();
+
+  bool no_overlap ()
+  {
+    return false;
+  }
+};
+
+/* Description of a memory reference by a built-in function.  */
+struct builtin_memref
+{
+  /* The original pointer argument to the built-in function.  */
+  tree ptr;
+  /* The referenced subobject or NULL if not available, and the base
+     object of the memory reference or NULL.  */
+  tree ref;
+  tree base;
+
+  /* The size of the BASE object, PTRDIFF_MAX if indeterminate, and
+     -1 until (lazily) initialized.  */
+  offset_int objsize;
+
+  /* The offset of the referenced subobject.  Used to avoid warnings for
+     (apparently) possibly but not definitively overlapping accesses to
+     member arrays.  */
+  offset_int refoff;
+
+  /* The offset range relative to the base.  */
+  offset_int offrange[2];
+  /* The size range of the access.  */
+  offset_int sizrange[2];
+
+  /* True for "bounded" string functions like strncat, and strncpy
+     and their variants that specify either an exact or upper bound
+     on the size of the accesses they perform.  For strncat both
+     the source and destination references are bounded.  For strncpy
+     only the destination reference is.  */
+  bool strbounded_p;
+
+  builtin_memref (tree, tree);
+
+  tree offset_out_of_bounds () const;
+};
+
+static bool check_bounds_or_overlap (tree, tree, tree, tree, tree,
+				      bool = false);
+static bool maybe_diag_overlap (location_t, tree, builtin_access &);
+
 /* Return true if NAME starts with __builtin_ or __sync_.  */
 
 static bool
@@ -2962,39 +3048,157 @@ determine_block_size (tree len, rtx len_rtx,
 			  GET_MODE_MASK (GET_MODE (len_rtx)));
 }
 
+/* Validate REF offsets in an EXPRession passed as an argument to a CALL
+   to a built-in function FUNC to make sure they are within the bounds
+   of the referenced object if its size is known, or PTRDIFF_MAX otherwise.
+   Both initial values of the offsets and their final value computed by
+   the function by incrementing the initial value by the size are
+   validated.  Return true if the offsets are not valid and a diagnostic
+   has been isssued.  */
+
+static bool
+maybe_diag_offset_bounds (location_t loc, tree call, tree func, tree expr,
+			  const builtin_memref &ref)
+{
+  if (!warn_array_bounds)
+    return false;
+
+  tree oobref = ref.offset_out_of_bounds ();
+  if (!oobref)
+    return false;
+
+  if (EXPR_HAS_LOCATION (expr))
+    loc = EXPR_LOCATION (expr);
+
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  tree type;
+
+  char rangestr[2][64];
+  if (ref.offrange[0] == ref.offrange[1])
+    sprintf (rangestr[0], "%lli", (long long) ref.offrange[0].to_shwi ());
+  else
+    sprintf (rangestr[0], "[%lli, %lli]",
+	     (long long) ref.offrange[0].to_shwi (),
+	     (long long) ref.offrange[1].to_shwi ());
+
+  if (oobref == error_mark_node)
+    {
+      if (ref.sizrange[0] == ref.sizrange[1])
+	sprintf (rangestr[1], "%lli", (long long) ref.sizrange[0].to_shwi ());
+      else
+	sprintf (rangestr[1], "[%lli, %lli]",
+		 (long long) ref.sizrange[0].to_shwi (),
+		 (long long) ref.sizrange[1].to_shwi ());
+
+      if (DECL_P (ref.base)
+	  && TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
+	{
+	  if (warning_at (loc, OPT_Warray_bounds,
+			  "%K%qD pointer overflow between offset %s "
+			  "and size %s accessing array %qD with type %qT",
+			  call, func, rangestr[0], rangestr[1], ref.base, type))
+	    inform (DECL_SOURCE_LOCATION (ref.base),
+		    "array %qD declared here", ref.base);
+	  else
+	    warning_at (loc, OPT_Warray_bounds,
+			"%K%qD pointer overflow between offset %s "
+			"and size %s",
+			call, func, rangestr[0], rangestr[1]);
+	}
+      else
+	warning_at (loc, OPT_Warray_bounds,
+		    "%K%qD pointer overflow between offset %s "
+		    "and size %s",
+		    call, func, rangestr[0], rangestr[1]);
+    }
+  else if (oobref == ref.base)
+    {
+      if (DECL_P (ref.base)
+	  && TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
+	{
+	  if (warning_at (loc, OPT_Warray_bounds,
+			  "%K%qD offset %s is out of the bounds of array "
+			  "%qD with type %qT",
+			  call, func, rangestr[0], ref.base, type))
+	    inform (DECL_SOURCE_LOCATION (ref.base),
+		    "array %qD declared here", ref.base);
+	}
+      else
+	warning_at (loc, OPT_Warray_bounds,
+		    "%K%qD offset %s is out of the bounds [0, %wu]",
+		    call, func, rangestr[0], ref.objsize.to_uhwi ());
+    }
+  else if (TREE_CODE (ref.ref) == MEM_REF)
+    {
+      tree type = TREE_TYPE (TREE_OPERAND (ref.ref, 0));
+      if (POINTER_TYPE_P (type))
+	type = TREE_TYPE (type);
+      type = TYPE_MAIN_VARIANT (type);
+
+      warning_at (loc, OPT_Warray_bounds,
+		  "%K%qD offset %s from the object at %qE is out "
+		  "of the bounds of %qT",
+		  call, func, rangestr[0], ref.base, type);
+    }
+  else
+    {
+      type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
+
+      warning_at (loc, OPT_Warray_bounds,
+		"%K%qD offset %s from the object at %qE is out "
+		"of the bounds of referenced subobject %qD with type %qT "
+		"at offset %wu",
+		call, func, rangestr[0], ref.base, TREE_OPERAND (ref.ref, 1),
+		type, ref.refoff.to_uhwi ());
+    }
+
+  return true;
+}
+
 /* Try to verify that the sizes and lengths of the arguments to a string
    manipulation function given by EXP are within valid bounds and that
-   the operation does not lead to buffer overflow.  Arguments other than
-   EXP may be null.  When non-null, the arguments have the following
-   meaning:
-   SIZE is the user-supplied size argument to the function (such as in
-   memcpy(d, s, SIZE) or strncpy(d, s, SIZE).  It specifies the exact
-   number of bytes to write.
-   MAXLEN is the user-supplied bound on the length of the source sequence
+   the operation does not lead to buffer overflow or read past the end.
+   For copy operations that must not overlap also verify that the source
+   and destination do not overlap one another.
+   Arguments other than EXP may be null.  When non-null, the arguments
+   have the following meaning:
+   DST is the destination of a copy call or null otherwise.
+   SRC is the source of a copy call or null otherwise.
+   DSTWRITE is the number of bytes written into the destination obtained
+   from the user-supplied size argument to the function (such as in
+   memcpy(DST, SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+   MAXREAD is the user-supplied bound on the length of the source sequence
    (such as in strncat(d, s, N).  It specifies the upper limit on the number
-   of bytes to write.
-   SRC is the source string (such as in strcpy(d, s)) when the expression
-   EXP is a string function call (as opposed to a memory call like memcpy).
-   As an exception, SRC can also be an integer denoting the precomputed
-   size of the source string or object (for functions like memcpy).
-   OBJSIZE is the size of the destination object specified by the last
+   of bytes to write.  If null it's taken to be the same as DSTWRITE.
+   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+   expression EXP is a string function call (as opposed to a memory call
+   like memcpy).  As an exception, SRCSTR can also be an integer denoting
+   the precomputed size of the source string or object (for functions like
+   memcpy).
+   DSTSIZE is the size of the destination object specified by the last
    argument to the _chk builtins, typically resulting from the expansion
-   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
-   OBJSIZE).
+   of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
+   DSTSIZE).
 
-   When SIZE is null LEN is checked to verify that it doesn't exceed
+   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
-   If the call is successfully verified as safe from buffer overflow
-   the function returns true, otherwise false..  */
+   If the call is successfully verified as safe return true, otherwise
+   return false.  */
 
 static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_access (tree exp, tree dst, tree src, tree dstwrite,
+	      tree maxread, tree srcstr, tree dstsize)
 {
+  int opt = OPT_Wstringop_overflow_;
+
   /* The size of the largest object is half the address space, or
-     SSIZE_MAX.  (This is way too permissive.)  */
-  tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
+     PTRDIFF_MAX.  (This is way too permissive.)  */
+  tree maxobjsize = max_object_size ();
 
+  /* Either the length of the source string for string functions or
+     the size of the source object for raw memory functions.  */
   tree slen = NULL_TREE;
 
   tree range[2] = { NULL_TREE, NULL_TREE };
@@ -3003,28 +3207,28 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
      function like strcpy is not known and the only thing that is
      known is that it must be at least one (for the terminating nul).  */
   bool at_least_one = false;
-  if (src)
+  if (srcstr)
     {
-      /* SRC is normally a pointer to string but as a special case
+      /* SRCSTR is normally a pointer to string but as a special case
 	 it can be an integer denoting the length of a string.  */
-      if (POINTER_TYPE_P (TREE_TYPE (src)))
+      if (POINTER_TYPE_P (TREE_TYPE (srcstr)))
 	{
 	  /* Try to determine the range of lengths the source string
 	     refers to.  If it can be determined and is less than
-	     the upper bound given by MAXLEN add one to it for
+	     the upper bound given by MAXREAD add one to it for
 	     the terminating nul.  Otherwise, set it to one for
-	     the same reason, or to MAXLEN as appropriate.  */
-	  get_range_strlen (src, range);
-	  if (range[0] && (!maxlen || TREE_CODE (maxlen) == INTEGER_CST))
+	     the same reason, or to MAXREAD as appropriate.  */
+	  get_range_strlen (srcstr, range);
+	  if (range[0] && (!maxread || TREE_CODE (maxread) == INTEGER_CST))
 	    {
-	      if (maxlen && tree_int_cst_le (maxlen, range[0]))
-		range[0] = range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[0]))
+		range[0] = range[1] = maxread;
 	      else
 		range[0] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[0], size_one_node);
 
-	      if (maxlen && tree_int_cst_le (maxlen, range[1]))
-		range[1] = maxlen;
+	      if (maxread && tree_int_cst_le (maxread, range[1]))
+		range[1] = maxread;
 	      else if (!integer_all_onesp (range[1]))
 		range[1] = fold_build2 (PLUS_EXPR, size_type_node,
 					range[1], size_one_node);
@@ -3038,10 +3242,10 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	    }
 	}
       else
-	slen = src;
+	slen = srcstr;
     }
 
-  if (!size && !maxlen)
+  if (!dstwrite && !maxread)
     {
       /* When the only available piece of data is the object size
 	 there is nothing to do.  */
@@ -3049,20 +3253,18 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	return true;
 
       /* Otherwise, when the length of the source sequence is known
-	 (as with with strlen), set SIZE to it.  */
+	 (as with strlen), set DSTWRITE to it.  */
       if (!range[0])
-	size = slen;
+	dstwrite = slen;
     }
 
-  if (!objsize)
-    objsize = maxobjsize;
+  if (!dstsize)
+    dstsize = maxobjsize;
 
-  /* The SIZE is exact if it's non-null, constant, and in range of
-     unsigned HOST_WIDE_INT.  */
-  bool exactsize = size && tree_fits_uhwi_p (size);
+  if (dstwrite)
+    get_size_range (dstwrite, range);
 
-  if (size)
-    get_size_range (size, range);
+  tree func = get_callee_fndecl (exp);
 
   /* First check the number of bytes to be written against the maximum
      object size.  */
@@ -3075,30 +3277,34 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	warning_at (loc, opt,
 		    "%K%qD specified size %E "
 		    "exceeds maximum object size %E",
-		    exp, get_callee_fndecl (exp), range[0], maxobjsize);
+		    exp, func, range[0], maxobjsize);
 	  else
 	    warning_at (loc, opt,
 			"%K%qD specified size between %E and %E "
 			"exceeds maximum object size %E",
-			exp, get_callee_fndecl (exp),
+			exp, func,
 			range[0], range[1], maxobjsize);
       return false;
     }
 
+  /* The number of bytes to write is "exact" if DSTWRITE is non-null,
+     constant, and in range of unsigned HOST_WIDE_INT.  */
+  bool exactwrite = dstwrite && tree_fits_uhwi_p (dstwrite);
+
   /* Next check the number of bytes to be written against the destination
      object size.  */
-  if (range[0] || !exactsize || integer_all_onesp (size))
+  if (range[0] || !exactwrite || integer_all_onesp (dstwrite))
     {
       if (range[0]
-	  && ((tree_fits_uhwi_p (objsize)
-	       && tree_int_cst_lt (objsize, range[0]))
-	      || (tree_fits_uhwi_p (size)
-		  && tree_int_cst_lt (size, range[0]))))
+	  && ((tree_fits_uhwi_p (dstsize)
+	       && tree_int_cst_lt (dstsize, range[0]))
+	      || (tree_fits_uhwi_p (dstwrite)
+		  && tree_int_cst_lt (dstwrite, range[0]))))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
 
-	  if (size == slen && at_least_one)
+	  if (dstwrite == slen && at_least_one)
 	    {
 	      /* This is a call to strcpy with a destination of 0 size
 		 and a source of unknown length.  The call will write
@@ -3106,7 +3312,7 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else if (tree_int_cst_equal (range[0], range[1]))
 	    warning_at (loc, opt,
@@ -3115,21 +3321,21 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 			      "of size %E overflows the destination")
 			 : G_("%K%qD writing %E bytes into a region "
 			      "of size %E overflows the destination")),
-			exp, get_callee_fndecl (exp), range[0], objsize);
+			exp, func, range[0], dstsize);
 	  else if (tree_int_cst_sign_bit (range[1]))
 	    {
 	      /* Avoid printing the upper bound if it's invalid.  */
 	      warning_at (loc, opt,
 			  "%K%qD writing %E or more bytes into a region "
 			  "of size %E overflows the destination",
-			  exp, get_callee_fndecl (exp), range[0], objsize);
+			  exp, func, range[0], dstsize);
 	    }
 	  else
 	    warning_at (loc, opt,
 			"%K%qD writing between %E and %E bytes into "
 			"a region of size %E overflows the destination",
-			exp, get_callee_fndecl (exp), range[0],	range[1],
-			objsize);
+			exp, func, range[0], range[1],
+			dstsize);
 
 	  /* Return error when an overflow has been detected.  */
 	  return false;
@@ -3139,11 +3345,15 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
   /* Check the maximum length of the source sequence against the size
      of the destination object if known, or against the maximum size
      of an object.  */
-  if (maxlen)
+  if (maxread)
     {
-      get_size_range (maxlen, range);
+      get_size_range (maxread, range);
 
-      if (range[0] && objsize && tree_fits_uhwi_p (objsize))
+      /* Use the lower end for MAXREAD from now on.  */
+      if (range[0])
+	maxread = range[0];
+
+      if (range[0] && dstsize && tree_fits_uhwi_p (dstsize))
 	{
 	  location_t loc = tree_nonartificial_location (exp);
 	  loc = expansion_point_location_if_in_system_header (loc);
@@ -3157,40 +3367,41 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], maxobjsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds maximum object size %E",
-			    exp, get_callee_fndecl (exp),
+			    exp, func,
 			    range[0], range[1], maxobjsize);
 
 	      return false;
 	    }
 
-	  if (objsize != maxobjsize && tree_int_cst_lt (objsize, range[0]))
+	  if (dstsize != maxobjsize && tree_int_cst_lt (dstsize, range[0]))
 	    {
 	      if (tree_int_cst_equal (range[0], range[1]))
 		warning_at (loc, opt,
 			    "%K%qD specified bound %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], objsize);
+			    exp, func,
+			    range[0], dstsize);
 	      else
 		warning_at (loc, opt,
 			    "%K%qD specified bound between %E and %E "
 			    "exceeds destination size %E",
-			    exp, get_callee_fndecl (exp),
-			    range[0], range[1], objsize);
+			    exp, func,
+			    range[0], range[1], dstsize);
 	      return false;
 	    }
 	}
     }
 
+  /* Check for reading past the end of SRC.  */
   if (slen
-      && slen == src
-      && size && range[0]
+      && slen == srcstr
+      && dstwrite && range[0]
       && tree_int_cst_lt (slen, range[0]))
     {
       location_t loc = tree_nonartificial_location (exp);
@@ -3200,23 +3411,32 @@ check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
 		    (tree_int_cst_equal (range[0], integer_one_node)
 		     ? G_("%K%qD reading %E byte from a region of size %E")
 		     : G_("%K%qD reading %E bytes from a region of size %E")),
-		    exp, get_callee_fndecl (exp), range[0], slen);
+		    exp, func, range[0], slen);
       else if (tree_int_cst_sign_bit (range[1]))
 	{
 	  /* Avoid printing the upper bound if it's invalid.  */
 	  warning_at (loc, opt,
 		      "%K%qD reading %E or more bytes from a region "
 		      "of size %E",
-		      exp, get_callee_fndecl (exp), range[0], slen);
+		      exp, func, range[0], slen);
 	}
       else
 	warning_at (loc, opt,
 		    "%K%qD reading between %E and %E bytes from a region "
 		    "of size %E",
-		    exp, get_callee_fndecl (exp), range[0], range[1], slen);
+		    exp, func, range[0], range[1], slen);
       return false;
     }
 
+  /* Check for out-of-bounds offsets and (except for memmove) overlapping
+     copies.  Avoid warning if one has already been issued (unlike
+     -Wstringop-overflow, -Wrestrict is checked in several places and
+     TREE_NO_WARNING applies to it but not to the former (there should
+     be a bit for every kind of warning)).  */
+  if (dst && src && !TREE_NO_WARNING (exp)
+      && !check_bounds_or_overlap (exp, dst, src, dstwrite, maxread))
+    return false;
+
   return true;
 }
 
@@ -3244,11 +3464,8 @@ compute_objsize (tree dest, int ostype)
    (no overflow or invalid sizes), false otherwise.  */
 
 static bool
-check_memop_sizes (tree exp, tree dest, tree src, tree size)
+check_memop_access (tree exp, tree dest, tree src, tree size)
 {
-  if (!warn_stringop_overflow)
-    return true;
-
   /* For functions like memset and memcpy that operate on raw memory
      try to determine the size of the largest source and destination
      object using type-0 Object Size regardless of the object size
@@ -3256,8 +3473,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size)
   tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
   tree dstsize = compute_objsize (dest, 0);
 
-  return check_sizes (OPT_Wstringop_overflow_, exp,
-		      size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+  return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
+		       srcsize, dstsize);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -3278,9 +3495,8 @@ expand_builtin_memchr (tree exp, rtx)
   if (warn_stringop_overflow)
     {
       tree size = compute_objsize (arg1, 0);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, len, /*maxlen=*/NULL_TREE,
-		   size, /*objsize=*/NULL_TREE);
+      check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		    /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
     }
 
   return NULL_RTX;
@@ -3302,7 +3518,7 @@ expand_builtin_memcpy (tree exp, rtx target)
   tree src = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, src, len);
+  check_memop_access (exp, dest, src, len);
 
   return expand_builtin_memory_copy_args (dest, src, len, target, exp,
 					  /*endp=*/ 0);
@@ -3322,7 +3538,7 @@ expand_builtin_memmove (tree exp, rtx)
   tree src = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, src, len);
+  check_memop_access (exp, dest, src, len);
 
   return NULL_RTX;
 }
@@ -3381,7 +3597,7 @@ expand_builtin_mempcpy (tree exp, rtx target)
   /* Avoid expanding mempcpy into memcpy when the call is determined
      to overflow the buffer.  This also prevents the same overflow
      from being diagnosed again when expanding memcpy.  */
-  if (!check_memop_sizes (exp, dest, src, len))
+  if (!check_memop_access (exp, dest, src, len))
     return NULL_RTX;
 
   return expand_builtin_mempcpy_args (dest, src, len,
@@ -3587,8 +3803,8 @@ expand_builtin_strcat (tree exp, rtx)
 
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+  check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+		destsize);
 
   return NULL_RTX;
 }
@@ -3607,11 +3823,14 @@ expand_builtin_strcpy (tree exp, rtx target)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
 
-  if (warn_stringop_overflow)
+  if (warn_stringop_overflow || warn_restrict || warn_array_bounds)
     {
-      tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+      /* Use Object Size type-1 for -Wrestrict.  */
+      int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 2;
+
+      tree destsize = compute_objsize (dest, ost);
+      check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		    src, destsize);
     }
 
   return expand_builtin_strcpy_args (dest, src, target);
@@ -3646,11 +3865,11 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
   dst = CALL_EXPR_ARG (exp, 0);
   src = CALL_EXPR_ARG (exp, 1);
 
-  if (warn_stringop_overflow)
+  if (warn_stringop_overflow || warn_restrict || warn_array_bounds)
     {
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+      check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		    src, destsize);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -3733,8 +3952,7 @@ expand_builtin_stpncpy (tree exp, rtx)
   /* The size of the destination object.  */
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 
   return NULL_RTX;
 }
@@ -3764,7 +3982,7 @@ check_strncat_sizes (tree exp, tree objsize)
 {
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
 
   /* Try to determine the range of lengths that the source expression
      refers to.  */
@@ -3788,32 +4006,32 @@ check_strncat_sizes (tree exp, tree objsize)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize)
-      && tree_int_cst_equal (objsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return false;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
+  /* The number of bytes to write is LEN but check_access will also
      check SRCLEN if LEN's value isn't known.  */
-  return check_sizes (OPT_Wstringop_overflow_,
-		      exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+  return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+		       objsize);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -3831,7 +4049,7 @@ expand_builtin_strncat (tree exp, rtx)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree src = CALL_EXPR_ARG (exp, 1);
   /* The upper bound on the number of bytes to write.  */
-  tree maxlen = CALL_EXPR_ARG (exp, 2);
+  tree maxread = CALL_EXPR_ARG (exp, 2);
   /* The length of the source sequence.  */
   tree slen = c_strlen (src, 1);
 
@@ -3854,32 +4072,30 @@ expand_builtin_strncat (tree exp, rtx)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize)
-      && tree_int_cst_equal (destsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return NULL_RTX;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
-     check SRCLEN if LEN's value isn't known.  */
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+  /* The number of bytes to write is SRCLEN.  */
+  check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize);
 
   return NULL_RTX;
 }
@@ -3907,10 +4123,10 @@ expand_builtin_strncpy (tree exp, rtx target)
 	  tree destsize = compute_objsize (dest,
 					   warn_stringop_overflow - 1);
 
-	  /* The number of bytes to write is LEN but check_sizes will also
+	  /* The number of bytes to write is LEN but check_access will also
 	     check SLEN if LEN's value isn't known.  */
-	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+	  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
+			destsize);
 	}
 
       /* We must be passed a constant len and src parameter.  */
@@ -4005,7 +4221,7 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
   tree val = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, NULL_TREE, len);
+  check_memop_access (exp, dest, NULL_TREE, len);
 
   return expand_builtin_memset_args (dest, val, len, target, mode, exp);
 }
@@ -4194,7 +4410,7 @@ expand_builtin_bzero (tree exp)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree size = CALL_EXPR_ARG (exp, 1);
 
-  check_memop_sizes (exp, dest, NULL_TREE, size);
+  check_memop_access (exp, dest, NULL_TREE, size);
 
   /* New argument list transforming bzero(ptr x, int y) to
      memset(ptr x, int 0, size_t y).   This is done this way
@@ -4253,14 +4469,12 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
   if (warn_stringop_overflow)
     {
       tree size = compute_objsize (arg1, 0);
-      if (check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE))
+      if (check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+			/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
 	{
 	  size = compute_objsize (arg2, 0);
-	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE);
+	  check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+			/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
 	}
     }
 
@@ -7637,6 +7851,23 @@ expand_builtin_with_bounds (tree exp, rtx target,
 	return target;
       break;
 
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMPCPY_CHKP:
+      if (call_expr_nargs (exp) > 3)
+	{
+	  /* memcpy_chkp (void *dst, size_t dstbnd,
+	                  const void *src, size_t srcbnd, size_t n)
+  	     and others take a pointer bound argument just after each
+	     pointer argument.  */
+	  tree dest = CALL_EXPR_ARG (exp, 0);
+	  tree src = CALL_EXPR_ARG (exp, 2);
+	  tree len = CALL_EXPR_ARG (exp, 4);
+
+	  check_memop_access (exp, dest, src, len);
+	  break;
+	}
+
     default:
       break;
     }
@@ -9653,8 +9884,6 @@ static rtx
 expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			   enum built_in_function fcode)
 {
-  tree dest, src, len, size;
-
   if (!validate_arglist (exp,
 			 POINTER_TYPE,
 			 fcode == BUILT_IN_MEMSET_CHK
@@ -9662,14 +9891,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
-  len = CALL_EXPR_ARG (exp, 2);
-  size = CALL_EXPR_ARG (exp, 3);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 3);
 
-  bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
-			       exp, len, /*maxlen=*/NULL_TREE,
-			       /*str=*/NULL_TREE, size);
+  bool sizes_ok = check_access (exp, dest, src, len, /*maxread=*/NULL_TREE,
+				/*str=*/NULL_TREE, size);
 
   if (!tree_fits_uhwi_p (size))
     return NULL_RTX;
@@ -9778,7 +10006,7 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
   /* The maximum length of the source sequence in a bounded operation
      (such as __strncat_chk) or null if the operation isn't bounded
      (such as __strcat_chk).  */
-  tree maxlen = NULL_TREE;
+  tree maxread = NULL_TREE;
 
   switch (fcode)
     {
@@ -9799,27 +10027,27 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
     case BUILT_IN_STRNCAT_CHK:
       catstr = CALL_EXPR_ARG (exp, 0);
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_STRNCPY_CHK:
     case BUILT_IN_STPNCPY_CHK:
       srcstr = CALL_EXPR_ARG (exp, 1);
-      maxlen = CALL_EXPR_ARG (exp, 2);
+      maxread = CALL_EXPR_ARG (exp, 2);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
 
     case BUILT_IN_SNPRINTF_CHK:
     case BUILT_IN_VSNPRINTF_CHK:
-      maxlen = CALL_EXPR_ARG (exp, 1);
+      maxread = CALL_EXPR_ARG (exp, 1);
       objsize = CALL_EXPR_ARG (exp, 3);
       break;
     default:
       gcc_unreachable ();
     }
 
-  if (catstr && maxlen)
+  if (catstr && maxread)
     {
       /* Check __strncat_chk.  There is no way to determine the length
 	 of the string to which the source string is being appended so
@@ -9828,8 +10056,10 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       return;
     }
 
-  check_sizes (OPT_Wstringop_overflow_, exp,
-	       /*size=*/NULL_TREE, maxlen, srcstr, objsize);
+  /* The destination argument is the first one for all built-ins above.  */
+  tree dst = CALL_EXPR_ARG (exp, 0);
+
+  check_access (exp, dst, srcstr, /*size=*/NULL_TREE, maxread, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9885,8 +10115,9 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 
   /* Add one for the terminating nul.  */
   len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
+
+  check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
+		/*maxread=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
@@ -10516,3 +10747,1287 @@ target_char_cst_p (tree t, char *p)
   *p = (char)tree_to_uhwi (t);
   return true;
 }
+
+/* Initialize a memory reference representation from a pointer PTR and
+   a size SIZE in bytes.  If SIZE is NULL_TREE then the size is assumed
+   to be unknown.  */
+
+builtin_memref::builtin_memref (tree expr, tree size)
+: ptr (expr),
+  ref (),
+  base (),
+  objsize (-1),
+  refoff (HOST_WIDE_INT_MIN),
+  offrange (),
+  sizrange (),
+  strbounded_p ()
+{
+  /* Unfortunately, wide_int default ctor is a no-op so array members
+     of the type must be set individually.  */
+  offrange[0] = offrange[1] = 0;
+  sizrange[0] = sizrange[1] = 0;
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  if (TREE_CODE (expr) == SSA_NAME)
+    {
+      /* Try to tease the offset out of the pointer.  */
+      gimple *stmt = SSA_NAME_DEF_STMT (expr);
+      if (gimple_assign_single_p (stmt)
+	  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+	expr = gimple_assign_rhs1 (stmt);
+      else if (is_gimple_assign (stmt))
+	{
+	  tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == NOP_EXPR)
+	    expr = gimple_assign_rhs1 (stmt);
+	  else if (code == POINTER_PLUS_EXPR)
+	    {
+	      expr = gimple_assign_rhs1 (stmt);
+
+	      tree offset = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (offset) == INTEGER_CST)
+		{
+		  offset_int off = int_cst_value (offset);
+		  offrange[0] = off;
+		  offrange[1] = off;
+
+		  if (TREE_CODE (expr) == SSA_NAME)
+		    {
+		      gimple *stmt = SSA_NAME_DEF_STMT (expr);
+		      if (gimple_assign_single_p (stmt)
+			  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+			expr = gimple_assign_rhs1 (stmt);
+		    }
+		}
+	      else if (TREE_CODE (offset) == SSA_NAME)
+		{
+		  wide_int min, max;
+		  value_range_type rng = get_range_info (offset, &min, &max);
+		  if (rng == VR_RANGE)
+		    {
+		      offrange[0] = min.to_shwi ();
+		      offrange[1] = max.to_shwi ();
+		    }
+		  else if (rng == VR_ANTI_RANGE)
+		    {
+		      offrange[0] = (max + 1).to_shwi ();
+		      offrange[1] = (min - 1).to_shwi ();
+		    }
+		  else
+		    {
+		      gimple *stmt = SSA_NAME_DEF_STMT (offset);
+		      if (is_gimple_assign (stmt)
+			  && gimple_assign_rhs_code (stmt) == NOP_EXPR)
+			{
+			  /* Use the bounds of the type of the NOP_EXPR operand
+			     even if it's signed.  The result doesn't trigger
+			     warnings but makes their output more readable.  */
+			  tree type = TREE_TYPE (gimple_assign_rhs1 (stmt));
+			  offrange[0] = wi::to_offset (TYPE_MIN_VALUE (type));
+			  offrange[1] = wi::to_offset (TYPE_MAX_VALUE (type));
+			}
+		      else
+			offrange[1] = maxobjsize;
+		    }
+		}
+	      else
+		offrange[1] = maxobjsize;
+	    }
+	}
+    }
+
+  if (TREE_CODE (expr) == ADDR_EXPR)
+    {
+      HOST_WIDE_INT off;
+      tree oper = TREE_OPERAND (expr, 0);
+
+      /* Determine the base object or pointer of the reference
+	 and its constant offset from the beginning of the base.  */
+      base = get_addr_base_and_unit_offset (oper, &off);
+
+      if (base)
+	{
+	  offrange[0] += off;
+	  offrange[1] += off;
+
+	  /* Stash the reference for offset validation. */
+	  ref = oper;
+
+	  /* Also stash the constant offset for offset validation.  */
+	  tree_code code = TREE_CODE (oper);
+	  if (code == ARRAY_REF || code == COMPONENT_REF)
+	    {
+	      if (code == COMPONENT_REF)
+		{
+		  tree field = TREE_OPERAND (ref, 1);
+		  refoff = (wi::to_offset (DECL_FIELD_OFFSET (field))
+			    + (wi::to_offset (DECL_FIELD_BIT_OFFSET (field))
+			       >> LOG2_BITS_PER_UNIT));
+		}
+	    }
+	  else if (code == MEM_REF)
+	    {
+	      oper = TREE_OPERAND (ref, 0);
+	      tree_code code = TREE_CODE (oper);
+	      if (code == ADDR_EXPR || code == SSA_NAME)
+		{
+		  tree type = TREE_TYPE (oper);
+		  if (POINTER_TYPE_P (type))
+		    {
+		      /* Take the size of the referenced type as
+			 the referenced object's size.  Set its offset
+			 to HWI_MAX here as a flag so it can be reset
+			 to zero at the end of this function.  */
+		      refoff = HOST_WIDE_INT_MAX;
+		    }
+		}
+	    }
+	}
+      else
+	{
+	  size = NULL_TREE;
+	  base = get_base_address (TREE_OPERAND (expr, 0));
+	}
+    }
+
+  if (!base)
+    base = build2 (MEM_REF, char_type_node, expr, null_pointer_node);
+
+  if (TREE_CODE (base) == MEM_REF)
+    {
+      offset_int off = mem_ref_offset (base);
+      refoff += off;
+      offrange[0] += off;
+      offrange[1] += off;
+      base = TREE_OPERAND (base, 0);
+    }
+
+  if (TREE_CODE (base) == SSA_NAME)
+    if (gimple *stmt = SSA_NAME_DEF_STMT (base))
+      {
+	enum gimple_code code = gimple_code (stmt);
+	if (code == GIMPLE_ASSIGN)
+	  if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+	    {
+	      base = gimple_assign_rhs1 (stmt);
+
+	      tree offset = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (offset) == INTEGER_CST)
+		{
+		  offset_int off = int_cst_value (offset);
+		  refoff += off;
+		  offrange[0] += off;
+		  offrange[1] += off;
+		}
+	    }
+
+	if (TREE_CODE (base) == SSA_NAME && SSA_NAME_VAR (base))
+	  base = SSA_NAME_VAR (base);
+      }
+
+  if (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);
+      sizrange[0] = wi::to_offset (range[0]);
+      sizrange[1] = wi::to_offset (range[1]);
+      /* get_size_range returns SIZE_MAX for the maximum size.
+	 Constrain it to the real maximum of PTRDIFF_MAX.  */
+      if (sizrange[1] > maxobjsize)
+	sizrange[1] = maxobjsize;
+    }
+  else
+    sizrange[1] = maxobjsize;
+
+  /* Reset the reference offset to zero (see above).  */
+  if (refoff >= HOST_WIDE_INT_MAX)
+    refoff = 0;
+}
+
+/* Return error_mark_node if the signed offset is exceeds the bounds
+   of the address space (PTRDIFF_MAX).  Otherwise, return BASE when
+   the offset exceeds the bounds of the BASE object. Otherwise return
+   NULL to inidctae the offset is in bounds.  */
+
+tree
+builtin_memref::offset_out_of_bounds () const
+{
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  /* A temporary, possibly adjusted, copy of the offset range.  */
+  offset_int offrng[2] = { offrange[0], offrange[1] };
+
+  if (DECL_P (base) && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE)
+    {
+      if (offrng[1] < offrng[0])
+  	offrng[1] = maxobjsize;
+    }
+
+  /* Conservative offset of the last byte of the referenced object.  */
+  offset_int endoff;
+
+  /* The bounds need not be ordered.  Set HIB to use as the index
+     of the larger of the bounds and LOB  as the opposite.  */
+  bool hib = wi::les_p (offrng[0], offrng[1]);
+  bool lob = !hib;
+
+  if (objsize < 0)
+    {
+      endoff = offrng[lob] + sizrange[0];
+
+      /* For a reference through a pointer to an object of unknown size
+	 all initial offsets are considered valid, positive as well as
+	 negative, since the pointer itself can point past the beginning
+	 of the object.  However, the sum of the lower bound of the offset
+	 and that of the size must be less than or equal than PTRDIFF_MAX.  */
+      if (endoff > maxobjsize)
+	return error_mark_node;
+
+      return NULL_TREE;
+    }
+
+  /* A reference to an object of known size must be within the bounds
+     of the base object.  */
+  if (offrng[hib] < 0 || offrng[lob] > objsize)
+    return base;
+
+  /* The extent of the reference must also be within the bounds of
+     the base object (if known) or the maximum object size otherwise.  */
+  endoff = wi::smax (offrng[lob], 0) + sizrange[0];
+  if (endoff > maxobjsize)
+    return error_mark_node;
+
+  if (endoff > objsize)
+    return base;
+
+  return NULL_TREE;
+}
+
+/* Create an association between DSTREF and SRCREF for access by
+   a call EXP to a memory or string built-in funtion.  */
+
+builtin_access::builtin_access (tree exp, builtin_memref &dst,
+				builtin_memref &src)
+: dstoff (), srcoff (), dstsiz (), srcsiz (),
+  dstref (&dst), srcref (&src), sizrange (), ovloff (), ovlsiz ()
+{
+  dstoff[0] = dstoff[1] = 0;
+  srcoff[0] = srcoff[1] = 0;
+  dstsiz[0] = dstsiz[1] = 0;
+  srcsiz[0] = srcsiz[1] = 0;
+
+  /* Object Size Type to use to determine the size of the destination
+     and source objects.  Overridden below for raw memory functions.  */
+  int ostype = 1;
+
+  /* True when the size of one reference depends on the offset of
+     itself or the other.  */
+  bool depends_p;
+
+  /* True when the size of the destination reference (THIS) has been
+     determined from SRCREF and so needs to be adjusted by the latter's
+     offset.  Only meaningful for bounded string functions like strncpy.  */
+  bool dstadjust_p = false;
+
+  /* The size argument number (depends on the built-in).  */
+  unsigned sizeargno = 2;
+  if (CALL_WITH_BOUNDS_P (exp))
+    sizeargno += 2;
+
+  tree func = get_callee_fndecl (exp);
+  switch (DECL_FUNCTION_CODE (func))
+    {
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMCPY_CHK_CHKP:
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHKP:
+    case BUILT_IN_MEMPCPY_CHK_CHKP:
+      /* Both the destination and the source accesses are considered
+	 bounded.  */
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::generic_overlap;
+      break;
+
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMMOVE_CHK_CHKP:
+      /* Like for memmcpy both the destination and the source accesses
+	 are considered bounded.  There is never any overlap.  */
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::no_overlap;
+      break;
+
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      /* Only the destination access is considered bounded in the same
+	 was as for memcpy.  */
+      depends_p = true;
+      dstref->strbounded_p = true;
+      detect_overlap = &builtin_access::generic_overlap;
+      break;
+
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STPCPY_CHKP:
+    case BUILT_IN_STPCPY_CHK_CHKP:
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STRCPY_CHKP:
+    case BUILT_IN_STRCPY_CHK_CHKP:
+    depends_p = true;
+      detect_overlap = &builtin_access::strcpy_overlap;
+      break;
+
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRCAT_CHKP:
+    case BUILT_IN_STRCAT_CHK_CHKP:
+      depends_p = true;
+      detect_overlap = &builtin_access::strcat_overlap;
+      break;
+
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+      /* Both the destination and the source accesses are considered
+	 bounded, although in a different way than for memcpy and
+	 strncpy.  */
+      depends_p = true;
+      dstref->strbounded_p = true;
+      srcref->strbounded_p = true;
+      detect_overlap = &builtin_access::strcat_overlap;
+      break;
+
+    default:
+      /* Handle other string functions here whose access may need
+	 to be validated for in-bounds offsets and non-overlapping
+	 copies.  (Not all _chkp functions have BUILT_IN_XXX_CHKP
+	 macros so they need to be handled here.)  */
+      return;
+    }
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  unsigned HOST_WIDE_INT wisize;
+  if (compute_builtin_object_size (dst.base, ostype, &wisize))
+    dst.objsize = wisize;
+  else if (VAR_P (dst.base) && TREE_CODE (TREE_TYPE (dst.base)) == ARRAY_TYPE)
+    {
+      if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (dst.base)))
+	dst.objsize = wi::to_offset (size);
+      else
+	dst.objsize = maxobjsize;
+    }
+  else
+    dst.objsize = HOST_WIDE_INT_MIN;
+
+  if (compute_builtin_object_size (src.base, ostype, &wisize))
+    src.objsize = wisize;
+  else if (VAR_P (src.base) && TREE_CODE (TREE_TYPE (src.base)) == ARRAY_TYPE)
+    {
+      if (tree size = TYPE_SIZE_UNIT (TREE_TYPE (src.base)))
+	src.objsize = wi::to_offset (size);
+      else
+	src.objsize = maxobjsize;
+    }
+  else
+    src.objsize = HOST_WIDE_INT_MIN;
+
+  /* If there is no dependency between the references or the base
+     objects of the two references aren't the same there's nothing
+     else to do.  */
+  if (depends_p && dstref->base != srcref->base)
+    return;
+
+  /* ...otherwise, make adjustments for references to the same object
+     by string functions to reflect the constraints imposed by the called
+     built-in function.  */
+
+  /* For bounded string functions determine the range of the bound
+     on the access.  */
+  offset_int bounds[2] = { 0, 0 };
+  if (dstref->strbounded_p)
+    {
+      tree size = CALL_EXPR_ARG (exp, sizeargno);
+      tree range[2];
+      if (get_size_range (size, range, true))
+	{
+	  bounds[0] = wi::to_offset (range[0]);
+	  bounds[1] = wi::to_offset (range[1]);
+	}
+
+      /* If both references' size ranges are indeterminate use the last
+	 (size) argument from the function call as a substitute.  This
+	 may only be necessary for strncpy (but not for memcpy where
+	 the size range would have been already determined this way).  */
+      if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize
+	  && srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+	{
+	  dstref->sizrange[0] = bounds[0];
+	  dstref->sizrange[1] = bounds[1];
+	}
+    }
+
+  /* The size range of a references involving the same base object
+     can be determined from the size range of the other reference.
+     This makes it possible to compute accurate offsets for warnings
+     involving functions like strcpy where the length of just one of
+     the two arguments is known (determined by tree-ssa-strlen).  */
+  if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize)
+    {
+      /* When the destination size is unknown set it to the size of
+	 the source.  */
+      dstref->sizrange[0] = srcref->sizrange[0];
+      dstref->sizrange[1] = srcref->sizrange[1];
+    }
+  else if (srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+    {
+      /* When the source size is unknown set it to the size of
+	 the destination.  */
+      srcref->sizrange[0] = dstref->sizrange[0];
+      srcref->sizrange[1] = dstref->sizrange[1];
+
+      if (depends_p)
+	{
+	  if (dstref->strbounded_p)
+	    {
+	      /* Strncpy read access is bounded.  */
+	      if (bounds[0] < srcref->sizrange[0])
+		srcref->sizrange[0] = bounds[0];
+	      if (bounds[1] < srcref->sizrange[1])
+		srcref->sizrange[1] = bounds[1];
+	    }
+
+	  /* For string functions, adjust the size range of the source
+	     reference by the inverse boundaries of the offset (because
+	     the higher  the offset into the string the shorter its
+	     length).  */
+	  if (srcref->offrange[1] < srcref->sizrange[0])
+	    srcref->sizrange[0] -= srcref->offrange[1];
+	  else
+	    srcref->sizrange[0] = 0;
+
+	  if (srcref->offrange[0] > 0)
+	    {
+	      if (srcref->offrange[0] < srcref->sizrange[1])
+		srcref->sizrange[1] -= srcref->offrange[0];
+	      else
+		srcref->sizrange[1] = 0;
+	    }
+
+	  dstadjust_p = true;
+	}
+    }
+
+  if (detect_overlap == &builtin_access::generic_overlap)
+    {
+      if (dstref->strbounded_p)
+	{
+	  dstref->sizrange[0] = bounds[0];
+	  dstref->sizrange[1] = bounds[1];
+
+	  if (dstref->sizrange[0] < srcref->sizrange[0])
+	    srcref->sizrange[0] = dstref->sizrange[0];
+
+	  if (dstref->sizrange[1] < srcref->sizrange[1])
+	    srcref->sizrange[1] = dstref->sizrange[1];
+	}
+    }
+  else if (detect_overlap == &builtin_access::strcpy_overlap)
+    {
+      /* For strcpy, adjust the destination size range to match that
+	 of the source computed above.  */
+      if (depends_p && dstadjust_p)
+	{
+	  dstref->sizrange[0] = srcref->sizrange[0];
+	  dstref->sizrange[1] = srcref->sizrange[1];
+	}
+    }
+}
+
+/* Return true if the bounded mempry (memcpy amd similar) or string function
+   access (strncpy and similar) ACS overlaps.  */
+
+bool
+builtin_access::generic_overlap ()
+{
+  builtin_access &acs = *this;
+  const builtin_memref *dstref = acs.dstref;
+  const builtin_memref *srcref = acs.srcref;
+
+  gcc_assert (dstref->base == srcref->base);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  offset_int maxsize = dstref->objsize < 0 ? maxobjsize : dstref->objsize;
+  gcc_assert (maxsize <= maxobjsize);
+
+  /* Adjust the larger bounds of the offsets (which may be the first
+     element if the lower bound is larger than the upper bound) to
+     make them valid for the smallest access (if possible) but no smaller
+     than the smaller bounds.  */
+  bool dstord = wi::les_p (acs.dstoff[0], acs.dstoff[1]);
+  if (maxsize < acs.dstoff[dstord] + acs.dstsiz[0])
+    acs.dstoff[dstord] = maxsize - acs.dstsiz[0];
+  if (acs.dstoff[dstord] < acs.dstoff[0])
+    acs.dstoff[dstord] = acs.dstoff[0];
+
+  bool srcord = wi::les_p (acs.srcoff[0], acs.srcoff[1]);
+  if (maxsize < acs.srcoff[srcord] + acs.srcsiz[0])
+    acs.srcoff[srcord] = maxsize - acs.srcsiz[0];
+  if (acs.srcoff[srcord] < acs.srcoff[0])
+    acs.srcoff[srcord] = acs.srcoff[0];
+
+  /* Determine the minimum and maximum space for the access given
+     the offsets.  */
+  offset_int space[2];
+  space[0] = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+  space[1] = space[0];
+
+  offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+  if (acs.srcsiz[0] > 0)
+    {
+      if (d < space[0])
+	space[0] = d;
+
+      if (space[1] < d)
+	space[1] = d;
+    }
+  else
+    space[1] = acs.dstsiz[1];
+
+  d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+  if (d < space[0])
+    space[0] = d;
+
+  if (space[1] < d)
+    space[1] = d;
+
+  /* Treat raw memory functions both of whose references are bounded
+     as special and permit uncertain overlaps to go undetected.  For
+     all kinds of constant offset and constant size accesses, if
+     overlap isn't certain it is not possible.  */
+  bool overlap_possible = space[0] < acs.dstsiz[1];
+  if (!overlap_possible)
+    return false;
+
+  bool overlap_certain = space[1] < acs.dstsiz[0];
+
+  if (!overlap_certain
+      && !dstref->strbounded_p
+      && detect_overlap == &builtin_access::generic_overlap)
+    return false;
+
+  /* True for stpcpy and strcpy.  */
+  bool stxcpy_p = detect_overlap == &builtin_access::strcpy_overlap;
+
+  /* True when the size of the source sequence is inveresely proportional
+     to the offset into it, such as in strcpy (DST, SRC + OFF).  */
+  bool sizdep_p = (stxcpy_p
+		   && acs.srcoff[0] != acs.srcoff[1]
+		   && acs.sizrange[0] != acs.sizrange[1]);
+
+  if (dstref->refoff >= 0
+      && srcref->refoff >= 0
+      && dstref->refoff != srcref->refoff
+      && (stxcpy_p || dstref->strbounded_p || srcref->strbounded_p))
+    return false;
+
+  /* The number of bytes written.  */
+  offset_int write_min = acs.dstsiz[0];
+  offset_int write_max = acs.dstsiz[1];
+
+  if (acs.dstoff[0] <= acs.srcoff[0])
+    {
+      /* The lower bound of the offset of the first reference is less
+	 than the lower bound of the second.  */
+      if (acs.dstoff[1] <= acs.srcoff[1])
+	{
+	  /* The upper bound of the offset of the first reference is less
+	     than the upper bound of the second.
+	     The size of the overlap is the size of the access minus
+	     the difference between the upper bound of the second offset
+	     and the lower bound of the first offset.  */
+	  if (overlap_certain)
+	    acs.ovlsiz[0]
+	      = (write_min - (acs.srcoff[1] - acs.dstoff[0])).to_shwi ();
+	  acs.ovlsiz[1]
+	    = (write_max - (acs.srcoff[0] - acs.dstoff[0])).to_shwi ();
+
+	  if (acs.ovlsiz[1] > acs.srcsiz[1])
+	    acs.ovlsiz[1] = acs.srcsiz[1].to_shwi ();
+	}
+      else
+	{
+	  /* The offset of the overlap is the lower bound of the first
+	     offset plus the initial non-overlapping size of the access.  */
+	  if (overlap_certain)
+	    acs.ovlsiz[0]
+	      = (write_min - (acs.dstoff[1] - acs.srcoff[0])).to_shwi ();
+
+	  acs.ovlsiz[1]
+	    = (write_max - (acs.dstoff[0] - acs.srcoff[0])).to_shwi ();
+	}
+
+      if (acs.ovlsiz[1])
+	acs.ovloff[1] = (acs.dstoff[0] + write_max).to_shwi () - acs.ovlsiz[1];
+      else
+	acs.ovloff[1] = acs.srcoff[0].to_shwi ();
+
+      if (acs.ovloff[1] >= acs.srcoff[0] + acs.srcsiz[0])
+	acs.ovloff[1] = (acs.srcoff[0] + acs.srcsiz[0]).to_shwi ();
+
+      if (acs.ovlsiz[0] < 0)
+	{
+	  acs.ovlsiz[0] = 0;
+	  acs.ovloff[0] = acs.ovloff[1];
+	}
+      else if (acs.ovlsiz[0] > 0)
+	acs.ovloff[0] = (acs.dstoff[0] + write_min).to_shwi () - acs.ovlsiz[0];
+      else if (acs.ovloff[1] && acs.ovlsiz[1] > 1)
+	{
+	  /* When the minimum overlap is zero then for strcpy the upper
+	     bound of the overlap offset (which is actually the smaller
+	     of the two offset bounds to correspond to the lager size
+	     of the overlap) is two less than the upper bound of the end
+	     offset of the overlap.  For other functions it's just one
+	     less.  */
+	  acs.ovloff[0] = (acs.ovloff[1] + acs.ovlsiz[1] - 1 - stxcpy_p);
+	}
+      else
+	acs.ovloff[0] = acs.ovloff[1];
+    }
+  else
+    {
+      /* The lower bound of the offset of the first reference is
+	 the same or greater that the lower bound of the second.
+	 The size of the overlap is the size of the access minus
+	 the difference between the upper bound of the first offset
+	 and the lower bound of the first offset.  */
+      acs.ovlsiz[0] = (write_min - (acs.dstoff[1] - acs.srcoff[0])).to_shwi ();
+      acs.ovlsiz[1] = (write_max - (acs.dstoff[0] - acs.srcoff[0])).to_shwi ();
+
+      if (acs.ovlsiz[1])
+	acs.ovloff[1] = (acs.srcoff[1] + write_max).to_shwi () - acs.ovlsiz[1];
+      else
+	acs.ovloff[1] = acs.dstoff[1].to_shwi ();
+
+      if (acs.ovlsiz[0] < 0)
+	{
+	  acs.ovlsiz[0] = 0;
+	  acs.ovloff[0] = acs.ovloff[1];
+	}
+      else if (acs.ovlsiz[0] > 0)
+	/* The offset of the overlap is the upper bound of the first
+	   offset plus the initial non-overlapping size of the access.  */
+	acs.ovloff[0] = (acs.srcoff[0] + write_min).to_shwi () - acs.ovlsiz[0];
+      else
+	acs.ovloff[0] = (acs.ovloff[1] + acs.ovlsiz[1] - 1 - sizdep_p);
+    }
+
+  return true;
+}
+
+/* Return true if the strcat-like access ACS overlaps.  */
+
+bool
+builtin_access::strcat_overlap ()
+{
+  builtin_access &acs = *this;
+  const builtin_memref *dstref = acs.dstref;
+  const builtin_memref *srcref = acs.srcref;
+
+  gcc_assert (dstref->base == srcref->base);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  gcc_assert (dstref->base && dstref->base == srcref->base);
+
+  /* Adjust for strcat-like accesses.  */
+
+  /* As a special case for strcat, set the DSTREF offsets to the length
+     of the source string since the function starts writing at the first
+     nul, and set the size to 1 for the length of the nul.  */
+  acs.dstoff[0] += acs.dstsiz[0];
+  acs.dstoff[1] += acs.dstsiz[1];
+
+  bool strfunc_unknown_args = acs.dstsiz[0] == 0 && acs.dstsiz[1] != 0;
+
+  /* The lower bound is zero when the size is unknown because then
+     overlap is not certain. */
+  acs.dstsiz[0] = strfunc_unknown_args ? 0 : 1;
+  acs.dstsiz[1] = 1;
+
+  offset_int maxsize = dstref->objsize < 0 ? maxobjsize : dstref->objsize;
+  gcc_assert (maxsize <= maxobjsize);
+
+  /* For references to the same base object, determine if there's a pair
+     of valid offsets into the two references such that access between
+     them doesn't overlap.  Adjust both upper bounds to be valid for
+     the smaller size (i.e., at most MAXSIZE - SIZE).  */
+
+  if (maxsize < acs.dstoff[1] + acs.dstsiz[0])
+    acs.dstoff[1] = maxsize - acs.dstsiz[0];
+
+  if (maxsize < acs.srcoff[1] + acs.srcsiz[0])
+    acs.srcoff[1] = maxsize - acs.srcsiz[0];
+
+  /* Check to see if there's enough space for both accesses without
+     overlap.  Determine the optimistic (maximum) amount of available
+     space.  */
+  offset_int space;
+  if (acs.dstoff[0] <= acs.srcoff[0])
+    {
+      if (acs.dstoff[1] < acs.srcoff[1])
+	space = acs.srcoff[1] + acs.srcsiz[0] - acs.dstoff[0];
+      else
+	space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+    }
+  else
+    space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+
+  /* Overlap is certain if the distance between the farthest offsets
+     of the opposite accesses is less than the sum of the lower bounds
+     of the sizes of the two accesses.  */
+  bool overlap_certain = space < acs.dstsiz[0] + acs.srcsiz[0];
+
+  /* For a constant-offset, constant size access, consider the largest
+     distance between the offset bounds and the lower bound of the access
+     size.  If the overlap isn't certain return success.  */
+  if (!overlap_certain
+      && acs.dstoff[0] == acs.dstoff[1]
+      && acs.srcoff[0] == acs.srcoff[1]
+      && acs.dstsiz[0] == acs.dstsiz[1]
+      && acs.srcsiz[0] == acs.srcsiz[1])
+    return false;
+
+  /* Overlap is not certain but may be possible.  */
+
+  offset_int access_min = acs.dstsiz[0] + acs.srcsiz[0];
+
+  /* Determine the conservative (minimum) amount of space.  */
+  space = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+  offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+  if (d < space)
+    space = d;
+  d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+  if (d < space)
+    space = d;
+
+  /* For a strict test (used for strcpy and similar with unknown or
+     variable bounds or sizes), consider the smallest distance between
+     the offset bounds and either the upper bound of the access size
+     if known, or the lower bound otherwise.  */
+  if (access_min <= space && (access_min != 0 || !strfunc_unknown_args))
+    return false;
+
+  /* When strcat overlap is certain it is always a single byte:
+     the terminatinn NUL, regardless of offsets and sizes.  When
+     overlap is only possible its range is [0, 1].  */
+  acs.ovlsiz[0] = dstref->sizrange[0] == dstref->sizrange[1] ? 1 : 0;
+  acs.ovlsiz[1] = 1;
+  acs.ovloff[0] = (dstref->sizrange[0] + dstref->offrange[0]).to_shwi ();
+  acs.ovloff[1] = (dstref->sizrange[1] + dstref->offrange[1]).to_shwi ();
+
+  acs.sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+  acs.sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+  return true;
+}
+
+/* Return true if the strcpy-like access ACS overlaps.  */
+
+bool
+builtin_access::strcpy_overlap ()
+{
+  return generic_overlap ();
+}
+
+
+/* Return true if THIS (AKA DSTREF) and SRCREF describe accesses that
+   either overlap one another or that, in order not to overlap, would
+   imply that the size of the referenced object(s) exceeds the maximum
+   size of an object.  Set SIZRANGE to the range of access sizes,
+   OVLOFF to the offsets where the overlap occurs (the offsets must
+   be valid so that their sum with the low bound of the access size
+   is no greater than PTRDIFF_MAX), and OVLSIZ to the rane of the overlap
+   sizes.
+   Otherwise, if THIS and SRCREF do not definitely overlap (even though
+   they may overlap in a way that's not apparent from the available data),
+   return false.
+   Used for -Wrestrict warnings.  */
+
+bool
+builtin_access::overlap ()
+{
+  builtin_access &acs = *this;
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  acs.sizrange[0] = wi::smax (dstref->sizrange[0],
+			      srcref->sizrange[0]).to_shwi ();
+  acs.sizrange[1] = wi::smax (dstref->sizrange[1],
+			      srcref->sizrange[1]).to_shwi ();
+
+  /* Check to see if the two references refer to regions that are
+     too large not to overlap in the address space (whose maximum
+     size is PTRDIFF_MAX).  */
+  offset_int size = dstref->sizrange[0] + srcref->sizrange[0];
+  if (maxobjsize < size)
+    {
+      acs.ovloff[0] = (maxobjsize - dstref->sizrange[0]).to_shwi ();
+      acs.ovlsiz[0] = (size - maxobjsize).to_shwi ();
+      return true;
+    }
+
+  /* If both base objects aren't known return the maximum possible
+     offset that would make them not overlap.  */
+  if (!dstref->base || !srcref->base)
+    return false;
+
+  /* If the base object is an array adjust the lower bound of the offset
+     to be non-negative.  */
+  if (dstref->base
+      && TREE_CODE (TREE_TYPE (dstref->base)) == ARRAY_TYPE)
+    acs.dstoff[0] = wi::smax (dstref->offrange[0], 0);
+  else
+    acs.dstoff[0] = dstref->offrange[0];
+
+  acs.dstoff[1] = dstref->offrange[1];
+
+  if (srcref->base
+      && TREE_CODE (TREE_TYPE (srcref->base)) == ARRAY_TYPE)
+    acs.srcoff[0] = wi::smax (srcref->offrange[0], 0);
+  else
+    acs.srcoff[0] = srcref->offrange[0];
+
+  acs.srcoff[1] = srcref->offrange[1];
+
+  /* When the lower bound of the offset is less that the upper bound
+     disregard	it and use the inverse of the maximum object size
+     instead.  The upper bound is the result of a negative offset
+     being represented as a large positive value.  */
+  if (acs.dstoff[1] < acs.dstoff[0])
+    acs.dstoff[0] = -maxobjsize;
+
+  /* Validate the offset and size of each reference on its own first.
+     This is independent of whether or not the base objects are the
+     same.  Normally, this would have already been detected and
+     diagnosed by -Warray-bounds, unless it has been disabled.  */
+  offset_int maxoff = acs.dstoff[0] + dstref->sizrange[0];
+  if (maxobjsize < maxoff)
+    {
+      acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+      acs.ovloff[0] = acs.dstoff[0].to_shwi () - acs.ovlsiz[0];
+      return true;
+    }
+
+  /* Repeat the same as above but for the source offsets.  */
+  if (acs.srcoff[1] < acs.srcoff[0])
+    acs.srcoff[0] = -maxobjsize;
+
+  maxoff = acs.srcoff[0] + srcref->sizrange[0];
+  if (maxobjsize < maxoff)
+    {
+      acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+      acs.ovlsiz[1] = (acs.srcoff[0] + srcref->sizrange[1]
+		       - maxobjsize).to_shwi ();
+      acs.ovloff[0] = acs.srcoff[0].to_shwi () - acs.ovlsiz[0];
+      return true;
+    }
+
+  if (dstref->base != srcref->base)
+    return false;
+
+  acs.dstsiz[0] = dstref->sizrange[0];
+  acs.dstsiz[1] = dstref->sizrange[1];
+
+  acs.srcsiz[0] = srcref->sizrange[0];
+  acs.srcsiz[1] = srcref->sizrange[1];
+
+  /* Call the appropriate function to determine the overlap.  */
+  if ((this->*detect_overlap) ())
+    {
+      sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+      sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+      return true;
+    }
+
+  return false;
+}
+
+/* Attempt to detect and diagnose an overlapping copy in a call expression
+   EXPR involving an an access ACS to a built-in memory or string function.
+   Return true when one has been detected, false otherwise.  */
+
+static bool
+maybe_diag_overlap (location_t loc, tree exp, builtin_access &acs)
+{
+  const builtin_memref &dstref = *acs.dstref;
+  const builtin_memref &srcref = *acs.srcref;
+
+  /* Determine the range of offsets and sizes of the overlap if it
+     exists and issue diagnostics.  */
+  HOST_WIDE_INT *ovloff = acs.ovloff;
+  HOST_WIDE_INT *ovlsiz = acs.ovlsiz;
+  HOST_WIDE_INT *sizrange = acs.sizrange;
+
+  tree func = get_callee_fndecl (exp);
+
+  if (!acs.overlap ())
+    return false;
+
+  /* To avoid a combinatorial explosion of diagnostics format the offsets
+     or their ranges as strings and use them in the warning calls below.  */
+  char offstr[3][64];
+
+  if (dstref.offrange[0] == dstref.offrange[1]
+      || dstref.offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr[0], "%lli", (long long) dstref.offrange[0].to_shwi ());
+  else
+    sprintf (offstr[0], "[%lli, %lli]",
+             (long long) dstref.offrange[0].to_shwi (),
+             (long long) dstref.offrange[1].to_shwi ());
+
+  if (srcref.offrange[0] == srcref.offrange[1]
+      || srcref.offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr[1], "%lli", (long long) srcref.offrange[0].to_shwi ());
+  else
+    sprintf (offstr[1], "[%lli, %lli]",
+	     (long long) srcref.offrange[0].to_shwi (),
+             (long long) srcref.offrange[1].to_shwi ());
+
+  if (ovloff[0] == ovloff[1] || !ovloff[1])
+    sprintf (offstr[2], "%lli", (long long) ovloff[0]);
+  else
+    sprintf (offstr[2], "[%lli, %lli]",
+	     (long long) ovloff[0], (long long) ovloff[1]);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  bool must_overlap = ovlsiz[0] > 0;
+
+  if (ovlsiz[1] == 0)
+    ovlsiz[1] = ovlsiz[0];
+
+  if (must_overlap)
+    {
+      /* Issue definitive "overlaps" diagnostic in this block.  */
+
+      if (sizrange[0] == sizrange[1])
+	{
+	  if (ovlsiz[0] == ovlsiz[1])
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? (ovlsiz[0] == 1
+			   ? G_("%K%qD accessing %wu byte at offsets %s "
+				"and %s overlaps %wu byte at offset %s")
+			   :  G_("%K%qD accessing %wu byte at offsets %s "
+				 "and %s overlaps %wu bytes at offset "
+				 "%s"))
+			: (ovlsiz[0] == 1
+			   ? G_("%K%qD accessing %wu bytes at offsets %s "
+				"and %s overlaps %wu byte at offset %s")
+			   : G_("%K%qD accessing %wu bytes at offsets %s "
+				"and %s overlaps %wu bytes at offset "
+				"%s")),
+			exp, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? G_("%K%qD accessing %wu byte at offsets %s "
+			     "and %s overlaps between %wu and %wu bytes "
+			     "at offset %s")
+			: G_("%K%qD accessing %wu bytes at offsets %s "
+			     "and %s overlaps between %wu and %wu bytes "
+			     "at offset %s"),
+			exp, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+			offstr[2]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? G_("%K%qD accessing %wu byte at offsets %s and "
+			     "%s overlaps %wu or more bytes at offset %s")
+			: G_("%K%qD accessing %wu bytes at offsets %s and "
+			     "%s overlaps %wu or more bytes at offset %s"),
+			exp, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  return true;
+	}
+
+      if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+	{
+	  if (ovlsiz[0] == ovlsiz[1])
+	    warning_at (loc, OPT_Wrestrict,
+			ovlsiz[0] == 1
+			? G_("%K%qD accessing between %wu and %wu bytes "
+			     "at offsets %s and %s overlaps %wu byte at "
+			     "offset %s")
+			: G_("%K%qD accessing between %wu and %wu bytes "
+			     "at offsets %s and %s overlaps %wu bytes "
+			     "at offset %s"),
+			exp, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD accessing between %wu and %wu bytes at "
+			"offsets %s and %s overlaps between %wu and %wu "
+			"bytes at offset %s",
+			exp, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+			offstr[2]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD accessing between %wu and %wu bytes at "
+			"offsets %s and %s overlaps %wu or more bytes "
+			"at offset %s",
+			exp, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  return true;
+	}
+
+      if (ovlsiz[0] != ovlsiz[1])
+	ovlsiz[1] = maxobjsize.to_shwi ();
+
+      if (ovlsiz[0] == ovlsiz[1])
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[0] == 1
+		    ? G_("%K%qD accessing %wu or more bytes at offsets "
+			 "%s and %s overlaps %wu byte at offset %s")
+		    :  G_("%K%qD accessing %wu or more bytes at offsets "
+			  "%s and %s overlaps %wu bytes at offset %s"),
+		    exp, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], offstr[2]);
+      else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD accessing %wu or more bytes at offsets %s "
+		    "and %s overlaps between %wu and %wu bytes "
+		    "at offset %s",
+		    exp, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], ovlsiz[1], offstr[2]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD accessing %wu or more bytes at offsets %s "
+		    "and %s overlaps %wu or more bytes at offset %s",
+		    exp, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], offstr[2]);
+      return true;
+    }
+
+  /* Issue "may overlap" diagnostics below.  */
+  gcc_assert (ovlsiz[0] == 0
+	      && ovlsiz[1] > 0
+	      && ovlsiz[1] < maxobjsize.to_shwi ());
+
+  /* Use more concise wording when one of the offsets is unbounded
+     to avoid confusing the user with large and mostly meaningless
+     numbers.  */
+  bool open_range = ((dstref.offrange[0] == -maxobjsize - 1
+		      && dstref.offrange[1] == maxobjsize)
+		     || (srcref.offrange[0] == -maxobjsize - 1
+			 && srcref.offrange[1] == maxobjsize));
+
+  if (sizrange[0] == sizrange[1] || sizrange[1] == 1)
+    {
+      if (ovlsiz[1] == 1)
+	{
+	  if (open_range)
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[1] == 1
+			? G_("%K%qD accessing %wu byte may overlap "
+			     "%wu byte")
+			: G_("%K%qD accessing %wu bytes may overlap "
+			     "%wu byte"),
+			exp, func, sizrange[1], ovlsiz[1]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[1] == 1
+			? G_("%K%qD accessing %wu byte at offsets %s "
+			     "and %s may overlap %wu byte at offset %s")
+			: G_("%K%qD accessing %wu bytes at offsets %s "
+			     "and %s may overlap %wu byte at offset %s"),
+			exp, func, sizrange[1], offstr[0], offstr[1],
+			ovlsiz[1], offstr[2]);
+	  return true;
+	}
+
+      if (open_range)
+	warning_at (loc, OPT_Wrestrict,
+		    sizrange[1] == 1
+		    ? G_("%K%qD accessing %wu byte may overlap "
+			 "up to %wu bytes")
+		    : G_("%K%qD accessing %wu bytes may overlap "
+			 "up to %wu bytes"),
+		    exp, func, sizrange[1], ovlsiz[1]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    sizrange[1] == 1
+		    ? G_("%K%qD accessing %wu byte at offsets %s and "
+			 "%s may overlap up to %wu bytes at offset %s")
+		    : G_("%K%qD accessing %wu bytes at offsets %s and "
+			 "%s may overlap up to %wu bytes at offset %s"),
+		    exp, func, sizrange[1], offstr[0], offstr[1],
+		    ovlsiz[1], offstr[2]);
+      return true;
+    }
+
+  if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+    {
+      if (open_range)
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[1] == 1
+		    ? G_("%K%qD accessing between %wu and %wu bytes "
+			 "may overlap %wu byte")
+		    : G_("%K%qD accessing between %wu and %wu bytes "
+			 "may overlap up to %wu bytes"),
+		    exp, func, sizrange[0], sizrange[1], ovlsiz[1]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[1] == 1
+		    ? G_("%K%qD accessing between %wu and %wu bytes "
+			 "at offsets %s and %s may overlap %wu byte "
+			 "at offset %s")
+		    : G_("%K%qD accessing between %wu and %wu bytes "
+			 "at offsets %s and %s may overlap up to %wu "
+			 "bytes at offset %s"),
+		    exp, func, sizrange[0], sizrange[1],
+		    offstr[0], offstr[1], ovlsiz[1], offstr[2]);
+      return true;
+    }
+
+  warning_at (loc, OPT_Wrestrict,
+	      ovlsiz[1] == 1
+	      ? G_("%K%qD accessing %wu or more bytes at offsets %s "
+		   "and %s may overlap %wu byte at offset %s")
+	      : G_("%K%qD accessing %wu or more bytes at offsets %s "
+		   "and %s may overlap up to %wu bytes at offset %s"),
+	      exp, func, sizrange[0], offstr[0], offstr[1],
+	      ovlsiz[1], offstr[2]);
+
+  return true;
+}
+
+/* Helper to create a minimal CALL_EXPR tree from a GIMPLE call STMT.
+   The tree is minimally populated to be be usable in a %K directive
+   with the same result as the corresponding STMT with %G. See
+   expand_call_stmt() in cfgexpand.c.  */
+
+static tree
+gcall_to_tree_minimal (gcall *stmt)
+{
+  tree exp = build_vl_exp (CALL_EXPR, gimple_call_num_args (stmt) + 3);
+  CALL_EXPR_FN (exp) = gimple_call_fn (stmt);
+  TREE_TYPE (exp) = gimple_call_return_type (stmt);
+  CALL_EXPR_STATIC_CHAIN (exp) = gimple_call_chain (stmt);
+  CALL_WITH_BOUNDS_P (exp) = gimple_call_with_bounds_p (stmt);
+ 
+  for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
+    {
+      tree arg = gimple_call_arg (stmt, i);
+      CALL_EXPR_ARG (exp, i) = arg;
+    }
+
+  SET_EXPR_LOCATION (exp, gimple_location (stmt));
+  return exp;
+}
+
+/* Attempt to detect and diagnose invalid offset bounds and (exept for
+   memmove) overlapping copy in a call expression EXP from SRC to DST
+   and DSTSIZE and SRCSIZE bytes, respectively.  Return false when one
+   or the other has been detected, true otherwise.  */
+
+static bool
+check_bounds_or_overlap (tree exp, tree dst, tree src, tree dstsize,
+			  tree srcsize, bool bounds_only /* = false */)
+{
+  location_t loc = tree_nonartificial_location (exp);
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  tree func = get_callee_fndecl (exp);
+
+  bool check_overlap
+    = (bounds_only
+       || (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+	   && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK));
+
+  builtin_memref dstref (dst, dstsize);
+  builtin_memref srcref (src, srcsize);
+
+  builtin_access acs (exp, dstref, srcref);
+
+  /* Validate offsets first to make sure they are within the bounds
+     of the destination object if its size is known, or PTRDIFF_MAX
+     otherwise.  */
+  if (maybe_diag_offset_bounds (loc, exp, func, dst, dstref)
+      || maybe_diag_offset_bounds (loc, exp, func, src, srcref))
+    {
+      TREE_NO_WARNING (exp) = 1;
+      return false;
+    }
+
+  if (check_overlap)
+    {
+      if (operand_equal_p (dst, src, 0))
+	{
+	  warning_at (loc, OPT_Wrestrict,
+		      "%K%qD source argument is the same as destination",
+		      exp, func);
+	  TREE_NO_WARNING (exp) = 1;
+	  return false;
+	}
+
+      /* Return false when overlap has been detected.  */
+      if (maybe_diag_overlap (loc, exp, acs))
+	{
+	  TREE_NO_WARNING (exp) = 1;
+	  return false;
+	}
+    }
+
+  return true;
+}
+
+/* Attempt to detect and diagnose invalid offset bounds and (except for
+   memmove) overlapping copy in a statement STMT from SRC to DST and
+   DSTSIZE and SRCIZE bytes, respectively.  Return false when one or
+   the other has been detected, true otherwise.  */
+
+bool
+check_bounds_or_overlap (gimple *stmt, tree dst, tree src,
+			  tree dstsize, tree srcsize,
+			  bool bounds_only /*= false */)
+{
+  if (gimple_no_warning_p (stmt))
+    return false;
+
+  tree exp = gcall_to_tree_minimal (as_a <gcall *> (stmt));
+
+  if (check_bounds_or_overlap (exp, dst, src, dstsize, srcsize, bounds_only))
+    return true;
+
+  /* If a warning has been issued (and NO_WARNING set) set the GIMPLE
+     no warning bit.  */
+  if (TREE_NO_WARNING (exp))
+    gimple_set_no_warning (stmt, true);
+
+  return false;
+}
+
+/* Return the maximum object size.  */
+
+tree
+max_object_size (void)
+{
+  /* To do: Make this a configurable parameter.  */
+  return TYPE_MAX_VALUE (ptrdiff_type_node);
+}
diff --git a/gcc/builtins.h b/gcc/builtins.h
index 4ae7056..52c7ed0 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -102,4 +102,8 @@ extern bool target_char_cst_p (tree t, char *p);
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
 
+extern bool check_bounds_or_overlap (gimple *, tree, tree, tree, tree,
+				     bool = false);
+extern tree max_object_size ();
+
 #endif
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 8f36c77..019ae0c 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5266,14 +5266,20 @@ check_function_restrict (const_tree fndecl, const_tree fntype,
 			 int nargs, tree *argarray)
 {
   int i;
-  tree parms;
+  tree parms = TYPE_ARG_TYPES (fntype);
 
   if (fndecl
-      && TREE_CODE (fndecl) == FUNCTION_DECL
-      && DECL_ARGUMENTS (fndecl))
-    parms = DECL_ARGUMENTS (fndecl);
-  else
-    parms = TYPE_ARG_TYPES (fntype);
+      && TREE_CODE (fndecl) == FUNCTION_DECL)
+    {
+      /* Skip checking built-ins here.  They are checked in more
+	 detail elsewhere.  */
+      if (DECL_BUILT_IN (fndecl)
+	  && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)
+	return;
+
+      if (DECL_ARGUMENTS (fndecl))
+	parms = DECL_ARGUMENTS (fndecl);
+    }
 
   for (i = 0; i < nargs; i++)
     TREE_VISITED (argarray[i]) = 0;
diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 13d2a59..09b2374 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1170,7 +1170,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall)
 Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier.
 
 Wrestrict
-C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++)
+C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
 Warn when an argument passed to a restrict-qualified parameter aliases with
 another argument.
 
diff --git a/gcc/calls.c b/gcc/calls.c
index 3730f43..364f2c0 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "builtins.h"
 
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
 #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -1194,7 +1195,7 @@ alloc_max_size (void)
 {
   if (!alloc_object_size_limit)
     {
-      alloc_object_size_limit = TYPE_MAX_VALUE (ssizetype);
+      alloc_object_size_limit = max_object_size ();
 
       if (warn_alloc_size_limit)
 	{
@@ -1213,7 +1214,7 @@ alloc_max_size (void)
 		     suffixes.  */
 		  if (!strcmp (end, "kB"))
 		    unit = 1000;
-		  else if (!strcasecmp (end, "KiB") || strcmp (end, "KB"))
+		  else if (!strcasecmp (end, "KiB") || !strcmp (end, "KB"))
 		    unit = 1024;
 		  else if (!strcmp (end, "MB"))
 		    unit = HOST_WIDE_INT_UC (1000) * 1000;
@@ -1245,7 +1246,8 @@ alloc_max_size (void)
 		{
 		  widest_int w = wi::mul (limit, unit);
 		  if (w < wi::to_widest (alloc_object_size_limit))
-		    alloc_object_size_limit = wide_int_to_tree (ssizetype, w);
+		    alloc_object_size_limit
+		      = wide_int_to_tree (ptrdiff_type_node, w);
 		}
 	    }
 	}
@@ -1254,13 +1256,17 @@ alloc_max_size (void)
 }
 
 /* Return true when EXP's range can be determined and set RANGE[] to it
-   after adjusting it if necessary to make EXP 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.  */
+   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.  */
 
 bool
-get_size_range (tree exp, tree range[2])
+get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
 {
   if (tree_fits_uhwi_p (exp))
     {
@@ -1269,20 +1275,33 @@ get_size_range (tree exp, tree range[2])
       return true;
     }
 
+  tree exptype = TREE_TYPE (exp);
+  bool integral = INTEGRAL_TYPE_P (exptype);
+
   wide_int min, max;
-  enum value_range_type range_type
-    = ((TREE_CODE (exp) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (exp)))
-       ? get_range_info (exp, &min, &max) : VR_VARYING);
+  enum value_range_type range_type;
+
+  if (TREE_CODE (exp) == SSA_NAME && integral)
+    range_type = get_range_info (exp, &min, &max);
+  else
+    range_type = VR_VARYING;
 
   if (range_type == VR_VARYING)
     {
-      /* No range information available.  */
+      if (integral)
+	{
+	  /* Use the full range of the type of the expression when
+	     no value range information is available.  */
+	  range[0] = TYPE_MIN_VALUE (exptype);
+	  range[1] = TYPE_MAX_VALUE (exptype);
+	  return true;
+	}
+
       range[0] = NULL_TREE;
       range[1] = NULL_TREE;
       return false;
     }
 
-  tree exptype = TREE_TYPE (exp);
   unsigned expprec = TYPE_PRECISION (exptype);
 
   bool signed_p = !TYPE_UNSIGNED (exptype);
@@ -1320,11 +1339,16 @@ get_size_range (tree exp, tree range[2])
 	{
 	  /* 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 set the range to
-	     [MAX, TYPE_MAX] so that when MAX is greater than the limit
-	     the whole range is diagnosed.  */
-	  min = max + 1;
-	  max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	     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
+	    {
+	      min = max + 1;
+	      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	    }
 	}
       else
 	{
diff --git a/gcc/calls.h b/gcc/calls.h
index df5817f..cd3d58d 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -38,6 +38,6 @@ extern bool pass_by_reference (CUMULATIVE_ARGS *, machine_mode,
 extern bool reference_callee_copied (CUMULATIVE_ARGS *, machine_mode,
 				     tree, bool);
 extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
-extern bool get_size_range (tree, tree[2]);
+extern bool get_size_range (tree, tree[2], bool = false);
 
 #endif // GCC_CALLS_H
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index d0e0782..9afe742 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2629,6 +2629,9 @@ expand_call_stmt (gcall *stmt)
   if (gimple_call_nothrow_p (stmt))
     TREE_NOTHROW (exp) = 1;
 
+  if (gimple_no_warning_p (stmt))
+    TREE_NO_WARNING (exp) = 1;
+
   CALL_EXPR_TAILCALL (exp) = gimple_call_tail_p (stmt);
   CALL_EXPR_MUST_TAIL_CALL (exp) = gimple_call_must_tail_p (stmt);
   CALL_EXPR_RETURN_SLOT_OPT (exp) = gimple_call_return_slot_opt_p (stmt);
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 7b1a697..9c8ebd7 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3864,6 +3864,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect Options}.
 -Wparentheses  @gol
 -Wpointer-sign  @gol
 -Wreorder   @gol
+-Wrestrict   @gol
 -Wreturn-type  @gol
 -Wsequence-point  @gol
 -Wsign-compare @r{(only in C++)}  @gol
@@ -6612,11 +6613,25 @@ reduce the padding and so make the structure smaller.
 Warn if anything is declared more than once in the same scope, even in
 cases where multiple declaration is valid and changes nothing.
 
-@item -Wrestrict
+@item -Wno-restrict
 @opindex Wrestrict
 @opindex Wno-restrict
-Warn when an argument passed to a restrict-qualified parameter
-aliases with another argument.
+Warn when an object referenced by a @code{restrict}-qualified parameter
+(or, in C++, @code{__restrict}-qualified parameter) is aliased by another
+argument, or when copies between such objects overlap.  For example,
+the call to the @code{strcpy} function below attempts to truncate the string
+by replacing its initial characters with the last four.  However, because
+the call writes the terminating NUL into @code{a[4]}, the copies overlap and
+the call is diagnosed.
+
+@smallexample
+struct foo
+@{
+  char a[] = "abcd1234";
+  strcpy (a, a + 4);
+@};
+@end smallexample
+The @option{-Wrestrict} is included in @option{-Wall}.
 
 @item -Wnested-externs @r{(C and Objective-C only)}
 @opindex Wnested-externs
diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
index cb33c1e..048a2e4 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -59,6 +59,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "stringpool.h"
 #include "attribs.h"
 #include "asan.h"
+#include "intl.h"
+#include "diagnostic-core.h"
 
 /* Return true when DECL can be referenced from current unit.
    FROM_DECL (if non-null) specify constructor of variable DECL was taken from.
@@ -658,13 +660,12 @@ size_must_be_zero_p (tree size)
   return wi::eq_p (min, wone) && wi::geu_p (max, ssize_max);
 }
 
-/* Fold function call to builtin mem{{,p}cpy,move}.  Return
-   false if no simplification can be made.
-   If ENDP is 0, return DEST (like memcpy).
-   If ENDP is 1, return DEST+LEN (like mempcpy).
-   If ENDP is 2, return DEST+LEN-1 (like stpcpy).
-   If ENDP is 3, return DEST, additionally *SRC and *DEST may overlap
-   (memmove).   */
+/* Fold function call to builtin mem{{,p}cpy,move}.  Try to detect and
+   diagnose (otherwise undefined) overlapping copies without preventing
+   folding.  When folded, GCC guarantees that overlapping memcpy has
+   the same semantics as memmove.  Call to the library memcpy need not
+   provide the same guarantee.  Return false if no simplification can
+   be made.  */
 
 static bool
 gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
@@ -676,6 +677,12 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
   tree destvar, srcvar;
   location_t loc = gimple_location (stmt);
 
+  tree func = gimple_call_fndecl (stmt);
+  bool nowarn = gimple_no_warning_p (stmt);
+  bool check_overlap = (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+			&& DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK
+			&& !nowarn);
+
   /* If the LEN parameter is a constant zero or in range where
      the only valid value is zero, return DEST.  */
   if (size_must_be_zero_p (len))
@@ -699,6 +706,15 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
      DEST{,+LEN,+LEN-1}.  */
   if (operand_equal_p (src, dest, 0))
     {
+      /* Avoid diagnosing exact overlap in calls to __builtin_memcpy.
+	 It's safe and may even be emitted by GCC itself (see bug
+	 32667).  However, diagnose it in explicit calls to the memcpy
+	 function.  */
+      if (check_overlap && *IDENTIFIER_POINTER (DECL_NAME (func)) != '_')
+	warning_at (loc, OPT_Wrestrict,
+		    "%qD source argument is the same as destination",
+		    func);
+
       unlink_stmt_vdef (stmt);
       if (gimple_vdef (stmt) && TREE_CODE (gimple_vdef (stmt)) == SSA_NAME)
 	release_ssa_name (gimple_vdef (stmt));
@@ -748,6 +764,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	  unsigned ilen = tree_to_uhwi (len);
 	  if (pow2p_hwi (ilen))
 	    {
+	      /* Detect invalid bounds and overlapping copies and issue
+		 either -Warray-bounds or -Wrestrict.  */
+	      if (check_bounds_or_overlap (stmt, dest, src, len, len))
+		gimple_set_no_warning (stmt, true);
+
 	      scalar_int_mode mode;
 	      tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
 	      if (type
@@ -1059,6 +1080,10 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	    }
 	}
 
+      /* Detect invalid bounds and overlapping copies and issue either
+	 -Warray-bounds or -Wrestrict.  */
+      check_bounds_or_overlap (stmt, dest, src, len, len);
+
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
 	{
@@ -1423,7 +1448,7 @@ get_range_strlen (tree arg, tree length[2], bitmap *visited, int type,
 	    tree op3 = gimple_assign_rhs3 (def_stmt);
 	    return get_range_strlen (op2, length, visited, type, fuzzy, flexp)
 	      && get_range_strlen (op3, length, visited, type, fuzzy, flexp);
-          }
+	  }
         return false;
 
       case GIMPLE_PHI:
@@ -1515,12 +1540,19 @@ static bool
 gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi,
 			    tree dest, tree src)
 {
-  location_t loc = gimple_location (gsi_stmt (*gsi));
+  gimple *stmt = gsi_stmt (*gsi);
+  location_t loc = gimple_location (stmt);
   tree fn;
 
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -2303,6 +2335,15 @@ gimple_fold_builtin_memory_chk (gimple_stmt_iterator *gsi,
      (resp. DEST+LEN for __mempcpy_chk).  */
   if (fcode != BUILT_IN_MEMSET_CHK && operand_equal_p (src, dest, 0))
     {
+      if (fcode != BUILT_IN_MEMMOVE && fcode != BUILT_IN_MEMMOVE_CHK)
+	{
+	  tree func = gimple_call_fndecl (stmt);
+
+	  warning_at (loc, OPT_Wrestrict,
+		      "%qD source argument is the same as destination",
+		      func);
+	}
+
       if (fcode != BUILT_IN_MEMPCPY_CHK)
 	{
 	  replace_call_with_value (gsi, dest);
@@ -2404,6 +2445,12 @@ gimple_fold_builtin_stxcpy_chk (gimple_stmt_iterator *gsi,
   /* If SRC and DEST are the same (and not volatile), return DEST.  */
   if (fcode == BUILT_IN_STRCPY_CHK && operand_equal_p (src, dest, 0))
     {
+      tree func = gimple_call_fndecl (stmt);
+
+      warning_at (loc, OPT_Wrestrict,
+		  "%qD source argument is the same as destination",
+		  func);
+
       replace_call_with_value (gsi, dest);
       return true;
     }
@@ -7295,3 +7342,4 @@ gimple_stmt_integer_valued_real_p (gimple *stmt, int depth)
       return false;
     }
 }
+
diff --git a/gcc/gimple.c b/gcc/gimple.c
index 1f291e1..b81edf9 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -361,6 +361,7 @@ gimple_build_call_from_tree (tree t, tree fnptrtype)
     gimple_call_set_arg (call, i, CALL_EXPR_ARG (t, i));
 
   gimple_set_block (call, TREE_BLOCK (t));
+  gimple_set_location (call, EXPR_LOCATION (t));
 
   /* Carry all the CALL_EXPR flags to the new GIMPLE_CALL.  */
   gimple_call_set_chain (call, CALL_EXPR_STATIC_CHAIN (t));
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-2.c b/gcc/testsuite/c-c++-common/Warray-bounds-2.c
new file mode 100644
index 0000000..874d3d5
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-bounds-2.c
@@ -0,0 +1,161 @@
+/* Test to exercise that -Warray-bounds warnings for memory and sring
+   functions are issued even when they are declared in system headers
+   (i.e., not just when they are explicitly declared in the source
+   file.)
+   Also verify that the warnings are issued even for calls where the
+   source of the excessive array bound is in a different function than
+   the call.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds" } */
+
+#include <stddef.h>
+#include <string.h>
+
+#define MAX  (__SIZE_MAX__ / 2)
+
+void sink (void*);
+
+struct __attribute__ ((packed)) Array
+{
+  char a13[13];
+  char a15[15];
+  char a17[17];
+};
+
+/* Exercise memcpy out-of-bounds offsets with an array of known size.  */
+
+void wrap_memcpy_src_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d, s + i, n);   /* { dg-warning "offset 46 from the object at .ar. is out of the bounds of .struct Array." "memcpy" } */
+}
+
+void call_memcpy_src_xsize (char *d, size_t n)
+{
+  struct Array ar;
+  sink (&ar);
+  wrap_memcpy_src_xsize (d, ar.a13, 46, n);
+  sink (&ar);
+}
+
+/* Exercise memcpy out-of-bounds offsets with an array of unknown size.  */
+
+void wrap_memcpy_src_ssize_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d, s + i, n);   /* { dg-warning "pointer overflow between offset \[0-9\]+ and size 3" "memcpy" } */
+}
+
+void call_memcpy_src_ssize_max (char *d, const char *s, size_t n)
+{
+  wrap_memcpy_src_ssize_max (d, s, MAX, 3);
+}
+
+void wrap_memcpy_dst_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d + i, s, n);   /* { dg-warning "offset 47 from the object at .ar. is out of the bounds of .struct Array." "memcpy" } */
+}
+void call_memcpy_dst_xsize (const char *s, size_t n)
+{
+  struct Array ar;
+  sink (&ar);
+  wrap_memcpy_dst_xsize (ar.a15, s, 34, n);
+  sink (&ar);
+}
+
+void wrap_memcpy_dst_ssize_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d + i, s, n);   /* { dg-warning "offset \[0-9\]+ from the object at .p. is out of the bounds of .struct Array." "memcpy" } */
+}
+
+void call_memcpy_dst_ssize_max (struct Array *p, const char *s, size_t n)
+{
+  wrap_memcpy_dst_ssize_max (p->a15, s, MAX, n);
+}
+
+
+void wrap_strcat_src_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcat (d, s + i);   /* { dg-warning "offset 46 from the object at .p. is out of the bounds of .struct Array." "strcat" } */
+}
+
+void call_strcat_src_xsize (char *d, const struct Array *p)
+{
+  wrap_strcat_src_xsize (d, p->a15, 15 + 17 + 1);
+}
+
+void wrap_strcat_dst_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcat (d + i, s);   /* { dg-warning "offset 47 from the object at .p. is out of the bounds of .struct Array." "strcat" } */
+}
+
+void call_strcat_dst_xsize (struct Array *p, const char *s)
+{
+  wrap_strcat_dst_xsize (p->a15, s, 15 + 17 + 2);
+}
+
+
+void wrap_strcpy_src_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcpy (d, s + i);   /* { dg-warning "offset 48 from the object at .p. is out of the bounds of .struct Array." "strcpy" } */
+}
+
+void call_strcpy_src_xsize (char *d, const struct Array *p)
+{
+  wrap_strcpy_src_xsize (d, p->a15, 15 + 17 + 3);
+}
+
+void wrap_strcpy_dst_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcpy (d + i, s);   /* { dg-warning "offset 49 from the object at .p. is out of the bounds of .struct Array." "strcpy" } */
+}
+
+void call_strcpy_dst_xsize (struct Array *p, const char *s)
+{
+  wrap_strcpy_dst_xsize (p->a15, s, 15 + 17 + 4);
+}
+
+
+/* Exercise strncpy out-of-bounds offsets with an array of known size.  */
+
+void wrap_strncpy_src_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d, s + i, n);   /* { dg-warning "offset 46 from the object at .p. is out of the bounds of 'struct Array." "strncpy" } */
+}
+
+void call_strncpy_src_xsize (char *d, const struct Array *p, size_t n)
+{
+  wrap_strncpy_src_xsize (d, p->a17, 17 + 1, n);
+}
+
+/* Exercise strncpy out-of-bounds offsets with an array of unknown size.  */
+
+void wrap_strncpy_src_ssize_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  /* Unlike in the similar call to memcpy(), there is no pointer
+     overflow here because the size N is not added to the source
+     offset.  */
+  strncpy (d, s + i, n);
+}
+
+void call_strncpy_src_ssize_max (char *d, const char *s, size_t n)
+{
+  wrap_strncpy_src_ssize_max (d, s, MAX, 3);
+}
+
+void wrap_strncpy_dst_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d + i, s, n);   /* { dg-warning "offset 47 from the object at 'p' is out of the bounds of .struct Array." "strncpy" } */
+}
+void call_strncpy_dst_xsize (struct Array *p, const char *s, size_t n)
+{
+  wrap_strncpy_dst_xsize (p->a17, s, 17 + 2, n);
+}
+
+void wrap_strncpy_dst_ssize_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d + i, s, n);   /* { dg-warning "offset -\[0-9\]+ from the object at .p. is out of the bounds of .struct Array." "strncpy" } */
+}
+
+void call_strncpy_dst_ssize_max (struct Array *p, const char *s, size_t n)
+{
+  wrap_strncpy_dst_ssize_max (p->a17, s, MAX, n);
+}
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds.c b/gcc/testsuite/c-c++-common/Warray-bounds.c
new file mode 100644
index 0000000..ff5d705
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-bounds.c
@@ -0,0 +1,406 @@
+/* Exercise that -Warray-bounds is issued for out-of-bounds offsets
+   in calls to built-in functions.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" }  */
+
+#define SIZE_MAX __SIZE_MAX__
+#define SSIZE_MAX __PTRDIFF_MAX__
+#define SSIZE_MIN (-SSIZE_MAX - 1)
+
+typedef __PTRDIFF_TYPE__ ssize_t;
+typedef __SIZE_TYPE__    size_t;
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+extern ssize_t signed_value (void)
+{
+  extern volatile ssize_t signed_value_source;
+  return signed_value_source;
+}
+
+extern size_t unsigned_value (void)
+{
+  extern volatile size_t unsigned_value_source;
+  return unsigned_value_source;
+}
+
+ssize_t signed_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return val < min || max < val ? min : val;
+}
+
+ssize_t signed_anti_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return min <= val && val <= max ? min == SSIZE_MIN ? max + 1 : min - 1 : val;
+}
+
+size_t unsigned_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+size_t unsigned_anti_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return min <= val && val <= max ? min == 0 ? max + 1 : min - 1 : val;
+}
+
+#define SR(min, max) signed_range ((min), (max))
+#define UR(min, max) unsigned_range ((min), (max))
+
+#define SAR(min, max) signed_anti_range ((min), (max))
+#define UAR(min, max) unsigned_anti_range ((min), (max))
+
+void sink (void*, ...);
+
+#define CAT(x, y)      x ## y
+#define CONCAT(x, y)   CAT (x, y)
+#define UNIQUE_NAME(x) CONCAT(x, __LINE__)
+
+#define T(type, N, dst, src, n) do {		\
+    extern type UNIQUE_NAME (a)[N];		\
+    type *a = UNIQUE_NAME (a);			\
+    type *pd = (dst);				\
+    const type *ps = (src);			\
+    FUNC (pd, ps, n);				\
+    sink (a, pd, ps);				\
+  } while (0)
+
+
+void test_memcpy_bounds (char *d, const char *s, size_t n)
+{
+#define FUNC memcpy
+
+  /* Verify that invalid offsets into an array of known size are
+     detected.  */
+
+  T (char, 1, a + SR (SSIZE_MIN, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of array \[^\n\r]* with type .char ?\\\[1]" } */
+  T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds of array" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  T (char, 1, a + UR (0, 1), s, n);
+  T (char, 1, a + UR (0, 2), s, n);
+  T (char, 1, a + UR (1, 2), s, n);
+  T (char, 1, a + UR (2, 3), s, n);       /* { dg-warning "offset \\\[2, 3] is out of the bounds of array " } */
+  T (char, 1, a + UR (2, SSIZE_MAX), s, n);  /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds of array " } */
+
+  /* Offsets in excess of SSIZE_MAX are treated as negative even if
+     they appear as large positive in the source.  It would be nice
+     if they retained their type but unfortunately that's not how
+     it works so be prepared for both in case it even gets fixed.  */
+  T (char, 1, a + UR (3, SIZE_MAX), s, n);   /* { dg-warning "offset \\\[3, -1] is out of the bounds of array" } */
+
+  /* Verify that invalid offsets into an array of unknown size are
+     detected.  */
+  extern char arr[];
+  T (char, 1, arr + SR (SSIZE_MIN,         0), s, n);
+  T (char, 1, arr + SR (SSIZE_MIN + 1,    -1), s, n);   /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of array " } */
+  T (char, 1, arr + SR (SSIZE_MIN,         1), s, n);
+  T (char, 1, arr + SR (SSIZE_MIN, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (       -2,        -1), s, n);   /* { dg-warning "offset \\\[-2, -1] is out of the bounds of array " } */
+  T (char, 1, arr + SR (       -1,         0), s, n);
+  T (char, 1, arr + SR (       -1,         1), s, n);
+  T (char, 1, arr + SR (       -1, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (        0,         1), s, n);
+  T (char, 1, arr + SR (        0, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (        1,         2), s, n);
+  T (char, 1, arr + SR (        1, SSIZE_MAX), s, n);
+
+  /* Verify that all offsets via a pointer to an uknown object are
+     accepted.  */
+
+  /* Negative indices between [SSIZE_MIN, SSIZE_MAX] are valid since
+     the pointer to which the offset is applied can be at a positive
+     offset from the beginning of an object.  */
+  T (char, 1, d + SR (SSIZE_MIN,         0), s, n);
+  T (char, 1, d + SR (SSIZE_MIN,        -1), s, n);
+  T (char, 1, d + SR (SSIZE_MIN,         1), s, n);
+  T (char, 1, d + SR (SSIZE_MIN, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (       -2,        -1), s, n);
+  T (char, 1, d + SR (       -1,         0), s, n);
+  T (char, 1, d + SR (       -1,         1), s, n);
+  T (char, 1, d + SR (       -1, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (        0,         1), s, n);
+  T (char, 1, d + SR (        0, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (        1,         2), s, n);
+  T (char, 1, d + SR (        1, SSIZE_MAX), s, n);
+}
+
+/* Verify offsets in an anti-range.  */
+
+void test_memcpy_bounds_anti_range (char *d, const char *s, size_t n)
+{
+  T (char, 9, a, a + SAR (-2, -1), 3);
+  T (char, 9, a, a + SAR (-1,  1), 3);
+  T (char, 9, a, a + SAR ( 0,  1), 3);
+  T (char, 9, a, a + SAR ( 0,  2), 3);
+  T (char, 9, a, a + SAR ( 0,  3), 3);
+  T (char, 9, a, a + SAR ( 0,  4), 3);
+  T (char, 9, a, a + SAR ( 0,  5), 3);
+  /* The initial source range is valid but the final range after the access
+     has complete cannot be.  */
+  T (char, 9, a, a + SAR ( 0,  6), 3);   /* { dg-warning "offset \\\[7, -1] is out of the bounds of array " } */
+
+  /* This fails because the offset isn't represented as an SSA_NAME
+     but rather as a GIMPLE_PHI (offset, 0).  With some effort it is
+     possible to extract the range from the PHI but it's not implemented
+     (yet).  */
+  T (char, 9, a, a + SAR ( 1,  6), 3);   /* { dg-warning "offset \\\[7, 0] is out of the bounds of array " "memcpy" { xfail *-*-* } } */
+
+  T (char, 9, a, a + SAR ( 2,  6), 3);   /* { dg-warning "offset \\\[7, 1] is out of the bounds of array " } */
+  T (char, 9, a, a + SAR ( 3,  6), 3);   /* { dg-warning "offset \\\[7, 2] is out of the bounds of array " } */
+
+  T (char, 9, a, a + SAR (-1,  7), 3);   /* { dg-warning "offset \\\[8, -2] is out of the bounds of array " } */
+  T (char, 9, a, a + SAR (-2,  8), 3);   /* { dg-warning "offset \\\[9, -3] is out of the bounds of array " } */
+
+  T (char, 9, a + SAR (-2, -1), a, 3);
+  T (char, 9, a + SAR (-1,  1), a, 3);
+  T (char, 9, a + SAR ( 0,  1), a, 3);
+  T (char, 9, a + SAR ( 0,  2), a, 3);
+  T (char, 9, a + SAR ( 0,  3), a, 3);
+  T (char, 9, a + SAR ( 0,  6), a, 3);   /* { dg-warning "offset \\\[7, -1] is out of the bounds of array " } */
+  T (char, 9, a + SAR (-1,  7), a, 3);   /* { dg-warning "offset \\\[8, -2] is out of the bounds of array " } */
+  T (char, 9, a + SAR (-2,  8), a, 3);   /* { dg-warning "offset \\\[9, -3] is out of the bounds of array " } */
+
+  ssize_t i = SAR (SSIZE_MIN + 1, SSIZE_MAX - 4);
+  T (char, 1, d, d + SAR (SSIZE_MIN + 3, SSIZE_MAX - 1), 3);
+  T (char, 1, d, d + SAR (SSIZE_MIN + 3, SSIZE_MAX - 3), 5);
+}
+
+void foo (char *d)
+{
+  T (char, 1, d, d + SAR (SSIZE_MIN + 3, SSIZE_MAX - 3), 5);
+}
+
+/* Verify that pointer overflow in the computation done by memcpy
+   (i.e., offset + size) is detected and diagnosed.  */
+
+void test_memcpy_overflow (char *d, const char *s, size_t n)
+{
+  extern char arr[];
+
+  /* Verify that offset overflow involving an array of unknown size
+     but known access size is detected.  This works except with small
+     sizes that are powers of 2 due to bug .  */
+  T (char, 1, arr + SR (SSIZE_MAX - 1, SSIZE_MAX), s, 1);
+  T (char, 1, arr + SR (SSIZE_MAX - 1, SSIZE_MAX), s, 2);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 2 accessing array " "bug " { xfail *-*-* } } */
+  T (char, 1, arr + SR (SSIZE_MAX - 2, SSIZE_MAX), s, 3);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 3 accessing array " } */
+  T (char, 1, arr + SR (SSIZE_MAX - 4, SSIZE_MAX), s, 5);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 5 accessing array " } */
+}
+
+void test_memmove_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC memmove
+
+    T (char, 1, a + SR (SSIZE_MIN + 1, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of array \[^\n\r]+ with type .char ?\\\[1]" } */
+  T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds of array" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  const int *pi = (const int*)s;
+  T (int,  2, a + SR (-1, 1), pi, n);
+  T (int,  2, a + SR (-1, 2), pi, n);
+  T (int,  2, a + SR ( 0, 2), pi, n);
+  T (int,  2, a + SR ( 0, 3), pi, n);
+  T (int,  2, a + SR ( 1, 3), pi, n);
+  T (int,  2, a + SR ( 2, 3), pi, n);
+  T (int,  2, a + SR ( 3, 4), pi, n);      /* { dg-warning "offset \\\[12, 16] is out of the bounds of array .\[^\n\r]+. with type .int ?\\\[2]." } */
+}
+
+
+void test_mempcpy_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC mempcpy
+
+  /* Verify that invalid offsets into an array of known size are
+     detected.  */
+
+  T (char, 1, a + SR (SSIZE_MIN, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds" } */
+T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  T (char, 1, a + UR (0, 1), s, n);
+  T (char, 1, a + UR (0, 2), s, n);
+  T (char, 1, a + UR (1, 2), s, n);
+  T (char, 1, a + UR (2, 3), s, n);       /* { dg-warning "offset \\\[2, 3] is out of the bounds of array " } */
+  T (char, 1, a + UR (2, SSIZE_MAX), s, n);  /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds of array" } */
+
+  /* Offsets in excess of SSIZE_MAX are treated as negative even if
+     they appear as large positive in the source.  It would be nice
+     if they retained their type but unfortunately that's not how
+     it works so be prepared for both in case it ever gets fixed.  */
+  T (char, 1, a + UR (3, SIZE_MAX), s, n);   /* { dg-warning "offset \\\[3, -1] is out of the bounds of array " } */
+
+  /* Verify that invalid offsets into an array of unknown size are
+     detected.  */
+  extern char arr[];
+  T (char, 1, arr + SR (SSIZE_MIN,         0), s, n);
+  T (char, 1, arr + SR (SSIZE_MIN,        -1), s, n);   /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of array" } */
+  T (char, 1, arr + SR (SSIZE_MIN,         1), s, n);
+  T (char, 1, arr + SR (SSIZE_MIN, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (       -2,        -1), s, n);   /* { dg-warning "offset \\\[-2, -1] is out of the bounds of array" } */
+  T (char, 1, arr + SR (       -1,         0), s, n);
+  T (char, 1, arr + SR (       -1,         1), s, n);
+  T (char, 1, arr + SR (       -1, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (        0,         1), s, n);
+  T (char, 1, arr + SR (        0, SSIZE_MAX), s, n);
+  T (char, 1, arr + SR (        1,         2), s, n);
+  T (char, 1, arr + SR (        1, SSIZE_MAX), s, n);
+
+  /* Verify that all offsets via a pointer to an uknown object are
+     accepted.  */
+
+  /* Negative indices between [SSIZE_MIN, SSIZE_MAX] are valid since
+     the pointer to which the offset is applied can be at a positive
+     offset from the beginning of an object.  */
+  T (char, 1, d + SR (SSIZE_MIN,         0), s, n);
+  T (char, 1, d + SR (SSIZE_MIN,        -1), s, n);
+  T (char, 1, d + SR (SSIZE_MIN,         1), s, n);
+  T (char, 1, d + SR (SSIZE_MIN, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (       -2,        -1), s, n);
+  T (char, 1, d + SR (       -1,         0), s, n);
+  T (char, 1, d + SR (       -1,         1), s, n);
+  T (char, 1, d + SR (       -1, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (        0,         1), s, n);
+  T (char, 1, d + SR (        0, SSIZE_MAX), s, n);
+  T (char, 1, d + SR (        1,         2), s, n);
+  T (char, 1, d + SR (        1, SSIZE_MAX), s, n);
+}
+
+#define TI(type, N, init, dst, src) do {	\
+    type UNIQUE_NAME (a)[N] = init;		\
+    type *a = UNIQUE_NAME (a);			\
+    type *pd = (dst);				\
+    const type *ps = (src);			\
+    FUNC (pd, ps);				\
+    sink (a, pd, ps, s);			\
+  } while (0)
+
+void test_strcpy_bounds (char *d, const char *s)
+{
+#undef FUNC
+#define FUNC strcpy
+
+  ssize_t i;
+
+  TI (char, 1, "",   a, a + SR (SSIZE_MIN, 0));
+  TI (char, 1, "",   a, a + SR (-1, 0));
+  TI (char, 1, "",   a, a + SR (-1, 1));
+  TI (char, 1, "",   a, a + SR (0, 1));
+  TI (char, 1, "",   a, a + SR (0, SSIZE_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (0, SSIZE_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (1, SSIZE_MAX - 1));
+  /* The following needs a warning for reading past the end.  */
+  TI (char, 2, "0",  a, a + SR (2, SSIZE_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (3, SSIZE_MAX - 1));   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[2\\\]." } */
+
+  TI (char, 3, "01", a, a + SR (0, SSIZE_MAX - 1));
+  TI (char, 3, "01", a, a + SR (1, SSIZE_MAX - 1));
+  TI (char, 3, "01", a, a + SR (2, SSIZE_MAX - 1));
+  /* The following needs a warning for reading past the end.  */
+  TI (char, 3, "01", a, a + SR (3, SSIZE_MAX - 1));
+  TI (char, 3, "01", a, a + SR (4, SSIZE_MAX - 1));   /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[3\\\]." } */
+
+  TI (char, 4, "012", a, a + SR (SSIZE_MAX - 2, SSIZE_MAX - 1));   /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[4\\\]." } */
+
+
+  TI (char, 1, "", a + SR (SSIZE_MIN, 0), s);
+  TI (char, 1, "", a + SR (-1, 0), s);
+  TI (char, 1, "", a + SR (-1, 1), s);
+  TI (char, 1, "", a + SR (0, 1), s);
+  TI (char, 1, "", a + SR (0, SSIZE_MAX - 1), s);
+  TI (char, 2, "", a + SR (0, SSIZE_MAX - 1), s);
+  TI (char, 2, "", a + SR (1, SSIZE_MAX - 1), s);
+  /* The following is diagnosed not because the initial source offset
+     it out of bounds (it isn't) but because the final source offset
+     after the access has completed, is.  It would be clearer if
+     the warning mentioned the final offset.  */
+  TI (char, 2, "", a + SR (2, SSIZE_MAX - 1), s);   /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[2\\\]." } */
+  TI (char, 2, "", a + SR (3, SSIZE_MAX - 1), s);   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[2\\\]." } */
+
+  TI (char, 3, "", a + SR (0, SSIZE_MAX - 1), s);
+  TI (char, 3, "", a + SR (1, SSIZE_MAX - 1), s);
+  TI (char, 3, "", a + SR (2, SSIZE_MAX - 1), s);
+  TI (char, 3, "", a + SR (3, SSIZE_MAX - 1), s);   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[3\\\]." } */
+  TI (char, 3, "", a + SR (4, SSIZE_MAX - 1), s);   /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[3\\\]." } */
+
+  TI (char, 4, "", a + SR (SSIZE_MAX - 2, SSIZE_MAX - 1), s);   /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds of array \[^\n\r\]+ with type .char ?\\\[4\\\]." } */
+}
+
+struct MA
+{
+  int i;
+  char a1_5[5];
+  char a2_11[11];
+};
+
+struct MA2
+{
+  struct MA ma1_3[3];
+  struct MA ma2_5[5];
+};
+
+struct MA3
+{
+  struct MA2 ma1_5[3];
+  struct MA2 ma1_7[7];
+};
+
+void test_strcpy_bounds_member_array (struct MA *pma,
+				      struct MA2 *pma2,
+				      struct MA3 *pma3,
+				      const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC(d, s, ignore) strcpy (d, s)
+
+  T (char, 1, pma->a1_5, s, 0);
+  T (char, 1, pma->a1_5 + 0, s, 0);
+  T (char, 1, pma->a1_5 + 1, s, 0);
+  T (char, 1, pma->a1_5 + 4, s, 0);
+
+  /* The following forms a pointer during the call that's outside
+     the bounds of the array it was derived from (pma->a1_5) so
+     it should be diagnosed but the representation of the pointer
+     addition doesn't contain information to distinguish it from
+     the valid pma->a2_11 + 1 so this is an XFAIL.  */
+  T (char, 1, pma->a1_5 + 5, s, 0);                 /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+  /* The following also forms an out-of-bounds pointer but similar
+     to the above, there is no reliable way to distinguish it from
+     (char*)&pma[1].i + 1 so this too is not diagnosed.  */
+  T (char, 1, pma->a1_5 + sizeof *pma + 1, s, 0);   /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+  T (char, 1, pma->a1_5 - 1, s, 0);   /* { dg-warning "offset -1 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+
+  T (char, 1, pma[1].a1_5, s, 0);
+  T (char, 1, pma[2].a1_5 + 0, s, 0);
+  T (char, 1, pma[3].a1_5 + 1, s, 0);
+  T (char, 1, pma[4].a1_5 + 4, s, 0);
+
+
+  extern struct MA3 ma3[3];
+  T (char, 1, ma3[0].ma1_5[0].ma1_3[0].a1_5 + 6, s, 0);
+}
diff --git a/gcc/testsuite/c-c++-common/Wrestrict-2.c b/gcc/testsuite/c-c++-common/Wrestrict-2.c
new file mode 100644
index 0000000..f440e7b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict-2.c
@@ -0,0 +1,70 @@
+/* PR 35503 - Warn about restricted pointers
+   Test to exercise that -Wrestrict warnings are issued for memory and
+   sring functions when they are declared in system headers (i.e., not
+   just when they are explicitly declared in the source file.)
+   Also verify that the warnings are issued even for calls where the
+   source of the aliasing violation is in a different function than
+   the restricted call.
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict" } */
+
+#include <string.h>
+
+void wrap_memcpy (void *d, const void *s, size_t n)
+{
+  memcpy (d, s, n);   /* { dg-warning "source argument is the same as destination" "memcpy" } */
+}
+
+void call_memcpy (void *d, size_t n)
+{
+  const void *s = d;
+  wrap_memcpy (d, s, n);
+}
+
+
+void wrap_strcat (char *d, const char *s)
+{
+  strcat (d, s);   /* { dg-warning "source argument is the same as destination" "strcat" } */
+}
+
+void call_strcat (char *d)
+{
+  const char *s = d;
+  wrap_strcat (d, s);
+}
+
+
+void wrap_strcpy (char *d, const char *s)
+{
+  strcpy (d, s);   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+}
+
+void call_strcpy (char *d)
+{
+  const char *s = d;
+  wrap_strcpy (d, s);
+}
+
+
+void wrap_strncat (char *d, const char *s, size_t n)
+{
+  strncat (d, s, n);   /* { dg-warning "source argument is the same as destination" "strncat" } */
+}
+
+void call_strncat (char *d, size_t n)
+{
+  const char *s = d;
+  wrap_strncat (d, s, n);
+}
+
+
+void wrap_strncpy (char *d, const char *s, size_t n)
+{
+  strncpy (d, s, n);   /* { dg-warning "source argument is the same as destination" "strncpy" } */
+}
+
+void call_strncpy (char *d, size_t n)
+{
+  const char *s = d;
+  wrap_strncpy (d, s, n);
+}
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
new file mode 100644
index 0000000..39b9c4c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -0,0 +1,985 @@
+/* PR 35503 - Warn about restricted pointers
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#define SIZE_MAX __SIZE_MAX__
+#define DIFF_MAX (__SIZE_MAX__ / 2)
+#define DIFF_MIN (-DIFF_MAX - 1)
+
+#if !defined LINE
+# define LINE 0
+#endif
+
+typedef __PTRDIFF_TYPE__ ssize_t;
+typedef __SIZE_TYPE__    size_t;
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+extern ssize_t signed_value (void)
+{
+  extern volatile ssize_t signed_value_source;
+  return signed_value_source;
+}
+
+extern size_t unsigned_value (void)
+{
+  extern volatile size_t unsigned_value_source;
+  return unsigned_value_source;
+}
+
+ssize_t signed_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return val < min || max < val ? min : val;
+}
+
+ssize_t signed_anti_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return min <= val && val <= max ? min == 0 ? max + 1 : min - 1 : val;
+}
+
+size_t unsigned_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+size_t unsigned_anti_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return min <= val && val <= max ? min == DIFF_MIN ? max + 1 : min - 1 : val;
+}
+
+#define SR(min, max) signed_range ((min), (max))
+#define UR(min, max) unsigned_range ((min), (max))
+
+#define SAR(min, max) signed_anti_range ((min), (max))
+#define UAR(min, max) unsigned_anti_range ((min), (max))
+
+
+void sink (void*, ...);
+
+/* Exercise memcpy with constant or known arguments.  */
+
+void test_memcpy_cst (void *d, const void *s)
+{
+#undef T
+#define T(dst, src, n) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+      void *pd = (dst);					\
+      const void *ps = (src);				\
+      memcpy (pd, ps, (n));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T (a, a, 0);
+  T (a, s = a, 3);           /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  /* This isn't detected because memcpy calls with small power-of-2 sizes
+     are intentionally folded into safe copies equivalent to memmove.
+     It's marked xfail only because there is value in detecting such
+     invalid calls for portability, and as a reminder of why it isn't
+     diagnosed.  */
+  T (a, a + 1, 1);           /* { dg-warning "\\\[-Wrestrict" "memcpy with a small power of 2 size" { xfail *-*-* } } */
+  T (a, a + 3, 3);
+  T (a, a + 3, 5);           /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  {
+    char a[3] = { 1, 2, 3 };
+
+    /* Verify that a call to memcpy with an exact overlap is diagnosed
+       (also tested above) but an excplicit one to __builtin_memcpy is
+       not.  See bug 32667 for the rationale.  */
+    (memcpy)(a, a, sizeof a);   /* { dg-warning "source argument is the same as destination" "memcpy" } */
+    sink (a);
+
+    __builtin_memcpy (a, a, sizeof a);
+    sink (a);
+  }
+
+  {
+    char a[3][7];
+    sink (a);
+
+    void *d = a[0];
+    const void *s = a[1];
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0];
+    s = a[1];
+    /* The following is only diagnosed for sizes that aren't small
+       powers of 2.  */
+    memcpy (d, s, sizeof a[0] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&a);
+  }
+
+  {
+    struct {
+      char a[7];
+      char b[7];
+      char c[7];
+    } x;
+    sink (&x);
+
+    void *d = x.a;
+    const void *s = x.b;
+    memcpy (d, s, sizeof x.a);
+    sink (&x);
+
+    d = x.a;
+    s = x.a;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 4;
+    s = x.b;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 6;
+    s = x.b;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b;
+    memcpy (d, s, 3);             /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b + 1;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 1;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 2;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 3;
+    memcpy (d, s, sizeof x.b);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+  }
+
+  {
+#undef T
+#define T(dst, src, n) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };	\
+      memcpy ((dst), (src), (n));			\
+      sink (a);						\
+    }							\
+    } while (0)
+
+    /* Verify the offset of the overlap is the same regardless of whether
+       the destination is at lower or higher offset than the source.  */
+    T (a, a + 1, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 1 overlaps 4 bytes at offset 1" "memcpy" } */
+    T (a, a + 2, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "memcpy" } */
+    T (a, a + 3, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 3 overlaps 2 bytes at offset 3" "memcpy" } */
+
+    T (a + 1, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 1 and 0 overlaps 4 bytes at offset 1" "memcpy" } */
+    T (a + 2, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 2 and 0 overlaps 3 bytes at offset 2" "memcpy" } */
+    T (a + 3, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 3 and 0 overlaps 2 bytes at offset 3" "memcpy" } */
+  }
+}
+
+/* Exercise memcpy with destination or source offset or size in
+   a determinate range.  */
+
+void test_memcpy_range (char *d, size_t sz)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    if (!LINE || LINE == __LINE__) {		\
+      char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };	\
+      void *pd = (dst);					\
+      const void *ps = (src);				\
+      memcpy (pd, ps, (n));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  ssize_t ir = SR (2, 3);
+  T (a + ir, a, 0);
+  T (a + ir, a, 1);
+  T (a + ir, a, 2);
+  T (a + ir, a, 3);
+  /* The following fails because the size is a small power of 2.  */
+  T (a + ir, a, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps between 1 and 2 bytes at offset \\\[3, 2]" "memcpy" { xfail *-*-*} } */
+  T (a + ir, a, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  T (d + ir, d, 0);
+  T (d + ir, d, 1);
+  T (d + ir, d, 2);
+  T (d + ir, d, 3);
+  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 1 byte at offset 3" "bug 79220" { xfail *-*-* } } */
+  T (d + ir, d, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  /* Because the size is constant and a power of 2 the following is
+     folded too early to detect the overlap.  */
+  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+  T (d + ir, d, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  /* Exercise the full range of size_t.  */
+  T (d + sz, d, 0);
+  T (d + sz, d, 1);
+  T (d + sz, d, 9);
+
+  T (a, a + 1, SR (0, 1));
+  T (a, a + 1, SR (0, 2));
+  T (a, a + 1, SR (1, 2));
+  T (a, a + 1, SR (2, 3));         /* { dg-warning "accessing between 2 and 3 bytes at offsets 0 and 1 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+  T (a, a + 1, UR (2, DIFF_MAX + (size_t)1));  /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a, a + 1, UR (2, SIZE_MAX - 1));        /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a, a + 2, SR (2, 3));
+  T (a, a + 2, SR (3, 4));         /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and 2 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+  T (a, a + 3, SR (3, 4));
+  T (a, a + 3, SR (4, 5));         /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and 3 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+  T (a, a + 3, SR (5, 6));         /* { dg-warning "accessing between 5 and 6 bytes at offsets 0 and 3 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+  T (a + 1, a, SR (0, 1));
+  T (a + 1, a, SR (0, 2));
+  T (a + 1, a, SR (1, 2));
+  T (a + 1, a, SR (2, 3));         /* { dg-warning "accessing between 2 and 3 bytes at offsets 1 and 0 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+  T (a + 1, a, UR (2, DIFF_MAX + (size_t)1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a + 1, a, UR (2, SIZE_MAX - 1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a + 2, a, SR (2, 3));
+  T (a + 2, a, SR (3, 4));         /* { dg-warning "accessing between 3 and 4 bytes at offsets 2 and 0 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+  T (a + 3, a, SR (3, 4));
+  T (a + 3, a, SR (4, 5));         /* { dg-warning "accessing between 4 and 5 bytes at offsets 3 and 0 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+  T (a + 3, a, SR (5, 6));         /* { dg-warning "accessing between 5 and 6 bytes at offsets 3 and 0 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+  ir = SR (2, 5);
+  T (a, a + ir, 4);
+  T (a, a + ir, 5);                /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] overlaps between 1 and 3 bytes at offset \\\[4, 2]" "memcpy" } */
+  T (a, a + ir, 6);                /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] overlaps between 3 and 4 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  /* Below, there are two possible regions for the source of the copy:
+       1) one just before the high end of the address space that's 3
+          bytes large close to the lower end of the offset range, and
+       2) another in the [DIFF_MIN, -8] range from D and so at least
+          8 bytes in size
+     A copy from (1) overlaps but one from (2) does not.  Verify that
+     the copy is not diagnosed.  (This test case was reduced from
+     the Linux kernel.) */
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 5);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 6);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 7);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 9);
+
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 5);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 6);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 7);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 9);
+
+  {
+    /* Create an offset in the range [0, -1].  */
+    size_t o = sz << 1;
+    T (d, d + o, 12345);
+    T (d + o, d, 23456);
+  }
+
+  /* Exercise memcpy with both destination and source pointer offsets
+     in some known range.  */
+  size_t n = UR (3, 4);
+  T (a + SR (1, 3), a + SR (1, 3), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3] and \\\[1, 3] overlaps between 1 and 4 bytes at offset \\\[3, 1]" "memcpy" } */
+  T (a + SR (1, 3), a + SR (2, 3), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3] and \\\[2, 3] overlaps between 1 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+  T (a + SR (1, 3), a + SR (3, 4), n);
+
+  T (a + SR (2, 3), a + SR (3, 4), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[2, 3] and \\\[3, 4] overlaps between 1 and 3 bytes at offset \\\[4, 3]" "memcpy" } */
+
+  T (a + SR (1, 3), a + SR (4, 5), n);
+  T (a + SR (2, 3), a + SR (4, 5), n);
+  T (a + SR (3, 4), a + SR (4, 5), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[3, 4] and \\\[4, 5] overlaps between 1 and 3 bytes at offset \\\[5, 4]" "memcpy" } */
+
+  /* Exercise the full range of size_t.  */
+  T (d, d + sz, 0);
+  T (d, d + sz, 1);
+  T (d, d + sz, 9);
+}
+
+/* Exercise memcpy with offset and/or size in a determinate anti-range.  */
+
+void test_memcpy_anti_range (char *d, const char *s)
+{
+  T (d, d + SAR (0, 3), 1);
+  T (d, d + SAR (0, 3), 2);
+  T (d, d + SAR (0, 3), 3);
+  T (d, d + SAR (0, 3), DIFF_MAX - 2);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 2" } */
+  T (d, d + SAR (0, 3), DIFF_MAX - 1);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 1" } */
+  T (d, d + SAR (0, 3), DIFF_MAX);       /* { dg-warning "overlaps \[0-9\]+ bytes at offset 0" } */
+
+  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" } */
+
+  /* Verify that a size in an anti-range ~[0, N] where N >= PTRDIFF_MAX
+     doesn't trigger a warning.  */
+  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)); */
+}
+
+/* Verify calls to memcpy() where the combination of offsets in some
+   range and size is such that either overlap is unavoidable or one
+   or both offsets would exceed the maximum size of an object
+   (DIFF_MAX).  */
+
+void test_memcpy_range_exceed (char *d, const char *s)
+{
+  /* Verify offset and size both in some range.  The memcpy checking
+     is less strict than that of string functions like strncpy and
+     doesn't trigger unless the overlap is certain.  The following
+     overlaps for (r == 3 && n > 3) but not, for example, for
+     (r == 4 && n == 4), and so it's not diagnosed.  */
+  ssize_t i, j;
+  size_t n;
+
+  i = SR (3, 5);
+  n = UR (4, 6);
+
+  T (a, a + i, n);
+  T (a + i, a, n);
+  /* Ditto for objects of unknown sizes.  */
+  T (d, d + i, n);
+  T (d + i, d, n);
+
+  /* Verify that a warning is issued for a copy between two regions
+     whose aggregate size would exceed DIFF_MAX if it were to not
+     overlap.  */
+  T (d, s, DIFF_MAX / 2);
+  T (d, s, DIFF_MAX / 2 + 1);   /* { dg-warning "overlaps 1 byte" "memcpy" } */
+  T (d, s, DIFF_MAX / 2 + 2);   /* { dg-warning "overlaps 3 bytes" "memcpy" } */
+  T (d, s, DIFF_MAX / 2 + 3);   /* { dg-warning "overlaps 5 bytes" "memcpy" } */
+
+  i = SR (DIFF_MAX - 2, DIFF_MAX);
+
+  /* Verify a warning for an out-of-bounds offset range and constant
+     size addition.  */
+  T (d, d + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, d, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+  T (d + 1, d + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, d + 1, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+  /* Verify that the warnings above are independent of whether the source
+     and destination are known to be based on the same object.  */
+  T (d, s + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, s, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+  T (d + 1, s + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, s + 1, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+#if __SIZEOF_SIZE_T__ == 8
+  /* Verfiy the offset and size computation is correct.  The overlap
+     offset mentioned in the warning plus sthe size of the access must
+     not exceed DIFF_MAX.  */
+  T (d, d + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+
+  T (d, s + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+  T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+#elif __SIZEOF_SIZE_T__ == 4
+  T (d, d + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+
+  T (d, s + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+  T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+#endif
+
+  i = SR (DIFF_MAX - 5, DIFF_MAX);
+  j = SR (DIFF_MAX - 9, DIFF_MAX);
+  n = UR (4, 5);
+  T (d + i, d + j, n);
+
+  n = UR (4, DIFF_MAX);
+  T (d + i, d + j, n);
+
+  n = UR (4, SIZE_MAX);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 8, DIFF_MAX);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 7, DIFF_MAX);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+  j = SR (DIFF_MAX - 6, DIFF_MAX);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+  n = UR (3, DIFF_MAX);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 6, DIFF_MAX);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 5, DIFF_MAX);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+  j = SR (DIFF_MAX - 4, DIFF_MAX);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+  j = SR (DIFF_MAX - 2, DIFF_MAX);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+}
+
+/* Exercise memcpy with destination and source of unknown size.  */
+
+void test_memcpy_var (char *d, const char *s)
+{
+  size_t n = unsigned_value ();
+
+  memcpy (d, d, 0);
+  sink (d);
+
+  memcpy (d, d, n);               /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  memcpy (d, &d[0], n);           /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  memcpy (&d[0], d,  n);          /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  /* The following overlaps if n is greater than 1.  */
+  s = d + 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + n;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + signed_value ();
+  memcpy (d, s, unsigned_value ());
+  sink (d);
+
+  s = d + 3;
+  n = 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 2;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 3;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 4;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+  sink (d);
+
+  s = d + 5;
+  n = 7;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  n = UR (0, 1);
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+}
+
+/* Exercise the absence of warnings with memmove.  */
+
+void test_memmove (void)
+{
+  {
+    char d[7];
+    sink (d);
+
+    const void *s = d;
+    memmove (d, s, 7);
+    sink (d);
+
+    s = d + 1;
+    memmove (d, s, 6);
+    sink (d);
+
+    s = d + 2;
+    memmove (d + 1, s, 5);
+    sink (d);
+  }
+}
+
+/* Exercise strcat with constant or known arguments.  */
+
+void test_strcat_cst (const char *s)
+{
+#undef T
+#define T(init, dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcat (pd, ps);					\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("0",   a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T ("01",  a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  /* The 3 bytes "12\0" being appended to "012" overwrite the final NUL.  */
+  T ("012", a, a + 1);            /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a, a + 2);            /* { dg-warning "accessing 2 bytes at offsets 0 and 2 overlaps 1 byte at offset 3" "strcat" } */
+  /* The nul copied from a[3] to a[3] overwrites itself so this is
+     diagnosed.  */
+  T ("012", a, a + 3);            /* { dg-warning "accessing 1 byte at offsets 0 and 3 overlaps 1 byte at offset 3" "strcat" } */
+
+  T ("012", a, a + 4);
+  T ("012", a, a + 5);
+  T ("012", a, a + 6);
+  T ("012", a, a + 7);
+  T ("012", a, a + 8);
+
+  T ("0",   a + 1, a);            /* { dg-warning "accessing 2 bytes at offsets 1 and 0 overlaps 1 byte at offset 1" "strcat" } */
+  T ("0",   a + 2, a);
+
+  /* The first of the two offsets in the diagnostic for strcat is that
+     of the first write into the destination, not that of the initial
+     read from it to compute its length.  */
+  T ("01",  a + 1, a);            /* { dg-warning "accessing 3 bytes at offsets 1 and 0 overlaps 1 byte at offset 2" "strcat" } */
+  T ("01",  a + 2, a);            /* { dg-warning "accessing 3 bytes at offsets 2 and 0 overlaps 1 byte at offset 2" "strcat" } */
+  T ("01",  a + 3, a);
+
+  T ("012", a + 1, a);            /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3 " "strcat" } */
+  T ("012", a + 4, a);
+  T ("012", a + 5, a);
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T ("012",      a, "012");
+  T ("012",      a, s);
+  T ("01234567", a, s);
+}
+
+/* Exercise strcat with destination and source of unknown length.  */
+
+void test_strcat_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcat (pd, ps);					\
+      sink (pd, ps);					\
+    }							\
+  } while (0)
+
+  T (d, d);                       /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T (d, d + 1);                   /* { dg-warning "accessing 0 or more bytes at offsets 0 and 1 may overlap 1 byte" "strcat" } */
+  T (d, d + 2);                   /* { dg-warning "accessing 0 or more bytes at offsets 0 and 2 may overlap 1 byte" "strcat" } */
+  T (d, d + 999);                 /* { dg-warning "accessing 0 or more bytes at offsets 0 and 999 may overlap 1 byte" "strcat" } */
+  T (d, d + -99);                 /* { dg-warning "accessing 0 or more bytes at offsets 0 and -99 may overlap 1 byte" "strcat" } */
+
+  size_t n = unsigned_value ();
+
+  T (d + n, d + n);                       /* { dg-warning "\\\[-Wrestrict" "strcat" } */
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T (d, "012");
+  T (d + 1, "0123");
+  T (d + n, "01234");
+  T (d, s);
+  T (d + 1, s);
+  T (d + n, s);
+
+  /* Since the offset is unknown the overlap in the call below, while
+     possible, is certainly not inevitable.  Conservatively, it should
+     not be diagnosed.  For safety, an argument for diagnosing can be
+     made.  It's a judgment call, partly determined by the effort and
+     complexity of treating this case differently from other similar
+     to it.   */
+  T (d, d + n); /* { dg-warning "may overlap" "strcat" } */
+}
+
+/* Exercise strcpy with constant or known arguments.  */
+
+void test_strcpy_cst (ssize_t i)
+{
+#undef T
+#define T(init, dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[8] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcpy (pd, ps);					\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcpy" } */
+  T ("012", a, a + 1);            /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strcpy" } */
+  T ("012", a, a + 2);
+  T ("012", a, a + 3);
+  /* The following doesn't overlap but it should trigger -Wstringop-overflow
+     for reading past the end.  */
+  T ("012", a, a + sizeof a);
+
+  /* The terminating nul written to d[2] overwrites s[0].  */
+  T ("0123", a, a + 2);           /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
+
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("01234", a, a + 2);          /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" } */
+
+  /* This happens to be safe in GCC but it's still wrong.  */
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  T ("012", a + 1, a);            /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 3 bytes at offset 1" "strcpy" } */
+  T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 2 bytes at offset 2" "strcpy" } */
+  T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3" "strcpy" } */
+  T ("012", a + 4, a);
+  /* The following doesn't overlap but it should trigger -Wstrinop-ovewrflow
+     for writing past the end.  */
+  T ("012", a + sizeof a, a);
+}
+
+/* Exercise strcpy with constant or known arguments offset by a range.
+   The tests verify the use of the lower bound of the range which is
+   more restrictive than using the upper bound for positive values.  */
+
+void test_strcpy_range (void)
+{
+#undef T
+#define T(N, init, dst, src)			\
+  do {						\
+    if (!LINE || LINE == __LINE__) {		\
+      char a[N] = init;				\
+      char *pd = (dst);				\
+      const char *ps = (src);			\
+      strcpy (pd, ps);				\
+      sink (a, pd, ps);				\
+    }						\
+  } while (0)
+
+  ssize_t r;
+
+  r = SR (0, 1);
+  T (8, "0", a + r, a);   /* { dg-warning "accessing between 1 and 2 bytes at offsets \\\[0, 1] and 0 overlaps up to 2 bytes at offset \\\[0, 1]" "strcpy" { xfail *-*-*} } */
+
+  r = SR (2, 5);
+  T (8, "01",  a + r, a);            /* { dg-warning "accessing 3 bytes at offsets \\\[2, 5] and 0 may overlap 1 byte at offset 2" } */
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[2, 5] and 0 may overlap up to 2 bytes at offset \\\[3, 2]" "strcpy" } */
+
+  /* The highest offset to which to copy without overflowing the 8-byte
+     destination is 3 and that overlaps 2 bytes.  */
+  T (8, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "strcpy" } */
+
+  /* With a 9-byte destination the highest offset is 4 and that still
+     overlaps 1 byte (the final NUL).  */
+  T (9, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 1 and 3 bytes at offset \\\[4, 2]" "strcpy" } */
+
+  /* With a 10-byte buffer it's possible to copy all 5 bytes without
+     overlap at (a + 5).  Copying at offsets 2 through 4 overflows
+     between 3 and 1 bytes, respectively.  */
+  T (10, "0123", a + r, a);          /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 may overlap up to 3 bytes at offset \\\[4, 2]" "strcpy" } */
+
+
+  r  = SR (3, 4);
+  T (8, "01",  a + r, a);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[3, 4] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+  /* The highest offset to which to copy without overflowing the 8-byte
+     destination is 3 and that overlaps 2 bytes.  */
+  T (8, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps 2 bytes at offset 3" "strcpy" } */
+
+  /* With a 9-byte destination the highest offset is 4 and that still
+     overlaps 1 byte (the final NUL).  */
+  T (9, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[4, 3]" "strcpy" } */
+
+  /* With a 10-byte buffer it's possible to copy all 5 bytes without
+     overlap at (a + 5).  Copying at offsets 2 through 4 overflows
+     between 3 and 1 bytes, respectively.  */
+  T (10, "0123", a + r, a);          /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[4, 3]" "strcpy" } */
+
+  T (8, "01",     a, a + r);
+  T (8, "012",    a, a + r);
+  T (8, "0123",   a, a + r);
+  T (8, "01234",  a, a + r);
+
+  /* With the smaller offset of 3 the final NUL definitely overlaps
+     the '4' at a[3], but with the larger offset of 4 there is no
+     overlap, so the warning is a "may overlap" and the size of
+     the overlap is 1 byte.  */
+  T (8, "012345", a, a + r);         /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and \\\[3, 4] may overlap 1 byte at offset 3" "strcpy" } */
+  T (8, "0123456", a, a + r);        /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and \\\[3, 4] may overlap up to 2 bytes at offset 3" "strcpy" } */
+
+  r = SR (3, DIFF_MAX - 3);
+  T (8, "01",  a + r, a);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[3, \[0-9\]+] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+  r = SR (DIFF_MAX - 2, DIFF_MAX - 1);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps" "strcpy" } */
+
+  /* Exercise the full range of ssize_t.  */
+  r = signed_value ();
+
+  /* The overlap in the cases below isn't inevitable but it is diagnosed
+     because it is possible and so the code is considered unsafe.  */
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap 1 byte" "strcpy" } */
+  T (8, "0", a + r, a);              /* { dg-warning "accessing 2 bytes may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes may overlap up to 4 bytes" "strcpy" } */
+
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap" "strcpy" } */
+  T (8, "0", a, a + r);              /* { dg-warning "accessing between 0 and 2 bytes may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a, a + r);            /* { dg-warning "accessing between 0 and 4 bytes may overlap up to 4 bytes" "strcpy" } */
+}
+
+/* Exercise strcpy with destination and/or source of unknown lengthu.  */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcpy (pd, ps);					\
+      sink (pd, ps);					\
+    }							\
+  } while (0)
+
+  T (d, s);
+
+  T (d, &d[0]);                   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+  T (&d[0], d);                   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  s = d;
+  T (d, s);                       /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  /* The following overlaps if *s is not nul.  It arguably should be
+     diagnosed.  */
+  T (d, d + 1);
+
+  /* The following overlaps if strlen (d) is greater than 1.  Like
+     the above, it possibly should be diagnosed too.  */
+  int r = SR (2, 3);
+  T (d, d + r);
+
+  /* The following overlaps only if strlen (s + n) >= n so it's not
+     diagnosed.  */
+  s = d + signed_value ();
+  T (d, s);
+}
+
+/* Exercise strncpy with constant or known arguments.  */
+
+void test_strncpy_cst (void)
+{
+#undef T
+#define T(init, dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[7] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strncpy (pd, ps, (size));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("012", a, a, 0);
+  T ("012", a, a, 1);             /* { dg-warning "source argument is the same as destination " "strncpy" } */
+  T ("012", a, a + 1, 1);
+  T ("012", a, a + 1, 2);         /* { dg-warning "accessing 2 bytes at offsets 0 and 1 overlaps 1 byte at offset 1" "strncpy" } */
+  T ("012", a, a + 1, 3);         /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strncpy" } */
+  T ("012", a, a + 2, 1);
+  T ("012", a, a + 2, 2);
+  /* The third written byte (nul) overwrites a[2].  */
+  T ("012", a, a + 2, 3);         /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+
+  T ("0123", a, a + 2, 1);
+  T ("0123", a, a + 2, 2);
+  /* The terminating nul written to a[2] overwrites s[0].  */
+  T ("0123", a, a + 2, 3);        /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+
+  T ("01234", a, a + 2, 1);
+  T ("01234", a, a + 2, 2);
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("01234", a, a + 2, 4);       /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+}
+
+
+/* Exercise strncpy with one or more arguments in a determinate range.  */
+
+void test_strncpy_range (char *d, size_t n)
+{
+#undef T
+#define T(init, dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = init;					\
+      strncpy ((dst), (src), (size));			\
+      sink (a, (dst), (src));				\
+    }							\
+  } while (0)
+
+  ssize_t i;
+
+  i = SR (0, 1);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);   /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[0, 1] may overlap 1 byte at offset 1" "strncpy" { xfail *-*-* } } */
+
+  i = SR (1, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);   /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[1, 5] may overlap 1 byte at offset 1" "strncpy" } */
+  T ("0123", a, a + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[1, 5] may overlap up to 2 bytes at offset \\\[2, 1]" "strncpy" } */
+
+  i = SR (2, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[2, 5] may overlap 1 byte at offset 2" "strncpy" } */
+  T ("0123", a, a + i, 4);   /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[2, 5] may overlap up to 2 bytes at offset \\\[3, 2]" "strncpy" } */
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+
+  i = SR (3, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);
+  T ("0123", a, a + i, 4);   /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[3, 5] may overlap 1 byte at offset 3" "strncpy" } */
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+
+  i = SR (4, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);
+  T ("0123", a, a + i, 4);
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+
+  /* Verify offset and size both in some range.  The strncpy checking
+     is more strict than that of memcpy and triggers even when the
+     overlap is possible but not inevitable.  The following overlaps
+     like so ('*' denotes the terminating NUL, '.' the appended NUL
+     that's not copied from the source):
+        a:        01234567*  (also indicates offset)
+        i = 4:    4567       none
+                  4567*      overlaps 1 at offset 4
+                  4567*.     overlaps 2 at offset 4
+        i = 5:    567*       none
+                  567*.      none
+                  567*..     overlaps 1 at offset 5  */
+  T ("01234567", a, a + i, UR (4, 6));   /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  /* Ditto for objects of unknown sizes.  */
+  T ("01234567", d, d + i, UR (4, 6));  /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  T ("01234567", a, a + i, UR (6, 7));  /* { dg-warning "accessing between 6 and 7 bytes at offsets 0 and \\\[4, 5] overlaps between 1 and 3 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  /* The following overlaps except in the unlikely case that value ()
+     is zero, so it's diagnosed.  */
+  T ("012", a, a, n);             /* { dg-warning "source argument is the same as destination " "strncpy" } */
+}
+
+
+/* Exercise strncpy with destination and source of unknown length.  */
+
+void test_strncpy_var (char *d, const char *s, size_t n)
+{
+#undef T
+#define T(dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {		\
+      char *pd = (dst);				\
+      const char *ps = (src);			\
+      strncpy (pd, ps, (size));			\
+      sink (pd, ps);				\
+    }						\
+  } while (0)
+
+  T (d, s, 1);
+  T (d, s, n);
+
+  T (d, d, 1);                    /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+  T (d, d, n);                    /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+
+  T (d,     d + 1, 1);
+  T (d,     d + 1, 2);            /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+  T (d + 1, d,     1);
+  T (d + 1, d,     2);            /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+}
+
+struct MemberArrays
+{
+  char a[7];
+  char b[8];
+  char c[9];
+};
+
+void test_strncpy_strcpy_var (struct MemberArrays *ar, const char *s)
+{
+  /* The following is safe and should not trigger a warning.  */
+  strncpy (ar->b, s, sizeof ar->b - 1);
+  ar->b[sizeof ar->b - 1] = '\0';
+  strcpy (ar->a, ar->b);
+  sink (ar);
+
+  /* The following is not as safe (it might overflow ar->a) but there
+     is no overlap so it also shouldn't trigger -Wrestrict.  */
+  strncpy (ar->c, s, sizeof ar->c - 1);
+  ar->c[sizeof ar->c - 1] = '\0';
+  strcpy (ar->a, ar->c);
+  sink (ar);
+}
diff --git a/gcc/testsuite/gcc.dg/Walloca-1.c b/gcc/testsuite/gcc.dg/Walloca-1.c
index ad39373..85e9160 100644
--- a/gcc/testsuite/gcc.dg/Walloca-1.c
+++ b/gcc/testsuite/gcc.dg/Walloca-1.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 /* { dg-require-effective-target alloca } */
-/* { dg-options "-Walloca-larger-than=2000 -O2" } */
+/* { dg-options "-Walloca-larger-than=2000 -O2 -ftrack-macro-expansion=0" } */
 
 #define alloca __builtin_alloca
 
diff --git a/gcc/testsuite/gcc.dg/memcpy-6.c b/gcc/testsuite/gcc.dg/memcpy-6.c
new file mode 100644
index 0000000..1d76e7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/memcpy-6.c
@@ -0,0 +1,40 @@
+/* Test to verify that overlapping memcpy with const sizes that are powers
+   of two are folded into into the same code as memmove, but that they
+   are diagnosed nonetheless.
+   { dg-do compile }
+   { dg-options "-O0 -Wrestrict -fdump-tree-optimized" } */
+
+char a[32];
+
+void fold_copy_2 (void)
+{
+  __builtin_memcpy (a + 1, a, 2);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_4 (void)
+{
+  __builtin_memcpy (a + 2, a, 4);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_8 (void)
+{
+  __builtin_memcpy (a + 3, a, 8);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_move_2 (void)
+{
+  __builtin_memmove (a + 1, a, 2);
+}
+
+void fold_move_4 (void)
+{
+  __builtin_memmove (a + 2, a, 4);
+}
+
+void fold_move_8 (void)
+{
+  __builtin_memmove (a + 3, a, 8);
+}
+
+/* { dg-final { scan-tree-dump-not "memcpy" "optimized" } }
+   { dg-final { scan-tree-dump-not "memmove" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c
index c0e7463..908d5a6 100644
--- a/gcc/testsuite/gcc.dg/pr69172.c
+++ b/gcc/testsuite/gcc.dg/pr69172.c
@@ -1,4 +1,5 @@
-/* PR tree-optimization/69172 */
+/* PR tree-optimization/69172 - ICE in make_ssa_name_fn,
+   at tree-ssanames.c:266 */
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
 
@@ -43,3 +44,7 @@ f6 (int x)
 {
   return __builtin___mempcpy_chk (&a, &a, x, 0);
 }
+
+/* The calls above violate strict aliasing.  Eliminate the -Wrestrict
+   warnings they trigger.
+  { dg-prune-output "\\\[-Wrestrict]" } */
diff --git a/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c b/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c
new file mode 100644
index 0000000..1be4922
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c
@@ -0,0 +1,68 @@
+/* { dg-do compile { target { ! x32 } } }
+   { dg-require-effective-target mempcpy }
+   { dg-options "-O2 -Wrestrict -fcheck-pointer-bounds -mmpx" } */
+
+#define USE_GNU
+#include "../../gcc.dg/strlenopt.h"
+
+/* There is no BUILT_IN_ST{P,R}NCPY_CHKP or BUILT_IN_STRNCAT_CHKP
+   so the test for them below are XFAIL.  */
+char *stpncpy (char *__restrict, const char *__restrict, size_t);
+char *strncpy (char *__restrict, const char *__restrict, size_t);
+char *strncat (char *__restrict, const char *__restrict, size_t);
+
+
+char a[8];
+
+void test_memcpy (void)
+{
+  memcpy (a, a + 1, 3);   /* { dg-warning ".memcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+void test_memmove (void)
+{
+  memmove (a, a + 1, 3);
+}
+
+void* test_mempcpy (void)
+{
+  return mempcpy (a, a + 1, 3);   /* { dg-warning ".mempcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+char* test_stpcpy (void)
+{
+  strcpy (a, "0123456");
+  return stpcpy (a, a + 2);   /* { dg-warning ".stpcpy\.chkp. accessing 6 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" } */
+}
+
+char* test_stpncpy (void)
+{
+  strcpy (a, "0123456");
+
+  /* There is no BUILT_IN_STPNCPY_CHKP so this isn't handled.  */
+  return stpncpy (a, a + 2, sizeof a);   /* { dg-warning ".stpcpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strcpy (void)
+{
+  strcpy (a, "0123456");
+  strcpy (a, a + 1);   /* { dg-warning ".strcpy\.chkp. accessing 7 bytes at offsets 0 and 1 overlaps 6 bytes at offset 1" } */
+}
+
+void test_strcat (int n)
+{
+  strcat (a, a + 3);   /* { dg-warning ".strcat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" } */
+}
+
+void test_strncat (int n)
+{
+  strncat (a, a + 3, sizeof a);   /* { dg-warning ".strncat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strncpy (int n)
+{
+  strcpy (a, "0123456");
+
+  /* There is no BUILT_IN_STRNCPY_CHKP so this isn't handled.  */
+  strncpy (a, a + 2, sizeof a);   /* { dg-warning ".strncpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 5 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 4ec0dac..ca33600 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -40,12 +40,17 @@ along with GCC; see the file COPYING3.  If not see
 #include "expr.h"
 #include "tree-dfa.h"
 #include "domwalk.h"
+#include "tree-ssa-alias.h"
 #include "tree-ssa-propagate.h"
 #include "params.h"
 #include "ipa-chkp.h"
 #include "tree-hash-traits.h"
 #include "builtins.h"
 #include "target.h"
+#include "diagnostic-core.h"
+#include "diagnostic.h"
+#include "intl.h"
+#include "attribs.h"
 
 /* A vector indexed by SSA_NAME_VERSION.  0 means unknown, positive value
    is an index into strinfo vector, negative value stands for
@@ -1362,7 +1367,7 @@ static void
 handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, len, lhs, args, type, fn, oldlen;
+  tree src, dst, srclen, len, lhs, type, fn, oldlen;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi, *olddsi, *zsi;
@@ -1478,6 +1483,22 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 	    }
 	}
       dsi->stmt = stmt;
+
+      /* Try to detect overlap before returning.  This catches cases
+	 like strcpy (d, d + n) where n is non-constant whose range
+	 is such that (n <= strlen (d) holds).
+
+	 OLDDSI->NONZERO_chars may have been reset by this point with
+	 oldlen holding it original value.  */
+      if (olddsi && oldlen)
+	{
+	  /* Add 1 for the terminating NUL.  */
+	  tree type = TREE_TYPE (oldlen);
+	  oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
+				build_int_cst (type, 1));
+	  check_bounds_or_overlap (stmt, olddsi->ptr, src, oldlen, NULL_TREE);
+	}
+
       return;
     }
 
@@ -1550,16 +1571,30 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (zsi != NULL)
     zsi->dont_invalidate = true;
 
-  if (fn == NULL_TREE)
-    return;
-
-  args = TYPE_ARG_TYPES (TREE_TYPE (fn));
-  type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+  if (fn)
+    {
+      tree args = TYPE_ARG_TYPES (TREE_TYPE (fn));
+      type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+    }
+  else
+    type = size_type_node;
 
   len = fold_convert_loc (loc, type, unshare_expr (srclen));
   len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
+
+  if (const strinfo *chksi = olddsi ? olddsi : dsi)
+    if (si
+	&& !check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
+      /* Avoid transforming strcpy when out-of-bounds offsets or
+	 overlapping access is detected.  */
+      return;
+
+  if (fn == NULL_TREE)
+    return;
+
   len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
 				  GSI_SAME_STMT);
+
   if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     {
       fprintf (dump_file, "Optimizing: ");
@@ -1607,6 +1642,68 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
     fprintf (dump_file, "not possible.\n");
 }
 
+/* Check the size argument to the built-in forms of stpncpy and strncpy
+   for out-of-bounds offsets or overlapping access, and if found, issue
+   warning(s).  */
+
+static void
+handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+
+  bool with_bounds = gimple_call_with_bounds_p (stmt);
+
+  tree dst = gimple_call_arg (stmt, with_bounds ? 1 : 0);
+  tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
+  tree dstsize = NULL_TREE, srcsize = NULL_TREE;
+
+  int didx = get_stridx (dst);
+  if (strinfo *sidst = didx > 0 ? get_strinfo (didx) : NULL)
+    {
+      /* Compute the size of the destination string including the NUL.  */
+      if (sidst->nonzero_chars)
+	{
+	  tree type = TREE_TYPE (sidst->nonzero_chars);
+	  dstsize = fold_build2 (PLUS_EXPR, type, sidst->nonzero_chars,
+				 build_int_cst (type, 1));
+	}
+      dst = sidst->ptr;
+    }
+
+  int sidx = get_stridx (src);
+  if (strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL)
+    {
+      /* Strncpy() et al. cannot modify the source string.  Prevent
+	 the rest of the pass from invalidating the strinfo data.  */
+      sisrc->dont_invalidate = true;
+
+      /* Compute the size of the source string including the NUL.  */
+      if (sisrc->nonzero_chars)
+	{
+	  tree type = TREE_TYPE (sisrc->nonzero_chars);
+	  srcsize = fold_build2 (PLUS_EXPR, type, sisrc->nonzero_chars,
+				 build_int_cst (type, 1));
+	}
+
+	src = sisrc->ptr;
+    }
+  else
+    srcsize = NULL_TREE;
+
+  check_bounds_or_overlap (stmt, dst, src, dstsize, srcsize);
+}
+
+/* Check the size argument to the built-in forms of stpncpy and strncpy
+   for out-of-bounds offsets or overlapping access, and if found, issue
+   warning(s).  */
+
+static void
+handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
+{
+  /* Same as stxncpy().  */
+  handle_builtin_stxncpy (bcode, gsi);
+}
+
 /* Handle a memcpy-like ({mem{,p}cpy,__mem{,p}cpy_chk}) call.
    If strlen of the second argument is known and length of the third argument
    is that plus one, strlen of the first argument is the same after this
@@ -1787,16 +1884,22 @@ static void
 handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr;
+  tree srclen, args, type, fn, objsz, endptr;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi;
-  location_t loc;
+  location_t loc = gimple_location (stmt);
   bool with_bounds = gimple_call_with_bounds_p (stmt);
 
-  src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
-  dst = gimple_call_arg (stmt, 0);
-  lhs = gimple_call_lhs (stmt);
+  tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
+  tree dst = gimple_call_arg (stmt, 0);
+
+  /* Bail if the source is the same as destination.  It will be diagnosed
+     elsewhere.  */
+  if (operand_equal_p (src, dst, 0))
+    return;
+
+  tree lhs = gimple_call_lhs (stmt);
 
   didx = get_stridx (dst);
   if (didx < 0)
@@ -1805,10 +1908,43 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   dsi = NULL;
   if (didx > 0)
     dsi = get_strinfo (didx);
+
+  srclen = NULL_TREE;
+  si = NULL;
+  idx = get_stridx (src);
+  if (idx < 0)
+    srclen = build_int_cst (size_type_node, ~idx);
+  else if (idx > 0)
+    {
+      si = get_strinfo (idx);
+      if (si != NULL)
+	srclen = get_string_length (si);
+    }
+
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
+      {
+	  /* The concatenation always involves copying at least one byte
+	     (the terminating nul), even if the source string is empty.
+	     If the source is unknown assume it's one character long and
+	     used that as both sizes.  */
+	tree slen = srclen;
+	if (slen)
+	  {
+	    tree type = TREE_TYPE (slen);
+	    slen = fold_build2 (PLUS_EXPR, type, slen, build_int_cst (type, 1));
+	  }
+
+	tree sptr = si && si->ptr ? si->ptr : src;
+
+	if (!check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
+	  /* Avoid transforming strcat when out-of-bounds offsets or
+	     overlapping access is detected.  */
+	  return;
+      }
+
       /* strcat (p, q) can be transformed into
-	 tmp = p + strlen (p); endptr = strpcpy (tmp, q);
+	 tmp = p + strlen (p); endptr = stpcpy (tmp, q);
 	 with length endptr - p if we need to compute the length
 	 later on.  Don't do this transformation if we don't need
 	 it.  */
@@ -1841,20 +1977,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       return;
     }
 
-  srclen = NULL_TREE;
-  si = NULL;
-  idx = get_stridx (src);
-  if (idx < 0)
-    srclen = build_int_cst (size_type_node, ~idx);
-  else if (idx > 0)
-    {
-      si = get_strinfo (idx);
-      if (si != NULL)
-	srclen = get_string_length (si);
-    }
-
-  loc = gimple_location (stmt);
-  dstlen = dsi->nonzero_chars;
+  tree dstlen = dsi->nonzero_chars;
   endptr = dsi->endptr;
 
   dsi = unshare_strinfo (dsi);
@@ -1915,7 +2038,23 @@ handle_builtin_strcat (enum built_in_function