diff mbox series

[PING,2] enhance -Wrestrict to handle string built-ins (PR 78918)

Message ID 11adcf4e-2ed0-c441-2ea7-538fb453b956@gmail.com
State New
Headers show
Series [PING,2] enhance -Wrestrict to handle string built-ins (PR 78918) | expand

Commit Message

Martin Sebor Nov. 16, 2017, 9:29 p.m. UTC
Ping.

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

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

Finally, I built Binutils and GDB with no warnings.

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

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

Comments

Jeff Law Nov. 22, 2017, 11:50 p.m. UTC | #1
On 11/16/2017 02:29 PM, Martin Sebor wrote:
> Ping.
> 
> I've fixed the outstanding false positive exposed by the Linux
> kernel.  The kernel builds with four instances of the warning,
> all of them valid (perfect overlap in memcpy).
> 
> I also built Glibc.  It shows one instance of the warning, also
> a true positive (cause by calling a restrict-qualified function
> with two copies of the same argument).
> 
> Finally, I built Binutils and GDB with no warnings.
> 
> The attached patch includes just that one fix, with everything
> else being the same.
> 
> On 11/09/2017 04:57 PM, Martin Sebor wrote:
>> Ping:
>>
>>   https://gcc.gnu.org/ml/gcc-patches/2017-10/msg01642.html
>>
>> On 10/23/2017 08:42 PM, Martin Sebor wrote:
>>> Attached is a reworked solution to enhance -Wrestrict while
>>> avoiding changing tree-vrp.c or any other VRP machinery.  Richard,
>>> in considering you suggestions I realized that the ao_ref struct
>>> isn't general enough to detect the kinds of problems I needed to
>>> etect (storing bit-offsets in HOST_WIDE_INT means out-of-bounds
>>> offsets cannot be represented or detected, leading to either false
>>> positives or false negatives).
So this seems to be a recurring theme, which makes me wonder if we
should have an ao_ref-like structure that deals in bytes rather than
bits and make it a first class citizen.   There's certainly clients that
work on bits and certainly clients that would prefer to work on bytes.



>>> Instead, the solution adds a couple of small classes to builtins.c
>>> to overcome this limitation (I'm contemplating moving them along
>>> with -Wstringop-overflow to a separate file to keep builtins.c
>>> from getting too much bigger).
>>>
>>> The detection of out-of-bounds offsets and overlapping accesses
>>> is relatively simple but the rest of the changes are somewhat
>>> involved because of the computation of the offsets and sizes of
>>> the overlaps.
>>>
>>> Jeff, as per your suggestion/request in an earlier review (bug
>>> 81117) I've renamed some of the existing functions to better
>>> reflect their new function (including renaming strlen_optimize_stmt
>>> in tree-ssa-strlen.c to strlen_check_and_optimize_stmt).  There's
>>> quite a bit of churn due to some of this renaming.  I don't think
>>> this renaming makes the review too difficult but if you feel
>>> differently I can [be persuaded to] split it out into a separate
>>> patch.
Understood.  FWIW, one way to deal with this that I've found works
reasonably well is to have an initial patch that just does the renaming
you want to do.  That separates the mechanical stuff from the meat of
the change.  They can be git squashed together just before committing or
committed as two changes back-to-back to eliminate or minimize the time
where the variable and function names are inconsistent.

It's also the case that independent little fixes should just go upstream
immediately.  I didn't see many, but the alloc_max_size fix for KB seems
like it should have just gone forward independent of the rest of the
changes.


>>>
>>> To validate the patch I compiled the Linux kernel and Binutils/GDB.
>>> There's one false positive I'm working on resolving that's caused
>>> by an incorrect interpretation of an offset in a range whose lower
>>> bound is greater than its upper bound.  This it so say that while
>>> I'm aware the patch isn't perfect it already works reasonably well
>>> in practice and I think it's close enough to review.
>>>
>>> Thanks
>>> Martin
>>
> 
> 
> gcc-78918.diff
> 
> 
> PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over self
> 
> gcc/c-family/ChangeLog:
> 
> 	PR tree-optimization/78918
> 	* c-common.c (check_function_restrict): Avoid checking built-ins.
> 	* c.opt (-Wrestrict): Include in -Wall.
> 
> gcc/ChangeLog:
> 
> 	PR tree-optimization/78918
> 	* builtins.c (builtin_memref, builtin_access): New classes.
> 	(check_bounds_or_overlap, maybe_diag_overlap): New static functions.
> 	(maybe_diag_offset_bounds): Same.
> 	(check_sizes): Rename...
> 	(check_access): ...to this.  Rename function arguments for clarity.
> 	(check_memop_sizes): Adjust names.
> 	(expand_builtin_memchr, expand_builtin_memcpy): Same.
> 	(expand_builtin_memmove, expand_builtin_mempcpy): Same.
> 	(expand_builtin_strcat, expand_builtin_stpncpy): Same.
> 	(check_strncat_sizes, expand_builtin_strncat): Same.
> 	(expand_builtin_strncpy, expand_builtin_memset): Same.
> 	(expand_builtin_bzero, expand_builtin_memcmp): Same.
> 	(expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
> 	(maybe_emit_sprintf_chk_warning): Same.
> 	(expand_builtin_strcpy): Adjust.
> 	(expand_builtin_stpcpy): Same.
> 	(expand_builtin_with_bounds): Detect out-of-bounds/overlapping
> 	accesses in pointer-checking forms of memcpy, memmove, and mempcpy.
> 	(gcall_to_tree_minimal, check_bounds_or_overlap, max_object_size):
> 	Define new functions.
> 	* builtins.h (check_bounds_or_overlap, max_object_size): Declare.
> 	* calls.c (alloc_max_size): Call max_object_size instead of
> 	hardcoding ssizetype limit.  Fix a typo.
> 	(get_size_range): Handle new argument.
> 	* calls.h (get_size_range): Add a new argument.
> 	* cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
> 	* doc/invoke.texi (-Wrestrict): Adjust, add example.
> 	* gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
> 	operations.
> 	(gimple_fold_builtin_memory_chk): Same.
> 	(gimple_fold_builtin_stxcpy_chk): New function.
> 	* gimple.c (gimple_build_call_from_tree): Propagate location.
> 	* tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
> 	operations.
> 	(handle_builtin_strcat): Same.
> 	(strlen_optimize_stmt): Rename...
> 	(strlen_check_and_optimize_stmt): ...to this.  Handle strncat,
> 	stpncpy, strncpy, and their checking forms.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR tree-optimization/78918
> 	* c-c++-common/Warray-bounds.c: New test.
> 	* c-c++-common/Wrestrict-2.c: New test.
> 	* c-c++-common/Wrestrict.c: New test.
> 	* c-c++-common/Wrestrict.s: New test.
> 	* gcc.dg/Walloca-1.c: Adjust/
> 	* gcc.dg/memcpy-6.c: New test.
> 	* gcc.dg/pr69172.c: Adjust.
> 	* gcc.target/i386/chkp-stropt-17.c: New test.
So I realize you don't have any code to answer this question, but your
thoughts on how much we might loose effectiveness if we didn't do the
warnings within gimple_fold_builtin_<whatever>, but instead broke out a
distinct pass to handle warnings?  My biggest design concern is the
warning from within the folder aspects.

Are there any interactions with the issues we touched on at the end of
our call Monday?  ie, in the case where there is no overlap, but the
strncat/strncpy overwrites the NUL in the source string, we we issue a
warning.  Do those issues arise in this code?

FWIW I have some concerns about how this code is going to interact with
the poly_int stuff.  The code which finds and extracts pointer offsets
in particular.  I'm sure we can adjust if that's necessary (one of the
meta comments I need to make sure I note in the poly_int thread is a
request for guidance WRT when code needs to be poly_int aware).




> 
> @@ -192,6 +193,91 @@ static tree do_mpfr_remquo (tree, tree, tree);
>  static tree do_mpfr_lgamma_r (tree, tree, tree);
>  static void expand_builtin_sync_synchronize (void);
>  
> +struct builtin_memref;
> +
> +/* Description of a memory access by a raw memory or string built-in
> +   function.  */
> +struct builtin_access
> +{
> +private:
> +  /* Temporaries used to compute the final result.  */
> +  offset_int dstoff[2];
> +  offset_int srcoff[2];
> +  offset_int dstsiz[2];
> +  offset_int srcsiz[2];
> +
> +  /* Member function to call to determine overlap.  */
> +  bool (builtin_access::*detect_overlap) ();

A nit.  For a POD type we use structs, otherwise make it a class.
Similarly for the builtin_memref class.

Check your ordering of members in builtin_memref.


Any reason not to define builtin_memref prior to builtin_access?


So I realize why you're not using the ao_ref infrastructure.  However,
have you reviewed the ao_ref bits to help ensure that builtin_memref
handles any various corner cases.  Are there pieces you can refactor and
share between the two pieces of infrastructure?  If I just look at the
ctor I see a ton of stuff that looks like fairly generic infrastructure
to take a memory reference an dig down into it to find the base and
offset.  I'd hate to have two completely different implementations of
that code with different bugs in each :(





> @@ -2962,39 +3048,157 @@ determine_block_size (tree len, rtx len_rtx,
>  			  GET_MODE_MASK (GET_MODE (len_rtx)));
>  }
>  
> +/* Validate REF offsets in an EXPRession passed as an argument to a CALL
> +   to a built-in function FUNC to make sure they are within the bounds
> +   of the referenced object if its size is known, or PTRDIFF_MAX otherwise.
> +   Both initial values of the offsets and their final value computed by
> +   the function by incrementing the initial value by the size are
> +   validated.  Return true if the offsets are not valid and a diagnostic
> +   has been isssued.  */
s/isssued/issued/






> +}
> +
> +/* Return error_mark_node if the signed offset is exceeds the bounds
> +   of the address space (PTRDIFF_MAX).  Otherwise, return BASE when
> +   the offset exceeds the bounds of the BASE object. Otherwise return
> +   NULL to inidctae the offset is in bounds.  */
s/inidctae/indicate/




> +
> +
> +/* Return true if THIS (AKA DSTREF) and SRCREF describe accesses that
> +   either overlap one another or that, in order not to overlap, would
> +   imply that the size of the referenced object(s) exceeds the maximum
> +   size of an object.  Set SIZRANGE to the range of access sizes,
> +   OVLOFF to the offsets where the overlap occurs (the offsets must
> +   be valid so that their sum with the low bound of the access size
> +   is no greater than PTRDIFF_MAX), and OVLSIZ to the rane of the overlap
> +   sizes.
> +   Otherwise, if THIS and SRCREF do not definitely overlap (even though
> +   they may overlap in a way that's not apparent from the available data),
> +   return false.
> +   Used for -Wrestrict warnings.  */
s/rane/range/


> +
> +bool
> +builtin_access::overlap ()
> +{
> +


> +
> +  /* When the lower bound of the offset is less that the upper bound
> +     disregard	it and use the inverse of the maximum object size
> +     instead.  The upper bound is the result of a negative offset
> +     being represented as a large positive value.  */
It looks like you've got an embedded tab  in that comment after "disregard".





> +/* Attempt to detect and diagnose invalid offset bounds and (exept for
> +   memmove) overlapping copy in a call expression EXP from SRC to DST
> +   and DSTSIZE and SRCSIZE bytes, respectively.  Return false when one
> +   or the other has been detected, true otherwise.  */
s/exept/except/





> diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
> index cb33c1e..048a2e4 100644
> --- a/gcc/gimple-fold.c
> +++ b/gcc/gimple-fold.c
> @@ -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);
> +
And one could argue that this should just be folded away in a manner
similar to the others below.  I'd support adding a case for this
independently of the warnings.


Not an ACK or NACK at this point.

Jeff
Martin Sebor Nov. 26, 2017, 12:53 a.m. UTC | #2
On 11/22/2017 04:50 PM, Jeff Law wrote:
> On 11/16/2017 02:29 PM, Martin Sebor wrote:
>> Ping.
>>
>> I've fixed the outstanding false positive exposed by the Linux
>> kernel.  The kernel builds with four instances of the warning,
>> all of them valid (perfect overlap in memcpy).
>>
>> I also built Glibc.  It shows one instance of the warning, also
>> a true positive (cause by calling a restrict-qualified function
>> with two copies of the same argument).
>>
>> Finally, I built Binutils and GDB with no warnings.
>>
>> The attached patch includes just that one fix, with everything
>> else being the same.
>>
>> On 11/09/2017 04:57 PM, Martin Sebor wrote:
>>> Ping:
>>>
>>>   https://gcc.gnu.org/ml/gcc-patches/2017-10/msg01642.html
>>>
>>> On 10/23/2017 08:42 PM, Martin Sebor wrote:
>>>> Attached is a reworked solution to enhance -Wrestrict while
>>>> avoiding changing tree-vrp.c or any other VRP machinery.  Richard,
>>>> in considering you suggestions I realized that the ao_ref struct
>>>> isn't general enough to detect the kinds of problems I needed to
>>>> etect (storing bit-offsets in HOST_WIDE_INT means out-of-bounds
>>>> offsets cannot be represented or detected, leading to either false
>>>> positives or false negatives).
> So this seems to be a recurring theme, which makes me wonder if we
> should have an ao_ref-like structure that deals in bytes rather than
> bits and make it a first class citizen.   There's certainly clients that
> work on bits and certainly clients that would prefer to work on bytes.

The class I introduced serves a different purpose than ao_ref and
stores a lot more data.

In https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82042#c3 Richard
says that "this [offset being HOST_WIDE_INT and storing bits] is
a know deficiency in ao_ref 'offset' (and also size and maxsize).
Blowing up to offset_int isn't really a good idea."

>>>> Instead, the solution adds a couple of small classes to builtins.c
>>>> to overcome this limitation (I'm contemplating moving them along
>>>> with -Wstringop-overflow to a separate file to keep builtins.c
>>>> from getting too much bigger).
>>>>
>>>> The detection of out-of-bounds offsets and overlapping accesses
>>>> is relatively simple but the rest of the changes are somewhat
>>>> involved because of the computation of the offsets and sizes of
>>>> the overlaps.
>>>>
>>>> Jeff, as per your suggestion/request in an earlier review (bug
>>>> 81117) I've renamed some of the existing functions to better
>>>> reflect their new function (including renaming strlen_optimize_stmt
>>>> in tree-ssa-strlen.c to strlen_check_and_optimize_stmt).  There's
>>>> quite a bit of churn due to some of this renaming.  I don't think
>>>> this renaming makes the review too difficult but if you feel
>>>> differently I can [be persuaded to] split it out into a separate
>>>> patch.
> Understood.  FWIW, one way to deal with this that I've found works
> reasonably well is to have an initial patch that just does the renaming
> you want to do.  That separates the mechanical stuff from the meat of
> the change.  They can be git squashed together just before committing or
> committed as two changes back-to-back to eliminate or minimize the time
> where the variable and function names are inconsistent.
>
> It's also the case that independent little fixes should just go upstream
> immediately.  I didn't see many, but the alloc_max_size fix for KB seems
> like it should have just gone forward independent of the rest of the
> changes.

Sure, I think that change was incidental, not intentional.  There
should be no others (AFAIK).

>>>> To validate the patch I compiled the Linux kernel and Binutils/GDB.
>>>> There's one false positive I'm working on resolving that's caused
>>>> by an incorrect interpretation of an offset in a range whose lower
>>>> bound is greater than its upper bound.  This it so say that while
>>>> I'm aware the patch isn't perfect it already works reasonably well
>>>> in practice and I think it's close enough to review.
>>>>
>>>> Thanks
>>>> Martin
>>>
>>
>>
>> gcc-78918.diff
>>
>>
>> PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over self
>>
>> gcc/c-family/ChangeLog:
>>
>> 	PR tree-optimization/78918
>> 	* c-common.c (check_function_restrict): Avoid checking built-ins.
>> 	* c.opt (-Wrestrict): Include in -Wall.
>>
>> gcc/ChangeLog:
>>
>> 	PR tree-optimization/78918
>> 	* builtins.c (builtin_memref, builtin_access): New classes.
>> 	(check_bounds_or_overlap, maybe_diag_overlap): New static functions.
>> 	(maybe_diag_offset_bounds): Same.
>> 	(check_sizes): Rename...
>> 	(check_access): ...to this.  Rename function arguments for clarity.
>> 	(check_memop_sizes): Adjust names.
>> 	(expand_builtin_memchr, expand_builtin_memcpy): Same.
>> 	(expand_builtin_memmove, expand_builtin_mempcpy): Same.
>> 	(expand_builtin_strcat, expand_builtin_stpncpy): Same.
>> 	(check_strncat_sizes, expand_builtin_strncat): Same.
>> 	(expand_builtin_strncpy, expand_builtin_memset): Same.
>> 	(expand_builtin_bzero, expand_builtin_memcmp): Same.
>> 	(expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
>> 	(maybe_emit_sprintf_chk_warning): Same.
>> 	(expand_builtin_strcpy): Adjust.
>> 	(expand_builtin_stpcpy): Same.
>> 	(expand_builtin_with_bounds): Detect out-of-bounds/overlapping
>> 	accesses in pointer-checking forms of memcpy, memmove, and mempcpy.
>> 	(gcall_to_tree_minimal, check_bounds_or_overlap, max_object_size):
>> 	Define new functions.
>> 	* builtins.h (check_bounds_or_overlap, max_object_size): Declare.
>> 	* calls.c (alloc_max_size): Call max_object_size instead of
>> 	hardcoding ssizetype limit.  Fix a typo.
>> 	(get_size_range): Handle new argument.
>> 	* calls.h (get_size_range): Add a new argument.
>> 	* cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
>> 	* doc/invoke.texi (-Wrestrict): Adjust, add example.
>> 	* gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
>> 	operations.
>> 	(gimple_fold_builtin_memory_chk): Same.
>> 	(gimple_fold_builtin_stxcpy_chk): New function.
>> 	* gimple.c (gimple_build_call_from_tree): Propagate location.
>> 	* tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
>> 	operations.
>> 	(handle_builtin_strcat): Same.
>> 	(strlen_optimize_stmt): Rename...
>> 	(strlen_check_and_optimize_stmt): ...to this.  Handle strncat,
>> 	stpncpy, strncpy, and their checking forms.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 	PR tree-optimization/78918
>> 	* c-c++-common/Warray-bounds.c: New test.
>> 	* c-c++-common/Wrestrict-2.c: New test.
>> 	* c-c++-common/Wrestrict.c: New test.
>> 	* c-c++-common/Wrestrict.s: New test.
>> 	* gcc.dg/Walloca-1.c: Adjust/
>> 	* gcc.dg/memcpy-6.c: New test.
>> 	* gcc.dg/pr69172.c: Adjust.
>> 	* gcc.target/i386/chkp-stropt-17.c: New test.
> So I realize you don't have any code to answer this question, but your
> thoughts on how much we might loose effectiveness if we didn't do the
> warnings within gimple_fold_builtin_<whatever>, but instead broke out a
> distinct pass to handle warnings?  My biggest design concern is the
> warning from within the folder aspects.

With optimization enabled the folder folds things like this into
MEM_REF which would prevent the warning unless the pass pass ran
with early optimizations.

   struct S { char a[7], b[7]; };

   void sink (void*);

   void f (void)
   {
     struct S s;
     sink (&s);

     unsigned n = sizeof s.a;
     memcpy (s.a + 4, s.b, n);

     sink (&s);
   }

Without optimization it isn't folded and so the memcpy call is
emitted (but there is no warning).  The overlapping MEM_REF copy
is safe but the overlapping memcpy call is not, so warning on it
is helpful.

I prototyped a pass over Thanksgiving for the -Wrestict code from
builtins.c to and ran it just after the sprintf pass.  There are
still lots of failures in the new test because it's non-trivial
to compute the same data as builtins.c does (the data is also
computed for -Wstringop-overflow).  I could move all that code
to the new pass as well to clear up the failures.  I could also
arrange for the pass to run multiple times to catch cases like
the one above.  I suspect this would trigger some false negatives
and/or positives due to the differences in range information, so
it would mean some cleanup in the tests.  What I can't do without
seriously compromising the feature is avoid calling into the new
pass from tree-ssa-strlen.c, but that would presumably be fine.

With that said, and although I'm not necessarily opposed to it,
moving all this code into its own pass would mean a non-trivial
amount of work for what seems like a questionable benefit.  All
it would achieve, as far as I can see, is duplicating some of
the work that's already been done: iterating over the GIMPLE,
testing for built-ins to handle, and extracting their arguments.
What exactly do you hope to accomplish by moving it into its own
pass?  (If it's a matter of keeping the warning code separate
that can easily be done by moving it to its own file.)

> Are there any interactions with the issues we touched on at the end of
> our call Monday?  ie, in the case where there is no overlap, but the
> strncat/strncpy overwrites the NUL in the source string, we we issue a
> warning.  Do those issues arise in this code?

The -Wrestrict warning is more strict for string functions than
for raw memory functions and when it can't rule out overlap for
string copies into the same object, it warns.  (For raw memory
functions, it only warns when it can prove there is an overlap.)
Other than that, there are no interactions and the -Wrestrict
code doesn't enable/disable any optimizations.

> FWIW I have some concerns about how this code is going to interact with
> the poly_int stuff.  The code which finds and extracts pointer offsets
> in particular.  I'm sure we can adjust if that's necessary (one of the
> meta comments I need to make sure I note in the poly_int thread is a
> request for guidance WRT when code needs to be poly_int aware).
>
>> @@ -192,6 +193,91 @@ static tree do_mpfr_remquo (tree, tree, tree);
>>  static tree do_mpfr_lgamma_r (tree, tree, tree);
>>  static void expand_builtin_sync_synchronize (void);
>>
>> +struct builtin_memref;
>> +
>> +/* Description of a memory access by a raw memory or string built-in
>> +   function.  */
>> +struct builtin_access
>> +{
>> +private:
>> +  /* Temporaries used to compute the final result.  */
>> +  offset_int dstoff[2];
>> +  offset_int srcoff[2];
>> +  offset_int dstsiz[2];
>> +  offset_int srcsiz[2];
>> +
>> +  /* Member function to call to determine overlap.  */
>> +  bool (builtin_access::*detect_overlap) ();
>
> A nit.  For a POD type we use structs, otherwise make it a class.
> Similarly for the builtin_memref class.
>
> Check your ordering of members in builtin_memref.
>
>
> Any reason not to define builtin_memref prior to builtin_access?

Not anymore.  At one point there was a circular dependency between
the two that I have eliminated.

> So I realize why you're not using the ao_ref infrastructure.  However,
> have you reviewed the ao_ref bits to help ensure that builtin_memref
> handles any various corner cases.  Are there pieces you can refactor and
> share between the two pieces of infrastructure?  If I just look at the
> ctor I see a ton of stuff that looks like fairly generic infrastructure
> to take a memory reference an dig down into it to find the base and
> offset.  I'd hate to have two completely different implementations of
> that code with different bugs in each :(

The only similarity between builtin_memref and ao_ref is between
the ctor and ao_ref_init_from_ptr_and_size.  The ctor is a bit more
involved because it deals with non-constant offsets.  But I'm not
aware of any corner cases in ao_ref_init_from_ptr_and_size (the
ctor started as a copy of the function and evolved to what it looks
like now).

Martin

>> @@ -2962,39 +3048,157 @@ determine_block_size (tree len, rtx len_rtx,
>>  			  GET_MODE_MASK (GET_MODE (len_rtx)));
>>  }
>>
>> +/* Validate REF offsets in an EXPRession passed as an argument to a CALL
>> +   to a built-in function FUNC to make sure they are within the bounds
>> +   of the referenced object if its size is known, or PTRDIFF_MAX otherwise.
>> +   Both initial values of the offsets and their final value computed by
>> +   the function by incrementing the initial value by the size are
>> +   validated.  Return true if the offsets are not valid and a diagnostic
>> +   has been isssued.  */
> s/isssued/issued/
>
>
>
>
>
>
>> +}
>> +
>> +/* Return error_mark_node if the signed offset is exceeds the bounds
>> +   of the address space (PTRDIFF_MAX).  Otherwise, return BASE when
>> +   the offset exceeds the bounds of the BASE object. Otherwise return
>> +   NULL to inidctae the offset is in bounds.  */
> s/inidctae/indicate/
>
>
>
>
>> +
>> +
>> +/* Return true if THIS (AKA DSTREF) and SRCREF describe accesses that
>> +   either overlap one another or that, in order not to overlap, would
>> +   imply that the size of the referenced object(s) exceeds the maximum
>> +   size of an object.  Set SIZRANGE to the range of access sizes,
>> +   OVLOFF to the offsets where the overlap occurs (the offsets must
>> +   be valid so that their sum with the low bound of the access size
>> +   is no greater than PTRDIFF_MAX), and OVLSIZ to the rane of the overlap
>> +   sizes.
>> +   Otherwise, if THIS and SRCREF do not definitely overlap (even though
>> +   they may overlap in a way that's not apparent from the available data),
>> +   return false.
>> +   Used for -Wrestrict warnings.  */
> s/rane/range/
>
>
>> +
>> +bool
>> +builtin_access::overlap ()
>> +{
>> +
>
>
>> +
>> +  /* When the lower bound of the offset is less that the upper bound
>> +     disregard	it and use the inverse of the maximum object size
>> +     instead.  The upper bound is the result of a negative offset
>> +     being represented as a large positive value.  */
> It looks like you've got an embedded tab  in that comment after "disregard".
>
>
>
>
>
>> +/* Attempt to detect and diagnose invalid offset bounds and (exept for
>> +   memmove) overlapping copy in a call expression EXP from SRC to DST
>> +   and DSTSIZE and SRCSIZE bytes, respectively.  Return false when one
>> +   or the other has been detected, true otherwise.  */
> s/exept/except/
>
>
>
>
>
>> diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
>> index cb33c1e..048a2e4 100644
>> --- a/gcc/gimple-fold.c
>> +++ b/gcc/gimple-fold.c
>> @@ -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);
>> +
> And one could argue that this should just be folded away in a manner
> similar to the others below.  I'd support adding a case for this
> independently of the warnings.
>
>
> Not an ACK or NACK at this point.
>
> Jeff
>
Richard Biener Nov. 27, 2017, 12:44 p.m. UTC | #3
On Thu, Nov 16, 2017 at 10:29 PM, Martin Sebor <msebor@gmail.com> wrote:
> Ping.
>
> I've fixed the outstanding false positive exposed by the Linux
> kernel.  The kernel builds with four instances of the warning,
> all of them valid (perfect overlap in memcpy).
>
> I also built Glibc.  It shows one instance of the warning, also
> a true positive (cause by calling a restrict-qualified function
> with two copies of the same argument).
>
> Finally, I built Binutils and GDB with no warnings.
>
> The attached patch includes just that one fix, with everything
> else being the same.

+             /* Detect invalid bounds and overlapping copies and issue
+                either -Warray-bounds or -Wrestrict.  */
+             if (check_bounds_or_overlap (stmt, dest, src, len, len))
+               gimple_set_no_warning (stmt, true);

if (! gimple_no_warning (stmt)
    && ...)

to avoid repeated work if this call doesn't get folded.

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

please don't do unrelated whitespace changes.

+
+  if (const strinfo *chksi = olddsi ? olddsi : dsi)
+    if (si
+       && !check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
+      /* Avoid transforming strcpy when out-of-bounds offsets or
+        overlapping access is detected.  */
+      return;

as I said elsewhere diagnostics should not prevent optimization.  Your warning
code isn't optimization-grade (that is, false positives are possible).

+       if (!check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
+         /* Avoid transforming strcat when out-of-bounds offsets or
+            overlapping access is detected.  */
+         return;
+      }

Likewise.

+      if (!check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
+         /* Avoid transforming strcat when out-of-bounds offsets or
+            overlapping access is detected.  */
+       return;

Likewise.

I have no strong opinion against the "code duplication" Jeff mentions with
regarding to builtin_access and friends.  The relation to ao_ref and friends
could be documented a bit and how builtin_memref/builtin_access are
not suitable for optimization.

Thanks,
Richard.

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

While beefing up the tests I found a few minor issues that
I also fixed (false negatives).

The fallout wasn't quite as bad as I thought, mainly thanks
to the narrow API for the checker.

Syncing up with the latest trunk has led to some more changes
in tree-ssa-strlen.

I've retested the patch with GDB and Glibc with the same results
as before.

The patch seems sizable (over 3KLOC without tests) but it's worth
noting that most of the complexity is actually not in determining
whether or not an overlap exists (that's quite simple) but rather
in computing its offset and size to mention in the warnings and
making sure the information is meaningful to the user even when
ranges are involved.  All the subtly different forms of warnings
also contribute substantially to the overall size.

Martin

On 11/25/2017 05:53 PM, Martin Sebor wrote:
> On 11/22/2017 04:50 PM, Jeff Law wrote:
>> On 11/16/2017 02:29 PM, Martin Sebor wrote:
>>> Ping.
>>>
>>> I've fixed the outstanding false positive exposed by the Linux
>>> kernel.  The kernel builds with four instances of the warning,
>>> all of them valid (perfect overlap in memcpy).
>>>
>>> I also built Glibc.  It shows one instance of the warning, also
>>> a true positive (cause by calling a restrict-qualified function
>>> with two copies of the same argument).
>>>
>>> Finally, I built Binutils and GDB with no warnings.
>>>
>>> The attached patch includes just that one fix, with everything
>>> else being the same.
>>>
>>> On 11/09/2017 04:57 PM, Martin Sebor wrote:
>>>> Ping:
>>>>
>>>>   https://gcc.gnu.org/ml/gcc-patches/2017-10/msg01642.html
>>>>
>>>> On 10/23/2017 08:42 PM, Martin Sebor wrote:
>>>>> Attached is a reworked solution to enhance -Wrestrict while
>>>>> avoiding changing tree-vrp.c or any other VRP machinery.  Richard,
>>>>> in considering you suggestions I realized that the ao_ref struct
>>>>> isn't general enough to detect the kinds of problems I needed to
>>>>> etect (storing bit-offsets in HOST_WIDE_INT means out-of-bounds
>>>>> offsets cannot be represented or detected, leading to either false
>>>>> positives or false negatives).
>> So this seems to be a recurring theme, which makes me wonder if we
>> should have an ao_ref-like structure that deals in bytes rather than
>> bits and make it a first class citizen.   There's certainly clients that
>> work on bits and certainly clients that would prefer to work on bytes.
>
> The class I introduced serves a different purpose than ao_ref and
> stores a lot more data.
>
> In https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82042#c3 Richard
> says that "this [offset being HOST_WIDE_INT and storing bits] is
> a know deficiency in ao_ref 'offset' (and also size and maxsize).
> Blowing up to offset_int isn't really a good idea."
>
>>>>> Instead, the solution adds a couple of small classes to builtins.c
>>>>> to overcome this limitation (I'm contemplating moving them along
>>>>> with -Wstringop-overflow to a separate file to keep builtins.c
>>>>> from getting too much bigger).
>>>>>
>>>>> The detection of out-of-bounds offsets and overlapping accesses
>>>>> is relatively simple but the rest of the changes are somewhat
>>>>> involved because of the computation of the offsets and sizes of
>>>>> the overlaps.
>>>>>
>>>>> Jeff, as per your suggestion/request in an earlier review (bug
>>>>> 81117) I've renamed some of the existing functions to better
>>>>> reflect their new function (including renaming strlen_optimize_stmt
>>>>> in tree-ssa-strlen.c to strlen_check_and_optimize_stmt).  There's
>>>>> quite a bit of churn due to some of this renaming.  I don't think
>>>>> this renaming makes the review too difficult but if you feel
>>>>> differently I can [be persuaded to] split it out into a separate
>>>>> patch.
>> Understood.  FWIW, one way to deal with this that I've found works
>> reasonably well is to have an initial patch that just does the renaming
>> you want to do.  That separates the mechanical stuff from the meat of
>> the change.  They can be git squashed together just before committing or
>> committed as two changes back-to-back to eliminate or minimize the time
>> where the variable and function names are inconsistent.
>>
>> It's also the case that independent little fixes should just go upstream
>> immediately.  I didn't see many, but the alloc_max_size fix for KB seems
>> like it should have just gone forward independent of the rest of the
>> changes.
>
> Sure, I think that change was incidental, not intentional.  There
> should be no others (AFAIK).
>
>>>>> To validate the patch I compiled the Linux kernel and Binutils/GDB.
>>>>> There's one false positive I'm working on resolving that's caused
>>>>> by an incorrect interpretation of an offset in a range whose lower
>>>>> bound is greater than its upper bound.  This it so say that while
>>>>> I'm aware the patch isn't perfect it already works reasonably well
>>>>> in practice and I think it's close enough to review.
>>>>>
>>>>> Thanks
>>>>> Martin
>>>>
>>>
>>>
>>> gcc-78918.diff
>>>
>>>
>>> PR tree-optimization/78918 - missing -Wrestrict on memcpy copying
>>> over self
>>>
>>> gcc/c-family/ChangeLog:
>>>
>>>     PR tree-optimization/78918
>>>     * c-common.c (check_function_restrict): Avoid checking built-ins.
>>>     * c.opt (-Wrestrict): Include in -Wall.
>>>
>>> gcc/ChangeLog:
>>>
>>>     PR tree-optimization/78918
>>>     * builtins.c (builtin_memref, builtin_access): New classes.
>>>     (check_bounds_or_overlap, maybe_diag_overlap): New static functions.
>>>     (maybe_diag_offset_bounds): Same.
>>>     (check_sizes): Rename...
>>>     (check_access): ...to this.  Rename function arguments for clarity.
>>>     (check_memop_sizes): Adjust names.
>>>     (expand_builtin_memchr, expand_builtin_memcpy): Same.
>>>     (expand_builtin_memmove, expand_builtin_mempcpy): Same.
>>>     (expand_builtin_strcat, expand_builtin_stpncpy): Same.
>>>     (check_strncat_sizes, expand_builtin_strncat): Same.
>>>     (expand_builtin_strncpy, expand_builtin_memset): Same.
>>>     (expand_builtin_bzero, expand_builtin_memcmp): Same.
>>>     (expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
>>>     (maybe_emit_sprintf_chk_warning): Same.
>>>     (expand_builtin_strcpy): Adjust.
>>>     (expand_builtin_stpcpy): Same.
>>>     (expand_builtin_with_bounds): Detect out-of-bounds/overlapping
>>>     accesses in pointer-checking forms of memcpy, memmove, and mempcpy.
>>>     (gcall_to_tree_minimal, check_bounds_or_overlap, max_object_size):
>>>     Define new functions.
>>>     * builtins.h (check_bounds_or_overlap, max_object_size): Declare.
>>>     * calls.c (alloc_max_size): Call max_object_size instead of
>>>     hardcoding ssizetype limit.  Fix a typo.
>>>     (get_size_range): Handle new argument.
>>>     * calls.h (get_size_range): Add a new argument.
>>>     * cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
>>>     * doc/invoke.texi (-Wrestrict): Adjust, add example.
>>>     * gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
>>>     operations.
>>>     (gimple_fold_builtin_memory_chk): Same.
>>>     (gimple_fold_builtin_stxcpy_chk): New function.
>>>     * gimple.c (gimple_build_call_from_tree): Propagate location.
>>>     * tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
>>>     operations.
>>>     (handle_builtin_strcat): Same.
>>>     (strlen_optimize_stmt): Rename...
>>>     (strlen_check_and_optimize_stmt): ...to this.  Handle strncat,
>>>     stpncpy, strncpy, and their checking forms.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>>     PR tree-optimization/78918
>>>     * c-c++-common/Warray-bounds.c: New test.
>>>     * c-c++-common/Wrestrict-2.c: New test.
>>>     * c-c++-common/Wrestrict.c: New test.
>>>     * c-c++-common/Wrestrict.s: New test.
>>>     * gcc.dg/Walloca-1.c: Adjust/
>>>     * gcc.dg/memcpy-6.c: New test.
>>>     * gcc.dg/pr69172.c: Adjust.
>>>     * gcc.target/i386/chkp-stropt-17.c: New test.
>> So I realize you don't have any code to answer this question, but your
>> thoughts on how much we might loose effectiveness if we didn't do the
>> warnings within gimple_fold_builtin_<whatever>, but instead broke out a
>> distinct pass to handle warnings?  My biggest design concern is the
>> warning from within the folder aspects.
>
> With optimization enabled the folder folds things like this into
> MEM_REF which would prevent the warning unless the pass pass ran
> with early optimizations.
>
>   struct S { char a[7], b[7]; };
>
>   void sink (void*);
>
>   void f (void)
>   {
>     struct S s;
>     sink (&s);
>
>     unsigned n = sizeof s.a;
>     memcpy (s.a + 4, s.b, n);
>
>     sink (&s);
>   }
>
> Without optimization it isn't folded and so the memcpy call is
> emitted (but there is no warning).  The overlapping MEM_REF copy
> is safe but the overlapping memcpy call is not, so warning on it
> is helpful.
>
> I prototyped a pass over Thanksgiving for the -Wrestict code from
> builtins.c to and ran it just after the sprintf pass.  There are
> still lots of failures in the new test because it's non-trivial
> to compute the same data as builtins.c does (the data is also
> computed for -Wstringop-overflow).  I could move all that code
> to the new pass as well to clear up the failures.  I could also
> arrange for the pass to run multiple times to catch cases like
> the one above.  I suspect this would trigger some false negatives
> and/or positives due to the differences in range information, so
> it would mean some cleanup in the tests.  What I can't do without
> seriously compromising the feature is avoid calling into the new
> pass from tree-ssa-strlen.c, but that would presumably be fine.
>
> With that said, and although I'm not necessarily opposed to it,
> moving all this code into its own pass would mean a non-trivial
> amount of work for what seems like a questionable benefit.  All
> it would achieve, as far as I can see, is duplicating some of
> the work that's already been done: iterating over the GIMPLE,
> testing for built-ins to handle, and extracting their arguments.
> What exactly do you hope to accomplish by moving it into its own
> pass?  (If it's a matter of keeping the warning code separate
> that can easily be done by moving it to its own file.)
>
>> Are there any interactions with the issues we touched on at the end of
>> our call Monday?  ie, in the case where there is no overlap, but the
>> strncat/strncpy overwrites the NUL in the source string, we we issue a
>> warning.  Do those issues arise in this code?
>
> The -Wrestrict warning is more strict for string functions than
> for raw memory functions and when it can't rule out overlap for
> string copies into the same object, it warns.  (For raw memory
> functions, it only warns when it can prove there is an overlap.)
> Other than that, there are no interactions and the -Wrestrict
> code doesn't enable/disable any optimizations.
>
>> FWIW I have some concerns about how this code is going to interact with
>> the poly_int stuff.  The code which finds and extracts pointer offsets
>> in particular.  I'm sure we can adjust if that's necessary (one of the
>> meta comments I need to make sure I note in the poly_int thread is a
>> request for guidance WRT when code needs to be poly_int aware).
>>
>>> @@ -192,6 +193,91 @@ static tree do_mpfr_remquo (tree, tree, tree);
>>>  static tree do_mpfr_lgamma_r (tree, tree, tree);
>>>  static void expand_builtin_sync_synchronize (void);
>>>
>>> +struct builtin_memref;
>>> +
>>> +/* Description of a memory access by a raw memory or string built-in
>>> +   function.  */
>>> +struct builtin_access
>>> +{
>>> +private:
>>> +  /* Temporaries used to compute the final result.  */
>>> +  offset_int dstoff[2];
>>> +  offset_int srcoff[2];
>>> +  offset_int dstsiz[2];
>>> +  offset_int srcsiz[2];
>>> +
>>> +  /* Member function to call to determine overlap.  */
>>> +  bool (builtin_access::*detect_overlap) ();
>>
>> A nit.  For a POD type we use structs, otherwise make it a class.
>> Similarly for the builtin_memref class.
>>
>> Check your ordering of members in builtin_memref.
>>
>>
>> Any reason not to define builtin_memref prior to builtin_access?
>
> Not anymore.  At one point there was a circular dependency between
> the two that I have eliminated.
>
>> So I realize why you're not using the ao_ref infrastructure.  However,
>> have you reviewed the ao_ref bits to help ensure that builtin_memref
>> handles any various corner cases.  Are there pieces you can refactor and
>> share between the two pieces of infrastructure?  If I just look at the
>> ctor I see a ton of stuff that looks like fairly generic infrastructure
>> to take a memory reference an dig down into it to find the base and
>> offset.  I'd hate to have two completely different implementations of
>> that code with different bugs in each :(
>
> The only similarity between builtin_memref and ao_ref is between
> the ctor and ao_ref_init_from_ptr_and_size.  The ctor is a bit more
> involved because it deals with non-constant offsets.  But I'm not
> aware of any corner cases in ao_ref_init_from_ptr_and_size (the
> ctor started as a copy of the function and evolved to what it looks
> like now).
>
> Martin
>
>>> @@ -2962,39 +3048,157 @@ determine_block_size (tree len, rtx len_rtx,
>>>                GET_MODE_MASK (GET_MODE (len_rtx)));
>>>  }
>>>
>>> +/* Validate REF offsets in an EXPRession passed as an argument to a
>>> CALL
>>> +   to a built-in function FUNC to make sure they are within the bounds
>>> +   of the referenced object if its size is known, or PTRDIFF_MAX
>>> otherwise.
>>> +   Both initial values of the offsets and their final value computed by
>>> +   the function by incrementing the initial value by the size are
>>> +   validated.  Return true if the offsets are not valid and a
>>> diagnostic
>>> +   has been isssued.  */
>> s/isssued/issued/
>>
>>
>>
>>
>>
>>
>>> +}
>>> +
>>> +/* Return error_mark_node if the signed offset is exceeds the bounds
>>> +   of the address space (PTRDIFF_MAX).  Otherwise, return BASE when
>>> +   the offset exceeds the bounds of the BASE object. Otherwise return
>>> +   NULL to inidctae the offset is in bounds.  */
>> s/inidctae/indicate/
>>
>>
>>
>>
>>> +
>>> +
>>> +/* Return true if THIS (AKA DSTREF) and SRCREF describe accesses that
>>> +   either overlap one another or that, in order not to overlap, would
>>> +   imply that the size of the referenced object(s) exceeds the maximum
>>> +   size of an object.  Set SIZRANGE to the range of access sizes,
>>> +   OVLOFF to the offsets where the overlap occurs (the offsets must
>>> +   be valid so that their sum with the low bound of the access size
>>> +   is no greater than PTRDIFF_MAX), and OVLSIZ to the rane of the
>>> overlap
>>> +   sizes.
>>> +   Otherwise, if THIS and SRCREF do not definitely overlap (even though
>>> +   they may overlap in a way that's not apparent from the available
>>> data),
>>> +   return false.
>>> +   Used for -Wrestrict warnings.  */
>> s/rane/range/
>>
>>
>>> +
>>> +bool
>>> +builtin_access::overlap ()
>>> +{
>>> +
>>
>>
>>> +
>>> +  /* When the lower bound of the offset is less that the upper bound
>>> +     disregard    it and use the inverse of the maximum object size
>>> +     instead.  The upper bound is the result of a negative offset
>>> +     being represented as a large positive value.  */
>> It looks like you've got an embedded tab  in that comment after
>> "disregard".
>>
>>
>>
>>
>>
>>> +/* Attempt to detect and diagnose invalid offset bounds and (exept for
>>> +   memmove) overlapping copy in a call expression EXP from SRC to DST
>>> +   and DSTSIZE and SRCSIZE bytes, respectively.  Return false when one
>>> +   or the other has been detected, true otherwise.  */
>> s/exept/except/
>>
>>
>>
>>
>>
>>> diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c
>>> index cb33c1e..048a2e4 100644
>>> --- a/gcc/gimple-fold.c
>>> +++ b/gcc/gimple-fold.c
>>> @@ -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);
>>> +
>> And one could argue that this should just be folded away in a manner
>> similar to the others below.  I'd support adding a case for this
>> independently of the warnings.
>>
>>
>> Not an ACK or NACK at this point.
>>
>> Jeff
>>
>
PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over self

gcc/c-family/ChangeLog:

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

gcc/ChangeLog:

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

gcc/testsuite/ChangeLog:

	PR tree-optimization/78918
	* c-c++-common/Warray-bounds.c: New test.
	* c-c++-common/Warray-bounds-2.c: New test.
	* c-c++-common/Warray-bounds-3.c: New test.
	* c-c++-common/Wrestrict-2.c: New test.
	* c-c++-common/Wrestrict.c: New test.
	* c-c++-common/Wrestrict.s: New test.
	* c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust
	* c-c++-common/Wsizeof-pointer-memaccess2.c: Same.
	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
	* g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
	* gcc.dg/memcpy-6.c: New test.
	* gcc.dg/pr69172.c: Adjust.
	* gcc.dg/pr79223.c: Same.
	* gcc.dg/Wrestrict-2.c: New test.
	* gcc.dg/Wrestrict.c: New test.
	* gcc.dg/Wsizeof-pointer-memaccess1.c
	* gcc.target/i386/chkp-stropt-17.c: New test.
	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Adjust.

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index c428eaa..35839d5 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1316,6 +1316,7 @@ OBJS = \
 	gimple-ssa-strength-reduction.o \
 	gimple-ssa-sprintf.o \
 	gimple-ssa-warn-alloca.o \
+	gimple-ssa-warn-restrict.o \
 	gimple-streamer-in.o \
 	gimple-streamer-out.o \
 	gimple-walk.o \
diff --git a/gcc/builtins.c b/gcc/builtins.c
index b974459..62ade43 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "alias.h"
 #include "fold-const.h"
 #include "fold-const-call.h"
+#include "gimple-ssa-warn-restrict.h"
 #include "stor-layout.h"
 #include "calls.h"
 #include "varasm.h"
@@ -3008,37 +3009,45 @@ determine_block_size (tree len, rtx len_rtx,
 
 /* 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.
+   Arguments other than EXP may be null.  When non-null, the arguments
+   have the following meaning:
+   DST is the destination of a copy call or NULL otherwise.
+   SRC is the source of a copy call or NULL otherwise.
+   DSTWRITE is the number of bytes written into the destination obtained
+   from the user-supplied size argument to the function (such as in
+   memcpy(DST, SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+   MAXREAD is the user-supplied bound on the length of the source sequence
    (such as in strncat(d, s, N).  It specifies the upper limit on the number
-   of bytes to write.
-   SRC is the source string (such as in strcpy(d, s)) when the expression
-   EXP is a string function call (as opposed to a memory call like memcpy).
-   As an exception, SRC can also be an integer denoting the precomputed
-   size of the source string or object (for functions like memcpy).
-   OBJSIZE is the size of the destination object specified by the last
+   of bytes to write.  If NULL, it's taken to be the same as DSTWRITE.
+   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+   expression EXP is a string function call (as opposed to a memory call
+   like memcpy).  As an exception, SRCSTR can also be an integer denoting
+   the precomputed size of the source string or object (for functions like
+   memcpy).
+   DSTSIZE is the size of the destination object specified by the last
    argument to the _chk builtins, typically resulting from the expansion
-   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
-   OBJSIZE).
+   of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
+   DSTSIZE).
 
-   When SIZE is null LEN is checked to verify that it doesn't exceed
+   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
-   If the call is successfully verified as safe from buffer overflow
-   the function returns true, otherwise false..  */
+   If the call is successfully verified as safe return true, otherwise
+   return false.  */
 
 static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_access (tree exp, tree, tree, tree dstwrite,
+	      tree maxread, tree srcstr, tree dstsize)
 {
+  int opt = OPT_Wstringop_overflow_;
+
   /* The size of the largest object is half the address space, or
-     SSIZE_MAX.  (This is way too permissive.)  */
-  tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
+     PTRDIFF_MAX.  (This is way too permissive.)  */
+  tree maxobjsize = max_object_size ();
 
+  /* Either the length of the source string for string functions or
+     the size of the source object for raw memory functions.  */
   tree slen = NULL_TREE;
 
   tree range[2] = { NULL_TREE, NULL_TREE };
@@ -3047,28 +3056,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);
@@ -3082,10 +3091,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.  */
@@ -3093,20 +3102,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.  */
@@ -3119,30 +3126,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
@@ -3150,7 +3161,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,
@@ -3159,21 +3170,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;
@@ -3183,11 +3194,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);
@@ -3201,40 +3216,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);
@@ -3244,20 +3260,20 @@ 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;
     }
 
@@ -3330,11 +3346,8 @@ compute_objsize (tree dest, int ostype)
    (no overflow or invalid sizes), false otherwise.  */
 
 static bool
-check_memop_sizes (tree exp, tree dest, tree src, tree size)
+check_memop_access (tree exp, tree dest, tree src, tree size)
 {
-  if (!warn_stringop_overflow)
-    return true;
-
   /* For functions like memset and memcpy that operate on raw memory
      try to determine the size of the largest source and destination
      object using type-0 Object Size regardless of the object size
@@ -3342,8 +3355,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size)
   tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
   tree dstsize = compute_objsize (dest, 0);
 
-  return check_sizes (OPT_Wstringop_overflow_, exp,
-		      size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+  return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
+		       srcsize, dstsize);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -3364,9 +3377,8 @@ expand_builtin_memchr (tree exp, rtx)
   if (warn_stringop_overflow)
     {
       tree size = compute_objsize (arg1, 0);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, len, /*maxlen=*/NULL_TREE,
-		   size, /*objsize=*/NULL_TREE);
+      check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		    /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
     }
 
   return NULL_RTX;
@@ -3388,7 +3400,7 @@ expand_builtin_memcpy (tree exp, rtx target)
   tree src = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, src, len);
+  check_memop_access (exp, dest, src, len);
 
   return expand_builtin_memory_copy_args (dest, src, len, target, exp,
 					  /*endp=*/ 0);
@@ -3408,7 +3420,7 @@ expand_builtin_memmove (tree exp, rtx)
   tree src = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, src, len);
+  check_memop_access (exp, dest, src, len);
 
   return NULL_RTX;
 }
@@ -3467,7 +3479,7 @@ expand_builtin_mempcpy (tree exp, rtx target)
   /* Avoid expanding mempcpy into memcpy when the call is determined
      to overflow the buffer.  This also prevents the same overflow
      from being diagnosed again when expanding memcpy.  */
-  if (!check_memop_sizes (exp, dest, src, len))
+  if (!check_memop_access (exp, dest, src, len))
     return NULL_RTX;
 
   return expand_builtin_mempcpy_args (dest, src, len,
@@ -3673,8 +3685,8 @@ expand_builtin_strcat (tree exp, rtx)
 
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+  check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+		destsize);
 
   return NULL_RTX;
 }
@@ -3696,8 +3708,8 @@ expand_builtin_strcpy (tree exp, rtx target)
   if (warn_stringop_overflow)
     {
       tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+      check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		    src, destsize);
     }
 
   return expand_builtin_strcpy_args (dest, src, target);
@@ -3735,8 +3747,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
   if (warn_stringop_overflow)
     {
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+      check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		    src, destsize);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -3819,8 +3831,7 @@ expand_builtin_stpncpy (tree exp, rtx)
   /* The size of the destination object.  */
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 
   return NULL_RTX;
 }
@@ -3850,7 +3861,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.  */
@@ -3874,32 +3885,32 @@ check_strncat_sizes (tree exp, tree objsize)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (objsize)
-      && tree_int_cst_equal (objsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return false;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
+  /* The number of bytes to write is LEN but check_access will also
      check SRCLEN if LEN's value isn't known.  */
-  return check_sizes (OPT_Wstringop_overflow_,
-		      exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+  return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+		       objsize);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -3917,7 +3928,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);
 
@@ -3940,52 +3951,34 @@ expand_builtin_strncat (tree exp, rtx)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN bytes and always appends the terminating
+  /* Strncat copies at most MAXREAD bytes and always appends the terminating
      nul so the specified upper bound should never be equal to (or greater
      than) the size of the destination.  */
-  if (tree_fits_uhwi_p (maxlen) && tree_fits_uhwi_p (destsize)
-      && tree_int_cst_equal (destsize, maxlen))
+  if (tree_fits_uhwi_p (maxread) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return NULL_RTX;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
-     check SRCLEN if LEN's value isn't known.  */
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+  /* The number of bytes to write is SRCLEN.  */
+  check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize);
 
   return NULL_RTX;
 }
 
-/* Helper to check the sizes of sequences and the destination of calls
-   to __builtin_strncpy (DST, SRC, CNT) and __builtin___strncpy_chk.
-   Returns true on success (no overflow warning), false otherwise.  */
-
-static bool
-check_strncpy_sizes (tree exp, tree dst, tree src, tree cnt)
-{
-  tree dstsize = compute_objsize (dst, warn_stringop_overflow - 1);
-
-  if (!check_sizes (OPT_Wstringop_overflow_,
-		    exp, cnt, /*maxlen=*/NULL_TREE, src, dstsize))
-    return false;
-
-  return true;
-}
-
 /* Expand expression EXP, which is a call to the strncpy builtin.  Return
    NULL_RTX if we failed the caller should emit a normal call.  */
 
@@ -4004,7 +3997,16 @@ expand_builtin_strncpy (tree exp, rtx target)
       /* The length of the source sequence.  */
       tree slen = c_strlen (src, 1);
 
-      check_strncpy_sizes (exp, dest, src, len);
+      if (warn_stringop_overflow)
+	{
+	  tree destsize = compute_objsize (dest,
+					   warn_stringop_overflow - 1);
+
+	  /* The number of bytes to write is LEN but check_access will also
+	     check SLEN if LEN's value isn't known.  */
+	  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
+			destsize);
+	}
 
       /* We must be passed a constant len and src parameter.  */
       if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
@@ -4098,7 +4100,7 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
   tree val = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, NULL_TREE, len);
+  check_memop_access (exp, dest, NULL_TREE, len);
 
   return expand_builtin_memset_args (dest, val, len, target, mode, exp);
 }
@@ -4287,7 +4289,7 @@ expand_builtin_bzero (tree exp)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree size = CALL_EXPR_ARG (exp, 1);
 
-  check_memop_sizes (exp, dest, NULL_TREE, size);
+  check_memop_access (exp, dest, NULL_TREE, size);
 
   /* New argument list transforming bzero(ptr x, int y) to
      memset(ptr x, int 0, size_t y).   This is done this way
@@ -4346,14 +4348,12 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
   if (warn_stringop_overflow)
     {
       tree size = compute_objsize (arg1, 0);
-      if (check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE))
+      if (check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+			/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
 	{
 	  size = compute_objsize (arg2, 0);
-	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE);
+	  check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+			/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
 	}
     }
 
@@ -7731,6 +7731,23 @@ expand_builtin_with_bounds (tree exp, rtx target,
 	return target;
       break;
 
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMPCPY_CHKP:
+      if (call_expr_nargs (exp) > 3)
+	{
+	  /* memcpy_chkp (void *dst, size_t dstbnd,
+	                  const void *src, size_t srcbnd, size_t n)
+  	     and others take a pointer bound argument just after each
+	     pointer argument.  */
+	  tree dest = CALL_EXPR_ARG (exp, 0);
+	  tree src = CALL_EXPR_ARG (exp, 2);
+	  tree len = CALL_EXPR_ARG (exp, 4);
+
+	  check_memop_access (exp, dest, src, len);
+	  break;
+	}
+
     default:
       break;
     }
@@ -9748,8 +9765,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
@@ -9757,14 +9772,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
-  len = CALL_EXPR_ARG (exp, 2);
-  size = CALL_EXPR_ARG (exp, 3);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 3);
 
-  bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
-			       exp, len, /*maxlen=*/NULL_TREE,
-			       /*str=*/NULL_TREE, size);
+  bool sizes_ok = check_access (exp, dest, src, len, /*maxread=*/NULL_TREE,
+				/*str=*/NULL_TREE, size);
 
   if (!tree_fits_uhwi_p (size))
     return NULL_RTX;
@@ -9873,7 +9887,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)
     {
@@ -9894,27 +9908,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
@@ -9923,8 +9937,10 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       return;
     }
 
-  check_sizes (OPT_Wstringop_overflow_, exp,
-	       /*size=*/NULL_TREE, maxlen, srcstr, objsize);
+  /* The destination argument is the first one for all built-ins above.  */
+  tree dst = CALL_EXPR_ARG (exp, 0);
+
+  check_access (exp, dst, srcstr, /*size=*/NULL_TREE, maxread, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9980,8 +9996,9 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 
   /* Add one for the terminating nul.  */
   len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
+
+  check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
+		/*maxread=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
@@ -10611,3 +10628,12 @@ target_char_cst_p (tree t, char *p)
   *p = (char)tree_to_uhwi (t);
   return true;
 }
+
+/* Return the maximum object size.  */
+
+tree
+max_object_size (void)
+{
+  /* To do: Make this a configurable parameter.  */
+  return TYPE_MAX_VALUE (ptrdiff_type_node);
+}
diff --git a/gcc/builtins.h b/gcc/builtins.h
index cf3fc17..7f34d29 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -103,4 +103,6 @@ extern bool target_char_cst_p (tree t, char *p);
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
 
-#endif
+extern tree max_object_size ();
+
+#endif /* GCC_BUILTINS_H */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 9970e61..ac25891 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5267,14 +5267,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 d857dfc..665e834 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1174,7 +1174,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall)
 Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier.
 
 Wrestrict
-C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++)
+C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
 Warn when an argument passed to a restrict-qualified parameter aliases with
 another argument.
 
diff --git a/gcc/calls.c b/gcc/calls.c
index 8ae9899..56c7fb6 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "builtins.h"
 
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
 #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -1194,7 +1195,7 @@ alloc_max_size (void)
 {
   if (!alloc_object_size_limit)
     {
-      alloc_object_size_limit = TYPE_MAX_VALUE (ssizetype);
+      alloc_object_size_limit = max_object_size ();
 
       if (warn_alloc_size_limit)
 	{
@@ -1213,7 +1214,7 @@ alloc_max_size (void)
 		     suffixes.  */
 		  if (!strcmp (end, "kB"))
 		    unit = 1000;
-		  else if (!strcasecmp (end, "KiB") || strcmp (end, "KB"))
+		  else if (!strcasecmp (end, "KiB") || !strcmp (end, "KB"))
 		    unit = 1024;
 		  else if (!strcmp (end, "MB"))
 		    unit = HOST_WIDE_INT_UC (1000) * 1000;
@@ -1245,7 +1246,8 @@ alloc_max_size (void)
 		{
 		  widest_int w = wi::mul (limit, unit);
 		  if (w < wi::to_widest (alloc_object_size_limit))
-		    alloc_object_size_limit = wide_int_to_tree (ssizetype, w);
+		    alloc_object_size_limit
+		      = wide_int_to_tree (ptrdiff_type_node, w);
 		}
 	    }
 	}
@@ -1254,13 +1256,17 @@ alloc_max_size (void)
 }
 
 /* Return true when EXP's range can be determined and set RANGE[] to it
-   after adjusting it if necessary to make EXP a valid size argument to
-   an allocation function declared with attribute alloc_size (whose
-   argument may be signed), or to a string manipulation function like
-   memset.  */
+   after adjusting it if necessary to make EXP a represents a valid size
+   of object, or a valid size argument to an allocation function declared
+   with attribute alloc_size (whose argument may be signed), or to a string
+   manipulation function like memset.  When ALLOW_ZERO is true, allow
+   returning a range of [0, 0] for a size in an anti-range [1, N] where
+   N > PTRDIFF_MAX.  A zero range is a (nearly) invalid argument to
+   allocation functions like malloc but it is a valid argument to
+   functions like memset.  */
 
 bool
-get_size_range (tree exp, tree range[2])
+get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
 {
   if (tree_fits_uhwi_p (exp))
     {
@@ -1269,20 +1275,33 @@ get_size_range (tree exp, tree range[2])
       return true;
     }
 
+  tree exptype = TREE_TYPE (exp);
+  bool integral = INTEGRAL_TYPE_P (exptype);
+
   wide_int min, max;
-  enum value_range_type range_type
-    = ((TREE_CODE (exp) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (exp)))
-       ? get_range_info (exp, &min, &max) : VR_VARYING);
+  enum value_range_type range_type;
+
+  if (TREE_CODE (exp) == SSA_NAME && integral)
+    range_type = get_range_info (exp, &min, &max);
+  else
+    range_type = VR_VARYING;
 
   if (range_type == VR_VARYING)
     {
-      /* No range information available.  */
+      if (integral)
+	{
+	  /* Use the full range of the type of the expression when
+	     no value range information is available.  */
+	  range[0] = TYPE_MIN_VALUE (exptype);
+	  range[1] = TYPE_MAX_VALUE (exptype);
+	  return true;
+	}
+
       range[0] = NULL_TREE;
       range[1] = NULL_TREE;
       return false;
     }
 
-  tree exptype = TREE_TYPE (exp);
   unsigned expprec = TYPE_PRECISION (exptype);
 
   bool signed_p = !TYPE_UNSIGNED (exptype);
@@ -1320,11 +1339,16 @@ get_size_range (tree exp, tree range[2])
 	{
 	  /* EXP is unsigned and not in the range [1, MAX].  That means
 	     it's either zero or greater than MAX.  Even though 0 would
-	     normally be detected by -Walloc-zero set the range to
-	     [MAX, TYPE_MAX] so that when MAX is greater than the limit
-	     the whole range is diagnosed.  */
-	  min = max + 1;
-	  max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	     normally be detected by -Walloc-zero, unless ALLOW_ZERO
+	     is true, set the range to [MAX, TYPE_MAX] so that when MAX
+	     is greater than the limit the whole range is diagnosed.  */
+	  if (allow_zero)
+	    min = max = wi::zero (expprec);
+	  else
+	    {
+	      min = max + 1;
+	      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	    }
 	}
       else
 	{
diff --git a/gcc/calls.h b/gcc/calls.h
index 9b7fa9a..641166e 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -38,8 +38,8 @@ extern bool pass_by_reference (CUMULATIVE_ARGS *, machine_mode,
 extern bool reference_callee_copied (CUMULATIVE_ARGS *, machine_mode,
 				     tree, bool);
 extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
-extern bool get_size_range (tree, tree[2]);
 extern tree get_attr_nonstring_decl (tree, tree * = NULL);
 extern void maybe_warn_nonstring_arg (tree, tree);
+extern bool get_size_range (tree, tree[2], bool = false);
 
 #endif // GCC_CALLS_H
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index d114060..53fe502 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2636,6 +2636,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 02a0c1e..bc7dd8e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3880,6 +3880,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
@@ -6699,11 +6700,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++, a @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 353a46e..632ef32 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ssa.h"
 #include "cgraph.h"
 #include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
 #include "fold-const.h"
 #include "stmt.h"
 #include "expr.h"
@@ -662,13 +663,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,
@@ -680,6 +680,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))
@@ -703,6 +709,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));
@@ -752,6 +767,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	  unsigned ilen = tree_to_uhwi (len);
 	  if (pow2p_hwi (ilen))
 	    {
+	      /* Detect invalid bounds and overlapping copies and issue
+		 either -Warray-bounds or -Wrestrict.  */
+	      if (!nowarn && check_bounds_or_overlap (stmt, dest, src, len, len))
+	      	gimple_set_no_warning (stmt, true);
+
 	      scalar_int_mode mode;
 	      tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
 	      if (type
@@ -1024,6 +1044,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	    }
 	}
 
+      /* Detect invalid bounds and overlapping copies and issue either
+	 -Warray-bounds or -Wrestrict.  */
+      if (!nowarn)
+	check_bounds_or_overlap (stmt, dest, src, len, len);
+
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
 	{
@@ -1388,7 +1413,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:
@@ -1480,12 +1505,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;
     }
@@ -2386,6 +2418,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);
@@ -2487,6 +2528,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-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c
new file mode 100644
index 0000000..5a58cae
--- /dev/null
+++ b/gcc/gimple-ssa-warn-restrict.c
@@ -0,0 +1,1686 @@
+/* Warn on violations of the restrict qualifier.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Contributed by Martin Sebor <msebor@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "tree-pass.h"
+#include "builtins.h"
+#include "ssa.h"
+#include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
+#include "diagnostic-core.h"
+#include "fold-const.h"
+#include "gimple-iterator.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
+#include "params.h"
+#include "tree-cfg.h"
+#include "tree-object-size.h"
+#include "calls.h"
+#include "cfgloop.h"
+#include "intl.h"
+
+const pass_data pass_data_wrestrict = {
+  GIMPLE_PASS,
+  "wrestrict",
+  OPTGROUP_NONE,
+  TV_NONE,
+  PROP_cfg, /* Properties_required.  */
+  0,	    /* properties_provided.  */
+  0,	    /* properties_destroyed.  */
+  0,	    /* properties_start */
+  0,	    /* properties_finish */
+};
+
+/* Pass to detect violations of strict aliasing requirements in calls
+   to built-in string and raw memory functions.  */
+class pass_wrestrict : public gimple_opt_pass
+{
+ public:
+  pass_wrestrict (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_wrestrict, ctxt)
+    { }
+
+  opt_pass *clone () { return new pass_wrestrict (m_ctxt); }
+
+  virtual bool gate (function *);
+  virtual unsigned int execute (function *);
+
+ private:
+  void check_call (gcall *);
+};
+
+bool
+pass_wrestrict::gate (function *fun ATTRIBUTE_UNUSED)
+{
+  return warn_array_bounds != 0 || warn_restrict != 0;
+}
+
+unsigned
+pass_wrestrict::execute (function *fun)
+{
+  basic_block bb;
+  FOR_EACH_BB_FN (bb, fun)
+    {
+      for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);
+	   gsi_next (&si))
+	{
+	  gimple *stmt = gsi_stmt (si);
+	  if (!is_gimple_call (stmt))
+	    continue;
+
+	  if (gcall *call = as_a <gcall *> (stmt))
+	    check_call (call);
+	}
+    }
+
+  return 0;
+}
+
+/* Description of a memory reference by a built-in function.  This
+   is similar to ao_ref but made especially suitable for -Wrestrict
+   and not for optimization.  */
+struct builtin_memref
+{
+  /* The original pointer argument to the built-in function.  */
+  tree ptr;
+  /* The referenced subobject or NULL if not available, and the base
+     object of the memory reference or NULL.  */
+  tree ref;
+  tree base;
+
+  /* The size of the BASE object, PTRDIFF_MAX if indeterminate, and
+     -1 until (lazily) initialized.  */
+  offset_int objsize;
+
+  /* The offset of the referenced subobject.  Used to avoid warnings for
+     (apparently) possibly but not definitively overlapping accesses to
+     member arrays.  */
+  offset_int refoff;
+
+  /* The offset range relative to the base.  */
+  offset_int offrange[2];
+  /* The size range of the access.  */
+  offset_int sizrange[2];
+
+  /* True for "bounded" string functions like strncat, and strncpy
+     and their variants that specify either an exact or upper bound
+     on the size of the accesses they perform.  For strncat both
+     the source and destination references are bounded.  For strncpy
+     only the destination reference is.  */
+  bool strbounded_p;
+
+  builtin_memref (tree, tree);
+
+  tree offset_out_of_bounds (offset_int[2]) const;
+};
+
+/* Description of a memory access by a raw memory or string built-in
+   function involving a pair of builtin_memref's.  */
+class builtin_access
+{
+public:
+  /* Destination and source memory reference.  */
+  builtin_memref* const dstref;
+  builtin_memref* const srcref;
+  /* The minimum and maximum size of the access. */
+  HOST_WIDE_INT sizrange[2];
+
+  /* The minimum and maximum offset of an overlap of the access
+     (if it does, in fact, overlap), and the size of the overlap.  */
+  HOST_WIDE_INT ovloff[2];
+  HOST_WIDE_INT ovlsiz[2];
+
+  builtin_access (tree, builtin_memref &, builtin_memref &);
+
+  /* Entry point to determine overlap.  */
+  bool overlap ();
+
+private:
+  /* Implementation functions used to determine overlap.  */
+  bool generic_overlap ();
+  bool strcat_overlap ();
+  bool strcpy_overlap ();
+
+  bool no_overlap ()
+  {
+    return false;
+  }
+
+private:
+  /* Temporaries used to compute the final result.  */
+  offset_int dstoff[2];
+  offset_int srcoff[2];
+  offset_int dstsiz[2];
+  offset_int srcsiz[2];
+
+  /* Pointer to a member function to call to determine overlap.  */
+  bool (builtin_access::*detect_overlap) ();
+};
+
+/* Initialize a memory reference representation from a pointer EXPR and
+   a size SIZE in bytes.  If SIZE is NULL_TREE then the size is assumed
+   to be unknown.  */
+
+builtin_memref::builtin_memref (tree expr, tree size)
+: ptr (expr),
+  ref (),
+  base (),
+  objsize (-1),
+  refoff (HOST_WIDE_INT_MIN),
+  offrange (),
+  sizrange (),
+  strbounded_p ()
+{
+  /* Unfortunately, wide_int default ctor is a no-op so array members
+     of the type must be set individually.  */
+  offrange[0] = offrange[1] = 0;
+  sizrange[0] = sizrange[1] = 0;
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  if (TREE_CODE (expr) == SSA_NAME)
+    {
+      /* Try to tease the offset out of the pointer.  */
+      gimple *stmt = SSA_NAME_DEF_STMT (expr);
+      if (gimple_assign_single_p (stmt)
+	  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+	expr = gimple_assign_rhs1 (stmt);
+      else if (is_gimple_assign (stmt))
+	{
+	  tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == NOP_EXPR)
+	    {
+	      tree rhs = gimple_assign_rhs1 (stmt);
+	      if (POINTER_TYPE_P (TREE_TYPE (rhs)))
+		expr = gimple_assign_rhs1 (stmt);
+	    }
+	  else if (code == POINTER_PLUS_EXPR)
+	    {
+	      expr = gimple_assign_rhs1 (stmt);
+
+	      tree offset = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (offset) == INTEGER_CST)
+		{
+		  offset_int off = int_cst_value (offset);
+		  offrange[0] = off;
+		  offrange[1] = off;
+
+		  if (TREE_CODE (expr) == SSA_NAME)
+		    {
+		      gimple *stmt = SSA_NAME_DEF_STMT (expr);
+		      if (gimple_assign_single_p (stmt)
+			  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+			expr = gimple_assign_rhs1 (stmt);
+		    }
+		}
+	      else if (TREE_CODE (offset) == SSA_NAME)
+		{
+		  wide_int min, max;
+		  value_range_type rng = get_range_info (offset, &min, &max);
+		  if (rng == VR_RANGE)
+		    {
+		      offrange[0] = min.to_shwi ();
+		      offrange[1] = max.to_shwi ();
+		    }
+		  else if (rng == VR_ANTI_RANGE)
+		    {
+		      offrange[0] = (max + 1).to_shwi ();
+		      offrange[1] = (min - 1).to_shwi ();
+		    }
+		  else
+		    {
+		      gimple *stmt = SSA_NAME_DEF_STMT (offset);
+		      if (is_gimple_assign (stmt)
+			  && gimple_assign_rhs_code (stmt) == NOP_EXPR)
+			{
+			  /* Use the bounds of the type of the NOP_EXPR operand
+			     even if it's signed.  The result doesn't trigger
+			     warnings but makes their output more readable.  */
+			  tree type = TREE_TYPE (gimple_assign_rhs1 (stmt));
+			  offrange[0] = wi::to_offset (TYPE_MIN_VALUE (type));
+			  offrange[1] = wi::to_offset (TYPE_MAX_VALUE (type));
+			}
+		      else
+			offrange[1] = maxobjsize;
+		    }
+		}
+	      else
+		offrange[1] = maxobjsize;
+	    }
+	}
+    }
+
+  if (TREE_CODE (expr) == ADDR_EXPR)
+    {
+      HOST_WIDE_INT off;
+      tree oper = TREE_OPERAND (expr, 0);
+
+      /* Determine the base object or pointer of the reference
+	 and its constant offset from the beginning of the base.  */
+      base = get_addr_base_and_unit_offset (oper, &off);
+
+      if (base)
+	{
+	  offrange[0] += off;
+	  offrange[1] += off;
+
+	  /* Stash the reference for offset validation. */
+	  ref = oper;
+
+	  /* Also stash the constant offset for offset validation.  */
+	  tree_code code = TREE_CODE (oper);
+	  if (code == ARRAY_REF || code == COMPONENT_REF)
+	    {
+	      if (code == COMPONENT_REF)
+		{
+		  tree field = TREE_OPERAND (ref, 1);
+		  tree fldoff = DECL_FIELD_OFFSET (field);
+		  if (TREE_CODE (fldoff) == INTEGER_CST)
+		    refoff = (wi::to_offset (fldoff)
+			      + (wi::to_offset (DECL_FIELD_BIT_OFFSET (field))
+				 >> LOG2_BITS_PER_UNIT));
+		}
+	    }
+	  else if (code == MEM_REF)
+	    {
+	      oper = TREE_OPERAND (ref, 0);
+	      tree_code code = TREE_CODE (oper);
+	      if (code == ADDR_EXPR || code == SSA_NAME)
+		{
+		  tree type = TREE_TYPE (oper);
+		  if (POINTER_TYPE_P (type))
+		    {
+		      /* Take the size of the referenced type as
+			 the referenced object's size.  Set its offset
+			 to HWI_MAX here as a flag so it can be reset
+			 to zero at the end of this function.  */
+		      refoff = HOST_WIDE_INT_MAX;
+		    }
+		}
+	    }
+	}
+      else
+	{
+	  size = NULL_TREE;
+	  base = get_base_address (TREE_OPERAND (expr, 0));
+	}
+    }
+
+  if (!base)
+    base = build2 (MEM_REF, char_type_node, expr, null_pointer_node);
+
+  if (TREE_CODE (base) == MEM_REF)
+    {
+      offset_int off = mem_ref_offset (base);
+      refoff += off;
+      offrange[0] += off;
+      offrange[1] += off;
+      base = TREE_OPERAND (base, 0);
+    }
+
+  if (TREE_CODE (base) == SSA_NAME)
+    if (gimple *stmt = SSA_NAME_DEF_STMT (base))
+      {
+	enum gimple_code code = gimple_code (stmt);
+	if (code == GIMPLE_ASSIGN)
+	  if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+	    {
+	      base = gimple_assign_rhs1 (stmt);
+
+	      tree offset = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (offset) == INTEGER_CST)
+		{
+		  offset_int off = int_cst_value (offset);
+		  refoff += off;
+		  offrange[0] += off;
+		  offrange[1] += off;
+		}
+	    }
+
+	if (TREE_CODE (base) == SSA_NAME && SSA_NAME_VAR (base))
+	  base = SSA_NAME_VAR (base);
+      }
+
+  if (size)
+    {
+      tree range[2];
+      /* Determine the size range, allowing for the result to be [0, 0]
+	 for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX.  */
+      get_size_range (size, range, true);
+      sizrange[0] = wi::to_offset (range[0]);
+      sizrange[1] = wi::to_offset (range[1]);
+      /* get_size_range returns SIZE_MAX for the maximum size.
+	 Constrain it to the real maximum of PTRDIFF_MAX.  */
+      if (sizrange[1] > maxobjsize)
+	sizrange[1] = maxobjsize;
+    }
+  else
+    sizrange[1] = maxobjsize;
+
+  /* Reset the reference offset to zero (see above).  */
+  if (refoff >= HOST_WIDE_INT_MAX)
+    refoff = 0;
+}
+
+/* Return error_mark_node if the signed offset exceeds the bounds
+   of the address space (PTRDIFF_MAX).  Otherwise, return BASE when
+   the offset exceeds the bounds of the BASE object, and set OOBOFF
+   to the past-the-end offset formed by the reference, including
+   its size.  Otherwise return NULL to indicate the offset is in
+   bounds.  */
+
+tree
+builtin_memref::offset_out_of_bounds (offset_int ooboff[2]) const
+{
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  /* A temporary, possibly adjusted, copy of the offset range.  */
+  offset_int offrng[2] = { offrange[0], offrange[1] };
+
+  if (DECL_P (base) && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE)
+    {
+      if (offrng[1] < offrng[0])
+  	offrng[1] = maxobjsize;
+    }
+
+  /* Conservative offset of the last byte of the referenced object.  */
+  offset_int endoff;
+
+  /* The bounds need not be ordered.  Set HIB to use as the index
+     of the larger of the bounds and LOB as the opposite.  */
+  bool hib = wi::les_p (offrng[0], offrng[1]);
+  bool lob = !hib;
+
+  if (objsize < 0)
+    {
+      endoff = offrng[lob] + sizrange[0];
+
+      /* For a reference through a pointer to an object of unknown size
+	 all initial offsets are considered valid, positive as well as
+	 negative, since the pointer itself can point past the beginning
+	 of the object.  However, the sum of the lower bound of the offset
+	 and that of the size must be less than or equal than PTRDIFF_MAX.  */
+      if (endoff > maxobjsize)
+	return error_mark_node;
+
+      return NULL_TREE;
+    }
+
+  /* A reference to an object of known size must be within the bounds
+     of the base object.  */
+  if (offrng[hib] < 0 || offrng[lob] > objsize)
+    return base;
+
+  /* The extent of the reference must also be within the bounds of
+     the base object (if known) or the maximum object size otherwise.  */
+  endoff = wi::smax (offrng[lob], 0) + sizrange[0];
+  if (endoff > maxobjsize)
+    return error_mark_node;
+
+  if (endoff <= objsize)
+    return NULL_TREE;
+
+  /* Set the out-of-bounds offset range to be one greater than
+     that delimited by the reference including its size.  */
+  ooboff[lob] = objsize + 1;
+
+  if (endoff > ooboff[lob])
+    ooboff[hib] = endoff;
+  else
+    ooboff[hib] = wi::smax (offrng[lob], 0) + sizrange[1];
+
+  return base;
+}
+
+/* Create an association between the memory references DST and SRC
+   for access by a call EXP to a memory or string built-in funtion.  */
+
+builtin_access::builtin_access (tree exp, builtin_memref &dst,
+				builtin_memref &src)
+: dstref (&dst), srcref (&src), sizrange (), ovloff (), ovlsiz (),
+  dstoff (), srcoff (), dstsiz (), srcsiz ()
+{
+  /* Zero out since the offset_int ctors invoked above are no-op.  */
+  dstoff[0] = dstoff[1] = 0;
+  srcoff[0] = srcoff[1] = 0;
+  dstsiz[0] = dstsiz[1] = 0;
+  srcsiz[0] = srcsiz[1] = 0;
+
+  /* Object Size Type to use to determine the size of the destination
+     and source objects.  Overridden below for raw memory functions.  */
+  int ostype = 1;
+
+  /* True when the size of one reference depends on the offset of
+     itself or the other.  */
+  bool depends_p;
+
+  /* True when the size of the destination reference DSTREF has been
+     determined from SRCREF and so needs to be adjusted by the latter's
+     offset.  Only meaningful for bounded string functions like strncpy.  */
+  bool dstadjust_p = false;
+
+  /* The size argument number (depends on the built-in).  */
+  unsigned sizeargno = 2;
+  if (CALL_WITH_BOUNDS_P (exp))
+    sizeargno += 2;
+
+  tree func = get_callee_fndecl (exp);
+  switch (DECL_FUNCTION_CODE (func))
+    {
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMCPY_CHK_CHKP:
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHKP:
+    case BUILT_IN_MEMPCPY_CHK_CHKP:
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::generic_overlap;
+      break;
+
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMMOVE_CHK_CHKP:
+      /* For memmove there is never any overlap to check for.  */
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::no_overlap;
+      break;
+
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      depends_p = true;
+      dstref->strbounded_p = true;
+      detect_overlap = &builtin_access::generic_overlap;
+      break;
+
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STPCPY_CHKP:
+    case BUILT_IN_STPCPY_CHK_CHKP:
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STRCPY_CHKP:
+    case BUILT_IN_STRCPY_CHK_CHKP:
+      depends_p = true;
+      detect_overlap = &builtin_access::strcpy_overlap;
+      break;
+
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRCAT_CHKP:
+    case BUILT_IN_STRCAT_CHK_CHKP:
+      depends_p = true;
+      detect_overlap = &builtin_access::strcat_overlap;
+      break;
+
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+      depends_p = true;
+      dstref->strbounded_p = true;
+      srcref->strbounded_p = true;
+      detect_overlap = &builtin_access::strcat_overlap;
+      break;
+
+    default:
+      /* Handle other string functions here whose access may need
+	 to be validated for in-bounds offsets and non-overlapping
+	 copies.  (Not all _chkp functions have BUILT_IN_XXX_CHKP
+	 macros so they need to be handled here.)  */
+      return;
+    }
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  /* Try to determine the size of the base object.  compute_objsize
+     expects a pointer so create one if BASE is a non-pointer object.  */
+  tree addr = dst.base;
+  if (!POINTER_TYPE_P (TREE_TYPE (addr)))
+    addr = build1 (ADDR_EXPR, (TREE_TYPE (addr)), addr);
+
+  if (tree dstsize = compute_objsize (addr, ostype))
+    dst.objsize = wi::to_offset (dstsize);
+  else if (POINTER_TYPE_P (TREE_TYPE (addr)))
+    dst.objsize = HOST_WIDE_INT_MIN;
+  else
+    dst.objsize = maxobjsize;
+
+  addr = src.base;
+  if (!POINTER_TYPE_P (TREE_TYPE (addr)))
+    addr = build1 (ADDR_EXPR, (TREE_TYPE (addr)), addr);
+
+  if (tree srcsize = compute_objsize (addr, ostype))
+    src.objsize = wi::to_offset (srcsize);
+  else if (POINTER_TYPE_P (TREE_TYPE (addr)))
+    src.objsize = HOST_WIDE_INT_MIN;
+  else
+    dst.objsize = maxobjsize;
+
+  /* If there is no dependency between the references or the base
+     objects of the two references aren't the same there's nothing
+     else to do.  */
+  if (depends_p && dstref->base != srcref->base)
+    return;
+
+  /* ...otherwise, make adjustments for references to the same object
+     by string functions to reflect the constraints imposed by the called
+     built-in function.  */
+
+  /* For bounded string functions determine the range of the bound
+     on the access.  */
+  offset_int bounds[2] = { 0, 0 };
+  if (dstref->strbounded_p)
+    {
+      tree size = CALL_EXPR_ARG (exp, sizeargno);
+      tree range[2];
+      if (get_size_range (size, range, true))
+	{
+	  bounds[0] = wi::to_offset (range[0]);
+	  bounds[1] = wi::to_offset (range[1]);
+	}
+
+      /* If both references' size ranges are indeterminate use the last
+	 (size) argument from the function call as a substitute.  This
+	 may only be necessary for strncpy (but not for memcpy where
+	 the size range would have been already determined this way).  */
+      if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize
+	  && srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+	{
+	  dstref->sizrange[0] = bounds[0];
+	  dstref->sizrange[1] = bounds[1];
+	}
+    }
+
+  /* The size range of one reference involving the same base object
+     can be determined from the size range of the other reference.
+     This makes it possible to compute accurate offsets for warnings
+     involving functions like strcpy where the length of just one of
+     the two arguments is known (determined by tree-ssa-strlen).  */
+  if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize)
+    {
+      /* When the destination size is unknown set it to the size of
+	 the source.  */
+      dstref->sizrange[0] = srcref->sizrange[0];
+      dstref->sizrange[1] = srcref->sizrange[1];
+    }
+  else if (srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+    {
+      /* When the source size is unknown set it to the size of
+	 the destination.  */
+      srcref->sizrange[0] = dstref->sizrange[0];
+      srcref->sizrange[1] = dstref->sizrange[1];
+
+      if (depends_p)
+	{
+	  if (dstref->strbounded_p)
+	    {
+	      /* Strncpy read access is bounded.  */
+	      if (bounds[0] < srcref->sizrange[0])
+		srcref->sizrange[0] = bounds[0];
+	      if (bounds[1] < srcref->sizrange[1])
+		srcref->sizrange[1] = bounds[1];
+	    }
+
+	  /* For string functions, adjust the size range of the source
+	     reference by the inverse boundaries of the offset (because
+	     the higher  the offset into the string the shorter its
+	     length).  */
+	  if (srcref->offrange[1] < srcref->sizrange[0])
+	    srcref->sizrange[0] -= srcref->offrange[1];
+	  else
+	    srcref->sizrange[0] = 0;
+
+	  if (srcref->offrange[0] > 0)
+	    {
+	      if (srcref->offrange[0] < srcref->sizrange[1])
+		srcref->sizrange[1] -= srcref->offrange[0];
+	      else
+		srcref->sizrange[1] = 0;
+	    }
+
+	  dstadjust_p = true;
+	}
+    }
+
+  if (detect_overlap == &builtin_access::generic_overlap)
+    {
+      if (dstref->strbounded_p)
+	{
+	  dstref->sizrange[0] = bounds[0];
+	  dstref->sizrange[1] = bounds[1];
+
+	  if (dstref->sizrange[0] < srcref->sizrange[0])
+	    srcref->sizrange[0] = dstref->sizrange[0];
+
+	  if (dstref->sizrange[1] < srcref->sizrange[1])
+	    srcref->sizrange[1] = dstref->sizrange[1];
+	}
+    }
+  else if (detect_overlap == &builtin_access::strcpy_overlap)
+    {
+      /* For strcpy, adjust the destination size range to match that
+	 of the source computed above.  */
+      if (depends_p && dstadjust_p)
+	{
+	  dstref->sizrange[0] = srcref->sizrange[0];
+	  dstref->sizrange[1] = srcref->sizrange[1];
+	}
+    }
+}
+
+/* Return true if the bounded mempry (memcpy amd similar) or string function
+   access (strncpy and similar) ACS overlaps.  */
+
+bool
+builtin_access::generic_overlap ()
+{
+  builtin_access &acs = *this;
+  const builtin_memref *dstref = acs.dstref;
+  const builtin_memref *srcref = acs.srcref;
+
+  gcc_assert (dstref->base == srcref->base);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  offset_int maxsize = dstref->objsize < 0 ? maxobjsize : dstref->objsize;
+  gcc_assert (maxsize <= maxobjsize);
+
+  /* Adjust the larger bounds of the offsets (which may be the first
+     element if the lower bound is larger than the upper bound) to
+     make them valid for the smallest access (if possible) but no smaller
+     than the smaller bounds.  */
+  bool dstord = wi::les_p (acs.dstoff[0], acs.dstoff[1]);
+  if (maxsize < acs.dstoff[dstord] + acs.dstsiz[0])
+    acs.dstoff[dstord] = maxsize - acs.dstsiz[0];
+  if (acs.dstoff[dstord] < acs.dstoff[0])
+    acs.dstoff[dstord] = acs.dstoff[0];
+
+  bool srcord = wi::les_p (acs.srcoff[0], acs.srcoff[1]);
+  if (maxsize < acs.srcoff[srcord] + acs.srcsiz[0])
+    acs.srcoff[srcord] = maxsize - acs.srcsiz[0];
+  if (acs.srcoff[srcord] < acs.srcoff[0])
+    acs.srcoff[srcord] = acs.srcoff[0];
+
+  /* Determine the minimum and maximum space for the access given
+     the offsets.  */
+  offset_int space[2];
+  space[0] = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+  space[1] = space[0];
+
+  offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+  if (acs.srcsiz[0] > 0)
+    {
+      if (d < space[0])
+	space[0] = d;
+
+      if (space[1] < d)
+	space[1] = d;
+    }
+  else
+    space[1] = acs.dstsiz[1];
+
+  d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+  if (d < space[0])
+    space[0] = d;
+
+  if (space[1] < d)
+    space[1] = d;
+
+  /* Treat raw memory functions both of whose references are bounded
+     as special and permit uncertain overlaps to go undetected.  For
+     all kinds of constant offset and constant size accesses, if
+     overlap isn't certain it is not possible.  */
+  bool overlap_possible = space[0] < acs.dstsiz[1];
+  if (!overlap_possible)
+    return false;
+
+  bool overlap_certain = space[1] < acs.dstsiz[0];
+
+  if (!overlap_certain
+      && !dstref->strbounded_p
+      && detect_overlap == &builtin_access::generic_overlap)
+    return false;
+
+  /* True for stpcpy and strcpy.  */
+  bool stxcpy_p = detect_overlap == &builtin_access::strcpy_overlap;
+
+  /* True when the size of the source sequence is inveresely proportional
+     to the offset into it, such as in strcpy (DST, SRC + OFF).  */
+  bool sizdep_p = (stxcpy_p
+		   && acs.srcoff[0] != acs.srcoff[1]
+		   && acs.sizrange[0] != acs.sizrange[1]);
+
+  if (dstref->refoff >= 0
+      && srcref->refoff >= 0
+      && dstref->refoff != srcref->refoff
+      && (stxcpy_p || dstref->strbounded_p || srcref->strbounded_p))
+    return false;
+
+  /* The number of bytes written.  */
+  offset_int write_min = acs.dstsiz[0];
+  offset_int write_max = acs.dstsiz[1];
+
+  if (acs.dstoff[0] <= acs.srcoff[0])
+    {
+      /* The lower bound of the offset of the first reference is less
+	 than the lower bound of the second.  */
+      if (acs.dstoff[1] <= acs.srcoff[1])
+	{
+	  /* The upper bound of the offset of the first reference is less
+	     than the upper bound of the second.
+	     The size of the overlap is the size of the access minus
+	     the difference between the upper bound of the second offset
+	     and the lower bound of the first offset.  */
+	  if (overlap_certain)
+	    acs.ovlsiz[0]
+	      = (write_min - (acs.srcoff[1] - acs.dstoff[0])).to_shwi ();
+	  acs.ovlsiz[1]
+	    = (write_max - (acs.srcoff[0] - acs.dstoff[0])).to_shwi ();
+
+	  if (acs.ovlsiz[1] > acs.srcsiz[1])
+	    acs.ovlsiz[1] = acs.srcsiz[1].to_shwi ();
+	}
+      else
+	{
+	  /* The offset of the overlap is the lower bound of the first
+	     offset plus the initial non-overlapping size of the access.  */
+	  if (overlap_certain)
+	    acs.ovlsiz[0]
+	      = (write_min - (acs.dstoff[1] - acs.srcoff[0])).to_shwi ();
+
+	  acs.ovlsiz[1]
+	    = (write_max - (acs.dstoff[0] - acs.srcoff[0])).to_shwi ();
+	}
+
+      if (acs.ovlsiz[1])
+	acs.ovloff[1] = (acs.dstoff[0] + write_max).to_shwi () - acs.ovlsiz[1];
+      else
+	acs.ovloff[1] = acs.srcoff[0].to_shwi ();
+
+      if (acs.ovloff[1] >= acs.srcoff[0] + acs.srcsiz[0])
+	acs.ovloff[1] = (acs.srcoff[0] + acs.srcsiz[0]).to_shwi ();
+
+      if (acs.ovlsiz[0] < 0)
+	{
+	  acs.ovlsiz[0] = 0;
+	  acs.ovloff[0] = acs.ovloff[1];
+	}
+      else if (acs.ovlsiz[0] > 0)
+	acs.ovloff[0] = (acs.dstoff[0] + write_min).to_shwi () - acs.ovlsiz[0];
+      else if (acs.ovloff[1] && acs.ovlsiz[1] > 1)
+	{
+	  /* When the minimum overlap is zero then for strcpy the upper
+	     bound of the overlap offset (which is actually the smaller
+	     of the two offset bounds to correspond to the lager size
+	     of the overlap) is two less than the upper bound of the end
+	     offset of the overlap.  For other functions it's just one
+	     less.  */
+	  acs.ovloff[0] = (acs.ovloff[1] + acs.ovlsiz[1] - 1 - stxcpy_p);
+	}
+      else
+	acs.ovloff[0] = acs.ovloff[1];
+    }
+  else
+    {
+      /* The lower bound of the offset of the first reference is
+	 the same or greater that the lower bound of the second.
+	 The size of the overlap is the size of the access minus
+	 the difference between the upper bound of the first offset
+	 and the lower bound of the first offset.  */
+      acs.ovlsiz[0] = (write_min - (acs.dstoff[1] - acs.srcoff[0])).to_shwi ();
+      acs.ovlsiz[1] = (write_max - (acs.dstoff[0] - acs.srcoff[0])).to_shwi ();
+
+      if (acs.ovlsiz[1])
+	acs.ovloff[1] = (acs.srcoff[1] + write_max).to_shwi () - acs.ovlsiz[1];
+      else
+	acs.ovloff[1] = acs.dstoff[1].to_shwi ();
+
+      if (acs.ovlsiz[0] < 0)
+	{
+	  acs.ovlsiz[0] = 0;
+	  acs.ovloff[0] = acs.ovloff[1];
+	}
+      else if (acs.ovlsiz[0] > 0)
+	/* The offset of the overlap is the upper bound of the first
+	   offset plus the initial non-overlapping size of the access.  */
+	acs.ovloff[0] = (acs.srcoff[0] + write_min).to_shwi () - acs.ovlsiz[0];
+      else
+	acs.ovloff[0] = (acs.ovloff[1] + acs.ovlsiz[1] - 1 - sizdep_p);
+    }
+
+  return true;
+}
+
+/* Return true if the strcat-like access overlaps.  */
+
+bool
+builtin_access::strcat_overlap ()
+{
+  builtin_access &acs = *this;
+  const builtin_memref *dstref = acs.dstref;
+  const builtin_memref *srcref = acs.srcref;
+
+  gcc_assert (dstref->base == srcref->base);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  gcc_assert (dstref->base && dstref->base == srcref->base);
+
+  /* Adjust for strcat-like accesses.  */
+
+  /* As a special case for strcat, set the DSTREF offsets to the length
+     of the source string since the function starts writing at the first
+     nul, and set the size to 1 for the length of the nul.  */
+  acs.dstoff[0] += acs.dstsiz[0];
+  acs.dstoff[1] += acs.dstsiz[1];
+
+  bool strfunc_unknown_args = acs.dstsiz[0] == 0 && acs.dstsiz[1] != 0;
+
+  /* The lower bound is zero when the size is unknown because then
+     overlap is not certain. */
+  acs.dstsiz[0] = strfunc_unknown_args ? 0 : 1;
+  acs.dstsiz[1] = 1;
+
+  offset_int maxsize = dstref->objsize < 0 ? maxobjsize : dstref->objsize;
+  gcc_assert (maxsize <= maxobjsize);
+
+  /* For references to the same base object, determine if there's a pair
+     of valid offsets into the two references such that access between
+     them doesn't overlap.  Adjust both upper bounds to be valid for
+     the smaller size (i.e., at most MAXSIZE - SIZE).  */
+
+  if (maxsize < acs.dstoff[1] + acs.dstsiz[0])
+    acs.dstoff[1] = maxsize - acs.dstsiz[0];
+
+  if (maxsize < acs.srcoff[1] + acs.srcsiz[0])
+    acs.srcoff[1] = maxsize - acs.srcsiz[0];
+
+  /* Check to see if there's enough space for both accesses without
+     overlap.  Determine the optimistic (maximum) amount of available
+     space.  */
+  offset_int space;
+  if (acs.dstoff[0] <= acs.srcoff[0])
+    {
+      if (acs.dstoff[1] < acs.srcoff[1])
+	space = acs.srcoff[1] + acs.srcsiz[0] - acs.dstoff[0];
+      else
+	space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+    }
+  else
+    space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+
+  /* Overlap is certain if the distance between the farthest offsets
+     of the opposite accesses is less than the sum of the lower bounds
+     of the sizes of the two accesses.  */
+  bool overlap_certain = space < acs.dstsiz[0] + acs.srcsiz[0];
+
+  /* For a constant-offset, constant size access, consider the largest
+     distance between the offset bounds and the lower bound of the access
+     size.  If the overlap isn't certain return success.  */
+  if (!overlap_certain
+      && acs.dstoff[0] == acs.dstoff[1]
+      && acs.srcoff[0] == acs.srcoff[1]
+      && acs.dstsiz[0] == acs.dstsiz[1]
+      && acs.srcsiz[0] == acs.srcsiz[1])
+    return false;
+
+  /* Overlap is not certain but may be possible.  */
+
+  offset_int access_min = acs.dstsiz[0] + acs.srcsiz[0];
+
+  /* Determine the conservative (minimum) amount of space.  */
+  space = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+  offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+  if (d < space)
+    space = d;
+  d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+  if (d < space)
+    space = d;
+
+  /* For a strict test (used for strcpy and similar with unknown or
+     variable bounds or sizes), consider the smallest distance between
+     the offset bounds and either the upper bound of the access size
+     if known, or the lower bound otherwise.  */
+  if (access_min <= space && (access_min != 0 || !strfunc_unknown_args))
+    return false;
+
+  /* When strcat overlap is certain it is always a single byte:
+     the terminatinn NUL, regardless of offsets and sizes.  When
+     overlap is only possible its range is [0, 1].  */
+  acs.ovlsiz[0] = dstref->sizrange[0] == dstref->sizrange[1] ? 1 : 0;
+  acs.ovlsiz[1] = 1;
+  acs.ovloff[0] = (dstref->sizrange[0] + dstref->offrange[0]).to_shwi ();
+  acs.ovloff[1] = (dstref->sizrange[1] + dstref->offrange[1]).to_shwi ();
+
+  acs.sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+  acs.sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+  return true;
+}
+
+/* Return true if the strcpy-like access overlaps.  */
+
+bool
+builtin_access::strcpy_overlap ()
+{
+  return generic_overlap ();
+}
+
+
+/* Return true if DSTREF and SRCREF describe accesses that either overlap
+   one another or that, in order not to overlap, would imply that the size
+   of the referenced object(s) exceeds the maximum size of an object.  Set
+   Otherwise, if DSTREF and SRCREF do not definitely overlap (even though
+   they may overlap in a way that's not apparent from the available data),
+   return false.  */
+
+bool
+builtin_access::overlap ()
+{
+  builtin_access &acs = *this;
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  acs.sizrange[0] = wi::smax (dstref->sizrange[0],
+			      srcref->sizrange[0]).to_shwi ();
+  acs.sizrange[1] = wi::smax (dstref->sizrange[1],
+			      srcref->sizrange[1]).to_shwi ();
+
+  /* Check to see if the two references refer to regions that are
+     too large not to overlap in the address space (whose maximum
+     size is PTRDIFF_MAX).  */
+  offset_int size = dstref->sizrange[0] + srcref->sizrange[0];
+  if (maxobjsize < size)
+    {
+      acs.ovloff[0] = (maxobjsize - dstref->sizrange[0]).to_shwi ();
+      acs.ovlsiz[0] = (size - maxobjsize).to_shwi ();
+      return true;
+    }
+
+  /* If both base objects aren't known return the maximum possible
+     offset that would make them not overlap.  */
+  if (!dstref->base || !srcref->base)
+    return false;
+
+  /* If the base object is an array adjust the lower bound of the offset
+     to be non-negative.  */
+  if (dstref->base
+      && TREE_CODE (TREE_TYPE (dstref->base)) == ARRAY_TYPE)
+    acs.dstoff[0] = wi::smax (dstref->offrange[0], 0);
+  else
+    acs.dstoff[0] = dstref->offrange[0];
+
+  acs.dstoff[1] = dstref->offrange[1];
+
+  if (srcref->base
+      && TREE_CODE (TREE_TYPE (srcref->base)) == ARRAY_TYPE)
+    acs.srcoff[0] = wi::smax (srcref->offrange[0], 0);
+  else
+    acs.srcoff[0] = srcref->offrange[0];
+
+  acs.srcoff[1] = srcref->offrange[1];
+
+  /* When the lower bound of the offset is less that the upper bound
+     disregard it and use the inverse of the maximum object size
+     instead.  The upper bound is the result of a negative offset
+     being represented as a large positive value.  */
+  if (acs.dstoff[1] < acs.dstoff[0])
+    acs.dstoff[0] = -maxobjsize;
+
+  /* Validate the offset and size of each reference on its own first.
+     This is independent of whether or not the base objects are the
+     same.  Normally, this would have already been detected and
+     diagnosed by -Warray-bounds, unless it has been disabled.  */
+  offset_int maxoff = acs.dstoff[0] + dstref->sizrange[0];
+  if (maxobjsize < maxoff)
+    {
+      acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+      acs.ovloff[0] = acs.dstoff[0].to_shwi () - acs.ovlsiz[0];
+      return true;
+    }
+
+  /* Repeat the same as above but for the source offsets.  */
+  if (acs.srcoff[1] < acs.srcoff[0])
+    acs.srcoff[0] = -maxobjsize;
+
+  maxoff = acs.srcoff[0] + srcref->sizrange[0];
+  if (maxobjsize < maxoff)
+    {
+      acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+      acs.ovlsiz[1] = (acs.srcoff[0] + srcref->sizrange[1]
+		       - maxobjsize).to_shwi ();
+      acs.ovloff[0] = acs.srcoff[0].to_shwi () - acs.ovlsiz[0];
+      return true;
+    }
+
+  if (dstref->base != srcref->base)
+    return false;
+
+  acs.dstsiz[0] = dstref->sizrange[0];
+  acs.dstsiz[1] = dstref->sizrange[1];
+
+  acs.srcsiz[0] = srcref->sizrange[0];
+  acs.srcsiz[1] = srcref->sizrange[1];
+
+  /* Call the appropriate function to determine the overlap.  */
+  if ((this->*detect_overlap) ())
+    {
+      sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+      sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+      return true;
+    }
+
+  return false;
+}
+
+/* Attempt to detect and diagnose an overlapping copy in a call expression
+   EXPR involving an an access ACS to a built-in memory or string function.
+   Return true when one has been detected, false otherwise.  */
+
+static bool
+maybe_diag_overlap (location_t loc, tree exp, builtin_access &acs)
+{
+  const builtin_memref &dstref = *acs.dstref;
+  const builtin_memref &srcref = *acs.srcref;
+
+  /* Determine the range of offsets and sizes of the overlap if it
+     exists and issue diagnostics.  */
+  HOST_WIDE_INT *ovloff = acs.ovloff;
+  HOST_WIDE_INT *ovlsiz = acs.ovlsiz;
+  HOST_WIDE_INT *sizrange = acs.sizrange;
+
+  tree func = get_callee_fndecl (exp);
+
+  if (!acs.overlap ())
+    return false;
+
+  /* To avoid a combinatorial explosion of diagnostics format the offsets
+     or their ranges as strings and use them in the warning calls below.  */
+  char offstr[3][64];
+
+  if (dstref.offrange[0] == dstref.offrange[1]
+      || dstref.offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr[0], "%lli", (long long) dstref.offrange[0].to_shwi ());
+  else
+    sprintf (offstr[0], "[%lli, %lli]",
+             (long long) dstref.offrange[0].to_shwi (),
+             (long long) dstref.offrange[1].to_shwi ());
+
+  if (srcref.offrange[0] == srcref.offrange[1]
+      || srcref.offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr[1], "%lli", (long long) srcref.offrange[0].to_shwi ());
+  else
+    sprintf (offstr[1], "[%lli, %lli]",
+	     (long long) srcref.offrange[0].to_shwi (),
+             (long long) srcref.offrange[1].to_shwi ());
+
+  if (ovloff[0] == ovloff[1] || !ovloff[1])
+    sprintf (offstr[2], "%lli", (long long) ovloff[0]);
+  else
+    sprintf (offstr[2], "[%lli, %lli]",
+	     (long long) ovloff[0], (long long) ovloff[1]);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  bool must_overlap = ovlsiz[0] > 0;
+
+  if (ovlsiz[1] == 0)
+    ovlsiz[1] = ovlsiz[0];
+
+  if (must_overlap)
+    {
+      /* Issue definitive "overlaps" diagnostic in this block.  */
+
+      if (sizrange[0] == sizrange[1])
+	{
+	  if (ovlsiz[0] == ovlsiz[1])
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? (ovlsiz[0] == 1
+			   ? G_("%K%qD accessing %wu byte at offsets %s "
+				"and %s overlaps %wu byte at offset %s")
+			   :  G_("%K%qD accessing %wu byte at offsets %s "
+				 "and %s overlaps %wu bytes at offset "
+				 "%s"))
+			: (ovlsiz[0] == 1
+			   ? G_("%K%qD accessing %wu bytes at offsets %s "
+				"and %s overlaps %wu byte at offset %s")
+			   : G_("%K%qD accessing %wu bytes at offsets %s "
+				"and %s overlaps %wu bytes at offset "
+				"%s")),
+			exp, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? G_("%K%qD accessing %wu byte at offsets %s "
+			     "and %s overlaps between %wu and %wu bytes "
+			     "at offset %s")
+			: G_("%K%qD accessing %wu bytes at offsets %s "
+			     "and %s overlaps between %wu and %wu bytes "
+			     "at offset %s"),
+			exp, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+			offstr[2]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? G_("%K%qD accessing %wu byte at offsets %s and "
+			     "%s overlaps %wu or more bytes at offset %s")
+			: G_("%K%qD accessing %wu bytes at offsets %s and "
+			     "%s overlaps %wu or more bytes at offset %s"),
+			exp, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  return true;
+	}
+
+      if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+	{
+	  if (ovlsiz[0] == ovlsiz[1])
+	    warning_at (loc, OPT_Wrestrict,
+			ovlsiz[0] == 1
+			? G_("%K%qD accessing between %wu and %wu bytes "
+			     "at offsets %s and %s overlaps %wu byte at "
+			     "offset %s")
+			: G_("%K%qD accessing between %wu and %wu bytes "
+			     "at offsets %s and %s overlaps %wu bytes "
+			     "at offset %s"),
+			exp, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD accessing between %wu and %wu bytes at "
+			"offsets %s and %s overlaps between %wu and %wu "
+			"bytes at offset %s",
+			exp, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+			offstr[2]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%K%qD accessing between %wu and %wu bytes at "
+			"offsets %s and %s overlaps %wu or more bytes "
+			"at offset %s",
+			exp, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  return true;
+	}
+
+      if (ovlsiz[0] != ovlsiz[1])
+	ovlsiz[1] = maxobjsize.to_shwi ();
+
+      if (ovlsiz[0] == ovlsiz[1])
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[0] == 1
+		    ? G_("%K%qD accessing %wu or more bytes at offsets "
+			 "%s and %s overlaps %wu byte at offset %s")
+		    :  G_("%K%qD accessing %wu or more bytes at offsets "
+			  "%s and %s overlaps %wu bytes at offset %s"),
+		    exp, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], offstr[2]);
+      else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD accessing %wu or more bytes at offsets %s "
+		    "and %s overlaps between %wu and %wu bytes "
+		    "at offset %s",
+		    exp, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], ovlsiz[1], offstr[2]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%K%qD accessing %wu or more bytes at offsets %s "
+		    "and %s overlaps %wu or more bytes at offset %s",
+		    exp, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], offstr[2]);
+      return true;
+    }
+
+  /* Issue "may overlap" diagnostics below.  */
+  gcc_assert (ovlsiz[0] == 0
+	      && ovlsiz[1] > 0
+	      && ovlsiz[1] <= maxobjsize.to_shwi ());
+
+  /* Use more concise wording when one of the offsets is unbounded
+     to avoid confusing the user with large and mostly meaningless
+     numbers.  */
+  bool open_range = ((dstref.offrange[0] == -maxobjsize - 1
+		      && dstref.offrange[1] == maxobjsize)
+		     || (srcref.offrange[0] == -maxobjsize - 1
+			 && srcref.offrange[1] == maxobjsize));
+
+  if (sizrange[0] == sizrange[1] || sizrange[1] == 1)
+    {
+      if (ovlsiz[1] == 1)
+	{
+	  if (open_range)
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[1] == 1
+			? G_("%K%qD accessing %wu byte may overlap "
+			     "%wu byte")
+			: G_("%K%qD accessing %wu bytes may overlap "
+			     "%wu byte"),
+			exp, func, sizrange[1], ovlsiz[1]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[1] == 1
+			? G_("%K%qD accessing %wu byte at offsets %s "
+			     "and %s may overlap %wu byte at offset %s")
+			: G_("%K%qD accessing %wu bytes at offsets %s "
+			     "and %s may overlap %wu byte at offset %s"),
+			exp, func, sizrange[1], offstr[0], offstr[1],
+			ovlsiz[1], offstr[2]);
+	  return true;
+	}
+
+      if (open_range)
+	warning_at (loc, OPT_Wrestrict,
+		    sizrange[1] == 1
+		    ? G_("%K%qD accessing %wu byte may overlap "
+			 "up to %wu bytes")
+		    : G_("%K%qD accessing %wu bytes may overlap "
+			 "up to %wu bytes"),
+		    exp, func, sizrange[1], ovlsiz[1]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    sizrange[1] == 1
+		    ? G_("%K%qD accessing %wu byte at offsets %s and "
+			 "%s may overlap up to %wu bytes at offset %s")
+		    : G_("%K%qD accessing %wu bytes at offsets %s and "
+			 "%s may overlap up to %wu bytes at offset %s"),
+		    exp, func, sizrange[1], offstr[0], offstr[1],
+		    ovlsiz[1], offstr[2]);
+      return true;
+    }
+
+  if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+    {
+      if (open_range)
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[1] == 1
+		    ? G_("%K%qD accessing between %wu and %wu bytes "
+			 "may overlap %wu byte")
+		    : G_("%K%qD accessing between %wu and %wu bytes "
+			 "may overlap up to %wu bytes"),
+		    exp, func, sizrange[0], sizrange[1], ovlsiz[1]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[1] == 1
+		    ? G_("%K%qD accessing between %wu and %wu bytes "
+			 "at offsets %s and %s may overlap %wu byte "
+			 "at offset %s")
+		    : G_("%K%qD accessing between %wu and %wu bytes "
+			 "at offsets %s and %s may overlap up to %wu "
+			 "bytes at offset %s"),
+		    exp, func, sizrange[0], sizrange[1],
+		    offstr[0], offstr[1], ovlsiz[1], offstr[2]);
+      return true;
+    }
+
+  warning_at (loc, OPT_Wrestrict,
+	      ovlsiz[1] == 1
+	      ? G_("%K%qD accessing %wu or more bytes at offsets %s "
+		   "and %s may overlap %wu byte at offset %s")
+	      : G_("%K%qD accessing %wu or more bytes at offsets %s "
+		   "and %s may overlap up to %wu bytes at offset %s"),
+	      exp, func, sizrange[0], offstr[0], offstr[1],
+	      ovlsiz[1], offstr[2]);
+
+  return true;
+}
+
+/* Validate REF offsets in an EXPRession passed as an argument to a CALL
+   to a built-in function FUNC to make sure they are within the bounds
+   of the referenced object if its size is known, or PTRDIFF_MAX otherwise.
+   Both initial values of the offsets and their final value computed by
+   the function by incrementing the initial value by the size are
+   validated.  Return true if the offsets are not valid and a diagnostic
+   has been issued.  */
+
+static bool
+maybe_diag_offset_bounds (location_t loc, tree call, tree func, tree expr,
+			  const builtin_memref &ref)
+{
+  if (!warn_array_bounds)
+    return false;
+
+  offset_int ooboff[] = { ref.offrange[0], ref.offrange[1] };
+  tree oobref = ref.offset_out_of_bounds (ooboff);
+  if (!oobref)
+    return false;
+
+  if (EXPR_HAS_LOCATION (expr))
+    loc = EXPR_LOCATION (expr);
+
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  tree type;
+
+  char rangestr[2][64];
+  if (ooboff[0] == ooboff[1]
+      || (ooboff[0] != ref.offrange[0]
+	  && ooboff[0].to_shwi () >= ooboff[1].to_shwi ()))
+    sprintf (rangestr[0], "%lli", (long long) ooboff[0].to_shwi ());
+  else
+    sprintf (rangestr[0], "[%lli, %lli]",
+	     (long long) ooboff[0].to_shwi (),
+	     (long long) ooboff[1].to_shwi ());
+
+  if (oobref == error_mark_node)
+    {
+      if (ref.sizrange[0] == ref.sizrange[1])
+	sprintf (rangestr[1], "%lli", (long long) ref.sizrange[0].to_shwi ());
+      else
+	sprintf (rangestr[1], "[%lli, %lli]",
+		 (long long) ref.sizrange[0].to_shwi (),
+		 (long long) ref.sizrange[1].to_shwi ());
+
+      if (DECL_P (ref.base)
+	  && TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
+	{
+	  if (warning_at (loc, OPT_Warray_bounds,
+			  "%K%qD pointer overflow between offset %s "
+			  "and size %s accessing array %qD with type %qT",
+			  call, func, rangestr[0], rangestr[1], ref.base, type))
+	    inform (DECL_SOURCE_LOCATION (ref.base),
+		    "array %qD declared here", ref.base);
+	  else
+	    warning_at (loc, OPT_Warray_bounds,
+			"%K%qD pointer overflow between offset %s "
+			"and size %s",
+			call, func, rangestr[0], rangestr[1]);
+	}
+      else
+	warning_at (loc, OPT_Warray_bounds,
+		    "%K%qD pointer overflow between offset %s "
+		    "and size %s",
+		    call, func, rangestr[0], rangestr[1]);
+    }
+  else if (oobref == ref.base)
+    {
+      const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+      /* True when the offset formed by an access to the reference
+	 is out of bounds, rather than the initial offset wich is
+	 in bounds.  This implies access past the end.  */
+      bool form = ooboff[0] != ref.offrange[0];
+
+      if (DECL_P (ref.base))
+	{
+	  if ((ref.objsize < maxobjsize
+	       && warning_at (loc, OPT_Warray_bounds,
+			      form
+			      ? G_("%K%qD forming offset %s is out of "
+				   "the bounds [0, %wu] of object %qD with "
+				   "type %qT")
+			      : G_("%K%qD offset %s is out of the bounds "
+				   "[0, %wu] of object %qD with type %qT"),
+			      call, func, rangestr[0], ref.objsize.to_uhwi (),
+			      ref.base, TREE_TYPE (ref.base)))
+	      || warning_at (loc, OPT_Warray_bounds,
+			     form
+			     ? G_("%K%qD forming offset %s is out of "
+				  "the bounds of object %qD with type %qT")
+			     : G_("%K%qD offset %s is out of the bounds "
+				  "of object %qD with type %qT"),
+			     call, func, rangestr[0],
+			     ref.base, TREE_TYPE (ref.base)))
+	    inform (DECL_SOURCE_LOCATION (ref.base),
+		    "%qD declared here", ref.base);
+	}
+      else if (ref.objsize < maxobjsize)
+	warning_at (loc, OPT_Warray_bounds,
+		    form
+		    ? G_("%K%qD forming offset %s is out of the bounds "
+			 "[0, %wu]")
+		    : G_("%K%qD offset %s is out of the bounds [0, %wu]"),
+		    call, func, rangestr[0], ref.objsize.to_uhwi ());
+      else
+	warning_at (loc, OPT_Warray_bounds,
+		    form
+		    ? G_("%K%qD forming offset %s is out of bounds")
+		    : G_("%K%qD offset %s is out of bounds"),
+		    call, func, rangestr[0]);	
+    }
+  else if (TREE_CODE (ref.ref) == MEM_REF)
+    {
+      tree type = TREE_TYPE (TREE_OPERAND (ref.ref, 0));
+      if (POINTER_TYPE_P (type))
+	type = TREE_TYPE (type);
+      type = TYPE_MAIN_VARIANT (type);
+
+      warning_at (loc, OPT_Warray_bounds,
+		  "%K%qD offset %s from the object at %qE is out "
+		  "of the bounds of %qT",
+		  call, func, rangestr[0], ref.base, type);
+    }
+  else
+    {
+      type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
+
+      warning_at (loc, OPT_Warray_bounds,
+		"%K%qD offset %s from the object at %qE is out "
+		"of the bounds of referenced subobject %qD with type %qT "
+		"at offset %wu",
+		call, func, rangestr[0], ref.base, TREE_OPERAND (ref.ref, 1),
+		type, ref.refoff.to_uhwi ());
+    }
+
+  return true;
+}
+
+/* Helper to create a minimal CALL_EXPR tree from a GIMPLE call STMT.
+   The tree is minimally populated to be be usable in a %K directive
+   with the same result as the corresponding STMT with %G. See
+   expand_call_stmt() in cfgexpand.c.  */
+
+static tree
+gcall_to_tree_minimal (gcall *stmt)
+{
+  tree exp = build_vl_exp (CALL_EXPR, gimple_call_num_args (stmt) + 3);
+  CALL_EXPR_FN (exp) = gimple_call_fn (stmt);
+  TREE_TYPE (exp) = gimple_call_return_type (stmt);
+  CALL_EXPR_STATIC_CHAIN (exp) = gimple_call_chain (stmt);
+  CALL_WITH_BOUNDS_P (exp) = gimple_call_with_bounds_p (stmt);
+
+  for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
+    {
+      tree arg = gimple_call_arg (stmt, i);
+      CALL_EXPR_ARG (exp, i) = arg;
+    }
+
+  SET_EXPR_LOCATION (exp, gimple_location (stmt));
+  return exp;
+}
+
+/* Attempt to detect and diagnose invalid offset bounds and (except for
+   memmove) overlapping copy in a call expression EXP from SRC to DST
+   and DSTSIZE and SRCSIZE bytes, respectively.  Return false when one
+   or the other has been detected, true otherwise.  */
+
+bool
+check_bounds_or_overlap (tree exp, tree dst, tree src, tree dstsize,
+			 tree srcsize, bool bounds_only /* = false */)
+{
+  location_t loc = tree_nonartificial_location (exp);
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  tree func = get_callee_fndecl (exp);
+
+  bool check_overlap
+    = (bounds_only
+       || (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+	   && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK));
+
+  builtin_memref dstref (dst, dstsize);
+  builtin_memref srcref (src, srcsize);
+
+  builtin_access acs (exp, dstref, srcref);
+
+  /* Validate offsets first to make sure they are within the bounds
+     of the destination object if its size is known, or PTRDIFF_MAX
+     otherwise.  */
+  if (maybe_diag_offset_bounds (loc, exp, func, dst, dstref)
+      || maybe_diag_offset_bounds (loc, exp, func, src, srcref))
+    {
+      TREE_NO_WARNING (exp) = 1;
+      return false;
+    }
+
+  if (check_overlap)
+    {
+      if (operand_equal_p (dst, src, 0))
+	{
+	  warning_at (loc, OPT_Wrestrict,
+		      "%K%qD source argument is the same as destination",
+		      exp, func);
+	  TREE_NO_WARNING (exp) = 1;
+	  return false;
+	}
+
+      /* Return false when overlap has been detected.  */
+      if (maybe_diag_overlap (loc, exp, acs))
+	{
+	  TREE_NO_WARNING (exp) = 1;
+	  return false;
+	}
+    }
+
+  return true;
+}
+
+/* Attempt to detect and diagnose invalid offset bounds and (except for
+   memmove) overlapping copy in a statement STMT from SRC to DST and
+   DSTSIZE and SRCSIZE bytes, respectively.  Return false when one or
+   the other has been detected, true otherwise.  */
+
+bool
+check_bounds_or_overlap (gimple *stmt, tree dst, tree src,
+			 tree dstsize, tree srcsize,
+			 bool bounds_only /*= false */)
+{
+  if (gimple_no_warning_p (stmt))
+    return false;
+
+  tree exp = gcall_to_tree_minimal (as_a <gcall *> (stmt));
+
+  if (check_bounds_or_overlap (exp, dst, src, dstsize, srcsize, bounds_only))
+    return true;
+
+  /* If a warning has been issued (and NO_WARNING set) set the GIMPLE
+     no warning bit.  */
+  if (TREE_NO_WARNING (exp))
+    gimple_set_no_warning (stmt, true);
+
+  return false;
+}
+
+void pass_wrestrict::check_call (gcall *call)
+{
+  tree func = gimple_call_fndecl (call);
+  if (!func || DECL_BUILT_IN_CLASS (func) != BUILT_IN_NORMAL)
+    return;
+
+  bool with_bounds = gimple_call_with_bounds_p (call);
+
+  /* Argument number to extract from the call (depends on the built-in
+     and its kind).  */
+  unsigned dst_idx = -1;
+  unsigned src_idx = -1;
+  unsigned bnd_idx = -1;
+  bool strfun = true;
+
+  switch (DECL_FUNCTION_CODE (func))
+    {
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMCPY_CHK_CHKP:
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHKP:
+    case BUILT_IN_MEMPCPY_CHK_CHKP:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMMOVE_CHK_CHKP:
+      strfun = false;
+      /* Fall through.  */
+
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      dst_idx = 0;
+      src_idx = 1 + with_bounds;
+      bnd_idx = 2 + 2 * with_bounds;
+      break;
+
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STPCPY_CHKP:
+    case BUILT_IN_STPCPY_CHK_CHKP:
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STRCPY_CHKP:
+    case BUILT_IN_STRCPY_CHK_CHKP:
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRCAT_CHKP:
+    case BUILT_IN_STRCAT_CHK_CHKP:
+      dst_idx = 0;
+      src_idx = 1 + with_bounds;
+      break;
+
+    default:
+      /* Handle other string functions here whose access may need
+	 to be validated for in-bounds offsets and non-overlapping
+	 copies.  (Not all _chkp functions have BUILT_IN_XXX_CHKP
+	 macros so they need to be handled here.)  */
+      return;
+    }
+
+  unsigned nargs = gimple_call_num_args (call);
+
+  tree dst = dst_idx < nargs ? gimple_call_arg (call, dst_idx) : NULL_TREE;
+  tree src = src_idx < nargs ? gimple_call_arg (call, src_idx) : NULL_TREE;
+  tree dstwr = bnd_idx < nargs ? gimple_call_arg (call, bnd_idx) : NULL_TREE;
+
+  /* For string functions with an unspecified or unknown bound,
+     assume the size of the access is one.  */
+  if (!dstwr && strfun)
+    dstwr = size_one_node;
+
+  check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE);
+}
+
+
+gimple_opt_pass *
+make_pass_warn_restrict (gcc::context *ctxt)
+{
+  return new pass_wrestrict (ctxt);
+}
diff --git a/gcc/gimple-ssa-warn-restrict.h b/gcc/gimple-ssa-warn-restrict.h
new file mode 100644
index 0000000..99d478c
--- /dev/null
+++ b/gcc/gimple-ssa-warn-restrict.h
@@ -0,0 +1,29 @@
+/* Warn on violations of the restrict qualifier.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Contributed by Martin Sebor <msebor@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GIMPLE_SSA_WARN_RESTRICT_H
+
+
+extern bool check_bounds_or_overlap (tree, tree, tree, tree, tree,
+				     bool = false);
+extern bool check_bounds_or_overlap (gimple *, tree, tree, tree, tree,
+				     bool = false);
+
+#endif /* GIMPLE_SSA_WARN_RESTRICT_H */
diff --git a/gcc/passes.def b/gcc/passes.def
index 00e75d2..a799ce3 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -330,6 +330,7 @@ along with GCC; see the file COPYING3.  If not see
 	 run the full propagators, run a specialized pass which
 	 only examines PHIs to discover const/copy propagation
 	 opportunities.  */
+      NEXT_PASS (pass_warn_restrict);
       NEXT_PASS (pass_phi_only_cprop);
       NEXT_PASS (pass_dse);
       NEXT_PASS (pass_cd_dce);
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-2.c b/gcc/testsuite/c-c++-common/Warray-bounds-2.c
new file mode 100644
index 0000000..becb3d4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-bounds-2.c
@@ -0,0 +1,204 @@
+/* Test to exercise that -Warray-bounds warnings for memory and sring
+   functions are issued even when they are declared in system headers
+   (i.e., not just when they are explicitly declared in the source
+   file.)
+   Also verify that the warnings are issued even for calls where the
+   source of the excessive array bound is in a different function than
+   the call.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds -Wno-stringop-overflow" } */
+
+#include <stddef.h>
+#include <string.h>
+
+#define MAX  (__SIZE_MAX__ / 2)
+
+void sink (void*);
+
+struct __attribute__ ((packed)) Array
+{
+  char a13[13];
+  char a15[15];
+  char a17[17];
+};
+
+/* Exercise memcpy out-of-bounds offsets with an array of known size.  */
+
+void wrap_memcpy_src_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d, s + i, n);   /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_src_xsize (char *d, size_t n)
+{
+  struct Array ar;
+  sink (&ar);
+  wrap_memcpy_src_xsize (d, ar.a13, 46, n);
+  sink (&ar);
+}
+
+/* Exercise memcpy out-of-bounds offsets with an array of unknown size.  */
+
+void wrap_memcpy_src_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d, s + i, n);   /* { dg-warning "pointer overflow between offset \[0-9\]+ and size 3" "memcpy" } */
+}
+
+void call_memcpy_src_diff_max (char *d, const char *s, size_t n)
+{
+  wrap_memcpy_src_diff_max (d, s, MAX, 3);
+}
+
+void wrap_memcpy_dst_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d + i, s, n);   /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar1. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_dst_xsize (const char *s, size_t n)
+{
+  struct Array ar1;       /* { dg-message ".ar1. declared here" } */
+  sink (&ar1);
+  wrap_memcpy_dst_xsize (ar1.a15, s, 34, n);
+  sink (&ar1);
+}
+
+void wrap_memcpy_dst_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d + i, s, n);   /* { dg-warning "offset -?\[0-9\]+ is out of the bounds \\\[0, 45] of object .ar2. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_dst_diff_max (const char *s, size_t n)
+{
+  struct Array ar2;       /* { dg-message ".ar2. declared here" } */
+  sink (&ar2);
+  wrap_memcpy_dst_diff_max (ar2.a15, s, MAX, n);
+  sink (&ar2);
+}
+
+
+void wrap_strcat_src_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcat (d, s + i);   /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar3. with type .(struct )?Array." "strcat" } */
+}
+
+void call_strcat_src_xsize (char *d)
+{
+  struct Array ar3;       /* { dg-message ".ar3. declared here" } */
+  sink (&ar3);
+  wrap_strcat_src_xsize (d, ar3.a15, 15 + 17 + 1);
+  sink (&ar3);
+}
+
+void wrap_strcat_dst_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcat (d + i, s);   /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar4. with type .(struct )?Array." "strcat" } */
+}
+
+void call_strcat_dst_xsize (const char *s)
+{
+  struct Array ar4;       /* { dg-message ".ar4. declared here" } */
+  sink (&ar4);
+  wrap_strcat_dst_xsize (ar4.a15, s, 15 + 17 + 2);
+  sink (&ar4);
+}
+
+
+void wrap_strcpy_src_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcpy (d, s + i);   /* { dg-warning "offset 48 is out of the bounds \\\[0, 45] of object .ar5. with type .(struct )?Array." "strcpy" } */
+}
+
+void call_strcpy_src_xsize (char *d)
+{
+  struct Array ar5;       /* { dg-message ".ar5. declared here" } */
+  sink (&ar5);
+  wrap_strcpy_src_xsize (d, ar5.a15, 15 + 17 + 3);
+  sink (&ar5);
+}
+
+void wrap_strcpy_dst_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcpy (d + i, s);   /* { dg-warning "offset 49 is out of the bounds \\\[0, 45] of object .ar6. with type .(struct )?Array." "strcpy" } */
+}
+
+void call_strcpy_dst_xsize (const char *s)
+{
+  struct Array ar6;       /* { dg-message ".ar6. declared here" } */
+  sink (&ar6);
+  wrap_strcpy_dst_xsize (ar6.a15, s, 15 + 17 + 4);
+  sink (&ar6);
+}
+
+
+/* Exercise strncpy out-of-bounds offsets with an array of known size.  */
+
+void wrap_strncpy_src_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d, s + i, n);   /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar7. with type '(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_src_xsize (char *d, size_t n)
+{
+  struct Array ar7;       /* { dg-message ".ar7. declared here" } */
+  sink (&ar7);
+  wrap_strncpy_src_xsize (d, ar7.a17, 17 + 1, n);
+  sink (&ar7);
+}
+
+/* Exercise strncpy out-of-bounds offsets with an array of unknown size.  */
+
+void wrap_strncpy_src_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  /* Unlike in the similar call to memcpy(), there is no pointer
+     overflow here because the size N is not added to the source
+     offset.  */
+  strncpy (d, s + i, n);
+}
+
+void call_strncpy_src_diff_max (char *d, const char *s, size_t n)
+{
+  wrap_strncpy_src_diff_max (d, s, MAX, 3);
+}
+
+void wrap_strncpy_dst_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d + i, s, n);   /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar8. with type .(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_dst_xsize (const char *s, size_t n)
+{
+  struct Array ar8;       /* { dg-message ".ar8. declared here" } */
+  sink (&ar8);
+  wrap_strncpy_dst_xsize (ar8.a17, s, 17 + 2, n);
+  sink (&ar8);
+}
+
+void wrap_strncpy_dst_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d + i, s, n);   /* { dg-warning "offset -\[0-9\]+ is out of the bounds \\\[0, 45] of object .ar9. with type .(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_dst_diff_max (const char *s, size_t n)
+{
+  struct Array ar9;       /* { dg-message ".ar9. declared here" } */
+  sink (&ar9);
+  wrap_strncpy_dst_diff_max (ar9.a17, s, MAX, n);
+  sink (&ar9);
+}
+
+void wrap_strncpy_dstarray_diff_neg (char *d, const char *s, ptrdiff_t i,
+				     size_t n)
+{
+  strncpy (d + i, s, n);   /* { dg-warning "offset -\[0-9\]+ is out of the bounds \\\[0, 90] of object .ar10. with type .(struct )?Array ?\\\[2]." "strncpy" } */
+}
+
+void call_strncpy_dstarray_diff_neg (const char *s, size_t n)
+{
+  struct Array ar10[2];    /* { dg-message ".ar10. declared here" } */
+  sink (&ar10);
+
+  int off = (char*)ar10[1].a17 - (char*)ar10 + 1;
+  wrap_strncpy_dstarray_diff_neg (ar10[1].a17, s, -off, n);
+
+  sink (&ar10);
+}
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-3.c b/gcc/testsuite/c-c++-common/Warray-bounds-3.c
new file mode 100644
index 0000000..baa4a23
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-bounds-3.c
@@ -0,0 +1,412 @@
+/* Exercise that -Warray-bounds is issued for out-of-bounds offsets
+   in calls to built-in functions.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" }  */
+
+#define SIZE_MAX __SIZE_MAX__
+#define DIFF_MAX __PTRDIFF_MAX__
+#define DIFF_MIN (-DIFF_MAX - 1)
+
+typedef __INT32_TYPE__   int32_t;
+typedef __PTRDIFF_TYPE__ ssize_t;
+typedef __SIZE_TYPE__    size_t;
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+extern ssize_t signed_value (void)
+{
+  extern volatile ssize_t signed_value_source;
+  return signed_value_source;
+}
+
+extern size_t unsigned_value (void)
+{
+  extern volatile size_t unsigned_value_source;
+  return unsigned_value_source;
+}
+
+ssize_t signed_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return val < min || max < val ? min : val;
+}
+
+ssize_t signed_anti_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return min <= val && val <= max ? min == DIFF_MIN ? max + 1 : min - 1 : val;
+}
+
+size_t unsigned_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+size_t unsigned_anti_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return min <= val && val <= max ? min == 0 ? max + 1 : min - 1 : val;
+}
+
+#define SR(min, max) signed_range ((min), (max))
+#define UR(min, max) unsigned_range ((min), (max))
+
+#define SAR(min, max) signed_anti_range ((min), (max))
+#define UAR(min, max) unsigned_anti_range ((min), (max))
+
+void sink (void*, ...);
+
+#define CAT(x, y)      x ## y
+#define CONCAT(x, y)   CAT (x, y)
+#define UNIQUE_NAME(x) CONCAT(x, __LINE__)
+
+#define T(type, N, dst, src, n) do {		\
+    extern type UNIQUE_NAME (a)[N];		\
+    type *a = UNIQUE_NAME (a);			\
+    type *pd = (dst);				\
+    const type *ps = (src);			\
+    FUNC (pd, ps, n);				\
+    sink (a, pd, ps);				\
+  } while (0)
+
+
+void test_memcpy_bounds (char *d, const char *s, size_t n)
+{
+#define FUNC memcpy
+
+  /* Verify that invalid offsets into an array of known size are
+     detected.  */
+
+  T (char, 1, a + SR (DIFF_MIN, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds \\\[0, 1] of object \[^\n\r]* with type .char ?\\\[1]" } */
+  T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds \\\[0, 1] of object" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  T (char, 1, a + UR (0, 1), s, n);
+  T (char, 1, a + UR (0, 2), s, n);
+  T (char, 1, a + UR (1, 2), s, n);
+  T (char, 1, a + UR (2, 3), s, n);       /* { dg-warning "offset \\\[2, 3] is out of the bounds \\\[0, 1] of object " } */
+  T (char, 1, a + UR (2, DIFF_MAX), s, n);  /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds \\\[0, 1] of object " "memcpy" } */
+
+  /* Offsets in excess of DIFF_MAX are treated as negative even if
+     they appear as large positive in the source.  It would be nice
+     if they retained their type but unfortunately that's not how
+     it works so be prepared for both in case it even gets fixed.  */
+  T (char, 1, a + UR (3, SIZE_MAX - 1), s, n);   /* { dg-warning "offset \\\[3, -2] is out of the bounds \\\[0, 1] of object" "memcpy" } */
+
+  /* Verify that invalid offsets into an array of unknown size are
+     detected.  */
+  extern char arr[];
+  T (char, 1, arr + SR (DIFF_MIN,             0), s, n);
+  T (char, 1, arr + SR (DIFF_MIN + 1,        -1), s, n);   /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of object " "memcpy" } */
+  T (char, 1, arr + SR (DIFF_MIN,             1), s, n);
+  T (char, 1, arr + SR (DIFF_MIN,      DIFF_MAX), s, n);
+  T (char, 1, arr + SR (       -2,           -1), s, n);   /* { dg-warning "offset \\\[-2, -1] is out of the bounds of object " "memcpy" } */
+  T (char, 1, arr + SR (       -1,            0), s, n);
+  T (char, 1, arr + SR (       -1,            1), s, n);
+  T (char, 1, arr + SR (       -1, DIFF_MAX - 1), s, n);
+  T (char, 1, arr + SR (        0,            1), s, n);
+  T (char, 1, arr + SR (        0, DIFF_MAX - 1), s, n);
+  T (char, 1, arr + SR (        1,            2), s, n);
+  T (char, 1, arr + SR (        1, DIFF_MAX - 1), s, n);
+
+  /* Verify that all offsets via a pointer to an uknown object are
+     accepted.  */
+
+  /* Negative indices between [DIFF_MIN, DIFF_MAX] are valid since
+     the pointer to which the offset is applied can be at a positive
+     offset from the beginning of an object.  */
+  T (char, 1, d + SR (DIFF_MIN,             0), s, n);
+  T (char, 1, d + SR (DIFF_MIN,            -1), s, n);
+  T (char, 1, d + SR (DIFF_MIN,             1), s, n);
+  T (char, 1, d + SR (DIFF_MIN,  DIFF_MAX - 1), s, n);
+  T (char, 1, d + SR (       -2,           -1), s, n);
+  T (char, 1, d + SR (       -1,            0), s, n);
+  T (char, 1, d + SR (       -1,            1), s, n);
+  T (char, 1, d + SR (       -1, DIFF_MAX - 1), s, n);
+  T (char, 1, d + SR (        0,            1), s, n);
+  T (char, 1, d + SR (        0, DIFF_MAX - 1), s, n);
+  T (char, 1, d + SR (        1,            2), s, n);
+  T (char, 1, d + SR (        1, DIFF_MAX - 1), s, n);
+}
+
+/* Verify offsets in an anti-range.  */
+
+void test_memcpy_bounds_anti_range (char *d, const char *s, size_t n)
+{
+  T (char, 9, a, a + SAR (-2, -1), 3);
+  T (char, 9, a, a + SAR (-1,  1), 3);
+  T (char, 9, a, a + SAR ( 0,  1), 3);
+  T (char, 9, a, a + SAR ( 0,  2), 3);
+  T (char, 9, a, a + SAR ( 0,  3), 3);
+  T (char, 9, a, a + SAR ( 0,  4), 3);
+  T (char, 9, a, a + SAR ( 0,  5), 3);
+  /* The initial source range is valid but the final range after the access
+     has complete cannot be.  The value mentioned in the warning is the final
+     offset, i.e., 7 + 3.  Including the whole final range because would be
+     confusing (the upper bound would either be negative or a very large
+     positive number) so only the lower bound is included.  */
+  T (char, 9, a, a + SAR ( 0,  6), 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  /* This fails because the offset isn't represented as an SSA_NAME
+     but rather as a GIMPLE_PHI (offset, 0).  With some effort it is
+     possible to extract the range from the PHI but it's not implemented
+     (yet).  */
+  T (char, 9, a, a + SAR ( 1,  6), 3);   /* { dg-warning "forming offset \\\[9, 0] is out of the bounds \\\[0, 9] of object " "memcpy" { xfail *-*-* } } */
+
+  T (char, 9, a, a + SAR ( 2,  6), 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR ( 3,  6), 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  T (char, 9, a, a + SAR (-1,  7), 3);   /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR (-2,  8), 3);   /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR (-3,  7), 5);   /* { dg-warning "forming offset \\\[10, 13] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  T (char, 9, a + SAR (-2, -1), a, 3);
+  T (char, 9, a + SAR (-1,  1), a, 3);
+  T (char, 9, a + SAR ( 0,  1), a, 3);
+  T (char, 9, a + SAR ( 0,  2), a, 3);
+  T (char, 9, a + SAR ( 0,  3), a, 3);
+  T (char, 9, a + SAR ( 0,  6), a, 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a + SAR (-1,  7), a, 3);   /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a + SAR (-2,  8), a, 3);   /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  ssize_t i = SAR (DIFF_MIN + 1, DIFF_MAX - 4);
+  T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 1), 3);
+  T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 3), 5);
+}
+
+void foo (char *d)
+{
+  T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 3), 5);
+}
+
+/* Verify that pointer overflow in the computation done by memcpy
+   (i.e., offset + size) is detected and diagnosed.  */
+
+void test_memcpy_overflow (char *d, const char *s, size_t n)
+{
+  extern char arr[];
+
+  /* Verify that offset overflow involving an array of unknown size
+     but known access size is detected.  This works except with small
+     sizes that are powers of 2 due to bug .  */
+  T (char, 1, arr + SR (DIFF_MAX - 1, DIFF_MAX), s, 1);
+  T (char, 1, arr + SR (DIFF_MAX - 1, DIFF_MAX), s, 2);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 2 accessing array " "bug " { xfail *-*-* } } */
+  T (char, 1, arr + SR (DIFF_MAX - 2, DIFF_MAX), s, 3);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 3 accessing array " "memcpy" } */
+  T (char, 1, arr + SR (DIFF_MAX - 4, DIFF_MAX), s, 5);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 5 accessing array " "memcpy" } */
+}
+
+void test_memmove_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC memmove
+
+    T (char, 1, a + SR (DIFF_MIN + 1, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds \\\[0, 1] of object \[^\n\r]+ with type .char ?\\\[1]" } */
+  T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds \\\[0, 1] of object" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  const int *pi = (const int*)s;
+  T (int,  2, a + SR (-1, 1), pi, n);
+  T (int,  2, a + SR (-1, 2), pi, n);
+  T (int,  2, a + SR ( 0, 2), pi, n);
+  T (int,  2, a + SR ( 0, 3), pi, n);
+  T (int,  2, a + SR ( 1, 3), pi, n);
+  T (int,  2, a + SR ( 2, 3), pi, n);
+
+  T (int32_t, 2, a + SR ( 3, 4), pi, n);      /* { dg-warning "offset \\\[12, 16] is out of the bounds \\\[0, 8] of object .\[^\n\r]+. with type .int32_t ?\\\[2]." } */
+}
+
+
+void test_mempcpy_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC mempcpy
+
+  /* Verify that invalid offsets into an array of known size are
+     detected.  */
+
+  T (char, 1, a + SR (DIFF_MIN, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds"  "mempcpy" } */
+T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds"  "mempcpy" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  T (char, 1, a + UR (0, 1), s, n);
+  T (char, 1, a + UR (0, 2), s, n);
+  T (char, 1, a + UR (1, 2), s, n);
+  T (char, 1, a + UR (2, 3), s, n);       /* { dg-warning "offset \\\[2, 3] is out of the bounds \\\[0, 1] of object "  "mempcpy" } */
+  T (char, 1, a + UR (2, DIFF_MAX), s, n);  /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds \\\[0, 1] of object"  "mempcpy" } */
+
+  /* Offsets in excess of DIFF_MAX are treated as negative even if
+     they appear as large positive in the source.  It would be nice
+     if they retained their type but unfortunately that's not how
+     it works so be prepared for both in case it ever gets fixed.  */
+  T (char, 1, a + UR (3, SIZE_MAX), s, n);   /* { dg-warning "offset \\\[3, -1] is out of the bounds \\\[0, 1] of object "  "mempcpy" } */
+
+  /* Verify that invalid offsets into an array of unknown size are
+     detected.  */
+  extern char arr[];
+  T (char, 1, arr + SR (DIFF_MIN,         0), s, n);
+  T (char, 1, arr + SR (DIFF_MIN,        -1), s, n);   /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of object"  "mempcpy" } */
+  T (char, 1, arr + SR (DIFF_MIN,         1), s, n);
+  T (char, 1, arr + SR (DIFF_MIN, DIFF_MAX), s, n);
+  T (char, 1, arr + SR (       -2,        -1), s, n);   /* { dg-warning "offset \\\[-2, -1] is out of the bounds of object"  "mempcpy" } */
+  T (char, 1, arr + SR (       -1,         0), s, n);
+  T (char, 1, arr + SR (       -1,         1), s, n);
+  T (char, 1, arr + SR (       -1, DIFF_MAX), s, n);
+  T (char, 1, arr + SR (        0,         1), s, n);
+  T (char, 1, arr + SR (        0, DIFF_MAX), s, n);
+  T (char, 1, arr + SR (        1,         2), s, n);
+  T (char, 1, arr + SR (        1, DIFF_MAX), s, n);
+
+  /* Verify that all offsets via a pointer to an uknown object are
+     accepted.  */
+
+  /* Negative indices between [DIFF_MIN, DIFF_MAX] are valid since
+     the pointer to which the offset is applied can be at a positive
+     offset from the beginning of an object.  */
+  T (char, 1, d + SR (DIFF_MIN,         0), s, n);
+  T (char, 1, d + SR (DIFF_MIN,        -1), s, n);
+  T (char, 1, d + SR (DIFF_MIN,         1), s, n);
+  T (char, 1, d + SR (DIFF_MIN, DIFF_MAX), s, n);
+  T (char, 1, d + SR (       -2,        -1), s, n);
+  T (char, 1, d + SR (       -1,         0), s, n);
+  T (char, 1, d + SR (       -1,         1), s, n);
+  T (char, 1, d + SR (       -1, DIFF_MAX), s, n);
+  T (char, 1, d + SR (        0,         1), s, n);
+  T (char, 1, d + SR (        0, DIFF_MAX), s, n);
+  T (char, 1, d + SR (        1,         2), s, n);
+  T (char, 1, d + SR (        1, DIFF_MAX), s, n);
+}
+
+#define TI(type, N, init, dst, src) do {	\
+    type UNIQUE_NAME (a)[N] = init;		\
+    type *a = UNIQUE_NAME (a);			\
+    type *pd = (dst);				\
+    const type *ps = (src);			\
+    FUNC (pd, ps);				\
+    sink (a, pd, ps, s);			\
+  } while (0)
+
+void test_strcpy_bounds (char *d, const char *s)
+{
+#undef FUNC
+#define FUNC strcpy
+
+  ssize_t i;
+
+  TI (char, 1, "",   a, a + SR (DIFF_MIN, 0));
+  TI (char, 1, "",   a, a + SR (-1, 0));
+  TI (char, 1, "",   a, a + SR (-1, 1));
+  TI (char, 1, "",   a, a + SR (0, 1));
+  TI (char, 1, "",   a, a + SR (0, DIFF_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (0, DIFF_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (1, DIFF_MAX - 1));
+  /* The following needs a warning for reading past the end.  */
+  TI (char, 2, "0",  a, a + SR (2, DIFF_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (3, DIFF_MAX - 1));   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
+
+  TI (char, 3, "01", a, a + SR (0, DIFF_MAX - 1));
+  TI (char, 3, "01", a, a + SR (1, DIFF_MAX - 1));
+  TI (char, 3, "01", a, a + SR (2, DIFF_MAX - 1));
+  /* The following needs a warning for reading past the end.  */
+  TI (char, 3, "01", a, a + SR (3, DIFF_MAX - 1));
+  TI (char, 3, "01", a, a + SR (4, DIFF_MAX - 1));   /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
+
+  TI (char, 4, "012", a, a + SR (DIFF_MAX - 2, DIFF_MAX - 1));   /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds \\\[0, 4] of object \[^\n\r\]+ with type .char ?\\\[4\\\]."  "strcpy" } */
+
+
+  TI (char, 1, "", a + SR (DIFF_MIN, 0), s);
+  TI (char, 1, "", a + SR (-1, 0), s);
+  TI (char, 1, "", a + SR (-1, 1), s);
+  TI (char, 1, "", a + SR (0, 1), s);
+  TI (char, 1, "", a + SR (0, DIFF_MAX - 1), s);
+  TI (char, 2, "", a + SR (0, DIFF_MAX - 1), s);
+  TI (char, 2, "", a + SR (1, DIFF_MAX - 1), s);
+  /* The following is diagnosed not because the initial source offset
+     it out of bounds (it isn't) but because the final source offset
+     after the access has completed, is.  It would be clearer if
+     the warning mentioned the final offset.  */
+  TI (char, 2, "", a + SR (2, DIFF_MAX - 1), s);   /* { dg-warning "forming offset 3 is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
+  TI (char, 2, "", a + SR (3, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
+
+  TI (char, 3, "", a + SR (0, DIFF_MAX - 1), s);
+  TI (char, 3, "", a + SR (1, DIFF_MAX - 1), s);
+  TI (char, 3, "", a + SR (2, DIFF_MAX - 1), s);
+  TI (char, 3, "", a + SR (3, DIFF_MAX - 1), s);   /* { dg-warning "forming offset 4 is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
+  TI (char, 3, "", a + SR (4, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
+
+  TI (char, 4, "", a + SR (DIFF_MAX - 2, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds \\\[0, 4] of object \[^\n\r\]+ with type .char ?\\\[4\\\]."  "strcpy" } */
+}
+
+struct MA
+{
+  int i;
+  char a1_5[5];
+  char a2_11[11];
+};
+
+struct MA2
+{
+  struct MA ma1_3[3];
+  struct MA ma2_5[5];
+};
+
+struct MA3
+{
+  struct MA2 ma1_5[3];
+  struct MA2 ma1_7[7];
+};
+
+void test_strcpy_bounds_member_array (struct MA *pma,
+				      struct MA2 *pma2,
+				      struct MA3 *pma3,
+				      const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC(d, s, ignore) strcpy (d, s)
+
+  T (char, 1, pma->a1_5, s, 0);
+  T (char, 1, pma->a1_5 + 0, s, 0);
+  T (char, 1, pma->a1_5 + 1, s, 0);
+  T (char, 1, pma->a1_5 + 4, s, 0);
+
+  /* The following forms a pointer during the call that's outside
+     the bounds of the array it was derived from (pma->a1_5) so
+     it should be diagnosed but the representation of the pointer
+     addition doesn't contain information to distinguish it from
+     the valid pma->a2_11 + 1 so this is an XFAIL.  */
+  T (char, 1, pma->a1_5 + 5, s, 0);                 /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+  /* The following also forms an out-of-bounds pointer but similar
+     to the above, there is no reliable way to distinguish it from
+     (char*)&pma[1].i + 1 so this too is not diagnosed.  */
+  T (char, 1, pma->a1_5 + sizeof *pma + 1, s, 0);   /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+  T (char, 1, pma->a1_5 - 1, s, 0);   /* { dg-warning "offset -1 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+
+  T (char, 1, pma[1].a1_5, s, 0);
+  T (char, 1, pma[2].a1_5 + 0, s, 0);
+  T (char, 1, pma[3].a1_5 + 1, s, 0);
+  T (char, 1, pma[4].a1_5 + 4, s, 0);
+
+
+  extern struct MA3 ma3[3];
+  T (char, 1, ma3[0].ma1_5[0].ma1_3[0].a1_5 + 6, s, 0);
+}
diff --git a/gcc/testsuite/c-c++-common/Wrestrict-2.c b/gcc/testsuite/c-c++-common/Wrestrict-2.c
new file mode 100644
index 0000000..f440e7b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict-2.c
@@ -0,0 +1,70 @@
+/* PR 35503 - Warn about restricted pointers
+   Test to exercise that -Wrestrict warnings are issued for memory and
+   sring functions when they are declared in system headers (i.e., not
+   just when they are explicitly declared in the source file.)
+   Also verify that the warnings are issued even for calls where the
+   source of the aliasing violation is in a different function than
+   the restricted call.
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict" } */
+
+#include <string.h>
+
+void wrap_memcpy (void *d, const void *s, size_t n)
+{
+  memcpy (d, s, n);   /* { dg-warning "source argument is the same as destination" "memcpy" } */
+}
+
+void call_memcpy (void *d, size_t n)
+{
+  const void *s = d;
+  wrap_memcpy (d, s, n);
+}
+
+
+void wrap_strcat (char *d, const char *s)
+{
+  strcat (d, s);   /* { dg-warning "source argument is the same as destination" "strcat" } */
+}
+
+void call_strcat (char *d)
+{
+  const char *s = d;
+  wrap_strcat (d, s);
+}
+
+
+void wrap_strcpy (char *d, const char *s)
+{
+  strcpy (d, s);   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+}
+
+void call_strcpy (char *d)
+{
+  const char *s = d;
+  wrap_strcpy (d, s);
+}
+
+
+void wrap_strncat (char *d, const char *s, size_t n)
+{
+  strncat (d, s, n);   /* { dg-warning "source argument is the same as destination" "strncat" } */
+}
+
+void call_strncat (char *d, size_t n)
+{
+  const char *s = d;
+  wrap_strncat (d, s, n);
+}
+
+
+void wrap_strncpy (char *d, const char *s, size_t n)
+{
+  strncpy (d, s, n);   /* { dg-warning "source argument is the same as destination" "strncpy" } */
+}
+
+void call_strncpy (char *d, size_t n)
+{
+  const char *s = d;
+  wrap_strncpy (d, s, n);
+}
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
new file mode 100644
index 0000000..d26a628
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -0,0 +1,982 @@
+/* PR 35503 - Warn about restricted pointers
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#define SIZE_MAX __SIZE_MAX__
+#define DIFF_MAX (__SIZE_MAX__ / 2)
+#define DIFF_MIN (-DIFF_MAX - 1)
+
+#if !defined LINE
+# define LINE 0
+#endif
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+extern ptrdiff_t signed_value (void)
+{
+  extern volatile ptrdiff_t signed_value_source;
+  return signed_value_source;
+}
+
+extern size_t unsigned_value (void)
+{
+  extern volatile size_t unsigned_value_source;
+  return unsigned_value_source;
+}
+
+ptrdiff_t signed_range (ptrdiff_t min, ptrdiff_t max)
+{
+  ptrdiff_t val = signed_value ();
+  return val < min || max < val ? min : val;
+}
+
+ptrdiff_t signed_anti_range (ptrdiff_t min, ptrdiff_t max)
+{
+  ptrdiff_t val = signed_value ();
+  return min <= val && val <= max ? min == 0 ? max + 1 : min - 1 : val;
+}
+
+size_t unsigned_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+size_t unsigned_anti_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return min <= val && val <= max ? min == DIFF_MIN ? max + 1 : min - 1 : val;
+}
+
+#define SR(min, max) signed_range ((min), (max))
+#define UR(min, max) unsigned_range ((min), (max))
+
+#define SAR(min, max) signed_anti_range ((min), (max))
+#define UAR(min, max) unsigned_anti_range ((min), (max))
+
+
+void sink (void*, ...);
+
+/* Exercise memcpy with constant or known arguments.  */
+
+void test_memcpy_cst (void *d, const void *s)
+{
+#undef T
+#define T(dst, src, n) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+      void *pd = (dst);					\
+      const void *ps = (src);				\
+      memcpy (pd, ps, (n));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T (a, a, 0);
+  T (a, s = a, 3);           /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  /* This isn't detected because memcpy calls with small power-of-2 sizes
+     are intentionally folded into safe copies equivalent to memmove.
+     It's marked xfail only because there is value in detecting such
+     invalid calls for portability, and as a reminder of why it isn't
+     diagnosed.  */
+  T (a, a + 1, 1);           /* { dg-warning "\\\[-Wrestrict" "memcpy with a small power of 2 size" { xfail *-*-* } } */
+  T (a, a + 3, 3);
+  T (a, a + 3, 5);           /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  {
+    char a[3] = { 1, 2, 3 };
+
+    /* Verify that a call to memcpy with an exact overlap is diagnosed
+       (also tested above) but an excplicit one to __builtin_memcpy is
+       not.  See bug 32667 for the rationale.  */
+    (memcpy)(a, a, sizeof a);   /* { dg-warning "source argument is the same as destination" "memcpy" } */
+    sink (a);
+
+    __builtin_memcpy (a, a, sizeof a);
+    sink (a);
+  }
+
+  {
+    char a[3][7];
+    sink (a);
+
+    void *d = a[0];
+    const void *s = a[1];
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0];
+    s = a[1];
+    /* The following is only diagnosed for sizes that aren't small
+       powers of 2.  */
+    memcpy (d, s, sizeof a[0] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&a);
+  }
+
+  {
+    struct {
+      char a[7];
+      char b[7];
+      char c[7];
+    } x;
+    sink (&x);
+
+    void *d = x.a;
+    const void *s = x.b;
+    memcpy (d, s, sizeof x.a);
+    sink (&x);
+
+    d = x.a;
+    s = x.a;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 4;
+    s = x.b;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 6;
+    s = x.b;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b;
+    memcpy (d, s, 3);             /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b + 1;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 1;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 2;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 3;
+    memcpy (d, s, sizeof x.b);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+  }
+
+  {
+#undef T
+#define T(dst, src, n) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };	\
+      memcpy ((dst), (src), (n));			\
+      sink (a);						\
+    }							\
+    } while (0)
+
+    /* Verify the offset of the overlap is the same regardless of whether
+       the destination is at lower or higher offset than the source.  */
+    T (a, a + 1, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 1 overlaps 4 bytes at offset 1" "memcpy" } */
+    T (a, a + 2, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "memcpy" } */
+    T (a, a + 3, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 3 overlaps 2 bytes at offset 3" "memcpy" } */
+
+    T (a + 1, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 1 and 0 overlaps 4 bytes at offset 1" "memcpy" } */
+    T (a + 2, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 2 and 0 overlaps 3 bytes at offset 2" "memcpy" } */
+    T (a + 3, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 3 and 0 overlaps 2 bytes at offset 3" "memcpy" } */
+  }
+}
+
+/* Exercise memcpy with destination or source offset or size in
+   a determinate range.  */
+
+void test_memcpy_range (char *d, size_t sz)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    if (!LINE || LINE == __LINE__) {		\
+      char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };	\
+      void *pd = (dst);					\
+      const void *ps = (src);				\
+      memcpy (pd, ps, (n));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  ptrdiff_t ir = SR (2, 3);
+  T (a + ir, a, 0);
+  T (a + ir, a, 1);
+  T (a + ir, a, 2);
+  T (a + ir, a, 3);
+  /* The following fails because the size is a small power of 2.  */
+  T (a + ir, a, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps between 1 and 2 bytes at offset \\\[3, 2]" "memcpy" { xfail *-*-*} } */
+  T (a + ir, a, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  T (d + ir, d, 0);
+  T (d + ir, d, 1);
+  T (d + ir, d, 2);
+  T (d + ir, d, 3);
+  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 1 byte at offset 3" "bug 79220" { xfail *-*-* } } */
+  T (d + ir, d, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  /* Because the size is constant and a power of 2 the following is
+     folded too early to detect the overlap.  */
+  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+  T (d + ir, d, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  /* Exercise the full range of size_t.  */
+  T (d + sz, d, 0);
+  T (d + sz, d, 1);
+  T (d + sz, d, 9);
+
+  T (a, a + 1, SR (0, 1));
+  T (a, a + 1, SR (0, 2));
+  T (a, a + 1, SR (1, 2));
+  T (a, a + 1, SR (2, 3));         /* { dg-warning "accessing between 2 and 3 bytes at offsets 0 and 1 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+  T (a, a + 1, UR (2, DIFF_MAX + (size_t)1));  /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a, a + 1, UR (2, SIZE_MAX - 1));        /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a, a + 2, SR (2, 3));
+  T (a, a + 2, SR (3, 4));         /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and 2 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+  T (a, a + 3, SR (3, 4));
+  T (a, a + 3, SR (4, 5));         /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and 3 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+  T (a, a + 3, SR (5, 6));         /* { dg-warning "accessing between 5 and 6 bytes at offsets 0 and 3 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+  T (a + 1, a, SR (0, 1));
+  T (a + 1, a, SR (0, 2));
+  T (a + 1, a, SR (1, 2));
+  T (a + 1, a, SR (2, 3));         /* { dg-warning "accessing between 2 and 3 bytes at offsets 1 and 0 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+  T (a + 1, a, UR (2, DIFF_MAX + (size_t)1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a + 1, a, UR (2, SIZE_MAX - 1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a + 2, a, SR (2, 3));
+  T (a + 2, a, SR (3, 4));         /* { dg-warning "accessing between 3 and 4 bytes at offsets 2 and 0 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+  T (a + 3, a, SR (3, 4));
+  T (a + 3, a, SR (4, 5));         /* { dg-warning "accessing between 4 and 5 bytes at offsets 3 and 0 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+  T (a + 3, a, SR (5, 6));         /* { dg-warning "accessing between 5 and 6 bytes at offsets 3 and 0 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+  ir = SR (2, 5);
+  T (a, a + ir, 4);
+  T (a, a + ir, 5);                /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] overlaps between 1 and 3 bytes at offset \\\[4, 2]" "memcpy" } */
+  T (a, a + ir, 6);                /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] overlaps between 3 and 4 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  /* Below, there are two possible regions for the source of the copy:
+       1) one just before the high end of the address space that's 3
+          bytes large close to the lower end of the offset range, and
+       2) another in the [DIFF_MIN, -8] range from D and so at least
+          8 bytes in size
+     A copy from (1) overlaps but one from (2) does not.  Verify that
+     the copy is not diagnosed.  (This test case was reduced from
+     the Linux kernel.) */
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 5);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 6);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 7);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 9);
+
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 5);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 6);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 7);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 9);
+
+  {
+    /* Create an offset in the range [0, -1].  */
+    size_t o = sz << 1;
+    T (d, d + o, 12345);
+    T (d + o, d, 23456);
+  }
+
+  /* Exercise memcpy with both destination and source pointer offsets
+     in some known range.  */
+  size_t n = UR (3, 4);
+  T (a + SR (1, 3), a + SR (1, 3), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3] and \\\[1, 3] overlaps between 1 and 4 bytes at offset \\\[3, 1]" "memcpy" } */
+  T (a + SR (1, 3), a + SR (2, 3), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3] and \\\[2, 3] overlaps between 1 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+  T (a + SR (1, 3), a + SR (3, 4), n);
+
+  T (a + SR (2, 3), a + SR (3, 4), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[2, 3] and \\\[3, 4] overlaps between 1 and 3 bytes at offset \\\[4, 3]" "memcpy" } */
+
+  T (a + SR (1, 3), a + SR (4, 5), n);
+  T (a + SR (2, 3), a + SR (4, 5), n);
+  T (a + SR (3, 4), a + SR (4, 5), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[3, 4] and \\\[4, 5] overlaps between 1 and 3 bytes at offset \\\[5, 4]" "memcpy" } */
+
+  /* Exercise the full range of size_t.  */
+  T (d, d + sz, 0);
+  T (d, d + sz, 1);
+  T (d, d + sz, 9);
+}
+
+/* Exercise memcpy with offset and/or size in a determinate anti-range.  */
+
+void test_memcpy_anti_range (char *d, const char *s)
+{
+  T (d, d + SAR (0, 3), 1);
+  T (d, d + SAR (0, 3), 2);
+  T (d, d + SAR (0, 3), 3);
+  T (d, d + SAR (0, 3), DIFF_MAX - 2);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 2" } */
+  T (d, d + SAR (0, 3), DIFF_MAX - 1);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 1" } */
+  T (d, d + SAR (0, 3), DIFF_MAX);       /* { dg-warning "overlaps \[0-9\]+ bytes at offset 0" } */
+
+  T (d, d + SAR (0, 3), UR (DIFF_MAX - 2, DIFF_MAX));               /* { dg-warning "accessing \[0-9\]+ or more bytes at offsets 0 and \\\[-?\[0-9\]+, -?\[0-9\]+] overlaps \[0-9\]+ bytes at offset 2" } */
+
+  /* Verify that a size in an anti-range ~[0, N] where N >= PTRDIFF_MAX
+     doesn't trigger a warning.  */
+  T (d, s, UAR (1, DIFF_MAX - 1));
+  T (d, s, UAR (1, DIFF_MAX));
+  T (d, s, UAR (1, SIZE_MAX - 1));
+
+  /* This causes the last dg-warning test to fail for some reason.
+     T (d, s, UAR (1, SIZE_MAX)); */
+}
+
+/* Verify calls to memcpy() where the combination of offsets in some
+   range and size is such that either overlap is unavoidable or one
+   or both offsets would exceed the maximum size of an object
+   (DIFF_MAX).  */
+
+void test_memcpy_range_exceed (char *d, const char *s)
+{
+  /* Verify offset and size both in some range.  The memcpy checking
+     is less strict than that of string functions like strncpy and
+     doesn't trigger unless the overlap is certain.  The following
+     overlaps for (r == 3 && n > 3) but not, for example, for
+     (r == 4 && n == 4), and so it's not diagnosed.  */
+  ptrdiff_t i = SR (3, 5);
+  size_t n = UR (4, 6);
+
+  T (a, a + i, n);
+  T (a + i, a, n);
+  /* Ditto for objects of unknown sizes.  */
+  T (d, d + i, n);
+  T (d + i, d, n);
+
+  /* Verify that a warning is issued for a copy between two regions
+     whose aggregate size would exceed DIFF_MAX if it were to not
+     overlap.  */
+  T (d, s, DIFF_MAX / 2);
+  T (d, s, DIFF_MAX / 2 + 1);   /* { dg-warning "overlaps 1 byte" "memcpy" } */
+  T (d, s, DIFF_MAX / 2 + 2);   /* { dg-warning "overlaps 3 bytes" "memcpy" } */
+  T (d, s, DIFF_MAX / 2 + 3);   /* { dg-warning "overlaps 5 bytes" "memcpy" } */
+
+  i = SR (DIFF_MAX - 2, DIFF_MAX);
+
+  /* Verify a warning for an out-of-bounds offset range and constant
+     size addition.  */
+  T (d, d + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, d, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+  T (d + 1, d + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, d + 1, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+  /* Verify that the warnings above are independent of whether the source
+     and destination are known to be based on the same object.  */
+  T (d, s + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, s, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+  T (d + 1, s + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, s + 1, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+#if __SIZEOF_SIZE_T__ == 8
+  /* Verfiy the offset and size computation is correct.  The overlap
+     offset mentioned in the warning plus sthe size of the access must
+     not exceed DIFF_MAX.  */
+  T (d, d + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+
+  T (d, s + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+  T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+#elif __SIZEOF_SIZE_T__ == 4
+  T (d, d + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+
+  T (d, s + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+  T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+#endif
+
+  ptrdiff_t j = SR (DIFF_MAX - 9, DIFF_MAX - 1);
+  i = SR (DIFF_MAX - 5, DIFF_MAX - 1);
+  n = UR (4, 5);
+  T (d + i, d + j, n);
+
+  n = UR (4, DIFF_MAX - 1);
+  T (d + i, d + j, n);
+
+  n = UR (4, SIZE_MAX - 1);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 8, DIFF_MAX - 1);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 7, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+  j = SR (DIFF_MAX - 6, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+  n = UR (3, DIFF_MAX);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 6, DIFF_MAX - 1);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 5, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+  j = SR (DIFF_MAX - 4, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+  j = SR (DIFF_MAX - 2, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+}
+
+/* Exercise memcpy with destination and source of unknown size.  */
+
+void test_memcpy_var (char *d, const char *s)
+{
+  size_t n = unsigned_value ();
+
+  memcpy (d, d, 0);
+  sink (d);
+
+  memcpy (d, d, n);               /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  memcpy (d, &d[0], n);           /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  memcpy (&d[0], d,  n);          /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  /* The following overlaps if n is greater than 1.  */
+  s = d + 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + n;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + signed_value ();
+  memcpy (d, s, unsigned_value ());
+  sink (d);
+
+  s = d + 3;
+  n = 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 2;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 3;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 4;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+  sink (d);
+
+  s = d + 5;
+  n = 7;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  n = UR (0, 1);
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+}
+
+/* Exercise the absence of warnings with memmove.  */
+
+void test_memmove (void)
+{
+  {
+    char d[7];
+    sink (d);
+
+    const void *s = d;
+    memmove (d, s, 7);
+    sink (d);
+
+    s = d + 1;
+    memmove (d, s, 6);
+    sink (d);
+
+    s = d + 2;
+    memmove (d + 1, s, 5);
+    sink (d);
+  }
+}
+
+/* Exercise strcat with constant or known arguments.  */
+
+void test_strcat_cst (const char *s)
+{
+#undef T
+#define T(init, dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcat (pd, ps);					\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("0",   a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T ("01",  a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  /* The 3 bytes "12\0" being appended to "012" overwrite the final NUL.  */
+  T ("012", a, a + 1);            /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a, a + 2);            /* { dg-warning "accessing 2 bytes at offsets 0 and 2 overlaps 1 byte at offset 3" "strcat" } */
+  /* The nul copied from a[3] to a[3] overwrites itself so this is
+     diagnosed.  */
+  T ("012", a, a + 3);            /* { dg-warning "accessing 1 byte at offsets 0 and 3 overlaps 1 byte at offset 3" "strcat" } */
+
+  T ("012", a, a + 4);
+  T ("012", a, a + 5);
+  T ("012", a, a + 6);
+  T ("012", a, a + 7);
+  T ("012", a, a + 8);
+
+  T ("0",   a + 1, a);            /* { dg-warning "accessing 2 bytes at offsets 1 and 0 overlaps 1 byte at offset 1" "strcat" } */
+  T ("0",   a + 2, a);
+
+  /* The first of the two offsets in the diagnostic for strcat is that
+     of the first write into the destination, not that of the initial
+     read from it to compute its length.  */
+  T ("01",  a + 1, a);            /* { dg-warning "accessing 3 bytes at offsets 1 and 0 overlaps 1 byte at offset 2" "strcat" } */
+  T ("01",  a + 2, a);            /* { dg-warning "accessing 3 bytes at offsets 2 and 0 overlaps 1 byte at offset 2" "strcat" } */
+  T ("01",  a + 3, a);
+
+  T ("012", a + 1, a);            /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3 " "strcat" } */
+  T ("012", a + 4, a);
+  T ("012", a + 5, a);
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T ("012",      a, "012");
+  T ("012",      a, s);
+  T ("01234567", a, s);
+}
+
+/* Exercise strcat with destination and source of unknown length.  */
+
+void test_strcat_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcat (pd, ps);					\
+      sink (pd, ps);					\
+    }							\
+  } while (0)
+
+  T (d, d);                       /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T (d, d + 1);                   /* { dg-warning "accessing 0 or more bytes at offsets 0 and 1 may overlap 1 byte" "strcat" } */
+  T (d, d + 2);                   /* { dg-warning "accessing 0 or more bytes at offsets 0 and 2 may overlap 1 byte" "strcat" } */
+  T (d, d + 999);                 /* { dg-warning "accessing 0 or more bytes at offsets 0 and 999 may overlap 1 byte" "strcat" } */
+  T (d, d + -99);                 /* { dg-warning "accessing 0 or more bytes at offsets 0 and -99 may overlap 1 byte" "strcat" } */
+
+  size_t n = unsigned_value ();
+
+  T (d + n, d + n);                       /* { dg-warning "\\\[-Wrestrict" "strcat" } */
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T (d, "012");
+  T (d + 1, "0123");
+  T (d + n, "01234");
+  T (d, s);
+  T (d + 1, s);
+  T (d + n, s);
+
+  /* Since the offset is unknown the overlap in the call below, while
+     possible, is certainly not inevitable.  Conservatively, it should
+     not be diagnosed.  For safety, an argument for diagnosing can be
+     made.  It's a judgment call, partly determined by the effort and
+     complexity of treating this case differently from other similar
+     to it.   */
+  T (d, d + n); /* { dg-warning "may overlap" "strcat" } */
+}
+
+/* Exercise strcpy with constant or known arguments.  */
+
+void test_strcpy_cst (ptrdiff_t i)
+{
+#undef T
+#define T(init, dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[8] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcpy (pd, ps);					\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcpy" } */
+  T ("012", a, a + 1);            /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strcpy" } */
+  T ("012", a, a + 2);
+  T ("012", a, a + 3);
+  /* The following doesn't overlap but it should trigger -Wstringop-overflow
+     for reading past the end.  */
+  T ("012", a, a + sizeof a);
+
+  /* The terminating nul written to d[2] overwrites s[0].  */
+  T ("0123", a, a + 2);           /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
+
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("01234", a, a + 2);          /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" } */
+
+  /* This happens to be safe in GCC but it's still wrong.  */
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  T ("012", a + 1, a);            /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 3 bytes at offset 1" "strcpy" } */
+  T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 2 bytes at offset 2" "strcpy" } */
+  T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3" "strcpy" } */
+  T ("012", a + 4, a);
+  /* The following doesn't overlap but it should trigger -Wstrinop-ovewrflow
+     for writing past the end.  */
+  T ("012", a + sizeof a, a);
+}
+
+/* Exercise strcpy with constant or known arguments offset by a range.
+   The tests verify the use of the lower bound of the range which is
+   more restrictive than using the upper bound for positive values.  */
+
+void test_strcpy_range (void)
+{
+#undef T
+#define T(N, init, dst, src)			\
+  do {						\
+    if (!LINE || LINE == __LINE__) {		\
+      char a[N] = init;				\
+      char *pd = (dst);				\
+      const char *ps = (src);			\
+      strcpy (pd, ps);				\
+      sink (a, pd, ps);				\
+    }						\
+  } while (0)
+
+  ptrdiff_t r;
+
+  r = SR (0, 1);
+  T (8, "0", a + r, a);   /* { dg-warning "accessing between 1 and 2 bytes at offsets \\\[0, 1] and 0 overlaps up to 2 bytes at offset \\\[0, 1]" "strcpy" { xfail *-*-*} } */
+
+  r = SR (2, 5);
+  T (8, "01",  a + r, a);            /* { dg-warning "accessing 3 bytes at offsets \\\[2, 5] and 0 may overlap 1 byte at offset 2" } */
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[2, 5] and 0 may overlap up to 2 bytes at offset \\\[3, 2]" "strcpy" } */
+
+  /* The highest offset to which to copy without overflowing the 8-byte
+     destination is 3 and that overlaps 2 bytes.  */
+  T (8, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "strcpy" } */
+
+  /* With a 9-byte destination the highest offset is 4 and that still
+     overlaps 1 byte (the final NUL).  */
+  T (9, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 1 and 3 bytes at offset \\\[4, 2]" "strcpy" } */
+
+  /* With a 10-byte buffer it's possible to copy all 5 bytes without
+     overlap at (a + 5).  Copying at offsets 2 through 4 overflows
+     between 3 and 1 bytes, respectively.  */
+  T (10, "0123", a + r, a);          /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 may overlap up to 3 bytes at offset \\\[4, 2]" "strcpy" } */
+
+
+  r  = SR (3, 4);
+  T (8, "01",  a + r, a);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[3, 4] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+  /* The highest offset to which to copy without overflowing the 8-byte
+     destination is 3 and that overlaps 2 bytes.  */
+  T (8, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps 2 bytes at offset 3" "strcpy" } */
+
+  /* With a 9-byte destination the highest offset is 4 and that still
+     overlaps 1 byte (the final NUL).  */
+  T (9, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[4, 3]" "strcpy" } */
+
+  /* With a 10-byte buffer it's possible to copy all 5 bytes without
+     overlap at (a + 5).  Copying at offsets 2 through 4 overflows
+     between 3 and 1 bytes, respectively.  */
+  T (10, "0123", a + r, a);          /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[4, 3]" "strcpy" } */
+
+  T (8, "01",     a, a + r);
+  T (8, "012",    a, a + r);
+  T (8, "0123",   a, a + r);
+  T (8, "01234",  a, a + r);
+
+  /* With the smaller offset of 3 the final NUL definitely overlaps
+     the '4' at a[3], but with the larger offset of 4 there is no
+     overlap, so the warning is a "may overlap" and the size of
+     the overlap is 1 byte.  */
+  T (8, "012345", a, a + r);         /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and \\\[3, 4] may overlap 1 byte at offset 3" "strcpy" } */
+  T (8, "0123456", a, a + r);        /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and \\\[3, 4] may overlap up to 2 bytes at offset 3" "strcpy" } */
+
+  r = SR (3, DIFF_MAX - 3);
+  T (8, "01",  a + r, a);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[3, \[0-9\]+] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+  r = SR (DIFF_MAX - 2, DIFF_MAX - 1);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps" "strcpy" } */
+
+  /* Exercise the full range of ptrdiff_t.  */
+  r = signed_value ();
+
+  /* The overlap in the cases below isn't inevitable but it is diagnosed
+     because it is possible and so the code is considered unsafe.  */
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap 1 byte" "strcpy" } */
+  T (8, "0", a + r, a);              /* { dg-warning "accessing 2 bytes may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes may overlap up to 4 bytes" "strcpy" } */
+
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap" "strcpy" } */
+  T (8, "0", a, a + r);              /* { dg-warning "accessing between 0 and 2 bytes may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a, a + r);            /* { dg-warning "accessing between 0 and 4 bytes may overlap up to 4 bytes" "strcpy" } */
+}
+
+/* Exercise strcpy with destination and/or source of unknown lengthu.  */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcpy (pd, ps);					\
+      sink (pd, ps);					\
+    }							\
+  } while (0)
+
+  T (d, s);
+
+  T (d, &d[0]);                   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+  T (&d[0], d);                   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  s = d;
+  T (d, s);                       /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  /* The following overlaps if *s is not nul.  It arguably should be
+     diagnosed.  */
+  T (d, d + 1);
+
+  /* The following overlaps if strlen (d) is greater than 1.  Like
+     the above, it possibly should be diagnosed too.  */
+  int r = SR (2, 3);
+  T (d, d + r);
+
+  /* The following overlaps only if strlen (s + n) >= n so it's not
+     diagnosed.  */
+  s = d + signed_value ();
+  T (d, s);
+}
+
+/* Exercise strncpy with constant or known arguments.  */
+
+void test_strncpy_cst (void)
+{
+#undef T
+#define T(init, dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[7] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strncpy (pd, ps, (size));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("012", a, a, 0);
+  T ("012", a, a, 1);             /* { dg-warning "source argument is the same as destination " "strncpy" } */
+  T ("012", a, a + 1, 1);
+  T ("012", a, a + 1, 2);         /* { dg-warning "accessing 2 bytes at offsets 0 and 1 overlaps 1 byte at offset 1" "strncpy" } */
+  T ("012", a, a + 1, 3);         /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strncpy" } */
+  T ("012", a, a + 2, 1);
+  T ("012", a, a + 2, 2);
+  /* The third written byte (nul) overwrites a[2].  */
+  T ("012", a, a + 2, 3);         /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+
+  T ("0123", a, a + 2, 1);
+  T ("0123", a, a + 2, 2);
+  /* The terminating nul written to a[2] overwrites s[0].  */
+  T ("0123", a, a + 2, 3);        /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+
+  T ("01234", a, a + 2, 1);
+  T ("01234", a, a + 2, 2);
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("01234", a, a + 2, 4);       /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+}
+
+
+/* Exercise strncpy with one or more arguments in a determinate range.  */
+
+void test_strncpy_range (char *d, size_t n)
+{
+#undef T
+#define T(init, dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = init;					\
+      strncpy ((dst), (src), (size));			\
+      sink (a, (dst), (src));				\
+    }							\
+  } while (0)
+
+  ptrdiff_t i;
+
+  i = SR (0, 1);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);   /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[0, 1] may overlap 1 byte at offset 1" "strncpy" { xfail *-*-* } } */
+
+  i = SR (1, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);   /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[1, 5] may overlap 1 byte at offset 1" "strncpy" } */
+  T ("0123", a, a + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[1, 5] may overlap up to 2 bytes at offset \\\[2, 1]" "strncpy" } */
+
+  i = SR (2, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[2, 5] may overlap 1 byte at offset 2" "strncpy" } */
+  T ("0123", a, a + i, 4);   /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[2, 5] may overlap up to 2 bytes at offset \\\[3, 2]" "strncpy" } */
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+
+  i = SR (3, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);
+  T ("0123", a, a + i, 4);   /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[3, 5] may overlap 1 byte at offset 3" "strncpy" } */
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+
+  i = SR (4, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);
+  T ("0123", a, a + i, 4);
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+
+  /* Verify offset and size both in some range.  The strncpy checking
+     is more strict than that of memcpy and triggers even when the
+     overlap is possible but not inevitable.  The following overlaps
+     like so ('*' denotes the terminating NUL, '.' the appended NUL
+     that's not copied from the source):
+        a:        01234567*  (also indicates offset)
+        i = 4:    4567       none
+                  4567*      overlaps 1 at offset 4
+                  4567*.     overlaps 2 at offset 4
+        i = 5:    567*       none
+                  567*.      none
+                  567*..     overlaps 1 at offset 5  */
+  T ("01234567", a, a + i, UR (4, 6));   /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  /* Ditto for objects of unknown sizes.  */
+  T ("01234567", d, d + i, UR (4, 6));  /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  T ("01234567", a, a + i, UR (6, 7));  /* { dg-warning "accessing between 6 and 7 bytes at offsets 0 and \\\[4, 5] overlaps between 1 and 3 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  /* The following overlaps except in the unlikely case that value ()
+     is zero, so it's diagnosed.  */
+  T ("012", a, a, n);             /* { dg-warning "source argument is the same as destination " "strncpy" } */
+}
+
+
+/* Exercise strncpy with destination and source of unknown length.  */
+
+void test_strncpy_var (char *d, const char *s, size_t n)
+{
+#undef T
+#define T(dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {		\
+      char *pd = (dst);				\
+      const char *ps = (src);			\
+      strncpy (pd, ps, (size));			\
+      sink (pd, ps);				\
+    }						\
+  } while (0)
+
+  T (d, s, 1);
+  T (d, s, n);
+
+  T (d, d, 1);                    /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+  T (d, d, n);                    /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+
+  T (d,     d + 1, 1);
+  T (d,     d + 1, 2);            /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+  T (d + 1, d,     1);
+  T (d + 1, d,     2);            /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+}
+
+struct MemberArrays
+{
+  char a[7];
+  char b[8];
+  char c[9];
+};
+
+void test_strncpy_strcpy_var (struct MemberArrays *ar, const char *s)
+{
+  /* The following is safe and should not trigger a warning.  */
+  strncpy (ar->b, s, sizeof ar->b - 1);
+  ar->b[sizeof ar->b - 1] = '\0';
+  strcpy (ar->a, ar->b);
+  sink (ar);
+
+  /* The following is not as safe (it might overflow ar->a) but there
+     is no overlap so it also shouldn't trigger -Wrestrict.  */
+  strncpy (ar->c, s, sizeof ar->c - 1);
+  ar->c[sizeof ar->c - 1] = '\0';
+  strcpy (ar->a, ar->c);
+  sink (ar);
+}
diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
index 7861bb0..4281e3b 100644
--- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
@@ -1,7 +1,7 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument" } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */
 /* { dg-require-effective-target alloca } */
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
index f7bfa35..d9a1555 100644
--- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
+++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
@@ -1,7 +1,7 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
-/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -Wno-stringop-truncation -Wno-c++-compat -ftrack-macro-expansion=0" {target c} } */
+/* { dg-options "-Wall -O2 -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
+/* { dg-options "-Wall -O2 -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-truncation -Wno-c++-compat -ftrack-macro-expansion=0" {target c} } */
 /* { dg-require-effective-target alloca } */
 
 #define bos(ptr) __builtin_object_size (ptr, 1)
diff --git a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
index 5bc5c4c..16340eb 100644
--- a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
+++ b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
@@ -1,6 +1,6 @@
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
+// { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
 // Test just twice, once with -O0 non-fortified, once with -O2 fortified.
 // { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } }
 // { dg-skip-if "" { *-*-* }  { "-flto" } { "" } }
diff --git a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
index f2c864b..2dff8f0 100644
--- a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
+++ b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
@@ -1,6 +1,6 @@
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
+// { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
 // Test just twice, once with -O0 non-fortified, once with -O2 fortified,
 // suppressing buffer overflow warnings.
 // { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } }
diff --git a/gcc/testsuite/gcc.dg/Wrestrict-2.c b/gcc/testsuite/gcc.dg/Wrestrict-2.c
new file mode 100644
index 0000000..d73e144
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wrestrict-2.c
@@ -0,0 +1,41 @@
+/* Test to verify that the temporary doesn't trigger a bogus -Warray-bounds
+   warning.  Distilled from libat_exchange_large_inplace in libatomic/gexch.c.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" }  */
+
+typedef typeof (sizeof 0) size_t;
+
+extern void *memcpy (void*, const void*, size_t);
+
+void libat_exchange_large_inplace (size_t n, void *mptr, void *vptr)
+{
+  char temp[1024];
+
+  size_t i = 0;
+
+  for (i = 0; n >= 1024; i += 1024, n -= 1024)
+    {
+      memcpy (temp, mptr + i, 1024);
+
+      /* The memcpy call below results in the following:
+	 unsigned long ivtmp.7;
+
+	 ivtmp.7_4 = (unsigned long) mptr_9(D);
+	 ...
+	 <bb 4>
+	 # ivtmp.7_22 = PHI <ivtmp.7_4(3), ivtmp.7_5(4)>
+	 ...
+	 _1 = (void *) ivtmp.7_22;
+	 ...
+	 memcpy (_1, _2, 1024);
+
+	 Treating _1 as a pointer results in the bogus:
+	   warning: 'memcpy' offset 0 is out of the bounds [0, 8] of object 'ivtmp.7' with type 'long unsigned int' [-Warray-bounds]
+	   memcpy (mptr + i, vptr + i, 1024);
+	   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+      */
+      memcpy (mptr + i, vptr + i, 1024);
+
+      memcpy (vptr + i, temp, 1024);
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/Wrestrict.c b/gcc/testsuite/gcc.dg/Wrestrict.c
new file mode 100644
index 0000000..076f878
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wrestrict.c
@@ -0,0 +1,34 @@
+/* Test to verify that VLAs are handled gracefully by -Wrestrict
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict" }  */
+
+typedef __SIZE_TYPE__ size_t;
+
+#define memcpy(d, s, n)  __builtin_memcpy (d, s, n)
+#define strcpy(d, s)     __builtin_strcpy (d, s)
+
+void test_vla (void *d, const char *s1, const char *s2, int i, size_t n)
+{
+  char a[n];
+  char b[n];
+
+  strcpy (a, s1);
+  strcpy (b, s2);
+
+  memcpy (d, i ? a : b, n);
+}
+
+
+void test_vla_member (void *d, const char *s1, const char *s2, int i, size_t n)
+{
+  struct S
+  {
+    char a[n];
+    char b[n];
+  } s;
+
+  strcpy (s.a, s1);
+  strcpy (s.b, s2);
+
+  memcpy (d, i ? s.a : s.b, n);
+}
diff --git a/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
index f4e8552..4d14de2 100644
--- a/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
 /* { dg-require-effective-target alloca } */
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/gcc.dg/memcpy-6.c b/gcc/testsuite/gcc.dg/memcpy-6.c
new file mode 100644
index 0000000..1d76e7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/memcpy-6.c
@@ -0,0 +1,40 @@
+/* Test to verify that overlapping memcpy with const sizes that are powers
+   of two are folded into into the same code as memmove, but that they
+   are diagnosed nonetheless.
+   { dg-do compile }
+   { dg-options "-O0 -Wrestrict -fdump-tree-optimized" } */
+
+char a[32];
+
+void fold_copy_2 (void)
+{
+  __builtin_memcpy (a + 1, a, 2);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_4 (void)
+{
+  __builtin_memcpy (a + 2, a, 4);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_8 (void)
+{
+  __builtin_memcpy (a + 3, a, 8);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_move_2 (void)
+{
+  __builtin_memmove (a + 1, a, 2);
+}
+
+void fold_move_4 (void)
+{
+  __builtin_memmove (a + 2, a, 4);
+}
+
+void fold_move_8 (void)
+{
+  __builtin_memmove (a + 3, a, 8);
+}
+
+/* { dg-final { scan-tree-dump-not "memcpy" "optimized" } }
+   { dg-final { scan-tree-dump-not "memmove" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c
index c0e7463..908d5a6 100644
--- a/gcc/testsuite/gcc.dg/pr69172.c
+++ b/gcc/testsuite/gcc.dg/pr69172.c
@@ -1,4 +1,5 @@
-/* PR tree-optimization/69172 */
+/* PR tree-optimization/69172 - ICE in make_ssa_name_fn,
+   at tree-ssanames.c:266 */
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
 
@@ -43,3 +44,7 @@ f6 (int x)
 {
   return __builtin___mempcpy_chk (&a, &a, x, 0);
 }
+
+/* The calls above violate strict aliasing.  Eliminate the -Wrestrict
+   warnings they trigger.
+  { dg-prune-output "\\\[-Wrestrict]" } */
diff --git a/gcc/testsuite/gcc.dg/pr79223.c b/gcc/testsuite/gcc.dg/pr79223.c
index 295d5c1..ef0dd1b 100644
--- a/gcc/testsuite/gcc.dg/pr79223.c
+++ b/gcc/testsuite/gcc.dg/pr79223.c
@@ -1,6 +1,6 @@
 /* PR middle-end/79223 - missing -Wstringop-overflow on a memmove overflow
    { dg-do compile }
-   { dg-additional-options "-O2 -Wall -std=gnu99" }  */
+   { dg-additional-options "-O2 -Wall -Wno-array-bounds -std=gnu99" }  */
 
 typedef __SIZE_TYPE__ size_t;
 
diff --git a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
index cd9dc72..11367d1 100644
--- a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" } */
 /* Test just twice, once with -O0 non-fortified, once with -O2 fortified.  */
 /* { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } } */
 /* { dg-skip-if "" { *-*-* }  { "-flto" } { "" } } */
diff --git a/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c b/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c
new file mode 100644
index 0000000..1be4922
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c
@@ -0,0 +1,68 @@
+/* { dg-do compile { target { ! x32 } } }
+   { dg-require-effective-target mempcpy }
+   { dg-options "-O2 -Wrestrict -fcheck-pointer-bounds -mmpx" } */
+
+#define USE_GNU
+#include "../../gcc.dg/strlenopt.h"
+
+/* There is no BUILT_IN_ST{P,R}NCPY_CHKP or BUILT_IN_STRNCAT_CHKP
+   so the test for them below are XFAIL.  */
+char *stpncpy (char *__restrict, const char *__restrict, size_t);
+char *strncpy (char *__restrict, const char *__restrict, size_t);
+char *strncat (char *__restrict, const char *__restrict, size_t);
+
+
+char a[8];
+
+void test_memcpy (void)
+{
+  memcpy (a, a + 1, 3);   /* { dg-warning ".memcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+void test_memmove (void)
+{
+  memmove (a, a + 1, 3);
+}
+
+void* test_mempcpy (void)
+{
+  return mempcpy (a, a + 1, 3);   /* { dg-warning ".mempcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+char* test_stpcpy (void)
+{
+  strcpy (a, "0123456");
+  return stpcpy (a, a + 2);   /* { dg-warning ".stpcpy\.chkp. accessing 6 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" } */
+}
+
+char* test_stpncpy (void)
+{
+  strcpy (a, "0123456");
+
+  /* There is no BUILT_IN_STPNCPY_CHKP so this isn't handled.  */
+  return stpncpy (a, a + 2, sizeof a);   /* { dg-warning ".stpcpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strcpy (void)
+{
+  strcpy (a, "0123456");
+  strcpy (a, a + 1);   /* { dg-warning ".strcpy\.chkp. accessing 7 bytes at offsets 0 and 1 overlaps 6 bytes at offset 1" } */
+}
+
+void test_strcat (int n)
+{
+  strcat (a, a + 3);   /* { dg-warning ".strcat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" } */
+}
+
+void test_strncat (int n)
+{
+  strncat (a, a + 3, sizeof a);   /* { dg-warning ".strncat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strncpy (int n)
+{
+  strcpy (a, "0123456");
+
+  /* There is no BUILT_IN_STRNCPY_CHKP so this isn't handled.  */
+  strncpy (a, a + 2, sizeof a);   /* { dg-warning ".strncpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 5 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 9777308..3c9ff35 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -459,6 +459,7 @@ extern gimple_opt_pass *make_pass_build_cgraph_edges (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_local_pure_const (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_nothrow (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tracer (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_warn_restrict (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_warn_unused_result (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_diagnose_tm_blocks (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_lower_tm (gcc::context *ctxt);
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 48b9241..554311d 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ssa.h"
 #include "cgraph.h"
 #include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
 #include "fold-const.h"
 #include "stor-layout.h"
 #include "gimple-fold.h"
@@ -173,6 +174,7 @@ struct laststmt_struct
 } laststmt;
 
 static int get_stridx_plus_constant (strinfo *, unsigned HOST_WIDE_INT, tree);
+static void handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *);
 
 /* Return:
 
@@ -1386,7 +1388,7 @@ static void
 handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, len, lhs, args, type, fn, oldlen;
+  tree src, dst, srclen, len, lhs, type, fn, oldlen;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi, *olddsi, *zsi;
@@ -1502,6 +1504,22 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 	    }
 	}
       dsi->stmt = stmt;
+
+      /* Try to detect overlap before returning.  This catches cases
+	 like strcpy (d, d + n) where n is non-constant whose range
+	 is such that (n <= strlen (d) holds).
+
+	 OLDDSI->NONZERO_chars may have been reset by this point with
+	 oldlen holding it original value.  */
+      if (olddsi && oldlen)
+	{
+	  /* Add 1 for the terminating NUL.  */
+	  tree type = TREE_TYPE (oldlen);
+	  oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
+				build_int_cst (type, 1));
+	  check_bounds_or_overlap (stmt, olddsi->ptr, src, oldlen, NULL_TREE);
+	}
+
       return;
     }
 
@@ -1574,14 +1592,31 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (zsi != NULL)
     zsi->dont_invalidate = true;
 
-  if (fn == NULL_TREE)
-    return;
-
-  args = TYPE_ARG_TYPES (TREE_TYPE (fn));
-  type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+  if (fn)
+    {
+      tree args = TYPE_ARG_TYPES (TREE_TYPE (fn));
+      type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+    }
+  else
+    type = size_type_node;
 
   len = fold_convert_loc (loc, type, unshare_expr (srclen));
   len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
+
+  /* Set the no-warning bit on the transformed statement?  */
+  bool set_no_warning = false;
+
+  if (const strinfo *chksi = olddsi ? olddsi : dsi)
+    if (si
+	&& !check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
+      {
+	gimple_set_no_warning (stmt, true);
+	set_no_warning = true;
+      }
+
+  if (fn == NULL_TREE)
+    return;
+
   len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
 				  GSI_SAME_STMT);
   if (dump_file && (dump_flags & TDF_DETAILS) != 0)
@@ -1629,6 +1664,21 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
     }
   else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     fprintf (dump_file, "not possible.\n");
+
+  if (set_no_warning)
+    gimple_set_no_warning (stmt, true);	
+}
+
+/* Check the size argument to the built-in forms of stpncpy and strncpy
+   for out-of-bounds offsets or overlapping access, and to see if the
+   size argument is derived from a call to strlen() on the source argument,
+   and if so, issue an appropriate warning.  */
+
+static void
+handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
+{
+  /* Same as stxncpy().  */
+  handle_builtin_stxncpy (bcode, gsi);
 }
 
 /* Return true if LEN depends on a call to strlen(SRC) in an interesting
@@ -1909,9 +1959,10 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
   return false;
 }
 
-/* Check the size argument to the built-in forms of stpncpy and strncpy
-   to see if it's derived from calling strlen() on the source argument
-   and if so, issue a warning.  */
+/* Check the arguments to the built-in forms of stpncpy and strncpy for
+   out-of-bounds offsets or overlapping access, and to see if the size
+   is derived from calling strlen() on the source argument, and if so,
+   issue the appropriate warning.  */
 
 static void
 handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
@@ -1923,8 +1974,50 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
 
   bool with_bounds = gimple_call_with_bounds_p (stmt);
 
+  tree dst = gimple_call_arg (stmt, with_bounds ? 1 : 0);
   tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
   tree len = gimple_call_arg (stmt, with_bounds ? 3 : 2);
+  tree dstsize = NULL_TREE, srcsize = NULL_TREE;
+
+  int didx = get_stridx (dst);
+  if (strinfo *sidst = didx > 0 ? get_strinfo (didx) : NULL)
+    {
+      /* Compute the size of the destination string including the NUL.  */
+      if (sidst->nonzero_chars)
+	{
+	  tree type = TREE_TYPE (sidst->nonzero_chars);
+	  dstsize = fold_build2 (PLUS_EXPR, type, sidst->nonzero_chars,
+				 build_int_cst (type, 1));
+	}
+      dst = sidst->ptr;
+    }
+
+  int sidx = get_stridx (src);
+  strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL;
+  if (sisrc)
+    {
+      /* Strncpy() et al. cannot modify the source string.  Prevent
+	 the rest of the pass from invalidating the strinfo data.  */
+      sisrc->dont_invalidate = true;
+
+      /* Compute the size of the source string including the NUL.  */
+      if (sisrc->nonzero_chars)
+	{
+	  tree type = TREE_TYPE (sisrc->nonzero_chars);
+	  srcsize = fold_build2 (PLUS_EXPR, type, sisrc->nonzero_chars,
+				 build_int_cst (type, 1));
+	}
+
+	src = sisrc->ptr;
+    }
+  else
+    srcsize = NULL_TREE;
+
+  if (!check_bounds_or_overlap (stmt, dst, src, dstsize, srcsize))
+    {
+      gimple_set_no_warning (stmt, true);
+      return;
+    }
 
   /* If the length argument was computed from strlen(S) for some string
      S retrieve the strinfo index for the string (PSS->FIRST) alonng with
@@ -1938,14 +2031,6 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
       return;
     }
 
-  int sidx = get_stridx (src);
-  strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL;
-
-  /* Strncpy() et al. cannot modify the source string.  Prevent the rest
-     of the pass from invalidating the strinfo data.  */
-  if (sisrc)
-    sisrc->dont_invalidate = true;
-
   /* Retrieve the strinfo data for the string S that LEN was computed
      from as some function F of strlen (S) (i.e., LEN need not be equal
      to strlen(S)).  */
@@ -1982,17 +2067,6 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
     }
 }
 
-/* Check the size argument to the built-in forms of strncat to see if
-   it's derived from calling strlen() on the source argument and if so,
-   issue a warning.  */
-
-static void
-handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
-{
-  /* Same as stxncpy().  */
-  handle_builtin_stxncpy (bcode, gsi);
-}
-
 /* Handle a memcpy-like ({mem{,p}cpy,__mem{,p}cpy_chk}) call.
    If strlen of the second argument is known and length of the third argument
    is that plus one, strlen of the first argument is the same after this
@@ -2173,16 +2247,22 @@ static void
 handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr;
+  tree srclen, args, type, fn, objsz, endptr;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi;
-  location_t loc;
+  location_t loc = gimple_location (stmt);
   bool with_bounds = gimple_call_with_bounds_p (stmt);
 
-  src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
-  dst = gimple_call_arg (stmt, 0);
-  lhs = gimple_call_lhs (stmt);
+  tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
+  tree dst = gimple_call_arg (stmt, 0);
+
+  /* Bail if the source is the same as destination.  It will be diagnosed
+     elsewhere.  */
+  if (operand_equal_p (src, dst, 0))
+    return;
+
+  tree lhs = gimple_call_lhs (stmt);
 
   didx = get_stridx (dst);
   if (didx < 0)
@@ -2191,10 +2271,47 @@ 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);
+    }
+
+  /* Set the no-warning bit on the transformed statement?  */
+  bool set_no_warning = false;
+
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
+      {
+	  /* The concatenation always involves copying at least one byte
+	     (the terminating nul), even if the source string is empty.
+	     If the source is unknown assume it's one character long and
+	     used that as both sizes.  */
+	tree slen = srclen;
+	if (slen)
+	  {
+	    tree type = TREE_TYPE (slen);
+	    slen = fold_build2 (PLUS_EXPR, type, slen, build_int_cst (type, 1));
+	  }
+
+	tree sptr = si && si->ptr ? si->ptr : src;
+
+	if (!check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
+	  {
+	    gimple_set_no_warning (stmt, true);
+	    set_no_warning = true;
+	  }
+      }
+
       /* 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.  */
@@ -2227,20 +2344,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       return;
     }
 
-  srclen = NULL_TREE;
-  si = NULL;
-  idx = get_stridx (src);
-  if (idx < 0)
-    srclen = build_int_cst (size_type_node, ~idx);
-  else if (idx > 0)
-    {
-      si = get_strinfo (idx);
-      if (si != NULL)
-	srclen = get_string_length (si);
-    }
-
-  loc = gimple_location (stmt);
-  dstlen = dsi->nonzero_chars;
+  tree dstlen = dsi->nonzero_chars;
   endptr = dsi->endptr;
 
   dsi = unshare_strinfo (dsi);
@@ -2301,7 +2405,24 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (fn == NULL_TREE)
     return;
 
-  len = NULL_TREE;
+  if (dsi && dstlen)
+    {
+      tree type = TREE_TYPE (dstlen);
+
+      /* Compute the size of the source sequence, including the nul.  */
+      tree srcsize = srclen ? srclen : size_zero_node;
+      srcsize = fold_build2 (PLUS_EXPR, type, srcsize, build_int_cst (type, 1));
+
+      tree sptr = si && si->ptr ? si->ptr : src;
+
+      if (!check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
+	{
+	  gimple_set_no_warning (stmt, true);
+	  set_no_warning = true;
+	}
+    }
+
+  tree len = NULL_TREE;
   if (srclen != NULL_TREE)
     {
       args = TYPE_ARG_TYPES (TREE_TYPE (fn));
@@ -2376,6 +2497,9 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
     }
   else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     fprintf (dump_file, "not possible.\n");
+
+  if (set_no_warning)
+    gimple_set_no_warning (stmt, true);
 }
 
 /* Handle a call to malloc or calloc.  */
@@ -2867,11 +2991,11 @@ fold_strstr_to_strncmp (tree rhs1, tree rhs2, gimple *stmt)
     }
 }
 
-/* Attempt to optimize a single statement at *GSI using string length
-   knowledge.  */
+/* Attempt to check for validity of the performed access a single statement
+   at *GSI using string length knowledge, and to optimize it.  */
 
 static bool
-strlen_optimize_stmt (gimple_stmt_iterator *gsi)
+strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi)
 {
   gimple *stmt = gsi_stmt (*gsi);
 
@@ -3147,7 +3271,7 @@ strlen_dom_walker::before_dom_children (basic_block bb)
 
   /* Attempt to optimize individual statements.  */
   for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
-    if (strlen_optimize_stmt (&gsi))
+    if (strlen_check_and_optimize_stmt (&gsi))
       gsi_next (&gsi);
 
   bb->aux = stridx_to_strinfo;
Martin Sebor Nov. 30, 2017, 12:52 a.m. UTC | #5
On 11/27/2017 05:44 AM, Richard Biener wrote:
> On Thu, Nov 16, 2017 at 10:29 PM, Martin Sebor <msebor@gmail.com> wrote:
>> Ping.
>>
>> I've fixed the outstanding false positive exposed by the Linux
>> kernel.  The kernel builds with four instances of the warning,
>> all of them valid (perfect overlap in memcpy).
>>
>> I also built Glibc.  It shows one instance of the warning, also
>> a true positive (cause by calling a restrict-qualified function
>> with two copies of the same argument).
>>
>> Finally, I built Binutils and GDB with no warnings.
>>
>> The attached patch includes just that one fix, with everything
>> else being the same.
>
> +             /* Detect invalid bounds and overlapping copies and issue
> +                either -Warray-bounds or -Wrestrict.  */
> +             if (check_bounds_or_overlap (stmt, dest, src, len, len))
> +               gimple_set_no_warning (stmt, true);
>
> if (! gimple_no_warning (stmt)
>     && ...)
>
> to avoid repeated work if this call doesn't get folded.

Sure.

>
> @@ -7295,3 +7342,4 @@ gimple_stmt_integer_valued_real_p (gimple *stmt,
> int depth)
>        return false;
>      }
>  }
> +
>
> please don't do unrelated whitespace changes.
>
> +
> +  if (const strinfo *chksi = olddsi ? olddsi : dsi)
> +    if (si
> +       && !check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
> +      /* Avoid transforming strcpy when out-of-bounds offsets or
> +        overlapping access is detected.  */
> +      return;
>
> as I said elsewhere diagnostics should not prevent optimization.  Your warning
> code isn't optimization-grade (that is, false positives are possible).

I remember you pointing it out earlier and agreeing that warning
options should not disable/enable optimizations.  In this case,
if GCC can avoid the undefined behavior by folding a call that
would otherwise result from calling the library function, it
makes sense to go ahead and fold it.  I forgot to allow for
that in the last revision of my patch but I've fixed it in
the updated one I just posted.

> +       if (!check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
> +         /* Avoid transforming strcat when out-of-bounds offsets or
> +            overlapping access is detected.  */
> +         return;
> +      }
>
> Likewise.
>
> +      if (!check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
> +         /* Avoid transforming strcat when out-of-bounds offsets or
> +            overlapping access is detected.  */
> +       return;
>
> Likewise.
>
> I have no strong opinion against the "code duplication" Jeff mentions with
> regarding to builtin_access and friends.  The relation to ao_ref and friends
> could be documented a bit and how builtin_memref/builtin_access are
> not suitable for optimization.

I added a comment to the classes to make it clearer.  Although
now that the classes are in a dedicated warning pass there is
little risk of someone misusing them for optimization.

Thanks
Martin

>
> Thanks,
> Richard.
>
>>
>> On 11/09/2017 04:57 PM, Martin Sebor wrote:
>>>
>>> Ping:
>>>
>>>   https://gcc.gnu.org/ml/gcc-patches/2017-10/msg01642.html
>>>
>>> On 10/23/2017 08:42 PM, Martin Sebor wrote:
>>>>
>>>> Attached is a reworked solution to enhance -Wrestrict while
>>>> avoiding changing tree-vrp.c or any other VRP machinery.  Richard,
>>>> in considering you suggestions I realized that the ao_ref struct
>>>> isn't general enough to detect the kinds of problems I needed to
>>>> etect (storing bit-offsets in HOST_WIDE_INT means out-of-bounds
>>>> offsets cannot be represented or detected, leading to either false
>>>> positives or false negatives).
>>>>
>>>> Instead, the solution adds a couple of small classes to builtins.c
>>>> to overcome this limitation (I'm contemplating moving them along
>>>> with -Wstringop-overflow to a separate file to keep builtins.c
>>>> from getting too much bigger).
>>>>
>>>> The detection of out-of-bounds offsets and overlapping accesses
>>>> is relatively simple but the rest of the changes are somewhat
>>>> involved because of the computation of the offsets and sizes of
>>>> the overlaps.
>>>>
>>>> Jeff, as per your suggestion/request in an earlier review (bug
>>>> 81117) I've renamed some of the existing functions to better
>>>> reflect their new function (including renaming strlen_optimize_stmt
>>>> in tree-ssa-strlen.c to strlen_check_and_optimize_stmt).  There's
>>>> quite a bit of churn due to some of this renaming.  I don't think
>>>> this renaming makes the review too difficult but if you feel
>>>> differently I can [be persuaded to] split it out into a separate
>>>> patch.
>>>>
>>>> To validate the patch I compiled the Linux kernel and Binutils/GDB.
>>>> There's one false positive I'm working on resolving that's caused
>>>> by an incorrect interpretation of an offset in a range whose lower
>>>> bound is greater than its upper bound.  This it so say that while
>>>> I'm aware the patch isn't perfect it already works reasonably well
>>>> in practice and I think it's close enough to review.
>>>>
>>>> Thanks
>>>> Martin
>>>
>>>
>>
Jeff Law Dec. 7, 2017, 8:16 p.m. UTC | #6
On 11/25/2017 05:53 PM, Martin Sebor wrote:
> On 11/22/2017 04:50 PM, Jeff Law wrote:
>> On 11/16/2017 02:29 PM, Martin Sebor wrote:
>>>> On 10/23/2017 08:42 PM, Martin Sebor wrote:
>>>>> Attached is a reworked solution to enhance -Wrestrict while
>>>>> avoiding changing tree-vrp.c or any other VRP machinery.  Richard,
>>>>> in considering you suggestions I realized that the ao_ref struct
>>>>> isn't general enough to detect the kinds of problems I needed to
>>>>> etect (storing bit-offsets in HOST_WIDE_INT means out-of-bounds
>>>>> offsets cannot be represented or detected, leading to either false
>>>>> positives or false negatives).
>> So this seems to be a recurring theme, which makes me wonder if we
>> should have an ao_ref-like structure that deals in bytes rather than
>> bits and make it a first class citizen.   There's certainly clients that
>> work on bits and certainly clients that would prefer to work on bytes.
> 
> The class I introduced serves a different purpose than ao_ref and
> stores a lot more data.
> 
> In https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82042#c3 Richard
> says that "this [offset being HOST_WIDE_INT and storing bits] is
> a know deficiency in ao_ref 'offset' (and also size and maxsize).
> Blowing up to offset_int isn't really a good idea."
Understood.  My point was that we seem to be stumbling into the same
class of problems in more than once place.  So we may want to consider
having a class that looks like like ao_ref, but which operates in byte
sized chunks.

I don't think it should be a requirement to go forward through.  It's
more of a long term question/concern.


>> So I realize you don't have any code to answer this question, but your
>> thoughts on how much we might loose effectiveness if we didn't do the
>> warnings within gimple_fold_builtin_<whatever>, but instead broke out a
>> distinct pass to handle warnings?  My biggest design concern is the
>> warning from within the folder aspects.
> 
> With optimization enabled the folder folds things like this into
> MEM_REF which would prevent the warning unless the pass pass ran
> with early optimizations.
> 
>   struct S { char a[7], b[7]; };
> 
>   void sink (void*);
> 
>   void f (void)
>   {
>     struct S s;
>     sink (&s);
> 
>     unsigned n = sizeof s.a;
>     memcpy (s.a + 4, s.b, n);
> 
>     sink (&s);
>   }
> 
> Without optimization it isn't folded and so the memcpy call is
> emitted (but there is no warning).  The overlapping MEM_REF copy
> is safe but the overlapping memcpy call is not, so warning on it
> is helpful.
Understood.  I'm just trying to get a sense for how an implementation as
a separate pass would affect the quality of the diags we generate.  I
expect we'd lose some, the question is do we lose so many that the
ultimate result really isn't useful in practice and to balance that
against the various pros/cons of the different approaches.


> 
> I prototyped a pass over Thanksgiving for the -Wrestict code from
> builtins.c to and ran it just after the sprintf pass.  There are
> still lots of failures in the new test because it's non-trivial
> to compute the same data as builtins.c does (the data is also
> computed for -Wstringop-overflow).  I could move all that code
> to the new pass as well to clear up the failures.  I could also
> arrange for the pass to run multiple times to catch cases like
> the one above.  I suspect this would trigger some false negatives
> and/or positives due to the differences in range information, so
> it would mean some cleanup in the tests.  What I can't do without
> seriously compromising the feature is avoid calling into the new
> pass from tree-ssa-strlen.c, but that would presumably be fine.
I note that you posted that as a follow-up.  I'll take a look at it
momentarily.


> 
> With that said, and although I'm not necessarily opposed to it,
> moving all this code into its own pass would mean a non-trivial
> amount of work for what seems like a questionable benefit.  All
> it would achieve, as far as I can see, is duplicating some of
> the work that's already been done: iterating over the GIMPLE,
> testing for built-ins to handle, and extracting their arguments.
> What exactly do you hope to accomplish by moving it into its own
> pass?  (If it's a matter of keeping the warning code separate
> that can easily be done by moving it to its own file.)
By moving the warning into its own pass we get several benefits.

1. There is a general desire not to mix diagnostics and code
transformations.  Splitting it out is in line with that goal.

2. We get the flexibility to put the diagnostic pass wherever in the
pipeline makes the most sense.  We can even have early/late versions to
try and increase precision

3. Sometimes, but not always the analysis can be shared.  In that model
the underlying analysis engine is where the bulk of the work happens.
The optimizations and diagnostics are just clients of the analysis
module.  It also encourages re-use of the analysis module.  Oh how I
wish someone had done this with tree-ssa-uninit.c :(

4. It sometimes makes the code cleaner.   You don't have to worry about
the optimizer coming along and invaliding some chunk of data, or
introducing new names that you don't have information for, etc etc.

Of course there's a series of pros for a single pass that handles both.
 I won't enumerate them.

THe point is to make a reasoned evaluation of the pros and cons of an
integrated vs separate approach.


> 
> The only similarity between builtin_memref and ao_ref is between
> the ctor and ao_ref_init_from_ptr_and_size.  The ctor is a bit more
> involved because it deals with non-constant offsets.  But I'm not
> aware of any corner cases in ao_ref_init_from_ptr_and_size (the
> ctor started as a copy of the function and evolved to what it looks
> like now).
Understood.  Let me take a look at the follow-ups.

Jeff
Jeff Law Dec. 7, 2017, 8:20 p.m. UTC | #7
On 11/27/2017 05:44 AM, Richard Biener wrote:
> 
> +
> +  if (const strinfo *chksi = olddsi ? olddsi : dsi)
> +    if (si
> +       && !check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
> +      /* Avoid transforming strcpy when out-of-bounds offsets or
> +        overlapping access is detected.  */
> +      return;
> 
> as I said elsewhere diagnostics should not prevent optimization.  Your warning
> code isn't optimization-grade (that is, false positives are possible).
> 
> +       if (!check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
> +         /* Avoid transforming strcat when out-of-bounds offsets or
> +            overlapping access is detected.  */
> +         return;
> +      }
> 
> Likewise.
> 
> +      if (!check_bounds_or_overlap (stmt, dst, sptr, dstlen, srcsize))
> +         /* Avoid transforming strcat when out-of-bounds offsets or
> +            overlapping access is detected.  */
> +       return;
> 
> Likewise.
I'll note that if we separate diagnostics from optimization these become
a non-issue.  The diagnostic bits simply wouldn't change code, plain and
simple... :-)

> 
> I have no strong opinion against the "code duplication" Jeff mentions with
> regarding to builtin_access and friends.  The relation to ao_ref and friends
> could be documented a bit and how builtin_memref/builtin_access are
> not suitable for optimization.
At the least documentation around not using those classes driving any
code generation changes/decisions seems wise.  We have the same issue
around compute_objsize/check_memop_sizes for the stringop-overflow patch
if I understand those bits correctly (i've got messages to get back to
on that thread as well..)

JEff
Jeff Law Dec. 7, 2017, 9:14 p.m. UTC | #8
On 11/29/2017 04:36 PM, Martin Sebor wrote:
> I've finished reimplementing the patch as a standalone pass.
> In the attached revision I also addressed your comments below
> as well as Richard's to allowing the strlen optimizations even
> for overlapping accesses.
> 
> While beefing up the tests I found a few minor issues that
> I also fixed (false negatives).
> 
> The fallout wasn't quite as bad as I thought, mainly thanks
> to the narrow API for the checker.
So still reading though this, but wanted to start with a question I hope
you can answer quickly.

In terms of coverage -- did we lose much in terms of cases that were
diagnosed in the original version, but aren't in this version?

jeff
Martin Sebor Dec. 7, 2017, 9:28 p.m. UTC | #9
On 12/07/2017 02:14 PM, Jeff Law wrote:
> On 11/29/2017 04:36 PM, Martin Sebor wrote:
>> I've finished reimplementing the patch as a standalone pass.
>> In the attached revision I also addressed your comments below
>> as well as Richard's to allowing the strlen optimizations even
>> for overlapping accesses.
>>
>> While beefing up the tests I found a few minor issues that
>> I also fixed (false negatives).
>>
>> The fallout wasn't quite as bad as I thought, mainly thanks
>> to the narrow API for the checker.
> So still reading though this, but wanted to start with a question I hope
> you can answer quickly.
>
> In terms of coverage -- did we lose much in terms of cases that were
> diagnosed in the original version, but aren't in this version?

I'm quite pleased to say that with the pass in the right place
(after vrp) the coverage is the same.

Martin
Jeff Law Dec. 7, 2017, 9:48 p.m. UTC | #10
On 12/07/2017 02:28 PM, Martin Sebor wrote:
> On 12/07/2017 02:14 PM, Jeff Law wrote:
>> On 11/29/2017 04:36 PM, Martin Sebor wrote:
>>> I've finished reimplementing the patch as a standalone pass.
>>> In the attached revision I also addressed your comments below
>>> as well as Richard's to allowing the strlen optimizations even
>>> for overlapping accesses.
>>>
>>> While beefing up the tests I found a few minor issues that
>>> I also fixed (false negatives).
>>>
>>> The fallout wasn't quite as bad as I thought, mainly thanks
>>> to the narrow API for the checker.
>> So still reading though this, but wanted to start with a question I hope
>> you can answer quickly.
>>
>> In terms of coverage -- did we lose much in terms of cases that were
>> diagnosed in the original version, but aren't in this version?
> 
> I'm quite pleased to say that with the pass in the right place
> (after vrp) the coverage is the same.
That's awesome.
Jeff
Jeff Law Dec. 7, 2017, 10:23 p.m. UTC | #11
On 11/29/2017 04:36 PM, Martin Sebor wrote:
> I've finished reimplementing the patch as a standalone pass.
> In the attached revision I also addressed your comments below
> as well as Richard's to allowing the strlen optimizations even
> for overlapping accesses.
> 
> While beefing up the tests I found a few minor issues that
> I also fixed (false negatives).
> 
> The fallout wasn't quite as bad as I thought, mainly thanks
> to the narrow API for the checker.
> 
> Syncing up with the latest trunk has led to some more changes
> in tree-ssa-strlen.
> 
> I've retested the patch with GDB and Glibc with the same results
> as before.
> 
> The patch seems sizable (over 3KLOC without tests) but it's worth
> noting that most of the complexity is actually not in determining
> whether or not an overlap exists (that's quite simple) but rather
> in computing its offset and size to mention in the warnings and
> making sure the information is meaningful to the user even when
> ranges are involved.  All the subtly different forms of warnings
> also contribute substantially to the overall size.
> 
> Martin
[ Huge snip. ]

> 
> gcc-78918.diff
> 
> 
> PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over self
> 
> gcc/c-family/ChangeLog:
> 
> 	PR tree-optimization/78918
> 	* c-common.c (check_function_restrict): Avoid checking built-ins.
> 	* c.opt (-Wrestrict): Include in -Wall.
> 
> gcc/ChangeLog:
> 
> 	PR tree-optimization/78918
> 	* Makefile.in (OBJS): Add gimple-ssa-warn-restrict.o.
> 	* builtins.c (check_sizes): Rename...
> 	(check_access): ...to this.  Rename function arguments for clarity.
> 	(check_memop_sizes): Adjust names.
> 	(expand_builtin_memchr, expand_builtin_memcpy): Same.
> 	(expand_builtin_memmove, expand_builtin_mempcpy): Same.
> 	(expand_builtin_strcat, expand_builtin_stpncpy): Same.
> 	(check_strncat_sizes, expand_builtin_strncat): Same.
> 	(expand_builtin_strncpy, expand_builtin_memset): Same.
> 	(expand_builtin_bzero, expand_builtin_memcmp): Same.
> 	(expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
> 	(maybe_emit_sprintf_chk_warning): Same.
> 	(expand_builtin_strcpy): Adjust.
> 	(expand_builtin_stpcpy): Same.
> 	(expand_builtin_with_bounds): Detect out-of-bounds accesses
> 	in pointer-checking forms of memcpy, memmove, and mempcpy.
> 	(gcall_to_tree_minimal, max_object_size): Define new functions.
> 	* builtins.h (max_object_size): Declare.
> 	* calls.c (alloc_max_size): Call max_object_size instead of
> 	hardcoding ssizetype limit.
> 	(get_size_range): Handle new argument.
> 	* calls.h (get_size_range): Add a new argument.
> 	* cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
> 	* doc/invoke.texi (-Wrestrict): Adjust, add example.
> 	* gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
> 	operations.
> 	(gimple_fold_builtin_memory_chk): Same.
> 	(gimple_fold_builtin_stxcpy_chk): New function.
> 	* gimple-ssa-warn-restrict.c: New source.
> 	* gimple-ssa-warn-restrict.h: New header.
> 	* gimple.c (gimple_build_call_from_tree): Propagate location.
> 	* passes.def (pass_warn_restrict): Add new pass.
> 	* tree-pass.h (make_pass_warn_restrict): Declare.
> 	* tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
> 	operations.
> 	(handle_builtin_strcat): Same.
> 	(strlen_optimize_stmt): Rename...
> 	(strlen_check_and_optimize_stmt): ...to this.  Handle strncat,
> 	stpncpy, strncpy, and their checking forms.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR tree-optimization/78918
> 	* c-c++-common/Warray-bounds.c: New test.
> 	* c-c++-common/Warray-bounds-2.c: New test.
> 	* c-c++-common/Warray-bounds-3.c: New test.
> 	* c-c++-common/Wrestrict-2.c: New test.
> 	* c-c++-common/Wrestrict.c: New test.
> 	* c-c++-common/Wrestrict.s: New test.
> 	* c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust
> 	* c-c++-common/Wsizeof-pointer-memaccess2.c: Same.
> 	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
> 	* g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
> 	* gcc.dg/memcpy-6.c: New test.
> 	* gcc.dg/pr69172.c: Adjust.
> 	* gcc.dg/pr79223.c: Same.
> 	* gcc.dg/Wrestrict-2.c: New test.
> 	* gcc.dg/Wrestrict.c: New test.
> 	* gcc.dg/Wsizeof-pointer-memaccess1.c
> 	* gcc.target/i386/chkp-stropt-17.c: New test.
> 	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Adjust.
> 
> @@ -3874,32 +3885,32 @@ check_strncat_sizes (tree exp, tree objsize)
>  				size_one_node)
>  		 : NULL_TREE);
>  
> -  /* Strncat copies at most MAXLEN bytes and always appends the terminating
> +  /* Strncat copies at most MAXREAD bytes and always appends the terminating
Nit.  Use "strncat" rather than "Strncat", even when starting a
sentence.  I saw this elsewhere.  You can fix these in a follow-up which
you can consider pre-approved.

So please correct me if I'm wrong.  WRT overall structure, you've got a
pass which walks the IL looking for suitable calls and potentially warns.

Additionally, that pass exports check_bounds_or_overlap which we use to
varying degress in the folder and strlen optimization pass.  It doesn't
really query any pass specific state, it's just an exported interface
into the meat of your optimization pass.

Presumably you have those exported interfaces so that you can diagnose
things prior to folding or other transformations that potentially
obfuscate the calls you want to diagnose?  Right?

The main pass (which runs after VRP) presumably runs to capture more
stuff that is exposed by the optimizers.  ie, the optimizers may
const/copy propagate things which are necessary to expose some of the
cases we want to capture.

Assuming I'm correct on those issues, this starts to look a lot like how
we structure early/late warnings.

--


I believe standard practice is to put the pass class into the anonymous
namespace -- pass_data_wstrict & pass_wrestrict as well as any methods
you define for them.  I'm sure there's a good reason why we do that,
though I find it annoying given GDB's poor support for anonymous
namespaces.  BUt please follow convention here.

Consider walking the blocks in dominator order.  If the pass can benefit
from range data it'll make adding the evrp analyzer easier.  As a
reference see how I changed the sprintf warnings bits to use a dominator
walk order.

On pass_wrestrict::check_call, put its return value type onto a separate
line.

It looks like you have to create an expression for each and every
builtin call statement you check.  Is there any way to defer creating
that CALL_EXPR until you know you need it in a diagnostic?  Can the uses
prior to the actual diagnostic get their data from elsewhere?

It looks like you're doing a lot more than just restrict checking in the
new pass.  I see array bounds checking and what appears to be general
pointer-out-of-bounds checking in the new pass. Can you comment on that?

Overall it looks pretty good.  I want to make sure I understand the
overall structure and reasoning behind that structure.

Jeff
Martin Sebor Dec. 8, 2017, 12:44 a.m. UTC | #12
On 12/07/2017 03:23 PM, Jeff Law wrote:
> On 11/29/2017 04:36 PM, Martin Sebor wrote:
>> I've finished reimplementing the patch as a standalone pass.
>> In the attached revision I also addressed your comments below
>> as well as Richard's to allowing the strlen optimizations even
>> for overlapping accesses.
>>
>> While beefing up the tests I found a few minor issues that
>> I also fixed (false negatives).
>>
>> The fallout wasn't quite as bad as I thought, mainly thanks
>> to the narrow API for the checker.
>>
>> Syncing up with the latest trunk has led to some more changes
>> in tree-ssa-strlen.
>>
>> I've retested the patch with GDB and Glibc with the same results
>> as before.
>>
>> The patch seems sizable (over 3KLOC without tests) but it's worth
>> noting that most of the complexity is actually not in determining
>> whether or not an overlap exists (that's quite simple) but rather
>> in computing its offset and size to mention in the warnings and
>> making sure the information is meaningful to the user even when
>> ranges are involved.  All the subtly different forms of warnings
>> also contribute substantially to the overall size.
>>
>> Martin
> [ Huge snip. ]
>
>>
>> gcc-78918.diff
>>
>>
>> PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over self
>>
>> gcc/c-family/ChangeLog:
>>
>> 	PR tree-optimization/78918
>> 	* c-common.c (check_function_restrict): Avoid checking built-ins.
>> 	* c.opt (-Wrestrict): Include in -Wall.
>>
>> gcc/ChangeLog:
>>
>> 	PR tree-optimization/78918
>> 	* Makefile.in (OBJS): Add gimple-ssa-warn-restrict.o.
>> 	* builtins.c (check_sizes): Rename...
>> 	(check_access): ...to this.  Rename function arguments for clarity.
>> 	(check_memop_sizes): Adjust names.
>> 	(expand_builtin_memchr, expand_builtin_memcpy): Same.
>> 	(expand_builtin_memmove, expand_builtin_mempcpy): Same.
>> 	(expand_builtin_strcat, expand_builtin_stpncpy): Same.
>> 	(check_strncat_sizes, expand_builtin_strncat): Same.
>> 	(expand_builtin_strncpy, expand_builtin_memset): Same.
>> 	(expand_builtin_bzero, expand_builtin_memcmp): Same.
>> 	(expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
>> 	(maybe_emit_sprintf_chk_warning): Same.
>> 	(expand_builtin_strcpy): Adjust.
>> 	(expand_builtin_stpcpy): Same.
>> 	(expand_builtin_with_bounds): Detect out-of-bounds accesses
>> 	in pointer-checking forms of memcpy, memmove, and mempcpy.
>> 	(gcall_to_tree_minimal, max_object_size): Define new functions.
>> 	* builtins.h (max_object_size): Declare.
>> 	* calls.c (alloc_max_size): Call max_object_size instead of
>> 	hardcoding ssizetype limit.
>> 	(get_size_range): Handle new argument.
>> 	* calls.h (get_size_range): Add a new argument.
>> 	* cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
>> 	* doc/invoke.texi (-Wrestrict): Adjust, add example.
>> 	* gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
>> 	operations.
>> 	(gimple_fold_builtin_memory_chk): Same.
>> 	(gimple_fold_builtin_stxcpy_chk): New function.
>> 	* gimple-ssa-warn-restrict.c: New source.
>> 	* gimple-ssa-warn-restrict.h: New header.
>> 	* gimple.c (gimple_build_call_from_tree): Propagate location.
>> 	* passes.def (pass_warn_restrict): Add new pass.
>> 	* tree-pass.h (make_pass_warn_restrict): Declare.
>> 	* tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
>> 	operations.
>> 	(handle_builtin_strcat): Same.
>> 	(strlen_optimize_stmt): Rename...
>> 	(strlen_check_and_optimize_stmt): ...to this.  Handle strncat,
>> 	stpncpy, strncpy, and their checking forms.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 	PR tree-optimization/78918
>> 	* c-c++-common/Warray-bounds.c: New test.
>> 	* c-c++-common/Warray-bounds-2.c: New test.
>> 	* c-c++-common/Warray-bounds-3.c: New test.
>> 	* c-c++-common/Wrestrict-2.c: New test.
>> 	* c-c++-common/Wrestrict.c: New test.
>> 	* c-c++-common/Wrestrict.s: New test.
>> 	* c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust
>> 	* c-c++-common/Wsizeof-pointer-memaccess2.c: Same.
>> 	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
>> 	* g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
>> 	* gcc.dg/memcpy-6.c: New test.
>> 	* gcc.dg/pr69172.c: Adjust.
>> 	* gcc.dg/pr79223.c: Same.
>> 	* gcc.dg/Wrestrict-2.c: New test.
>> 	* gcc.dg/Wrestrict.c: New test.
>> 	* gcc.dg/Wsizeof-pointer-memaccess1.c
>> 	* gcc.target/i386/chkp-stropt-17.c: New test.
>> 	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Adjust.
>>
>> @@ -3874,32 +3885,32 @@ check_strncat_sizes (tree exp, tree objsize)
>>  				size_one_node)
>>  		 : NULL_TREE);
>>
>> -  /* Strncat copies at most MAXLEN bytes and always appends the terminating
>> +  /* Strncat copies at most MAXREAD bytes and always appends the terminating
> Nit.  Use "strncat" rather than "Strncat", even when starting a
> sentence.  I saw this elsewhere.  You can fix these in a follow-up which
> you can consider pre-approved.
>
> So please correct me if I'm wrong.  WRT overall structure, you've got a
> pass which walks the IL looking for suitable calls and potentially warns.
>
> Additionally, that pass exports check_bounds_or_overlap which we use to
> varying degress in the folder and strlen optimization pass.  It doesn't
> really query any pass specific state, it's just an exported interface
> into the meat of your optimization pass.

Right.  The pass has no state.  It had none when it was in
builtins.c and it still has none now that it's its own pass.
There may be an opportunity in the future to start tracking
things like VLA sizes which would mean adding state to it.

>
> Presumably you have those exported interfaces so that you can diagnose
> things prior to folding or other transformations that potentially
> obfuscate the calls you want to diagnose?  Right?

Right.

>
> The main pass (which runs after VRP) presumably runs to capture more
> stuff that is exposed by the optimizers.  ie, the optimizers may
> const/copy propagate things which are necessary to expose some of the
> cases we want to capture.

Right.

>
> Assuming I'm correct on those issues, this starts to look a lot like how
> we structure early/late warnings.
>
> --
>
>
> I believe standard practice is to put the pass class into the anonymous
> namespace -- pass_data_wstrict & pass_wrestrict as well as any methods
> you define for them.  I'm sure there's a good reason why we do that,
> though I find it annoying given GDB's poor support for anonymous
> namespaces.  BUt please follow convention here.

Sure.  IME, the anonymous namespaces makes setting breakpoints
difficult (e.g., in the tree-ssa-sprintf.c pass).  I can't just
use the break command followed by the name of a function defined
in such a namespace.  But it's not a big deal and I don't mind
wrapping things in an anonymous namespace here.

>
> Consider walking the blocks in dominator order.  If the pass can benefit
> from range data it'll make adding the evrp analyzer easier.  As a
> reference see how I changed the sprintf warnings bits to use a dominator
> walk order.

I remember you did that for the sprintf pass but since you'd
mentioned some runtime overhead and there was no apparent benefit
in this case I used a plain gimple pass.  But I'm fine changing
it if you're not concerned about the overhead and think it might
be helpful in the near future.

>
> On pass_wrestrict::check_call, put its return value type onto a separate
> line.
>

Ack.

> It looks like you have to create an expression for each and every
> builtin call statement you check.  Is there any way to defer creating
> that CALL_EXPR until you know you need it in a diagnostic?  Can the uses
> prior to the actual diagnostic get their data from elsewhere?

In builtins.c the CALL_EXPR was only created for callers outside
builtins.c (i.e., gimple-fold.c and tree-ssa-strlen.c).  Otherwise
the machinery worked with CALL_EXPR which is what its callers
within the file already had.

With the move to a gimple pass this has changed.  There are no
callers that work with CALL_EXPR anymore, they all work with
gimple statements.  So the CALL_EXPR interface is unnecessary
and can and probably should be replaced with a gimple*.  It
didn't occur to me to do it when I was scrambling to move
the code and get the prototype working, and then to get it
finished.  Let me look into making the change now.

>
> It looks like you're doing a lot more than just restrict checking in the
> new pass.  I see array bounds checking and what appears to be general
> pointer-out-of-bounds checking in the new pass. Can you comment on that?

Detecting overlap and out-of-bounds accesses are closely related.
For example:

   void f (void)
   {
     char a[9] = "01234";

     if (i < 3 || i > 7)
       i = 3;

     char *d = a + i;   // i's range is [3, 7]

     const char *s = a + 4;

     strncpy (d, s, 4);
   }

If i > 5 the copy would overflow (form an out-of-bounds pointer)
and so -Wrestrict assumes i <= 5 holds, which means the copy
overlaps because the source points to the trailing "4\0" and
the destination to the NUL at a[5].  I.e., -Wrestrict can't
just consider overlap, it must also consider object bounds.

Other more obscure cases involve arrays of unknown bounds (whose
lower bound must be zero and upper bound PTRDIFF_MAX) and pointers
to unknown objects where (whose bounds are [PTRDIFF_MIN,
PTRDIFF_MAX]).  -Wrestrict has to check the bounds anyway so if
the size of a copy into the same object is large enough that it
would overflow its bounds, it makes sense to diagnose it using
-Warray-bounds.  If it doesn't overflow the bounds but does
overlap then the checker issues -Wrestrict.

> Overall it looks pretty good.  I want to make sure I understand the
> overall structure and reasoning behind that structure.

Sure.  The structure of the pass is simple.  It iterates over
calls to built-ins, checking each pointer argument to make sure
it's valid both before and after the access, and finally checking
the access for overlaps.

The complexity (in my mind) is in computing the offsets and sizes
of the overlap when it happens.

Martin
Martin Sebor Dec. 8, 2017, 7:19 p.m. UTC | #13
Attached is revision 8 of the patch with the changes suggested
and/or requested below.

Martin

On 12/07/2017 05:44 PM, Martin Sebor wrote:
> On 12/07/2017 03:23 PM, Jeff Law wrote:
>> On 11/29/2017 04:36 PM, Martin Sebor wrote:
>>> I've finished reimplementing the patch as a standalone pass.
>>> In the attached revision I also addressed your comments below
>>> as well as Richard's to allowing the strlen optimizations even
>>> for overlapping accesses.
>>>
>>> While beefing up the tests I found a few minor issues that
>>> I also fixed (false negatives).
>>>
>>> The fallout wasn't quite as bad as I thought, mainly thanks
>>> to the narrow API for the checker.
>>>
>>> Syncing up with the latest trunk has led to some more changes
>>> in tree-ssa-strlen.
>>>
>>> I've retested the patch with GDB and Glibc with the same results
>>> as before.
>>>
>>> The patch seems sizable (over 3KLOC without tests) but it's worth
>>> noting that most of the complexity is actually not in determining
>>> whether or not an overlap exists (that's quite simple) but rather
>>> in computing its offset and size to mention in the warnings and
>>> making sure the information is meaningful to the user even when
>>> ranges are involved.  All the subtly different forms of warnings
>>> also contribute substantially to the overall size.
>>>
>>> Martin
>> [ Huge snip. ]
>>
>>>
>>> gcc-78918.diff
>>>
>>>
>>> PR tree-optimization/78918 - missing -Wrestrict on memcpy copying
>>> over self
>>>
>>> gcc/c-family/ChangeLog:
>>>
>>>     PR tree-optimization/78918
>>>     * c-common.c (check_function_restrict): Avoid checking built-ins.
>>>     * c.opt (-Wrestrict): Include in -Wall.
>>>
>>> gcc/ChangeLog:
>>>
>>>     PR tree-optimization/78918
>>>     * Makefile.in (OBJS): Add gimple-ssa-warn-restrict.o.
>>>     * builtins.c (check_sizes): Rename...
>>>     (check_access): ...to this.  Rename function arguments for clarity.
>>>     (check_memop_sizes): Adjust names.
>>>     (expand_builtin_memchr, expand_builtin_memcpy): Same.
>>>     (expand_builtin_memmove, expand_builtin_mempcpy): Same.
>>>     (expand_builtin_strcat, expand_builtin_stpncpy): Same.
>>>     (check_strncat_sizes, expand_builtin_strncat): Same.
>>>     (expand_builtin_strncpy, expand_builtin_memset): Same.
>>>     (expand_builtin_bzero, expand_builtin_memcmp): Same.
>>>     (expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
>>>     (maybe_emit_sprintf_chk_warning): Same.
>>>     (expand_builtin_strcpy): Adjust.
>>>     (expand_builtin_stpcpy): Same.
>>>     (expand_builtin_with_bounds): Detect out-of-bounds accesses
>>>     in pointer-checking forms of memcpy, memmove, and mempcpy.
>>>     (gcall_to_tree_minimal, max_object_size): Define new functions.
>>>     * builtins.h (max_object_size): Declare.
>>>     * calls.c (alloc_max_size): Call max_object_size instead of
>>>     hardcoding ssizetype limit.
>>>     (get_size_range): Handle new argument.
>>>     * calls.h (get_size_range): Add a new argument.
>>>     * cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
>>>     * doc/invoke.texi (-Wrestrict): Adjust, add example.
>>>     * gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
>>>     operations.
>>>     (gimple_fold_builtin_memory_chk): Same.
>>>     (gimple_fold_builtin_stxcpy_chk): New function.
>>>     * gimple-ssa-warn-restrict.c: New source.
>>>     * gimple-ssa-warn-restrict.h: New header.
>>>     * gimple.c (gimple_build_call_from_tree): Propagate location.
>>>     * passes.def (pass_warn_restrict): Add new pass.
>>>     * tree-pass.h (make_pass_warn_restrict): Declare.
>>>     * tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
>>>     operations.
>>>     (handle_builtin_strcat): Same.
>>>     (strlen_optimize_stmt): Rename...
>>>     (strlen_check_and_optimize_stmt): ...to this.  Handle strncat,
>>>     stpncpy, strncpy, and their checking forms.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>>     PR tree-optimization/78918
>>>     * c-c++-common/Warray-bounds.c: New test.
>>>     * c-c++-common/Warray-bounds-2.c: New test.
>>>     * c-c++-common/Warray-bounds-3.c: New test.
>>>     * c-c++-common/Wrestrict-2.c: New test.
>>>     * c-c++-common/Wrestrict.c: New test.
>>>     * c-c++-common/Wrestrict.s: New test.
>>>     * c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust
>>>     * c-c++-common/Wsizeof-pointer-memaccess2.c: Same.
>>>     * g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
>>>     * g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
>>>     * gcc.dg/memcpy-6.c: New test.
>>>     * gcc.dg/pr69172.c: Adjust.
>>>     * gcc.dg/pr79223.c: Same.
>>>     * gcc.dg/Wrestrict-2.c: New test.
>>>     * gcc.dg/Wrestrict.c: New test.
>>>     * gcc.dg/Wsizeof-pointer-memaccess1.c
>>>     * gcc.target/i386/chkp-stropt-17.c: New test.
>>>     * gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Adjust.
>>>
>>> @@ -3874,32 +3885,32 @@ check_strncat_sizes (tree exp, tree objsize)
>>>                  size_one_node)
>>>           : NULL_TREE);
>>>
>>> -  /* Strncat copies at most MAXLEN bytes and always appends the
>>> terminating
>>> +  /* Strncat copies at most MAXREAD bytes and always appends the
>>> terminating
>> Nit.  Use "strncat" rather than "Strncat", even when starting a
>> sentence.  I saw this elsewhere.  You can fix these in a follow-up which
>> you can consider pre-approved.
>>
>> So please correct me if I'm wrong.  WRT overall structure, you've got a
>> pass which walks the IL looking for suitable calls and potentially warns.
>>
>> Additionally, that pass exports check_bounds_or_overlap which we use to
>> varying degress in the folder and strlen optimization pass.  It doesn't
>> really query any pass specific state, it's just an exported interface
>> into the meat of your optimization pass.
>
> Right.  The pass has no state.  It had none when it was in
> builtins.c and it still has none now that it's its own pass.
> There may be an opportunity in the future to start tracking
> things like VLA sizes which would mean adding state to it.
>
>>
>> Presumably you have those exported interfaces so that you can diagnose
>> things prior to folding or other transformations that potentially
>> obfuscate the calls you want to diagnose?  Right?
>
> Right.
>
>>
>> The main pass (which runs after VRP) presumably runs to capture more
>> stuff that is exposed by the optimizers.  ie, the optimizers may
>> const/copy propagate things which are necessary to expose some of the
>> cases we want to capture.
>
> Right.
>
>>
>> Assuming I'm correct on those issues, this starts to look a lot like how
>> we structure early/late warnings.
>>
>> --
>>
>>
>> I believe standard practice is to put the pass class into the anonymous
>> namespace -- pass_data_wstrict & pass_wrestrict as well as any methods
>> you define for them.  I'm sure there's a good reason why we do that,
>> though I find it annoying given GDB's poor support for anonymous
>> namespaces.  BUt please follow convention here.
>
> Sure.  IME, the anonymous namespaces makes setting breakpoints
> difficult (e.g., in the tree-ssa-sprintf.c pass).  I can't just
> use the break command followed by the name of a function defined
> in such a namespace.  But it's not a big deal and I don't mind
> wrapping things in an anonymous namespace here.
>
>>
>> Consider walking the blocks in dominator order.  If the pass can benefit
>> from range data it'll make adding the evrp analyzer easier.  As a
>> reference see how I changed the sprintf warnings bits to use a dominator
>> walk order.
>
> I remember you did that for the sprintf pass but since you'd
> mentioned some runtime overhead and there was no apparent benefit
> in this case I used a plain gimple pass.  But I'm fine changing
> it if you're not concerned about the overhead and think it might
> be helpful in the near future.
>
>>
>> On pass_wrestrict::check_call, put its return value type onto a separate
>> line.
>>
>
> Ack.
>
>> It looks like you have to create an expression for each and every
>> builtin call statement you check.  Is there any way to defer creating
>> that CALL_EXPR until you know you need it in a diagnostic?  Can the uses
>> prior to the actual diagnostic get their data from elsewhere?
>
> In builtins.c the CALL_EXPR was only created for callers outside
> builtins.c (i.e., gimple-fold.c and tree-ssa-strlen.c).  Otherwise
> the machinery worked with CALL_EXPR which is what its callers
> within the file already had.
>
> With the move to a gimple pass this has changed.  There are no
> callers that work with CALL_EXPR anymore, they all work with
> gimple statements.  So the CALL_EXPR interface is unnecessary
> and can and probably should be replaced with a gimple*.  It
> didn't occur to me to do it when I was scrambling to move
> the code and get the prototype working, and then to get it
> finished.  Let me look into making the change now.
>
>>
>> It looks like you're doing a lot more than just restrict checking in the
>> new pass.  I see array bounds checking and what appears to be general
>> pointer-out-of-bounds checking in the new pass. Can you comment on that?
>
> Detecting overlap and out-of-bounds accesses are closely related.
> For example:
>
>   void f (void)
>   {
>     char a[9] = "01234";
>
>     if (i < 3 || i > 7)
>       i = 3;
>
>     char *d = a + i;   // i's range is [3, 7]
>
>     const char *s = a + 4;
>
>     strncpy (d, s, 4);
>   }
>
> If i > 5 the copy would overflow (form an out-of-bounds pointer)
> and so -Wrestrict assumes i <= 5 holds, which means the copy
> overlaps because the source points to the trailing "4\0" and
> the destination to the NUL at a[5].  I.e., -Wrestrict can't
> just consider overlap, it must also consider object bounds.
>
> Other more obscure cases involve arrays of unknown bounds (whose
> lower bound must be zero and upper bound PTRDIFF_MAX) and pointers
> to unknown objects where (whose bounds are [PTRDIFF_MIN,
> PTRDIFF_MAX]).  -Wrestrict has to check the bounds anyway so if
> the size of a copy into the same object is large enough that it
> would overflow its bounds, it makes sense to diagnose it using
> -Warray-bounds.  If it doesn't overflow the bounds but does
> overlap then the checker issues -Wrestrict.
>
>> Overall it looks pretty good.  I want to make sure I understand the
>> overall structure and reasoning behind that structure.
>
> Sure.  The structure of the pass is simple.  It iterates over
> calls to built-ins, checking each pointer argument to make sure
> it's valid both before and after the access, and finally checking
> the access for overlaps.
>
> The complexity (in my mind) is in computing the offsets and sizes
> of the overlap when it happens.
>
> Martin
PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over self

gcc/c-family/ChangeLog:

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

gcc/ChangeLog:

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

gcc/testsuite/ChangeLog:

	PR tree-optimization/78918
	* c-c++-common/Warray-bounds.c: New test.
	* c-c++-common/Warray-bounds-2.c: New test.
	* c-c++-common/Warray-bounds-3.c: New test.
	* c-c++-common/Wrestrict-2.c: New test.
	* c-c++-common/Wrestrict.c: New test.
	* c-c++-common/Wrestrict.s: New test.
	* c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust
	* c-c++-common/Wsizeof-pointer-memaccess2.c: Same.
	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
	* g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
	* gcc.dg/memcpy-6.c: New test.
	* gcc.dg/pr69172.c: Adjust.
	* gcc.dg/pr79223.c: Same.
	* gcc.dg/Wrestrict-2.c: New test.
	* gcc.dg/Wrestrict.c: New test.
	* gcc.dg/Wsizeof-pointer-memaccess1.c
	* gcc.target/i386/chkp-stropt-17.c: New test.
	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Adjust.

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index c428eaa..35839d5 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1316,6 +1316,7 @@ OBJS = \
 	gimple-ssa-strength-reduction.o \
 	gimple-ssa-sprintf.o \
 	gimple-ssa-warn-alloca.o \
+	gimple-ssa-warn-restrict.o \
 	gimple-streamer-in.o \
 	gimple-streamer-out.o \
 	gimple-walk.o \
diff --git a/gcc/builtins.c b/gcc/builtins.c
index b974459..b1378e7 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "alias.h"
 #include "fold-const.h"
 #include "fold-const-call.h"
+#include "gimple-ssa-warn-restrict.h"
 #include "stor-layout.h"
 #include "calls.h"
 #include "varasm.h"
@@ -3008,37 +3009,45 @@ determine_block_size (tree len, rtx len_rtx,
 
 /* 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.
+   Arguments other than EXP may be null.  When non-null, the arguments
+   have the following meaning:
+   DST is the destination of a copy call or NULL otherwise.
+   SRC is the source of a copy call or NULL otherwise.
+   DSTWRITE is the number of bytes written into the destination obtained
+   from the user-supplied size argument to the function (such as in
+   memcpy(DST, SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+   MAXREAD is the user-supplied bound on the length of the source sequence
    (such as in strncat(d, s, N).  It specifies the upper limit on the number
-   of bytes to write.
-   SRC is the source string (such as in strcpy(d, s)) when the expression
-   EXP is a string function call (as opposed to a memory call like memcpy).
-   As an exception, SRC can also be an integer denoting the precomputed
-   size of the source string or object (for functions like memcpy).
-   OBJSIZE is the size of the destination object specified by the last
+   of bytes to write.  If NULL, it's taken to be the same as DSTWRITE.
+   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+   expression EXP is a string function call (as opposed to a memory call
+   like memcpy).  As an exception, SRCSTR can also be an integer denoting
+   the precomputed size of the source string or object (for functions like
+   memcpy).
+   DSTSIZE is the size of the destination object specified by the last
    argument to the _chk builtins, typically resulting from the expansion
-   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
-   OBJSIZE).
+   of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
+   DSTSIZE).
 
-   When SIZE is null LEN is checked to verify that it doesn't exceed
+   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
-   If the call is successfully verified as safe from buffer overflow
-   the function returns true, otherwise false..  */
+   If the call is successfully verified as safe return true, otherwise
+   return false.  */
 
 static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_access (tree exp, tree, tree, tree dstwrite,
+	      tree maxread, tree srcstr, tree dstsize)
 {
+  int opt = OPT_Wstringop_overflow_;
+
   /* The size of the largest object is half the address space, or
-     SSIZE_MAX.  (This is way too permissive.)  */
-  tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
+     PTRDIFF_MAX.  (This is way too permissive.)  */
+  tree maxobjsize = max_object_size ();
 
+  /* Either the length of the source string for string functions or
+     the size of the source object for raw memory functions.  */
   tree slen = NULL_TREE;
 
   tree range[2] = { NULL_TREE, NULL_TREE };
@@ -3047,28 +3056,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);
@@ -3082,10 +3091,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.  */
@@ -3093,20 +3102,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.  */
@@ -3119,30 +3126,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
@@ -3150,7 +3161,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,
@@ -3159,21 +3170,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;
@@ -3183,11 +3194,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);
@@ -3201,40 +3216,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);
@@ -3244,20 +3260,20 @@ 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;
     }
 
@@ -3330,11 +3346,8 @@ compute_objsize (tree dest, int ostype)
    (no overflow or invalid sizes), false otherwise.  */
 
 static bool
-check_memop_sizes (tree exp, tree dest, tree src, tree size)
+check_memop_access (tree exp, tree dest, tree src, tree size)
 {
-  if (!warn_stringop_overflow)
-    return true;
-
   /* For functions like memset and memcpy that operate on raw memory
      try to determine the size of the largest source and destination
      object using type-0 Object Size regardless of the object size
@@ -3342,8 +3355,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size)
   tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
   tree dstsize = compute_objsize (dest, 0);
 
-  return check_sizes (OPT_Wstringop_overflow_, exp,
-		      size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+  return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
+		       srcsize, dstsize);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -3364,9 +3377,8 @@ expand_builtin_memchr (tree exp, rtx)
   if (warn_stringop_overflow)
     {
       tree size = compute_objsize (arg1, 0);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, len, /*maxlen=*/NULL_TREE,
-		   size, /*objsize=*/NULL_TREE);
+      check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		    /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
     }
 
   return NULL_RTX;
@@ -3388,7 +3400,7 @@ expand_builtin_memcpy (tree exp, rtx target)
   tree src = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, src, len);
+  check_memop_access (exp, dest, src, len);
 
   return expand_builtin_memory_copy_args (dest, src, len, target, exp,
 					  /*endp=*/ 0);
@@ -3408,7 +3420,7 @@ expand_builtin_memmove (tree exp, rtx)
   tree src = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, src, len);
+  check_memop_access (exp, dest, src, len);
 
   return NULL_RTX;
 }
@@ -3467,7 +3479,7 @@ expand_builtin_mempcpy (tree exp, rtx target)
   /* Avoid expanding mempcpy into memcpy when the call is determined
      to overflow the buffer.  This also prevents the same overflow
      from being diagnosed again when expanding memcpy.  */
-  if (!check_memop_sizes (exp, dest, src, len))
+  if (!check_memop_access (exp, dest, src, len))
     return NULL_RTX;
 
   return expand_builtin_mempcpy_args (dest, src, len,
@@ -3673,8 +3685,8 @@ expand_builtin_strcat (tree exp, rtx)
 
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+  check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+		destsize);
 
   return NULL_RTX;
 }
@@ -3696,8 +3708,8 @@ expand_builtin_strcpy (tree exp, rtx target)
   if (warn_stringop_overflow)
     {
       tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+      check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		    src, destsize);
     }
 
   return expand_builtin_strcpy_args (dest, src, target);
@@ -3735,8 +3747,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
   if (warn_stringop_overflow)
     {
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+      check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		    src, destsize);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -3819,8 +3831,7 @@ expand_builtin_stpncpy (tree exp, rtx)
   /* The size of the destination object.  */
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 
   return NULL_RTX;
 }
@@ -3850,7 +3861,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.  */
@@ -3874,32 +3885,32 @@ check_strncat_sizes (tree exp, tree objsize)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN 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))
+  /* The strncat function 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 (maxread) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return false;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
+  /* The number of bytes to write is LEN but check_access will also
      check SRCLEN if LEN's value isn't known.  */
-  return check_sizes (OPT_Wstringop_overflow_,
-		      exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+  return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+		       objsize);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -3917,7 +3928,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);
 
@@ -3940,52 +3951,34 @@ expand_builtin_strncat (tree exp, rtx)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN 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))
+  /* The strncat function 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 (maxread) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return NULL_RTX;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
-     check SRCLEN if LEN's value isn't known.  */
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+  /* The number of bytes to write is SRCLEN.  */
+  check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize);
 
   return NULL_RTX;
 }
 
-/* Helper to check the sizes of sequences and the destination of calls
-   to __builtin_strncpy (DST, SRC, CNT) and __builtin___strncpy_chk.
-   Returns true on success (no overflow warning), false otherwise.  */
-
-static bool
-check_strncpy_sizes (tree exp, tree dst, tree src, tree cnt)
-{
-  tree dstsize = compute_objsize (dst, warn_stringop_overflow - 1);
-
-  if (!check_sizes (OPT_Wstringop_overflow_,
-		    exp, cnt, /*maxlen=*/NULL_TREE, src, dstsize))
-    return false;
-
-  return true;
-}
-
 /* Expand expression EXP, which is a call to the strncpy builtin.  Return
    NULL_RTX if we failed the caller should emit a normal call.  */
 
@@ -4004,7 +3997,16 @@ expand_builtin_strncpy (tree exp, rtx target)
       /* The length of the source sequence.  */
       tree slen = c_strlen (src, 1);
 
-      check_strncpy_sizes (exp, dest, src, len);
+      if (warn_stringop_overflow)
+	{
+	  tree destsize = compute_objsize (dest,
+					   warn_stringop_overflow - 1);
+
+	  /* The number of bytes to write is LEN but check_access will also
+	     check SLEN if LEN's value isn't known.  */
+	  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
+			destsize);
+	}
 
       /* We must be passed a constant len and src parameter.  */
       if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
@@ -4098,7 +4100,7 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
   tree val = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, NULL_TREE, len);
+  check_memop_access (exp, dest, NULL_TREE, len);
 
   return expand_builtin_memset_args (dest, val, len, target, mode, exp);
 }
@@ -4287,7 +4289,7 @@ expand_builtin_bzero (tree exp)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree size = CALL_EXPR_ARG (exp, 1);
 
-  check_memop_sizes (exp, dest, NULL_TREE, size);
+  check_memop_access (exp, dest, NULL_TREE, size);
 
   /* New argument list transforming bzero(ptr x, int y) to
      memset(ptr x, int 0, size_t y).   This is done this way
@@ -4346,14 +4348,12 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
   if (warn_stringop_overflow)
     {
       tree size = compute_objsize (arg1, 0);
-      if (check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE))
+      if (check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+			/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
 	{
 	  size = compute_objsize (arg2, 0);
-	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE);
+	  check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+			/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
 	}
     }
 
@@ -7731,6 +7731,23 @@ expand_builtin_with_bounds (tree exp, rtx target,
 	return target;
       break;
 
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMPCPY_CHKP:
+      if (call_expr_nargs (exp) > 3)
+	{
+	  /* memcpy_chkp (void *dst, size_t dstbnd,
+	                  const void *src, size_t srcbnd, size_t n)
+  	     and others take a pointer bound argument just after each
+	     pointer argument.  */
+	  tree dest = CALL_EXPR_ARG (exp, 0);
+	  tree src = CALL_EXPR_ARG (exp, 2);
+	  tree len = CALL_EXPR_ARG (exp, 4);
+
+	  check_memop_access (exp, dest, src, len);
+	  break;
+	}
+
     default:
       break;
     }
@@ -9748,8 +9765,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
@@ -9757,14 +9772,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
-  len = CALL_EXPR_ARG (exp, 2);
-  size = CALL_EXPR_ARG (exp, 3);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 3);
 
-  bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
-			       exp, len, /*maxlen=*/NULL_TREE,
-			       /*str=*/NULL_TREE, size);
+  bool sizes_ok = check_access (exp, dest, src, len, /*maxread=*/NULL_TREE,
+				/*str=*/NULL_TREE, size);
 
   if (!tree_fits_uhwi_p (size))
     return NULL_RTX;
@@ -9873,7 +9887,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)
     {
@@ -9894,27 +9908,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
@@ -9923,8 +9937,10 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       return;
     }
 
-  check_sizes (OPT_Wstringop_overflow_, exp,
-	       /*size=*/NULL_TREE, maxlen, srcstr, objsize);
+  /* The destination argument is the first one for all built-ins above.  */
+  tree dst = CALL_EXPR_ARG (exp, 0);
+
+  check_access (exp, dst, srcstr, /*size=*/NULL_TREE, maxread, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9980,8 +9996,9 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 
   /* Add one for the terminating nul.  */
   len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
+
+  check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
+		/*maxread=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
@@ -10611,3 +10628,12 @@ target_char_cst_p (tree t, char *p)
   *p = (char)tree_to_uhwi (t);
   return true;
 }
+
+/* Return the maximum object size.  */
+
+tree
+max_object_size (void)
+{
+  /* To do: Make this a configurable parameter.  */
+  return TYPE_MAX_VALUE (ptrdiff_type_node);
+}
diff --git a/gcc/builtins.h b/gcc/builtins.h
index cf3fc17..7f34d29 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -103,4 +103,6 @@ extern bool target_char_cst_p (tree t, char *p);
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
 
-#endif
+extern tree max_object_size ();
+
+#endif /* GCC_BUILTINS_H */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 9970e61..ac25891 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5267,14 +5267,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 d857dfc..665e834 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1174,7 +1174,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall)
 Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier.
 
 Wrestrict
-C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++)
+C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
 Warn when an argument passed to a restrict-qualified parameter aliases with
 another argument.
 
diff --git a/gcc/calls.c b/gcc/calls.c
index 8ae9899..da6c96b 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "builtins.h"
 
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
 #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -1194,7 +1195,7 @@ alloc_max_size (void)
 {
   if (!alloc_object_size_limit)
     {
-      alloc_object_size_limit = TYPE_MAX_VALUE (ssizetype);
+      alloc_object_size_limit = max_object_size ();
 
       if (warn_alloc_size_limit)
 	{
@@ -1245,7 +1246,8 @@ alloc_max_size (void)
 		{
 		  widest_int w = wi::mul (limit, unit);
 		  if (w < wi::to_widest (alloc_object_size_limit))
-		    alloc_object_size_limit = wide_int_to_tree (ssizetype, w);
+		    alloc_object_size_limit
+		      = wide_int_to_tree (ptrdiff_type_node, w);
 		}
 	    }
 	}
@@ -1254,13 +1256,17 @@ alloc_max_size (void)
 }
 
 /* Return true when EXP's range can be determined and set RANGE[] to it
-   after adjusting it if necessary to make EXP a valid size argument to
-   an allocation function declared with attribute alloc_size (whose
-   argument may be signed), or to a string manipulation function like
-   memset.  */
+   after adjusting it if necessary to make EXP a represents a valid size
+   of object, or a valid size argument to an allocation function declared
+   with attribute alloc_size (whose argument may be signed), or to a string
+   manipulation function like memset.  When ALLOW_ZERO is true, allow
+   returning a range of [0, 0] for a size in an anti-range [1, N] where
+   N > PTRDIFF_MAX.  A zero range is a (nearly) invalid argument to
+   allocation functions like malloc but it is a valid argument to
+   functions like memset.  */
 
 bool
-get_size_range (tree exp, tree range[2])
+get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
 {
   if (tree_fits_uhwi_p (exp))
     {
@@ -1269,20 +1275,33 @@ get_size_range (tree exp, tree range[2])
       return true;
     }
 
+  tree exptype = TREE_TYPE (exp);
+  bool integral = INTEGRAL_TYPE_P (exptype);
+
   wide_int min, max;
-  enum value_range_type range_type
-    = ((TREE_CODE (exp) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (exp)))
-       ? get_range_info (exp, &min, &max) : VR_VARYING);
+  enum value_range_type range_type;
+
+  if (TREE_CODE (exp) == SSA_NAME && integral)
+    range_type = get_range_info (exp, &min, &max);
+  else
+    range_type = VR_VARYING;
 
   if (range_type == VR_VARYING)
     {
-      /* No range information available.  */
+      if (integral)
+	{
+	  /* Use the full range of the type of the expression when
+	     no value range information is available.  */
+	  range[0] = TYPE_MIN_VALUE (exptype);
+	  range[1] = TYPE_MAX_VALUE (exptype);
+	  return true;
+	}
+
       range[0] = NULL_TREE;
       range[1] = NULL_TREE;
       return false;
     }
 
-  tree exptype = TREE_TYPE (exp);
   unsigned expprec = TYPE_PRECISION (exptype);
 
   bool signed_p = !TYPE_UNSIGNED (exptype);
@@ -1320,11 +1339,16 @@ get_size_range (tree exp, tree range[2])
 	{
 	  /* EXP is unsigned and not in the range [1, MAX].  That means
 	     it's either zero or greater than MAX.  Even though 0 would
-	     normally be detected by -Walloc-zero set the range to
-	     [MAX, TYPE_MAX] so that when MAX is greater than the limit
-	     the whole range is diagnosed.  */
-	  min = max + 1;
-	  max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	     normally be detected by -Walloc-zero, unless ALLOW_ZERO
+	     is true, set the range to [MAX, TYPE_MAX] so that when MAX
+	     is greater than the limit the whole range is diagnosed.  */
+	  if (allow_zero)
+	    min = max = wi::zero (expprec);
+	  else
+	    {
+	      min = max + 1;
+	      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	    }
 	}
       else
 	{
diff --git a/gcc/calls.h b/gcc/calls.h
index 9b7fa9a..641166e 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -38,8 +38,8 @@ extern bool pass_by_reference (CUMULATIVE_ARGS *, machine_mode,
 extern bool reference_callee_copied (CUMULATIVE_ARGS *, machine_mode,
 				     tree, bool);
 extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
-extern bool get_size_range (tree, tree[2]);
 extern tree get_attr_nonstring_decl (tree, tree * = NULL);
 extern void maybe_warn_nonstring_arg (tree, tree);
+extern bool get_size_range (tree, tree[2], bool = false);
 
 #endif // GCC_CALLS_H
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index d114060..53fe502 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2636,6 +2636,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 02a0c1e..bc7dd8e 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3880,6 +3880,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
@@ -6699,11 +6700,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++, a @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 353a46e..a9d063b 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ssa.h"
 #include "cgraph.h"
 #include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
 #include "fold-const.h"
 #include "stmt.h"
 #include "expr.h"
@@ -662,13 +663,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,
@@ -680,6 +680,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))
@@ -703,6 +709,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));
@@ -752,6 +767,13 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	  unsigned ilen = tree_to_uhwi (len);
 	  if (pow2p_hwi (ilen))
 	    {
+	      /* Detect invalid bounds and overlapping copies and issue
+		 either -Warray-bounds or -Wrestrict.  */
+	      if (!nowarn
+		  && check_bounds_or_overlap (as_a <gcall *>(stmt),
+					      dest, src, len, len))
+	      	gimple_set_no_warning (stmt, true);
+
 	      scalar_int_mode mode;
 	      tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
 	      if (type
@@ -1024,6 +1046,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	    }
 	}
 
+      /* Detect invalid bounds and overlapping copies and issue either
+	 -Warray-bounds or -Wrestrict.  */
+      if (!nowarn)
+	check_bounds_or_overlap (as_a <gcall *>(stmt), dest, src, len, len);
+
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
 	{
@@ -1388,7 +1415,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:
@@ -1480,12 +1507,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;
     }
@@ -2386,6 +2420,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);
@@ -2487,6 +2530,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-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c
new file mode 100644
index 0000000..e4e33a1
--- /dev/null
+++ b/gcc/gimple-ssa-warn-restrict.c
@@ -0,0 +1,1682 @@
+/* Pass to detect and issue warnings for violations of the restrict
+   qualifier.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Contributed by Martin Sebor <msebor@redhat.com>.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "domwalk.h"
+#include "tree-pass.h"
+#include "builtins.h"
+#include "ssa.h"
+#include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
+#include "diagnostic-core.h"
+#include "fold-const.h"
+#include "gimple-iterator.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
+#include "params.h"
+#include "tree-cfg.h"
+#include "tree-object-size.h"
+#include "calls.h"
+#include "cfgloop.h"
+#include "intl.h"
+
+namespace {
+
+const pass_data pass_data_wrestrict = {
+  GIMPLE_PASS,
+  "wrestrict",
+  OPTGROUP_NONE,
+  TV_NONE,
+  PROP_cfg, /* Properties_required.  */
+  0,	    /* properties_provided.  */
+  0,	    /* properties_destroyed.  */
+  0,	    /* properties_start */
+  0,	    /* properties_finish */
+};
+
+/* Pass to detect violations of strict aliasing requirements in calls
+   to built-in string and raw memory functions.  */
+class pass_wrestrict : public gimple_opt_pass
+{
+ public:
+  pass_wrestrict (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_wrestrict, ctxt)
+    { }
+
+  opt_pass *clone () { return new pass_wrestrict (m_ctxt); }
+
+  virtual bool gate (function *);
+  virtual unsigned int execute (function *);
+};
+
+bool
+pass_wrestrict::gate (function *fun ATTRIBUTE_UNUSED)
+{
+  return warn_array_bounds != 0 || warn_restrict != 0;
+}
+
+/* Class to walk the basic blocks of a function in dominator order.  */
+class wrestrict_dom_walker : public dom_walker
+{
+ public:
+  wrestrict_dom_walker () : dom_walker (CDI_DOMINATORS) {}
+
+  edge before_dom_children (basic_block) FINAL OVERRIDE;
+  bool handle_gimple_call (gimple_stmt_iterator *);
+
+ private:
+  void check_call (gcall *);
+};
+
+edge
+wrestrict_dom_walker::before_dom_children (basic_block bb)
+{
+  /* Iterate over statements, looking for function calls.  */
+  for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);
+       gsi_next (&si))
+    {
+      gimple *stmt = gsi_stmt (si);
+      if (!is_gimple_call (stmt))
+	continue;
+
+      if (gcall *call = as_a <gcall *> (stmt))
+	check_call (call);
+    }
+
+  return NULL;
+}
+
+/* Execute the pass for function FUN, walking in dominator order.  */
+
+unsigned
+pass_wrestrict::execute (function *fun)
+{
+  calculate_dominance_info (CDI_DOMINATORS);
+
+  wrestrict_dom_walker walker;
+  walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
+
+  return 0;
+}
+
+/* Description of a memory reference by a built-in function.  This
+   is similar to ao_ref but made especially suitable for -Wrestrict
+   and not for optimization.  */
+struct builtin_memref
+{
+  /* The original pointer argument to the built-in function.  */
+  tree ptr;
+  /* The referenced subobject or NULL if not available, and the base
+     object of the memory reference or NULL.  */
+  tree ref;
+  tree base;
+
+  /* The size of the BASE object, PTRDIFF_MAX if indeterminate, and
+     -1 until (lazily) initialized.  */
+  offset_int objsize;
+
+  /* The offset of the referenced subobject.  Used to avoid warnings for
+     (apparently) possibly but not definitively overlapping accesses to
+     member arrays.  */
+  offset_int refoff;
+
+  /* The offset range relative to the base.  */
+  offset_int offrange[2];
+  /* The size range of the access.  */
+  offset_int sizrange[2];
+
+  /* True for "bounded" string functions like strncat, and strncpy
+     and their variants that specify either an exact or upper bound
+     on the size of the accesses they perform.  For strncat both
+     the source and destination references are bounded.  For strncpy
+     only the destination reference is.  */
+  bool strbounded_p;
+
+  builtin_memref (tree, tree);
+
+  tree offset_out_of_bounds (offset_int[2]) const;
+};
+
+/* Description of a memory access by a raw memory or string built-in
+   function involving a pair of builtin_memref's.  */
+class builtin_access
+{
+ public:
+  /* Destination and source memory reference.  */
+  builtin_memref* const dstref;
+  builtin_memref* const srcref;
+  /* The minimum and maximum size of the access. */
+  HOST_WIDE_INT sizrange[2];
+
+  /* The minimum and maximum offset of an overlap of the access
+     (if it does, in fact, overlap), and the size of the overlap.  */
+  HOST_WIDE_INT ovloff[2];
+  HOST_WIDE_INT ovlsiz[2];
+
+  builtin_access (gcall *, builtin_memref &, builtin_memref &);
+
+  /* Entry point to determine overlap.  */
+  bool overlap ();
+
+ private:
+  /* Implementation functions used to determine overlap.  */
+  bool generic_overlap ();
+  bool strcat_overlap ();
+  bool strcpy_overlap ();
+
+  bool no_overlap ()
+  {
+    return false;
+  }
+
+ private:
+  /* Temporaries used to compute the final result.  */
+  offset_int dstoff[2];
+  offset_int srcoff[2];
+  offset_int dstsiz[2];
+  offset_int srcsiz[2];
+
+  /* Pointer to a member function to call to determine overlap.  */
+  bool (builtin_access::*detect_overlap) ();
+};
+
+/* Initialize a memory reference representation from a pointer EXPR and
+   a size SIZE in bytes.  If SIZE is NULL_TREE then the size is assumed
+   to be unknown.  */
+
+builtin_memref::builtin_memref (tree expr, tree size)
+: ptr (expr),
+  ref (),
+  base (),
+  objsize (-1),
+  refoff (HOST_WIDE_INT_MIN),
+  offrange (),
+  sizrange (),
+  strbounded_p ()
+{
+  /* Unfortunately, wide_int default ctor is a no-op so array members
+     of the type must be set individually.  */
+  offrange[0] = offrange[1] = 0;
+  sizrange[0] = sizrange[1] = 0;
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  if (TREE_CODE (expr) == SSA_NAME)
+    {
+      /* Try to tease the offset out of the pointer.  */
+      gimple *stmt = SSA_NAME_DEF_STMT (expr);
+      if (gimple_assign_single_p (stmt)
+	  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+	expr = gimple_assign_rhs1 (stmt);
+      else if (is_gimple_assign (stmt))
+	{
+	  tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == NOP_EXPR)
+	    {
+	      tree rhs = gimple_assign_rhs1 (stmt);
+	      if (POINTER_TYPE_P (TREE_TYPE (rhs)))
+		expr = gimple_assign_rhs1 (stmt);
+	    }
+	  else if (code == POINTER_PLUS_EXPR)
+	    {
+	      expr = gimple_assign_rhs1 (stmt);
+
+	      tree offset = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (offset) == INTEGER_CST)
+		{
+		  offset_int off = int_cst_value (offset);
+		  offrange[0] = off;
+		  offrange[1] = off;
+
+		  if (TREE_CODE (expr) == SSA_NAME)
+		    {
+		      gimple *stmt = SSA_NAME_DEF_STMT (expr);
+		      if (gimple_assign_single_p (stmt)
+			  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+			expr = gimple_assign_rhs1 (stmt);
+		    }
+		}
+	      else if (TREE_CODE (offset) == SSA_NAME)
+		{
+		  wide_int min, max;
+		  value_range_type rng = get_range_info (offset, &min, &max);
+		  if (rng == VR_RANGE)
+		    {
+		      offrange[0] = min.to_shwi ();
+		      offrange[1] = max.to_shwi ();
+		    }
+		  else if (rng == VR_ANTI_RANGE)
+		    {
+		      offrange[0] = (max + 1).to_shwi ();
+		      offrange[1] = (min - 1).to_shwi ();
+		    }
+		  else
+		    {
+		      gimple *stmt = SSA_NAME_DEF_STMT (offset);
+		      if (is_gimple_assign (stmt)
+			  && gimple_assign_rhs_code (stmt) == NOP_EXPR)
+			{
+			  /* Use the bounds of the type of the NOP_EXPR operand
+			     even if it's signed.  The result doesn't trigger
+			     warnings but makes their output more readable.  */
+			  tree type = TREE_TYPE (gimple_assign_rhs1 (stmt));
+			  offrange[0] = wi::to_offset (TYPE_MIN_VALUE (type));
+			  offrange[1] = wi::to_offset (TYPE_MAX_VALUE (type));
+			}
+		      else
+			offrange[1] = maxobjsize;
+		    }
+		}
+	      else
+		offrange[1] = maxobjsize;
+	    }
+	}
+    }
+
+  if (TREE_CODE (expr) == ADDR_EXPR)
+    {
+      HOST_WIDE_INT off;
+      tree oper = TREE_OPERAND (expr, 0);
+
+      /* Determine the base object or pointer of the reference
+	 and its constant offset from the beginning of the base.  */
+      base = get_addr_base_and_unit_offset (oper, &off);
+
+      if (base)
+	{
+	  offrange[0] += off;
+	  offrange[1] += off;
+
+	  /* Stash the reference for offset validation. */
+	  ref = oper;
+
+	  /* Also stash the constant offset for offset validation.  */
+	  tree_code code = TREE_CODE (oper);
+	  if (code == ARRAY_REF || code == COMPONENT_REF)
+	    {
+	      if (code == COMPONENT_REF)
+		{
+		  tree field = TREE_OPERAND (ref, 1);
+		  tree fldoff = DECL_FIELD_OFFSET (field);
+		  if (TREE_CODE (fldoff) == INTEGER_CST)
+		    refoff = (wi::to_offset (fldoff)
+			      + (wi::to_offset (DECL_FIELD_BIT_OFFSET (field))
+				 >> LOG2_BITS_PER_UNIT));
+		}
+	    }
+	  else if (code == MEM_REF)
+	    {
+	      oper = TREE_OPERAND (ref, 0);
+	      tree_code code = TREE_CODE (oper);
+	      if (code == ADDR_EXPR || code == SSA_NAME)
+		{
+		  tree type = TREE_TYPE (oper);
+		  if (POINTER_TYPE_P (type))
+		    {
+		      /* Take the size of the referenced type as
+			 the referenced object's size.  Set its offset
+			 to HWI_MAX here as a flag so it can be reset
+			 to zero at the end of this function.  */
+		      refoff = HOST_WIDE_INT_MAX;
+		    }
+		}
+	    }
+	}
+      else
+	{
+	  size = NULL_TREE;
+	  base = get_base_address (TREE_OPERAND (expr, 0));
+	}
+    }
+
+  if (!base)
+    base = build2 (MEM_REF, char_type_node, expr, null_pointer_node);
+
+  if (TREE_CODE (base) == MEM_REF)
+    {
+      offset_int off = mem_ref_offset (base);
+      refoff += off;
+      offrange[0] += off;
+      offrange[1] += off;
+      base = TREE_OPERAND (base, 0);
+    }
+
+  if (TREE_CODE (base) == SSA_NAME)
+    if (gimple *stmt = SSA_NAME_DEF_STMT (base))
+      {
+	enum gimple_code code = gimple_code (stmt);
+	if (code == GIMPLE_ASSIGN)
+	  if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+	    {
+	      base = gimple_assign_rhs1 (stmt);
+
+	      tree offset = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (offset) == INTEGER_CST)
+		{
+		  offset_int off = int_cst_value (offset);
+		  refoff += off;
+		  offrange[0] += off;
+		  offrange[1] += off;
+		}
+	    }
+
+	if (TREE_CODE (base) == SSA_NAME && SSA_NAME_VAR (base))
+	  base = SSA_NAME_VAR (base);
+      }
+
+  if (size)
+    {
+      tree range[2];
+      /* Determine the size range, allowing for the result to be [0, 0]
+	 for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX.  */
+      get_size_range (size, range, true);
+      sizrange[0] = wi::to_offset (range[0]);
+      sizrange[1] = wi::to_offset (range[1]);
+      /* get_size_range returns SIZE_MAX for the maximum size.
+	 Constrain it to the real maximum of PTRDIFF_MAX.  */
+      if (sizrange[1] > maxobjsize)
+	sizrange[1] = maxobjsize;
+    }
+  else
+    sizrange[1] = maxobjsize;
+
+  /* Reset the reference offset to zero (see above).  */
+  if (refoff >= HOST_WIDE_INT_MAX)
+    refoff = 0;
+}
+
+/* Return error_mark_node if the signed offset exceeds the bounds
+   of the address space (PTRDIFF_MAX).  Otherwise, return BASE when
+   the offset exceeds the bounds of the BASE object, and set OOBOFF
+   to the past-the-end offset formed by the reference, including
+   its size.  Otherwise return NULL to indicate the offset is in
+   bounds.  */
+
+tree
+builtin_memref::offset_out_of_bounds (offset_int ooboff[2]) const
+{
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  /* A temporary, possibly adjusted, copy of the offset range.  */
+  offset_int offrng[2] = { offrange[0], offrange[1] };
+
+  if (DECL_P (base) && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE)
+    {
+      if (offrng[1] < offrng[0])
+  	offrng[1] = maxobjsize;
+    }
+
+  /* Conservative offset of the last byte of the referenced object.  */
+  offset_int endoff;
+
+  /* The bounds need not be ordered.  Set HIB to use as the index
+     of the larger of the bounds and LOB as the opposite.  */
+  bool hib = wi::les_p (offrng[0], offrng[1]);
+  bool lob = !hib;
+
+  if (objsize < 0)
+    {
+      endoff = offrng[lob] + sizrange[0];
+
+      /* For a reference through a pointer to an object of unknown size
+	 all initial offsets are considered valid, positive as well as
+	 negative, since the pointer itself can point past the beginning
+	 of the object.  However, the sum of the lower bound of the offset
+	 and that of the size must be less than or equal than PTRDIFF_MAX.  */
+      if (endoff > maxobjsize)
+	return error_mark_node;
+
+      return NULL_TREE;
+    }
+
+  /* A reference to an object of known size must be within the bounds
+     of the base object.  */
+  if (offrng[hib] < 0 || offrng[lob] > objsize)
+    return base;
+
+  /* The extent of the reference must also be within the bounds of
+     the base object (if known) or the maximum object size otherwise.  */
+  endoff = wi::smax (offrng[lob], 0) + sizrange[0];
+  if (endoff > maxobjsize)
+    return error_mark_node;
+
+  if (endoff <= objsize)
+    return NULL_TREE;
+
+  /* Set the out-of-bounds offset range to be one greater than
+     that delimited by the reference including its size.  */
+  ooboff[lob] = objsize + 1;
+
+  if (endoff > ooboff[lob])
+    ooboff[hib] = endoff;
+  else
+    ooboff[hib] = wi::smax (offrng[lob], 0) + sizrange[1];
+
+  return base;
+}
+
+/* Create an association between the memory references DST and SRC
+   for access by a call EXPR to a memory or string built-in funtion.  */
+
+builtin_access::builtin_access (gcall *call, builtin_memref &dst,
+				builtin_memref &src)
+: dstref (&dst), srcref (&src), sizrange (), ovloff (), ovlsiz (),
+  dstoff (), srcoff (), dstsiz (), srcsiz ()
+{
+  /* Zero out since the offset_int ctors invoked above are no-op.  */
+  dstoff[0] = dstoff[1] = 0;
+  srcoff[0] = srcoff[1] = 0;
+  dstsiz[0] = dstsiz[1] = 0;
+  srcsiz[0] = srcsiz[1] = 0;
+
+  /* Object Size Type to use to determine the size of the destination
+     and source objects.  Overridden below for raw memory functions.  */
+  int ostype = 1;
+
+  /* True when the size of one reference depends on the offset of
+     itself or the other.  */
+  bool depends_p;
+
+  /* True when the size of the destination reference DSTREF has been
+     determined from SRCREF and so needs to be adjusted by the latter's
+     offset.  Only meaningful for bounded string functions like strncpy.  */
+  bool dstadjust_p = false;
+
+  /* The size argument number (depends on the built-in).  */
+  unsigned sizeargno = 2;
+  if (gimple_call_with_bounds_p (call))
+    sizeargno += 2;
+
+  tree func = gimple_call_fndecl (call);
+  switch (DECL_FUNCTION_CODE (func))
+    {
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMCPY_CHK_CHKP:
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHKP:
+    case BUILT_IN_MEMPCPY_CHK_CHKP:
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::generic_overlap;
+      break;
+
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMMOVE_CHK_CHKP:
+      /* For memmove there is never any overlap to check for.  */
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::no_overlap;
+      break;
+
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      depends_p = true;
+      dstref->strbounded_p = true;
+      detect_overlap = &builtin_access::generic_overlap;
+      break;
+
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STPCPY_CHKP:
+    case BUILT_IN_STPCPY_CHK_CHKP:
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STRCPY_CHKP:
+    case BUILT_IN_STRCPY_CHK_CHKP:
+      depends_p = true;
+      detect_overlap = &builtin_access::strcpy_overlap;
+      break;
+
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRCAT_CHKP:
+    case BUILT_IN_STRCAT_CHK_CHKP:
+      depends_p = true;
+      detect_overlap = &builtin_access::strcat_overlap;
+      break;
+
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+      depends_p = true;
+      dstref->strbounded_p = true;
+      srcref->strbounded_p = true;
+      detect_overlap = &builtin_access::strcat_overlap;
+      break;
+
+    default:
+      /* Handle other string functions here whose access may need
+	 to be validated for in-bounds offsets and non-overlapping
+	 copies.  (Not all _chkp functions have BUILT_IN_XXX_CHKP
+	 macros so they need to be handled here.)  */
+      return;
+    }
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  /* Try to determine the size of the base object.  compute_objsize
+     expects a pointer so create one if BASE is a non-pointer object.  */
+  tree addr = dst.base;
+  if (!POINTER_TYPE_P (TREE_TYPE (addr)))
+    addr = build1 (ADDR_EXPR, (TREE_TYPE (addr)), addr);
+
+  if (tree dstsize = compute_objsize (addr, ostype))
+    dst.objsize = wi::to_offset (dstsize);
+  else if (POINTER_TYPE_P (TREE_TYPE (addr)))
+    dst.objsize = HOST_WIDE_INT_MIN;
+  else
+    dst.objsize = maxobjsize;
+
+  addr = src.base;
+  if (!POINTER_TYPE_P (TREE_TYPE (addr)))
+    addr = build1 (ADDR_EXPR, (TREE_TYPE (addr)), addr);
+
+  if (tree srcsize = compute_objsize (addr, ostype))
+    src.objsize = wi::to_offset (srcsize);
+  else if (POINTER_TYPE_P (TREE_TYPE (addr)))
+    src.objsize = HOST_WIDE_INT_MIN;
+  else
+    dst.objsize = maxobjsize;
+
+  /* If there is no dependency between the references or the base
+     objects of the two references aren't the same there's nothing
+     else to do.  */
+  if (depends_p && dstref->base != srcref->base)
+    return;
+
+  /* ...otherwise, make adjustments for references to the same object
+     by string functions to reflect the constraints imposed by the called
+     built-in function.  */
+
+  /* For bounded string functions determine the range of the bound
+     on the access.  */
+  offset_int bounds[2] = { 0, 0 };
+  if (dstref->strbounded_p)
+    {
+      tree size = gimple_call_arg (call, sizeargno);
+      tree range[2];
+      if (get_size_range (size, range, true))
+	{
+	  bounds[0] = wi::to_offset (range[0]);
+	  bounds[1] = wi::to_offset (range[1]);
+	}
+
+      /* If both references' size ranges are indeterminate use the last
+	 (size) argument from the function call as a substitute.  This
+	 may only be necessary for strncpy (but not for memcpy where
+	 the size range would have been already determined this way).  */
+      if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize
+	  && srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+	{
+	  dstref->sizrange[0] = bounds[0];
+	  dstref->sizrange[1] = bounds[1];
+	}
+    }
+
+  /* The size range of one reference involving the same base object
+     can be determined from the size range of the other reference.
+     This makes it possible to compute accurate offsets for warnings
+     involving functions like strcpy where the length of just one of
+     the two arguments is known (determined by tree-ssa-strlen).  */
+  if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize)
+    {
+      /* When the destination size is unknown set it to the size of
+	 the source.  */
+      dstref->sizrange[0] = srcref->sizrange[0];
+      dstref->sizrange[1] = srcref->sizrange[1];
+    }
+  else if (srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+    {
+      /* When the source size is unknown set it to the size of
+	 the destination.  */
+      srcref->sizrange[0] = dstref->sizrange[0];
+      srcref->sizrange[1] = dstref->sizrange[1];
+
+      if (depends_p)
+	{
+	  if (dstref->strbounded_p)
+	    {
+	      /* Read access by strncpy is bounded.  */
+	      if (bounds[0] < srcref->sizrange[0])
+		srcref->sizrange[0] = bounds[0];
+	      if (bounds[1] < srcref->sizrange[1])
+		srcref->sizrange[1] = bounds[1];
+	    }
+
+	  /* For string functions, adjust the size range of the source
+	     reference by the inverse boundaries of the offset (because
+	     the higher  the offset into the string the shorter its
+	     length).  */
+	  if (srcref->offrange[1] < srcref->sizrange[0])
+	    srcref->sizrange[0] -= srcref->offrange[1];
+	  else
+	    srcref->sizrange[0] = 0;
+
+	  if (srcref->offrange[0] > 0)
+	    {
+	      if (srcref->offrange[0] < srcref->sizrange[1])
+		srcref->sizrange[1] -= srcref->offrange[0];
+	      else
+		srcref->sizrange[1] = 0;
+	    }
+
+	  dstadjust_p = true;
+	}
+    }
+
+  if (detect_overlap == &builtin_access::generic_overlap)
+    {
+      if (dstref->strbounded_p)
+	{
+	  dstref->sizrange[0] = bounds[0];
+	  dstref->sizrange[1] = bounds[1];
+
+	  if (dstref->sizrange[0] < srcref->sizrange[0])
+	    srcref->sizrange[0] = dstref->sizrange[0];
+
+	  if (dstref->sizrange[1] < srcref->sizrange[1])
+	    srcref->sizrange[1] = dstref->sizrange[1];
+	}
+    }
+  else if (detect_overlap == &builtin_access::strcpy_overlap)
+    {
+      /* For strcpy, adjust the destination size range to match that
+	 of the source computed above.  */
+      if (depends_p && dstadjust_p)
+	{
+	  dstref->sizrange[0] = srcref->sizrange[0];
+	  dstref->sizrange[1] = srcref->sizrange[1];
+	}
+    }
+}
+
+/* Return true if the bounded mempry (memcpy amd similar) or string function
+   access (strncpy and similar) ACS overlaps.  */
+
+bool
+builtin_access::generic_overlap ()
+{
+  builtin_access &acs = *this;
+  const builtin_memref *dstref = acs.dstref;
+  const builtin_memref *srcref = acs.srcref;
+
+  gcc_assert (dstref->base == srcref->base);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  offset_int maxsize = dstref->objsize < 0 ? maxobjsize : dstref->objsize;
+  gcc_assert (maxsize <= maxobjsize);
+
+  /* Adjust the larger bounds of the offsets (which may be the first
+     element if the lower bound is larger than the upper bound) to
+     make them valid for the smallest access (if possible) but no smaller
+     than the smaller bounds.  */
+  bool dstord = wi::les_p (acs.dstoff[0], acs.dstoff[1]);
+  if (maxsize < acs.dstoff[dstord] + acs.dstsiz[0])
+    acs.dstoff[dstord] = maxsize - acs.dstsiz[0];
+  if (acs.dstoff[dstord] < acs.dstoff[0])
+    acs.dstoff[dstord] = acs.dstoff[0];
+
+  bool srcord = wi::les_p (acs.srcoff[0], acs.srcoff[1]);
+  if (maxsize < acs.srcoff[srcord] + acs.srcsiz[0])
+    acs.srcoff[srcord] = maxsize - acs.srcsiz[0];
+  if (acs.srcoff[srcord] < acs.srcoff[0])
+    acs.srcoff[srcord] = acs.srcoff[0];
+
+  /* Determine the minimum and maximum space for the access given
+     the offsets.  */
+  offset_int space[2];
+  space[0] = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+  space[1] = space[0];
+
+  offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+  if (acs.srcsiz[0] > 0)
+    {
+      if (d < space[0])
+	space[0] = d;
+
+      if (space[1] < d)
+	space[1] = d;
+    }
+  else
+    space[1] = acs.dstsiz[1];
+
+  d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+  if (d < space[0])
+    space[0] = d;
+
+  if (space[1] < d)
+    space[1] = d;
+
+  /* Treat raw memory functions both of whose references are bounded
+     as special and permit uncertain overlaps to go undetected.  For
+     all kinds of constant offset and constant size accesses, if
+     overlap isn't certain it is not possible.  */
+  bool overlap_possible = space[0] < acs.dstsiz[1];
+  if (!overlap_possible)
+    return false;
+
+  bool overlap_certain = space[1] < acs.dstsiz[0];
+
+  if (!overlap_certain
+      && !dstref->strbounded_p
+      && detect_overlap == &builtin_access::generic_overlap)
+    return false;
+
+  /* True for stpcpy and strcpy.  */
+  bool stxcpy_p = detect_overlap == &builtin_access::strcpy_overlap;
+
+  /* True when the size of the source sequence is inveresely proportional
+     to the offset into it, such as in strcpy (DST, SRC + OFF).  */
+  bool sizdep_p = (stxcpy_p
+		   && acs.srcoff[0] != acs.srcoff[1]
+		   && acs.sizrange[0] != acs.sizrange[1]);
+
+  if (dstref->refoff >= 0
+      && srcref->refoff >= 0
+      && dstref->refoff != srcref->refoff
+      && (stxcpy_p || dstref->strbounded_p || srcref->strbounded_p))
+    return false;
+
+  /* The number of bytes written.  */
+  offset_int write_min = acs.dstsiz[0];
+  offset_int write_max = acs.dstsiz[1];
+
+  if (acs.dstoff[0] <= acs.srcoff[0])
+    {
+      /* The lower bound of the offset of the first reference is less
+	 than the lower bound of the second.  */
+      if (acs.dstoff[1] <= acs.srcoff[1])
+	{
+	  /* The upper bound of the offset of the first reference is less
+	     than the upper bound of the second.
+	     The size of the overlap is the size of the access minus
+	     the difference between the upper bound of the second offset
+	     and the lower bound of the first offset.  */
+	  if (overlap_certain)
+	    acs.ovlsiz[0]
+	      = (write_min - (acs.srcoff[1] - acs.dstoff[0])).to_shwi ();
+	  acs.ovlsiz[1]
+	    = (write_max - (acs.srcoff[0] - acs.dstoff[0])).to_shwi ();
+
+	  if (acs.ovlsiz[1] > acs.srcsiz[1])
+	    acs.ovlsiz[1] = acs.srcsiz[1].to_shwi ();
+	}
+      else
+	{
+	  /* The offset of the overlap is the lower bound of the first
+	     offset plus the initial non-overlapping size of the access.  */
+	  if (overlap_certain)
+	    acs.ovlsiz[0]
+	      = (write_min - (acs.dstoff[1] - acs.srcoff[0])).to_shwi ();
+
+	  acs.ovlsiz[1]
+	    = (write_max - (acs.dstoff[0] - acs.srcoff[0])).to_shwi ();
+	}
+
+      if (acs.ovlsiz[1])
+	acs.ovloff[1] = (acs.dstoff[0] + write_max).to_shwi () - acs.ovlsiz[1];
+      else
+	acs.ovloff[1] = acs.srcoff[0].to_shwi ();
+
+      if (acs.ovloff[1] >= acs.srcoff[0] + acs.srcsiz[0])
+	acs.ovloff[1] = (acs.srcoff[0] + acs.srcsiz[0]).to_shwi ();
+
+      if (acs.ovlsiz[0] < 0)
+	{
+	  acs.ovlsiz[0] = 0;
+	  acs.ovloff[0] = acs.ovloff[1];
+	}
+      else if (acs.ovlsiz[0] > 0)
+	acs.ovloff[0] = (acs.dstoff[0] + write_min).to_shwi () - acs.ovlsiz[0];
+      else if (acs.ovloff[1] && acs.ovlsiz[1] > 1)
+	{
+	  /* When the minimum overlap is zero then for strcpy the upper
+	     bound of the overlap offset (which is actually the smaller
+	     of the two offset bounds to correspond to the lager size
+	     of the overlap) is two less than the upper bound of the end
+	     offset of the overlap.  For other functions it's just one
+	     less.  */
+	  acs.ovloff[0] = (acs.ovloff[1] + acs.ovlsiz[1] - 1 - stxcpy_p);
+	}
+      else
+	acs.ovloff[0] = acs.ovloff[1];
+    }
+  else
+    {
+      /* The lower bound of the offset of the first reference is
+	 the same or greater that the lower bound of the second.
+	 The size of the overlap is the size of the access minus
+	 the difference between the upper bound of the first offset
+	 and the lower bound of the first offset.  */
+      acs.ovlsiz[0] = (write_min - (acs.dstoff[1] - acs.srcoff[0])).to_shwi ();
+      acs.ovlsiz[1] = (write_max - (acs.dstoff[0] - acs.srcoff[0])).to_shwi ();
+
+      if (acs.ovlsiz[1])
+	acs.ovloff[1] = (acs.srcoff[1] + write_max).to_shwi () - acs.ovlsiz[1];
+      else
+	acs.ovloff[1] = acs.dstoff[1].to_shwi ();
+
+      if (acs.ovlsiz[0] < 0)
+	{
+	  acs.ovlsiz[0] = 0;
+	  acs.ovloff[0] = acs.ovloff[1];
+	}
+      else if (acs.ovlsiz[0] > 0)
+	/* The offset of the overlap is the upper bound of the first
+	   offset plus the initial non-overlapping size of the access.  */
+	acs.ovloff[0] = (acs.srcoff[0] + write_min).to_shwi () - acs.ovlsiz[0];
+      else
+	acs.ovloff[0] = (acs.ovloff[1] + acs.ovlsiz[1] - 1 - sizdep_p);
+    }
+
+  return true;
+}
+
+/* Return true if the strcat-like access overlaps.  */
+
+bool
+builtin_access::strcat_overlap ()
+{
+  builtin_access &acs = *this;
+  const builtin_memref *dstref = acs.dstref;
+  const builtin_memref *srcref = acs.srcref;
+
+  gcc_assert (dstref->base == srcref->base);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  gcc_assert (dstref->base && dstref->base == srcref->base);
+
+  /* Adjust for strcat-like accesses.  */
+
+  /* As a special case for strcat, set the DSTREF offsets to the length
+     of the source string since the function starts writing at the first
+     nul, and set the size to 1 for the length of the nul.  */
+  acs.dstoff[0] += acs.dstsiz[0];
+  acs.dstoff[1] += acs.dstsiz[1];
+
+  bool strfunc_unknown_args = acs.dstsiz[0] == 0 && acs.dstsiz[1] != 0;
+
+  /* The lower bound is zero when the size is unknown because then
+     overlap is not certain. */
+  acs.dstsiz[0] = strfunc_unknown_args ? 0 : 1;
+  acs.dstsiz[1] = 1;
+
+  offset_int maxsize = dstref->objsize < 0 ? maxobjsize : dstref->objsize;
+  gcc_assert (maxsize <= maxobjsize);
+
+  /* For references to the same base object, determine if there's a pair
+     of valid offsets into the two references such that access between
+     them doesn't overlap.  Adjust both upper bounds to be valid for
+     the smaller size (i.e., at most MAXSIZE - SIZE).  */
+
+  if (maxsize < acs.dstoff[1] + acs.dstsiz[0])
+    acs.dstoff[1] = maxsize - acs.dstsiz[0];
+
+  if (maxsize < acs.srcoff[1] + acs.srcsiz[0])
+    acs.srcoff[1] = maxsize - acs.srcsiz[0];
+
+  /* Check to see if there's enough space for both accesses without
+     overlap.  Determine the optimistic (maximum) amount of available
+     space.  */
+  offset_int space;
+  if (acs.dstoff[0] <= acs.srcoff[0])
+    {
+      if (acs.dstoff[1] < acs.srcoff[1])
+	space = acs.srcoff[1] + acs.srcsiz[0] - acs.dstoff[0];
+      else
+	space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+    }
+  else
+    space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+
+  /* Overlap is certain if the distance between the farthest offsets
+     of the opposite accesses is less than the sum of the lower bounds
+     of the sizes of the two accesses.  */
+  bool overlap_certain = space < acs.dstsiz[0] + acs.srcsiz[0];
+
+  /* For a constant-offset, constant size access, consider the largest
+     distance between the offset bounds and the lower bound of the access
+     size.  If the overlap isn't certain return success.  */
+  if (!overlap_certain
+      && acs.dstoff[0] == acs.dstoff[1]
+      && acs.srcoff[0] == acs.srcoff[1]
+      && acs.dstsiz[0] == acs.dstsiz[1]
+      && acs.srcsiz[0] == acs.srcsiz[1])
+    return false;
+
+  /* Overlap is not certain but may be possible.  */
+
+  offset_int access_min = acs.dstsiz[0] + acs.srcsiz[0];
+
+  /* Determine the conservative (minimum) amount of space.  */
+  space = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+  offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+  if (d < space)
+    space = d;
+  d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+  if (d < space)
+    space = d;
+
+  /* For a strict test (used for strcpy and similar with unknown or
+     variable bounds or sizes), consider the smallest distance between
+     the offset bounds and either the upper bound of the access size
+     if known, or the lower bound otherwise.  */
+  if (access_min <= space && (access_min != 0 || !strfunc_unknown_args))
+    return false;
+
+  /* When strcat overlap is certain it is always a single byte:
+     the terminatinn NUL, regardless of offsets and sizes.  When
+     overlap is only possible its range is [0, 1].  */
+  acs.ovlsiz[0] = dstref->sizrange[0] == dstref->sizrange[1] ? 1 : 0;
+  acs.ovlsiz[1] = 1;
+  acs.ovloff[0] = (dstref->sizrange[0] + dstref->offrange[0]).to_shwi ();
+  acs.ovloff[1] = (dstref->sizrange[1] + dstref->offrange[1]).to_shwi ();
+
+  acs.sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+  acs.sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+  return true;
+}
+
+/* Return true if the strcpy-like access overlaps.  */
+
+bool
+builtin_access::strcpy_overlap ()
+{
+  return generic_overlap ();
+}
+
+
+/* Return true if DSTREF and SRCREF describe accesses that either overlap
+   one another or that, in order not to overlap, would imply that the size
+   of the referenced object(s) exceeds the maximum size of an object.  Set
+   Otherwise, if DSTREF and SRCREF do not definitely overlap (even though
+   they may overlap in a way that's not apparent from the available data),
+   return false.  */
+
+bool
+builtin_access::overlap ()
+{
+  builtin_access &acs = *this;
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  acs.sizrange[0] = wi::smax (dstref->sizrange[0],
+			      srcref->sizrange[0]).to_shwi ();
+  acs.sizrange[1] = wi::smax (dstref->sizrange[1],
+			      srcref->sizrange[1]).to_shwi ();
+
+  /* Check to see if the two references refer to regions that are
+     too large not to overlap in the address space (whose maximum
+     size is PTRDIFF_MAX).  */
+  offset_int size = dstref->sizrange[0] + srcref->sizrange[0];
+  if (maxobjsize < size)
+    {
+      acs.ovloff[0] = (maxobjsize - dstref->sizrange[0]).to_shwi ();
+      acs.ovlsiz[0] = (size - maxobjsize).to_shwi ();
+      return true;
+    }
+
+  /* If both base objects aren't known return the maximum possible
+     offset that would make them not overlap.  */
+  if (!dstref->base || !srcref->base)
+    return false;
+
+  /* If the base object is an array adjust the lower bound of the offset
+     to be non-negative.  */
+  if (dstref->base
+      && TREE_CODE (TREE_TYPE (dstref->base)) == ARRAY_TYPE)
+    acs.dstoff[0] = wi::smax (dstref->offrange[0], 0);
+  else
+    acs.dstoff[0] = dstref->offrange[0];
+
+  acs.dstoff[1] = dstref->offrange[1];
+
+  if (srcref->base
+      && TREE_CODE (TREE_TYPE (srcref->base)) == ARRAY_TYPE)
+    acs.srcoff[0] = wi::smax (srcref->offrange[0], 0);
+  else
+    acs.srcoff[0] = srcref->offrange[0];
+
+  acs.srcoff[1] = srcref->offrange[1];
+
+  /* When the lower bound of the offset is less that the upper bound
+     disregard it and use the inverse of the maximum object size
+     instead.  The upper bound is the result of a negative offset
+     being represented as a large positive value.  */
+  if (acs.dstoff[1] < acs.dstoff[0])
+    acs.dstoff[0] = -maxobjsize;
+
+  /* Validate the offset and size of each reference on its own first.
+     This is independent of whether or not the base objects are the
+     same.  Normally, this would have already been detected and
+     diagnosed by -Warray-bounds, unless it has been disabled.  */
+  offset_int maxoff = acs.dstoff[0] + dstref->sizrange[0];
+  if (maxobjsize < maxoff)
+    {
+      acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+      acs.ovloff[0] = acs.dstoff[0].to_shwi () - acs.ovlsiz[0];
+      return true;
+    }
+
+  /* Repeat the same as above but for the source offsets.  */
+  if (acs.srcoff[1] < acs.srcoff[0])
+    acs.srcoff[0] = -maxobjsize;
+
+  maxoff = acs.srcoff[0] + srcref->sizrange[0];
+  if (maxobjsize < maxoff)
+    {
+      acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+      acs.ovlsiz[1] = (acs.srcoff[0] + srcref->sizrange[1]
+		       - maxobjsize).to_shwi ();
+      acs.ovloff[0] = acs.srcoff[0].to_shwi () - acs.ovlsiz[0];
+      return true;
+    }
+
+  if (dstref->base != srcref->base)
+    return false;
+
+  acs.dstsiz[0] = dstref->sizrange[0];
+  acs.dstsiz[1] = dstref->sizrange[1];
+
+  acs.srcsiz[0] = srcref->sizrange[0];
+  acs.srcsiz[1] = srcref->sizrange[1];
+
+  /* Call the appropriate function to determine the overlap.  */
+  if ((this->*detect_overlap) ())
+    {
+      sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+      sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+      return true;
+    }
+
+  return false;
+}
+
+/* Attempt to detect and diagnose an overlapping copy in a call expression
+   EXPR involving an an access ACS to a built-in memory or string function.
+   Return true when one has been detected, false otherwise.  */
+
+static bool
+maybe_diag_overlap (location_t loc, gcall *call, builtin_access &acs)
+{
+  const builtin_memref &dstref = *acs.dstref;
+  const builtin_memref &srcref = *acs.srcref;
+
+  /* Determine the range of offsets and sizes of the overlap if it
+     exists and issue diagnostics.  */
+  HOST_WIDE_INT *ovloff = acs.ovloff;
+  HOST_WIDE_INT *ovlsiz = acs.ovlsiz;
+  HOST_WIDE_INT *sizrange = acs.sizrange;
+
+  tree func = gimple_call_fndecl (call);
+
+  if (!acs.overlap ())
+    return false;
+
+  /* To avoid a combinatorial explosion of diagnostics format the offsets
+     or their ranges as strings and use them in the warning calls below.  */
+  char offstr[3][64];
+
+  if (dstref.offrange[0] == dstref.offrange[1]
+      || dstref.offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr[0], "%lli", (long long) dstref.offrange[0].to_shwi ());
+  else
+    sprintf (offstr[0], "[%lli, %lli]",
+             (long long) dstref.offrange[0].to_shwi (),
+             (long long) dstref.offrange[1].to_shwi ());
+
+  if (srcref.offrange[0] == srcref.offrange[1]
+      || srcref.offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr[1], "%lli", (long long) srcref.offrange[0].to_shwi ());
+  else
+    sprintf (offstr[1], "[%lli, %lli]",
+	     (long long) srcref.offrange[0].to_shwi (),
+             (long long) srcref.offrange[1].to_shwi ());
+
+  if (ovloff[0] == ovloff[1] || !ovloff[1])
+    sprintf (offstr[2], "%lli", (long long) ovloff[0]);
+  else
+    sprintf (offstr[2], "[%lli, %lli]",
+	     (long long) ovloff[0], (long long) ovloff[1]);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  bool must_overlap = ovlsiz[0] > 0;
+
+  if (ovlsiz[1] == 0)
+    ovlsiz[1] = ovlsiz[0];
+
+  if (must_overlap)
+    {
+      /* Issue definitive "overlaps" diagnostic in this block.  */
+
+      if (sizrange[0] == sizrange[1])
+	{
+	  if (ovlsiz[0] == ovlsiz[1])
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? (ovlsiz[0] == 1
+			   ? G_("%G%qD accessing %wu byte at offsets %s "
+				"and %s overlaps %wu byte at offset %s")
+			   :  G_("%G%qD accessing %wu byte at offsets %s "
+				 "and %s overlaps %wu bytes at offset "
+				 "%s"))
+			: (ovlsiz[0] == 1
+			   ? G_("%G%qD accessing %wu bytes at offsets %s "
+				"and %s overlaps %wu byte at offset %s")
+			   : G_("%G%qD accessing %wu bytes at offsets %s "
+				"and %s overlaps %wu bytes at offset "
+				"%s")),
+			call, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? G_("%G%qD accessing %wu byte at offsets %s "
+			     "and %s overlaps between %wu and %wu bytes "
+			     "at offset %s")
+			: G_("%G%qD accessing %wu bytes at offsets %s "
+			     "and %s overlaps between %wu and %wu bytes "
+			     "at offset %s"),
+			call, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+			offstr[2]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? G_("%G%qD accessing %wu byte at offsets %s and "
+			     "%s overlaps %wu or more bytes at offset %s")
+			: G_("%G%qD accessing %wu bytes at offsets %s and "
+			     "%s overlaps %wu or more bytes at offset %s"),
+			call, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  return true;
+	}
+
+      if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+	{
+	  if (ovlsiz[0] == ovlsiz[1])
+	    warning_at (loc, OPT_Wrestrict,
+			ovlsiz[0] == 1
+			? G_("%G%qD accessing between %wu and %wu bytes "
+			     "at offsets %s and %s overlaps %wu byte at "
+			     "offset %s")
+			: G_("%G%qD accessing between %wu and %wu bytes "
+			     "at offsets %s and %s overlaps %wu bytes "
+			     "at offset %s"),
+			call, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	    warning_at (loc, OPT_Wrestrict,
+			"%G%qD accessing between %wu and %wu bytes at "
+			"offsets %s and %s overlaps between %wu and %wu "
+			"bytes at offset %s",
+			call, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+			offstr[2]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%G%qD accessing between %wu and %wu bytes at "
+			"offsets %s and %s overlaps %wu or more bytes "
+			"at offset %s",
+			call, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  return true;
+	}
+
+      if (ovlsiz[0] != ovlsiz[1])
+	ovlsiz[1] = maxobjsize.to_shwi ();
+
+      if (ovlsiz[0] == ovlsiz[1])
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[0] == 1
+		    ? G_("%G%qD accessing %wu or more bytes at offsets "
+			 "%s and %s overlaps %wu byte at offset %s")
+		    :  G_("%G%qD accessing %wu or more bytes at offsets "
+			  "%s and %s overlaps %wu bytes at offset %s"),
+		    call, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], offstr[2]);
+      else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	warning_at (loc, OPT_Wrestrict,
+		    "%G%qD accessing %wu or more bytes at offsets %s "
+		    "and %s overlaps between %wu and %wu bytes "
+		    "at offset %s",
+		    call, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], ovlsiz[1], offstr[2]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%G%qD accessing %wu or more bytes at offsets %s "
+		    "and %s overlaps %wu or more bytes at offset %s",
+		    call, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], offstr[2]);
+      return true;
+    }
+
+  /* Issue "may overlap" diagnostics below.  */
+  gcc_assert (ovlsiz[0] == 0
+	      && ovlsiz[1] > 0
+	      && ovlsiz[1] <= maxobjsize.to_shwi ());
+
+  /* Use more concise wording when one of the offsets is unbounded
+     to avoid confusing the user with large and mostly meaningless
+     numbers.  */
+  bool open_range = ((dstref.offrange[0] == -maxobjsize - 1
+		      && dstref.offrange[1] == maxobjsize)
+		     || (srcref.offrange[0] == -maxobjsize - 1
+			 && srcref.offrange[1] == maxobjsize));
+
+  if (sizrange[0] == sizrange[1] || sizrange[1] == 1)
+    {
+      if (ovlsiz[1] == 1)
+	{
+	  if (open_range)
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[1] == 1
+			? G_("%G%qD accessing %wu byte may overlap "
+			     "%wu byte")
+			: G_("%G%qD accessing %wu bytes may overlap "
+			     "%wu byte"),
+			call, func, sizrange[1], ovlsiz[1]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[1] == 1
+			? G_("%G%qD accessing %wu byte at offsets %s "
+			     "and %s may overlap %wu byte at offset %s")
+			: G_("%G%qD accessing %wu bytes at offsets %s "
+			     "and %s may overlap %wu byte at offset %s"),
+			call, func, sizrange[1], offstr[0], offstr[1],
+			ovlsiz[1], offstr[2]);
+	  return true;
+	}
+
+      if (open_range)
+	warning_at (loc, OPT_Wrestrict,
+		    sizrange[1] == 1
+		    ? G_("%G%qD accessing %wu byte may overlap "
+			 "up to %wu bytes")
+		    : G_("%G%qD accessing %wu bytes may overlap "
+			 "up to %wu bytes"),
+		    call, func, sizrange[1], ovlsiz[1]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    sizrange[1] == 1
+		    ? G_("%G%qD accessing %wu byte at offsets %s and "
+			 "%s may overlap up to %wu bytes at offset %s")
+		    : G_("%G%qD accessing %wu bytes at offsets %s and "
+			 "%s may overlap up to %wu bytes at offset %s"),
+		    call, func, sizrange[1], offstr[0], offstr[1],
+		    ovlsiz[1], offstr[2]);
+      return true;
+    }
+
+  if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+    {
+      if (open_range)
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[1] == 1
+		    ? G_("%G%qD accessing between %wu and %wu bytes "
+			 "may overlap %wu byte")
+		    : G_("%G%qD accessing between %wu and %wu bytes "
+			 "may overlap up to %wu bytes"),
+		    call, func, sizrange[0], sizrange[1], ovlsiz[1]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[1] == 1
+		    ? G_("%G%qD accessing between %wu and %wu bytes "
+			 "at offsets %s and %s may overlap %wu byte "
+			 "at offset %s")
+		    : G_("%G%qD accessing between %wu and %wu bytes "
+			 "at offsets %s and %s may overlap up to %wu "
+			 "bytes at offset %s"),
+		    call, func, sizrange[0], sizrange[1],
+		    offstr[0], offstr[1], ovlsiz[1], offstr[2]);
+      return true;
+    }
+
+  warning_at (loc, OPT_Wrestrict,
+	      ovlsiz[1] == 1
+	      ? G_("%G%qD accessing %wu or more bytes at offsets %s "
+		   "and %s may overlap %wu byte at offset %s")
+	      : G_("%G%qD accessing %wu or more bytes at offsets %s "
+		   "and %s may overlap up to %wu bytes at offset %s"),
+	      call, func, sizrange[0], offstr[0], offstr[1],
+	      ovlsiz[1], offstr[2]);
+
+  return true;
+}
+
+/* Validate REF offsets in an EXPRession passed as an argument to a CALL
+   to a built-in function FUNC to make sure they are within the bounds
+   of the referenced object if its size is known, or PTRDIFF_MAX otherwise.
+   Both initial values of the offsets and their final value computed by
+   the function by incrementing the initial value by the size are
+   validated.  Return true if the offsets are not valid and a diagnostic
+   has been issued.  */
+
+static bool
+maybe_diag_offset_bounds (location_t loc, gcall *call, tree func, tree expr,
+			  const builtin_memref &ref)
+{
+  if (!warn_array_bounds)
+    return false;
+
+  offset_int ooboff[] = { ref.offrange[0], ref.offrange[1] };
+  tree oobref = ref.offset_out_of_bounds (ooboff);
+  if (!oobref)
+    return false;
+
+  if (EXPR_HAS_LOCATION (expr))
+    loc = EXPR_LOCATION (expr);
+
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  tree type;
+
+  char rangestr[2][64];
+  if (ooboff[0] == ooboff[1]
+      || (ooboff[0] != ref.offrange[0]
+	  && ooboff[0].to_shwi () >= ooboff[1].to_shwi ()))
+    sprintf (rangestr[0], "%lli", (long long) ooboff[0].to_shwi ());
+  else
+    sprintf (rangestr[0], "[%lli, %lli]",
+	     (long long) ooboff[0].to_shwi (),
+	     (long long) ooboff[1].to_shwi ());
+
+  if (oobref == error_mark_node)
+    {
+      if (ref.sizrange[0] == ref.sizrange[1])
+	sprintf (rangestr[1], "%lli", (long long) ref.sizrange[0].to_shwi ());
+      else
+	sprintf (rangestr[1], "[%lli, %lli]",
+		 (long long) ref.sizrange[0].to_shwi (),
+		 (long long) ref.sizrange[1].to_shwi ());
+
+      if (DECL_P (ref.base)
+	  && TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
+	{
+	  if (warning_at (loc, OPT_Warray_bounds,
+			  "%G%qD pointer overflow between offset %s "
+			  "and size %s accessing array %qD with type %qT",
+			  call, func, rangestr[0], rangestr[1], ref.base, type))
+	    inform (DECL_SOURCE_LOCATION (ref.base),
+		    "array %qD declared here", ref.base);
+	  else
+	    warning_at (loc, OPT_Warray_bounds,
+			"%G%qD pointer overflow between offset %s "
+			"and size %s",
+			call, func, rangestr[0], rangestr[1]);
+	}
+      else
+	warning_at (loc, OPT_Warray_bounds,
+		    "%G%qD pointer overflow between offset %s "
+		    "and size %s",
+		    call, func, rangestr[0], rangestr[1]);
+    }
+  else if (oobref == ref.base)
+    {
+      const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+      /* True when the offset formed by an access to the reference
+	 is out of bounds, rather than the initial offset wich is
+	 in bounds.  This implies access past the end.  */
+      bool form = ooboff[0] != ref.offrange[0];
+
+      if (DECL_P (ref.base))
+	{
+	  if ((ref.objsize < maxobjsize
+	       && warning_at (loc, OPT_Warray_bounds,
+			      form
+			      ? G_("%G%qD forming offset %s is out of "
+				   "the bounds [0, %wu] of object %qD with "
+				   "type %qT")
+			      : G_("%G%qD offset %s is out of the bounds "
+				   "[0, %wu] of object %qD with type %qT"),
+			      call, func, rangestr[0], ref.objsize.to_uhwi (),
+			      ref.base, TREE_TYPE (ref.base)))
+	      || warning_at (loc, OPT_Warray_bounds,
+			     form
+			     ? G_("%G%qD forming offset %s is out of "
+				  "the bounds of object %qD with type %qT")
+			     : G_("%G%qD offset %s is out of the bounds "
+				  "of object %qD with type %qT"),
+			     call, func, rangestr[0],
+			     ref.base, TREE_TYPE (ref.base)))
+	    inform (DECL_SOURCE_LOCATION (ref.base),
+		    "%qD declared here", ref.base);
+	}
+      else if (ref.objsize < maxobjsize)
+	warning_at (loc, OPT_Warray_bounds,
+		    form
+		    ? G_("%G%qD forming offset %s is out of the bounds "
+			 "[0, %wu]")
+		    : G_("%G%qD offset %s is out of the bounds [0, %wu]"),
+		    call, func, rangestr[0], ref.objsize.to_uhwi ());
+      else
+	warning_at (loc, OPT_Warray_bounds,
+		    form
+		    ? G_("%G%qD forming offset %s is out of bounds")
+		    : G_("%G%qD offset %s is out of bounds"),
+		    call, func, rangestr[0]);	
+    }
+  else if (TREE_CODE (ref.ref) == MEM_REF)
+    {
+      tree type = TREE_TYPE (TREE_OPERAND (ref.ref, 0));
+      if (POINTER_TYPE_P (type))
+	type = TREE_TYPE (type);
+      type = TYPE_MAIN_VARIANT (type);
+
+      warning_at (loc, OPT_Warray_bounds,
+		  "%G%qD offset %s from the object at %qE is out "
+		  "of the bounds of %qT",
+		  call, func, rangestr[0], ref.base, type);
+    }
+  else
+    {
+      type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
+
+      warning_at (loc, OPT_Warray_bounds,
+		"%G%qD offset %s from the object at %qE is out "
+		"of the bounds of referenced subobject %qD with type %qT "
+		"at offset %wu",
+		call, func, rangestr[0], ref.base, TREE_OPERAND (ref.ref, 1),
+		type, ref.refoff.to_uhwi ());
+    }
+
+  return true;
+}
+
+/* Check a CALL statement for restrict-violations and issue warnings
+   if/when appropriate.  */
+
+void
+wrestrict_dom_walker::check_call (gcall *call)
+{
+  /* Avoid checking the call if it has already been diagnosed for
+     some reason.  */
+  if (gimple_no_warning_p (call))
+    return;
+
+  tree func = gimple_call_fndecl (call);
+  if (!func || DECL_BUILT_IN_CLASS (func) != BUILT_IN_NORMAL)
+    return;
+
+  bool with_bounds = gimple_call_with_bounds_p (call);
+
+  /* Argument number to extract from the call (depends on the built-in
+     and its kind).  */
+  unsigned dst_idx = -1;
+  unsigned src_idx = -1;
+  unsigned bnd_idx = -1;
+
+  /* Is this CALL to a string function (as opposed to one to a raw
+     memory function).  */
+  bool strfun = true;
+
+  switch (DECL_FUNCTION_CODE (func))
+    {
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMCPY_CHK_CHKP:
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHKP:
+    case BUILT_IN_MEMPCPY_CHK_CHKP:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMMOVE_CHK_CHKP:
+      strfun = false;
+      /* Fall through.  */
+
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      dst_idx = 0;
+      src_idx = 1 + with_bounds;
+      bnd_idx = 2 + 2 * with_bounds;
+      break;
+
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STPCPY_CHKP:
+    case BUILT_IN_STPCPY_CHK_CHKP:
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STRCPY_CHKP:
+    case BUILT_IN_STRCPY_CHK_CHKP:
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRCAT_CHKP:
+    case BUILT_IN_STRCAT_CHK_CHKP:
+      dst_idx = 0;
+      src_idx = 1 + with_bounds;
+      break;
+
+    default:
+      /* Handle other string functions here whose access may need
+	 to be validated for in-bounds offsets and non-overlapping
+	 copies.  (Not all _chkp functions have BUILT_IN_XXX_CHKP
+	 macros so they need to be handled here.)  */
+      return;
+    }
+
+  unsigned nargs = gimple_call_num_args (call);
+
+  tree dst = dst_idx < nargs ? gimple_call_arg (call, dst_idx) : NULL_TREE;
+  tree src = src_idx < nargs ? gimple_call_arg (call, src_idx) : NULL_TREE;
+  tree dstwr = bnd_idx < nargs ? gimple_call_arg (call, bnd_idx) : NULL_TREE;
+
+  /* For string functions with an unspecified or unknown bound,
+     assume the size of the access is one.  */
+  if (!dstwr && strfun)
+    dstwr = size_one_node;
+
+  if (check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
+    return;
+
+  /* Avoid diagnosing the call again.  */
+  gimple_set_no_warning (call, true);
+}
+
+} /* anonymous namespace */
+
+/* Attempt to detect and diagnose invalid offset bounds and (except for
+   memmove) overlapping copy in a call expression EXPR from SRC to DST
+   and DSTSIZE and SRCSIZE bytes, respectively.  Return false when one
+   or the other has been detected, true otherwise.  */
+
+bool
+check_bounds_or_overlap (gcall *call, tree dst, tree src, tree dstsize,
+			 tree srcsize, bool bounds_only /* = false */)
+{
+  location_t loc = gimple_location (call);
+
+  if (tree block = gimple_block (call))
+    if (location_t *pbloc = block_nonartificial_location (block))
+      loc = *pbloc;
+
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  tree func = gimple_call_fndecl (call);
+
+  bool check_overlap
+    = (bounds_only
+       || (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+	   && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK));
+
+  builtin_memref dstref (dst, dstsize);
+  builtin_memref srcref (src, srcsize);
+
+  builtin_access acs (call, dstref, srcref);
+
+  /* Validate offsets first to make sure they are within the bounds
+     of the destination object if its size is known, or PTRDIFF_MAX
+     otherwise.  */
+  if (maybe_diag_offset_bounds (loc, call, func, dst, dstref)
+      || maybe_diag_offset_bounds (loc, call, func, src, srcref))
+    {
+      gimple_set_no_warning (call, true);
+      return false;
+    }
+
+  if (check_overlap)
+    {
+      if (operand_equal_p (dst, src, 0))
+	{
+	  warning_at (loc, OPT_Wrestrict,
+		      "%G%qD source argument is the same as destination",
+		      call, func);
+	  gimple_set_no_warning (call, true);
+	  return false;
+	}
+
+      /* Return false when overlap has been detected.  */
+      if (maybe_diag_overlap (loc, call, acs))
+	{
+	  gimple_set_no_warning (call, true);
+	  return false;
+	}
+    }
+
+  return true;
+}
+
+gimple_opt_pass *
+make_pass_warn_restrict (gcc::context *ctxt)
+{
+  return new pass_wrestrict (ctxt);
+}
diff --git a/gcc/gimple-ssa-warn-restrict.h b/gcc/gimple-ssa-warn-restrict.h
new file mode 100644
index 0000000..02581aa
--- /dev/null
+++ b/gcc/gimple-ssa-warn-restrict.h
@@ -0,0 +1,26 @@
+/* Warn on violations of the restrict qualifier.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Contributed by Martin Sebor <msebor@redhat.com>.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GIMPLE_SSA_WARN_RESTRICT_H
+
+extern bool check_bounds_or_overlap (gcall *, tree, tree, tree, tree,
+				     bool = false);
+
+#endif /* GIMPLE_SSA_WARN_RESTRICT_H */
diff --git a/gcc/passes.def b/gcc/passes.def
index 00e75d2..a799ce3 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -330,6 +330,7 @@ along with GCC; see the file COPYING3.  If not see
 	 run the full propagators, run a specialized pass which
 	 only examines PHIs to discover const/copy propagation
 	 opportunities.  */
+      NEXT_PASS (pass_warn_restrict);
       NEXT_PASS (pass_phi_only_cprop);
       NEXT_PASS (pass_dse);
       NEXT_PASS (pass_cd_dce);
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-2.c b/gcc/testsuite/c-c++-common/Warray-bounds-2.c
new file mode 100644
index 0000000..becb3d4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-bounds-2.c
@@ -0,0 +1,204 @@
+/* Test to exercise that -Warray-bounds warnings for memory and sring
+   functions are issued even when they are declared in system headers
+   (i.e., not just when they are explicitly declared in the source
+   file.)
+   Also verify that the warnings are issued even for calls where the
+   source of the excessive array bound is in a different function than
+   the call.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds -Wno-stringop-overflow" } */
+
+#include <stddef.h>
+#include <string.h>
+
+#define MAX  (__SIZE_MAX__ / 2)
+
+void sink (void*);
+
+struct __attribute__ ((packed)) Array
+{
+  char a13[13];
+  char a15[15];
+  char a17[17];
+};
+
+/* Exercise memcpy out-of-bounds offsets with an array of known size.  */
+
+void wrap_memcpy_src_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d, s + i, n);   /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_src_xsize (char *d, size_t n)
+{
+  struct Array ar;
+  sink (&ar);
+  wrap_memcpy_src_xsize (d, ar.a13, 46, n);
+  sink (&ar);
+}
+
+/* Exercise memcpy out-of-bounds offsets with an array of unknown size.  */
+
+void wrap_memcpy_src_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d, s + i, n);   /* { dg-warning "pointer overflow between offset \[0-9\]+ and size 3" "memcpy" } */
+}
+
+void call_memcpy_src_diff_max (char *d, const char *s, size_t n)
+{
+  wrap_memcpy_src_diff_max (d, s, MAX, 3);
+}
+
+void wrap_memcpy_dst_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d + i, s, n);   /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar1. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_dst_xsize (const char *s, size_t n)
+{
+  struct Array ar1;       /* { dg-message ".ar1. declared here" } */
+  sink (&ar1);
+  wrap_memcpy_dst_xsize (ar1.a15, s, 34, n);
+  sink (&ar1);
+}
+
+void wrap_memcpy_dst_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d + i, s, n);   /* { dg-warning "offset -?\[0-9\]+ is out of the bounds \\\[0, 45] of object .ar2. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_dst_diff_max (const char *s, size_t n)
+{
+  struct Array ar2;       /* { dg-message ".ar2. declared here" } */
+  sink (&ar2);
+  wrap_memcpy_dst_diff_max (ar2.a15, s, MAX, n);
+  sink (&ar2);
+}
+
+
+void wrap_strcat_src_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcat (d, s + i);   /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar3. with type .(struct )?Array." "strcat" } */
+}
+
+void call_strcat_src_xsize (char *d)
+{
+  struct Array ar3;       /* { dg-message ".ar3. declared here" } */
+  sink (&ar3);
+  wrap_strcat_src_xsize (d, ar3.a15, 15 + 17 + 1);
+  sink (&ar3);
+}
+
+void wrap_strcat_dst_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcat (d + i, s);   /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar4. with type .(struct )?Array." "strcat" } */
+}
+
+void call_strcat_dst_xsize (const char *s)
+{
+  struct Array ar4;       /* { dg-message ".ar4. declared here" } */
+  sink (&ar4);
+  wrap_strcat_dst_xsize (ar4.a15, s, 15 + 17 + 2);
+  sink (&ar4);
+}
+
+
+void wrap_strcpy_src_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcpy (d, s + i);   /* { dg-warning "offset 48 is out of the bounds \\\[0, 45] of object .ar5. with type .(struct )?Array." "strcpy" } */
+}
+
+void call_strcpy_src_xsize (char *d)
+{
+  struct Array ar5;       /* { dg-message ".ar5. declared here" } */
+  sink (&ar5);
+  wrap_strcpy_src_xsize (d, ar5.a15, 15 + 17 + 3);
+  sink (&ar5);
+}
+
+void wrap_strcpy_dst_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcpy (d + i, s);   /* { dg-warning "offset 49 is out of the bounds \\\[0, 45] of object .ar6. with type .(struct )?Array." "strcpy" } */
+}
+
+void call_strcpy_dst_xsize (const char *s)
+{
+  struct Array ar6;       /* { dg-message ".ar6. declared here" } */
+  sink (&ar6);
+  wrap_strcpy_dst_xsize (ar6.a15, s, 15 + 17 + 4);
+  sink (&ar6);
+}
+
+
+/* Exercise strncpy out-of-bounds offsets with an array of known size.  */
+
+void wrap_strncpy_src_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d, s + i, n);   /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar7. with type '(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_src_xsize (char *d, size_t n)
+{
+  struct Array ar7;       /* { dg-message ".ar7. declared here" } */
+  sink (&ar7);
+  wrap_strncpy_src_xsize (d, ar7.a17, 17 + 1, n);
+  sink (&ar7);
+}
+
+/* Exercise strncpy out-of-bounds offsets with an array of unknown size.  */
+
+void wrap_strncpy_src_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  /* Unlike in the similar call to memcpy(), there is no pointer
+     overflow here because the size N is not added to the source
+     offset.  */
+  strncpy (d, s + i, n);
+}
+
+void call_strncpy_src_diff_max (char *d, const char *s, size_t n)
+{
+  wrap_strncpy_src_diff_max (d, s, MAX, 3);
+}
+
+void wrap_strncpy_dst_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d + i, s, n);   /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar8. with type .(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_dst_xsize (const char *s, size_t n)
+{
+  struct Array ar8;       /* { dg-message ".ar8. declared here" } */
+  sink (&ar8);
+  wrap_strncpy_dst_xsize (ar8.a17, s, 17 + 2, n);
+  sink (&ar8);
+}
+
+void wrap_strncpy_dst_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d + i, s, n);   /* { dg-warning "offset -\[0-9\]+ is out of the bounds \\\[0, 45] of object .ar9. with type .(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_dst_diff_max (const char *s, size_t n)
+{
+  struct Array ar9;       /* { dg-message ".ar9. declared here" } */
+  sink (&ar9);
+  wrap_strncpy_dst_diff_max (ar9.a17, s, MAX, n);
+  sink (&ar9);
+}
+
+void wrap_strncpy_dstarray_diff_neg (char *d, const char *s, ptrdiff_t i,
+				     size_t n)
+{
+  strncpy (d + i, s, n);   /* { dg-warning "offset -\[0-9\]+ is out of the bounds \\\[0, 90] of object .ar10. with type .(struct )?Array ?\\\[2]." "strncpy" } */
+}
+
+void call_strncpy_dstarray_diff_neg (const char *s, size_t n)
+{
+  struct Array ar10[2];    /* { dg-message ".ar10. declared here" } */
+  sink (&ar10);
+
+  int off = (char*)ar10[1].a17 - (char*)ar10 + 1;
+  wrap_strncpy_dstarray_diff_neg (ar10[1].a17, s, -off, n);
+
+  sink (&ar10);
+}
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-3.c b/gcc/testsuite/c-c++-common/Warray-bounds-3.c
new file mode 100644
index 0000000..baa4a23
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-bounds-3.c
@@ -0,0 +1,412 @@
+/* Exercise that -Warray-bounds is issued for out-of-bounds offsets
+   in calls to built-in functions.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" }  */
+
+#define SIZE_MAX __SIZE_MAX__
+#define DIFF_MAX __PTRDIFF_MAX__
+#define DIFF_MIN (-DIFF_MAX - 1)
+
+typedef __INT32_TYPE__   int32_t;
+typedef __PTRDIFF_TYPE__ ssize_t;
+typedef __SIZE_TYPE__    size_t;
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+extern ssize_t signed_value (void)
+{
+  extern volatile ssize_t signed_value_source;
+  return signed_value_source;
+}
+
+extern size_t unsigned_value (void)
+{
+  extern volatile size_t unsigned_value_source;
+  return unsigned_value_source;
+}
+
+ssize_t signed_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return val < min || max < val ? min : val;
+}
+
+ssize_t signed_anti_range (ssize_t min, ssize_t max)
+{
+  ssize_t val = signed_value ();
+  return min <= val && val <= max ? min == DIFF_MIN ? max + 1 : min - 1 : val;
+}
+
+size_t unsigned_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+size_t unsigned_anti_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return min <= val && val <= max ? min == 0 ? max + 1 : min - 1 : val;
+}
+
+#define SR(min, max) signed_range ((min), (max))
+#define UR(min, max) unsigned_range ((min), (max))
+
+#define SAR(min, max) signed_anti_range ((min), (max))
+#define UAR(min, max) unsigned_anti_range ((min), (max))
+
+void sink (void*, ...);
+
+#define CAT(x, y)      x ## y
+#define CONCAT(x, y)   CAT (x, y)
+#define UNIQUE_NAME(x) CONCAT(x, __LINE__)
+
+#define T(type, N, dst, src, n) do {		\
+    extern type UNIQUE_NAME (a)[N];		\
+    type *a = UNIQUE_NAME (a);			\
+    type *pd = (dst);				\
+    const type *ps = (src);			\
+    FUNC (pd, ps, n);				\
+    sink (a, pd, ps);				\
+  } while (0)
+
+
+void test_memcpy_bounds (char *d, const char *s, size_t n)
+{
+#define FUNC memcpy
+
+  /* Verify that invalid offsets into an array of known size are
+     detected.  */
+
+  T (char, 1, a + SR (DIFF_MIN, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds \\\[0, 1] of object \[^\n\r]* with type .char ?\\\[1]" } */
+  T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds \\\[0, 1] of object" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  T (char, 1, a + UR (0, 1), s, n);
+  T (char, 1, a + UR (0, 2), s, n);
+  T (char, 1, a + UR (1, 2), s, n);
+  T (char, 1, a + UR (2, 3), s, n);       /* { dg-warning "offset \\\[2, 3] is out of the bounds \\\[0, 1] of object " } */
+  T (char, 1, a + UR (2, DIFF_MAX), s, n);  /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds \\\[0, 1] of object " "memcpy" } */
+
+  /* Offsets in excess of DIFF_MAX are treated as negative even if
+     they appear as large positive in the source.  It would be nice
+     if they retained their type but unfortunately that's not how
+     it works so be prepared for both in case it even gets fixed.  */
+  T (char, 1, a + UR (3, SIZE_MAX - 1), s, n);   /* { dg-warning "offset \\\[3, -2] is out of the bounds \\\[0, 1] of object" "memcpy" } */
+
+  /* Verify that invalid offsets into an array of unknown size are
+     detected.  */
+  extern char arr[];
+  T (char, 1, arr + SR (DIFF_MIN,             0), s, n);
+  T (char, 1, arr + SR (DIFF_MIN + 1,        -1), s, n);   /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of object " "memcpy" } */
+  T (char, 1, arr + SR (DIFF_MIN,             1), s, n);
+  T (char, 1, arr + SR (DIFF_MIN,      DIFF_MAX), s, n);
+  T (char, 1, arr + SR (       -2,           -1), s, n);   /* { dg-warning "offset \\\[-2, -1] is out of the bounds of object " "memcpy" } */
+  T (char, 1, arr + SR (       -1,            0), s, n);
+  T (char, 1, arr + SR (       -1,            1), s, n);
+  T (char, 1, arr + SR (       -1, DIFF_MAX - 1), s, n);
+  T (char, 1, arr + SR (        0,            1), s, n);
+  T (char, 1, arr + SR (        0, DIFF_MAX - 1), s, n);
+  T (char, 1, arr + SR (        1,            2), s, n);
+  T (char, 1, arr + SR (        1, DIFF_MAX - 1), s, n);
+
+  /* Verify that all offsets via a pointer to an uknown object are
+     accepted.  */
+
+  /* Negative indices between [DIFF_MIN, DIFF_MAX] are valid since
+     the pointer to which the offset is applied can be at a positive
+     offset from the beginning of an object.  */
+  T (char, 1, d + SR (DIFF_MIN,             0), s, n);
+  T (char, 1, d + SR (DIFF_MIN,            -1), s, n);
+  T (char, 1, d + SR (DIFF_MIN,             1), s, n);
+  T (char, 1, d + SR (DIFF_MIN,  DIFF_MAX - 1), s, n);
+  T (char, 1, d + SR (       -2,           -1), s, n);
+  T (char, 1, d + SR (       -1,            0), s, n);
+  T (char, 1, d + SR (       -1,            1), s, n);
+  T (char, 1, d + SR (       -1, DIFF_MAX - 1), s, n);
+  T (char, 1, d + SR (        0,            1), s, n);
+  T (char, 1, d + SR (        0, DIFF_MAX - 1), s, n);
+  T (char, 1, d + SR (        1,            2), s, n);
+  T (char, 1, d + SR (        1, DIFF_MAX - 1), s, n);
+}
+
+/* Verify offsets in an anti-range.  */
+
+void test_memcpy_bounds_anti_range (char *d, const char *s, size_t n)
+{
+  T (char, 9, a, a + SAR (-2, -1), 3);
+  T (char, 9, a, a + SAR (-1,  1), 3);
+  T (char, 9, a, a + SAR ( 0,  1), 3);
+  T (char, 9, a, a + SAR ( 0,  2), 3);
+  T (char, 9, a, a + SAR ( 0,  3), 3);
+  T (char, 9, a, a + SAR ( 0,  4), 3);
+  T (char, 9, a, a + SAR ( 0,  5), 3);
+  /* The initial source range is valid but the final range after the access
+     has complete cannot be.  The value mentioned in the warning is the final
+     offset, i.e., 7 + 3.  Including the whole final range because would be
+     confusing (the upper bound would either be negative or a very large
+     positive number) so only the lower bound is included.  */
+  T (char, 9, a, a + SAR ( 0,  6), 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  /* This fails because the offset isn't represented as an SSA_NAME
+     but rather as a GIMPLE_PHI (offset, 0).  With some effort it is
+     possible to extract the range from the PHI but it's not implemented
+     (yet).  */
+  T (char, 9, a, a + SAR ( 1,  6), 3);   /* { dg-warning "forming offset \\\[9, 0] is out of the bounds \\\[0, 9] of object " "memcpy" { xfail *-*-* } } */
+
+  T (char, 9, a, a + SAR ( 2,  6), 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR ( 3,  6), 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  T (char, 9, a, a + SAR (-1,  7), 3);   /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR (-2,  8), 3);   /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR (-3,  7), 5);   /* { dg-warning "forming offset \\\[10, 13] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  T (char, 9, a + SAR (-2, -1), a, 3);
+  T (char, 9, a + SAR (-1,  1), a, 3);
+  T (char, 9, a + SAR ( 0,  1), a, 3);
+  T (char, 9, a + SAR ( 0,  2), a, 3);
+  T (char, 9, a + SAR ( 0,  3), a, 3);
+  T (char, 9, a + SAR ( 0,  6), a, 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a + SAR (-1,  7), a, 3);   /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a + SAR (-2,  8), a, 3);   /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  ssize_t i = SAR (DIFF_MIN + 1, DIFF_MAX - 4);
+  T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 1), 3);
+  T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 3), 5);
+}
+
+void foo (char *d)
+{
+  T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 3), 5);
+}
+
+/* Verify that pointer overflow in the computation done by memcpy
+   (i.e., offset + size) is detected and diagnosed.  */
+
+void test_memcpy_overflow (char *d, const char *s, size_t n)
+{
+  extern char arr[];
+
+  /* Verify that offset overflow involving an array of unknown size
+     but known access size is detected.  This works except with small
+     sizes that are powers of 2 due to bug .  */
+  T (char, 1, arr + SR (DIFF_MAX - 1, DIFF_MAX), s, 1);
+  T (char, 1, arr + SR (DIFF_MAX - 1, DIFF_MAX), s, 2);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 2 accessing array " "bug " { xfail *-*-* } } */
+  T (char, 1, arr + SR (DIFF_MAX - 2, DIFF_MAX), s, 3);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 3 accessing array " "memcpy" } */
+  T (char, 1, arr + SR (DIFF_MAX - 4, DIFF_MAX), s, 5);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 5 accessing array " "memcpy" } */
+}
+
+void test_memmove_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC memmove
+
+    T (char, 1, a + SR (DIFF_MIN + 1, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds \\\[0, 1] of object \[^\n\r]+ with type .char ?\\\[1]" } */
+  T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds \\\[0, 1] of object" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  const int *pi = (const int*)s;
+  T (int,  2, a + SR (-1, 1), pi, n);
+  T (int,  2, a + SR (-1, 2), pi, n);
+  T (int,  2, a + SR ( 0, 2), pi, n);
+  T (int,  2, a + SR ( 0, 3), pi, n);
+  T (int,  2, a + SR ( 1, 3), pi, n);
+  T (int,  2, a + SR ( 2, 3), pi, n);
+
+  T (int32_t, 2, a + SR ( 3, 4), pi, n);      /* { dg-warning "offset \\\[12, 16] is out of the bounds \\\[0, 8] of object .\[^\n\r]+. with type .int32_t ?\\\[2]." } */
+}
+
+
+void test_mempcpy_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC mempcpy
+
+  /* Verify that invalid offsets into an array of known size are
+     detected.  */
+
+  T (char, 1, a + SR (DIFF_MIN, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds"  "mempcpy" } */
+T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds"  "mempcpy" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  T (char, 1, a + UR (0, 1), s, n);
+  T (char, 1, a + UR (0, 2), s, n);
+  T (char, 1, a + UR (1, 2), s, n);
+  T (char, 1, a + UR (2, 3), s, n);       /* { dg-warning "offset \\\[2, 3] is out of the bounds \\\[0, 1] of object "  "mempcpy" } */
+  T (char, 1, a + UR (2, DIFF_MAX), s, n);  /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds \\\[0, 1] of object"  "mempcpy" } */
+
+  /* Offsets in excess of DIFF_MAX are treated as negative even if
+     they appear as large positive in the source.  It would be nice
+     if they retained their type but unfortunately that's not how
+     it works so be prepared for both in case it ever gets fixed.  */
+  T (char, 1, a + UR (3, SIZE_MAX), s, n);   /* { dg-warning "offset \\\[3, -1] is out of the bounds \\\[0, 1] of object "  "mempcpy" } */
+
+  /* Verify that invalid offsets into an array of unknown size are
+     detected.  */
+  extern char arr[];
+  T (char, 1, arr + SR (DIFF_MIN,         0), s, n);
+  T (char, 1, arr + SR (DIFF_MIN,        -1), s, n);   /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of object"  "mempcpy" } */
+  T (char, 1, arr + SR (DIFF_MIN,         1), s, n);
+  T (char, 1, arr + SR (DIFF_MIN, DIFF_MAX), s, n);
+  T (char, 1, arr + SR (       -2,        -1), s, n);   /* { dg-warning "offset \\\[-2, -1] is out of the bounds of object"  "mempcpy" } */
+  T (char, 1, arr + SR (       -1,         0), s, n);
+  T (char, 1, arr + SR (       -1,         1), s, n);
+  T (char, 1, arr + SR (       -1, DIFF_MAX), s, n);
+  T (char, 1, arr + SR (        0,         1), s, n);
+  T (char, 1, arr + SR (        0, DIFF_MAX), s, n);
+  T (char, 1, arr + SR (        1,         2), s, n);
+  T (char, 1, arr + SR (        1, DIFF_MAX), s, n);
+
+  /* Verify that all offsets via a pointer to an uknown object are
+     accepted.  */
+
+  /* Negative indices between [DIFF_MIN, DIFF_MAX] are valid since
+     the pointer to which the offset is applied can be at a positive
+     offset from the beginning of an object.  */
+  T (char, 1, d + SR (DIFF_MIN,         0), s, n);
+  T (char, 1, d + SR (DIFF_MIN,        -1), s, n);
+  T (char, 1, d + SR (DIFF_MIN,         1), s, n);
+  T (char, 1, d + SR (DIFF_MIN, DIFF_MAX), s, n);
+  T (char, 1, d + SR (       -2,        -1), s, n);
+  T (char, 1, d + SR (       -1,         0), s, n);
+  T (char, 1, d + SR (       -1,         1), s, n);
+  T (char, 1, d + SR (       -1, DIFF_MAX), s, n);
+  T (char, 1, d + SR (        0,         1), s, n);
+  T (char, 1, d + SR (        0, DIFF_MAX), s, n);
+  T (char, 1, d + SR (        1,         2), s, n);
+  T (char, 1, d + SR (        1, DIFF_MAX), s, n);
+}
+
+#define TI(type, N, init, dst, src) do {	\
+    type UNIQUE_NAME (a)[N] = init;		\
+    type *a = UNIQUE_NAME (a);			\
+    type *pd = (dst);				\
+    const type *ps = (src);			\
+    FUNC (pd, ps);				\
+    sink (a, pd, ps, s);			\
+  } while (0)
+
+void test_strcpy_bounds (char *d, const char *s)
+{
+#undef FUNC
+#define FUNC strcpy
+
+  ssize_t i;
+
+  TI (char, 1, "",   a, a + SR (DIFF_MIN, 0));
+  TI (char, 1, "",   a, a + SR (-1, 0));
+  TI (char, 1, "",   a, a + SR (-1, 1));
+  TI (char, 1, "",   a, a + SR (0, 1));
+  TI (char, 1, "",   a, a + SR (0, DIFF_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (0, DIFF_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (1, DIFF_MAX - 1));
+  /* The following needs a warning for reading past the end.  */
+  TI (char, 2, "0",  a, a + SR (2, DIFF_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (3, DIFF_MAX - 1));   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
+
+  TI (char, 3, "01", a, a + SR (0, DIFF_MAX - 1));
+  TI (char, 3, "01", a, a + SR (1, DIFF_MAX - 1));
+  TI (char, 3, "01", a, a + SR (2, DIFF_MAX - 1));
+  /* The following needs a warning for reading past the end.  */
+  TI (char, 3, "01", a, a + SR (3, DIFF_MAX - 1));
+  TI (char, 3, "01", a, a + SR (4, DIFF_MAX - 1));   /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
+
+  TI (char, 4, "012", a, a + SR (DIFF_MAX - 2, DIFF_MAX - 1));   /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds \\\[0, 4] of object \[^\n\r\]+ with type .char ?\\\[4\\\]."  "strcpy" } */
+
+
+  TI (char, 1, "", a + SR (DIFF_MIN, 0), s);
+  TI (char, 1, "", a + SR (-1, 0), s);
+  TI (char, 1, "", a + SR (-1, 1), s);
+  TI (char, 1, "", a + SR (0, 1), s);
+  TI (char, 1, "", a + SR (0, DIFF_MAX - 1), s);
+  TI (char, 2, "", a + SR (0, DIFF_MAX - 1), s);
+  TI (char, 2, "", a + SR (1, DIFF_MAX - 1), s);
+  /* The following is diagnosed not because the initial source offset
+     it out of bounds (it isn't) but because the final source offset
+     after the access has completed, is.  It would be clearer if
+     the warning mentioned the final offset.  */
+  TI (char, 2, "", a + SR (2, DIFF_MAX - 1), s);   /* { dg-warning "forming offset 3 is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
+  TI (char, 2, "", a + SR (3, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
+
+  TI (char, 3, "", a + SR (0, DIFF_MAX - 1), s);
+  TI (char, 3, "", a + SR (1, DIFF_MAX - 1), s);
+  TI (char, 3, "", a + SR (2, DIFF_MAX - 1), s);
+  TI (char, 3, "", a + SR (3, DIFF_MAX - 1), s);   /* { dg-warning "forming offset 4 is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
+  TI (char, 3, "", a + SR (4, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
+
+  TI (char, 4, "", a + SR (DIFF_MAX - 2, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds \\\[0, 4] of object \[^\n\r\]+ with type .char ?\\\[4\\\]."  "strcpy" } */
+}
+
+struct MA
+{
+  int i;
+  char a1_5[5];
+  char a2_11[11];
+};
+
+struct MA2
+{
+  struct MA ma1_3[3];
+  struct MA ma2_5[5];
+};
+
+struct MA3
+{
+  struct MA2 ma1_5[3];
+  struct MA2 ma1_7[7];
+};
+
+void test_strcpy_bounds_member_array (struct MA *pma,
+				      struct MA2 *pma2,
+				      struct MA3 *pma3,
+				      const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC(d, s, ignore) strcpy (d, s)
+
+  T (char, 1, pma->a1_5, s, 0);
+  T (char, 1, pma->a1_5 + 0, s, 0);
+  T (char, 1, pma->a1_5 + 1, s, 0);
+  T (char, 1, pma->a1_5 + 4, s, 0);
+
+  /* The following forms a pointer during the call that's outside
+     the bounds of the array it was derived from (pma->a1_5) so
+     it should be diagnosed but the representation of the pointer
+     addition doesn't contain information to distinguish it from
+     the valid pma->a2_11 + 1 so this is an XFAIL.  */
+  T (char, 1, pma->a1_5 + 5, s, 0);                 /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+  /* The following also forms an out-of-bounds pointer but similar
+     to the above, there is no reliable way to distinguish it from
+     (char*)&pma[1].i + 1 so this too is not diagnosed.  */
+  T (char, 1, pma->a1_5 + sizeof *pma + 1, s, 0);   /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+  T (char, 1, pma->a1_5 - 1, s, 0);   /* { dg-warning "offset -1 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+
+  T (char, 1, pma[1].a1_5, s, 0);
+  T (char, 1, pma[2].a1_5 + 0, s, 0);
+  T (char, 1, pma[3].a1_5 + 1, s, 0);
+  T (char, 1, pma[4].a1_5 + 4, s, 0);
+
+
+  extern struct MA3 ma3[3];
+  T (char, 1, ma3[0].ma1_5[0].ma1_3[0].a1_5 + 6, s, 0);
+}
diff --git a/gcc/testsuite/c-c++-common/Wrestrict-2.c b/gcc/testsuite/c-c++-common/Wrestrict-2.c
new file mode 100644
index 0000000..f440e7b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict-2.c
@@ -0,0 +1,70 @@
+/* PR 35503 - Warn about restricted pointers
+   Test to exercise that -Wrestrict warnings are issued for memory and
+   sring functions when they are declared in system headers (i.e., not
+   just when they are explicitly declared in the source file.)
+   Also verify that the warnings are issued even for calls where the
+   source of the aliasing violation is in a different function than
+   the restricted call.
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict" } */
+
+#include <string.h>
+
+void wrap_memcpy (void *d, const void *s, size_t n)
+{
+  memcpy (d, s, n);   /* { dg-warning "source argument is the same as destination" "memcpy" } */
+}
+
+void call_memcpy (void *d, size_t n)
+{
+  const void *s = d;
+  wrap_memcpy (d, s, n);
+}
+
+
+void wrap_strcat (char *d, const char *s)
+{
+  strcat (d, s);   /* { dg-warning "source argument is the same as destination" "strcat" } */
+}
+
+void call_strcat (char *d)
+{
+  const char *s = d;
+  wrap_strcat (d, s);
+}
+
+
+void wrap_strcpy (char *d, const char *s)
+{
+  strcpy (d, s);   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+}
+
+void call_strcpy (char *d)
+{
+  const char *s = d;
+  wrap_strcpy (d, s);
+}
+
+
+void wrap_strncat (char *d, const char *s, size_t n)
+{
+  strncat (d, s, n);   /* { dg-warning "source argument is the same as destination" "strncat" } */
+}
+
+void call_strncat (char *d, size_t n)
+{
+  const char *s = d;
+  wrap_strncat (d, s, n);
+}
+
+
+void wrap_strncpy (char *d, const char *s, size_t n)
+{
+  strncpy (d, s, n);   /* { dg-warning "source argument is the same as destination" "strncpy" } */
+}
+
+void call_strncpy (char *d, size_t n)
+{
+  const char *s = d;
+  wrap_strncpy (d, s, n);
+}
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
new file mode 100644
index 0000000..d26a628
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -0,0 +1,982 @@
+/* PR 35503 - Warn about restricted pointers
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#define SIZE_MAX __SIZE_MAX__
+#define DIFF_MAX (__SIZE_MAX__ / 2)
+#define DIFF_MIN (-DIFF_MAX - 1)
+
+#if !defined LINE
+# define LINE 0
+#endif
+
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+extern ptrdiff_t signed_value (void)
+{
+  extern volatile ptrdiff_t signed_value_source;
+  return signed_value_source;
+}
+
+extern size_t unsigned_value (void)
+{
+  extern volatile size_t unsigned_value_source;
+  return unsigned_value_source;
+}
+
+ptrdiff_t signed_range (ptrdiff_t min, ptrdiff_t max)
+{
+  ptrdiff_t val = signed_value ();
+  return val < min || max < val ? min : val;
+}
+
+ptrdiff_t signed_anti_range (ptrdiff_t min, ptrdiff_t max)
+{
+  ptrdiff_t val = signed_value ();
+  return min <= val && val <= max ? min == 0 ? max + 1 : min - 1 : val;
+}
+
+size_t unsigned_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+size_t unsigned_anti_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return min <= val && val <= max ? min == DIFF_MIN ? max + 1 : min - 1 : val;
+}
+
+#define SR(min, max) signed_range ((min), (max))
+#define UR(min, max) unsigned_range ((min), (max))
+
+#define SAR(min, max) signed_anti_range ((min), (max))
+#define UAR(min, max) unsigned_anti_range ((min), (max))
+
+
+void sink (void*, ...);
+
+/* Exercise memcpy with constant or known arguments.  */
+
+void test_memcpy_cst (void *d, const void *s)
+{
+#undef T
+#define T(dst, src, n) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+      void *pd = (dst);					\
+      const void *ps = (src);				\
+      memcpy (pd, ps, (n));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T (a, a, 0);
+  T (a, s = a, 3);           /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  /* This isn't detected because memcpy calls with small power-of-2 sizes
+     are intentionally folded into safe copies equivalent to memmove.
+     It's marked xfail only because there is value in detecting such
+     invalid calls for portability, and as a reminder of why it isn't
+     diagnosed.  */
+  T (a, a + 1, 1);           /* { dg-warning "\\\[-Wrestrict" "memcpy with a small power of 2 size" { xfail *-*-* } } */
+  T (a, a + 3, 3);
+  T (a, a + 3, 5);           /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  {
+    char a[3] = { 1, 2, 3 };
+
+    /* Verify that a call to memcpy with an exact overlap is diagnosed
+       (also tested above) but an excplicit one to __builtin_memcpy is
+       not.  See bug 32667 for the rationale.  */
+    (memcpy)(a, a, sizeof a);   /* { dg-warning "source argument is the same as destination" "memcpy" } */
+    sink (a);
+
+    __builtin_memcpy (a, a, sizeof a);
+    sink (a);
+  }
+
+  {
+    char a[3][7];
+    sink (a);
+
+    void *d = a[0];
+    const void *s = a[1];
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0];
+    s = a[1];
+    /* The following is only diagnosed for sizes that aren't small
+       powers of 2.  */
+    memcpy (d, s, sizeof a[0] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&a);
+  }
+
+  {
+    struct {
+      char a[7];
+      char b[7];
+      char c[7];
+    } x;
+    sink (&x);
+
+    void *d = x.a;
+    const void *s = x.b;
+    memcpy (d, s, sizeof x.a);
+    sink (&x);
+
+    d = x.a;
+    s = x.a;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 4;
+    s = x.b;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 6;
+    s = x.b;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b;
+    memcpy (d, s, 3);             /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b + 1;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 1;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 2;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 3;
+    memcpy (d, s, sizeof x.b);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+  }
+
+  {
+#undef T
+#define T(dst, src, n) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };	\
+      memcpy ((dst), (src), (n));			\
+      sink (a);						\
+    }							\
+    } while (0)
+
+    /* Verify the offset of the overlap is the same regardless of whether
+       the destination is at lower or higher offset than the source.  */
+    T (a, a + 1, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 1 overlaps 4 bytes at offset 1" "memcpy" } */
+    T (a, a + 2, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "memcpy" } */
+    T (a, a + 3, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 3 overlaps 2 bytes at offset 3" "memcpy" } */
+
+    T (a + 1, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 1 and 0 overlaps 4 bytes at offset 1" "memcpy" } */
+    T (a + 2, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 2 and 0 overlaps 3 bytes at offset 2" "memcpy" } */
+    T (a + 3, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 3 and 0 overlaps 2 bytes at offset 3" "memcpy" } */
+  }
+}
+
+/* Exercise memcpy with destination or source offset or size in
+   a determinate range.  */
+
+void test_memcpy_range (char *d, size_t sz)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    if (!LINE || LINE == __LINE__) {		\
+      char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };	\
+      void *pd = (dst);					\
+      const void *ps = (src);				\
+      memcpy (pd, ps, (n));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  ptrdiff_t ir = SR (2, 3);
+  T (a + ir, a, 0);
+  T (a + ir, a, 1);
+  T (a + ir, a, 2);
+  T (a + ir, a, 3);
+  /* The following fails because the size is a small power of 2.  */
+  T (a + ir, a, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps between 1 and 2 bytes at offset \\\[3, 2]" "memcpy" { xfail *-*-*} } */
+  T (a + ir, a, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  T (d + ir, d, 0);
+  T (d + ir, d, 1);
+  T (d + ir, d, 2);
+  T (d + ir, d, 3);
+  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 1 byte at offset 3" "bug 79220" { xfail *-*-* } } */
+  T (d + ir, d, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  /* Because the size is constant and a power of 2 the following is
+     folded too early to detect the overlap.  */
+  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+  T (d + ir, d, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  /* Exercise the full range of size_t.  */
+  T (d + sz, d, 0);
+  T (d + sz, d, 1);
+  T (d + sz, d, 9);
+
+  T (a, a + 1, SR (0, 1));
+  T (a, a + 1, SR (0, 2));
+  T (a, a + 1, SR (1, 2));
+  T (a, a + 1, SR (2, 3));         /* { dg-warning "accessing between 2 and 3 bytes at offsets 0 and 1 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+  T (a, a + 1, UR (2, DIFF_MAX + (size_t)1));  /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a, a + 1, UR (2, SIZE_MAX - 1));        /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a, a + 2, SR (2, 3));
+  T (a, a + 2, SR (3, 4));         /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and 2 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+  T (a, a + 3, SR (3, 4));
+  T (a, a + 3, SR (4, 5));         /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and 3 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+  T (a, a + 3, SR (5, 6));         /* { dg-warning "accessing between 5 and 6 bytes at offsets 0 and 3 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+  T (a + 1, a, SR (0, 1));
+  T (a + 1, a, SR (0, 2));
+  T (a + 1, a, SR (1, 2));
+  T (a + 1, a, SR (2, 3));         /* { dg-warning "accessing between 2 and 3 bytes at offsets 1 and 0 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+  T (a + 1, a, UR (2, DIFF_MAX + (size_t)1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a + 1, a, UR (2, SIZE_MAX - 1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a + 2, a, SR (2, 3));
+  T (a + 2, a, SR (3, 4));         /* { dg-warning "accessing between 3 and 4 bytes at offsets 2 and 0 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+  T (a + 3, a, SR (3, 4));
+  T (a + 3, a, SR (4, 5));         /* { dg-warning "accessing between 4 and 5 bytes at offsets 3 and 0 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+  T (a + 3, a, SR (5, 6));         /* { dg-warning "accessing between 5 and 6 bytes at offsets 3 and 0 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+  ir = SR (2, 5);
+  T (a, a + ir, 4);
+  T (a, a + ir, 5);                /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] overlaps between 1 and 3 bytes at offset \\\[4, 2]" "memcpy" } */
+  T (a, a + ir, 6);                /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] overlaps between 3 and 4 bytes at offset \\\[3, 2]" "memcpy" } */
+
+  /* Below, there are two possible regions for the source of the copy:
+       1) one just before the high end of the address space that's 3
+          bytes large close to the lower end of the offset range, and
+       2) another in the [DIFF_MIN, -8] range from D and so at least
+          8 bytes in size
+     A copy from (1) overlaps but one from (2) does not.  Verify that
+     the copy is not diagnosed.  (This test case was reduced from
+     the Linux kernel.) */
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 5);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 6);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 7);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 9);
+
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 5);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 6);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 7);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 9);
+
+  {
+    /* Create an offset in the range [0, -1].  */
+    size_t o = sz << 1;
+    T (d, d + o, 12345);
+    T (d + o, d, 23456);
+  }
+
+  /* Exercise memcpy with both destination and source pointer offsets
+     in some known range.  */
+  size_t n = UR (3, 4);
+  T (a + SR (1, 3), a + SR (1, 3), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3] and \\\[1, 3] overlaps between 1 and 4 bytes at offset \\\[3, 1]" "memcpy" } */
+  T (a + SR (1, 3), a + SR (2, 3), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3] and \\\[2, 3] overlaps between 1 and 3 bytes at offset \\\[3, 2]" "memcpy" } */
+  T (a + SR (1, 3), a + SR (3, 4), n);
+
+  T (a + SR (2, 3), a + SR (3, 4), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[2, 3] and \\\[3, 4] overlaps between 1 and 3 bytes at offset \\\[4, 3]" "memcpy" } */
+
+  T (a + SR (1, 3), a + SR (4, 5), n);
+  T (a + SR (2, 3), a + SR (4, 5), n);
+  T (a + SR (3, 4), a + SR (4, 5), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[3, 4] and \\\[4, 5] overlaps between 1 and 3 bytes at offset \\\[5, 4]" "memcpy" } */
+
+  /* Exercise the full range of size_t.  */
+  T (d, d + sz, 0);
+  T (d, d + sz, 1);
+  T (d, d + sz, 9);
+}
+
+/* Exercise memcpy with offset and/or size in a determinate anti-range.  */
+
+void test_memcpy_anti_range (char *d, const char *s)
+{
+  T (d, d + SAR (0, 3), 1);
+  T (d, d + SAR (0, 3), 2);
+  T (d, d + SAR (0, 3), 3);
+  T (d, d + SAR (0, 3), DIFF_MAX - 2);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 2" } */
+  T (d, d + SAR (0, 3), DIFF_MAX - 1);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 1" } */
+  T (d, d + SAR (0, 3), DIFF_MAX);       /* { dg-warning "overlaps \[0-9\]+ bytes at offset 0" } */
+
+  T (d, d + SAR (0, 3), UR (DIFF_MAX - 2, DIFF_MAX));               /* { dg-warning "accessing \[0-9\]+ or more bytes at offsets 0 and \\\[-?\[0-9\]+, -?\[0-9\]+] overlaps \[0-9\]+ bytes at offset 2" } */
+
+  /* Verify that a size in an anti-range ~[0, N] where N >= PTRDIFF_MAX
+     doesn't trigger a warning.  */
+  T (d, s, UAR (1, DIFF_MAX - 1));
+  T (d, s, UAR (1, DIFF_MAX));
+  T (d, s, UAR (1, SIZE_MAX - 1));
+
+  /* This causes the last dg-warning test to fail for some reason.
+     T (d, s, UAR (1, SIZE_MAX)); */
+}
+
+/* Verify calls to memcpy() where the combination of offsets in some
+   range and size is such that either overlap is unavoidable or one
+   or both offsets would exceed the maximum size of an object
+   (DIFF_MAX).  */
+
+void test_memcpy_range_exceed (char *d, const char *s)
+{
+  /* Verify offset and size both in some range.  The memcpy checking
+     is less strict than that of string functions like strncpy and
+     doesn't trigger unless the overlap is certain.  The following
+     overlaps for (r == 3 && n > 3) but not, for example, for
+     (r == 4 && n == 4), and so it's not diagnosed.  */
+  ptrdiff_t i = SR (3, 5);
+  size_t n = UR (4, 6);
+
+  T (a, a + i, n);
+  T (a + i, a, n);
+  /* Ditto for objects of unknown sizes.  */
+  T (d, d + i, n);
+  T (d + i, d, n);
+
+  /* Verify that a warning is issued for a copy between two regions
+     whose aggregate size would exceed DIFF_MAX if it were to not
+     overlap.  */
+  T (d, s, DIFF_MAX / 2);
+  T (d, s, DIFF_MAX / 2 + 1);   /* { dg-warning "overlaps 1 byte" "memcpy" } */
+  T (d, s, DIFF_MAX / 2 + 2);   /* { dg-warning "overlaps 3 bytes" "memcpy" } */
+  T (d, s, DIFF_MAX / 2 + 3);   /* { dg-warning "overlaps 5 bytes" "memcpy" } */
+
+  i = SR (DIFF_MAX - 2, DIFF_MAX);
+
+  /* Verify a warning for an out-of-bounds offset range and constant
+     size addition.  */
+  T (d, d + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, d, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+  T (d + 1, d + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, d + 1, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+  /* Verify that the warnings above are independent of whether the source
+     and destination are known to be based on the same object.  */
+  T (d, s + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, s, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+  T (d + 1, s + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, s + 1, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+#if __SIZEOF_SIZE_T__ == 8
+  /* Verfiy the offset and size computation is correct.  The overlap
+     offset mentioned in the warning plus sthe size of the access must
+     not exceed DIFF_MAX.  */
+  T (d, d + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+
+  T (d, s + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+  T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+#elif __SIZEOF_SIZE_T__ == 4
+  T (d, d + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+
+  T (d, s + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+  T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+#endif
+
+  ptrdiff_t j = SR (DIFF_MAX - 9, DIFF_MAX - 1);
+  i = SR (DIFF_MAX - 5, DIFF_MAX - 1);
+  n = UR (4, 5);
+  T (d + i, d + j, n);
+
+  n = UR (4, DIFF_MAX - 1);
+  T (d + i, d + j, n);
+
+  n = UR (4, SIZE_MAX - 1);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 8, DIFF_MAX - 1);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 7, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+  j = SR (DIFF_MAX - 6, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+  n = UR (3, DIFF_MAX);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 6, DIFF_MAX - 1);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 5, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+  j = SR (DIFF_MAX - 4, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+  j = SR (DIFF_MAX - 2, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+}
+
+/* Exercise memcpy with destination and source of unknown size.  */
+
+void test_memcpy_var (char *d, const char *s)
+{
+  size_t n = unsigned_value ();
+
+  memcpy (d, d, 0);
+  sink (d);
+
+  memcpy (d, d, n);               /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  memcpy (d, &d[0], n);           /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  memcpy (&d[0], d,  n);          /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  /* The following overlaps if n is greater than 1.  */
+  s = d + 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + n;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + signed_value ();
+  memcpy (d, s, unsigned_value ());
+  sink (d);
+
+  s = d + 3;
+  n = 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 2;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 3;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 4;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+  sink (d);
+
+  s = d + 5;
+  n = 7;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  n = UR (0, 1);
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+}
+
+/* Exercise the absence of warnings with memmove.  */
+
+void test_memmove (void)
+{
+  {
+    char d[7];
+    sink (d);
+
+    const void *s = d;
+    memmove (d, s, 7);
+    sink (d);
+
+    s = d + 1;
+    memmove (d, s, 6);
+    sink (d);
+
+    s = d + 2;
+    memmove (d + 1, s, 5);
+    sink (d);
+  }
+}
+
+/* Exercise strcat with constant or known arguments.  */
+
+void test_strcat_cst (const char *s)
+{
+#undef T
+#define T(init, dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcat (pd, ps);					\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("0",   a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T ("01",  a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  /* The 3 bytes "12\0" being appended to "012" overwrite the final NUL.  */
+  T ("012", a, a + 1);            /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a, a + 2);            /* { dg-warning "accessing 2 bytes at offsets 0 and 2 overlaps 1 byte at offset 3" "strcat" } */
+  /* The nul copied from a[3] to a[3] overwrites itself so this is
+     diagnosed.  */
+  T ("012", a, a + 3);            /* { dg-warning "accessing 1 byte at offsets 0 and 3 overlaps 1 byte at offset 3" "strcat" } */
+
+  T ("012", a, a + 4);
+  T ("012", a, a + 5);
+  T ("012", a, a + 6);
+  T ("012", a, a + 7);
+  T ("012", a, a + 8);
+
+  T ("0",   a + 1, a);            /* { dg-warning "accessing 2 bytes at offsets 1 and 0 overlaps 1 byte at offset 1" "strcat" } */
+  T ("0",   a + 2, a);
+
+  /* The first of the two offsets in the diagnostic for strcat is that
+     of the first write into the destination, not that of the initial
+     read from it to compute its length.  */
+  T ("01",  a + 1, a);            /* { dg-warning "accessing 3 bytes at offsets 1 and 0 overlaps 1 byte at offset 2" "strcat" } */
+  T ("01",  a + 2, a);            /* { dg-warning "accessing 3 bytes at offsets 2 and 0 overlaps 1 byte at offset 2" "strcat" } */
+  T ("01",  a + 3, a);
+
+  T ("012", a + 1, a);            /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3 " "strcat" } */
+  T ("012", a + 4, a);
+  T ("012", a + 5, a);
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T ("012",      a, "012");
+  T ("012",      a, s);
+  T ("01234567", a, s);
+}
+
+/* Exercise strcat with destination and source of unknown length.  */
+
+void test_strcat_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcat (pd, ps);					\
+      sink (pd, ps);					\
+    }							\
+  } while (0)
+
+  T (d, d);                       /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T (d, d + 1);                   /* { dg-warning "accessing 0 or more bytes at offsets 0 and 1 may overlap 1 byte" "strcat" } */
+  T (d, d + 2);                   /* { dg-warning "accessing 0 or more bytes at offsets 0 and 2 may overlap 1 byte" "strcat" } */
+  T (d, d + 999);                 /* { dg-warning "accessing 0 or more bytes at offsets 0 and 999 may overlap 1 byte" "strcat" } */
+  T (d, d + -99);                 /* { dg-warning "accessing 0 or more bytes at offsets 0 and -99 may overlap 1 byte" "strcat" } */
+
+  size_t n = unsigned_value ();
+
+  T (d + n, d + n);                       /* { dg-warning "\\\[-Wrestrict" "strcat" } */
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T (d, "012");
+  T (d + 1, "0123");
+  T (d + n, "01234");
+  T (d, s);
+  T (d + 1, s);
+  T (d + n, s);
+
+  /* Since the offset is unknown the overlap in the call below, while
+     possible, is certainly not inevitable.  Conservatively, it should
+     not be diagnosed.  For safety, an argument for diagnosing can be
+     made.  It's a judgment call, partly determined by the effort and
+     complexity of treating this case differently from other similar
+     to it.   */
+  T (d, d + n); /* { dg-warning "may overlap" "strcat" } */
+}
+
+/* Exercise strcpy with constant or known arguments.  */
+
+void test_strcpy_cst (ptrdiff_t i)
+{
+#undef T
+#define T(init, dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[8] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcpy (pd, ps);					\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcpy" } */
+  T ("012", a, a + 1);            /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strcpy" } */
+  T ("012", a, a + 2);
+  T ("012", a, a + 3);
+  /* The following doesn't overlap but it should trigger -Wstringop-overflow
+     for reading past the end.  */
+  T ("012", a, a + sizeof a);
+
+  /* The terminating nul written to d[2] overwrites s[0].  */
+  T ("0123", a, a + 2);           /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
+
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("01234", a, a + 2);          /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" } */
+
+  /* This happens to be safe in GCC but it's still wrong.  */
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  T ("012", a + 1, a);            /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 3 bytes at offset 1" "strcpy" } */
+  T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 2 bytes at offset 2" "strcpy" } */
+  T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3" "strcpy" } */
+  T ("012", a + 4, a);
+  /* The following doesn't overlap but it should trigger -Wstrinop-ovewrflow
+     for writing past the end.  */
+  T ("012", a + sizeof a, a);
+}
+
+/* Exercise strcpy with constant or known arguments offset by a range.
+   The tests verify the use of the lower bound of the range which is
+   more restrictive than using the upper bound for positive values.  */
+
+void test_strcpy_range (void)
+{
+#undef T
+#define T(N, init, dst, src)			\
+  do {						\
+    if (!LINE || LINE == __LINE__) {		\
+      char a[N] = init;				\
+      char *pd = (dst);				\
+      const char *ps = (src);			\
+      strcpy (pd, ps);				\
+      sink (a, pd, ps);				\
+    }						\
+  } while (0)
+
+  ptrdiff_t r;
+
+  r = SR (0, 1);
+  T (8, "0", a + r, a);   /* { dg-warning "accessing between 1 and 2 bytes at offsets \\\[0, 1] and 0 overlaps up to 2 bytes at offset \\\[0, 1]" "strcpy" { xfail *-*-*} } */
+
+  r = SR (2, 5);
+  T (8, "01",  a + r, a);            /* { dg-warning "accessing 3 bytes at offsets \\\[2, 5] and 0 may overlap 1 byte at offset 2" } */
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[2, 5] and 0 may overlap up to 2 bytes at offset \\\[3, 2]" "strcpy" } */
+
+  /* The highest offset to which to copy without overflowing the 8-byte
+     destination is 3 and that overlaps 2 bytes.  */
+  T (8, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 2 and 3 bytes at offset \\\[3, 2]" "strcpy" } */
+
+  /* With a 9-byte destination the highest offset is 4 and that still
+     overlaps 1 byte (the final NUL).  */
+  T (9, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 1 and 3 bytes at offset \\\[4, 2]" "strcpy" } */
+
+  /* With a 10-byte buffer it's possible to copy all 5 bytes without
+     overlap at (a + 5).  Copying at offsets 2 through 4 overflows
+     between 3 and 1 bytes, respectively.  */
+  T (10, "0123", a + r, a);          /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 may overlap up to 3 bytes at offset \\\[4, 2]" "strcpy" } */
+
+
+  r  = SR (3, 4);
+  T (8, "01",  a + r, a);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[3, 4] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+  /* The highest offset to which to copy without overflowing the 8-byte
+     destination is 3 and that overlaps 2 bytes.  */
+  T (8, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps 2 bytes at offset 3" "strcpy" } */
+
+  /* With a 9-byte destination the highest offset is 4 and that still
+     overlaps 1 byte (the final NUL).  */
+  T (9, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[4, 3]" "strcpy" } */
+
+  /* With a 10-byte buffer it's possible to copy all 5 bytes without
+     overlap at (a + 5).  Copying at offsets 2 through 4 overflows
+     between 3 and 1 bytes, respectively.  */
+  T (10, "0123", a + r, a);          /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[4, 3]" "strcpy" } */
+
+  T (8, "01",     a, a + r);
+  T (8, "012",    a, a + r);
+  T (8, "0123",   a, a + r);
+  T (8, "01234",  a, a + r);
+
+  /* With the smaller offset of 3 the final NUL definitely overlaps
+     the '4' at a[3], but with the larger offset of 4 there is no
+     overlap, so the warning is a "may overlap" and the size of
+     the overlap is 1 byte.  */
+  T (8, "012345", a, a + r);         /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and \\\[3, 4] may overlap 1 byte at offset 3" "strcpy" } */
+  T (8, "0123456", a, a + r);        /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and \\\[3, 4] may overlap up to 2 bytes at offset 3" "strcpy" } */
+
+  r = SR (3, DIFF_MAX - 3);
+  T (8, "01",  a + r, a);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[3, \[0-9\]+] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+  r = SR (DIFF_MAX - 2, DIFF_MAX - 1);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps" "strcpy" } */
+
+  /* Exercise the full range of ptrdiff_t.  */
+  r = signed_value ();
+
+  /* The overlap in the cases below isn't inevitable but it is diagnosed
+     because it is possible and so the code is considered unsafe.  */
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap 1 byte" "strcpy" } */
+  T (8, "0", a + r, a);              /* { dg-warning "accessing 2 bytes may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes may overlap up to 4 bytes" "strcpy" } */
+
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap" "strcpy" } */
+  T (8, "0", a, a + r);              /* { dg-warning "accessing between 0 and 2 bytes may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a, a + r);            /* { dg-warning "accessing between 0 and 4 bytes may overlap up to 4 bytes" "strcpy" } */
+}
+
+/* Exercise strcpy with destination and/or source of unknown lengthu.  */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcpy (pd, ps);					\
+      sink (pd, ps);					\
+    }							\
+  } while (0)
+
+  T (d, s);
+
+  T (d, &d[0]);                   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+  T (&d[0], d);                   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  s = d;
+  T (d, s);                       /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  /* The following overlaps if *s is not nul.  It arguably should be
+     diagnosed.  */
+  T (d, d + 1);
+
+  /* The following overlaps if strlen (d) is greater than 1.  Like
+     the above, it possibly should be diagnosed too.  */
+  int r = SR (2, 3);
+  T (d, d + r);
+
+  /* The following overlaps only if strlen (s + n) >= n so it's not
+     diagnosed.  */
+  s = d + signed_value ();
+  T (d, s);
+}
+
+/* Exercise strncpy with constant or known arguments.  */
+
+void test_strncpy_cst (void)
+{
+#undef T
+#define T(init, dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[7] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strncpy (pd, ps, (size));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("012", a, a, 0);
+  T ("012", a, a, 1);             /* { dg-warning "source argument is the same as destination " "strncpy" } */
+  T ("012", a, a + 1, 1);
+  T ("012", a, a + 1, 2);         /* { dg-warning "accessing 2 bytes at offsets 0 and 1 overlaps 1 byte at offset 1" "strncpy" } */
+  T ("012", a, a + 1, 3);         /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strncpy" } */
+  T ("012", a, a + 2, 1);
+  T ("012", a, a + 2, 2);
+  /* The third written byte (nul) overwrites a[2].  */
+  T ("012", a, a + 2, 3);         /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+
+  T ("0123", a, a + 2, 1);
+  T ("0123", a, a + 2, 2);
+  /* The terminating nul written to a[2] overwrites s[0].  */
+  T ("0123", a, a + 2, 3);        /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+
+  T ("01234", a, a + 2, 1);
+  T ("01234", a, a + 2, 2);
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("01234", a, a + 2, 4);       /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+}
+
+
+/* Exercise strncpy with one or more arguments in a determinate range.  */
+
+void test_strncpy_range (char *d, size_t n)
+{
+#undef T
+#define T(init, dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = init;					\
+      strncpy ((dst), (src), (size));			\
+      sink (a, (dst), (src));				\
+    }							\
+  } while (0)
+
+  ptrdiff_t i;
+
+  i = SR (0, 1);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);   /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[0, 1] may overlap 1 byte at offset 1" "strncpy" { xfail *-*-* } } */
+
+  i = SR (1, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);   /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[1, 5] may overlap 1 byte at offset 1" "strncpy" } */
+  T ("0123", a, a + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[1, 5] may overlap up to 2 bytes at offset \\\[2, 1]" "strncpy" } */
+
+  i = SR (2, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[2, 5] may overlap 1 byte at offset 2" "strncpy" } */
+  T ("0123", a, a + i, 4);   /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[2, 5] may overlap up to 2 bytes at offset \\\[3, 2]" "strncpy" } */
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+
+  i = SR (3, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);
+  T ("0123", a, a + i, 4);   /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[3, 5] may overlap 1 byte at offset 3" "strncpy" } */
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+
+  i = SR (4, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);
+  T ("0123", a, a + i, 4);
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+
+  /* Verify offset and size both in some range.  The strncpy checking
+     is more strict than that of memcpy and triggers even when the
+     overlap is possible but not inevitable.  The following overlaps
+     like so ('*' denotes the terminating NUL, '.' the appended NUL
+     that's not copied from the source):
+        a:        01234567*  (also indicates offset)
+        i = 4:    4567       none
+                  4567*      overlaps 1 at offset 4
+                  4567*.     overlaps 2 at offset 4
+        i = 5:    567*       none
+                  567*.      none
+                  567*..     overlaps 1 at offset 5  */
+  T ("01234567", a, a + i, UR (4, 6));   /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  /* Ditto for objects of unknown sizes.  */
+  T ("01234567", d, d + i, UR (4, 6));  /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  T ("01234567", a, a + i, UR (6, 7));  /* { dg-warning "accessing between 6 and 7 bytes at offsets 0 and \\\[4, 5] overlaps between 1 and 3 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  /* The following overlaps except in the unlikely case that value ()
+     is zero, so it's diagnosed.  */
+  T ("012", a, a, n);             /* { dg-warning "source argument is the same as destination " "strncpy" } */
+}
+
+
+/* Exercise strncpy with destination and source of unknown length.  */
+
+void test_strncpy_var (char *d, const char *s, size_t n)
+{
+#undef T
+#define T(dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {		\
+      char *pd = (dst);				\
+      const char *ps = (src);			\
+      strncpy (pd, ps, (size));			\
+      sink (pd, ps);				\
+    }						\
+  } while (0)
+
+  T (d, s, 1);
+  T (d, s, n);
+
+  T (d, d, 1);                    /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+  T (d, d, n);                    /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+
+  T (d,     d + 1, 1);
+  T (d,     d + 1, 2);            /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+  T (d + 1, d,     1);
+  T (d + 1, d,     2);            /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+}
+
+struct MemberArrays
+{
+  char a[7];
+  char b[8];
+  char c[9];
+};
+
+void test_strncpy_strcpy_var (struct MemberArrays *ar, const char *s)
+{
+  /* The following is safe and should not trigger a warning.  */
+  strncpy (ar->b, s, sizeof ar->b - 1);
+  ar->b[sizeof ar->b - 1] = '\0';
+  strcpy (ar->a, ar->b);
+  sink (ar);
+
+  /* The following is not as safe (it might overflow ar->a) but there
+     is no overlap so it also shouldn't trigger -Wrestrict.  */
+  strncpy (ar->c, s, sizeof ar->c - 1);
+  ar->c[sizeof ar->c - 1] = '\0';
+  strcpy (ar->a, ar->c);
+  sink (ar);
+}
diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
index 7861bb0..4281e3b 100644
--- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
@@ -1,7 +1,7 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument" } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */
 /* { dg-require-effective-target alloca } */
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
index f7bfa35..d9a1555 100644
--- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
+++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
@@ -1,7 +1,7 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
-/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -Wno-stringop-truncation -Wno-c++-compat -ftrack-macro-expansion=0" {target c} } */
+/* { dg-options "-Wall -O2 -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
+/* { dg-options "-Wall -O2 -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-truncation -Wno-c++-compat -ftrack-macro-expansion=0" {target c} } */
 /* { dg-require-effective-target alloca } */
 
 #define bos(ptr) __builtin_object_size (ptr, 1)
diff --git a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
index 5bc5c4c..16340eb 100644
--- a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
+++ b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
@@ -1,6 +1,6 @@
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
+// { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
 // Test just twice, once with -O0 non-fortified, once with -O2 fortified.
 // { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } }
 // { dg-skip-if "" { *-*-* }  { "-flto" } { "" } }
diff --git a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
index f2c864b..2dff8f0 100644
--- a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
+++ b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
@@ -1,6 +1,6 @@
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
+// { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
 // Test just twice, once with -O0 non-fortified, once with -O2 fortified,
 // suppressing buffer overflow warnings.
 // { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } }
diff --git a/gcc/testsuite/gcc.dg/Wrestrict-2.c b/gcc/testsuite/gcc.dg/Wrestrict-2.c
new file mode 100644
index 0000000..d73e144
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wrestrict-2.c
@@ -0,0 +1,41 @@
+/* Test to verify that the temporary doesn't trigger a bogus -Warray-bounds
+   warning.  Distilled from libat_exchange_large_inplace in libatomic/gexch.c.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" }  */
+
+typedef typeof (sizeof 0) size_t;
+
+extern void *memcpy (void*, const void*, size_t);
+
+void libat_exchange_large_inplace (size_t n, void *mptr, void *vptr)
+{
+  char temp[1024];
+
+  size_t i = 0;
+
+  for (i = 0; n >= 1024; i += 1024, n -= 1024)
+    {
+      memcpy (temp, mptr + i, 1024);
+
+      /* The memcpy call below results in the following:
+	 unsigned long ivtmp.7;
+
+	 ivtmp.7_4 = (unsigned long) mptr_9(D);
+	 ...
+	 <bb 4>
+	 # ivtmp.7_22 = PHI <ivtmp.7_4(3), ivtmp.7_5(4)>
+	 ...
+	 _1 = (void *) ivtmp.7_22;
+	 ...
+	 memcpy (_1, _2, 1024);
+
+	 Treating _1 as a pointer results in the bogus:
+	   warning: 'memcpy' offset 0 is out of the bounds [0, 8] of object 'ivtmp.7' with type 'long unsigned int' [-Warray-bounds]
+	   memcpy (mptr + i, vptr + i, 1024);
+	   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+      */
+      memcpy (mptr + i, vptr + i, 1024);
+
+      memcpy (vptr + i, temp, 1024);
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/Wrestrict.c b/gcc/testsuite/gcc.dg/Wrestrict.c
new file mode 100644
index 0000000..076f878
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wrestrict.c
@@ -0,0 +1,34 @@
+/* Test to verify that VLAs are handled gracefully by -Wrestrict
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict" }  */
+
+typedef __SIZE_TYPE__ size_t;
+
+#define memcpy(d, s, n)  __builtin_memcpy (d, s, n)
+#define strcpy(d, s)     __builtin_strcpy (d, s)
+
+void test_vla (void *d, const char *s1, const char *s2, int i, size_t n)
+{
+  char a[n];
+  char b[n];
+
+  strcpy (a, s1);
+  strcpy (b, s2);
+
+  memcpy (d, i ? a : b, n);
+}
+
+
+void test_vla_member (void *d, const char *s1, const char *s2, int i, size_t n)
+{
+  struct S
+  {
+    char a[n];
+    char b[n];
+  } s;
+
+  strcpy (s.a, s1);
+  strcpy (s.b, s2);
+
+  memcpy (d, i ? s.a : s.b, n);
+}
diff --git a/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
index f4e8552..4d14de2 100644
--- a/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
 /* { dg-require-effective-target alloca } */
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/gcc.dg/memcpy-6.c b/gcc/testsuite/gcc.dg/memcpy-6.c
new file mode 100644
index 0000000..1d76e7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/memcpy-6.c
@@ -0,0 +1,40 @@
+/* Test to verify that overlapping memcpy with const sizes that are powers
+   of two are folded into into the same code as memmove, but that they
+   are diagnosed nonetheless.
+   { dg-do compile }
+   { dg-options "-O0 -Wrestrict -fdump-tree-optimized" } */
+
+char a[32];
+
+void fold_copy_2 (void)
+{
+  __builtin_memcpy (a + 1, a, 2);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_4 (void)
+{
+  __builtin_memcpy (a + 2, a, 4);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_8 (void)
+{
+  __builtin_memcpy (a + 3, a, 8);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_move_2 (void)
+{
+  __builtin_memmove (a + 1, a, 2);
+}
+
+void fold_move_4 (void)
+{
+  __builtin_memmove (a + 2, a, 4);
+}
+
+void fold_move_8 (void)
+{
+  __builtin_memmove (a + 3, a, 8);
+}
+
+/* { dg-final { scan-tree-dump-not "memcpy" "optimized" } }
+   { dg-final { scan-tree-dump-not "memmove" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c
index c0e7463..908d5a6 100644
--- a/gcc/testsuite/gcc.dg/pr69172.c
+++ b/gcc/testsuite/gcc.dg/pr69172.c
@@ -1,4 +1,5 @@
-/* PR tree-optimization/69172 */
+/* PR tree-optimization/69172 - ICE in make_ssa_name_fn,
+   at tree-ssanames.c:266 */
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
 
@@ -43,3 +44,7 @@ f6 (int x)
 {
   return __builtin___mempcpy_chk (&a, &a, x, 0);
 }
+
+/* The calls above violate strict aliasing.  Eliminate the -Wrestrict
+   warnings they trigger.
+  { dg-prune-output "\\\[-Wrestrict]" } */
diff --git a/gcc/testsuite/gcc.dg/pr79223.c b/gcc/testsuite/gcc.dg/pr79223.c
index 295d5c1..ef0dd1b 100644
--- a/gcc/testsuite/gcc.dg/pr79223.c
+++ b/gcc/testsuite/gcc.dg/pr79223.c
@@ -1,6 +1,6 @@
 /* PR middle-end/79223 - missing -Wstringop-overflow on a memmove overflow
    { dg-do compile }
-   { dg-additional-options "-O2 -Wall -std=gnu99" }  */
+   { dg-additional-options "-O2 -Wall -Wno-array-bounds -std=gnu99" }  */
 
 typedef __SIZE_TYPE__ size_t;
 
diff --git a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
index cd9dc72..11367d1 100644
--- a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" } */
 /* Test just twice, once with -O0 non-fortified, once with -O2 fortified.  */
 /* { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } } */
 /* { dg-skip-if "" { *-*-* }  { "-flto" } { "" } } */
diff --git a/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c b/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c
new file mode 100644
index 0000000..1be4922
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c
@@ -0,0 +1,68 @@
+/* { dg-do compile { target { ! x32 } } }
+   { dg-require-effective-target mempcpy }
+   { dg-options "-O2 -Wrestrict -fcheck-pointer-bounds -mmpx" } */
+
+#define USE_GNU
+#include "../../gcc.dg/strlenopt.h"
+
+/* There is no BUILT_IN_ST{P,R}NCPY_CHKP or BUILT_IN_STRNCAT_CHKP
+   so the test for them below are XFAIL.  */
+char *stpncpy (char *__restrict, const char *__restrict, size_t);
+char *strncpy (char *__restrict, const char *__restrict, size_t);
+char *strncat (char *__restrict, const char *__restrict, size_t);
+
+
+char a[8];
+
+void test_memcpy (void)
+{
+  memcpy (a, a + 1, 3);   /* { dg-warning ".memcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+void test_memmove (void)
+{
+  memmove (a, a + 1, 3);
+}
+
+void* test_mempcpy (void)
+{
+  return mempcpy (a, a + 1, 3);   /* { dg-warning ".mempcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+char* test_stpcpy (void)
+{
+  strcpy (a, "0123456");
+  return stpcpy (a, a + 2);   /* { dg-warning ".stpcpy\.chkp. accessing 6 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" } */
+}
+
+char* test_stpncpy (void)
+{
+  strcpy (a, "0123456");
+
+  /* There is no BUILT_IN_STPNCPY_CHKP so this isn't handled.  */
+  return stpncpy (a, a + 2, sizeof a);   /* { dg-warning ".stpcpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strcpy (void)
+{
+  strcpy (a, "0123456");
+  strcpy (a, a + 1);   /* { dg-warning ".strcpy\.chkp. accessing 7 bytes at offsets 0 and 1 overlaps 6 bytes at offset 1" } */
+}
+
+void test_strcat (int n)
+{
+  strcat (a, a + 3);   /* { dg-warning ".strcat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" } */
+}
+
+void test_strncat (int n)
+{
+  strncat (a, a + 3, sizeof a);   /* { dg-warning ".strncat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strncpy (int n)
+{
+  strcpy (a, "0123456");
+
+  /* There is no BUILT_IN_STRNCPY_CHKP so this isn't handled.  */
+  strncpy (a, a + 2, sizeof a);   /* { dg-warning ".strncpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 5 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 9777308..3c9ff35 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -459,6 +459,7 @@ extern gimple_opt_pass *make_pass_build_cgraph_edges (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_local_pure_const (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_nothrow (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tracer (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_warn_restrict (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_warn_unused_result (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_diagnose_tm_blocks (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_lower_tm (gcc::context *ctxt);
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 48b9241..1fb0f0f 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ssa.h"
 #include "cgraph.h"
 #include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
 #include "fold-const.h"
 #include "stor-layout.h"
 #include "gimple-fold.h"
@@ -173,6 +174,7 @@ struct laststmt_struct
 } laststmt;
 
 static int get_stridx_plus_constant (strinfo *, unsigned HOST_WIDE_INT, tree);
+static void handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *);
 
 /* Return:
 
@@ -1386,7 +1388,7 @@ static void
 handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, len, lhs, args, type, fn, oldlen;
+  tree src, dst, srclen, len, lhs, type, fn, oldlen;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi, *olddsi, *zsi;
@@ -1502,6 +1504,23 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 	    }
 	}
       dsi->stmt = stmt;
+
+      /* Try to detect overlap before returning.  This catches cases
+	 like strcpy (d, d + n) where n is non-constant whose range
+	 is such that (n <= strlen (d) holds).
+
+	 OLDDSI->NONZERO_chars may have been reset by this point with
+	 oldlen holding it original value.  */
+      if (olddsi && oldlen)
+	{
+	  /* Add 1 for the terminating NUL.  */
+	  tree type = TREE_TYPE (oldlen);
+	  oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
+				build_int_cst (type, 1));
+	  check_bounds_or_overlap (as_a <gcall *>(stmt), olddsi->ptr, src,
+				   oldlen, NULL_TREE);
+	}
+
       return;
     }
 
@@ -1574,14 +1593,32 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (zsi != NULL)
     zsi->dont_invalidate = true;
 
-  if (fn == NULL_TREE)
-    return;
-
-  args = TYPE_ARG_TYPES (TREE_TYPE (fn));
-  type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+  if (fn)
+    {
+      tree args = TYPE_ARG_TYPES (TREE_TYPE (fn));
+      type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+    }
+  else
+    type = size_type_node;
 
   len = fold_convert_loc (loc, type, unshare_expr (srclen));
   len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
+
+  /* Set the no-warning bit on the transformed statement?  */
+  bool set_no_warning = false;
+
+  if (const strinfo *chksi = olddsi ? olddsi : dsi)
+    if (si
+	&& !check_bounds_or_overlap (as_a <gcall *>(stmt), chksi->ptr, si->ptr,
+				     NULL_TREE, len))
+      {
+	gimple_set_no_warning (stmt, true);
+	set_no_warning = true;
+      }
+
+  if (fn == NULL_TREE)
+    return;
+
   len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
 				  GSI_SAME_STMT);
   if (dump_file && (dump_flags & TDF_DETAILS) != 0)
@@ -1629,6 +1666,21 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
     }
   else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     fprintf (dump_file, "not possible.\n");
+
+  if (set_no_warning)
+    gimple_set_no_warning (stmt, true);	
+}
+
+/* Check the size argument to the built-in forms of stpncpy and strncpy
+   for out-of-bounds offsets or overlapping access, and to see if the
+   size argument is derived from a call to strlen() on the source argument,
+   and if so, issue an appropriate warning.  */
+
+static void
+handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
+{
+  /* Same as stxncpy().  */
+  handle_builtin_stxncpy (bcode, gsi);
 }
 
 /* Return true if LEN depends on a call to strlen(SRC) in an interesting
@@ -1909,9 +1961,10 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
   return false;
 }
 
-/* Check the size argument to the built-in forms of stpncpy and strncpy
-   to see if it's derived from calling strlen() on the source argument
-   and if so, issue a warning.  */
+/* Check the arguments to the built-in forms of stpncpy and strncpy for
+   out-of-bounds offsets or overlapping access, and to see if the size
+   is derived from calling strlen() on the source argument, and if so,
+   issue the appropriate warning.  */
 
 static void
 handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
@@ -1923,8 +1976,51 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
 
   bool with_bounds = gimple_call_with_bounds_p (stmt);
 
+  tree dst = gimple_call_arg (stmt, with_bounds ? 1 : 0);
   tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
   tree len = gimple_call_arg (stmt, with_bounds ? 3 : 2);
+  tree dstsize = NULL_TREE, srcsize = NULL_TREE;
+
+  int didx = get_stridx (dst);
+  if (strinfo *sidst = didx > 0 ? get_strinfo (didx) : NULL)
+    {
+      /* Compute the size of the destination string including the NUL.  */
+      if (sidst->nonzero_chars)
+	{
+	  tree type = TREE_TYPE (sidst->nonzero_chars);
+	  dstsize = fold_build2 (PLUS_EXPR, type, sidst->nonzero_chars,
+				 build_int_cst (type, 1));
+	}
+      dst = sidst->ptr;
+    }
+
+  int sidx = get_stridx (src);
+  strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL;
+  if (sisrc)
+    {
+      /* Strncpy() et al. cannot modify the source string.  Prevent
+	 the rest of the pass from invalidating the strinfo data.  */
+      sisrc->dont_invalidate = true;
+
+      /* Compute the size of the source string including the NUL.  */
+      if (sisrc->nonzero_chars)
+	{
+	  tree type = TREE_TYPE (sisrc->nonzero_chars);
+	  srcsize = fold_build2 (PLUS_EXPR, type, sisrc->nonzero_chars,
+				 build_int_cst (type, 1));
+	}
+
+	src = sisrc->ptr;
+    }
+  else
+    srcsize = NULL_TREE;
+
+  if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, src,
+				dstsize, srcsize))
+    {
+      gimple_set_no_warning (stmt, true);
+      return;
+    }
 
   /* If the length argument was computed from strlen(S) for some string
      S retrieve the strinfo index for the string (PSS->FIRST) alonng with
@@ -1938,14 +2034,6 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
       return;
     }
 
-  int sidx = get_stridx (src);
-  strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL;
-
-  /* Strncpy() et al. cannot modify the source string.  Prevent the rest
-     of the pass from invalidating the strinfo data.  */
-  if (sisrc)
-    sisrc->dont_invalidate = true;
-
   /* Retrieve the strinfo data for the string S that LEN was computed
      from as some function F of strlen (S) (i.e., LEN need not be equal
      to strlen(S)).  */
@@ -1982,17 +2070,6 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
     }
 }
 
-/* Check the size argument to the built-in forms of strncat to see if
-   it's derived from calling strlen() on the source argument and if so,
-   issue a warning.  */
-
-static void
-handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
-{
-  /* Same as stxncpy().  */
-  handle_builtin_stxncpy (bcode, gsi);
-}
-
 /* Handle a memcpy-like ({mem{,p}cpy,__mem{,p}cpy_chk}) call.
    If strlen of the second argument is known and length of the third argument
    is that plus one, strlen of the first argument is the same after this
@@ -2173,16 +2250,22 @@ static void
 handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr;
+  tree srclen, args, type, fn, objsz, endptr;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi;
-  location_t loc;
+  location_t loc = gimple_location (stmt);
   bool with_bounds = gimple_call_with_bounds_p (stmt);
 
-  src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
-  dst = gimple_call_arg (stmt, 0);
-  lhs = gimple_call_lhs (stmt);
+  tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
+  tree dst = gimple_call_arg (stmt, 0);
+
+  /* Bail if the source is the same as destination.  It will be diagnosed
+     elsewhere.  */
+  if (operand_equal_p (src, dst, 0))
+    return;
+
+  tree lhs = gimple_call_lhs (stmt);
 
   didx = get_stridx (dst);
   if (didx < 0)
@@ -2191,10 +2274,48 @@ 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);
+    }
+
+  /* Set the no-warning bit on the transformed statement?  */
+  bool set_no_warning = false;
+
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
+      {
+	  /* The concatenation always involves copying at least one byte
+	     (the terminating nul), even if the source string is empty.
+	     If the source is unknown assume it's one character long and
+	     used that as both sizes.  */
+	tree slen = srclen;
+	if (slen)
+	  {
+	    tree type = TREE_TYPE (slen);
+	    slen = fold_build2 (PLUS_EXPR, type, slen, build_int_cst (type, 1));
+	  }
+
+	tree sptr = si && si->ptr ? si->ptr : src;
+
+	if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr,
+				      NULL_TREE, slen))
+	  {
+	    gimple_set_no_warning (stmt, true);
+	    set_no_warning = true;
+	  }
+      }
+
       /* 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.  */
@@ -2227,20 +2348,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       return;
     }
 
-  srclen = NULL_TREE;
-  si = NULL;
-  idx = get_stridx (src);
-  if (idx < 0)
-    srclen = build_int_cst (size_type_node, ~idx);
-  else if (idx > 0)
-    {
-      si = get_strinfo (idx);
-      if (si != NULL)
-	srclen = get_string_length (si);
-    }
-
-  loc = gimple_location (stmt);
-  dstlen = dsi->nonzero_chars;
+  tree dstlen = dsi->nonzero_chars;
   endptr = dsi->endptr;
 
   dsi = unshare_strinfo (dsi);
@@ -2301,7 +2409,25 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (fn == NULL_TREE)
     return;
 
-  len = NULL_TREE;
+  if (dsi && dstlen)
+    {
+      tree type = TREE_TYPE (dstlen);
+
+      /* Compute the size of the source sequence, including the nul.  */
+      tree srcsize = srclen ? srclen : size_zero_node;
+      srcsize = fold_build2 (PLUS_EXPR, type, srcsize, build_int_cst (type, 1));
+
+      tree sptr = si && si->ptr ? si->ptr : src;
+
+      if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr,
+				    dstlen, srcsize))
+	{
+	  gimple_set_no_warning (stmt, true);
+	  set_no_warning = true;
+	}
+    }
+
+  tree len = NULL_TREE;
   if (srclen != NULL_TREE)
     {
       args = TYPE_ARG_TYPES (TREE_TYPE (fn));
@@ -2376,6 +2502,9 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
     }
   else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     fprintf (dump_file, "not possible.\n");
+
+  if (set_no_warning)
+    gimple_set_no_warning (stmt, true);
 }
 
 /* Handle a call to malloc or calloc.  */
@@ -2867,11 +2996,11 @@ fold_strstr_to_strncmp (tree rhs1, tree rhs2, gimple *stmt)
     }
 }
 
-/* Attempt to optimize a single statement at *GSI using string length
-   knowledge.  */
+/* Attempt to check for validity of the performed access a single statement
+   at *GSI using string length knowledge, and to optimize it.  */
 
 static bool
-strlen_optimize_stmt (gimple_stmt_iterator *gsi)
+strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi)
 {
   gimple *stmt = gsi_stmt (*gsi);
 
@@ -3147,7 +3276,7 @@ strlen_dom_walker::before_dom_children (basic_block bb)
 
   /* Attempt to optimize individual statements.  */
   for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
-    if (strlen_optimize_stmt (&gsi))
+    if (strlen_check_and_optimize_stmt (&gsi))
       gsi_next (&gsi);
 
   bb->aux = stridx_to_strinfo;
Jeff Law Dec. 11, 2017, 10:27 p.m. UTC | #14
On 12/08/2017 12:19 PM, Martin Sebor wrote:
> Attached is revision 8 of the patch with the changes suggested
> and/or requested below.

[ Big snip. ]

> 
> 
> gcc-78918.diff
> 
> 
> PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over self
> 
> gcc/c-family/ChangeLog:
> 
> 	PR tree-optimization/78918
> 	* c-common.c (check_function_restrict): Avoid checking built-ins.
> 	* c.opt (-Wrestrict): Include in -Wall.
> 
> gcc/ChangeLog:
> 
> 	PR tree-optimization/78918
> 	* Makefile.in (OBJS): Add gimple-ssa-warn-restrict.o.
> 	* builtins.c (check_sizes): Rename...
> 	(check_access): ...to this.  Rename function arguments for clarity.
> 	(check_memop_sizes): Adjust names.
> 	(expand_builtin_memchr, expand_builtin_memcpy): Same.
> 	(expand_builtin_memmove, expand_builtin_mempcpy): Same.
> 	(expand_builtin_strcat, expand_builtin_stpncpy): Same.
> 	(check_strncat_sizes, expand_builtin_strncat): Same.
> 	(expand_builtin_strncpy, expand_builtin_memset): Same.
> 	(expand_builtin_bzero, expand_builtin_memcmp): Same.
> 	(expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
> 	(maybe_emit_sprintf_chk_warning): Same.
> 	(expand_builtin_strcpy): Adjust.
> 	(expand_builtin_stpcpy): Same.
> 	(expand_builtin_with_bounds): Detect out-of-bounds accesses
> 	in pointer-checking forms of memcpy, memmove, and mempcpy.
> 	(gcall_to_tree_minimal, max_object_size): Define new functions.
> 	* builtins.h (max_object_size): Declare.
> 	* calls.c (alloc_max_size): Call max_object_size instead of
> 	hardcoding ssizetype limit.
> 	(get_size_range): Handle new argument.
> 	* calls.h (get_size_range): Add a new argument.
> 	* cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
> 	* doc/invoke.texi (-Wrestrict): Adjust, add example.
> 	* gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
> 	operations.
> 	(gimple_fold_builtin_memory_chk): Same.
> 	(gimple_fold_builtin_stxcpy_chk): New function.
> 	* gimple-ssa-warn-restrict.c: New source.
> 	* gimple-ssa-warn-restrict.h: New header.
> 	* gimple.c (gimple_build_call_from_tree): Propagate location.
> 	* passes.def (pass_warn_restrict): Add new pass.
> 	* tree-pass.h (make_pass_warn_restrict): Declare.
> 	* tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
> 	operations.
> 	(handle_builtin_strcat): Same.
> 	(strlen_optimize_stmt): Rename...
> 	(strlen_check_and_optimize_stmt): ...to this.  Handle strncat,
> 	stpncpy, strncpy, and their checking forms.
> 
> gcc/testsuite/ChangeLog:
> 
> 	PR tree-optimization/78918
> 	* c-c++-common/Warray-bounds.c: New test.
> 	* c-c++-common/Warray-bounds-2.c: New test.
> 	* c-c++-common/Warray-bounds-3.c: New test.
> 	* c-c++-common/Wrestrict-2.c: New test.
> 	* c-c++-common/Wrestrict.c: New test.
> 	* c-c++-common/Wrestrict.s: New test.
> 	* c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust
> 	* c-c++-common/Wsizeof-pointer-memaccess2.c: Same.
> 	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
> 	* g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
> 	* gcc.dg/memcpy-6.c: New test.
> 	* gcc.dg/pr69172.c: Adjust.
> 	* gcc.dg/pr79223.c: Same.
> 	* gcc.dg/Wrestrict-2.c: New test.
> 	* gcc.dg/Wrestrict.c: New test.
> 	* gcc.dg/Wsizeof-pointer-memaccess1.c
> 	* gcc.target/i386/chkp-stropt-17.c: New test.
> 	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Adjust.
OK.  Thanks for your patience.  I know this was a ton of work and even
more waiting.

jeff
Martin Sebor Dec. 17, 2017, 12:01 a.m. UTC | #15
On 12/11/2017 03:27 PM, Jeff Law wrote:
> On 12/08/2017 12:19 PM, Martin Sebor wrote:
>> Attached is revision 8 of the patch with the changes suggested
>> and/or requested below.
>
> [ Big snip. ]
>
>>
>>
>> gcc-78918.diff
>>
>>
>> PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over self
>>
>> gcc/c-family/ChangeLog:
>>
>> 	PR tree-optimization/78918
>> 	* c-common.c (check_function_restrict): Avoid checking built-ins.
>> 	* c.opt (-Wrestrict): Include in -Wall.
>>
>> gcc/ChangeLog:
>>
>> 	PR tree-optimization/78918
>> 	* Makefile.in (OBJS): Add gimple-ssa-warn-restrict.o.
>> 	* builtins.c (check_sizes): Rename...
>> 	(check_access): ...to this.  Rename function arguments for clarity.
>> 	(check_memop_sizes): Adjust names.
>> 	(expand_builtin_memchr, expand_builtin_memcpy): Same.
>> 	(expand_builtin_memmove, expand_builtin_mempcpy): Same.
>> 	(expand_builtin_strcat, expand_builtin_stpncpy): Same.
>> 	(check_strncat_sizes, expand_builtin_strncat): Same.
>> 	(expand_builtin_strncpy, expand_builtin_memset): Same.
>> 	(expand_builtin_bzero, expand_builtin_memcmp): Same.
>> 	(expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
>> 	(maybe_emit_sprintf_chk_warning): Same.
>> 	(expand_builtin_strcpy): Adjust.
>> 	(expand_builtin_stpcpy): Same.
>> 	(expand_builtin_with_bounds): Detect out-of-bounds accesses
>> 	in pointer-checking forms of memcpy, memmove, and mempcpy.
>> 	(gcall_to_tree_minimal, max_object_size): Define new functions.
>> 	* builtins.h (max_object_size): Declare.
>> 	* calls.c (alloc_max_size): Call max_object_size instead of
>> 	hardcoding ssizetype limit.
>> 	(get_size_range): Handle new argument.
>> 	* calls.h (get_size_range): Add a new argument.
>> 	* cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
>> 	* doc/invoke.texi (-Wrestrict): Adjust, add example.
>> 	* gimple-fold.c (gimple_fold_builtin_memory_op): Detect overlapping
>> 	operations.
>> 	(gimple_fold_builtin_memory_chk): Same.
>> 	(gimple_fold_builtin_stxcpy_chk): New function.
>> 	* gimple-ssa-warn-restrict.c: New source.
>> 	* gimple-ssa-warn-restrict.h: New header.
>> 	* gimple.c (gimple_build_call_from_tree): Propagate location.
>> 	* passes.def (pass_warn_restrict): Add new pass.
>> 	* tree-pass.h (make_pass_warn_restrict): Declare.
>> 	* tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
>> 	operations.
>> 	(handle_builtin_strcat): Same.
>> 	(strlen_optimize_stmt): Rename...
>> 	(strlen_check_and_optimize_stmt): ...to this.  Handle strncat,
>> 	stpncpy, strncpy, and their checking forms.
>>
>> gcc/testsuite/ChangeLog:
>>
>> 	PR tree-optimization/78918
>> 	* c-c++-common/Warray-bounds.c: New test.
>> 	* c-c++-common/Warray-bounds-2.c: New test.
>> 	* c-c++-common/Warray-bounds-3.c: New test.
>> 	* c-c++-common/Wrestrict-2.c: New test.
>> 	* c-c++-common/Wrestrict.c: New test.
>> 	* c-c++-common/Wrestrict.s: New test.
>> 	* c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust
>> 	* c-c++-common/Wsizeof-pointer-memaccess2.c: Same.
>> 	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
>> 	* g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
>> 	* gcc.dg/memcpy-6.c: New test.
>> 	* gcc.dg/pr69172.c: Adjust.
>> 	* gcc.dg/pr79223.c: Same.
>> 	* gcc.dg/Wrestrict-2.c: New test.
>> 	* gcc.dg/Wrestrict.c: New test.
>> 	* gcc.dg/Wsizeof-pointer-memaccess1.c
>> 	* gcc.target/i386/chkp-stropt-17.c: New test.
>> 	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Adjust.
> OK.  Thanks for your patience.  I know this was a ton of work and even
> more waiting.

Thanks.  In more testing I uncovered a few minor glitches.  I've
fixed those and committed r255755.  Attached is the committed patch
for reference.

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

gcc/c-family/ChangeLog:

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

gcc/ChangeLog:

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

gcc/testsuite/ChangeLog:

	PR tree-optimization/78918
	* c-c++-common/Warray-bounds.c: New test.
	* c-c++-common/Warray-bounds-2.c: New test.
	* c-c++-common/Warray-bounds-3.c: New test.
	* c-c++-common/Warray-bounds-4.c: New test.
	* c-c++-common/Warray-bounds-5.c: New test.
	* c-c++-common/Wrestrict-2.c: New test.
	* c-c++-common/Wrestrict.c: New test.
	* c-c++-common/Wrestrict.s: New test.
	* c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust
	* c-c++-common/Wsizeof-pointer-memaccess2.c: Same.
	* g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
	* g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
	* gcc.dg/range.h: New header.
	* gcc.dg/memcpy-6.c: New test.
	* gcc.dg/pr69172.c: Adjust.
	* gcc.dg/pr79223.c: Same.
	* gcc.dg/pr81345.c: Adjust.
	* gcc.dg/Wobjsize-1.c: Same.
	* gcc.dg/Wrestrict-2.c: New test.
	* gcc.dg/Wrestrict.c: New test.
	* gcc.dg/Wsizeof-pointer-memaccess1.c: Adjust.
	* gcc.dg/builtin-stpncpy.c: Same.
	* gcc.dg/builtin-stringop-chk-1.c: Same.
	* gcc.target/i386/chkp-stropt-17.c: New test.
	* gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Adjust.

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 115cbe5..d9f27de 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1316,6 +1316,7 @@ OBJS = \
 	gimple-ssa-strength-reduction.o \
 	gimple-ssa-sprintf.o \
 	gimple-ssa-warn-alloca.o \
+	gimple-ssa-warn-restrict.o \
 	gimple-streamer-in.o \
 	gimple-streamer-out.o \
 	gimple-walk.o \
diff --git a/gcc/builtins.c b/gcc/builtins.c
index 6b25253..4b06f64 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -43,6 +43,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "alias.h"
 #include "fold-const.h"
 #include "fold-const-call.h"
+#include "gimple-ssa-warn-restrict.h"
 #include "stor-layout.h"
 #include "calls.h"
 #include "varasm.h"
@@ -3003,37 +3004,45 @@ determine_block_size (tree len, rtx len_rtx,
 
 /* 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.
+   Arguments other than EXP may be null.  When non-null, the arguments
+   have the following meaning:
+   DST is the destination of a copy call or NULL otherwise.
+   SRC is the source of a copy call or NULL otherwise.
+   DSTWRITE is the number of bytes written into the destination obtained
+   from the user-supplied size argument to the function (such as in
+   memcpy(DST, SRCs, DSTWRITE) or strncpy(DST, DRC, DSTWRITE).
+   MAXREAD is the user-supplied bound on the length of the source sequence
    (such as in strncat(d, s, N).  It specifies the upper limit on the number
-   of bytes to write.
-   SRC is the source string (such as in strcpy(d, s)) when the expression
-   EXP is a string function call (as opposed to a memory call like memcpy).
-   As an exception, SRC can also be an integer denoting the precomputed
-   size of the source string or object (for functions like memcpy).
-   OBJSIZE is the size of the destination object specified by the last
+   of bytes to write.  If NULL, it's taken to be the same as DSTWRITE.
+   SRCSTR is the source string (such as in strcpy(DST, SRC)) when the
+   expression EXP is a string function call (as opposed to a memory call
+   like memcpy).  As an exception, SRCSTR can also be an integer denoting
+   the precomputed size of the source string or object (for functions like
+   memcpy).
+   DSTSIZE is the size of the destination object specified by the last
    argument to the _chk builtins, typically resulting from the expansion
-   of __builtin_object_size (such as in __builtin___strcpy_chk(d, s,
-   OBJSIZE).
+   of __builtin_object_size (such as in __builtin___strcpy_chk(DST, SRC,
+   DSTSIZE).
 
-   When SIZE is null LEN is checked to verify that it doesn't exceed
+   When DSTWRITE is null LEN is checked to verify that it doesn't exceed
    SIZE_MAX.
 
-   If the call is successfully verified as safe from buffer overflow
-   the function returns true, otherwise false..  */
+   If the call is successfully verified as safe return true, otherwise
+   return false.  */
 
 static bool
-check_sizes (int opt, tree exp, tree size, tree maxlen, tree src, tree objsize)
+check_access (tree exp, tree, tree, tree dstwrite,
+	      tree maxread, tree srcstr, tree dstsize)
 {
+  int opt = OPT_Wstringop_overflow_;
+
   /* The size of the largest object is half the address space, or
-     SSIZE_MAX.  (This is way too permissive.)  */
-  tree maxobjsize = TYPE_MAX_VALUE (ssizetype);
+     PTRDIFF_MAX.  (This is way too permissive.)  */
+  tree maxobjsize = max_object_size ();
 
+  /* Either the length of the source string for string functions or
+     the size of the source object for raw memory functions.  */
   tree slen = NULL_TREE;
 
   tree range[2] = { NULL_TREE, NULL_TREE };
@@ -3042,28 +3051,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);
@@ -3077,10 +3086,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.  */
@@ -3088,20 +3097,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.  */
@@ -3114,30 +3121,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
@@ -3145,7 +3156,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,
@@ -3154,21 +3165,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;
@@ -3178,11 +3189,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);
@@ -3196,40 +3211,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);
@@ -3239,20 +3255,20 @@ 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;
     }
 
@@ -3325,11 +3341,8 @@ compute_objsize (tree dest, int ostype)
    (no overflow or invalid sizes), false otherwise.  */
 
 static bool
-check_memop_sizes (tree exp, tree dest, tree src, tree size)
+check_memop_access (tree exp, tree dest, tree src, tree size)
 {
-  if (!warn_stringop_overflow)
-    return true;
-
   /* For functions like memset and memcpy that operate on raw memory
      try to determine the size of the largest source and destination
      object using type-0 Object Size regardless of the object size
@@ -3337,8 +3350,8 @@ check_memop_sizes (tree exp, tree dest, tree src, tree size)
   tree srcsize = src ? compute_objsize (src, 0) : NULL_TREE;
   tree dstsize = compute_objsize (dest, 0);
 
-  return check_sizes (OPT_Wstringop_overflow_, exp,
-		      size, /*maxlen=*/NULL_TREE, srcsize, dstsize);
+  return check_access (exp, dest, src, size, /*maxread=*/NULL_TREE,
+		       srcsize, dstsize);
 }
 
 /* Validate memchr arguments without performing any expansion.
@@ -3359,9 +3372,8 @@ expand_builtin_memchr (tree exp, rtx)
   if (warn_stringop_overflow)
     {
       tree size = compute_objsize (arg1, 0);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, len, /*maxlen=*/NULL_TREE,
-		   size, /*objsize=*/NULL_TREE);
+      check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+		    /*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
     }
 
   return NULL_RTX;
@@ -3383,7 +3395,7 @@ expand_builtin_memcpy (tree exp, rtx target)
   tree src = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, src, len);
+  check_memop_access (exp, dest, src, len);
 
   return expand_builtin_memory_copy_args (dest, src, len, target, exp,
 					  /*endp=*/ 0);
@@ -3403,7 +3415,7 @@ expand_builtin_memmove (tree exp, rtx)
   tree src = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, src, len);
+  check_memop_access (exp, dest, src, len);
 
   return NULL_RTX;
 }
@@ -3462,7 +3474,7 @@ expand_builtin_mempcpy (tree exp, rtx target)
   /* Avoid expanding mempcpy into memcpy when the call is determined
      to overflow the buffer.  This also prevents the same overflow
      from being diagnosed again when expanding memcpy.  */
-  if (!check_memop_sizes (exp, dest, src, len))
+  if (!check_memop_access (exp, dest, src, len))
     return NULL_RTX;
 
   return expand_builtin_mempcpy_args (dest, src, len,
@@ -3668,8 +3680,8 @@ expand_builtin_strcat (tree exp, rtx)
 
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+  check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE, src,
+		destsize);
 
   return NULL_RTX;
 }
@@ -3691,8 +3703,8 @@ expand_builtin_strcpy (tree exp, rtx target)
   if (warn_stringop_overflow)
     {
       tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+      check_access (exp, dest, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		    src, destsize);
     }
 
   return expand_builtin_strcpy_args (dest, src, target);
@@ -3730,8 +3742,8 @@ expand_builtin_stpcpy (tree exp, rtx target, machine_mode mode)
   if (warn_stringop_overflow)
     {
       tree destsize = compute_objsize (dst, warn_stringop_overflow - 1);
-      check_sizes (OPT_Wstringop_overflow_,
-		   exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, src, destsize);
+      check_access (exp, dst, src, /*size=*/NULL_TREE, /*maxread=*/NULL_TREE,
+		    src, destsize);
     }
 
   /* If return value is ignored, transform stpcpy into strcpy.  */
@@ -3814,8 +3826,7 @@ expand_builtin_stpncpy (tree exp, rtx)
   /* The size of the destination object.  */
   tree destsize = compute_objsize (dest, warn_stringop_overflow - 1);
 
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, len, /*maxlen=*/NULL_TREE, src, destsize);
+  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src, destsize);
 
   return NULL_RTX;
 }
@@ -3845,7 +3856,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.  */
@@ -3869,32 +3880,32 @@ check_strncat_sizes (tree exp, tree objsize)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN 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))
+  /* The strncat function 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 (maxread) && tree_fits_uhwi_p (objsize)
+      && tree_int_cst_equal (objsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return false;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
+  /* The number of bytes to write is LEN but check_access will also
      check SRCLEN if LEN's value isn't known.  */
-  return check_sizes (OPT_Wstringop_overflow_,
-		      exp, /*size=*/NULL_TREE, maxlen, srclen, objsize);
+  return check_access (exp, dest, src, /*size=*/NULL_TREE, maxread, srclen,
+		       objsize);
 }
 
 /* Similar to expand_builtin_strcat, do some very basic size validation
@@ -3912,7 +3923,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);
 
@@ -3935,52 +3946,34 @@ expand_builtin_strncat (tree exp, rtx)
 				size_one_node)
 		 : NULL_TREE);
 
-  /* Strncat copies at most MAXLEN 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))
+  /* The strncat function 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 (maxread) && tree_fits_uhwi_p (destsize)
+      && tree_int_cst_equal (destsize, maxread))
     {
       location_t loc = tree_nonartificial_location (exp);
       loc = expansion_point_location_if_in_system_header (loc);
 
       warning_at (loc, OPT_Wstringop_overflow_,
 		  "%K%qD specified bound %E equals destination size",
-		  exp, get_callee_fndecl (exp), maxlen);
+		  exp, get_callee_fndecl (exp), maxread);
 
       return NULL_RTX;
     }
 
   if (!srclen
-      || (maxlen && tree_fits_uhwi_p (maxlen)
+      || (maxread && tree_fits_uhwi_p (maxread)
 	  && tree_fits_uhwi_p (srclen)
-	  && tree_int_cst_lt (maxlen, srclen)))
-    srclen = maxlen;
+	  && tree_int_cst_lt (maxread, srclen)))
+    srclen = maxread;
 
-  /* The number of bytes to write is LEN but check_sizes will also
-     check SRCLEN if LEN's value isn't known.  */
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, maxlen, srclen, destsize);
+  /* The number of bytes to write is SRCLEN.  */
+  check_access (exp, dest, src, NULL_TREE, maxread, srclen, destsize);
 
   return NULL_RTX;
 }
 
-/* Helper to check the sizes of sequences and the destination of calls
-   to __builtin_strncpy (DST, SRC, CNT) and __builtin___strncpy_chk.
-   Returns true on success (no overflow warning), false otherwise.  */
-
-static bool
-check_strncpy_sizes (tree exp, tree dst, tree src, tree cnt)
-{
-  tree dstsize = compute_objsize (dst, warn_stringop_overflow - 1);
-
-  if (!check_sizes (OPT_Wstringop_overflow_,
-		    exp, cnt, /*maxlen=*/NULL_TREE, src, dstsize))
-    return false;
-
-  return true;
-}
-
 /* Expand expression EXP, which is a call to the strncpy builtin.  Return
    NULL_RTX if we failed the caller should emit a normal call.  */
 
@@ -3999,7 +3992,16 @@ expand_builtin_strncpy (tree exp, rtx target)
       /* The length of the source sequence.  */
       tree slen = c_strlen (src, 1);
 
-      check_strncpy_sizes (exp, dest, src, len);
+      if (warn_stringop_overflow)
+	{
+	  tree destsize = compute_objsize (dest,
+					   warn_stringop_overflow - 1);
+
+	  /* The number of bytes to write is LEN but check_access will also
+	     check SLEN if LEN's value isn't known.  */
+	  check_access (exp, dest, src, len, /*maxread=*/NULL_TREE, src,
+			destsize);
+	}
 
       /* We must be passed a constant len and src parameter.  */
       if (!tree_fits_uhwi_p (len) || !slen || !tree_fits_uhwi_p (slen))
@@ -4093,7 +4095,7 @@ expand_builtin_memset (tree exp, rtx target, machine_mode mode)
   tree val = CALL_EXPR_ARG (exp, 1);
   tree len = CALL_EXPR_ARG (exp, 2);
 
-  check_memop_sizes (exp, dest, NULL_TREE, len);
+  check_memop_access (exp, dest, NULL_TREE, len);
 
   return expand_builtin_memset_args (dest, val, len, target, mode, exp);
 }
@@ -4282,7 +4284,7 @@ expand_builtin_bzero (tree exp)
   tree dest = CALL_EXPR_ARG (exp, 0);
   tree size = CALL_EXPR_ARG (exp, 1);
 
-  check_memop_sizes (exp, dest, NULL_TREE, size);
+  check_memop_access (exp, dest, NULL_TREE, size);
 
   /* New argument list transforming bzero(ptr x, int y) to
      memset(ptr x, int 0, size_t y).   This is done this way
@@ -4341,14 +4343,12 @@ expand_builtin_memcmp (tree exp, rtx target, bool result_eq)
   if (warn_stringop_overflow)
     {
       tree size = compute_objsize (arg1, 0);
-      if (check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE))
+      if (check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+			/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE))
 	{
 	  size = compute_objsize (arg2, 0);
-	  check_sizes (OPT_Wstringop_overflow_,
-		       exp, len, /*maxlen=*/NULL_TREE,
-		       size, /*objsize=*/NULL_TREE);
+	  check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, len,
+			/*maxread=*/NULL_TREE, size, /*objsize=*/NULL_TREE);
 	}
     }
 
@@ -7718,6 +7718,23 @@ expand_builtin_with_bounds (tree exp, rtx target,
 	return target;
       break;
 
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMPCPY_CHKP:
+      if (call_expr_nargs (exp) > 3)
+	{
+	  /* memcpy_chkp (void *dst, size_t dstbnd,
+	                  const void *src, size_t srcbnd, size_t n)
+  	     and others take a pointer bound argument just after each
+	     pointer argument.  */
+	  tree dest = CALL_EXPR_ARG (exp, 0);
+	  tree src = CALL_EXPR_ARG (exp, 2);
+	  tree len = CALL_EXPR_ARG (exp, 4);
+
+	  check_memop_access (exp, dest, src, len);
+	  break;
+	}
+
     default:
       break;
     }
@@ -9735,8 +9752,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
@@ -9744,14 +9759,13 @@ expand_builtin_memory_chk (tree exp, rtx target, machine_mode mode,
 			 INTEGER_TYPE, INTEGER_TYPE, VOID_TYPE))
     return NULL_RTX;
 
-  dest = CALL_EXPR_ARG (exp, 0);
-  src = CALL_EXPR_ARG (exp, 1);
-  len = CALL_EXPR_ARG (exp, 2);
-  size = CALL_EXPR_ARG (exp, 3);
+  tree dest = CALL_EXPR_ARG (exp, 0);
+  tree src = CALL_EXPR_ARG (exp, 1);
+  tree len = CALL_EXPR_ARG (exp, 2);
+  tree size = CALL_EXPR_ARG (exp, 3);
 
-  bool sizes_ok = check_sizes (OPT_Wstringop_overflow_,
-			       exp, len, /*maxlen=*/NULL_TREE,
-			       /*str=*/NULL_TREE, size);
+  bool sizes_ok = check_access (exp, dest, src, len, /*maxread=*/NULL_TREE,
+				/*str=*/NULL_TREE, size);
 
   if (!tree_fits_uhwi_p (size))
     return NULL_RTX;
@@ -9860,7 +9874,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;
   /* The exact size of the access (such as in __strncpy_chk).  */
   tree size = NULL_TREE;
 
@@ -9883,7 +9897,7 @@ 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;
 
@@ -9896,14 +9910,14 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
 
     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
@@ -9912,8 +9926,10 @@ maybe_emit_chk_warning (tree exp, enum built_in_function fcode)
       return;
     }
 
-  check_sizes (OPT_Wstringop_overflow_, exp,
-	       size, maxlen, srcstr, objsize);
+  /* The destination argument is the first one for all built-ins above.  */
+  tree dst = CALL_EXPR_ARG (exp, 0);
+
+  check_access (exp, dst, srcstr, size, maxread, srcstr, objsize);
 }
 
 /* Emit warning if a buffer overflow is detected at compile time
@@ -9969,8 +9985,9 @@ maybe_emit_sprintf_chk_warning (tree exp, enum built_in_function fcode)
 
   /* Add one for the terminating nul.  */
   len = fold_build2 (PLUS_EXPR, TREE_TYPE (len), len, size_one_node);
-  check_sizes (OPT_Wstringop_overflow_,
-	       exp, /*size=*/NULL_TREE, /*maxlen=*/NULL_TREE, len, size);
+
+  check_access (exp, /*dst=*/NULL_TREE, /*src=*/NULL_TREE, /*size=*/NULL_TREE,
+		/*maxread=*/NULL_TREE, len, size);
 }
 
 /* Emit warning if a free is called with address of a variable.  */
@@ -10600,3 +10617,12 @@ target_char_cst_p (tree t, char *p)
   *p = (char)tree_to_uhwi (t);
   return true;
 }
+
+/* Return the maximum object size.  */
+
+tree
+max_object_size (void)
+{
+  /* To do: Make this a configurable parameter.  */
+  return TYPE_MAX_VALUE (ptrdiff_type_node);
+}
diff --git a/gcc/builtins.h b/gcc/builtins.h
index cf3fc17..7f34d29 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -103,4 +103,6 @@ extern bool target_char_cst_p (tree t, char *p);
 extern internal_fn associated_internal_fn (tree);
 extern internal_fn replacement_internal_fn (gcall *);
 
-#endif
+extern tree max_object_size ();
+
+#endif /* GCC_BUILTINS_H */
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index 6a343a3..197a71f 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -5319,14 +5319,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 31b50ee..5b929d9 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -1178,7 +1178,7 @@ C ObjC Var(warn_duplicate_decl_specifier) Warning LangEnabledBy(C ObjC,Wall)
 Warn when a declaration has duplicate const, volatile, restrict or _Atomic specifier.
 
 Wrestrict
-C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++)
+C ObjC C++ ObjC++ Var(warn_restrict) Warning LangEnabledBy(C ObjC C++ ObjC++, Wall)
 Warn when an argument passed to a restrict-qualified parameter aliases with
 another argument.
 
diff --git a/gcc/calls.c b/gcc/calls.c
index 8ae9899..da6c96b 100644
--- a/gcc/calls.c
+++ b/gcc/calls.c
@@ -54,6 +54,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "intl.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "builtins.h"
 
 /* Like PREFERRED_STACK_BOUNDARY but in units of bytes, not bits.  */
 #define STACK_BYTES (PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT)
@@ -1194,7 +1195,7 @@ alloc_max_size (void)
 {
   if (!alloc_object_size_limit)
     {
-      alloc_object_size_limit = TYPE_MAX_VALUE (ssizetype);
+      alloc_object_size_limit = max_object_size ();
 
       if (warn_alloc_size_limit)
 	{
@@ -1245,7 +1246,8 @@ alloc_max_size (void)
 		{
 		  widest_int w = wi::mul (limit, unit);
 		  if (w < wi::to_widest (alloc_object_size_limit))
-		    alloc_object_size_limit = wide_int_to_tree (ssizetype, w);
+		    alloc_object_size_limit
+		      = wide_int_to_tree (ptrdiff_type_node, w);
 		}
 	    }
 	}
@@ -1254,13 +1256,17 @@ alloc_max_size (void)
 }
 
 /* Return true when EXP's range can be determined and set RANGE[] to it
-   after adjusting it if necessary to make EXP a valid size argument to
-   an allocation function declared with attribute alloc_size (whose
-   argument may be signed), or to a string manipulation function like
-   memset.  */
+   after adjusting it if necessary to make EXP a represents a valid size
+   of object, or a valid size argument to an allocation function declared
+   with attribute alloc_size (whose argument may be signed), or to a string
+   manipulation function like memset.  When ALLOW_ZERO is true, allow
+   returning a range of [0, 0] for a size in an anti-range [1, N] where
+   N > PTRDIFF_MAX.  A zero range is a (nearly) invalid argument to
+   allocation functions like malloc but it is a valid argument to
+   functions like memset.  */
 
 bool
-get_size_range (tree exp, tree range[2])
+get_size_range (tree exp, tree range[2], bool allow_zero /* = false */)
 {
   if (tree_fits_uhwi_p (exp))
     {
@@ -1269,20 +1275,33 @@ get_size_range (tree exp, tree range[2])
       return true;
     }
 
+  tree exptype = TREE_TYPE (exp);
+  bool integral = INTEGRAL_TYPE_P (exptype);
+
   wide_int min, max;
-  enum value_range_type range_type
-    = ((TREE_CODE (exp) == SSA_NAME && INTEGRAL_TYPE_P (TREE_TYPE (exp)))
-       ? get_range_info (exp, &min, &max) : VR_VARYING);
+  enum value_range_type range_type;
+
+  if (TREE_CODE (exp) == SSA_NAME && integral)
+    range_type = get_range_info (exp, &min, &max);
+  else
+    range_type = VR_VARYING;
 
   if (range_type == VR_VARYING)
     {
-      /* No range information available.  */
+      if (integral)
+	{
+	  /* Use the full range of the type of the expression when
+	     no value range information is available.  */
+	  range[0] = TYPE_MIN_VALUE (exptype);
+	  range[1] = TYPE_MAX_VALUE (exptype);
+	  return true;
+	}
+
       range[0] = NULL_TREE;
       range[1] = NULL_TREE;
       return false;
     }
 
-  tree exptype = TREE_TYPE (exp);
   unsigned expprec = TYPE_PRECISION (exptype);
 
   bool signed_p = !TYPE_UNSIGNED (exptype);
@@ -1320,11 +1339,16 @@ get_size_range (tree exp, tree range[2])
 	{
 	  /* EXP is unsigned and not in the range [1, MAX].  That means
 	     it's either zero or greater than MAX.  Even though 0 would
-	     normally be detected by -Walloc-zero set the range to
-	     [MAX, TYPE_MAX] so that when MAX is greater than the limit
-	     the whole range is diagnosed.  */
-	  min = max + 1;
-	  max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	     normally be detected by -Walloc-zero, unless ALLOW_ZERO
+	     is true, set the range to [MAX, TYPE_MAX] so that when MAX
+	     is greater than the limit the whole range is diagnosed.  */
+	  if (allow_zero)
+	    min = max = wi::zero (expprec);
+	  else
+	    {
+	      min = max + 1;
+	      max = wi::to_wide (TYPE_MAX_VALUE (exptype));
+	    }
 	}
       else
 	{
diff --git a/gcc/calls.h b/gcc/calls.h
index 9b7fa9a..641166e 100644
--- a/gcc/calls.h
+++ b/gcc/calls.h
@@ -38,8 +38,8 @@ extern bool pass_by_reference (CUMULATIVE_ARGS *, machine_mode,
 extern bool reference_callee_copied (CUMULATIVE_ARGS *, machine_mode,
 				     tree, bool);
 extern void maybe_warn_alloc_args_overflow (tree, tree, tree[2], int[2]);
-extern bool get_size_range (tree, tree[2]);
 extern tree get_attr_nonstring_decl (tree, tree * = NULL);
 extern void maybe_warn_nonstring_arg (tree, tree);
+extern bool get_size_range (tree, tree[2], bool = false);
 
 #endif // GCC_CALLS_H
diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c
index a238e8d..467bad5 100644
--- a/gcc/cfgexpand.c
+++ b/gcc/cfgexpand.c
@@ -2639,6 +2639,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 001bbea..27b07b9 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -3868,6 +3868,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
@@ -6711,11 +6712,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++, a @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 403fbb8..87ce3d8 100644
--- a/gcc/gimple-fold.c
+++ b/gcc/gimple-fold.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ssa.h"
 #include "cgraph.h"
 #include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
 #include "fold-const.h"
 #include "stmt.h"
 #include "expr.h"
@@ -663,13 +664,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,
@@ -681,6 +681,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))
@@ -704,6 +710,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));
@@ -753,6 +768,13 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	  unsigned ilen = tree_to_uhwi (len);
 	  if (pow2p_hwi (ilen))
 	    {
+	      /* Detect invalid bounds and overlapping copies and issue
+		 either -Warray-bounds or -Wrestrict.  */
+	      if (!nowarn
+		  && check_bounds_or_overlap (as_a <gcall *>(stmt),
+					      dest, src, len, len))
+	      	gimple_set_no_warning (stmt, true);
+
 	      scalar_int_mode mode;
 	      tree type = lang_hooks.types.type_for_size (ilen * 8, 1);
 	      if (type
@@ -1025,6 +1047,11 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi,
 	    }
 	}
 
+      /* Detect invalid bounds and overlapping copies and issue either
+	 -Warray-bounds or -Wrestrict.  */
+      if (!nowarn)
+	check_bounds_or_overlap (as_a <gcall *>(stmt), dest, src, len, len);
+
       gimple *new_stmt;
       if (is_gimple_reg_type (TREE_TYPE (srcvar)))
 	{
@@ -1418,7 +1445,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:
@@ -1510,12 +1537,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;
     }
@@ -2416,6 +2450,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);
@@ -2517,6 +2560,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-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c
new file mode 100644
index 0000000..a4191e6
--- /dev/null
+++ b/gcc/gimple-ssa-warn-restrict.c
@@ -0,0 +1,1761 @@
+/* Pass to detect and issue warnings for violations of the restrict
+   qualifier.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Contributed by Martin Sebor <msebor@redhat.com>.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+#include "domwalk.h"
+#include "tree-pass.h"
+#include "builtins.h"
+#include "ssa.h"
+#include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
+#include "diagnostic-core.h"
+#include "fold-const.h"
+#include "gimple-iterator.h"
+#include "tree-dfa.h"
+#include "tree-ssa.h"
+#include "params.h"
+#include "tree-cfg.h"
+#include "tree-object-size.h"
+#include "calls.h"
+#include "cfgloop.h"
+#include "intl.h"
+
+namespace {
+
+const pass_data pass_data_wrestrict = {
+  GIMPLE_PASS,
+  "wrestrict",
+  OPTGROUP_NONE,
+  TV_NONE,
+  PROP_cfg, /* Properties_required.  */
+  0,	    /* properties_provided.  */
+  0,	    /* properties_destroyed.  */
+  0,	    /* properties_start */
+  0,	    /* properties_finish */
+};
+
+/* Pass to detect violations of strict aliasing requirements in calls
+   to built-in string and raw memory functions.  */
+class pass_wrestrict : public gimple_opt_pass
+{
+ public:
+  pass_wrestrict (gcc::context *ctxt)
+    : gimple_opt_pass (pass_data_wrestrict, ctxt)
+    { }
+
+  opt_pass *clone () { return new pass_wrestrict (m_ctxt); }
+
+  virtual bool gate (function *);
+  virtual unsigned int execute (function *);
+};
+
+bool
+pass_wrestrict::gate (function *fun ATTRIBUTE_UNUSED)
+{
+  return warn_array_bounds != 0 || warn_restrict != 0;
+}
+
+/* Class to walk the basic blocks of a function in dominator order.  */
+class wrestrict_dom_walker : public dom_walker
+{
+ public:
+  wrestrict_dom_walker () : dom_walker (CDI_DOMINATORS) {}
+
+  edge before_dom_children (basic_block) FINAL OVERRIDE;
+  bool handle_gimple_call (gimple_stmt_iterator *);
+
+ private:
+  void check_call (gcall *);
+};
+
+edge
+wrestrict_dom_walker::before_dom_children (basic_block bb)
+{
+  /* Iterate over statements, looking for function calls.  */
+  for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);
+       gsi_next (&si))
+    {
+      gimple *stmt = gsi_stmt (si);
+      if (!is_gimple_call (stmt))
+	continue;
+
+      if (gcall *call = as_a <gcall *> (stmt))
+	check_call (call);
+    }
+
+  return NULL;
+}
+
+/* Execute the pass for function FUN, walking in dominator order.  */
+
+unsigned
+pass_wrestrict::execute (function *fun)
+{
+  calculate_dominance_info (CDI_DOMINATORS);
+
+  wrestrict_dom_walker walker;
+  walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
+
+  return 0;
+}
+
+/* Description of a memory reference by a built-in function.  This
+   is similar to ao_ref but made especially suitable for -Wrestrict
+   and not for optimization.  */
+struct builtin_memref
+{
+  /* The original pointer argument to the built-in function.  */
+  tree ptr;
+  /* The referenced subobject or NULL if not available, and the base
+     object of the memory reference or NULL.  */
+  tree ref;
+  tree base;
+
+  /* The size of the BASE object, PTRDIFF_MAX if indeterminate,
+     and negative until (possibly lazily) initialized.  */
+  offset_int basesize;
+
+  /* The non-negative offset of the referenced subobject.  Used to avoid
+     warnings for (apparently) possibly but not definitively overlapping
+     accesses to member arrays.  Negative when unknown/invalid.  */
+  offset_int refoff;
+
+  /* The offset range relative to the base.  */
+  offset_int offrange[2];
+  /* The size range of the access to this reference.  */
+  offset_int sizrange[2];
+
+  /* True for "bounded" string functions like strncat, and strncpy
+     and their variants that specify either an exact or upper bound
+     on the size of the accesses they perform.  For strncat both
+     the source and destination references are bounded.  For strncpy
+     only the destination reference is.  */
+  bool strbounded_p;
+
+  builtin_memref (tree, tree);
+
+  tree offset_out_of_bounds (int, offset_int[2]) const;
+};
+
+/* Description of a memory access by a raw memory or string built-in
+   function involving a pair of builtin_memref's.  */
+class builtin_access
+{
+ public:
+  /* Destination and source memory reference.  */
+  builtin_memref* const dstref;
+  builtin_memref* const srcref;
+  /* The size range of the access.  It's the greater of the accesses
+     to the two references.  */
+  HOST_WIDE_INT sizrange[2];
+
+  /* The minimum and maximum offset of an overlap of the access
+     (if it does, in fact, overlap), and the size of the overlap.  */
+  HOST_WIDE_INT ovloff[2];
+  HOST_WIDE_INT ovlsiz[2];
+
+  /* True to consider valid only accesses to the smallest subobject
+     and false for raw memory functions.  */
+  bool strict () const
+  {
+    return detect_overlap != &builtin_access::generic_overlap;
+  }
+
+  builtin_access (gcall *, builtin_memref &, builtin_memref &);
+
+  /* Entry point to determine overlap.  */
+  bool overlap ();
+
+ private:
+  /* Implementation functions used to determine overlap.  */
+  bool generic_overlap ();
+  bool strcat_overlap ();
+  bool strcpy_overlap ();
+
+  bool no_overlap ()
+  {
+    return false;
+  }
+
+  offset_int overlap_size (const offset_int [2], const offset_int[2],
+			   offset_int [2]);
+
+ private:
+  /* Temporaries used to compute the final result.  */
+  offset_int dstoff[2];
+  offset_int srcoff[2];
+  offset_int dstsiz[2];
+  offset_int srcsiz[2];
+
+  /* Pointer to a member function to call to determine overlap.  */
+  bool (builtin_access::*detect_overlap) ();
+};
+
+/* Initialize a memory reference representation from a pointer EXPR and
+   a size SIZE in bytes.  If SIZE is NULL_TREE then the size is assumed
+   to be unknown.  */
+
+builtin_memref::builtin_memref (tree expr, tree size)
+: ptr (expr),
+  ref (),
+  base (),
+  basesize (-1),
+  refoff (HOST_WIDE_INT_MIN),
+  offrange (),
+  sizrange (),
+  strbounded_p ()
+{
+  /* Unfortunately, wide_int default ctor is a no-op so array members
+     of the type must be set individually.  */
+  offrange[0] = offrange[1] = 0;
+  sizrange[0] = sizrange[1] = 0;
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  if (TREE_CODE (expr) == SSA_NAME)
+    {
+      /* Try to tease the offset out of the pointer.  */
+      gimple *stmt = SSA_NAME_DEF_STMT (expr);
+      if (gimple_assign_single_p (stmt)
+	  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+	expr = gimple_assign_rhs1 (stmt);
+      else if (is_gimple_assign (stmt))
+	{
+	  tree_code code = gimple_assign_rhs_code (stmt);
+	  if (code == NOP_EXPR)
+	    {
+	      tree rhs = gimple_assign_rhs1 (stmt);
+	      if (POINTER_TYPE_P (TREE_TYPE (rhs)))
+		expr = gimple_assign_rhs1 (stmt);
+	    }
+	  else if (code == POINTER_PLUS_EXPR)
+	    {
+	      expr = gimple_assign_rhs1 (stmt);
+
+	      tree offset = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (offset) == INTEGER_CST)
+		{
+		  offset_int off = int_cst_value (offset);
+		  offrange[0] = off;
+		  offrange[1] = off;
+
+		  if (TREE_CODE (expr) == SSA_NAME)
+		    {
+		      gimple *stmt = SSA_NAME_DEF_STMT (expr);
+		      if (gimple_assign_single_p (stmt)
+			  && gimple_assign_rhs_code (stmt) == ADDR_EXPR)
+			expr = gimple_assign_rhs1 (stmt);
+		    }
+		}
+	      else if (TREE_CODE (offset) == SSA_NAME)
+		{
+		  wide_int min, max;
+		  value_range_type rng = get_range_info (offset, &min, &max);
+		  if (rng == VR_RANGE)
+		    {
+		      offrange[0] = min.to_shwi ();
+		      offrange[1] = max.to_shwi ();
+		    }
+		  else if (rng == VR_ANTI_RANGE)
+		    {
+		      offrange[0] = (max + 1).to_shwi ();
+		      offrange[1] = (min - 1).to_shwi ();
+		    }
+		  else
+		    {
+		      gimple *stmt = SSA_NAME_DEF_STMT (offset);
+		      if (is_gimple_assign (stmt)
+			  && gimple_assign_rhs_code (stmt) == NOP_EXPR)
+			{
+			  /* Use the bounds of the type of the NOP_EXPR operand
+			     even if it's signed.  The result doesn't trigger
+			     warnings but makes their output more readable.  */
+			  tree type = TREE_TYPE (gimple_assign_rhs1 (stmt));
+			  offrange[0] = wi::to_offset (TYPE_MIN_VALUE (type));
+			  offrange[1] = wi::to_offset (TYPE_MAX_VALUE (type));
+			}
+		      else
+			offrange[1] = maxobjsize;
+		    }
+		}
+	      else
+		offrange[1] = maxobjsize;
+	    }
+	}
+    }
+
+  if (TREE_CODE (expr) == ADDR_EXPR)
+    {
+      HOST_WIDE_INT off;
+      tree oper = TREE_OPERAND (expr, 0);
+
+      /* Determine the base object or pointer of the reference
+	 and its constant offset from the beginning of the base.  */
+      base = get_addr_base_and_unit_offset (oper, &off);
+
+      if (base)
+	{
+	  offrange[0] += off;
+	  offrange[1] += off;
+
+	  /* Stash the reference for offset validation. */
+	  ref = oper;
+
+	  /* Also stash the constant offset for offset validation.  */
+	  tree_code code = TREE_CODE (oper);
+	  if (code == COMPONENT_REF)
+	    {
+	      tree field = TREE_OPERAND (ref, 1);
+	      tree fldoff = DECL_FIELD_OFFSET (field);
+	      if (TREE_CODE (fldoff) == INTEGER_CST)
+		refoff = off + wi::to_offset (fldoff);
+	    }
+	}
+      else
+	{
+	  size = NULL_TREE;
+	  base = get_base_address (TREE_OPERAND (expr, 0));
+	}
+    }
+
+  if (!base)
+    base = build2 (MEM_REF, char_type_node, expr, null_pointer_node);
+
+  if (TREE_CODE (base) == MEM_REF)
+    {
+      offset_int off = mem_ref_offset (base);
+      refoff += off;
+      offrange[0] += off;
+      offrange[1] += off;
+      base = TREE_OPERAND (base, 0);
+    }
+
+  if (TREE_CODE (base) == SSA_NAME)
+    if (gimple *stmt = SSA_NAME_DEF_STMT (base))
+      {
+	enum gimple_code code = gimple_code (stmt);
+	if (code == GIMPLE_ASSIGN)
+	  if (gimple_assign_rhs_code (stmt) == POINTER_PLUS_EXPR)
+	    {
+	      base = gimple_assign_rhs1 (stmt);
+
+	      tree offset = gimple_assign_rhs2 (stmt);
+	      if (TREE_CODE (offset) == INTEGER_CST)
+		{
+		  offset_int off = int_cst_value (offset);
+		  refoff += off;
+		  offrange[0] += off;
+		  offrange[1] += off;
+		}
+	    }
+
+	if (TREE_CODE (base) == SSA_NAME && SSA_NAME_VAR (base))
+	  base = SSA_NAME_VAR (base);
+      }
+
+  if (size)
+    {
+      tree range[2];
+      /* Determine the size range, allowing for the result to be [0, 0]
+	 for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX.  */
+      get_size_range (size, range, true);
+      sizrange[0] = wi::to_offset (range[0]);
+      sizrange[1] = wi::to_offset (range[1]);
+      /* get_size_range returns SIZE_MAX for the maximum size.
+	 Constrain it to the real maximum of PTRDIFF_MAX.  */
+      if (sizrange[1] > maxobjsize)
+	sizrange[1] = maxobjsize;
+    }
+  else
+    sizrange[1] = maxobjsize;
+}
+
+/* Return error_mark_node if the signed offset exceeds the bounds
+   of the address space (PTRDIFF_MAX).  Otherwise, return either
+   BASE or REF when the offset exceeds the bounds of the BASE or
+   REF object, and set OOBOFF to the past-the-end offset formed
+   by the reference, including its size.  When STRICT is non-zero
+   use REF size, when available, otherwise use BASE size.  When
+   STRICT is greater than 1, use the size of the last array member
+   as the bound, otherwise treat such a member as a flexible array
+   member.  Return NULL when the offset is in bounds.  */
+
+tree
+builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[2]) const
+{
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  /* A temporary, possibly adjusted, copy of the offset range.  */
+  offset_int offrng[2] = { offrange[0], offrange[1] };
+
+  if (DECL_P (base) && TREE_CODE (TREE_TYPE (base)) == ARRAY_TYPE)
+    {
+      if (offrng[1] < offrng[0])
+  	offrng[1] = maxobjsize;
+    }
+
+  /* Conservative offset of the last byte of the referenced object.  */
+  offset_int endoff;
+
+  /* The bounds need not be ordered.  Set HIB to use as the index
+     of the larger of the bounds and LOB as the opposite.  */
+  bool hib = wi::les_p (offrng[0], offrng[1]);
+  bool lob = !hib;
+
+  if (basesize < 0)
+    {
+      endoff = offrng[lob] + sizrange[0];
+
+      /* For a reference through a pointer to an object of unknown size
+	 all initial offsets are considered valid, positive as well as
+	 negative, since the pointer itself can point past the beginning
+	 of the object.  However, the sum of the lower bound of the offset
+	 and that of the size must be less than or equal than PTRDIFF_MAX.  */
+      if (endoff > maxobjsize)
+	return error_mark_node;
+
+      return NULL_TREE;
+    }
+
+  /* A reference to an object of known size must be within the bounds
+     of the base object.  */
+  if (offrng[hib] < 0 || offrng[lob] > basesize)
+    return base;
+
+  /* The extent of the reference must also be within the bounds of
+     the base object (if known) or the maximum object size otherwise.  */
+  endoff = wi::smax (offrng[lob], 0) + sizrange[0];
+  if (endoff > maxobjsize)
+    return error_mark_node;
+
+  offset_int size = basesize;
+  tree obj = base;
+
+  if (strict
+      && DECL_P (obj)
+      && ref
+      && refoff >= 0
+      && TREE_CODE (ref) == COMPONENT_REF
+      && (strict > 1
+	  || !array_at_struct_end_p (ref)))
+    {
+      /* If the reference is to a member subobject, the offset must
+	 be within the bounds of the subobject.  */
+      tree field = TREE_OPERAND (ref, 1);
+      tree type = TREE_TYPE (field);
+      if (tree sz = TYPE_SIZE_UNIT (type))
+	if (TREE_CODE (sz) == INTEGER_CST)
+	  {
+	    size = refoff + wi::to_offset (sz);
+	    obj = ref;
+	  }
+    }
+
+  if (endoff <= size)
+    return NULL_TREE;
+
+  /* Set the out-of-bounds offset range to be one greater than
+     that delimited by the reference including its size.  */
+  ooboff[lob] = size + 1;
+
+  if (endoff > ooboff[lob])
+    ooboff[hib] = endoff;
+  else
+    ooboff[hib] = wi::smax (offrng[lob], 0) + sizrange[1];
+
+  return obj;
+}
+
+/* Create an association between the memory references DST and SRC
+   for access by a call EXPR to a memory or string built-in funtion.  */
+
+builtin_access::builtin_access (gcall *call, builtin_memref &dst,
+				builtin_memref &src)
+: dstref (&dst), srcref (&src), sizrange (), ovloff (), ovlsiz (),
+  dstoff (), srcoff (), dstsiz (), srcsiz ()
+{
+  /* Zero out since the offset_int ctors invoked above are no-op.  */
+  dstoff[0] = dstoff[1] = 0;
+  srcoff[0] = srcoff[1] = 0;
+  dstsiz[0] = dstsiz[1] = 0;
+  srcsiz[0] = srcsiz[1] = 0;
+
+  /* Object Size Type to use to determine the size of the destination
+     and source objects.  Overridden below for raw memory functions.  */
+  int ostype = 1;
+
+  /* True when the size of one reference depends on the offset of
+     itself or the other.  */
+  bool depends_p = true;
+
+  /* True when the size of the destination reference DSTREF has been
+     determined from SRCREF and so needs to be adjusted by the latter's
+     offset.  Only meaningful for bounded string functions like strncpy.  */
+  bool dstadjust_p = false;
+
+  /* The size argument number (depends on the built-in).  */
+  unsigned sizeargno = 2;
+  if (gimple_call_with_bounds_p (call))
+    sizeargno += 2;
+
+  tree func = gimple_call_fndecl (call);
+  switch (DECL_FUNCTION_CODE (func))
+    {
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMCPY_CHK_CHKP:
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHKP:
+    case BUILT_IN_MEMPCPY_CHK_CHKP:
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::generic_overlap;
+      break;
+
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMMOVE_CHK_CHKP:
+      /* For memmove there is never any overlap to check for.  */
+      ostype = 0;
+      depends_p = false;
+      detect_overlap = &builtin_access::no_overlap;
+      break;
+
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      dstref->strbounded_p = true;
+      detect_overlap = &builtin_access::strcpy_overlap;
+      break;
+
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STPCPY_CHKP:
+    case BUILT_IN_STPCPY_CHK_CHKP:
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STRCPY_CHKP:
+    case BUILT_IN_STRCPY_CHK_CHKP:
+      detect_overlap = &builtin_access::strcpy_overlap;
+      break;
+
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRCAT_CHKP:
+    case BUILT_IN_STRCAT_CHK_CHKP:
+      detect_overlap = &builtin_access::strcat_overlap;
+      break;
+
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+      dstref->strbounded_p = true;
+      srcref->strbounded_p = true;
+      detect_overlap = &builtin_access::strcat_overlap;
+      break;
+
+    default:
+      /* Handle other string functions here whose access may need
+	 to be validated for in-bounds offsets and non-overlapping
+	 copies.  (Not all _chkp functions have BUILT_IN_XXX_CHKP
+	 macros so they need to be handled here.)  */
+      return;
+    }
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  /* Try to determine the size of the base object.  compute_objsize
+     expects a pointer so create one if BASE is a non-pointer object.  */
+  tree addr;
+  if (dst.basesize < 0)
+    {
+      addr = dst.base;
+      if (!POINTER_TYPE_P (TREE_TYPE (addr)))
+	addr = build1 (ADDR_EXPR, (TREE_TYPE (addr)), addr);
+
+      if (tree dstsize = compute_objsize (addr, ostype))
+	dst.basesize = wi::to_offset (dstsize);
+      else if (POINTER_TYPE_P (TREE_TYPE (addr)))
+	dst.basesize = HOST_WIDE_INT_MIN;
+      else
+	dst.basesize = maxobjsize;
+    }
+
+  if (src.basesize < 0)
+    {
+      addr = src.base;
+      if (!POINTER_TYPE_P (TREE_TYPE (addr)))
+	addr = build1 (ADDR_EXPR, (TREE_TYPE (addr)), addr);
+
+      if (tree srcsize = compute_objsize (addr, ostype))
+	src.basesize = wi::to_offset (srcsize);
+      else if (POINTER_TYPE_P (TREE_TYPE (addr)))
+	src.basesize = HOST_WIDE_INT_MIN;
+      else
+	src.basesize = maxobjsize;
+    }
+
+  /* If there is no dependency between the references or the base
+     objects of the two references aren't the same there's nothing
+     else to do.  */
+  if (depends_p && dstref->base != srcref->base)
+    return;
+
+  /* ...otherwise, make adjustments for references to the same object
+     by string built-in functions to reflect the constraints imposed
+     by the function.  */
+
+  /* For bounded string functions determine the range of the bound
+     on the access.  For others, the range stays unbounded.  */
+  offset_int bounds[2] = { maxobjsize, maxobjsize };
+  if (dstref->strbounded_p)
+    {
+      tree size = gimple_call_arg (call, sizeargno);
+      tree range[2];
+      if (get_size_range (size, range, true))
+	{
+	  bounds[0] = wi::to_offset (range[0]);
+	  bounds[1] = wi::to_offset (range[1]);
+	}
+
+      /* If both references' size ranges are indeterminate use the last
+	 (size) argument from the function call as a substitute.  This
+	 may only be necessary for strncpy (but not for memcpy where
+	 the size range would have been already determined this way).  */
+      if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize
+	  && srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+	{
+	  dstref->sizrange[0] = bounds[0];
+	  dstref->sizrange[1] = bounds[1];
+	}
+    }
+
+  /* The size range of one reference involving the same base object
+     can be determined from the size range of the other reference.
+     This makes it possible to compute accurate offsets for warnings
+     involving functions like strcpy where the length of just one of
+     the two arguments is known (determined by tree-ssa-strlen).  */
+  if (dstref->sizrange[0] == 0 && dstref->sizrange[1] == maxobjsize)
+    {
+      /* When the destination size is unknown set it to the size of
+	 the source.  */
+      dstref->sizrange[0] = srcref->sizrange[0];
+      dstref->sizrange[1] = srcref->sizrange[1];
+    }
+  else if (srcref->sizrange[0] == 0 && srcref->sizrange[1] == maxobjsize)
+    {
+      /* When the source size is unknown set it to the size of
+	 the destination.  */
+      srcref->sizrange[0] = dstref->sizrange[0];
+      srcref->sizrange[1] = dstref->sizrange[1];
+
+      if (depends_p)
+	{
+	  if (dstref->strbounded_p)
+	    {
+	      /* Read access by strncpy is bounded.  */
+	      if (bounds[0] < srcref->sizrange[0])
+		srcref->sizrange[0] = bounds[0];
+	      if (bounds[1] < srcref->sizrange[1])
+		srcref->sizrange[1] = bounds[1];
+	    }
+
+	  /* For string functions, adjust the size range of the source
+	     reference by the inverse boundaries of the offset (because
+	     the higher  the offset into the string the shorter its
+	     length).  */
+	  if (srcref->offrange[1] < srcref->sizrange[0])
+	    srcref->sizrange[0] -= srcref->offrange[1];
+	  else
+	    srcref->sizrange[0] = 0;
+
+	  if (srcref->offrange[0] > 0)
+	    {
+	      if (srcref->offrange[0] < srcref->sizrange[1])
+		srcref->sizrange[1] -= srcref->offrange[0];
+	      else
+		srcref->sizrange[1] = 0;
+	    }
+
+	  dstadjust_p = true;
+	}
+    }
+
+  if (detect_overlap == &builtin_access::generic_overlap)
+    {
+      if (dstref->strbounded_p)
+	{
+	  dstref->sizrange[0] = bounds[0];
+	  dstref->sizrange[1] = bounds[1];
+
+	  if (dstref->sizrange[0] < srcref->sizrange[0])
+	    srcref->sizrange[0] = dstref->sizrange[0];
+
+	  if (dstref->sizrange[1] < srcref->sizrange[1])
+	    srcref->sizrange[1] = dstref->sizrange[1];
+	}
+    }
+  else if (detect_overlap == &builtin_access::strcpy_overlap)
+    {
+      if (!dstref->strbounded_p)
+	{
+	  /* For strcpy, adjust the destination size range to match that
+	     of the source computed above.  */
+	  if (depends_p && dstadjust_p)
+	    {
+	      dstref->sizrange[0] = srcref->sizrange[0];
+	      dstref->sizrange[1] = srcref->sizrange[1];
+	    }
+	}
+    }
+
+  if (dstref->strbounded_p)
+    {
+      /* For strncpy, adjust the destination size range to match that
+	 of the source computed above.  */
+      dstref->sizrange[0] = bounds[0];
+      dstref->sizrange[1] = bounds[1];
+
+      if (bounds[0] < srcref->sizrange[0])
+	srcref->sizrange[0] = bounds[0];
+
+      if (bounds[1] < srcref->sizrange[1])
+	srcref->sizrange[1] = bounds[1];
+    }
+}
+
+offset_int
+builtin_access::overlap_size (const offset_int a[2], const offset_int b[2],
+			      offset_int *off)
+{
+  const offset_int *p = a;
+  const offset_int *q = b;
+
+  /* Point P at the bigger of the two ranges and Q at the smaller.  */
+  if (wi::lts_p (a[1] - a[0], b[1] - b[0]))
+    {
+      p = b;
+      q = a;
+    }
+
+  if (p[0] < q[0])
+    {
+      if (p[1] < q[0])
+	return 0;
+
+      *off = q[0];
+      return wi::smin (p[1], q[1]) - q[0];
+    }
+
+  if (q[1] < p[0])
+    return 0;
+
+  off[0] = p[0];
+  return q[1] - p[0];
+}
+
+/* Return true if the bounded mempry (memcpy amd similar) or string function
+   access (strncpy and similar) ACS overlaps.  */
+
+bool
+builtin_access::generic_overlap ()
+{
+  builtin_access &acs = *this;
+  const builtin_memref *dstref = acs.dstref;
+  const builtin_memref *srcref = acs.srcref;
+
+  gcc_assert (dstref->base == srcref->base);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize;
+  gcc_assert (maxsize <= maxobjsize);
+
+  /* Adjust the larger bounds of the offsets (which may be the first
+     element if the lower bound is larger than the upper bound) to
+     make them valid for the smallest access (if possible) but no smaller
+     than the smaller bounds.  */
+  gcc_assert (wi::les_p (acs.dstoff[0], acs.dstoff[1]));
+
+  if (maxsize < acs.dstoff[1] + acs.dstsiz[0])
+    acs.dstoff[1] = maxsize - acs.dstsiz[0];
+  if (acs.dstoff[1] < acs.dstoff[0])
+    acs.dstoff[1] = acs.dstoff[0];
+
+  gcc_assert (wi::les_p (acs.srcoff[0], acs.srcoff[1]));
+
+  if (maxsize < acs.srcoff[1] + acs.srcsiz[0])
+    acs.srcoff[1] = maxsize - acs.srcsiz[0];
+  if (acs.srcoff[1] < acs.srcoff[0])
+    acs.srcoff[1] = acs.srcoff[0];
+
+  /* Determine the minimum and maximum space for the access given
+     the offsets.  */
+  offset_int space[2];
+  space[0] = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+  space[1] = space[0];
+
+  offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+  if (acs.srcsiz[0] > 0)
+    {
+      if (d < space[0])
+	space[0] = d;
+
+      if (space[1] < d)
+	space[1] = d;
+    }
+  else
+    space[1] = acs.dstsiz[1];
+
+  d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+  if (d < space[0])
+    space[0] = d;
+
+  if (space[1] < d)
+    space[1] = d;
+
+  /* Treat raw memory functions both of whose references are bounded
+     as special and permit uncertain overlaps to go undetected.  For
+     all kinds of constant offset and constant size accesses, if
+     overlap isn't certain it is not possible.  */
+  bool overlap_possible = space[0] < acs.dstsiz[1];
+  if (!overlap_possible)
+    return false;
+
+  bool overlap_certain = space[1] < acs.dstsiz[0];
+
+  /* True when the size of one reference depends on the offset of
+     the other.  */
+  bool depends_p = detect_overlap != &builtin_access::generic_overlap;
+
+  if (!overlap_certain
+      && !dstref->strbounded_p
+      && !depends_p)
+    return false;
+
+  /* True for stpcpy and strcpy.  */
+  bool stxcpy_p = (!dstref->strbounded_p
+		   && detect_overlap == &builtin_access::strcpy_overlap);
+
+  if (dstref->refoff >= 0
+      && srcref->refoff >= 0
+      && dstref->refoff != srcref->refoff
+      && (stxcpy_p || dstref->strbounded_p || srcref->strbounded_p))
+    return false;
+
+  offset_int siz[2] = { maxobjsize + 1, 0 };
+
+  ovloff[0] = HOST_WIDE_INT_MAX;
+  ovloff[1] = HOST_WIDE_INT_MIN;
+
+  /* Adjustment to the lower bound of the offset of the overlap to
+     account for a subset of unbounded string calls where the size
+     of the destination string depends on the length of the source
+     which in turn depends on the offset into it.  */
+  bool sub1;
+
+  if (stxcpy_p)
+    {
+      sub1 = acs.dstoff[0] <= acs.srcoff[0];
+
+      /* Iterate over the extreme locations (on the horizontal axis formed
+	 by their offsets) and sizes of two regions and find their smallest
+	 and largest overlap and the corresponding offsets.  */
+      for (unsigned i = 0; i != 2; ++i)
+	{
+	  const offset_int a[2] = {
+	    acs.dstoff[i], acs.dstoff[i] + acs.dstsiz[!i]
+	  };
+
+	  const offset_int b[2] = {
+	    acs.srcoff[i], acs.srcoff[i] + acs.srcsiz[!i]
+	  };
+
+	  offset_int off;
+	  offset_int sz = overlap_size (a, b, &off);
+	  if (sz < siz[0])
+	    siz[0] = sz;
+
+	  if (siz[1] <= sz)
+	    siz[1] = sz;
+
+	  if (sz != 0)
+	    {
+	      if (wi::lts_p (off, ovloff[0]))
+		ovloff[0] = off.to_shwi ();
+	      if (wi::lts_p (ovloff[1], off))
+		ovloff[1] = off.to_shwi ();
+	    }
+	}
+    }
+  else
+    {
+      sub1 = !depends_p;
+
+      /* Iterate over the extreme locations (on the horizontal axis
+	 formed by their offsets) and sizes of two regions and find
+	 their smallest and largest overlap and the corresponding
+	 offsets.  */
+
+      for (unsigned io = 0; io != 2; ++io)
+	for (unsigned is = 0; is != 2; ++is)
+	  {
+	    const offset_int a[2] = {
+	      acs.dstoff[io], acs.dstoff[io] + acs.dstsiz[is]
+	    };
+
+	    for (unsigned jo = 0; jo != 2; ++jo)
+	      for (unsigned js = 0; js != 2; ++js)
+		{
+		  if (depends_p)
+		    {
+		      /* For st{p,r}ncpy the size of the source sequence
+			 depends on the offset into it.  */
+		      if (js)
+			break;
+		      js = !jo;
+		    }
+
+		  const offset_int b[2] = {
+		    acs.srcoff[jo], acs.srcoff[jo] + acs.srcsiz[js]
+		  };
+
+		  offset_int off;
+		  offset_int sz = overlap_size (a, b, &off);
+		  if (sz < siz[0])
+		    siz[0] = sz;
+
+		  if (siz[1] <= sz)
+		    siz[1] = sz;
+
+		  if (sz != 0)
+		    {
+		      if (wi::lts_p (off, ovloff[0]))
+			ovloff[0] = off.to_shwi ();
+		      if (wi::lts_p (ovloff[1], off))
+			ovloff[1] = off.to_shwi ();
+		    }
+		}
+	  }
+    }
+
+  ovlsiz[0] = siz[0].to_shwi ();
+  ovlsiz[1] = siz[1].to_shwi ();
+
+  if (ovlsiz[0] == 0 && ovlsiz[1] > 1)
+    ovloff[0] = ovloff[1] + ovlsiz[1] - 1 - sub1;
+
+  return true;
+}
+
+/* Return true if the strcat-like access overlaps.  */
+
+bool
+builtin_access::strcat_overlap ()
+{
+  builtin_access &acs = *this;
+  const builtin_memref *dstref = acs.dstref;
+  const builtin_memref *srcref = acs.srcref;
+
+  gcc_assert (dstref->base == srcref->base);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  gcc_assert (dstref->base && dstref->base == srcref->base);
+
+  /* Adjust for strcat-like accesses.  */
+
+  /* As a special case for strcat, set the DSTREF offsets to the length
+     of the source string since the function starts writing at the first
+     nul, and set the size to 1 for the length of the nul.  */
+  acs.dstoff[0] += acs.dstsiz[0];
+  acs.dstoff[1] += acs.dstsiz[1];
+
+  bool strfunc_unknown_args = acs.dstsiz[0] == 0 && acs.dstsiz[1] != 0;
+
+  /* The lower bound is zero when the size is unknown because then
+     overlap is not certain. */
+  acs.dstsiz[0] = strfunc_unknown_args ? 0 : 1;
+  acs.dstsiz[1] = 1;
+
+  offset_int maxsize = dstref->basesize < 0 ? maxobjsize : dstref->basesize;
+  gcc_assert (maxsize <= maxobjsize);
+
+  /* For references to the same base object, determine if there's a pair
+     of valid offsets into the two references such that access between
+     them doesn't overlap.  Adjust both upper bounds to be valid for
+     the smaller size (i.e., at most MAXSIZE - SIZE).  */
+
+  if (maxsize < acs.dstoff[1] + acs.dstsiz[0])
+    acs.dstoff[1] = maxsize - acs.dstsiz[0];
+
+  if (maxsize < acs.srcoff[1] + acs.srcsiz[0])
+    acs.srcoff[1] = maxsize - acs.srcsiz[0];
+
+  /* Check to see if there's enough space for both accesses without
+     overlap.  Determine the optimistic (maximum) amount of available
+     space.  */
+  offset_int space;
+  if (acs.dstoff[0] <= acs.srcoff[0])
+    {
+      if (acs.dstoff[1] < acs.srcoff[1])
+	space = acs.srcoff[1] + acs.srcsiz[0] - acs.dstoff[0];
+      else
+	space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+    }
+  else
+    space = acs.dstoff[1] + acs.dstsiz[0] - acs.srcoff[0];
+
+  /* Overlap is certain if the distance between the farthest offsets
+     of the opposite accesses is less than the sum of the lower bounds
+     of the sizes of the two accesses.  */
+  bool overlap_certain = space < acs.dstsiz[0] + acs.srcsiz[0];
+
+  /* For a constant-offset, constant size access, consider the largest
+     distance between the offset bounds and the lower bound of the access
+     size.  If the overlap isn't certain return success.  */
+  if (!overlap_certain
+      && acs.dstoff[0] == acs.dstoff[1]
+      && acs.srcoff[0] == acs.srcoff[1]
+      && acs.dstsiz[0] == acs.dstsiz[1]
+      && acs.srcsiz[0] == acs.srcsiz[1])
+    return false;
+
+  /* Overlap is not certain but may be possible.  */
+
+  offset_int access_min = acs.dstsiz[0] + acs.srcsiz[0];
+
+  /* Determine the conservative (minimum) amount of space.  */
+  space = wi::abs (acs.dstoff[0] - acs.srcoff[0]);
+  offset_int d = wi::abs (acs.dstoff[0] - acs.srcoff[1]);
+  if (d < space)
+    space = d;
+  d = wi::abs (acs.dstoff[1] - acs.srcoff[0]);
+  if (d < space)
+    space = d;
+
+  /* For a strict test (used for strcpy and similar with unknown or
+     variable bounds or sizes), consider the smallest distance between
+     the offset bounds and either the upper bound of the access size
+     if known, or the lower bound otherwise.  */
+  if (access_min <= space && (access_min != 0 || !strfunc_unknown_args))
+    return false;
+
+  /* When strcat overlap is certain it is always a single byte:
+     the terminatinn NUL, regardless of offsets and sizes.  When
+     overlap is only possible its range is [0, 1].  */
+  acs.ovlsiz[0] = dstref->sizrange[0] == dstref->sizrange[1] ? 1 : 0;
+  acs.ovlsiz[1] = 1;
+  acs.ovloff[0] = (dstref->sizrange[0] + dstref->offrange[0]).to_shwi ();
+  acs.ovloff[1] = (dstref->sizrange[1] + dstref->offrange[1]).to_shwi ();
+
+  acs.sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+  acs.sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+  return true;
+}
+
+/* Return true if the strcpy-like access overlaps.  */
+
+bool
+builtin_access::strcpy_overlap ()
+{
+  return generic_overlap ();
+}
+
+
+/* Return true if DSTREF and SRCREF describe accesses that either overlap
+   one another or that, in order not to overlap, would imply that the size
+   of the referenced object(s) exceeds the maximum size of an object.  Set
+   Otherwise, if DSTREF and SRCREF do not definitely overlap (even though
+   they may overlap in a way that's not apparent from the available data),
+   return false.  */
+
+bool
+builtin_access::overlap ()
+{
+  builtin_access &acs = *this;
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+  acs.sizrange[0] = wi::smax (dstref->sizrange[0],
+			      srcref->sizrange[0]).to_shwi ();
+  acs.sizrange[1] = wi::smax (dstref->sizrange[1],
+			      srcref->sizrange[1]).to_shwi ();
+
+  /* Check to see if the two references refer to regions that are
+     too large not to overlap in the address space (whose maximum
+     size is PTRDIFF_MAX).  */
+  offset_int size = dstref->sizrange[0] + srcref->sizrange[0];
+  if (maxobjsize < size)
+    {
+      acs.ovloff[0] = (maxobjsize - dstref->sizrange[0]).to_shwi ();
+      acs.ovlsiz[0] = (size - maxobjsize).to_shwi ();
+      return true;
+    }
+
+  /* If both base objects aren't known return the maximum possible
+     offset that would make them not overlap.  */
+  if (!dstref->base || !srcref->base)
+    return false;
+
+  /* If the base object is an array adjust the lower bound of the offset
+     to be non-negative.  */
+  if (dstref->base
+      && TREE_CODE (TREE_TYPE (dstref->base)) == ARRAY_TYPE)
+    acs.dstoff[0] = wi::smax (dstref->offrange[0], 0);
+  else
+    acs.dstoff[0] = dstref->offrange[0];
+
+  acs.dstoff[1] = dstref->offrange[1];
+
+  if (srcref->base
+      && TREE_CODE (TREE_TYPE (srcref->base)) == ARRAY_TYPE)
+    acs.srcoff[0] = wi::smax (srcref->offrange[0], 0);
+  else
+    acs.srcoff[0] = srcref->offrange[0];
+
+  acs.srcoff[1] = srcref->offrange[1];
+
+  /* When the lower bound of the offset is less that the upper bound
+     disregard it and use the inverse of the maximum object size
+     instead.  The upper bound is the result of a negative offset
+     being represented as a large positive value.  */
+  if (acs.dstoff[1] < acs.dstoff[0])
+    acs.dstoff[0] = -maxobjsize;
+
+  /* Validate the offset and size of each reference on its own first.
+     This is independent of whether or not the base objects are the
+     same.  Normally, this would have already been detected and
+     diagnosed by -Warray-bounds, unless it has been disabled.  */
+  offset_int maxoff = acs.dstoff[0] + dstref->sizrange[0];
+  if (maxobjsize < maxoff)
+    {
+      acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+      acs.ovloff[0] = acs.dstoff[0].to_shwi () - acs.ovlsiz[0];
+      return true;
+    }
+
+  /* Repeat the same as above but for the source offsets.  */
+  if (acs.srcoff[1] < acs.srcoff[0])
+    acs.srcoff[0] = -maxobjsize;
+
+  maxoff = acs.srcoff[0] + srcref->sizrange[0];
+  if (maxobjsize < maxoff)
+    {
+      acs.ovlsiz[0] = (maxoff - maxobjsize).to_shwi ();
+      acs.ovlsiz[1] = (acs.srcoff[0] + srcref->sizrange[1]
+		       - maxobjsize).to_shwi ();
+      acs.ovloff[0] = acs.srcoff[0].to_shwi () - acs.ovlsiz[0];
+      return true;
+    }
+
+  if (dstref->base != srcref->base)
+    return false;
+
+  acs.dstsiz[0] = dstref->sizrange[0];
+  acs.dstsiz[1] = dstref->sizrange[1];
+
+  acs.srcsiz[0] = srcref->sizrange[0];
+  acs.srcsiz[1] = srcref->sizrange[1];
+
+  /* Call the appropriate function to determine the overlap.  */
+  if ((this->*detect_overlap) ())
+    {
+      sizrange[0] = wi::smax (acs.dstsiz[0], srcref->sizrange[0]).to_shwi ();
+      sizrange[1] = wi::smax (acs.dstsiz[1], srcref->sizrange[1]).to_shwi ();
+      return true;
+    }
+
+  return false;
+}
+
+/* Attempt to detect and diagnose an overlapping copy in a call expression
+   EXPR involving an an access ACS to a built-in memory or string function.
+   Return true when one has been detected, false otherwise.  */
+
+static bool
+maybe_diag_overlap (location_t loc, gcall *call, builtin_access &acs)
+{
+  if (!acs.overlap ())
+    return false;
+
+  /* For convenience.  */
+  const builtin_memref &dstref = *acs.dstref;
+  const builtin_memref &srcref = *acs.srcref;
+
+  /* Determine the range of offsets and sizes of the overlap if it
+     exists and issue diagnostics.  */
+  HOST_WIDE_INT *ovloff = acs.ovloff;
+  HOST_WIDE_INT *ovlsiz = acs.ovlsiz;
+  HOST_WIDE_INT *sizrange = acs.sizrange;
+
+  tree func = gimple_call_fndecl (call);
+
+  /* To avoid a combinatorial explosion of diagnostics format the offsets
+     or their ranges as strings and use them in the warning calls below.  */
+  char offstr[3][64];
+
+  if (dstref.offrange[0] == dstref.offrange[1]
+      || dstref.offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr[0], "%lli", (long long) dstref.offrange[0].to_shwi ());
+  else
+    sprintf (offstr[0], "[%lli, %lli]",
+             (long long) dstref.offrange[0].to_shwi (),
+             (long long) dstref.offrange[1].to_shwi ());
+
+  if (srcref.offrange[0] == srcref.offrange[1]
+      || srcref.offrange[1] > HOST_WIDE_INT_MAX)
+    sprintf (offstr[1], "%lli", (long long) srcref.offrange[0].to_shwi ());
+  else
+    sprintf (offstr[1], "[%lli, %lli]",
+	     (long long) srcref.offrange[0].to_shwi (),
+             (long long) srcref.offrange[1].to_shwi ());
+
+  if (ovloff[0] == ovloff[1] || !ovloff[1])
+    sprintf (offstr[2], "%lli", (long long) ovloff[0]);
+  else
+    sprintf (offstr[2], "[%lli, %lli]",
+	     (long long) ovloff[0], (long long) ovloff[1]);
+
+  const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+  bool must_overlap = ovlsiz[0] > 0;
+
+  if (ovlsiz[1] == 0)
+    ovlsiz[1] = ovlsiz[0];
+
+  if (must_overlap)
+    {
+      /* Issue definitive "overlaps" diagnostic in this block.  */
+
+      if (sizrange[0] == sizrange[1])
+	{
+	  if (ovlsiz[0] == ovlsiz[1])
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? (ovlsiz[0] == 1
+			   ? G_("%G%qD accessing %wu byte at offsets %s "
+				"and %s overlaps %wu byte at offset %s")
+			   :  G_("%G%qD accessing %wu byte at offsets %s "
+				 "and %s overlaps %wu bytes at offset "
+				 "%s"))
+			: (ovlsiz[0] == 1
+			   ? G_("%G%qD accessing %wu bytes at offsets %s "
+				"and %s overlaps %wu byte at offset %s")
+			   : G_("%G%qD accessing %wu bytes at offsets %s "
+				"and %s overlaps %wu bytes at offset "
+				"%s")),
+			call, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? G_("%G%qD accessing %wu byte at offsets %s "
+			     "and %s overlaps between %wu and %wu bytes "
+			     "at offset %s")
+			: G_("%G%qD accessing %wu bytes at offsets %s "
+			     "and %s overlaps between %wu and %wu bytes "
+			     "at offset %s"),
+			call, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+			offstr[2]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[0] == 1
+			? G_("%G%qD accessing %wu byte at offsets %s and "
+			     "%s overlaps %wu or more bytes at offset %s")
+			: G_("%G%qD accessing %wu bytes at offsets %s and "
+			     "%s overlaps %wu or more bytes at offset %s"),
+			call, func, sizrange[0],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  return true;
+	}
+
+      if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+	{
+	  if (ovlsiz[0] == ovlsiz[1])
+	    warning_at (loc, OPT_Wrestrict,
+			ovlsiz[0] == 1
+			? G_("%G%qD accessing between %wu and %wu bytes "
+			     "at offsets %s and %s overlaps %wu byte at "
+			     "offset %s")
+			: G_("%G%qD accessing between %wu and %wu bytes "
+			     "at offsets %s and %s overlaps %wu bytes "
+			     "at offset %s"),
+			call, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	    warning_at (loc, OPT_Wrestrict,
+			"%G%qD accessing between %wu and %wu bytes at "
+			"offsets %s and %s overlaps between %wu and %wu "
+			"bytes at offset %s",
+			call, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], ovlsiz[1],
+			offstr[2]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			"%G%qD accessing between %wu and %wu bytes at "
+			"offsets %s and %s overlaps %wu or more bytes "
+			"at offset %s",
+			call, func, sizrange[0], sizrange[1],
+			offstr[0], offstr[1], ovlsiz[0], offstr[2]);
+	  return true;
+	}
+
+      if (ovlsiz[0] != ovlsiz[1])
+	ovlsiz[1] = maxobjsize.to_shwi ();
+
+      if (ovlsiz[0] == ovlsiz[1])
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[0] == 1
+		    ? G_("%G%qD accessing %wu or more bytes at offsets "
+			 "%s and %s overlaps %wu byte at offset %s")
+		    :  G_("%G%qD accessing %wu or more bytes at offsets "
+			  "%s and %s overlaps %wu bytes at offset %s"),
+		    call, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], offstr[2]);
+      else if (ovlsiz[1] >= 0 && ovlsiz[1] < maxobjsize.to_shwi ())
+	warning_at (loc, OPT_Wrestrict,
+		    "%G%qD accessing %wu or more bytes at offsets %s "
+		    "and %s overlaps between %wu and %wu bytes "
+		    "at offset %s",
+		    call, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], ovlsiz[1], offstr[2]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    "%G%qD accessing %wu or more bytes at offsets %s "
+		    "and %s overlaps %wu or more bytes at offset %s",
+		    call, func, sizrange[0], offstr[0], offstr[1],
+		    ovlsiz[0], offstr[2]);
+      return true;
+    }
+
+  /* Issue "may overlap" diagnostics below.  */
+  gcc_assert (ovlsiz[0] == 0
+	      && ovlsiz[1] > 0
+	      && ovlsiz[1] <= maxobjsize.to_shwi ());
+
+  /* Use more concise wording when one of the offsets is unbounded
+     to avoid confusing the user with large and mostly meaningless
+     numbers.  */
+  bool open_range = ((dstref.offrange[0] == -maxobjsize - 1
+		      && dstref.offrange[1] == maxobjsize)
+		     || (srcref.offrange[0] == -maxobjsize - 1
+			 && srcref.offrange[1] == maxobjsize));
+
+  if (sizrange[0] == sizrange[1] || sizrange[1] == 1)
+    {
+      if (ovlsiz[1] == 1)
+	{
+	  if (open_range)
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[1] == 1
+			? G_("%G%qD accessing %wu byte may overlap "
+			     "%wu byte")
+			: G_("%G%qD accessing %wu bytes may overlap "
+			     "%wu byte"),
+			call, func, sizrange[1], ovlsiz[1]);
+	  else
+	    warning_at (loc, OPT_Wrestrict,
+			sizrange[1] == 1
+			? G_("%G%qD accessing %wu byte at offsets %s "
+			     "and %s may overlap %wu byte at offset %s")
+			: G_("%G%qD accessing %wu bytes at offsets %s "
+			     "and %s may overlap %wu byte at offset %s"),
+			call, func, sizrange[1], offstr[0], offstr[1],
+			ovlsiz[1], offstr[2]);
+	  return true;
+	}
+
+      if (open_range)
+	warning_at (loc, OPT_Wrestrict,
+		    sizrange[1] == 1
+		    ? G_("%G%qD accessing %wu byte may overlap "
+			 "up to %wu bytes")
+		    : G_("%G%qD accessing %wu bytes may overlap "
+			 "up to %wu bytes"),
+		    call, func, sizrange[1], ovlsiz[1]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    sizrange[1] == 1
+		    ? G_("%G%qD accessing %wu byte at offsets %s and "
+			 "%s may overlap up to %wu bytes at offset %s")
+		    : G_("%G%qD accessing %wu bytes at offsets %s and "
+			 "%s may overlap up to %wu bytes at offset %s"),
+		    call, func, sizrange[1], offstr[0], offstr[1],
+		    ovlsiz[1], offstr[2]);
+      return true;
+    }
+
+  if (sizrange[1] >= 0 && sizrange[1] < maxobjsize.to_shwi ())
+    {
+      if (open_range)
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[1] == 1
+		    ? G_("%G%qD accessing between %wu and %wu bytes "
+			 "may overlap %wu byte")
+		    : G_("%G%qD accessing between %wu and %wu bytes "
+			 "may overlap up to %wu bytes"),
+		    call, func, sizrange[0], sizrange[1], ovlsiz[1]);
+      else
+	warning_at (loc, OPT_Wrestrict,
+		    ovlsiz[1] == 1
+		    ? G_("%G%qD accessing between %wu and %wu bytes "
+			 "at offsets %s and %s may overlap %wu byte "
+			 "at offset %s")
+		    : G_("%G%qD accessing between %wu and %wu bytes "
+			 "at offsets %s and %s may overlap up to %wu "
+			 "bytes at offset %s"),
+		    call, func, sizrange[0], sizrange[1],
+		    offstr[0], offstr[1], ovlsiz[1], offstr[2]);
+      return true;
+    }
+
+  warning_at (loc, OPT_Wrestrict,
+	      ovlsiz[1] == 1
+	      ? G_("%G%qD accessing %wu or more bytes at offsets %s "
+		   "and %s may overlap %wu byte at offset %s")
+	      : G_("%G%qD accessing %wu or more bytes at offsets %s "
+		   "and %s may overlap up to %wu bytes at offset %s"),
+	      call, func, sizrange[0], offstr[0], offstr[1],
+	      ovlsiz[1], offstr[2]);
+
+  return true;
+}
+
+/* Validate REF offsets in an EXPRession passed as an argument to a CALL
+   to a built-in function FUNC to make sure they are within the bounds
+   of the referenced object if its size is known, or PTRDIFF_MAX otherwise.
+   Both initial values of the offsets and their final value computed by
+   the function by incrementing the initial value by the size are
+   validated.  Return true if the offsets are not valid and a diagnostic
+   has been issued.  */
+
+static bool
+maybe_diag_offset_bounds (location_t loc, gcall *call, tree func, int strict,
+			  tree expr, const builtin_memref &ref)
+{
+  if (!warn_array_bounds)
+    return false;
+
+  offset_int ooboff[] = { ref.offrange[0], ref.offrange[1] };
+  tree oobref = ref.offset_out_of_bounds (strict, ooboff);
+  if (!oobref)
+    return false;
+
+  if (EXPR_HAS_LOCATION (expr))
+    loc = EXPR_LOCATION (expr);
+
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  tree type;
+
+  char rangestr[2][64];
+  if (ooboff[0] == ooboff[1]
+      || (ooboff[0] != ref.offrange[0]
+	  && ooboff[0].to_shwi () >= ooboff[1].to_shwi ()))
+    sprintf (rangestr[0], "%lli", (long long) ooboff[0].to_shwi ());
+  else
+    sprintf (rangestr[0], "[%lli, %lli]",
+	     (long long) ooboff[0].to_shwi (),
+	     (long long) ooboff[1].to_shwi ());
+
+  if (oobref == error_mark_node)
+    {
+      if (ref.sizrange[0] == ref.sizrange[1])
+	sprintf (rangestr[1], "%lli", (long long) ref.sizrange[0].to_shwi ());
+      else
+	sprintf (rangestr[1], "[%lli, %lli]",
+		 (long long) ref.sizrange[0].to_shwi (),
+		 (long long) ref.sizrange[1].to_shwi ());
+
+      if (DECL_P (ref.base)
+	  && TREE_CODE (type = TREE_TYPE (ref.base)) == ARRAY_TYPE)
+	{
+	  if (warning_at (loc, OPT_Warray_bounds,
+			  "%G%qD pointer overflow between offset %s "
+			  "and size %s accessing array %qD with type %qT",
+			  call, func, rangestr[0], rangestr[1], ref.base, type))
+	    inform (DECL_SOURCE_LOCATION (ref.base),
+		    "array %qD declared here", ref.base);
+	  else
+	    warning_at (loc, OPT_Warray_bounds,
+			"%G%qD pointer overflow between offset %s "
+			"and size %s",
+			call, func, rangestr[0], rangestr[1]);
+	}
+      else
+	warning_at (loc, OPT_Warray_bounds,
+		    "%G%qD pointer overflow between offset %s "
+		    "and size %s",
+		    call, func, rangestr[0], rangestr[1]);
+    }
+  else if (oobref == ref.base)
+    {
+      const offset_int maxobjsize = tree_to_shwi (max_object_size ());
+
+      /* True when the offset formed by an access to the reference
+	 is out of bounds, rather than the initial offset wich is
+	 in bounds.  This implies access past the end.  */
+      bool form = ooboff[0] != ref.offrange[0];
+
+      if (DECL_P (ref.base))
+	{
+	  if ((ref.basesize < maxobjsize
+	       && warning_at (loc, OPT_Warray_bounds,
+			      form
+			      ? G_("%G%qD forming offset %s is out of "
+				   "the bounds [0, %wu] of object %qD with "
+				   "type %qT")
+			      : G_("%G%qD offset %s is out of the bounds "
+				   "[0, %wu] of object %qD with type %qT"),
+			      call, func, rangestr[0], ref.basesize.to_uhwi (),
+			      ref.base, TREE_TYPE (ref.base)))
+	      || warning_at (loc, OPT_Warray_bounds,
+			     form
+			     ? G_("%G%qD forming offset %s is out of "
+				  "the bounds of object %qD with type %qT")
+			     : G_("%G%qD offset %s is out of the bounds "
+				  "of object %qD with type %qT"),
+			     call, func, rangestr[0],
+			     ref.base, TREE_TYPE (ref.base)))
+	    inform (DECL_SOURCE_LOCATION (ref.base),
+		    "%qD declared here", ref.base);
+	}
+      else if (ref.basesize < maxobjsize)
+	warning_at (loc, OPT_Warray_bounds,
+		    form
+		    ? G_("%G%qD forming offset %s is out of the bounds "
+			 "[0, %wu]")
+		    : G_("%G%qD offset %s is out of the bounds [0, %wu]"),
+		    call, func, rangestr[0], ref.basesize.to_uhwi ());
+      else
+	warning_at (loc, OPT_Warray_bounds,
+		    form
+		    ? G_("%G%qD forming offset %s is out of bounds")
+		    : G_("%G%qD offset %s is out of bounds"),
+		    call, func, rangestr[0]);	
+    }
+  else if (TREE_CODE (ref.ref) == MEM_REF)
+    {
+      tree type = TREE_TYPE (TREE_OPERAND (ref.ref, 0));
+      if (POINTER_TYPE_P (type))
+	type = TREE_TYPE (type);
+      type = TYPE_MAIN_VARIANT (type);
+
+      warning_at (loc, OPT_Warray_bounds,
+		  "%G%qD offset %s from the object at %qE is out "
+		  "of the bounds of %qT",
+		  call, func, rangestr[0], ref.base, type);
+    }
+  else
+    {
+      type = TYPE_MAIN_VARIANT (TREE_TYPE (ref.ref));
+
+      warning_at (loc, OPT_Warray_bounds,
+		"%G%qD offset %s from the object at %qE is out "
+		"of the bounds of referenced subobject %qD with type %qT "
+		"at offset %wu",
+		call, func, rangestr[0], ref.base, TREE_OPERAND (ref.ref, 1),
+		type, ref.refoff.to_uhwi ());
+    }
+
+  return true;
+}
+
+/* Check a CALL statement for restrict-violations and issue warnings
+   if/when appropriate.  */
+
+void
+wrestrict_dom_walker::check_call (gcall *call)
+{
+  /* Avoid checking the call if it has already been diagnosed for
+     some reason.  */
+  if (gimple_no_warning_p (call))
+    return;
+
+  tree func = gimple_call_fndecl (call);
+  if (!func || DECL_BUILT_IN_CLASS (func) != BUILT_IN_NORMAL)
+    return;
+
+  bool with_bounds = gimple_call_with_bounds_p (call);
+
+  /* Argument number to extract from the call (depends on the built-in
+     and its kind).  */
+  unsigned dst_idx = -1;
+  unsigned src_idx = -1;
+  unsigned bnd_idx = -1;
+
+  /* Is this CALL to a string function (as opposed to one to a raw
+     memory function).  */
+  bool strfun = true;
+
+  switch (DECL_FUNCTION_CODE (func))
+    {
+    case BUILT_IN_MEMCPY:
+    case BUILT_IN_MEMCPY_CHK:
+    case BUILT_IN_MEMCPY_CHKP:
+    case BUILT_IN_MEMCPY_CHK_CHKP:
+    case BUILT_IN_MEMPCPY:
+    case BUILT_IN_MEMPCPY_CHK:
+    case BUILT_IN_MEMPCPY_CHKP:
+    case BUILT_IN_MEMPCPY_CHK_CHKP:
+    case BUILT_IN_MEMMOVE:
+    case BUILT_IN_MEMMOVE_CHK:
+    case BUILT_IN_MEMMOVE_CHKP:
+    case BUILT_IN_MEMMOVE_CHK_CHKP:
+      strfun = false;
+      /* Fall through.  */
+
+    case BUILT_IN_STPNCPY:
+    case BUILT_IN_STPNCPY_CHK:
+    case BUILT_IN_STRNCAT:
+    case BUILT_IN_STRNCAT_CHK:
+    case BUILT_IN_STRNCPY:
+    case BUILT_IN_STRNCPY_CHK:
+      dst_idx = 0;
+      src_idx = 1 + with_bounds;
+      bnd_idx = 2 + 2 * with_bounds;
+      break;
+
+    case BUILT_IN_STPCPY:
+    case BUILT_IN_STPCPY_CHK:
+    case BUILT_IN_STPCPY_CHKP:
+    case BUILT_IN_STPCPY_CHK_CHKP:
+    case BUILT_IN_STRCPY:
+    case BUILT_IN_STRCPY_CHK:
+    case BUILT_IN_STRCPY_CHKP:
+    case BUILT_IN_STRCPY_CHK_CHKP:
+    case BUILT_IN_STRCAT:
+    case BUILT_IN_STRCAT_CHK:
+    case BUILT_IN_STRCAT_CHKP:
+    case BUILT_IN_STRCAT_CHK_CHKP:
+      dst_idx = 0;
+      src_idx = 1 + with_bounds;
+      break;
+
+    default:
+      /* Handle other string functions here whose access may need
+	 to be validated for in-bounds offsets and non-overlapping
+	 copies.  (Not all _chkp functions have BUILT_IN_XXX_CHKP
+	 macros so they need to be handled here.)  */
+      return;
+    }
+
+  unsigned nargs = gimple_call_num_args (call);
+
+  tree dst = dst_idx < nargs ? gimple_call_arg (call, dst_idx) : NULL_TREE;
+  tree src = src_idx < nargs ? gimple_call_arg (call, src_idx) : NULL_TREE;
+  tree dstwr = bnd_idx < nargs ? gimple_call_arg (call, bnd_idx) : NULL_TREE;
+
+  /* For string functions with an unspecified or unknown bound,
+     assume the size of the access is one.  */
+  if (!dstwr && strfun)
+    dstwr = size_one_node;
+
+  if (check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
+    return;
+
+  /* Avoid diagnosing the call again.  */
+  gimple_set_no_warning (call, true);
+}
+
+} /* anonymous namespace */
+
+/* Attempt to detect and diagnose invalid offset bounds and (except for
+   memmove) overlapping copy in a call expression EXPR from SRC to DST
+   and DSTSIZE and SRCSIZE bytes, respectively.  Both DSTSIZE and
+   SRCSIZE may be NULL.  Return false when one or the other has been
+   detected and diagnosed, true otherwise.  */
+
+bool
+check_bounds_or_overlap (gcall *call, tree dst, tree src, tree dstsize,
+			 tree srcsize, bool bounds_only /* = false */)
+{
+  location_t loc = gimple_location (call);
+
+  if (tree block = gimple_block (call))
+    if (location_t *pbloc = block_nonartificial_location (block))
+      loc = *pbloc;
+
+  loc = expansion_point_location_if_in_system_header (loc);
+
+  tree func = gimple_call_fndecl (call);
+
+  builtin_memref dstref (dst, dstsize);
+  builtin_memref srcref (src, srcsize);
+
+  builtin_access acs (call, dstref, srcref);
+
+  /* Set STRICT to the value of the -Warray-bounds=N argument for
+     string functions or when N > 1.  */
+  int strict = (acs.strict () || warn_array_bounds > 1 ? warn_array_bounds : 0);
+
+  /* Validate offsets first to make sure they are within the bounds
+     of the destination object if its size is known, or PTRDIFF_MAX
+     otherwise.  */
+  if (maybe_diag_offset_bounds (loc, call, func, strict, dst, dstref)
+      || maybe_diag_offset_bounds (loc, call, func, strict, src, srcref))
+    {
+      gimple_set_no_warning (call, true);
+      return false;
+    }
+
+  bool check_overlap
+    = (warn_restrict
+       && (bounds_only
+	   || (DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE
+	       && DECL_FUNCTION_CODE (func) != BUILT_IN_MEMMOVE_CHK)));
+
+  if (!check_overlap)
+    return true;
+
+  if (operand_equal_p (dst, src, 0))
+    {
+      warning_at (loc, OPT_Wrestrict,
+		  "%G%qD source argument is the same as destination",
+		  call, func);
+      gimple_set_no_warning (call, true);
+      return false;
+    }
+
+  /* Return false when overlap has been detected.  */
+  if (maybe_diag_overlap (loc, call, acs))
+    {
+      gimple_set_no_warning (call, true);
+      return false;
+    }
+
+  return true;
+}
+
+gimple_opt_pass *
+make_pass_warn_restrict (gcc::context *ctxt)
+{
+  return new pass_wrestrict (ctxt);
+}
diff --git a/gcc/gimple-ssa-warn-restrict.h b/gcc/gimple-ssa-warn-restrict.h
new file mode 100644
index 0000000..02581aa
--- /dev/null
+++ b/gcc/gimple-ssa-warn-restrict.h
@@ -0,0 +1,26 @@
+/* Warn on violations of the restrict qualifier.
+   Copyright (C) 2017 Free Software Foundation, Inc.
+   Contributed by Martin Sebor <msebor@redhat.com>.
+
+   This file is part of GCC.
+
+   GCC is free software; you can redistribute it and/or modify it under
+   the terms of the GNU General Public License as published by the Free
+   Software Foundation; either version 3, or (at your option) any later
+   version.
+
+   GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+   WARRANTY; without even the implied warranty of MERCHANTABILITY or
+   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+   for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with GCC; see the file COPYING3.  If not see
+   <http://www.gnu.org/licenses/>.  */
+
+#ifndef GIMPLE_SSA_WARN_RESTRICT_H
+
+extern bool check_bounds_or_overlap (gcall *, tree, tree, tree, tree,
+				     bool = false);
+
+#endif /* GIMPLE_SSA_WARN_RESTRICT_H */
diff --git a/gcc/passes.def b/gcc/passes.def
index 06dcd19..67adae5 100644
--- a/gcc/passes.def
+++ b/gcc/passes.def
@@ -332,6 +332,7 @@ along with GCC; see the file COPYING3.  If not see
 	 run the full propagators, run a specialized pass which
 	 only examines PHIs to discover const/copy propagation
 	 opportunities.  */
+      NEXT_PASS (pass_warn_restrict);
       NEXT_PASS (pass_phi_only_cprop);
       NEXT_PASS (pass_dse);
       NEXT_PASS (pass_cd_dce);
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-2.c b/gcc/testsuite/c-c++-common/Warray-bounds-2.c
new file mode 100644
index 0000000..becb3d4
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-bounds-2.c
@@ -0,0 +1,204 @@
+/* Test to exercise that -Warray-bounds warnings for memory and sring
+   functions are issued even when they are declared in system headers
+   (i.e., not just when they are explicitly declared in the source
+   file.)
+   Also verify that the warnings are issued even for calls where the
+   source of the excessive array bound is in a different function than
+   the call.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds -Wno-stringop-overflow" } */
+
+#include <stddef.h>
+#include <string.h>
+
+#define MAX  (__SIZE_MAX__ / 2)
+
+void sink (void*);
+
+struct __attribute__ ((packed)) Array
+{
+  char a13[13];
+  char a15[15];
+  char a17[17];
+};
+
+/* Exercise memcpy out-of-bounds offsets with an array of known size.  */
+
+void wrap_memcpy_src_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d, s + i, n);   /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_src_xsize (char *d, size_t n)
+{
+  struct Array ar;
+  sink (&ar);
+  wrap_memcpy_src_xsize (d, ar.a13, 46, n);
+  sink (&ar);
+}
+
+/* Exercise memcpy out-of-bounds offsets with an array of unknown size.  */
+
+void wrap_memcpy_src_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d, s + i, n);   /* { dg-warning "pointer overflow between offset \[0-9\]+ and size 3" "memcpy" } */
+}
+
+void call_memcpy_src_diff_max (char *d, const char *s, size_t n)
+{
+  wrap_memcpy_src_diff_max (d, s, MAX, 3);
+}
+
+void wrap_memcpy_dst_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d + i, s, n);   /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar1. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_dst_xsize (const char *s, size_t n)
+{
+  struct Array ar1;       /* { dg-message ".ar1. declared here" } */
+  sink (&ar1);
+  wrap_memcpy_dst_xsize (ar1.a15, s, 34, n);
+  sink (&ar1);
+}
+
+void wrap_memcpy_dst_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  memcpy (d + i, s, n);   /* { dg-warning "offset -?\[0-9\]+ is out of the bounds \\\[0, 45] of object .ar2. with type .(struct )?Array." "memcpy" } */
+}
+
+void call_memcpy_dst_diff_max (const char *s, size_t n)
+{
+  struct Array ar2;       /* { dg-message ".ar2. declared here" } */
+  sink (&ar2);
+  wrap_memcpy_dst_diff_max (ar2.a15, s, MAX, n);
+  sink (&ar2);
+}
+
+
+void wrap_strcat_src_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcat (d, s + i);   /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar3. with type .(struct )?Array." "strcat" } */
+}
+
+void call_strcat_src_xsize (char *d)
+{
+  struct Array ar3;       /* { dg-message ".ar3. declared here" } */
+  sink (&ar3);
+  wrap_strcat_src_xsize (d, ar3.a15, 15 + 17 + 1);
+  sink (&ar3);
+}
+
+void wrap_strcat_dst_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcat (d + i, s);   /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar4. with type .(struct )?Array." "strcat" } */
+}
+
+void call_strcat_dst_xsize (const char *s)
+{
+  struct Array ar4;       /* { dg-message ".ar4. declared here" } */
+  sink (&ar4);
+  wrap_strcat_dst_xsize (ar4.a15, s, 15 + 17 + 2);
+  sink (&ar4);
+}
+
+
+void wrap_strcpy_src_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcpy (d, s + i);   /* { dg-warning "offset 48 is out of the bounds \\\[0, 45] of object .ar5. with type .(struct )?Array." "strcpy" } */
+}
+
+void call_strcpy_src_xsize (char *d)
+{
+  struct Array ar5;       /* { dg-message ".ar5. declared here" } */
+  sink (&ar5);
+  wrap_strcpy_src_xsize (d, ar5.a15, 15 + 17 + 3);
+  sink (&ar5);
+}
+
+void wrap_strcpy_dst_xsize (char *d, const char *s, ptrdiff_t i)
+{
+  strcpy (d + i, s);   /* { dg-warning "offset 49 is out of the bounds \\\[0, 45] of object .ar6. with type .(struct )?Array." "strcpy" } */
+}
+
+void call_strcpy_dst_xsize (const char *s)
+{
+  struct Array ar6;       /* { dg-message ".ar6. declared here" } */
+  sink (&ar6);
+  wrap_strcpy_dst_xsize (ar6.a15, s, 15 + 17 + 4);
+  sink (&ar6);
+}
+
+
+/* Exercise strncpy out-of-bounds offsets with an array of known size.  */
+
+void wrap_strncpy_src_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d, s + i, n);   /* { dg-warning "offset 46 is out of the bounds \\\[0, 45] of object .ar7. with type '(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_src_xsize (char *d, size_t n)
+{
+  struct Array ar7;       /* { dg-message ".ar7. declared here" } */
+  sink (&ar7);
+  wrap_strncpy_src_xsize (d, ar7.a17, 17 + 1, n);
+  sink (&ar7);
+}
+
+/* Exercise strncpy out-of-bounds offsets with an array of unknown size.  */
+
+void wrap_strncpy_src_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  /* Unlike in the similar call to memcpy(), there is no pointer
+     overflow here because the size N is not added to the source
+     offset.  */
+  strncpy (d, s + i, n);
+}
+
+void call_strncpy_src_diff_max (char *d, const char *s, size_t n)
+{
+  wrap_strncpy_src_diff_max (d, s, MAX, 3);
+}
+
+void wrap_strncpy_dst_xsize (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d + i, s, n);   /* { dg-warning "offset 47 is out of the bounds \\\[0, 45] of object .ar8. with type .(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_dst_xsize (const char *s, size_t n)
+{
+  struct Array ar8;       /* { dg-message ".ar8. declared here" } */
+  sink (&ar8);
+  wrap_strncpy_dst_xsize (ar8.a17, s, 17 + 2, n);
+  sink (&ar8);
+}
+
+void wrap_strncpy_dst_diff_max (char *d, const char *s, ptrdiff_t i, size_t n)
+{
+  strncpy (d + i, s, n);   /* { dg-warning "offset -\[0-9\]+ is out of the bounds \\\[0, 45] of object .ar9. with type .(struct )?Array." "strncpy" } */
+}
+
+void call_strncpy_dst_diff_max (const char *s, size_t n)
+{
+  struct Array ar9;       /* { dg-message ".ar9. declared here" } */
+  sink (&ar9);
+  wrap_strncpy_dst_diff_max (ar9.a17, s, MAX, n);
+  sink (&ar9);
+}
+
+void wrap_strncpy_dstarray_diff_neg (char *d, const char *s, ptrdiff_t i,
+				     size_t n)
+{
+  strncpy (d + i, s, n);   /* { dg-warning "offset -\[0-9\]+ is out of the bounds \\\[0, 90] of object .ar10. with type .(struct )?Array ?\\\[2]." "strncpy" } */
+}
+
+void call_strncpy_dstarray_diff_neg (const char *s, size_t n)
+{
+  struct Array ar10[2];    /* { dg-message ".ar10. declared here" } */
+  sink (&ar10);
+
+  int off = (char*)ar10[1].a17 - (char*)ar10 + 1;
+  wrap_strncpy_dstarray_diff_neg (ar10[1].a17, s, -off, n);
+
+  sink (&ar10);
+}
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-3.c b/gcc/testsuite/c-c++-common/Warray-bounds-3.c
new file mode 100644
index 0000000..8bb4f1c
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-bounds-3.c
@@ -0,0 +1,410 @@
+/* Exercise that -Warray-bounds is issued for out-of-bounds offsets
+   in calls to built-in functions.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" }  */
+
+#include "../gcc.dg/range.h"
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+void sink (void*, ...);
+
+#define CAT(x, y)      x ## y
+#define CONCAT(x, y)   CAT (x, y)
+#define UNIQUE_NAME(x) CONCAT(x, __LINE__)
+
+#define T(type, N, dst, src, n) do {		\
+    extern type UNIQUE_NAME (a)[N];		\
+    type *a = UNIQUE_NAME (a);			\
+    type *pd = (dst);				\
+    const type *ps = (src);			\
+    FUNC (pd, ps, n);				\
+    sink (a, pd, ps);				\
+  } while (0)
+
+
+void test_memcpy_bounds (char *d, const char *s, size_t n)
+{
+#define FUNC memcpy
+
+  /* Verify that invalid offsets into an array of known size are
+     detected.  */
+
+  T (char, 1, a + SR (DIFF_MIN, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds \\\[0, 1] of object \[^\n\r]* with type .char ?\\\[1]" } */
+  T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds \\\[0, 1] of object" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  T (char, 1, a + UR (0, 1), s, n);
+  T (char, 1, a + UR (0, 2), s, n);
+  T (char, 1, a + UR (1, 2), s, n);
+  T (char, 1, a + UR (2, 3), s, n);       /* { dg-warning "offset \\\[2, 3] is out of the bounds \\\[0, 1] of object " } */
+  T (char, 1, a + UR (2, DIFF_MAX), s, n);  /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds \\\[0, 1] of object " "memcpy" } */
+
+  /* Offsets in excess of DIFF_MAX are treated as negative even if
+     they appear as large positive in the source.  It would be nice
+     if they retained their type but unfortunately that's not how
+     it works so be prepared for both in case it even gets fixed.  */
+  T (char, 1, a + UR (3, SIZE_MAX - 1), s, n);   /* { dg-warning "offset \\\[3, -2] is out of the bounds \\\[0, 1] of object" "memcpy" } */
+
+  /* Verify that invalid offsets into an array of unknown size are
+     detected.  */
+  extern char arr[];
+  T (char, 1, arr + SR (DIFF_MIN,             0), s, n);
+  T (char, 1, arr + SR (DIFF_MIN + 1,        -1), s, n);   /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of object " "memcpy" } */
+  T (char, 1, arr + SR (DIFF_MIN,             1), s, n);
+  T (char, 1, arr + SR (DIFF_MIN,      DIFF_MAX), s, n);
+  T (char, 1, arr + SR (       -2,           -1), s, n);   /* { dg-warning "offset \\\[-2, -1] is out of the bounds of object " "memcpy" } */
+  T (char, 1, arr + SR (       -1,            0), s, n);
+  T (char, 1, arr + SR (       -1,            1), s, n);
+  T (char, 1, arr + SR (       -1, DIFF_MAX - 1), s, n);
+  T (char, 1, arr + SR (        0,            1), s, n);
+  T (char, 1, arr + SR (        0, DIFF_MAX - 1), s, n);
+  T (char, 1, arr + SR (        1,            2), s, n);
+  T (char, 1, arr + SR (        1, DIFF_MAX - 1), s, n);
+
+  /* Verify that all offsets via a pointer to an uknown object are
+     accepted.  */
+
+  /* Negative indices between [DIFF_MIN, DIFF_MAX] are valid since
+     the pointer to which the offset is applied can be at a positive
+     offset from the beginning of an object.  */
+  T (char, 1, d + SR (DIFF_MIN,             0), s, n);
+  T (char, 1, d + SR (DIFF_MIN,            -1), s, n);
+  T (char, 1, d + SR (DIFF_MIN,             1), s, n);
+  T (char, 1, d + SR (DIFF_MIN,  DIFF_MAX - 1), s, n);
+  T (char, 1, d + SR (       -2,           -1), s, n);
+  T (char, 1, d + SR (       -1,            0), s, n);
+  T (char, 1, d + SR (       -1,            1), s, n);
+  T (char, 1, d + SR (       -1, DIFF_MAX - 1), s, n);
+  T (char, 1, d + SR (        0,            1), s, n);
+  T (char, 1, d + SR (        0, DIFF_MAX - 1), s, n);
+  T (char, 1, d + SR (        1,            2), s, n);
+  T (char, 1, d + SR (        1, DIFF_MAX - 1), s, n);
+}
+
+/* Verify offsets in an anti-range.  */
+
+void test_memcpy_bounds_anti_range (char *d, const char *s, size_t n)
+{
+  T (char, 9, a, a + SAR (-2, -1), 3);
+  T (char, 9, a, a + SAR (-1,  1), 3);
+  T (char, 9, a, a + SAR ( 0,  1), 3);
+  T (char, 9, a, a + SAR ( 0,  2), 3);
+  T (char, 9, a, a + SAR ( 0,  3), 3);
+  T (char, 9, a, a + SAR ( 0,  4), 3);
+  T (char, 9, a, a + SAR ( 0,  5), 3);
+  /* The initial source range is valid but the final range after the access
+     has complete cannot be.  The value mentioned in the warning is the final
+     offset, i.e., 7 + 3.  Including the whole final range because would be
+     confusing (the upper bound would either be negative or a very large
+     positive number) so only the lower bound is included.  */
+  T (char, 9, a, a + SAR ( 0,  6), 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  /* This fails because the offset isn't represented as an SSA_NAME
+     but rather as a GIMPLE_PHI (offset, 0).  With some effort it is
+     possible to extract the range from the PHI but it's not implemented
+     (yet).  */
+  T (char, 9, a, a + SAR ( 1,  6), 3);   /* { dg-warning "forming offset \\\[9, 0] is out of the bounds \\\[0, 9] of object " "memcpy" { xfail *-*-* } } */
+
+  T (char, 9, a, a + SAR ( 2,  6), 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR ( 3,  6), 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  T (char, 9, a, a + SAR (-1,  7), 3);   /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR (-2,  8), 3);   /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a, a + SAR (-3,  7), 5);   /* { dg-warning "forming offset \\\[10, 13] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  T (char, 9, a + SAR (-2, -1), a, 3);
+  T (char, 9, a + SAR (-1,  1), a, 3);
+  T (char, 9, a + SAR ( 0,  1), a, 3);
+  T (char, 9, a + SAR ( 0,  2), a, 3);
+  T (char, 9, a + SAR ( 0,  3), a, 3);
+  T (char, 9, a + SAR ( 0,  6), a, 3);   /* { dg-warning "forming offset 10 is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a + SAR (-1,  7), a, 3);   /* { dg-warning "forming offset \\\[10, 11] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+  T (char, 9, a + SAR (-2,  8), a, 3);   /* { dg-warning "forming offset \\\[10, 12] is out of the bounds \\\[0, 9] of object " "memcpy" } */
+
+  ptrdiff_t i = SAR (DIFF_MIN + 1, DIFF_MAX - 4);
+  T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 1), 3);
+  T (char, 1, d, d + SAR (DIFF_MIN + 3, DIFF_MAX - 3), 5);
+}
+
+/* Verify that pointer overflow in the computation done by memcpy
+   (i.e., offset + size) is detected and diagnosed.  */
+
+void test_memcpy_overflow (char *d, const char *s, size_t n)
+{
+  extern char arr[];
+
+  /* Verify that offset overflow involving an array of unknown size
+     but known access size is detected.  This works except with small
+     sizes that are powers of 2 due to bug .  */
+  T (char, 1, arr + SR (DIFF_MAX - 1, DIFF_MAX), s, 1);
+  T (char, 1, arr + SR (DIFF_MAX - 1, DIFF_MAX), s, 2);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 2 accessing array " "bug " { xfail *-*-* } } */
+  T (char, 1, arr + SR (DIFF_MAX - 2, DIFF_MAX), s, 3);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 3 accessing array " "memcpy" } */
+  T (char, 1, arr + SR (DIFF_MAX - 4, DIFF_MAX), s, 5);  /* { dg-warning "pointer overflow between offset \\\[\[0-9\]+, \[0-9\]+] and size 5 accessing array " "memcpy" } */
+}
+
+void test_memcpy_bounds_memarray_range (void)
+{
+#undef TM
+#define TM(mem, dst, src, n)			\
+  do {						\
+    struct MA { char a5[5]; int i; } ma;	\
+    sink (&ma);   /* Initialize arrays.  */	\
+    memcpy (dst, src, n);			\
+    sink (&ma);					\
+  } while (0)
+
+  ptrdiff_t i = SR (1, 2);
+
+  TM (ma.a5, ma.a5 + i, ma.a5, 1);
+  TM (ma.a5, ma.a5 + i, ma.a5, 3);
+  TM (ma.a5, ma.a5 + i, ma.a5, 5);
+  TM (ma.a5, ma.a5 + i, ma.a5, 7);     /* diagnosed with -Warray-bounds=2 */
+}
+
+void test_memmove_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC memmove
+
+    T (char, 1, a + SR (DIFF_MIN + 1, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds \\\[0, 1] of object \[^\n\r]+ with type .char ?\\\[1]" } */
+  T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds \\\[0, 1] of object" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  const int *pi = (const int*)s;
+  T (int,  2, a + SR (-1, 1), pi, n);
+  T (int,  2, a + SR (-1, 2), pi, n);
+  T (int,  2, a + SR ( 0, 2), pi, n);
+  T (int,  2, a + SR ( 0, 3), pi, n);
+  T (int,  2, a + SR ( 1, 3), pi, n);
+  T (int,  2, a + SR ( 2, 3), pi, n);
+
+  T (int32_t, 2, a + SR ( 3, 4), pi, n);      /* { dg-warning "offset \\\[12, 16] is out of the bounds \\\[0, 8] of object .\[^\n\r]+. with type .int32_t ?\\\[2]." } */
+}
+
+
+void test_mempcpy_bounds (char *d, const char *s, size_t n)
+{
+#undef FUNC
+#define FUNC mempcpy
+
+  /* Verify that invalid offsets into an array of known size are
+     detected.  */
+
+  T (char, 1, a + SR (DIFF_MIN, -1), s, n);     /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds"  "mempcpy" } */
+T (char, 1, a + SR (-2, -1), s, n);     /* { dg-warning "offset \\\[-2, -1] is out of the bounds"  "mempcpy" } */
+  T (char, 1, a + SR (-2, 0), s, n);
+
+  T (char, 1, a + UR (0, 1), s, n);
+  T (char, 1, a + UR (0, 2), s, n);
+  T (char, 1, a + UR (1, 2), s, n);
+  T (char, 1, a + UR (2, 3), s, n);       /* { dg-warning "offset \\\[2, 3] is out of the bounds \\\[0, 1] of object "  "mempcpy" } */
+  T (char, 1, a + UR (2, DIFF_MAX), s, n);  /* { dg-warning "offset \\\[2, \[0-9\]+] is out of the bounds \\\[0, 1] of object"  "mempcpy" } */
+
+  /* Offsets in excess of DIFF_MAX are treated as negative even if
+     they appear as large positive in the source.  It would be nice
+     if they retained their type but unfortunately that's not how
+     it works so be prepared for both in case it ever gets fixed.  */
+  T (char, 1, a + UR (3, SIZE_MAX), s, n);   /* { dg-warning "offset \\\[3, -1] is out of the bounds \\\[0, 1] of object "  "mempcpy" } */
+
+  /* Verify that invalid offsets into an array of unknown size are
+     detected.  */
+  extern char arr[];
+  T (char, 1, arr + SR (DIFF_MIN,         0), s, n);
+  T (char, 1, arr + SR (DIFF_MIN,        -1), s, n);   /* { dg-warning "offset \\\[-\[0-9\]+, -1] is out of the bounds of object"  "mempcpy" } */
+  T (char, 1, arr + SR (DIFF_MIN,         1), s, n);
+  T (char, 1, arr + SR (DIFF_MIN, DIFF_MAX), s, n);
+  T (char, 1, arr + SR (       -2,        -1), s, n);   /* { dg-warning "offset \\\[-2, -1] is out of the bounds of object"  "mempcpy" } */
+  T (char, 1, arr + SR (       -1,         0), s, n);
+  T (char, 1, arr + SR (       -1,         1), s, n);
+  T (char, 1, arr + SR (       -1, DIFF_MAX), s, n);
+  T (char, 1, arr + SR (        0,         1), s, n);
+  T (char, 1, arr + SR (        0, DIFF_MAX), s, n);
+  T (char, 1, arr + SR (        1,         2), s, n);
+  T (char, 1, arr + SR (        1, DIFF_MAX), s, n);
+
+  /* Verify that all offsets via a pointer to an uknown object are
+     accepted.  */
+
+  /* Negative indices between [DIFF_MIN, DIFF_MAX] are valid since
+     the pointer to which the offset is applied can be at a positive
+     offset from the beginning of an object.  */
+  T (char, 1, d + SR (DIFF_MIN,         0), s, n);
+  T (char, 1, d + SR (DIFF_MIN,        -1), s, n);
+  T (char, 1, d + SR (DIFF_MIN,         1), s, n);
+  T (char, 1, d + SR (DIFF_MIN, DIFF_MAX), s, n);
+  T (char, 1, d + SR (       -2,        -1), s, n);
+  T (char, 1, d + SR (       -1,         0), s, n);
+  T (char, 1, d + SR (       -1,         1), s, n);
+  T (char, 1, d + SR (       -1, DIFF_MAX), s, n);
+  T (char, 1, d + SR (        0,         1), s, n);
+  T (char, 1, d + SR (        0, DIFF_MAX), s, n);
+  T (char, 1, d + SR (        1,         2), s, n);
+  T (char, 1, d + SR (        1, DIFF_MAX), s, n);
+}
+
+#define TI(type, N, init, dst, src) do {	\
+    type UNIQUE_NAME (a)[N] = init;		\
+    type *a = UNIQUE_NAME (a);			\
+    type *pd = (dst);				\
+    const type *ps = (src);			\
+    FUNC (pd, ps);				\
+    sink (a, pd, ps, s);			\
+  } while (0)
+
+void test_strcpy_bounds (char *d, const char *s)
+{
+#undef FUNC
+#define FUNC strcpy
+
+  ptrdiff_t i;
+
+  TI (char, 1, "",   a, a + SR (DIFF_MIN, 0));
+  TI (char, 1, "",   a, a + SR (-1, 0));
+  TI (char, 1, "",   a, a + SR (-1, 1));
+  TI (char, 1, "",   a, a + SR (0, 1));
+  TI (char, 1, "",   a, a + SR (0, DIFF_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (0, DIFF_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (1, DIFF_MAX - 1));
+  /* The following needs a warning for reading past the end.  */
+  TI (char, 2, "0",  a, a + SR (2, DIFF_MAX - 1));
+  TI (char, 2, "0",  a, a + SR (3, DIFF_MAX - 1));   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
+
+  TI (char, 3, "01", a, a + SR (0, DIFF_MAX - 1));
+  TI (char, 3, "01", a, a + SR (1, DIFF_MAX - 1));
+  TI (char, 3, "01", a, a + SR (2, DIFF_MAX - 1));
+  /* The following needs a warning for reading past the end.  */
+  TI (char, 3, "01", a, a + SR (3, DIFF_MAX - 1));
+  TI (char, 3, "01", a, a + SR (4, DIFF_MAX - 1));   /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
+
+  TI (char, 4, "012", a, a + SR (DIFF_MAX - 2, DIFF_MAX - 1));   /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds \\\[0, 4] of object \[^\n\r\]+ with type .char ?\\\[4\\\]."  "strcpy" } */
+
+
+  TI (char, 1, "", a + SR (DIFF_MIN, 0), s);
+  TI (char, 1, "", a + SR (-1, 0), s);
+  TI (char, 1, "", a + SR (-1, 1), s);
+  TI (char, 1, "", a + SR (0, 1), s);
+  TI (char, 1, "", a + SR (0, DIFF_MAX - 1), s);
+  TI (char, 2, "", a + SR (0, DIFF_MAX - 1), s);
+  TI (char, 2, "", a + SR (1, DIFF_MAX - 1), s);
+  /* The following is diagnosed not because the initial source offset
+     it out of bounds (it isn't) but because the final source offset
+     after the access has completed, is.  It would be clearer if
+     the warning mentioned the final offset.  */
+  TI (char, 2, "", a + SR (2, DIFF_MAX - 1), s);   /* { dg-warning "forming offset 3 is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
+  TI (char, 2, "", a + SR (3, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[3, \[0-9\]+] is out of the bounds \\\[0, 2] of object \[^\n\r\]+ with type .char ?\\\[2\\\]."  "strcpy" } */
+
+  TI (char, 3, "", a + SR (0, DIFF_MAX - 1), s);
+  TI (char, 3, "", a + SR (1, DIFF_MAX - 1), s);
+  TI (char, 3, "", a + SR (2, DIFF_MAX - 1), s);
+  TI (char, 3, "", a + SR (3, DIFF_MAX - 1), s);   /* { dg-warning "forming offset 4 is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
+  TI (char, 3, "", a + SR (4, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[4, \[0-9\]+] is out of the bounds \\\[0, 3] of object \[^\n\r\]+ with type .char ?\\\[3\\\]."  "strcpy" } */
+
+  TI (char, 4, "", a + SR (DIFF_MAX - 2, DIFF_MAX - 1), s);   /* { dg-warning "offset \\\[\[0-9\]+, \[0-9\]+] is out of the bounds \\\[0, 4] of object \[^\n\r\]+ with type .char ?\\\[4\\\]."  "strcpy" } */
+}
+
+struct MA
+{
+  int i;
+  char a5[5];
+  char a11[11];
+};
+
+struct MA2
+{
+  struct MA ma3[3];
+  struct MA ma5[5];
+  char ax[];
+};
+
+struct MA3
+{
+  struct MA2 ma5[3];
+  struct MA2 ma7[7];
+};
+
+void test_strcpy_bounds_memarray_range (void)
+{
+#undef TM
+#define TM(mem, init, dst, src)			\
+  do {						\
+    struct MA ma;				\
+    strcpy (ma.mem, init);			\
+    strcpy (dst, src);				\
+    sink (&ma);					\
+  } while (0)
+
+  ptrdiff_t i = SR (1, 2);
+
+  TM (a5, "0",    ma.a5 + i, ma.a5);
+  TM (a5, "01",   ma.a5 + i, ma.a5);
+  TM (a5, "012",  ma.a5 + i, ma.a5);
+  TM (a5, "0123", ma.a5 + i, ma.a5);     /* { dg-warning "offset 10 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char\\\[5]. at offset 4" "strcpy" { xfail *-*-* } } */
+
+  TM (a11, "0",       ma.a5, ma.a11);
+  TM (a11, "01",      ma.a5, ma.a11);
+  TM (a11, "012",     ma.a5, ma.a11);
+  TM (a11, "0123",    ma.a5, ma.a11);
+  TM (a11, "01234",   ma.a5, ma.a11);    /* { dg-warning "offset 10 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
+  TM (a11, "012345",  ma.a5, ma.a11);    /* { dg-warning "offset \\\[10, 11] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
+  TM (a11, "0123456", ma.a5, ma.a11);    /* { dg-warning "offset \\\[10, 12] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]' at offset 4" } */
+
+  TM (a11, "0123456", ma.a11 + i, "789abcd");
+}
+
+void test_strcpy_bounds_memarray_var (struct MA *pma,
+				      struct MA2 *pma2,
+				      struct MA3 *pma3,
+				      const char *s, size_t n)
+{
+#undef TM
+#define TM(dst, src) do {			\
+    strcpy (dst, src);				\
+    sink (dst, src);				\
+  } while (0)
+
+  TM (pma->a5, s);
+  TM (pma->a5 + 0, s);
+  TM (pma->a5 + 1, s);
+  TM (pma->a5 + 4, s);
+
+  /* The following forms a pointer during the call that's outside
+     the bounds of the array it was derived from (pma->a5) so
+     it should be diagnosed but the representation of the pointer
+     addition doesn't contain information to distinguish it from
+     the valid pma->a11 + 1 so this is an XFAIL.  */
+  TM (pma->a5 + 5, s);                 /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+  /* The following also forms an out-of-bounds pointer but similar
+     to the above, there is no reliable way to distinguish it from
+     (char*)&pma[1].i + 1 so this too is not diagnosed.  */
+  TM (pma->a5 + sizeof *pma + 1, s);   /* { dg-warning "offset 17 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+  TM (pma->a5 - 1, s);   /* { dg-warning "offset -1 from the object at .pma. is out of the bounds of .struct MA." "strcpy" { xfail *-*-* } } */
+
+  TM (pma[1].a5, s);
+  TM (pma[2].a5 + 0, s);
+  TM (pma[3].a5 + 1, s);
+  TM (pma[4].a5 + 4, s);
+
+
+  extern struct MA3 ma3[3];
+  TM (ma3[0].ma5[0].ma3[0].a5 + 6, s);
+}
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-4.c b/gcc/testsuite/c-c++-common/Warray-bounds-4.c
new file mode 100644
index 0000000..57b9ffa
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-bounds-4.c
@@ -0,0 +1,68 @@
+/* Exercise that -Warray-bounds is issued for out-of-bounds offsets
+   in calls to built-in functions.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds=2 -Wno-stringop-overflow -ftrack-macro-expansion=0" }  */
+
+#include "../gcc.dg/range.h"
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+struct MA { char a5[5], a7[7]; };
+
+void sink (void*, ...);
+
+void test_memcpy_bounds_memarray_range (void)
+{
+#undef TM
+#define TM(mem, dst, src, n)			\
+  do {						\
+    struct MA ma;				\
+    sink (&ma);   /* Initialize arrays.  */	\
+    memcpy (dst, src, n);			\
+    sink (&ma);					\
+  } while (0)
+
+  ptrdiff_t j = SR (1, 2);
+
+  TM (ma.a5, ma.a5 + j, ma.a5, 1);
+  TM (ma.a5, ma.a5 + j, ma.a5, 3);
+  TM (ma.a5, ma.a5 + j, ma.a5, 5);
+  TM (ma.a5, ma.a5 + j, ma.a5, 7);        /* { dg-warning "offset \\\[6, 8] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */
+  TM (ma.a5, ma.a5 + j, ma.a5, 9);        /* { dg-warning "offset \\\[6, 10] from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a5. with type .char ?\\\[5]. at offset 0" } */
+}
+
+void test_strcpy_bounds_memarray_range (void)
+{
+#undef TM
+#define TM(a5init, a7init, dst, src)		\
+  do {						\
+    struct MA ma = { a5init, a7init };		\
+    strcpy (dst, src);				\
+    sink (&ma);					\
+  } while (0)
+
+  ptrdiff_t i = SR (1, 2);
+
+  TM ("0", "",     ma.a5 + i, ma.a5);
+  TM ("01", "",    ma.a5 + i, ma.a5);
+  TM ("012", "",   ma.a5 + i, ma.a5);
+  TM ("0123", "",  ma.a5 + i, ma.a5);     /* { dg-warning "offset 6 from the object at .ma. is out of the bounds of referenced subobject .a5. with type .char\\\[5]. at offset 0" "strcpy" { xfail *-*-* } } */
+  TM ("", "012345", ma.a7 + i, ma.a7);    /* { dg-warning "offset 13 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a7. with type .char ?\\\[7]. at offset 5" } */
+}
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds-5.c b/gcc/testsuite/c-c++-common/Warray-bounds-5.c
new file mode 100644
index 0000000..d864a47
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Warray-bounds-5.c
@@ -0,0 +1,40 @@
+/* Exercise that -Warray-bounds is handled correctly for subobjects.
+   Test case derived from the halt_fast_timekeeper function in Linux
+   kernel/time/timekeeping.c.
+   { dg-do compile }
+   { dg-options "-O2 -Warray-bounds=2 -Wno-stringop-overflow -ftrack-macro-expansion=0" }  */
+
+struct A
+{
+  int i;
+  void *p;
+  int j;
+};
+
+struct B
+{
+  struct A a;
+
+  int i;
+};
+
+void sink (void*);
+
+static void halt_fast_timekeeper (struct B *b)
+{
+  static struct A a;
+
+  struct A *pa = &b->a;
+
+  __builtin_memcpy (&a, pa, sizeof *pa);   /* { dg-bogus "\\\[-Warray-bounds" } */
+  sink (&a);
+}
+
+struct C { int i; struct B b; } c;
+
+void timekeeping_suspend (void)
+{
+  struct B *p = &c.b;
+
+  halt_fast_timekeeper (p);
+}
diff --git a/gcc/testsuite/c-c++-common/Warray-bounds.c b/gcc/testsuite/c-c++-common/Warray-bounds.c
index 4b9d6aa..391e636 100644
--- a/gcc/testsuite/c-c++-common/Warray-bounds.c
+++ b/gcc/testsuite/c-c++-common/Warray-bounds.c
@@ -4,33 +4,10 @@
    { dg-require-effective-target alloca }
    { dg-options "-O2 -Warray-bounds -ftrack-macro-expansion=0" }  */
 
-#define SIZE_MAX  __SIZE_MAX__
-#define DIFF_MAX  __PTRDIFF_MAX__
-#define DIFF_MIN  (-DIFF_MAX - 1)
+#include "../gcc.dg/range.h"
 
 #define offsetof(T, m)   __builtin_offsetof (T, m)
 
-typedef __PTRDIFF_TYPE__ ssize_t;
-typedef __SIZE_TYPE__    size_t;
-
-extern ssize_t signed_value (void)
-{
-  extern volatile ssize_t signed_value_source;
-  return signed_value_source;
-}
-
-extern size_t unsigned_value (void)
-{
-  extern volatile size_t unsigned_value_source;
-  return unsigned_value_source;
-}
-
-ssize_t signed_range (ssize_t min, ssize_t max)
-{
-  ssize_t val = signed_value ();
-  return val < min || max < val ? min : val;
-}
-
 typedef struct AX { int n; char ax[]; } AX;
 
 typedef struct A1 { int i; char a1[1]; } A1;
@@ -98,7 +75,7 @@ void farr_s16_7 (void)
   T (ax_7[DIFF_MAX / 2][0]);              /* { dg-warning "array subscript \[0-9\]+ is above array bounds" } */
   T (ax_7[SIZE_MAX][0]);                  /* { dg-warning "array subscript \[0-9\]+ is above array bounds" } */
 
-  ssize_t i = R (DIFF_MIN, -1);
+  ptrdiff_t i = R (DIFF_MIN, -1);
   T (ax_7[i][0]);                         /* { dg-warning "array subscript -1 is below array bounds" } */
 
   T (ax_7[R (DIFF_MIN, -1)][0]);          /* { dg-warning "array subscript -1 is below array bounds" } */
diff --git a/gcc/testsuite/c-c++-common/Wrestrict-2.c b/gcc/testsuite/c-c++-common/Wrestrict-2.c
new file mode 100644
index 0000000..f440e7b
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict-2.c
@@ -0,0 +1,70 @@
+/* PR 35503 - Warn about restricted pointers
+   Test to exercise that -Wrestrict warnings are issued for memory and
+   sring functions when they are declared in system headers (i.e., not
+   just when they are explicitly declared in the source file.)
+   Also verify that the warnings are issued even for calls where the
+   source of the aliasing violation is in a different function than
+   the restricted call.
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict" } */
+
+#include <string.h>
+
+void wrap_memcpy (void *d, const void *s, size_t n)
+{
+  memcpy (d, s, n);   /* { dg-warning "source argument is the same as destination" "memcpy" } */
+}
+
+void call_memcpy (void *d, size_t n)
+{
+  const void *s = d;
+  wrap_memcpy (d, s, n);
+}
+
+
+void wrap_strcat (char *d, const char *s)
+{
+  strcat (d, s);   /* { dg-warning "source argument is the same as destination" "strcat" } */
+}
+
+void call_strcat (char *d)
+{
+  const char *s = d;
+  wrap_strcat (d, s);
+}
+
+
+void wrap_strcpy (char *d, const char *s)
+{
+  strcpy (d, s);   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+}
+
+void call_strcpy (char *d)
+{
+  const char *s = d;
+  wrap_strcpy (d, s);
+}
+
+
+void wrap_strncat (char *d, const char *s, size_t n)
+{
+  strncat (d, s, n);   /* { dg-warning "source argument is the same as destination" "strncat" } */
+}
+
+void call_strncat (char *d, size_t n)
+{
+  const char *s = d;
+  wrap_strncat (d, s, n);
+}
+
+
+void wrap_strncpy (char *d, const char *s, size_t n)
+{
+  strncpy (d, s, n);   /* { dg-warning "source argument is the same as destination" "strncpy" } */
+}
+
+void call_strncpy (char *d, size_t n)
+{
+  const char *s = d;
+  wrap_strncpy (d, s, n);
+}
diff --git a/gcc/testsuite/c-c++-common/Wrestrict.c b/gcc/testsuite/c-c++-common/Wrestrict.c
new file mode 100644
index 0000000..671497e
--- /dev/null
+++ b/gcc/testsuite/c-c++-common/Wrestrict.c
@@ -0,0 +1,992 @@
+/* PR 35503 - Warn about restricted pointers
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict -ftrack-macro-expansion=0" } */
+
+#include "../gcc.dg/range.h"
+
+#if !defined LINE
+# define LINE 0
+#endif
+
+#if __cplusplus
+#  define restrict __restrict
+extern "C" {
+#endif
+
+extern void* memcpy (void* restrict, const void* restrict, size_t);
+extern void* mempcpy (void* restrict, const void* restrict, size_t);
+extern void* memmove (void*, const void*, size_t);
+
+extern char* stpcpy (char* restrict, const char* restrict);
+
+extern char* strcat (char* restrict, const char* restrict);
+extern char* strcpy (char* restrict, const char* restrict);
+extern char* strncpy (char* restrict, const char* restrict, size_t);
+
+#if __cplusplus
+}   /* extern "C" */
+#endif
+
+void sink (void*, ...);
+
+struct MemArrays
+{
+  char a8[8];
+  char a16[16];
+  char ax[];
+};
+
+/* Exercise memcpy with constant or known arguments.  */
+
+void test_memcpy_cst (void *d, const void *s)
+{
+#undef T
+#define T(dst, src, n) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };	\
+      void *pd = (dst);					\
+      const void *ps = (src);				\
+      memcpy (pd, ps, (n));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T (a, a, 0);
+  T (a, s = a, 3);           /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  /* This isn't detected because memcpy calls with small power-of-2 sizes
+     are intentionally folded into safe copies equivalent to memmove.
+     It's marked xfail only because there is value in detecting such
+     invalid calls for portability, and as a reminder of why it isn't
+     diagnosed.  */
+  T (a, a + 1, 1);           /* { dg-warning "\\\[-Wrestrict" "memcpy with a small power of 2 size" { xfail *-*-* } } */
+  T (a, a + 3, 3);
+  T (a, a + 3, 5);           /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  {
+    char a[3] = { 1, 2, 3 };
+
+    /* Verify that a call to memcpy with an exact overlap is diagnosed
+       (also tested above) but an excplicit one to __builtin_memcpy is
+       not.  See bug 32667 for the rationale.  */
+    (memcpy)(a, a, sizeof a);   /* { dg-warning "source argument is the same as destination" "memcpy" } */
+    sink (a);
+
+    __builtin_memcpy (a, a, sizeof a);
+    sink (a);
+  }
+
+  {
+    char a[3][7];
+    sink (a);
+
+    void *d = a[0];
+    const void *s = a[1];
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0];
+    s = a[1];
+    /* The following is only diagnosed for sizes that aren't small
+       powers of 2.  */
+    memcpy (d, s, sizeof a[0] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0]);
+    sink (&a);
+
+    d = a[0] + 1;
+    s = a[1] + 1;
+    memcpy (d, s, sizeof a[0] + 2); /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&a);
+  }
+
+  {
+    struct {
+      char a[7];
+      char b[7];
+      char c[7];
+    } x;
+    sink (&x);
+
+    void *d = x.a;
+    const void *s = x.b;
+    memcpy (d, s, sizeof x.a);
+    sink (&x);
+
+    d = x.a;
+    s = x.a;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 4;
+    s = x.b;
+    memcpy (d, s, sizeof x.a);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 6;
+    s = x.b;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b;
+    memcpy (d, s, 3);             /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+
+    d = x.a + 7;
+    s = x.b + 1;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, 1);
+    sink (&x);
+
+    d = x.b;
+    s = x.a;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 1;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 2;
+    memcpy (d, s, sizeof x.b);
+    sink (&x);
+
+    d = x.b + 2;
+    s = x.a + 3;
+    memcpy (d, s, sizeof x.b);    /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+    sink (&x);
+  }
+
+  {
+#undef T
+#define T(dst, src, n) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };	\
+      memcpy ((dst), (src), (n));			\
+      sink (a);						\
+    }							\
+    } while (0)
+
+    /* Verify the offset of the overlap is the same regardless of whether
+       the destination is at lower or higher offset than the source.  */
+    T (a, a + 1, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 1 overlaps 4 bytes at offset 1" "memcpy" } */
+    T (a, a + 2, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "memcpy" } */
+    T (a, a + 3, 5);             /* { dg-warning "accessing 5 bytes at offsets 0 and 3 overlaps 2 bytes at offset 3" "memcpy" } */
+
+    T (a + 1, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 1 and 0 overlaps 4 bytes at offset 1" "memcpy" } */
+    T (a + 2, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 2 and 0 overlaps 3 bytes at offset 2" "memcpy" } */
+    T (a + 3, a, 5);             /* { dg-warning "accessing 5 bytes at offsets 3 and 0 overlaps 2 bytes at offset 3" "memcpy" } */
+  }
+}
+
+/* Exercise memcpy with destination or source offset or size in
+   a determinate range.  */
+
+void test_memcpy_range (char *d, size_t sz)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    if (!LINE || LINE == __LINE__) {		\
+      char a[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 };	\
+      void *pd = (dst);					\
+      const void *ps = (src);				\
+      memcpy (pd, ps, (n));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  ptrdiff_t ir = SR (2, 3);
+  T (a + ir, a, 0);
+  T (a + ir, a, 1);
+  T (a + ir, a, 2);
+  T (a + ir, a, 3);
+  /* The following fails because the size is a small power of 2.  */
+  T (a + ir, a, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps between 1 and 2 bytes at offset \\\[3, 2]" "memcpy" { xfail *-*-*} } */
+  T (a + ir, a, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[2, 3]" "memcpy" } */
+
+  T (d + ir, d, 0);
+  T (d + ir, d, 1);
+  T (d + ir, d, 2);
+  T (d + ir, d, 3);
+  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 1 byte at offset 3" "bug 79220" { xfail *-*-* } } */
+  T (d + ir, d, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[2, 3]" "memcpy" } */
+
+  /* Because the size is constant and a power of 2 the following is
+     folded too early to detect the overlap.  */
+  T (d + ir, d, 4);               /* { dg-warning "accessing 4 bytes at offsets \\\[2, 3] and 0 overlaps 2 byte at offset 2" "" { xfail *-*-* } } */
+  T (d + ir, d, 5);               /* { dg-warning "accessing 5 bytes at offsets \\\[2, 3] and 0 overlaps between 2 and 3 bytes at offset \\\[2, 3]" "memcpy" } */
+
+  /* Exercise the full range of size_t.  */
+  T (d + sz, d, 0);
+  T (d + sz, d, 1);
+  T (d + sz, d, 9);
+
+  T (a, a + 1, SR (0, 1));
+  T (a, a + 1, SR (0, 2));
+  T (a, a + 1, SR (1, 2));
+  T (a, a + 1, SR (2, 3));         /* { dg-warning "accessing between 2 and 3 bytes at offsets 0 and 1 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+  T (a, a + 1, UR (2, DIFF_MAX + (size_t)1));  /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a, a + 1, UR (2, SIZE_MAX - 1));        /* { dg-warning "accessing 2 or more bytes at offsets 0 and 1 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a, a + 2, SR (2, 3));
+  T (a, a + 2, SR (3, 4));         /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and 2 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+  T (a, a + 3, SR (3, 4));
+  T (a, a + 3, SR (4, 5));         /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and 3 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+  T (a, a + 3, SR (5, 6));         /* { dg-warning "accessing between 5 and 6 bytes at offsets 0 and 3 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+  T (a + 1, a, SR (0, 1));
+  T (a + 1, a, SR (0, 2));
+  T (a + 1, a, SR (1, 2));
+  T (a + 1, a, SR (2, 3));         /* { dg-warning "accessing between 2 and 3 bytes at offsets 1 and 0 overlaps between 1 and 2 bytes at offset 1" "memcpy" } */
+  T (a + 1, a, UR (2, DIFF_MAX + (size_t)1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a + 1, a, UR (2, SIZE_MAX - 1)); /* { dg-warning "accessing 2 or more bytes at offsets 1 and 0 overlaps 1 or more bytes at offset 1" "memcpy" } */
+  T (a + 2, a, SR (2, 3));
+  T (a + 2, a, SR (3, 4));         /* { dg-warning "accessing between 3 and 4 bytes at offsets 2 and 0 overlaps between 1 and 2 bytes at offset 2" "memcpy" } */
+  T (a + 3, a, SR (3, 4));
+  T (a + 3, a, SR (4, 5));         /* { dg-warning "accessing between 4 and 5 bytes at offsets 3 and 0 overlaps between 1 and 2 bytes at offset 3" "memcpy" } */
+  T (a + 3, a, SR (5, 6));         /* { dg-warning "accessing between 5 and 6 bytes at offsets 3 and 0 overlaps between 2 and 3 bytes at offset 3" "memcpy" } */
+
+  ir = SR (2, 5);
+  T (a, a + ir, 4);
+  T (a, a + ir, 5);                /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] overlaps between 1 and 3 bytes at offset \\\[2, 4]" "memcpy" } */
+  T (a, a + ir, 6);                /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] overlaps between 3 and 4 bytes at offset \\\[2, 3]" "memcpy" } */
+
+  /* Below, there are two possible regions for the source of the copy:
+       1) one just before the high end of the address space that's 3
+          bytes large close to the lower end of the offset range, and
+       2) another in the [DIFF_MIN, -8] range from D and so at least
+          8 bytes in size
+     A copy from (1) overlaps but one from (2) does not.  Verify that
+     the copy is not diagnosed.  (This test case was reduced from
+     the Linux kernel.) */
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 5);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 6);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 7);
+  T (d, d + UR (DIFF_MAX - 3, SIZE_MAX - 7), 9);
+
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 5);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 6);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 7);
+  T (d + UR (DIFF_MAX - 3, SIZE_MAX - 7), d, 9);
+
+  {
+    /* Create an offset in the range [0, -1].  */
+    size_t o = sz << 1;
+    T (d, d + o, 12345);
+    T (d + o, d, 23456);
+  }
+
+  /* Exercise memcpy with both destination and source pointer offsets
+     in some known range.  */
+  size_t n = UR (3, 4);
+  T (a + SR (1, 3), a + SR (1, 3), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3] and \\\[1, 3] overlaps between 1 and 4 bytes at offset \\\[1, 3]" "memcpy" } */
+
+  /* This is an interesting case:
+       memcpy (a + i, a + j, n) with i in [1, 3], j in [2, 3], and n in [3, 4]
+     we have the following possibilities ('^' denotesthe destination offset,
+     '*' marks the overlap, and '?' is the possible overlap for large n):
+       i j | a = 012345678   SIZ  OFF (size and offset of the overlap)
+       1 2 |      ^**?       2-3   2
+       1 3 |      ^ *?       1-2   3
+       2 2 |       ***?      3-4   2
+       2 3 |       ^**?      2-3   3
+       3 3 |        ***?     3-4   3
+     There are two ways to present the results:
+     1) overlaps between 1 and 4 bytes at offset [2, 3]
+     2) overlaps between 1 and 4 bytes at offset 2.  */
+  T (a + SR (1, 3), a + SR (2, 3), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[1, 3] and \\\[2, 3] overlaps between 1 and 4 bytes at offset \\\[2, 3]" "memcpy" } */
+  T (a + SR (1, 3), a + SR (3, 4), n);
+
+  T (a + SR (2, 3), a + SR (3, 4), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[2, 3] and \\\[3, 4] overlaps between 1 and 4 bytes at offset \\\[3, 4]" "memcpy" } */
+
+  T (a + SR (1, 3), a + SR (4, 5), n);
+  T (a + SR (2, 3), a + SR (4, 5), n);
+  T (a + SR (3, 4), a + SR (4, 5), n);  /* { dg-warning "accessing between 3 and 4 bytes at offsets \\\[3, 4] and \\\[4, 5] overlaps between 1 and 4 bytes at offset \\\[4, 5]" "memcpy" } */
+
+  /* Exercise the full range of size_t.  */
+  T (d, d + sz, 0);
+  T (d, d + sz, 1);
+  T (d, d + sz, 9);
+}
+
+/* Exercise memcpy with offset and/or size in a determinate anti-range.  */
+
+void test_memcpy_anti_range (char *d, const char *s)
+{
+  T (d, d + SAR (0, 3), 1);
+  T (d, d + SAR (0, 3), 2);
+  T (d, d + SAR (0, 3), 3);
+  T (d, d + SAR (0, 3), DIFF_MAX - 2);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 2" } */
+  T (d, d + SAR (0, 3), DIFF_MAX - 1);   /* { dg-warning "overlaps \[0-9\]+ bytes at offset 1" } */
+  T (d, d + SAR (0, 3), DIFF_MAX);       /* { dg-warning "overlaps \[0-9\]+ bytes at offset 0" } */
+
+  T (d, d + SAR (0, 3), UR (DIFF_MAX - 2, DIFF_MAX));               /* { dg-warning "accessing \[0-9\]+ or more bytes at offsets 0 and \\\[-?\[0-9\]+, -?\[0-9\]+] overlaps \[0-9\]+ bytes at offset 2" } */
+
+  /* Verify that a size in an anti-range ~[0, N] where N >= PTRDIFF_MAX
+     doesn't trigger a warning.  */
+  T (d, s, UAR (1, DIFF_MAX - 1));
+  T (d, s, UAR (1, DIFF_MAX));
+  T (d, s, UAR (1, SIZE_MAX - 1));
+
+  /* This causes the last dg-warning test to fail for some reason.
+     T (d, s, UAR (1, SIZE_MAX)); */
+}
+
+/* Verify calls to memcpy() where the combination of offsets in some
+   range and size is such that either overlap is unavoidable or one
+   or both offsets would exceed the maximum size of an object
+   (DIFF_MAX).  */
+
+void test_memcpy_range_exceed (char *d, const char *s)
+{
+  /* Verify offset and size both in some range.  The memcpy checking
+     is less strict than that of string functions like strncpy and
+     doesn't trigger unless the overlap is certain.  The following
+     overlaps for (r == 3 && n > 3) but not, for example, for
+     (r == 4 && n == 4), and so it's not diagnosed.  */
+  ptrdiff_t i = SR (3, 5);
+  size_t n = UR (4, 6);
+
+  T (a, a + i, n);
+  T (a + i, a, n);
+  /* Ditto for objects of unknown sizes.  */
+  T (d, d + i, n);
+  T (d + i, d, n);
+
+  /* Verify that a warning is issued for a copy between two regions
+     whose aggregate size would exceed DIFF_MAX if it were to not
+     overlap.  */
+  T (d, s, DIFF_MAX / 2);
+  T (d, s, DIFF_MAX / 2 + 1);   /* { dg-warning "overlaps 1 byte" "memcpy" } */
+  T (d, s, DIFF_MAX / 2 + 2);   /* { dg-warning "overlaps 3 bytes" "memcpy" } */
+  T (d, s, DIFF_MAX / 2 + 3);   /* { dg-warning "overlaps 5 bytes" "memcpy" } */
+
+  i = SR (DIFF_MAX - 2, DIFF_MAX);
+
+  /* Verify a warning for an out-of-bounds offset range and constant
+     size addition.  */
+  T (d, d + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, d, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+  T (d + 1, d + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, d + 1, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+  /* Verify that the warnings above are independent of whether the source
+     and destination are known to be based on the same object.  */
+  T (d, s + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, s, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps 1 byte" "memcpy" } */
+
+  T (d + 1, s + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 1 and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 byte" "memcpy" } */
+  T (d + i, s + 1, 3);   /* { dg-warning "accessing 3 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 1 overlaps 1 byte" "memcpy" } */
+
+#if __SIZEOF_SIZE_T__ == 8
+  /* Verfiy the offset and size computation is correct.  The overlap
+     offset mentioned in the warning plus sthe size of the access must
+     not exceed DIFF_MAX.  */
+  T (d, d + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+
+  T (d, s + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[9223372036854775805, 9223372036854775807] overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+  T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[9223372036854775805, 9223372036854775807] and 0 overlaps 3 bytes at offset 9223372036854775802" "LP64" { target lp64 } } */
+#elif __SIZEOF_SIZE_T__ == 4
+  T (d, d + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+  T (d + i, d, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+
+  T (d, s + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2147483645, 2147483647] overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32 } } */
+  T (d + i, s, 5);   /* { dg-warning "accessing 5 bytes at offsets \\\[2147483645, 2147483647] and 0 overlaps 3 bytes at offset 2147483642" "ILP32" { target ilp32} } */
+#endif
+
+  ptrdiff_t j = SR (DIFF_MAX - 9, DIFF_MAX - 1);
+  i = SR (DIFF_MAX - 5, DIFF_MAX - 1);
+  n = UR (4, 5);
+  T (d + i, d + j, n);
+
+  n = UR (4, DIFF_MAX - 1);
+  T (d + i, d + j, n);
+
+  n = UR (4, SIZE_MAX - 1);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 8, DIFF_MAX - 1);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 7, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+  j = SR (DIFF_MAX - 6, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 4( or more)? bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps" "memcpy" } */
+
+  n = UR (3, DIFF_MAX);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 6, DIFF_MAX - 1);
+  T (d + i, d + j, n);
+
+  j = SR (DIFF_MAX - 5, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+  j = SR (DIFF_MAX - 4, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+
+  j = SR (DIFF_MAX - 2, DIFF_MAX - 1);
+  T (d + i, d + j, n);   /* { dg-warning "accessing 3 or more bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and \\\[\[0-9\]+, \[0-9\]+] overlaps 1 or more bytes" "memcpy" } */
+}
+
+/* Exercise memcpy with destination and source of unknown size.  */
+
+void test_memcpy_var (char *d, const char *s)
+{
+  size_t n = unsigned_value ();
+
+  memcpy (d, d, 0);
+  sink (d);
+
+  memcpy (d, d, n);               /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  memcpy (d, &d[0], n);           /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  memcpy (&d[0], d,  n);          /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "source argument is the same as destination" "memcpy" } */
+  sink (d);
+
+  /* The following overlaps if n is greater than 1.  */
+  s = d + 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + n;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + signed_value ();
+  memcpy (d, s, unsigned_value ());
+  sink (d);
+
+  s = d + 3;
+  n = 1;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 2;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 3;
+  memcpy (d, s, n);
+  sink (d);
+
+  s = d + 3;
+  n = 4;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+  sink (d);
+
+  s = d + 5;
+  n = 7;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+
+  n = UR (0, 1);
+  s = d;
+  memcpy (d, s, n);               /* { dg-warning "\\\[-Wrestrict" "memcpy" } */
+}
+
+
+void test_memcpy_memarrray (struct MemArrays *p)
+{
+#undef T
+#define T(dst, src, n) do {			\
+    if (!LINE || LINE == __LINE__) {		\
+      void *pd = (dst);				\
+      const void *ps = (src);			\
+      memcpy (pd, ps, (n));			\
+      sink (pd, ps);				\
+    }						\
+  } while (0)
+
+  T (p->a8, p->a8, 0);
+  T (p->a8, p->a8 + 1, 1);
+  T (p->a8, p->a8 + 2, 2);
+  T (p->a8, p->a8 + 8, 1);
+
+  T (p->a8, p->a8 + 2, 3);        /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
+}
+
+/* Exercise the absence of warnings with memmove.  */
+
+void test_memmove (void)
+{
+  {
+    char d[7];
+    sink (d);
+
+    const void *s = d;
+    memmove (d, s, 7);
+    sink (d);
+
+    s = d + 1;
+    memmove (d, s, 6);
+    sink (d);
+
+    s = d + 2;
+    memmove (d + 1, s, 5);
+    sink (d);
+  }
+}
+
+/* Exercise strcat with constant or known arguments.  */
+
+void test_strcat_cst (const char *s)
+{
+#undef T
+#define T(init, dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcat (pd, ps);					\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("0",   a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T ("01",  a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcat" } */
+  /* The 3 bytes "12\0" being appended to "012" overwrite the final NUL.  */
+  T ("012", a, a + 1);            /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a, a + 2);            /* { dg-warning "accessing 2 bytes at offsets 0 and 2 overlaps 1 byte at offset 3" "strcat" } */
+  /* The nul copied from a[3] to a[3] overwrites itself so this is
+     diagnosed.  */
+  T ("012", a, a + 3);            /* { dg-warning "accessing 1 byte at offsets 0 and 3 overlaps 1 byte at offset 3" "strcat" } */
+
+  T ("012", a, a + 4);
+  T ("012", a, a + 5);
+  T ("012", a, a + 6);
+  T ("012", a, a + 7);
+  T ("012", a, a + 8);
+
+  T ("0",   a + 1, a);            /* { dg-warning "accessing 2 bytes at offsets 1 and 0 overlaps 1 byte at offset 1" "strcat" } */
+  T ("0",   a + 2, a);
+
+  /* The first of the two offsets in the diagnostic for strcat is that
+     of the first write into the destination, not that of the initial
+     read from it to compute its length.  */
+  T ("01",  a + 1, a);            /* { dg-warning "accessing 3 bytes at offsets 1 and 0 overlaps 1 byte at offset 2" "strcat" } */
+  T ("01",  a + 2, a);            /* { dg-warning "accessing 3 bytes at offsets 2 and 0 overlaps 1 byte at offset 2" "strcat" } */
+  T ("01",  a + 3, a);
+
+  T ("012", a + 1, a);            /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 1 byte at offset 3" "strcat" } */
+  T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3 " "strcat" } */
+  T ("012", a + 4, a);
+  T ("012", a + 5, a);
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T ("012",      a, "012");
+  T ("012",      a, s);
+  T ("01234567", a, s);
+}
+
+/* Exercise strcat with destination and source of unknown length.  */
+
+void test_strcat_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcat (pd, ps);					\
+      sink (pd, ps);					\
+    }							\
+  } while (0)
+
+  T (d, d);                       /* { dg-warning "source argument is the same as destination" "strcat" } */
+  T (d, d + 1);                   /* { dg-warning "accessing 0 or more bytes at offsets 0 and 1 may overlap 1 byte" "strcat" } */
+  T (d, d + 2);                   /* { dg-warning "accessing 0 or more bytes at offsets 0 and 2 may overlap 1 byte" "strcat" } */
+  T (d, d + 999);                 /* { dg-warning "accessing 0 or more bytes at offsets 0 and 999 may overlap 1 byte" "strcat" } */
+  T (d, d + -99);                 /* { dg-warning "accessing 0 or more bytes at offsets 0 and -99 may overlap 1 byte" "strcat" } */
+
+  size_t n = unsigned_value ();
+
+  T (d + n, d + n);                       /* { dg-warning "\\\[-Wrestrict" "strcat" } */
+
+  /* Verify that the obviously benign cases below aren't diagnosed.  */
+  T (d, "012");
+  T (d + 1, "0123");
+  T (d + n, "01234");
+  T (d, s);
+  T (d + 1, s);
+  T (d + n, s);
+
+  /* Since the offset is unknown the overlap in the call below, while
+     possible, is certainly not inevitable.  Conservatively, it should
+     not be diagnosed.  For safety, an argument for diagnosing can be
+     made.  It's a judgment call, partly determined by the effort and
+     complexity of treating this case differently from other similar
+     to it.   */
+  T (d, d + n); /* { dg-warning "may overlap" "strcat" } */
+}
+
+/* Exercise strcpy with constant or known arguments.  */
+
+void test_strcpy_cst (ptrdiff_t i)
+{
+#undef T
+#define T(init, dst, src) do {				\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[8] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcpy (pd, ps);					\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcpy" } */
+  T ("012", a, a + 1);            /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strcpy" } */
+  T ("012", a, a + 2);
+  T ("012", a, a + 3);
+  /* The following doesn't overlap but it should trigger -Wstringop-overflow
+     for reading past the end.  */
+  T ("012", a, a + sizeof a);
+
+  /* The terminating nul written to d[2] overwrites s[0].  */
+  T ("0123", a, a + 2);           /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" } */
+
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("01234", a, a + 2);          /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" } */
+
+  /* This happens to be safe in GCC but it's still wrong.  */
+  T ("012", a, a);                /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  T ("012", a + 1, a);            /* { dg-warning "accessing 4 bytes at offsets 1 and 0 overlaps 3 bytes at offset 1" "strcpy" } */
+  T ("012", a + 2, a);            /* { dg-warning "accessing 4 bytes at offsets 2 and 0 overlaps 2 bytes at offset 2" "strcpy" } */
+  T ("012", a + 3, a);            /* { dg-warning "accessing 4 bytes at offsets 3 and 0 overlaps 1 byte at offset 3" "strcpy" } */
+  T ("012", a + 4, a);
+  /* The following doesn't overlap but it should trigger -Wstrinop-ovewrflow
+     for writing past the end.  */
+  T ("012", a + sizeof a, a);
+}
+
+/* Exercise strcpy with constant or known arguments offset by a range.
+   The tests verify the use of the lower bound of the range which is
+   more restrictive than using the upper bound for positive values.  */
+
+void test_strcpy_range (void)
+{
+#undef T
+#define T(N, init, dst, src)			\
+  do {						\
+    if (!LINE || LINE == __LINE__) {		\
+      char a[N] = init;				\
+      char *pd = (dst);				\
+      const char *ps = (src);			\
+      strcpy (pd, ps);				\
+      sink (a, pd, ps);				\
+    }						\
+  } while (0)
+
+  ptrdiff_t r;
+
+  r = SR (0, 1);
+  T (8, "0", a + r, a);   /* { dg-warning "accessing between 1 and 2 bytes at offsets \\\[0, 1] and 0 overlaps up to 2 bytes at offset \\\[0, 1]" "strcpy" { xfail *-*-*} } */
+
+  r = SR (2, 5);
+  T (8, "01",  a + r, a);            /* { dg-warning "accessing 3 bytes at offsets \\\[2, 5] and 0 may overlap 1 byte at offset 2" } */
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[2, 5] and 0 may overlap up to 2 bytes at offset \\\[3, 2]" "strcpy" } */
+
+  /* The highest offset to which to copy without overflowing the 8-byte
+     destination is 3 and that overlaps 2 bytes.  */
+  T (8, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 2 and 3 bytes at offset \\\[2, 3]" "strcpy" } */
+
+  /* With a 9-byte destination the highest offset is 4 and that still
+     overlaps 1 byte (the final NUL).  */
+  T (9, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 overlaps between 1 and 3 bytes at offset \\\[2, 4]" "strcpy" } */
+
+  /* With a 10-byte buffer it's possible to copy all 5 bytes without
+     overlap at (a + 5).  Copying at offsets 2 through 4 overflows
+     between 3 and 1 bytes, respectively.  */
+  T (10, "0123", a + r, a);          /* { dg-warning "accessing 5 bytes at offsets \\\[2, 5] and 0 may overlap up to 3 bytes at offset \\\[4, 2]" "strcpy" } */
+
+
+  r  = SR (3, 4);
+  T (8, "01",  a + r, a);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[3, 4] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+  /* The highest offset to which to copy without overflowing the 8-byte
+     destination is 3 and that overlaps 2 bytes.  */
+  T (8, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps 2 bytes at offset 3" "strcpy" } */
+
+  /* With a 9-byte destination the highest offset is 4 and that still
+     overlaps 1 byte (the final NUL).  */
+  T (9, "0123", a + r, a);           /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[3, 4]" "strcpy" } */
+
+  /* With a 10-byte buffer it's possible to copy all 5 bytes without
+     overlap at (a + 5).  Copying at offsets 2 through 4 overflows
+     between 3 and 1 bytes, respectively.  */
+  T (10, "0123", a + r, a);          /* { dg-warning "accessing 5 bytes at offsets \\\[3, 4] and 0 overlaps between 1 and 2 bytes at offset \\\[3, 4]" "strcpy" } */
+
+  T (8, "01",     a, a + r);
+  T (8, "012",    a, a + r);
+  T (8, "0123",   a, a + r);
+  T (8, "01234",  a, a + r);
+
+  /* With the smaller offset of 3 the final NUL definitely overlaps
+     the '4' at a[3], but with the larger offset of 4 there is no
+     overlap, so the warning is a "may overlap" and the size of
+     the overlap is 1 byte.  */
+  T (8, "012345", a, a + r);         /* { dg-warning "accessing between 3 and 4 bytes at offsets 0 and \\\[3, 4] may overlap 1 byte at offset 3" "strcpy" } */
+  T (8, "0123456", a, a + r);        /* { dg-warning "accessing between 4 and 5 bytes at offsets 0 and \\\[3, 4] may overlap up to 2 bytes at offset 3" "strcpy" } */
+
+  r = SR (3, DIFF_MAX - 3);
+  T (8, "01",  a + r, a);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[3, \[0-9\]+] and 0 may overlap 1 byte at offset 3" "strcpy" } */
+
+  r = SR (DIFF_MAX - 2, DIFF_MAX - 1);
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps" "strcpy" } */
+
+  /* Exercise the full range of ptrdiff_t.  */
+  r = signed_value ();
+
+  /* The overlap in the cases below isn't inevitable but it is diagnosed
+     because it is possible and so the code is considered unsafe.  */
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap 1 byte" "strcpy" } */
+  T (8, "0", a + r, a);              /* { dg-warning "accessing 2 bytes may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes may overlap up to 4 bytes" "strcpy" } */
+
+  T (8, "", a, a + r);               /* { dg-warning "accessing 1 byte may overlap" "strcpy" } */
+  T (8, "0", a, a + r);              /* { dg-warning "accessing between 0 and 2 bytes may overlap up to 2 bytes" "strcpy" } */
+  T (8, "012", a, a + r);            /* { dg-warning "accessing between 0 and 4 bytes may overlap up to 4 bytes" "strcpy" } */
+}
+
+/* Exercise strcpy with destination and/or source of unknown lengthu.  */
+
+void test_strcpy_var (char *d, const char *s)
+{
+#undef T
+#define T(dst, src) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strcpy (pd, ps);					\
+      sink (pd, ps);					\
+    }							\
+  } while (0)
+
+  T (d, s);
+
+  T (d, &d[0]);                   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+  T (&d[0], d);                   /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  s = d;
+  T (d, s);                       /* { dg-warning "source argument is the same as destination" "strcpy" } */
+
+  /* The following overlaps if *s is not nul.  It arguably should be
+     diagnosed.  */
+  T (d, d + 1);
+
+  /* The following overlaps if strlen (d) is greater than 1.  Like
+     the above, it possibly should be diagnosed too.  */
+  int r = SR (2, 3);
+  T (d, d + r);
+
+  /* The following overlaps only if strlen (s + n) >= n so it's not
+     diagnosed.  */
+  s = d + signed_value ();
+  T (d, s);
+}
+
+/* Exercise strncpy with constant or known arguments.  */
+
+void test_strncpy_cst (void)
+{
+#undef T
+#define T(init, dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = init;					\
+      char *pd = (dst);					\
+      const char *ps = (src);				\
+      strncpy (pd, ps, (size));				\
+      sink (a, pd, ps);					\
+    }							\
+  } while (0)
+
+  T ("012", a, a, 0);
+  T ("012", a, a, 1);             /* { dg-warning "source argument is the same as destination " "strncpy" } */
+
+  T ("012", a, a + 1, 1);
+  T ("012", a, a + 1, 2);         /* { dg-warning "accessing 2 bytes at offsets 0 and 1 overlaps 1 byte at offset 1" "strncpy" } */
+  T ("012", a, a + 1, 3);         /* { dg-warning "accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" "strncpy" } */
+  T ("012", a, a + 1, 4);         /* { dg-warning "accessing 4 bytes at offsets 0 and 1 overlaps 3 bytes at offset 1" "strncpy" } */
+  T ("012", a, a + 1, 5);         /* { dg-warning "accessing 5 bytes at offsets 0 and 1 overlaps 3 bytes at offset 1" "strncpy" } */
+  T ("012", a, a + 1, 6);         /* { dg-warning "accessing 6 bytes at offsets 0 and 1 overlaps 3 bytes at offset 1" "strncpy" } */
+
+  T ("012", a, a + 2, 1);
+  T ("012", a, a + 2, 2);
+  /* The third written byte (nul) overwrites a[2].  */
+  T ("012", a, a + 2, 3);         /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+  T ("012", a, a + 2, 4);         /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" "strncpy" } */
+  T ("012", a, a + 2, 5);         /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" "strncpy" } */
+
+  T ("0123", a, a + 2, 1);
+  T ("0123", a, a + 2, 2);
+  /* The terminating nul written to a[2] overwrites s[0].  */
+  T ("0123", a, a + 2, 3);        /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+  T ("0123", a, a + 2, 4);        /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" "strncpy" } */
+  T ("0123", a, a + 2, 5);        /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "strncpy" } */
+  T ("0123", a, a + 2, 6);        /* { dg-warning "accessing 6 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "strncpy" } */
+
+  T ("01234", a, a + 2, 1);
+  T ("01234", a, a + 2, 2);
+  T ("01234", a, a + 2, 3);       /* { dg-warning "accessing 3 bytes at offsets 0 and 2 overlaps 1 byte at offset 2" "strncpy" } */
+  /* The '5' copied from s[2] to d[2] overwrites s[0].  */
+  T ("01234", a, a + 2, 4);       /* { dg-warning "accessing 4 bytes at offsets 0 and 2 overlaps 2 bytes at offset 2" "strncpy" } */
+  T ("01234", a, a + 2, 5);       /* { dg-warning "accessing 5 bytes at offsets 0 and 2 overlaps 3 bytes at offset 2" "strncpy" } */
+}
+
+
+/* Exercise strncpy with one or more arguments in a determinate range.  */
+
+void test_strncpy_range (char *d, size_t n)
+{
+#undef T
+#define T(init, dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {			\
+      char a[9] = init;					\
+      strncpy ((dst), (src), (size));			\
+      sink (a, (dst), (src));				\
+    }							\
+  } while (0)
+
+  ptrdiff_t i;
+
+  i = SR (0, 1);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  /* Offset in the range [0, i] is represented as a PHI (&a, &a + i)
+     that the implementation isn't equipped to handle yet.  */
+  T ("0123", a, a + i, 2);   /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[0, 1] may overlap 1 byte at offset 1" "strncpy" { xfail *-*-* } } */
+
+  i = SR (1, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);   /* { dg-warning "accessing 2 bytes at offsets 0 and \\\[1, 5] may overlap 1 byte at offset 1" "strncpy" } */
+  T ("0123", a, a + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[1, 5] may overlap up to 2 bytes at offset \\\[2, 1]" "strncpy" } */
+  T ("0123", a, a + i, 4);   /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[1, 5] may overlap up to 3 bytes at offset \\\[3, 1]" "strncpy" } */
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[1, 5] may overlap up to 4 bytes at offset \\\[4, 1]" "strncpy" } */
+
+  i = SR (2, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);   /* { dg-warning "accessing 3 bytes at offsets 0 and \\\[2, 5] may overlap 1 byte at offset 2" "strncpy" } */
+  T ("0123", a, a + i, 4);   /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[2, 5] may overlap up to 2 bytes at offset \\\[3, 2]" "strncpy" } */
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[2, 5] may overlap up to 3 bytes at offset \\\[4, 2]" "strncpy" } */
+
+  i = SR (3, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);
+  T ("0123", a, a + i, 4);   /* { dg-warning "accessing 4 bytes at offsets 0 and \\\[3, 5] may overlap 1 byte at offset 3" "strncpy" } */
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[3, 5] may overlap up to 2 bytes at offset \\\[4, 3]" "strncpy" } */
+
+  i = SR (4, 5);
+  T ("0123", a, a + i, 0);
+  T ("0123", a, a + i, 1);
+  T ("0123", a, a + i, 2);
+  T ("0123", a, a + i, 3);
+  T ("0123", a, a + i, 4);
+  T ("0123", a, a + i, 5);   /* { dg-warning "accessing 5 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+  T ("0123", a, a + i, 6);   /* { dg-warning "accessing 6 bytes at offsets 0 and \\\[4, 5] may overlap 1 byte at offset 4" "strncpy" } */
+
+  /* Verify offset and size both in some range.  The strncpy checking
+     is more strict than that of memcpy and triggers even when the
+     overlap is possible but not inevitable.  The following overlaps
+     like so ('*' denotes the terminating NUL, '.' the appended NUL
+     that's not copied from the source):
+        a:        01234567*  (also indicates offset)
+        i = 4:    4567       none
+                  4567*      overlaps 1 at offset 4
+                  4567*.     overlaps 2 at offset 4
+        i = 5:    567*       none
+                  567*.      none
+                  567*..     overlaps 1 at offset 5  */
+  T ("01234567", a, a + i, UR (4, 6));   /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  /* Ditto for objects of unknown sizes.  */
+  T ("01234567", d, d + i, UR (4, 6));  /* { dg-warning "accessing between 4 and 6 bytes at offsets 0 and \\\[4, 5] may overlap up to 2 bytes at offset \\\[5, 4]" "strncpy" } */
+
+  T ("01234567", a, a + i, UR (6, 7));  /* { dg-warning "accessing between 6 and 7 bytes at offsets 0 and \\\[4, 5] overlaps between 1 and 3 bytes at offset \\\[4, 5]" "strncpy" } */
+
+  /* The following overlaps except in the unlikely case that value ()
+     is zero, so it's diagnosed.  */
+  T ("012", a, a, n);             /* { dg-warning "source argument is the same as destination " "strncpy" } */
+}
+
+
+/* Exercise strncpy with destination and source of unknown length.  */
+
+void test_strncpy_var (char *d, const char *s, size_t n)
+{
+#undef T
+#define T(dst, src, size) do {			\
+    if (!LINE || LINE == __LINE__) {		\
+      char *pd = (dst);				\
+      const char *ps = (src);			\
+      strncpy (pd, ps, (size));			\
+      sink (pd, ps);				\
+    }						\
+  } while (0)
+
+  T (d, s, 1);
+  T (d, s, n);
+
+  T (d, d, 1);                    /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+  T (d, d, n);                    /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+
+  T (d,     d + 1, 1);
+  T (d,     d + 1, 2);            /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+  T (d + 1, d,     1);
+  T (d + 1, d,     2);            /* { dg-warning "\\\[-Wrestrict" "strncpy" } */
+}
+
+struct MemberArrays
+{
+  char a[7];
+  char b[8];
+  char c[9];
+};
+
+void test_strncpy_strcpy_var (struct MemberArrays *ar, const char *s)
+{
+  /* The following is safe and should not trigger a warning.  */
+  strncpy (ar->b, s, sizeof ar->b - 1);
+  ar->b[sizeof ar->b - 1] = '\0';
+  strcpy (ar->a, ar->b);
+  sink (ar);
+
+  /* The following is not as safe (it might overflow ar->a) but there
+     is no overlap so it also shouldn't trigger -Wrestrict.  */
+  strncpy (ar->c, s, sizeof ar->c - 1);
+  ar->c[sizeof ar->c - 1] = '\0';
+  strcpy (ar->a, ar->c);
+  sink (ar);
+}
diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
index 7861bb0..4281e3b 100644
--- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess1.c
@@ -1,7 +1,7 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument" } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-c++-compat" { target c } } */
 /* { dg-require-effective-target alloca } */
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
index f7bfa35..d9a1555 100644
--- a/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
+++ b/gcc/testsuite/c-c++-common/Wsizeof-pointer-memaccess2.c
@@ -1,7 +1,7 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
-/* { dg-options "-Wall -O2 -Wno-sizeof-array-argument -Wno-stringop-truncation -Wno-c++-compat -ftrack-macro-expansion=0" {target c} } */
+/* { dg-options "-Wall -O2 -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-truncation -ftrack-macro-expansion=0" } */
+/* { dg-options "-Wall -O2 -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-truncation -Wno-c++-compat -ftrack-macro-expansion=0" {target c} } */
 /* { dg-require-effective-target alloca } */
 
 #define bos(ptr) __builtin_object_size (ptr, 1)
diff --git a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
index 5bc5c4c..16340eb 100644
--- a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
+++ b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess1.C
@@ -1,6 +1,6 @@
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
+// { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
 // Test just twice, once with -O0 non-fortified, once with -O2 fortified.
 // { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } }
 // { dg-skip-if "" { *-*-* }  { "-flto" } { "" } }
diff --git a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
index f2c864b..2dff8f0 100644
--- a/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
+++ b/gcc/testsuite/g++.dg/torture/Wsizeof-pointer-memaccess2.C
@@ -1,6 +1,6 @@
 // Test -Wsizeof-pointer-memaccess warnings.
 // { dg-do compile }
-// { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
+// { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" }
 // Test just twice, once with -O0 non-fortified, once with -O2 fortified,
 // suppressing buffer overflow warnings.
 // { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } }
diff --git a/gcc/testsuite/gcc.dg/Wobjsize-1.c b/gcc/testsuite/gcc.dg/Wobjsize-1.c
index 19c7e68..e80c8ad 100644
--- a/gcc/testsuite/gcc.dg/Wobjsize-1.c
+++ b/gcc/testsuite/gcc.dg/Wobjsize-1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -Wall" } */
+/* { dg-options "-O2 -Wall -Wno-array-bounds" } */
 
 #include "Wobjsize-1.h"
 
diff --git a/gcc/testsuite/gcc.dg/Wrestrict-2.c b/gcc/testsuite/gcc.dg/Wrestrict-2.c
new file mode 100644
index 0000000..d73e144
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wrestrict-2.c
@@ -0,0 +1,41 @@
+/* Test to verify that the temporary doesn't trigger a bogus -Warray-bounds
+   warning.  Distilled from libat_exchange_large_inplace in libatomic/gexch.c.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" }  */
+
+typedef typeof (sizeof 0) size_t;
+
+extern void *memcpy (void*, const void*, size_t);
+
+void libat_exchange_large_inplace (size_t n, void *mptr, void *vptr)
+{
+  char temp[1024];
+
+  size_t i = 0;
+
+  for (i = 0; n >= 1024; i += 1024, n -= 1024)
+    {
+      memcpy (temp, mptr + i, 1024);
+
+      /* The memcpy call below results in the following:
+	 unsigned long ivtmp.7;
+
+	 ivtmp.7_4 = (unsigned long) mptr_9(D);
+	 ...
+	 <bb 4>
+	 # ivtmp.7_22 = PHI <ivtmp.7_4(3), ivtmp.7_5(4)>
+	 ...
+	 _1 = (void *) ivtmp.7_22;
+	 ...
+	 memcpy (_1, _2, 1024);
+
+	 Treating _1 as a pointer results in the bogus:
+	   warning: 'memcpy' offset 0 is out of the bounds [0, 8] of object 'ivtmp.7' with type 'long unsigned int' [-Warray-bounds]
+	   memcpy (mptr + i, vptr + i, 1024);
+	   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+      */
+      memcpy (mptr + i, vptr + i, 1024);
+
+      memcpy (vptr + i, temp, 1024);
+    }
+}
diff --git a/gcc/testsuite/gcc.dg/Wrestrict.c b/gcc/testsuite/gcc.dg/Wrestrict.c
new file mode 100644
index 0000000..076f878
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wrestrict.c
@@ -0,0 +1,34 @@
+/* Test to verify that VLAs are handled gracefully by -Wrestrict
+   { dg-do compile }
+   { dg-options "-O2 -Wrestrict" }  */
+
+typedef __SIZE_TYPE__ size_t;
+
+#define memcpy(d, s, n)  __builtin_memcpy (d, s, n)
+#define strcpy(d, s)     __builtin_strcpy (d, s)
+
+void test_vla (void *d, const char *s1, const char *s2, int i, size_t n)
+{
+  char a[n];
+  char b[n];
+
+  strcpy (a, s1);
+  strcpy (b, s2);
+
+  memcpy (d, i ? a : b, n);
+}
+
+
+void test_vla_member (void *d, const char *s1, const char *s2, int i, size_t n)
+{
+  struct S
+  {
+    char a[n];
+    char b[n];
+  } s;
+
+  strcpy (s.a, s1);
+  strcpy (s.b, s2);
+
+  memcpy (d, i ? s.a : s.b, n);
+}
diff --git a/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
index f4e8552..4d14de2 100644
--- a/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/gcc.dg/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow" } */
 /* { dg-require-effective-target alloca } */
 
 typedef __SIZE_TYPE__ size_t;
diff --git a/gcc/testsuite/gcc.dg/builtin-stpncpy.c b/gcc/testsuite/gcc.dg/builtin-stpncpy.c
index 9200798..938221b 100644
--- a/gcc/testsuite/gcc.dg/builtin-stpncpy.c
+++ b/gcc/testsuite/gcc.dg/builtin-stpncpy.c
@@ -1,6 +1,6 @@
 /* PR tree-optimization/80669 - Bad -Wstringop-overflow warnings for stpncpy
    { dg-do compile }
-   { dg-options "-O2 -Wall -Wno-stringop-truncation" } */
+   { dg-options "-O2 -Wall -Wno-array-bounds -Wno-restrict -Wno-stringop-truncation" } */
 
 #define SIZE_MAX __SIZE_MAX__
 
diff --git a/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
index 10048f3..afd07dd 100644
--- a/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
+++ b/gcc/testsuite/gcc.dg/builtin-stringop-chk-1.c
@@ -36,7 +36,10 @@ test (int arg, ...)
   vx = stpcpy (&buf2[18], "a");
   vx = stpcpy (&buf2[18], "ab"); /* { dg-warning "writing 3" "stpcpy" } */
   strncpy (&buf2[18], "a", 2);
-  strncpy (&buf2[18], "a", 3); /* { dg-warning "writing 3 bytes into a region of size 2" "strncpy" } */
+
+  /* Both warnings below are equally meaningful.  */
+  strncpy (&buf2[18], "a", 3); /* { dg-warning "(writing 3 bytes into a region of size 2|specified bound 3 exceeds destination size 2)" "strncpy" } */
+
   strncpy (&buf2[18], "abc", 2);
   strncpy (&buf2[18], "abc", 3); /* { dg-warning "writing 3 " "strncpy" } */
   memset (buf2, '\0', sizeof (buf2));
@@ -93,7 +96,7 @@ void
 test2 (const H h)
 {
   char c;
-  strncpy (&c, str, 3); /* { dg-warning "writing 3 bytes into a region of size 1" "strncpy" } */
+  strncpy (&c, str, 3); /* { dg-warning "(writing 3 bytes into a region of size 1|specified bound 3 exceeds destination size 1)" "strncpy" } */
 
   struct { char b[4]; } x;
   sprintf (x.b, "%s", "ABCD"); /* { dg-warning "writing 5" "sprintf" } */
diff --git a/gcc/testsuite/gcc.dg/memcpy-6.c b/gcc/testsuite/gcc.dg/memcpy-6.c
new file mode 100644
index 0000000..1d76e7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/memcpy-6.c
@@ -0,0 +1,40 @@
+/* Test to verify that overlapping memcpy with const sizes that are powers
+   of two are folded into into the same code as memmove, but that they
+   are diagnosed nonetheless.
+   { dg-do compile }
+   { dg-options "-O0 -Wrestrict -fdump-tree-optimized" } */
+
+char a[32];
+
+void fold_copy_2 (void)
+{
+  __builtin_memcpy (a + 1, a, 2);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_4 (void)
+{
+  __builtin_memcpy (a + 2, a, 4);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_copy_8 (void)
+{
+  __builtin_memcpy (a + 3, a, 8);   /* { dg-warning "\\\[-Wrestrict]" } */
+}
+
+void fold_move_2 (void)
+{
+  __builtin_memmove (a + 1, a, 2);
+}
+
+void fold_move_4 (void)
+{
+  __builtin_memmove (a + 2, a, 4);
+}
+
+void fold_move_8 (void)
+{
+  __builtin_memmove (a + 3, a, 8);
+}
+
+/* { dg-final { scan-tree-dump-not "memcpy" "optimized" } }
+   { dg-final { scan-tree-dump-not "memmove" "optimized" } } */
diff --git a/gcc/testsuite/gcc.dg/pr69172.c b/gcc/testsuite/gcc.dg/pr69172.c
index c0e7463..908d5a6 100644
--- a/gcc/testsuite/gcc.dg/pr69172.c
+++ b/gcc/testsuite/gcc.dg/pr69172.c
@@ -1,4 +1,5 @@
-/* PR tree-optimization/69172 */
+/* PR tree-optimization/69172 - ICE in make_ssa_name_fn,
+   at tree-ssanames.c:266 */
 /* { dg-do compile } */
 /* { dg-options "-O2" } */
 
@@ -43,3 +44,7 @@ f6 (int x)
 {
   return __builtin___mempcpy_chk (&a, &a, x, 0);
 }
+
+/* The calls above violate strict aliasing.  Eliminate the -Wrestrict
+   warnings they trigger.
+  { dg-prune-output "\\\[-Wrestrict]" } */
diff --git a/gcc/testsuite/gcc.dg/pr79223.c b/gcc/testsuite/gcc.dg/pr79223.c
index 295d5c1..ef0dd1b 100644
--- a/gcc/testsuite/gcc.dg/pr79223.c
+++ b/gcc/testsuite/gcc.dg/pr79223.c
@@ -1,6 +1,6 @@
 /* PR middle-end/79223 - missing -Wstringop-overflow on a memmove overflow
    { dg-do compile }
-   { dg-additional-options "-O2 -Wall -std=gnu99" }  */
+   { dg-additional-options "-O2 -Wall -Wno-array-bounds -std=gnu99" }  */
 
 typedef __SIZE_TYPE__ size_t;
 
diff --git a/gcc/testsuite/gcc.dg/pr81345.c b/gcc/testsuite/gcc.dg/pr81345.c
index c2cbad7..14661f5 100644
--- a/gcc/testsuite/gcc.dg/pr81345.c
+++ b/gcc/testsuite/gcc.dg/pr81345.c
@@ -1,6 +1,6 @@
 /* PR other/81345 - -Wall resets -Wstringop-overflow to 1 from the default 2
    { dg-do compile }
-   { dg-options "-O2 -Wall" } */
+   { dg-options "-O2 -Wall -Wno-array-bounds" } */
 
 char a[3];
 
diff --git a/gcc/testsuite/gcc.dg/range.h b/gcc/testsuite/gcc.dg/range.h
new file mode 100644
index 0000000..0b4d9e8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/range.h
@@ -0,0 +1,57 @@
+#ifndef RANGE_H
+
+/* Definitions of helper functions and macros to create expressions
+   in a specified range.  Not all the symbols declared here are
+   defined.  */
+
+#define SIZE_MAX __SIZE_MAX__
+#define DIFF_MAX __PTRDIFF_MAX__
+#define DIFF_MIN (-DIFF_MAX - 1)
+
+typedef __INT32_TYPE__   int32_t;
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__    size_t;
+
+static inline ptrdiff_t signed_value (void)
+{
+  extern volatile ptrdiff_t signed_value_source;
+  return signed_value_source;
+}
+
+static inline size_t unsigned_value (void)
+{
+  extern volatile size_t unsigned_value_source;
+  return unsigned_value_source;
+}
+
+static inline ptrdiff_t signed_range (ptrdiff_t min, ptrdiff_t max)
+{
+  ptrdiff_t val = signed_value ();
+  return val < min || max < val ? min : val;
+}
+
+static inline ptrdiff_t signed_anti_range (ptrdiff_t min, ptrdiff_t max)
+{
+  ptrdiff_t val = signed_value ();
+  return min <= val && val <= max ? min == DIFF_MIN ? max + 1 : min - 1 : val;
+}
+
+static inline size_t unsigned_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return val < min || max < val ? min : val;
+}
+
+static inline size_t unsigned_anti_range (size_t min, size_t max)
+{
+  size_t val = unsigned_value ();
+  return min <= val && val <= max ? min == 0 ? max + 1 : min - 1 : val;
+}
+
+#define SR(min, max) signed_range ((min), (max))
+#define UR(min, max) unsigned_range ((min), (max))
+
+#define SAR(min, max) signed_anti_range ((min), (max))
+#define UAR(min, max) unsigned_anti_range ((min), (max))
+
+#endif /* RANGE_H */
diff --git a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
index cd9dc72..11367d1 100644
--- a/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
+++ b/gcc/testsuite/gcc.dg/torture/Wsizeof-pointer-memaccess1.c
@@ -1,6 +1,6 @@
 /* Test -Wsizeof-pointer-memaccess warnings.  */
 /* { dg-do compile } */
-/* { dg-options "-Wall -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" } */
+/* { dg-options "-Wall -Wno-array-bounds -Wno-sizeof-array-argument -Wno-stringop-overflow -Wno-stringop-truncation" } */
 /* Test just twice, once with -O0 non-fortified, once with -O2 fortified.  */
 /* { dg-skip-if "" { *-*-* }  { "*" } { "-O0" "-O2" } } */
 /* { dg-skip-if "" { *-*-* }  { "-flto" } { "" } } */
diff --git a/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c b/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c
new file mode 100644
index 0000000..1be4922
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/chkp-stropt-17.c
@@ -0,0 +1,68 @@
+/* { dg-do compile { target { ! x32 } } }
+   { dg-require-effective-target mempcpy }
+   { dg-options "-O2 -Wrestrict -fcheck-pointer-bounds -mmpx" } */
+
+#define USE_GNU
+#include "../../gcc.dg/strlenopt.h"
+
+/* There is no BUILT_IN_ST{P,R}NCPY_CHKP or BUILT_IN_STRNCAT_CHKP
+   so the test for them below are XFAIL.  */
+char *stpncpy (char *__restrict, const char *__restrict, size_t);
+char *strncpy (char *__restrict, const char *__restrict, size_t);
+char *strncat (char *__restrict, const char *__restrict, size_t);
+
+
+char a[8];
+
+void test_memcpy (void)
+{
+  memcpy (a, a + 1, 3);   /* { dg-warning ".memcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+void test_memmove (void)
+{
+  memmove (a, a + 1, 3);
+}
+
+void* test_mempcpy (void)
+{
+  return mempcpy (a, a + 1, 3);   /* { dg-warning ".mempcpy\.chkp. accessing 3 bytes at offsets 0 and 1 overlaps 2 bytes at offset 1" } */
+}
+
+char* test_stpcpy (void)
+{
+  strcpy (a, "0123456");
+  return stpcpy (a, a + 2);   /* { dg-warning ".stpcpy\.chkp. accessing 6 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" } */
+}
+
+char* test_stpncpy (void)
+{
+  strcpy (a, "0123456");
+
+  /* There is no BUILT_IN_STPNCPY_CHKP so this isn't handled.  */
+  return stpncpy (a, a + 2, sizeof a);   /* { dg-warning ".stpcpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 4 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strcpy (void)
+{
+  strcpy (a, "0123456");
+  strcpy (a, a + 1);   /* { dg-warning ".strcpy\.chkp. accessing 7 bytes at offsets 0 and 1 overlaps 6 bytes at offset 1" } */
+}
+
+void test_strcat (int n)
+{
+  strcat (a, a + 3);   /* { dg-warning ".strcat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" } */
+}
+
+void test_strncat (int n)
+{
+  strncat (a, a + 3, sizeof a);   /* { dg-warning ".strncat\.chkp. accessing 0 or more bytes at offsets 0 and 3 may overlap 1 byte" "bug 82652" { xfail *-*-* } } */
+}
+
+void test_strncpy (int n)
+{
+  strcpy (a, "0123456");
+
+  /* There is no BUILT_IN_STRNCPY_CHKP so this isn't handled.  */
+  strncpy (a, a + 2, sizeof a);   /* { dg-warning ".strncpy\.chkp. accessing 7 bytes at offsets 0 and 2 overlaps 5 bytes at offset 2" "bug 82652" { xfail *-*-* } } */
+}
diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h
index 97ace1e..323f37d 100644
--- a/gcc/tree-pass.h
+++ b/gcc/tree-pass.h
@@ -461,6 +461,7 @@ extern gimple_opt_pass *make_pass_build_cgraph_edges (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_local_pure_const (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_nothrow (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_tracer (gcc::context *ctxt);
+extern gimple_opt_pass *make_pass_warn_restrict (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_warn_unused_result (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_diagnose_tm_blocks (gcc::context *ctxt);
 extern gimple_opt_pass *make_pass_lower_tm (gcc::context *ctxt);
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 94f20ef..8f893ec 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "ssa.h"
 #include "cgraph.h"
 #include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-restrict.h"
 #include "fold-const.h"
 #include "stor-layout.h"
 #include "gimple-fold.h"
@@ -173,6 +174,7 @@ struct laststmt_struct
 } laststmt;
 
 static int get_stridx_plus_constant (strinfo *, unsigned HOST_WIDE_INT, tree);
+static void handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *);
 
 /* Return:
 
@@ -1386,7 +1388,7 @@ static void
 handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, len, lhs, args, type, fn, oldlen;
+  tree src, dst, srclen, len, lhs, type, fn, oldlen;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi, *olddsi, *zsi;
@@ -1502,6 +1504,23 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 	    }
 	}
       dsi->stmt = stmt;
+
+      /* Try to detect overlap before returning.  This catches cases
+	 like strcpy (d, d + n) where n is non-constant whose range
+	 is such that (n <= strlen (d) holds).
+
+	 OLDDSI->NONZERO_chars may have been reset by this point with
+	 oldlen holding it original value.  */
+      if (olddsi && oldlen)
+	{
+	  /* Add 1 for the terminating NUL.  */
+	  tree type = TREE_TYPE (oldlen);
+	  oldlen = fold_build2 (PLUS_EXPR, type, oldlen,
+				build_int_cst (type, 1));
+	  check_bounds_or_overlap (as_a <gcall *>(stmt), olddsi->ptr, src,
+				   oldlen, NULL_TREE);
+	}
+
       return;
     }
 
@@ -1574,14 +1593,32 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (zsi != NULL)
     zsi->dont_invalidate = true;
 
-  if (fn == NULL_TREE)
-    return;
-
-  args = TYPE_ARG_TYPES (TREE_TYPE (fn));
-  type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+  if (fn)
+    {
+      tree args = TYPE_ARG_TYPES (TREE_TYPE (fn));
+      type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args)));
+    }
+  else
+    type = size_type_node;
 
   len = fold_convert_loc (loc, type, unshare_expr (srclen));
   len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
+
+  /* Set the no-warning bit on the transformed statement?  */
+  bool set_no_warning = false;
+
+  if (const strinfo *chksi = olddsi ? olddsi : dsi)
+    if (si
+	&& !check_bounds_or_overlap (as_a <gcall *>(stmt), chksi->ptr, si->ptr,
+				     NULL_TREE, len))
+      {
+	gimple_set_no_warning (stmt, true);
+	set_no_warning = true;
+      }
+
+  if (fn == NULL_TREE)
+    return;
+
   len = force_gimple_operand_gsi (gsi, len, true, NULL_TREE, true,
 				  GSI_SAME_STMT);
   if (dump_file && (dump_flags & TDF_DETAILS) != 0)
@@ -1629,6 +1666,21 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi)
     }
   else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     fprintf (dump_file, "not possible.\n");
+
+  if (set_no_warning)
+    gimple_set_no_warning (stmt, true);	
+}
+
+/* Check the size argument to the built-in forms of stpncpy and strncpy
+   for out-of-bounds offsets or overlapping access, and to see if the
+   size argument is derived from a call to strlen() on the source argument,
+   and if so, issue an appropriate warning.  */
+
+static void
+handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
+{
+  /* Same as stxncpy().  */
+  handle_builtin_stxncpy (bcode, gsi);
 }
 
 /* Return true if LEN depends on a call to strlen(SRC) in an interesting
@@ -1909,9 +1961,10 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt)
   return false;
 }
 
-/* Check the size argument to the built-in forms of stpncpy and strncpy
-   to see if it's derived from calling strlen() on the source argument
-   and if so, issue a warning.  */
+/* Check the arguments to the built-in forms of stpncpy and strncpy for
+   out-of-bounds offsets or overlapping access, and to see if the size
+   is derived from calling strlen() on the source argument, and if so,
+   issue the appropriate warning.  */
 
 static void
 handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
@@ -1923,8 +1976,51 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
 
   bool with_bounds = gimple_call_with_bounds_p (stmt);
 
+  tree dst = gimple_call_arg (stmt, with_bounds ? 1 : 0);
   tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
   tree len = gimple_call_arg (stmt, with_bounds ? 3 : 2);
+  tree dstsize = NULL_TREE, srcsize = NULL_TREE;
+
+  int didx = get_stridx (dst);
+  if (strinfo *sidst = didx > 0 ? get_strinfo (didx) : NULL)
+    {
+      /* Compute the size of the destination string including the NUL.  */
+      if (sidst->nonzero_chars)
+	{
+	  tree type = TREE_TYPE (sidst->nonzero_chars);
+	  dstsize = fold_build2 (PLUS_EXPR, type, sidst->nonzero_chars,
+				 build_int_cst (type, 1));
+	}
+      dst = sidst->ptr;
+    }
+
+  int sidx = get_stridx (src);
+  strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL;
+  if (sisrc)
+    {
+      /* strncat() and strncpy() can modify the source string by writing
+	 over the terminating nul so SISRC->DONT_INVALIDATE must be left
+	 clear.  */
+
+      /* Compute the size of the source string including the NUL.  */
+      if (sisrc->nonzero_chars)
+	{
+	  tree type = TREE_TYPE (sisrc->nonzero_chars);
+	  srcsize = fold_build2 (PLUS_EXPR, type, sisrc->nonzero_chars,
+				 build_int_cst (type, 1));
+	}
+
+	src = sisrc->ptr;
+    }
+  else
+    srcsize = NULL_TREE;
+
+  if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, src,
+				dstsize, srcsize))
+    {
+      gimple_set_no_warning (stmt, true);
+      return;
+    }
 
   /* If the length argument was computed from strlen(S) for some string
      S retrieve the strinfo index for the string (PSS->FIRST) alonng with
@@ -1938,13 +2034,6 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
       return;
     }
 
-  int sidx = get_stridx (src);
-  strinfo *sisrc = sidx > 0 ? get_strinfo (sidx) : NULL;
-
-  /* strncat() and strncpy() can modify the source string by writing
-     over the terminating nul so SISRC->DONT_INVALIDATE must be left
-     clear.  */
-
   /* Retrieve the strinfo data for the string S that LEN was computed
      from as some function F of strlen (S) (i.e., LEN need not be equal
      to strlen(S)).  */
@@ -1981,17 +2070,6 @@ handle_builtin_stxncpy (built_in_function, gimple_stmt_iterator *gsi)
     }
 }
 
-/* Check the size argument to the built-in forms of strncat to see if
-   it's derived from calling strlen() on the source argument and if so,
-   issue a warning.  */
-
-static void
-handle_builtin_strncat (built_in_function bcode, gimple_stmt_iterator *gsi)
-{
-  /* Same as stxncpy().  */
-  handle_builtin_stxncpy (bcode, gsi);
-}
-
 /* Handle a memcpy-like ({mem{,p}cpy,__mem{,p}cpy_chk}) call.
    If strlen of the second argument is known and length of the third argument
    is that plus one, strlen of the first argument is the same after this
@@ -2172,16 +2250,22 @@ static void
 handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
 {
   int idx, didx;
-  tree src, dst, srclen, dstlen, len, lhs, args, type, fn, objsz, endptr;
+  tree srclen, args, type, fn, objsz, endptr;
   bool success;
   gimple *stmt = gsi_stmt (*gsi);
   strinfo *si, *dsi;
-  location_t loc;
+  location_t loc = gimple_location (stmt);
   bool with_bounds = gimple_call_with_bounds_p (stmt);
 
-  src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
-  dst = gimple_call_arg (stmt, 0);
-  lhs = gimple_call_lhs (stmt);
+  tree src = gimple_call_arg (stmt, with_bounds ? 2 : 1);
+  tree dst = gimple_call_arg (stmt, 0);
+
+  /* Bail if the source is the same as destination.  It will be diagnosed
+     elsewhere.  */
+  if (operand_equal_p (src, dst, 0))
+    return;
+
+  tree lhs = gimple_call_lhs (stmt);
 
   didx = get_stridx (dst);
   if (didx < 0)
@@ -2190,10 +2274,48 @@ 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);
+    }
+
+  /* Set the no-warning bit on the transformed statement?  */
+  bool set_no_warning = false;
+
   if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
     {
+      {
+	  /* The concatenation always involves copying at least one byte
+	     (the terminating nul), even if the source string is empty.
+	     If the source is unknown assume it's one character long and
+	     used that as both sizes.  */
+	tree slen = srclen;
+	if (slen)
+	  {
+	    tree type = TREE_TYPE (slen);
+	    slen = fold_build2 (PLUS_EXPR, type, slen, build_int_cst (type, 1));
+	  }
+
+	tree sptr = si && si->ptr ? si->ptr : src;
+
+	if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr,
+				      NULL_TREE, slen))
+	  {
+	    gimple_set_no_warning (stmt, true);
+	    set_no_warning = true;
+	  }
+      }
+
       /* 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.  */
@@ -2226,20 +2348,7 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
       return;
     }
 
-  srclen = NULL_TREE;
-  si = NULL;
-  idx = get_stridx (src);
-  if (idx < 0)
-    srclen = build_int_cst (size_type_node, ~idx);
-  else if (idx > 0)
-    {
-      si = get_strinfo (idx);
-      if (si != NULL)
-	srclen = get_string_length (si);
-    }
-
-  loc = gimple_location (stmt);
-  dstlen = dsi->nonzero_chars;
+  tree dstlen = dsi->nonzero_chars;
   endptr = dsi->endptr;
 
   dsi = unshare_strinfo (dsi);
@@ -2300,7 +2409,25 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
   if (fn == NULL_TREE)
     return;
 
-  len = NULL_TREE;
+  if (dsi && dstlen)
+    {
+      tree type = TREE_TYPE (dstlen);
+
+      /* Compute the size of the source sequence, including the nul.  */
+      tree srcsize = srclen ? srclen : size_zero_node;
+      srcsize = fold_build2 (PLUS_EXPR, type, srcsize, build_int_cst (type, 1));
+
+      tree sptr = si && si->ptr ? si->ptr : src;
+
+      if (!check_bounds_or_overlap (as_a <gcall *>(stmt), dst, sptr,
+				    dstlen, srcsize))
+	{
+	  gimple_set_no_warning (stmt, true);
+	  set_no_warning = true;
+	}
+    }
+
+  tree len = NULL_TREE;
   if (srclen != NULL_TREE)
     {
       args = TYPE_ARG_TYPES (TREE_TYPE (fn));
@@ -2375,6 +2502,9 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi)
     }
   else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
     fprintf (dump_file, "not possible.\n");
+
+  if (set_no_warning)
+    gimple_set_no_warning (stmt, true);
 }
 
 /* Handle a call to malloc or calloc.  */
@@ -2866,11 +2996,11 @@ fold_strstr_to_strncmp (tree rhs1, tree rhs2, gimple *stmt)
     }
 }
 
-/* Attempt to optimize a single statement at *GSI using string length
-   knowledge.  */
+/* Attempt to check for validity of the performed access a single statement
+   at *GSI using string length knowledge, and to optimize it.  */
 
 static bool
-strlen_optimize_stmt (gimple_stmt_iterator *gsi)
+strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi)
 {
   gimple *stmt = gsi_stmt (*gsi);
 
@@ -3146,7 +3276,7 @@ strlen_dom_walker::before_dom_children (basic_block bb)
 
   /* Attempt to optimize individual statements.  */
   for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi); )
-    if (strlen_optimize_stmt (&gsi))
+    if (strlen_check_and_optimize_stmt (&gsi))
       gsi_next (&gsi);
 
   bb->aux = stridx_to_strinfo;
H.J. Lu Dec. 17, 2017, 1:32 p.m. UTC | #16
On Sat, Dec 16, 2017 at 4:01 PM, Martin Sebor <msebor@gmail.com> wrote:
> On 12/11/2017 03:27 PM, Jeff Law wrote:
>>
>> On 12/08/2017 12:19 PM, Martin Sebor wrote:
>>>
>>> Attached is revision 8 of the patch with the changes suggested
>>> and/or requested below.
>>
>>
>> [ Big snip. ]
>>
>>>
>>>
>>> gcc-78918.diff
>>>
>>>
>>> PR tree-optimization/78918 - missing -Wrestrict on memcpy copying over
>>> self
>>>
>>> gcc/c-family/ChangeLog:
>>>
>>>         PR tree-optimization/78918
>>>         * c-common.c (check_function_restrict): Avoid checking built-ins.
>>>         * c.opt (-Wrestrict): Include in -Wall.
>>>
>>> gcc/ChangeLog:
>>>
>>>         PR tree-optimization/78918
>>>         * Makefile.in (OBJS): Add gimple-ssa-warn-restrict.o.
>>>         * builtins.c (check_sizes): Rename...
>>>         (check_access): ...to this.  Rename function arguments for
>>> clarity.
>>>         (check_memop_sizes): Adjust names.
>>>         (expand_builtin_memchr, expand_builtin_memcpy): Same.
>>>         (expand_builtin_memmove, expand_builtin_mempcpy): Same.
>>>         (expand_builtin_strcat, expand_builtin_stpncpy): Same.
>>>         (check_strncat_sizes, expand_builtin_strncat): Same.
>>>         (expand_builtin_strncpy, expand_builtin_memset): Same.
>>>         (expand_builtin_bzero, expand_builtin_memcmp): Same.
>>>         (expand_builtin_memory_chk, maybe_emit_chk_warning): Same.
>>>         (maybe_emit_sprintf_chk_warning): Same.
>>>         (expand_builtin_strcpy): Adjust.
>>>         (expand_builtin_stpcpy): Same.
>>>         (expand_builtin_with_bounds): Detect out-of-bounds accesses
>>>         in pointer-checking forms of memcpy, memmove, and mempcpy.
>>>         (gcall_to_tree_minimal, max_object_size): Define new functions.
>>>         * builtins.h (max_object_size): Declare.
>>>         * calls.c (alloc_max_size): Call max_object_size instead of
>>>         hardcoding ssizetype limit.
>>>         (get_size_range): Handle new argument.
>>>         * calls.h (get_size_range): Add a new argument.
>>>         * cfgexpand.c (expand_call_stmt): Propagate no-warning bit.
>>>         * doc/invoke.texi (-Wrestrict): Adjust, add example.
>>>         * gimple-fold.c (gimple_fold_builtin_memory_op): Detect
>>> overlapping
>>>         operations.
>>>         (gimple_fold_builtin_memory_chk): Same.
>>>         (gimple_fold_builtin_stxcpy_chk): New function.
>>>         * gimple-ssa-warn-restrict.c: New source.
>>>         * gimple-ssa-warn-restrict.h: New header.
>>>         * gimple.c (gimple_build_call_from_tree): Propagate location.
>>>         * passes.def (pass_warn_restrict): Add new pass.
>>>         * tree-pass.h (make_pass_warn_restrict): Declare.
>>>         * tree-ssa-strlen.c (handle_builtin_strcpy): Detect overlapping
>>>         operations.
>>>         (handle_builtin_strcat): Same.
>>>         (strlen_optimize_stmt): Rename...
>>>         (strlen_check_and_optimize_stmt): ...to this.  Handle strncat,
>>>         stpncpy, strncpy, and their checking forms.
>>>
>>> gcc/testsuite/ChangeLog:
>>>
>>>         PR tree-optimization/78918
>>>         * c-c++-common/Warray-bounds.c: New test.
>>>         * c-c++-common/Warray-bounds-2.c: New test.
>>>         * c-c++-common/Warray-bounds-3.c: New test.
>>>         * c-c++-common/Wrestrict-2.c: New test.
>>>         * c-c++-common/Wrestrict.c: New test.
>>>         * c-c++-common/Wrestrict.s: New test.
>>>         * c-c++-common/Wsizeof-pointer-memaccess1.c: Adjust
>>>         * c-c++-common/Wsizeof-pointer-memaccess2.c: Same.
>>>         * g++.dg/torture/Wsizeof-pointer-memaccess1.C: Same.
>>>         * g++.dg/torture/Wsizeof-pointer-memaccess2.C: Same.
>>>         * gcc.dg/memcpy-6.c: New test.
>>>         * gcc.dg/pr69172.c: Adjust.
>>>         * gcc.dg/pr79223.c: Same.
>>>         * gcc.dg/Wrestrict-2.c: New test.
>>>         * gcc.dg/Wrestrict.c: New test.
>>>         * gcc.dg/Wsizeof-pointer-memaccess1.c
>>>         * gcc.target/i386/chkp-stropt-17.c: New test.
>>>         * gcc.dg/torture/Wsizeof-pointer-memaccess1.c: Adjust.
>>
>> OK.  Thanks for your patience.  I know this was a ton of work and even
>> more waiting.
>
>
> Thanks.  In more testing I uncovered a few minor glitches.  I've
> fixed those and committed r255755.  Attached is the committed patch
> for reference.
>
> Martin

This caused:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83446
Tom de Vries Jan. 8, 2018, 9:32 a.m. UTC | #17
On 12/17/2017 01:01 AM, Martin Sebor wrote:

>>>     * c-c++-common/Wrestrict.c: New test.


>    681	  /* The following doesn't overlap but it should trigger -Wstrinop-ovewrflow
>    682	     for writing past the end.  */
>    683	  T ("012", a + sizeof a, a);

For nvptx, the warning actually shows up and is classified as excess error:
...
gcc/testsuite/c-c++-common/Wrestrict.c:683:3: warning: 
'__builtin_memcpy' writing 4 bytes into a region of size 0 overflows the 
destination [-Wstringop-overflow=]
...

>    760	  r = SR (DIFF_MAX - 2, DIFF_MAX - 1);
>    761	  T (8, "012", a + r, a);            /* { dg-warning "accessing 4 bytes at offsets \\\[\[0-9\]+, \[0-9\]+] and 0 overlaps" "strcpy" } */
>    762	

Likewise, the warning triggers here:
...
gcc/testsuite/c-c++-common/Wrestrict.c:761:3: warning: 
'__builtin_memcpy' writing 4 bytes into a region of size 0 overflows the 
destination [-Wstringop-overflow=]
...


 >>>     * c-c++-common/Warray-bounds-4.c: New test.

>     66	  TM ("0123", "",  ma.a5 + i, ma.a5);     /* { dg-warning "offset 6 from the object at .ma. is out of the bounds of referenced subobject .a5. with type .char\\\[5]. at offset 0" "strcpy" { xfail *-*-* } } */
>     67	  TM ("", "012345", ma.a7 + i, ma.a7);    /* { dg-warning "offset 13 from the object at .ma. is out of the bounds of referenced subobject .\(MA::\)?a7. with type .char ?\\\[7]. at offset 5" } */

And this warning fails to trigger:
...
FAIL: c-c++-common/Warray-bounds-4.c  -Wc++-compat   (test for warnings, 
line 67)
...

Thanks,
- Tom
diff mbox series

Patch

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

gcc/c-family/ChangeLog:

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

gcc/ChangeLog:

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

gcc/testsuite/ChangeLog:

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

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