diff mbox

enhance -Wrestrict to handle string built-ins (PR 78918)

Message ID 8a0de82f-d68e-9c37-0406-f216e3f92884@gmail.com
State New
Headers show

Commit Message

Martin Sebor July 16, 2017, 11:47 p.m. UTC
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
diff mbox

Patch

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