On 08/22/2017 02:45 AM, Richard Biener wrote:
On Mon, Aug 21, 2017 at 10:10 PM, Martin Sebor <mse...@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
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));

Reply via email to