diff mbox series

enable ranger and caching in pass_waccess

Message ID 9a041eca-97c3-f250-8bab-8fadc72cd283@gmail.com
State New
Headers show
Series enable ranger and caching in pass_waccess | expand

Commit Message

Martin Sebor Aug. 19, 2021, 11:09 p.m. UTC
The attached patch changes the new access warning pass to use
the per-function ranger instance.  To do that it makes a number
of the global static functions members of the pass (that involved
moving one to a later point in the file, increasing the diff;
the body of the function hasn't changed otherwise).  Still more
functions remain.  At the same time, the patch also enables
the simple pointer_query cache to avoid repeatedly recomputing
the properties of related pointers into the same objects, and
makes the cache more effective (trunk fails to cache a bunch of
intermediate results).  Finally, the patch enhances the debugging
support for the cache.

Other than the ranger/caching the changes have no user-visible
effect.

Tested on x86_64-linux.

Martin

Previous patches in this series:
https://gcc.gnu.org/pipermail/gcc-patches/2021-August/577526.html
https://gcc.gnu.org/pipermail/gcc-patches/2021-August/576821.html
https://gcc.gnu.org/pipermail/gcc-patches/2021-July/575377.html

Comments

Andrew MacLeod Aug. 20, 2021, 1:09 p.m. UTC | #1
On 8/19/21 7:09 PM, Martin Sebor via Gcc-patches wrote:
> The attached patch changes the new access warning pass to use
> the per-function ranger instance.  To do that it makes a number
> of the global static functions members of the pass (that involved
> moving one to a later point in the file, increasing the diff;
> the body of the function hasn't changed otherwise).  Still more
> functions remain.  At the same time, the patch also enables
> the simple pointer_query cache to avoid repeatedly recomputing
> the properties of related pointers into the same objects, and
> makes the cache more effective (trunk fails to cache a bunch of
> intermediate results).  Finally, the patch enhances the debugging
> support for the cache.
>
> Other than the ranger/caching the changes have no user-visible
> effect.


Why are you calling enable/disable ranger if you are passing a ranger 
instance around instead of using the get_range_query (cfun)->range* calls?

Are you planning to transition to using the get_range_query() interface 
instead of keeping a range_query pointer in the pointer_query class?

Andrew
Martin Sebor Aug. 20, 2021, 10:16 p.m. UTC | #2
On 8/20/21 7:09 AM, Andrew MacLeod wrote:
> On 8/19/21 7:09 PM, Martin Sebor via Gcc-patches wrote:
>> The attached patch changes the new access warning pass to use
>> the per-function ranger instance.  To do that it makes a number
>> of the global static functions members of the pass (that involved
>> moving one to a later point in the file, increasing the diff;
>> the body of the function hasn't changed otherwise).  Still more
>> functions remain.  At the same time, the patch also enables
>> the simple pointer_query cache to avoid repeatedly recomputing
>> the properties of related pointers into the same objects, and
>> makes the cache more effective (trunk fails to cache a bunch of
>> intermediate results).  Finally, the patch enhances the debugging
>> support for the cache.
>>
>> Other than the ranger/caching the changes have no user-visible
>> effect.
> 
> 
> Why are you calling enable/disable ranger if you are passing a ranger 
> instance around instead of using the get_range_query (cfun)->range* calls?

The pass stores an instance of the pointer_query class which in
turn stores a pointer to range_query (which is a copy of the ranger).
So storing it also in pass_waccess isn't necessary and can be
removed.  I've made that change in the attached update.  I'm not
sure the corresponding pointer should at some point also be removed
from the pointer_query class and replaced by calls to get_range_query
(cfun).  If so, that would take some surgery to the strlen pass which
also uses pointer_query and isn't quite ready to make this switch.


> 
> Are you planning to transition to using the get_range_query() interface 
> instead of keeping a range_query pointer in the pointer_query class?

This pass and to a smaller extent the pointer_query class that's
used by it and the strlen pass are still a work in progress.
I also still need to convert the strlen pass to use Ranger and
I expect it will take some changes to pointer_query.  So at that
point, if going through get_range_query (cfun) everywhere is what
you recommend, I'm happy to do it.

Anyway, attached is an updated revision with the m_ranger member
removed and a few helpers changed to take a range_query argument
to use the pointer_query member instead.  It was retested on
x86_64-linux.

Martin

PS There has been an effort to get rid of global variables from GCC,
or, as the first step, to avoid accessing them directly(*).  If and
when that happens, it seems like each pass will have to store either
the ranger instance as a member (directly or indirectly, via a member
of a class that stores it) or the function passed to pass::execute()
if it wants to access either.

[*] https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573975.html
The patch at the link above wasn't approved but IIUC removing globals
from GCC is still a goal.
Martin Sebor Aug. 25, 2021, 3:20 p.m. UTC | #3
Ping: Andrew, did I answer your questions?  Do you (or anyone else)
have any other comments on the latest patch below?

https://gcc.gnu.org/pipermail/gcc-patches/2021-August/577865.html

On 8/20/21 4:16 PM, Martin Sebor wrote:
> On 8/20/21 7:09 AM, Andrew MacLeod wrote:
>> On 8/19/21 7:09 PM, Martin Sebor via Gcc-patches wrote:
>>> The attached patch changes the new access warning pass to use
>>> the per-function ranger instance.  To do that it makes a number
>>> of the global static functions members of the pass (that involved
>>> moving one to a later point in the file, increasing the diff;
>>> the body of the function hasn't changed otherwise).  Still more
>>> functions remain.  At the same time, the patch also enables
>>> the simple pointer_query cache to avoid repeatedly recomputing
>>> the properties of related pointers into the same objects, and
>>> makes the cache more effective (trunk fails to cache a bunch of
>>> intermediate results).  Finally, the patch enhances the debugging
>>> support for the cache.
>>>
>>> Other than the ranger/caching the changes have no user-visible
>>> effect.
>>
>>
>> Why are you calling enable/disable ranger if you are passing a ranger 
>> instance around instead of using the get_range_query (cfun)->range* 
>> calls?
> 
> The pass stores an instance of the pointer_query class which in
> turn stores a pointer to range_query (which is a copy of the ranger).
> So storing it also in pass_waccess isn't necessary and can be
> removed.  I've made that change in the attached update.  I'm not
> sure the corresponding pointer should at some point also be removed
> from the pointer_query class and replaced by calls to get_range_query
> (cfun).  If so, that would take some surgery to the strlen pass which
> also uses pointer_query and isn't quite ready to make this switch.
> 
> 
>>
>> Are you planning to transition to using the get_range_query() 
>> interface instead of keeping a range_query pointer in the 
>> pointer_query class?
> 
> This pass and to a smaller extent the pointer_query class that's
> used by it and the strlen pass are still a work in progress.
> I also still need to convert the strlen pass to use Ranger and
> I expect it will take some changes to pointer_query.  So at that
> point, if going through get_range_query (cfun) everywhere is what
> you recommend, I'm happy to do it.
> 
> Anyway, attached is an updated revision with the m_ranger member
> removed and a few helpers changed to take a range_query argument
> to use the pointer_query member instead.  It was retested on
> x86_64-linux.
> 
> Martin
> 
> PS There has been an effort to get rid of global variables from GCC,
> or, as the first step, to avoid accessing them directly(*).  If and
> when that happens, it seems like each pass will have to store either
> the ranger instance as a member (directly or indirectly, via a member
> of a class that stores it) or the function passed to pass::execute()
> if it wants to access either.
> 
> [*] https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573975.html
> The patch at the link above wasn't approved but IIUC removing globals
> from GCC is still a goal.
Andrew MacLeod Aug. 25, 2021, 4:14 p.m. UTC | #4
On 8/25/21 11:20 AM, Martin Sebor wrote:
> Ping: Andrew, did I answer your questions?  Do you (or anyone else)
> have any other comments on the latest patch below?
>
> https://gcc.gnu.org/pipermail/gcc-patches/2021-August/577865.html


I wasn't attempting to block it, its outside my review purview..

I was merely commenting that you should not need a pointer to a 
range_query at all anymore


>
>>
>>>
>>> Are you planning to transition to using the get_range_query() 
>>> interface instead of keeping a range_query pointer in the 
>>> pointer_query class?
>>
>> This pass and to a smaller extent the pointer_query class that's
>> used by it and the strlen pass are still a work in progress.
>> I also still need to convert the strlen pass to use Ranger and
>> I expect it will take some changes to pointer_query.  So at that
>> point, if going through get_range_query (cfun) everywhere is what
>> you recommend, I'm happy to do it.
>>
absolutely. you should not need to even know whether you have a ranger 
instance running or not. get_range_query will give you whichever is 
active, and there is ALWAYS something active.. defaulting to the global 
version.

the code in get_range() seems to be the end of the call chain which uses 
the pointer and should be consolidated to something much simpler

      if (rvals && stmt)
         {
           if (!rvals->range_of_expr (vr, val, stmt))
             return NULL_TREE;

         <...>

       // ?? This entire function should use get_range_query or
    get_global_range_query (),
       // instead of doing something different for RVALS and global ranges.

       if (!get_global_range_query ()->range_of_expr (vr, val) ||
    vr.undefined_p ())
         return NULL_TREE;


This entire section can boil down to something like

if (!get_range_query (cfun)->range_of_expr (vr, val, stmt))
   return NULL;


>>
>>
>> PS There has been an effort to get rid of global variables from GCC,
>> or, as the first step, to avoid accessing them directly(*).  If and
>> when that happens, it seems like each pass will have to store either
>> the ranger instance as a member (directly or indirectly, via a member
>> of a class that stores it) or the function passed to pass::execute()
>> if it wants to access either.
>>
>> [*] https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573975.html
>> The patch at the link above wasn't approved but IIUC removing globals
>> from GCC is still a goal.
>
I have no idea what direction that is going, but the net effect will be 
the same in the end.  You'll be calling get_range_query() with either 
the function pointer, or the pass pointer, or something.. but you should 
never need to pass around  a pointer to either a ranger or range-query.  
As I said earlier, this will likely be a pass property and accessed thru 
the pass eventually... but until then, use what we have.. cfun.  It'll 
be trivial to transition down the road to whatever the ultimate solution is.

Andrew
Martin Sebor Aug. 25, 2021, 9:26 p.m. UTC | #5
On 8/25/21 10:14 AM, Andrew MacLeod wrote:
> On 8/25/21 11:20 AM, Martin Sebor wrote:
>> Ping: Andrew, did I answer your questions?  Do you (or anyone else)
>> have any other comments on the latest patch below?
>>
>> https://gcc.gnu.org/pipermail/gcc-patches/2021-August/577865.html
> 
> 
> I wasn't attempting to block it, its outside my review purview..
> 
> I was merely commenting that you should not need a pointer to a 
> range_query at all anymore
> 
> 
>>
>>>
>>>>
>>>> Are you planning to transition to using the get_range_query() 
>>>> interface instead of keeping a range_query pointer in the 
>>>> pointer_query class?
>>>
>>> This pass and to a smaller extent the pointer_query class that's
>>> used by it and the strlen pass are still a work in progress.
>>> I also still need to convert the strlen pass to use Ranger and
>>> I expect it will take some changes to pointer_query.  So at that
>>> point, if going through get_range_query (cfun) everywhere is what
>>> you recommend, I'm happy to do it.
>>>
> absolutely. you should not need to even know whether you have a ranger 
> instance running or not. get_range_query will give you whichever is 
> active, and there is ALWAYS something active.. defaulting to the global 
> version.
> 
> the code in get_range() seems to be the end of the call chain which uses 
> the pointer and should be consolidated to something much simpler
> 
>       if (rvals && stmt)
>          {
>            if (!rvals->range_of_expr (vr, val, stmt))
>              return NULL_TREE;
> 
>          <...>
> 
>        // ?? This entire function should use get_range_query or
>     get_global_range_query (),
>        // instead of doing something different for RVALS and global ranges.
> 
>        if (!get_global_range_query ()->range_of_expr (vr, val) ||
>     vr.undefined_p ())
>          return NULL_TREE;
> 
> 
> This entire section can boil down to something like
> 
> if (!get_range_query (cfun)->range_of_expr (vr, val, stmt))
>    return NULL;

So get_range_query (cfun) will never be null?  And no special handling
of INTEGER_CST is needed, or checking for SSA_NAME.  Nice.  Attached
is another revision of the same patch with this function simplified.

(FYI: I expect the whole function to eventually go away, and to make
other similar simplifications, but I'm not there yet.)

>>> PS There has been an effort to get rid of global variables from GCC,
>>> or, as the first step, to avoid accessing them directly(*).  If and
>>> when that happens, it seems like each pass will have to store either
>>> the ranger instance as a member (directly or indirectly, via a member
>>> of a class that stores it) or the function passed to pass::execute()
>>> if it wants to access either.
>>>
>>> [*] https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573975.html
>>> The patch at the link above wasn't approved but IIUC removing globals
>>> from GCC is still a goal.
>>
> I have no idea what direction that is going, but the net effect will be 
> the same in the end.  You'll be calling get_range_query() with either 
> the function pointer, or the pass pointer, or something.. but you should 
> never need to pass around  a pointer to either a ranger or range-query.  
> As I said earlier, this will likely be a pass property and accessed thru 
> the pass eventually... but until then, use what we have.. cfun.  It'll 
> be trivial to transition down the road to whatever the ultimate solution is.

Okay, I'll do that in my future changes.

Martin
Martin Sebor Aug. 30, 2021, 2:27 p.m. UTC | #6
Ping: https://gcc.gnu.org/pipermail/gcc-patches/2021-August/578135.html

Are there any further comments on the patch?

Richard, you were kind enough to review the first two patches in this
series.  Would you mind doing the same for this one?  It continues in
the same vein.

Martin

On 8/25/21 3:26 PM, Martin Sebor wrote:
> On 8/25/21 10:14 AM, Andrew MacLeod wrote:
>> On 8/25/21 11:20 AM, Martin Sebor wrote:
>>> Ping: Andrew, did I answer your questions?  Do you (or anyone else)
>>> have any other comments on the latest patch below?
>>>
>>> https://gcc.gnu.org/pipermail/gcc-patches/2021-August/577865.html
>>
>>
>> I wasn't attempting to block it, its outside my review purview..
>>
>> I was merely commenting that you should not need a pointer to a 
>> range_query at all anymore
>>
>>
>>>
>>>>
>>>>>
>>>>> Are you planning to transition to using the get_range_query() 
>>>>> interface instead of keeping a range_query pointer in the 
>>>>> pointer_query class?
>>>>
>>>> This pass and to a smaller extent the pointer_query class that's
>>>> used by it and the strlen pass are still a work in progress.
>>>> I also still need to convert the strlen pass to use Ranger and
>>>> I expect it will take some changes to pointer_query.  So at that
>>>> point, if going through get_range_query (cfun) everywhere is what
>>>> you recommend, I'm happy to do it.
>>>>
>> absolutely. you should not need to even know whether you have a ranger 
>> instance running or not. get_range_query will give you whichever is 
>> active, and there is ALWAYS something active.. defaulting to the 
>> global version.
>>
>> the code in get_range() seems to be the end of the call chain which 
>> uses the pointer and should be consolidated to something much simpler
>>
>>       if (rvals && stmt)
>>          {
>>            if (!rvals->range_of_expr (vr, val, stmt))
>>              return NULL_TREE;
>>
>>          <...>
>>
>>        // ?? This entire function should use get_range_query or
>>     get_global_range_query (),
>>        // instead of doing something different for RVALS and global 
>> ranges.
>>
>>        if (!get_global_range_query ()->range_of_expr (vr, val) ||
>>     vr.undefined_p ())
>>          return NULL_TREE;
>>
>>
>> This entire section can boil down to something like
>>
>> if (!get_range_query (cfun)->range_of_expr (vr, val, stmt))
>>    return NULL;
> 
> So get_range_query (cfun) will never be null?  And no special handling
> of INTEGER_CST is needed, or checking for SSA_NAME.  Nice.  Attached
> is another revision of the same patch with this function simplified.
> 
> (FYI: I expect the whole function to eventually go away, and to make
> other similar simplifications, but I'm not there yet.)
> 
>>>> PS There has been an effort to get rid of global variables from GCC,
>>>> or, as the first step, to avoid accessing them directly(*).  If and
>>>> when that happens, it seems like each pass will have to store either
>>>> the ranger instance as a member (directly or indirectly, via a member
>>>> of a class that stores it) or the function passed to pass::execute()
>>>> if it wants to access either.
>>>>
>>>> [*] https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573975.html
>>>> The patch at the link above wasn't approved but IIUC removing globals
>>>> from GCC is still a goal.
>>>
>> I have no idea what direction that is going, but the net effect will 
>> be the same in the end.  You'll be calling get_range_query() with 
>> either the function pointer, or the pass pointer, or something.. but 
>> you should never need to pass around  a pointer to either a ranger or 
>> range-query. As I said earlier, this will likely be a pass property 
>> and accessed thru the pass eventually... but until then, use what we 
>> have.. cfun.  It'll be trivial to transition down the road to whatever 
>> the ultimate solution is.
> 
> Okay, I'll do that in my future changes.
> 
> Martin
Richard Biener Aug. 31, 2021, 6 a.m. UTC | #7
On Mon, Aug 30, 2021 at 4:27 PM Martin Sebor <msebor@gmail.com> wrote:
>
> Ping: https://gcc.gnu.org/pipermail/gcc-patches/2021-August/578135.html
>
> Are there any further comments on the patch?
>
> Richard, you were kind enough to review the first two patches in this
> series.  Would you mind doing the same for this one?  It continues in
> the same vein.

OK.

Richard.

> Martin
>
> On 8/25/21 3:26 PM, Martin Sebor wrote:
> > On 8/25/21 10:14 AM, Andrew MacLeod wrote:
> >> On 8/25/21 11:20 AM, Martin Sebor wrote:
> >>> Ping: Andrew, did I answer your questions?  Do you (or anyone else)
> >>> have any other comments on the latest patch below?
> >>>
> >>> https://gcc.gnu.org/pipermail/gcc-patches/2021-August/577865.html
> >>
> >>
> >> I wasn't attempting to block it, its outside my review purview..
> >>
> >> I was merely commenting that you should not need a pointer to a
> >> range_query at all anymore
> >>
> >>
> >>>
> >>>>
> >>>>>
> >>>>> Are you planning to transition to using the get_range_query()
> >>>>> interface instead of keeping a range_query pointer in the
> >>>>> pointer_query class?
> >>>>
> >>>> This pass and to a smaller extent the pointer_query class that's
> >>>> used by it and the strlen pass are still a work in progress.
> >>>> I also still need to convert the strlen pass to use Ranger and
> >>>> I expect it will take some changes to pointer_query.  So at that
> >>>> point, if going through get_range_query (cfun) everywhere is what
> >>>> you recommend, I'm happy to do it.
> >>>>
> >> absolutely. you should not need to even know whether you have a ranger
> >> instance running or not. get_range_query will give you whichever is
> >> active, and there is ALWAYS something active.. defaulting to the
> >> global version.
> >>
> >> the code in get_range() seems to be the end of the call chain which
> >> uses the pointer and should be consolidated to something much simpler
> >>
> >>       if (rvals && stmt)
> >>          {
> >>            if (!rvals->range_of_expr (vr, val, stmt))
> >>              return NULL_TREE;
> >>
> >>          <...>
> >>
> >>        // ?? This entire function should use get_range_query or
> >>     get_global_range_query (),
> >>        // instead of doing something different for RVALS and global
> >> ranges.
> >>
> >>        if (!get_global_range_query ()->range_of_expr (vr, val) ||
> >>     vr.undefined_p ())
> >>          return NULL_TREE;
> >>
> >>
> >> This entire section can boil down to something like
> >>
> >> if (!get_range_query (cfun)->range_of_expr (vr, val, stmt))
> >>    return NULL;
> >
> > So get_range_query (cfun) will never be null?  And no special handling
> > of INTEGER_CST is needed, or checking for SSA_NAME.  Nice.  Attached
> > is another revision of the same patch with this function simplified.
> >
> > (FYI: I expect the whole function to eventually go away, and to make
> > other similar simplifications, but I'm not there yet.)
> >
> >>>> PS There has been an effort to get rid of global variables from GCC,
> >>>> or, as the first step, to avoid accessing them directly(*).  If and
> >>>> when that happens, it seems like each pass will have to store either
> >>>> the ranger instance as a member (directly or indirectly, via a member
> >>>> of a class that stores it) or the function passed to pass::execute()
> >>>> if it wants to access either.
> >>>>
> >>>> [*] https://gcc.gnu.org/pipermail/gcc-patches/2021-June/573975.html
> >>>> The patch at the link above wasn't approved but IIUC removing globals
> >>>> from GCC is still a goal.
> >>>
> >> I have no idea what direction that is going, but the net effect will
> >> be the same in the end.  You'll be calling get_range_query() with
> >> either the function pointer, or the pass pointer, or something.. but
> >> you should never need to pass around  a pointer to either a ranger or
> >> range-query. As I said earlier, this will likely be a pass property
> >> and accessed thru the pass eventually... but until then, use what we
> >> have.. cfun.  It'll be trivial to transition down the road to whatever
> >> the ultimate solution is.
> >
> > Okay, I'll do that in my future changes.
> >
> > Martin
>
diff mbox series

Patch

gcc/ChangeLog:

	* gimple-ssa-warn-access.cc (check_memop_access): Remove template and
	make a member function.
	(maybe_check_dealloc_call): Make a pass_waccess member function.
	(class pass_waccess): Add and rename members.
	(pass_waccess::pass_waccess): Adjust to name change.
	(pass_waccess::~pass_waccess): Same.
	(check_alloca): Make a member function.
	(check_alloc_size_call): Same.
	(check_strcat): Same.
	(check_strncat): Same.
	(check_stxcpy): Same.
	(check_stxncpy): Same.
	(check_strncmp): Same.
	(maybe_warn_rdwr_sizes): Rename...
	(pass_waccess::maybe_check_access_sizes): ...to this.
	(pass_waccess::check_call): Adjust to name changes.
	(pass_waccess::maybe_check_dealloc_call): Make a pass_waccess member
	function.
	(pass_waccess::execute): Adjust to name changes.
	* gimple-ssa-warn-access.h (check_memop_access): Remove.
	* pointer-query.cc (access_ref::phi): Handle null pointer.
	(access_ref::inform_access): Same.
	(pointer_query::put_ref): Modify a cached value, not a copy of it.
	(pointer_query::dump): New function.
	(compute_objsize_r): Avoid overwriting access_ref::bndrng.  Cache
	more results.
	* pointer-query.h (pointer_query::dump): Declare.
	* tree-ssa-strlen.c (printf_strlen_execute): Factor code out into
	pointer_query::put_ref.

gcc/testsuite/ChangeLog:

	* gcc.dg/Wstringop-overflow-73.c: New test.

diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 4a2dd9ade77..4473b093f88 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -1511,41 +1511,6 @@  check_access (tree expr, tree dstwrite,
 			    mode, pad);
 }
 
-/* Helper to determine and check the sizes of the source and the destination
-   of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls.  EXP is the
-   call expression, DEST is the destination argument, SRC is the source
-   argument or null, and LEN is the number of bytes.  Use Object Size type-0
-   regardless of the OPT_Wstringop_overflow_ setting.  Return true on success
-   (no overflow or invalid sizes), false otherwise.  */
-
-template <class GimpleOrTree>
-static bool
-check_memop_access (GimpleOrTree expr, tree dest, tree src, tree size)
-{
-  /* 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
-     type specified by the option.  */
-  access_data data (expr, access_read_write);
-  tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE;
-  tree dstsize = compute_objsize (dest, 0, &data.dst);
-
-  return check_access (expr, size, /*maxread=*/NULL_TREE,
-		       srcsize, dstsize, data.mode, &data);
-}
-
-bool
-check_memop_access (gimple *stmt, tree dest, tree src, tree size)
-{
-  return check_memop_access<gimple *>(stmt, dest, src, size);
-}
-
-bool
-check_memop_access (tree expr, tree dest, tree src, tree size)
-{
-  return check_memop_access<tree>(expr, dest, src, size);
-}
-
 /* A convenience wrapper for check_access above to check access
    by a read-only function like puts.  */
 
@@ -2093,135 +2058,6 @@  warn_dealloc_offset (location_t loc, gimple *call, const access_ref &aref)
   return true;
 }
 
-/* Issue a warning if a deallocation function such as free, realloc,
-   or C++ operator delete is called with an argument not returned by
-   a matching allocation function such as malloc or the corresponding
-   form of C++ operatorn new.  */
-
-static void
-maybe_check_dealloc_call (gcall *call)
-{
-  tree fndecl = gimple_call_fndecl (call);
-  if (!fndecl)
-    return;
-
-  unsigned argno = fndecl_dealloc_argno (fndecl);
-  if ((unsigned) call_nargs (call) <= argno)
-    return;
-
-  tree ptr = gimple_call_arg (call, argno);
-  if (integer_zerop (ptr))
-    return;
-
-  access_ref aref;
-  if (!compute_objsize (ptr, 0, &aref))
-    return;
-
-  tree ref = aref.ref;
-  if (integer_zerop (ref))
-    return;
-
-  tree dealloc_decl = fndecl;
-  location_t loc = gimple_location (call);
-
-  if (DECL_P (ref) || EXPR_P (ref))
-    {
-      /* Diagnose freeing a declared object.  */
-      if (aref.ref_declared ()
-	  && warning_at (loc, OPT_Wfree_nonheap_object,
-			 "%qD called on unallocated object %qD",
-			 dealloc_decl, ref))
-	{
-	  inform (get_location (ref), "declared here");
-	  return;
-	}
-
-      /* Diagnose freeing a pointer that includes a positive offset.
-	 Such a pointer cannot refer to the beginning of an allocated
-	 object.  A negative offset may refer to it.  */
-      if (aref.sizrng[0] != aref.sizrng[1]
-	  && warn_dealloc_offset (loc, call, aref))
-	return;
-    }
-  else if (CONSTANT_CLASS_P (ref))
-    {
-      if (warning_at (loc, OPT_Wfree_nonheap_object,
-		      "%qD called on a pointer to an unallocated "
-		      "object %qE", dealloc_decl, ref))
-	{
-	  if (TREE_CODE (ptr) == SSA_NAME)
-	    {
-	      gimple *def_stmt = SSA_NAME_DEF_STMT (ptr);
-	      if (is_gimple_assign (def_stmt))
-		{
-		  location_t loc = gimple_location (def_stmt);
-		  inform (loc, "assigned here");
-		}
-	    }
-	  return;
-	}
-    }
-  else if (TREE_CODE (ref) == SSA_NAME)
-    {
-      /* Also warn if the pointer argument refers to the result
-	 of an allocation call like alloca or VLA.  */
-      gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
-      if (is_gimple_call (def_stmt))
-	{
-	  bool warned = false;
-	  if (gimple_call_alloc_p (def_stmt))
-	    {
-	      if (matching_alloc_calls_p (def_stmt, dealloc_decl))
-		{
-		  if (warn_dealloc_offset (loc, call, aref))
-		    return;
-		}
-	      else
-		{
-		  tree alloc_decl = gimple_call_fndecl (def_stmt);
-		  const opt_code opt =
-		    (DECL_IS_OPERATOR_NEW_P (alloc_decl)
-		     || DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
-		     ? OPT_Wmismatched_new_delete
-		     : OPT_Wmismatched_dealloc);
-		  warned = warning_at (loc, opt,
-				       "%qD called on pointer returned "
-				       "from a mismatched allocation "
-				       "function", dealloc_decl);
-		}
-	    }
-	  else if (gimple_call_builtin_p (def_stmt, BUILT_IN_ALLOCA)
-		   || gimple_call_builtin_p (def_stmt,
-					     BUILT_IN_ALLOCA_WITH_ALIGN))
-	    warned = warning_at (loc, OPT_Wfree_nonheap_object,
-				 "%qD called on pointer to "
-				 "an unallocated object",
-				 dealloc_decl);
-	  else if (warn_dealloc_offset (loc, call, aref))
-	    return;
-
-	  if (warned)
-	    {
-	      tree fndecl = gimple_call_fndecl (def_stmt);
-	      inform (gimple_location (def_stmt),
-		      "returned from %qD", fndecl);
-	      return;
-	    }
-	}
-      else if (gimple_nop_p (def_stmt))
-	{
-	  ref = SSA_NAME_VAR (ref);
-	  /* Diagnose freeing a pointer that includes a positive offset.  */
-	  if (TREE_CODE (ref) == PARM_DECL
-	      && !aref.deref
-	      && aref.sizrng[0] != aref.sizrng[1]
-	      && aref.offrng[0] > 0 && aref.offrng[1] > 0
-	      && warn_dealloc_offset (loc, call, aref))
-	    return;
-	}
-    }
-}
-
 namespace {
 
 const pass_data pass_data_waccess = {
@@ -2249,6 +2085,11 @@  class pass_waccess : public gimple_opt_pass
   virtual bool gate (function *);
   virtual unsigned int execute (function *);
 
+private:
+  /* Not copyable or assignable.  */
+  pass_waccess (pass_waccess &) = delete;
+  void operator= (pass_waccess &) = delete;
+
   /* Check a call to a built-in function.  */
   bool check_builtin (gcall *);
 
@@ -2259,17 +2100,25 @@  class pass_waccess : public gimple_opt_pass
   void check (basic_block);
 
   /* Check a call to a function.  */
- void check (gcall *);
+  void check (gcall *);
 
-private:
-  /* Not copyable or assignable.  */
-  pass_waccess (pass_waccess &) = delete;
-  void operator= (pass_waccess &) = delete;
+  /* Check a call to the named built-in function.  */
+  void check_alloca (gcall *);
+  void check_alloc_size_call (gcall *);
+  void check_strcat (gcall *);
+  void check_strncat (gcall *);
+  void check_stxcpy (gcall *);
+  void check_stxncpy (gcall *);
+  void check_strncmp (gcall *);
+  void check_memop_access (gimple *, tree, tree, tree);
+
+  void maybe_check_dealloc_call (gcall *);
+  void maybe_check_access_sizes (rdwr_map *, tree, tree, gimple *);
 
   /* A pointer_query object and its cache to store information about
      pointers and their targets in.  */
-  pointer_query ptr_qry;
-  pointer_query::cache_type var_cache;
+  pointer_query m_ptr_qry;
+  pointer_query::cache_type m_var_cache;
 
   gimple_ranger *m_ranger;
 };
@@ -2278,8 +2127,8 @@  private:
 
 pass_waccess::pass_waccess (gcc::context *ctxt)
   : gimple_opt_pass (pass_data_waccess, ctxt),
-    ptr_qry (m_ranger, &var_cache),
-    var_cache (),
+    m_ptr_qry (m_ranger, &m_var_cache),
+    m_var_cache (),
     m_ranger ()
 {
 }
@@ -2288,7 +2137,7 @@  pass_waccess::pass_waccess (gcc::context *ctxt)
 
 pass_waccess::~pass_waccess ()
 {
-  ptr_qry.flush_cache ();
+  m_ptr_qry.flush_cache ();
 }
 
 /* Return true when any checks performed by the pass are enabled.  */
@@ -2476,8 +2325,8 @@  maybe_warn_alloc_args_overflow (gimple *stmt, const tree args[2],
 
 /* Check a call to an alloca function for an excessive size.  */
 
-static void
-check_alloca (gimple *stmt)
+void
+pass_waccess::check_alloca (gcall *stmt)
 {
   if ((warn_vla_limit >= HOST_WIDE_INT_MAX
        && warn_alloc_size_limit < warn_vla_limit)
@@ -2497,8 +2346,8 @@  check_alloca (gimple *stmt)
 
 /* Check a call to an allocation function for an excessive size.  */
 
-static void
-check_alloc_size_call (gimple *stmt)
+void
+pass_waccess::check_alloc_size_call (gcall *stmt)
 {
   if (gimple_call_num_args (stmt) < 1)
     /* Avoid invalid calls to functions without a prototype.  */
@@ -2547,8 +2396,8 @@  check_alloc_size_call (gimple *stmt)
 
 /* Check a call STMT to strcat() for overflow and warn if it does.  */
 
-static void
-check_strcat (gimple *stmt)
+void
+pass_waccess::check_strcat (gcall *stmt)
 {
   if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
@@ -2563,8 +2412,8 @@  check_strcat (gimple *stmt)
   access_data data (stmt, access_read_write, NULL_TREE, true,
 		    NULL_TREE, true);
   const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-  compute_objsize (src, ost, &data.src);
-  tree destsize = compute_objsize (dest, ost, &data.dst);
+  compute_objsize (src, ost, &data.src, &m_ptr_qry);
+  tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
 
   check_access (stmt, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
 		src, destsize, data.mode, &data);
@@ -2572,8 +2421,8 @@  check_strcat (gimple *stmt)
 
 /* Check a call STMT to strcat() for overflow and warn if it does.  */
 
-static void
-check_strncat (gimple *stmt)
+void
+pass_waccess::check_strncat (gcall *stmt)
 {
   if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
@@ -2605,7 +2454,8 @@  check_strncat (gimple *stmt)
   /* Try to verify that the destination is big enough for the shortest
      string.  First try to determine the size of the destination object
      into which the source is being copied.  */
-  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
+  const int ost = warn_stringop_overflow - 1;
+  tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
 
   /* Add one for the terminating nul.  */
   tree srclen = (maxlen
@@ -2640,8 +2490,8 @@  check_strncat (gimple *stmt)
 /* Check a call STMT to stpcpy() or strcpy() for overflow and warn
    if it does.  */
 
-static void
-check_stxcpy (gimple *stmt)
+void
+pass_waccess::check_stxcpy (gcall *stmt)
 {
   tree dst = call_arg (stmt, 0);
   tree src = call_arg (stmt, 1);
@@ -2661,8 +2511,8 @@  check_stxcpy (gimple *stmt)
       access_data data (stmt, access_read_write, NULL_TREE, true,
 			NULL_TREE, true);
       const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-      compute_objsize (src, ost, &data.src);
-      tree dstsize = compute_objsize (dst, ost, &data.dst);
+      compute_objsize (src, ost, &data.src, &m_ptr_qry);
+      tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
       check_access (stmt, /*dstwrite=*/ NULL_TREE,
 		    /*maxread=*/ NULL_TREE, /*srcstr=*/ src,
 		    dstsize, data.mode, &data);
@@ -2678,8 +2528,8 @@  check_stxcpy (gimple *stmt)
 /* Check a call STMT to stpncpy() or strncpy() for overflow and warn
    if it does.  */
 
-static void
-check_stxncpy (gimple *stmt)
+void
+pass_waccess::check_stxncpy (gcall *stmt)
 {
   if (!warn_stringop_overflow)
     return;
@@ -2691,8 +2541,8 @@  check_stxncpy (gimple *stmt)
 
   access_data data (stmt, access_read_write, len, true, len, true);
   const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-  compute_objsize (src, ost, &data.src);
-  tree dstsize = compute_objsize (dst, ost, &data.dst);
+  compute_objsize (src, ost, &data.src, &m_ptr_qry);
+  tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
 
   check_access (stmt, /*dstwrite=*/len,
 		/*maxread=*/len, src, dstsize, data.mode, &data);
@@ -2701,8 +2551,8 @@  check_stxncpy (gimple *stmt)
 /* Check a call STMT to stpncpy() or strncpy() for overflow and warn
    if it does.  */
 
-static void
-check_strncmp (gimple *stmt)
+void
+pass_waccess::check_strncmp (gcall *stmt)
 {
   if (!warn_stringop_overread)
     return;
@@ -2757,8 +2607,8 @@  check_strncmp (gimple *stmt)
 
   /* compute_objsize almost never fails (and ultimately should never
      fail).  Don't bother to handle the rare case when it does.  */
-  if (!compute_objsize (arg1, 1, &adata1.src)
-      || !compute_objsize (arg2, 1, &adata2.src))
+  if (!compute_objsize (arg1, 1, &adata1.src, &m_ptr_qry)
+      || !compute_objsize (arg2, 1, &adata2.src, &m_ptr_qry))
     return;
 
   /* Compute the size of the remaining space in each array after
@@ -2792,6 +2642,29 @@  check_strncmp (gimple *stmt)
     }
 }
 
+/* Determine and check the sizes of the source and the destination
+   of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls.  STMT is
+   the call statement, DEST is the destination argument, SRC is the source
+   argument or null, and SIZE is the number of bytes being accessed.  Use
+   Object Size type-0 regardless of the OPT_Wstringop_overflow_ setting.
+   Return true on success (no overflow or invalid sizes), false otherwise.  */
+
+void
+pass_waccess::check_memop_access (gimple *stmt, tree dest, tree src, tree size)
+{
+  /* 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
+     type specified by the option.  */
+  access_data data (stmt, access_read_write);
+  tree srcsize
+    = src ? compute_objsize (src, 0, &data.src, &m_ptr_qry) : NULL_TREE;
+  tree dstsize = compute_objsize (dest, 0, &data.dst, &m_ptr_qry);
+
+  check_access (stmt, size, /*maxread=*/NULL_TREE,
+		srcsize, dstsize, data.mode, &data);
+}
+
 /* Check call STMT to a built-in function for invalid accesses.  Return
    true if a call has been handled.  */
 
@@ -2950,8 +2823,9 @@  append_attrname (const std::pair<int, attr_access> &access,
    arguments and diagnose past-the-end accesses and related problems
    in the function call EXP.  */
 
-static void
-maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, gimple *stmt)
+void
+pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
+					gimple *stmt)
 {
   auto_diagnostic_group adg;
 
@@ -3142,7 +3016,7 @@  maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, gimple *stmt)
 			NULL_TREE, false);
       access_ref* const pobj = (access.second.mode == access_write_only
 				? &data.dst : &data.src);
-      tree objsize = compute_objsize (ptr, 1, pobj);
+      tree objsize = compute_objsize (ptr, 1, pobj, &m_ptr_qry);
 
       /* The size of the destination or source object.  */
       tree dstsize = NULL_TREE, srcsize = NULL_TREE;
@@ -3258,7 +3132,7 @@  pass_waccess::check_call (gcall *stmt)
 
   /* Check attribute access arguments.  */
   tree fndecl = gimple_call_fndecl (stmt);
-  maybe_warn_rdwr_sizes (&rdwr_idx, fndecl, fntype, stmt);
+  maybe_check_access_sizes (&rdwr_idx, fndecl, fntype, stmt);
 
   check_alloc_size_call (stmt);
   return true;
@@ -3276,6 +3150,138 @@  check_nonstring_args (gcall *stmt)
   maybe_warn_nonstring_arg (fndecl, stmt);
 }
 
+/* Issue a warning if a deallocation function such as free, realloc,
+   or C++ operator delete is called with an argument not returned by
+   a matching allocation function such as malloc or the corresponding
+   form of C++ operatorn new.  */
+
+void
+pass_waccess::maybe_check_dealloc_call (gcall *call)
+{
+  tree fndecl = gimple_call_fndecl (call);
+  if (!fndecl)
+    return;
+
+  unsigned argno = fndecl_dealloc_argno (fndecl);
+  if ((unsigned) call_nargs (call) <= argno)
+    return;
+
+  tree ptr = gimple_call_arg (call, argno);
+  if (integer_zerop (ptr))
+    return;
+
+  access_ref aref;
+  if (!compute_objsize (ptr, 0, &aref, &m_ptr_qry))
+    return;
+
+  tree ref = aref.ref;
+  if (integer_zerop (ref))
+    return;
+
+  tree dealloc_decl = fndecl;
+  location_t loc = gimple_location (call);
+
+  if (DECL_P (ref) || EXPR_P (ref))
+    {
+      /* Diagnose freeing a declared object.  */
+      if (aref.ref_declared ()
+	  && warning_at (loc, OPT_Wfree_nonheap_object,
+			 "%qD called on unallocated object %qD",
+			 dealloc_decl, ref))
+	{
+	  inform (get_location (ref), "declared here");
+	  return;
+	}
+
+      /* Diagnose freeing a pointer that includes a positive offset.
+	 Such a pointer cannot refer to the beginning of an allocated
+	 object.  A negative offset may refer to it.  */
+      if (aref.sizrng[0] != aref.sizrng[1]
+	  && warn_dealloc_offset (loc, call, aref))
+	return;
+    }
+  else if (CONSTANT_CLASS_P (ref))
+    {
+      if (warning_at (loc, OPT_Wfree_nonheap_object,
+		      "%qD called on a pointer to an unallocated "
+		      "object %qE", dealloc_decl, ref))
+	{
+	  if (TREE_CODE (ptr) == SSA_NAME)
+	    {
+	      gimple *def_stmt = SSA_NAME_DEF_STMT (ptr);
+	      if (is_gimple_assign (def_stmt))
+		{
+		  location_t loc = gimple_location (def_stmt);
+		  inform (loc, "assigned here");
+		}
+	    }
+	  return;
+	}
+    }
+  else if (TREE_CODE (ref) == SSA_NAME)
+    {
+      /* Also warn if the pointer argument refers to the result
+	 of an allocation call like alloca or VLA.  */
+      gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
+      if (!def_stmt)
+	return;
+
+      if (is_gimple_call (def_stmt))
+	{
+	  bool warned = false;
+	  if (gimple_call_alloc_p (def_stmt))
+	    {
+	      if (matching_alloc_calls_p (def_stmt, dealloc_decl))
+		{
+		  if (warn_dealloc_offset (loc, call, aref))
+		    return;
+		}
+	      else
+		{
+		  tree alloc_decl = gimple_call_fndecl (def_stmt);
+		  const opt_code opt =
+		    (DECL_IS_OPERATOR_NEW_P (alloc_decl)
+		     || DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
+		     ? OPT_Wmismatched_new_delete
+		     : OPT_Wmismatched_dealloc);
+		  warned = warning_at (loc, opt,
+				       "%qD called on pointer returned "
+				       "from a mismatched allocation "
+				       "function", dealloc_decl);
+		}
+	    }
+	  else if (gimple_call_builtin_p (def_stmt, BUILT_IN_ALLOCA)
+		   || gimple_call_builtin_p (def_stmt,
+					     BUILT_IN_ALLOCA_WITH_ALIGN))
+	    warned = warning_at (loc, OPT_Wfree_nonheap_object,
+				 "%qD called on pointer to "
+				 "an unallocated object",
+				 dealloc_decl);
+	  else if (warn_dealloc_offset (loc, call, aref))
+	    return;
+
+	  if (warned)
+	    {
+	      tree fndecl = gimple_call_fndecl (def_stmt);
+	      inform (gimple_location (def_stmt),
+		      "returned from %qD", fndecl);
+	      return;
+	    }
+	}
+      else if (gimple_nop_p (def_stmt))
+	{
+	  ref = SSA_NAME_VAR (ref);
+	  /* Diagnose freeing a pointer that includes a positive offset.  */
+	  if (TREE_CODE (ref) == PARM_DECL
+	      && !aref.deref
+	      && aref.sizrng[0] != aref.sizrng[1]
+	      && aref.offrng[0] > 0 && aref.offrng[1] > 0
+	      && warn_dealloc_offset (loc, call, aref))
+	    return;
+	}
+    }
+}
+
 /* Check call STMT for invalid accesses.  */
 
 void
@@ -3317,8 +3323,15 @@  pass_waccess::execute (function *fun)
   FOR_EACH_BB_FN (bb, fun)
     check (bb);
 
-  /* Release the ranger instance and replace it with a global ranger.  */
+  if (dump_file)
+    m_ptr_qry.dump (dump_file, (dump_flags & TDF_DETAILS) != 0);
+
+  m_ptr_qry.flush_cache ();
+
+  /* Release the ranger instance and replace it with a global ranger.
+     Also reset the pointer since calling disable_ranger() deletes it.  */
   disable_ranger (fun);
+  m_ranger = NULL;
 
   return 0;
 }
diff --git a/gcc/gimple-ssa-warn-access.h b/gcc/gimple-ssa-warn-access.h
index 1cd3a28c421..00f5bb1a7b2 100644
--- a/gcc/gimple-ssa-warn-access.h
+++ b/gcc/gimple-ssa-warn-access.h
@@ -45,7 +45,6 @@  class access_data;
 extern bool check_access (tree, tree, tree, tree, tree, access_mode,
 			  const access_data * = NULL);
 
-extern bool check_memop_access (tree, tree, tree, tree);
 extern bool check_read_access (gimple *, tree, tree = NULL_TREE, int ost = 1);
 extern bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
 
diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 99caf78bfa7..af622ba04b2 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -34,10 +34,13 @@ 
 #include "stringpool.h"
 #include "attribs.h"
 #include "gimple-fold.h"
+#include "gimple-ssa.h"
 #include "intl.h"
 #include "attr-fnspec.h"
 #include "gimple-range.h"
 #include "pointer-query.h"
+#include "tree-pretty-print.h"
+#include "tree-ssanames.h"
 
 static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
 			       pointer_query *);
@@ -628,7 +631,7 @@  access_ref::phi () const
     return NULL;
 
   gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
-  if (gimple_code (def_stmt) != GIMPLE_PHI)
+  if (!def_stmt || gimple_code (def_stmt) != GIMPLE_PHI)
     return NULL;
 
   return as_a <gphi *> (def_stmt);
@@ -1042,6 +1045,9 @@  access_ref::inform_access (access_mode mode) const
   if (TREE_CODE (ref) == SSA_NAME)
     {
       gimple *stmt = SSA_NAME_DEF_STMT (ref);
+      if (!stmt)
+	return;
+
       if (is_gimple_call (stmt))
 	{
 	  loc = gimple_location (stmt);
@@ -1343,7 +1349,7 @@  pointer_query::put_ref (tree ptr, const access_ref &ref, int ostype /* = 1 */)
   if (var_cache->access_refs.length () <= cache_idx)
     var_cache->access_refs.safe_grow_cleared (cache_idx + 1);
 
-  access_ref cache_ref = var_cache->access_refs[cache_idx - 1];
+  access_ref &cache_ref = var_cache->access_refs[cache_idx];
   if (cache_ref.ref)
   {
     gcc_checking_assert (cache_ref.ref == ref.ref);
@@ -1364,6 +1370,102 @@  pointer_query::flush_cache ()
   var_cache->access_refs.release ();
 }
 
+/* Dump statistics and, optionally, cache contents to DUMP_FILE.  */
+
+void
+pointer_query::dump (FILE *dump_file, bool contents /* = false */)
+{
+  unsigned nused = 0, nrefs = 0;
+  unsigned nidxs = var_cache->indices.length ();
+  for (unsigned i = 0; i != nidxs; ++i)
+    {
+      unsigned ari = var_cache->indices[i];
+      if (!ari)
+	continue;
+
+      ++nused;
+
+      const access_ref &aref = var_cache->access_refs[ari];
+      if (!aref.ref)
+	continue;
+
+      ++nrefs;
+    }
+
+  fprintf (dump_file, "pointer_query counters:\n"
+	   "  index cache size:   %u\n"
+	   "  index entries:      %u\n"
+	   "  access cache size:  %u\n"
+	   "  access entries:     %u\n"
+	   "  hits:               %u\n"
+	   "  misses:             %u\n"
+	   "  failures:           %u\n"
+	   "  max_depth:          %u\n",
+	   nidxs, nused,
+	   var_cache->access_refs.length (), nrefs,
+	   hits, misses, failures, max_depth);
+
+  if (!contents || !nidxs)
+    return;
+
+  fputs ("\npointer_query cache contents:\n", dump_file);
+
+  for (unsigned i = 0; i != nidxs; ++i)
+    {
+      unsigned ari = var_cache->indices[i];
+      if (!ari)
+	continue;
+
+      const access_ref &aref = var_cache->access_refs[ari];
+      if (!aref.ref)
+	continue;
+
+      /* The level-1 cache index corresponds to the SSA_NAME_VERSION
+	 shifted left by one and ORed with the Object Size Type in
+	 the lowest bit.  Print the two separately.  */
+      unsigned ver = i >> 1;
+      unsigned ost = i & 1;
+
+      fprintf (dump_file, "  %u.%u[%u]: ", ver, ost, ari);
+      if (tree name = ssa_name (ver))
+	{
+	  print_generic_expr (dump_file, name);
+	  fputs (" = ", dump_file);
+	}
+      else
+	fprintf (dump_file, "  _%u = ", ver);
+
+      if (gphi *phi = aref.phi ())
+	{
+	  fputs ("PHI <", dump_file);
+	  unsigned nargs = gimple_phi_num_args (phi);
+	  for (unsigned i = 0; i != nargs; ++i)
+	    {
+	      tree arg = gimple_phi_arg_def (phi, i);
+	      print_generic_expr (dump_file, arg);
+	      if (i + 1 < nargs)
+		fputs (", ", dump_file);
+	    }
+	  fputc ('>', dump_file);
+	}
+      else
+	print_generic_expr (dump_file, aref.ref);
+
+      if (aref.offrng[0] != aref.offrng[1])
+	fprintf (dump_file, " + [%lli, %lli]",
+		 (long long) aref.offrng[0].to_shwi (),
+		 (long long) aref.offrng[1].to_shwi ());
+      else if (aref.offrng[0] != 0)
+	fprintf (dump_file, " %c %lli",
+		 aref.offrng[0] < 0 ? '-' : '+',
+		 (long long) aref.offrng[0].to_shwi ());
+
+      fputc ('\n', dump_file);
+    }
+
+  fputc ('\n', dump_file);
+}
+
 /* A helper of compute_objsize_r() to determine the size from an assignment
    statement STMT with the RHS of either MIN_EXPR or MAX_EXPR.  */
 
@@ -1778,8 +1880,14 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 	  if (const access_ref *cache_ref = qry->get_ref (ptr))
 	    {
 	      /* If the pointer is in the cache set *PREF to what it refers
-		 to and return success.  */
+		 to and return success.
+		 FIXME: BNDRNG is determined by each access and so it doesn't
+		 belong in access_ref.  Until the design is changed, keep it
+		 unchanged here.  */
+	      const offset_int bndrng[2] = { pref->bndrng[0], pref->bndrng[1] };
 	      *pref = *cache_ref;
+	      pref->bndrng[0] = bndrng[0];
+	      pref->bndrng[1] = bndrng[1];
 	      return true;
 	    }
 	}
@@ -1928,9 +2036,13 @@  compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 	  return true;
 	}
 
-      if (code == ADDR_EXPR
-	  || code == SSA_NAME)
-	return compute_objsize_r (rhs, ostype, pref, snlim, qry);
+      if (code == ADDR_EXPR || code == SSA_NAME)
+	{
+	  if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
+	    return false;
+	  qry->put_ref (ptr, *pref);
+	  return true;
+	}
 
       /* (This could also be an assignment from a nonlocal pointer.)  Save
 	 PTR to mention in diagnostics but otherwise treat it as a pointer
diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h
index eb7e90dde03..3c8172c652d 100644
--- a/gcc/pointer-query.h
+++ b/gcc/pointer-query.h
@@ -186,6 +186,9 @@  public:
   /* Flush the cache.  */
   void flush_cache ();
 
+  /* Dump statistics and optionally cache contents to DUMP_FILE.  */
+  void dump (FILE *, bool = false);
+
   /* A Ranger instance.  May be null to use global ranges.  */
   range_query *rvals;
   /* Cache of SSA_NAMEs.  May be null to disable caching.  */
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-73.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-73.c
new file mode 100644
index 00000000000..0bb4afecc7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-73.c
@@ -0,0 +1,35 @@ 
+/*
+  { dg-do compile }
+  { dg-options "-Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+int memcmp (const void*, const void*, size_t);
+int strncmp (const char*, const char*, size_t);
+char* stpncpy (char*, const char*, size_t);
+char* strncpy (char*, const char*, size_t);
+
+extern char a4[4], b5[5];
+
+struct A { char a4[4]; };
+
+extern volatile int i;
+extern void* volatile ptr;
+
+void test_stpncpy (struct A *p)
+{
+  ptr = stpncpy (a4, b5, 4);
+  ptr = stpncpy (a4, b5, 5);      // { dg-warning "writing 5 bytes" }
+
+  ptr = stpncpy (p->a4, b5, 4);
+  ptr = stpncpy (p->a4, b5, 5);   // { dg-warning "writing 5 bytes" }
+}
+
+void test_strncpy (struct A *p)
+{
+  ptr = strncpy (a4, b5, 4);
+  ptr = strncpy (a4, b5, 5);      // { dg-warning "writing 5 bytes" }
+
+  ptr = strncpy (p->a4, b5, 4);
+  ptr = strncpy (p->a4, b5, 5);   // { dg-warning "writing 5 bytes" }
+}
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 15391da8104..11d1712576d 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -5820,27 +5820,7 @@  printf_strlen_execute (function *fun, bool warn_only)
   walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
 
   if (dump_file && (dump_flags & TDF_DETAILS))
-    {
-      unsigned nused = 0;
-      unsigned nidxs = walker.ptr_qry.var_cache->indices.length ();
-      for (unsigned i = 0; i != nidxs; ++i)
-	if (walker.ptr_qry.var_cache->indices[i])
-	  ++nused;
-
-      fprintf (dump_file, "pointer_query counters\n"
-	       "  index cache size:  %u\n"
-	       "  utilization:       %u%%\n"
-	       "  access cache size: %u\n"
-	       "  hits:              %u\n"
-	       "  misses:            %u\n"
-	       "  failures:          %u\n"
-	       "  max_depth:         %u\n",
-	       nidxs,
-	       nidxs == 0 ? 0 : (nused * 100) / nidxs,
-	       walker.ptr_qry.var_cache->access_refs.length (),
-	       walker.ptr_qry.hits, walker.ptr_qry.misses,
-	       walker.ptr_qry.failures, walker.ptr_qry.max_depth);
-    }
+    walker.ptr_qry.dump (dump_file);
 
   ssa_ver_to_stridx.release ();
   strinfo_pool.release ();