fix up compute_objsize (including PR 103143)

Message ID 65d1e530-a4cc-de27-1198-0dcaa08274bd@gmail.com
State New
Headers
Series fix up compute_objsize (including PR 103143) |

Commit Message

Martin Sebor Nov. 9, 2021, 2:34 a.m. UTC
  The pointer-query code that implements compute_objsize() that's
in turn used by most middle end access warnings now has a few
warts in it and (at least) one bug.  With the exception of
the bug the warts aren't behind any user-visible bugs that
I know of but they do cause problems in new code I've been
implementing on top of it.  Besides fixing the one bug (just
a typo) the attached patch cleans up these latent issues:

1) It moves the bndrng member from the access_ref class to
    access_data.  As a FIXME in the code notes, the member never
    did belong in the former and only takes up space in the cache.

2) The compute_objsize_r() function is big, unwieldy, and tedious
    to step through because of all the if statements that are better
    coded as one switch statement.  This change factors out more
    of its code into smaller handler functions as has been suggested
    and done a few times before.

3) (2) exposed a few places where I fail to pass the current
    GIMPLE statement down to ranger.  This leads to worse quality
    range info, including possible false positives and negatives.
    I just spotted these problems in code review but I haven't
    taken the time to come up with test cases.  This change fixes
    these oversights as well.

4) The handling of PHI statements is also in one big, hard-to-
    follow function.  This change moves the handling of each PHI
    argument into its own handler which merges it into the previous
    argument.  This makes the code easier to work with and opens it
    to reuse also for MIN_EXPR and MAX_EXPR.  (This is primarily
    used to print informational notes after warnings.)

5) Finally, the patch factors code to dump each access_ref
    cached by the pointer_query cache out of pointer_query::dump
    and into access_ref::dump.  This helps with debugging.

These changes should have no user-visible effect and other than
a regression test for the typo (PR 103143) come with no tests.
They've been tested on x86_64-linux.

Martin
  

Comments

Martin Sebor Nov. 15, 2021, 4:49 p.m. UTC | #1
Ping for the following cleanup patch:
https://gcc.gnu.org/pipermail/gcc-patches/2021-November/583735.html

On 11/8/21 7:34 PM, Martin Sebor wrote:
> The pointer-query code that implements compute_objsize() that's
> in turn used by most middle end access warnings now has a few
> warts in it and (at least) one bug.  With the exception of
> the bug the warts aren't behind any user-visible bugs that
> I know of but they do cause problems in new code I've been
> implementing on top of it.  Besides fixing the one bug (just
> a typo) the attached patch cleans up these latent issues:
> 
> 1) It moves the bndrng member from the access_ref class to
>     access_data.  As a FIXME in the code notes, the member never
>     did belong in the former and only takes up space in the cache.
> 
> 2) The compute_objsize_r() function is big, unwieldy, and tedious
>     to step through because of all the if statements that are better
>     coded as one switch statement.  This change factors out more
>     of its code into smaller handler functions as has been suggested
>     and done a few times before.
> 
> 3) (2) exposed a few places where I fail to pass the current
>     GIMPLE statement down to ranger.  This leads to worse quality
>     range info, including possible false positives and negatives.
>     I just spotted these problems in code review but I haven't
>     taken the time to come up with test cases.  This change fixes
>     these oversights as well.
> 
> 4) The handling of PHI statements is also in one big, hard-to-
>     follow function.  This change moves the handling of each PHI
>     argument into its own handler which merges it into the previous
>     argument.  This makes the code easier to work with and opens it
>     to reuse also for MIN_EXPR and MAX_EXPR.  (This is primarily
>     used to print informational notes after warnings.)
> 
> 5) Finally, the patch factors code to dump each access_ref
>     cached by the pointer_query cache out of pointer_query::dump
>     and into access_ref::dump.  This helps with debugging.
> 
> These changes should have no user-visible effect and other than
> a regression test for the typo (PR 103143) come with no tests.
> They've been tested on x86_64-linux.
> 
> Martin
  
Martin Sebor Nov. 22, 2021, 4:41 p.m. UTC | #2
Ping:
https://gcc.gnu.org/pipermail/gcc-patches/2021-November/583735.html

On 11/15/21 9:49 AM, Martin Sebor wrote:
> Ping for the following cleanup patch:
> https://gcc.gnu.org/pipermail/gcc-patches/2021-November/583735.html
> 
> On 11/8/21 7:34 PM, Martin Sebor wrote:
>> The pointer-query code that implements compute_objsize() that's
>> in turn used by most middle end access warnings now has a few
>> warts in it and (at least) one bug.  With the exception of
>> the bug the warts aren't behind any user-visible bugs that
>> I know of but they do cause problems in new code I've been
>> implementing on top of it.  Besides fixing the one bug (just
>> a typo) the attached patch cleans up these latent issues:
>>
>> 1) It moves the bndrng member from the access_ref class to
>>     access_data.  As a FIXME in the code notes, the member never
>>     did belong in the former and only takes up space in the cache.
>>
>> 2) The compute_objsize_r() function is big, unwieldy, and tedious
>>     to step through because of all the if statements that are better
>>     coded as one switch statement.  This change factors out more
>>     of its code into smaller handler functions as has been suggested
>>     and done a few times before.
>>
>> 3) (2) exposed a few places where I fail to pass the current
>>     GIMPLE statement down to ranger.  This leads to worse quality
>>     range info, including possible false positives and negatives.
>>     I just spotted these problems in code review but I haven't
>>     taken the time to come up with test cases.  This change fixes
>>     these oversights as well.
>>
>> 4) The handling of PHI statements is also in one big, hard-to-
>>     follow function.  This change moves the handling of each PHI
>>     argument into its own handler which merges it into the previous
>>     argument.  This makes the code easier to work with and opens it
>>     to reuse also for MIN_EXPR and MAX_EXPR.  (This is primarily
>>     used to print informational notes after warnings.)
>>
>> 5) Finally, the patch factors code to dump each access_ref
>>     cached by the pointer_query cache out of pointer_query::dump
>>     and into access_ref::dump.  This helps with debugging.
>>
>> These changes should have no user-visible effect and other than
>> a regression test for the typo (PR 103143) come with no tests.
>> They've been tested on x86_64-linux.
>>
>> Martin
>
  
Martin Sebor Nov. 29, 2021, 3:49 p.m. UTC | #3
Ping:
https://gcc.gnu.org/pipermail/gcc-patches/2021-November/583735.html

On 11/22/21 9:41 AM, Martin Sebor wrote:
> Ping:
> https://gcc.gnu.org/pipermail/gcc-patches/2021-November/583735.html
> 
> On 11/15/21 9:49 AM, Martin Sebor wrote:
>> Ping for the following cleanup patch:
>> https://gcc.gnu.org/pipermail/gcc-patches/2021-November/583735.html
>>
>> On 11/8/21 7:34 PM, Martin Sebor wrote:
>>> The pointer-query code that implements compute_objsize() that's
>>> in turn used by most middle end access warnings now has a few
>>> warts in it and (at least) one bug.  With the exception of
>>> the bug the warts aren't behind any user-visible bugs that
>>> I know of but they do cause problems in new code I've been
>>> implementing on top of it.  Besides fixing the one bug (just
>>> a typo) the attached patch cleans up these latent issues:
>>>
>>> 1) It moves the bndrng member from the access_ref class to
>>>     access_data.  As a FIXME in the code notes, the member never
>>>     did belong in the former and only takes up space in the cache.
>>>
>>> 2) The compute_objsize_r() function is big, unwieldy, and tedious
>>>     to step through because of all the if statements that are better
>>>     coded as one switch statement.  This change factors out more
>>>     of its code into smaller handler functions as has been suggested
>>>     and done a few times before.
>>>
>>> 3) (2) exposed a few places where I fail to pass the current
>>>     GIMPLE statement down to ranger.  This leads to worse quality
>>>     range info, including possible false positives and negatives.
>>>     I just spotted these problems in code review but I haven't
>>>     taken the time to come up with test cases.  This change fixes
>>>     these oversights as well.
>>>
>>> 4) The handling of PHI statements is also in one big, hard-to-
>>>     follow function.  This change moves the handling of each PHI
>>>     argument into its own handler which merges it into the previous
>>>     argument.  This makes the code easier to work with and opens it
>>>     to reuse also for MIN_EXPR and MAX_EXPR.  (This is primarily
>>>     used to print informational notes after warnings.)
>>>
>>> 5) Finally, the patch factors code to dump each access_ref
>>>     cached by the pointer_query cache out of pointer_query::dump
>>>     and into access_ref::dump.  This helps with debugging.
>>>
>>> These changes should have no user-visible effect and other than
>>> a regression test for the typo (PR 103143) come with no tests.
>>> They've been tested on x86_64-linux.
>>>
>>> Martin
>>
>
  
Jeff Law Dec. 4, 2021, midnight UTC | #4
On 11/8/2021 7:34 PM, Martin Sebor via Gcc-patches wrote:
> The pointer-query code that implements compute_objsize() that's
> in turn used by most middle end access warnings now has a few
> warts in it and (at least) one bug.  With the exception of
> the bug the warts aren't behind any user-visible bugs that
> I know of but they do cause problems in new code I've been
> implementing on top of it.  Besides fixing the one bug (just
> a typo) the attached patch cleans up these latent issues:
>
> 1) It moves the bndrng member from the access_ref class to
>    access_data.  As a FIXME in the code notes, the member never
>    did belong in the former and only takes up space in the cache.
>
> 2) The compute_objsize_r() function is big, unwieldy, and tedious
>    to step through because of all the if statements that are better
>    coded as one switch statement.  This change factors out more
>    of its code into smaller handler functions as has been suggested
>    and done a few times before.
>
> 3) (2) exposed a few places where I fail to pass the current
>    GIMPLE statement down to ranger.  This leads to worse quality
>    range info, including possible false positives and negatives.
>    I just spotted these problems in code review but I haven't
>    taken the time to come up with test cases.  This change fixes
>    these oversights as well.
>
> 4) The handling of PHI statements is also in one big, hard-to-
>    follow function.  This change moves the handling of each PHI
>    argument into its own handler which merges it into the previous
>    argument.  This makes the code easier to work with and opens it
>    to reuse also for MIN_EXPR and MAX_EXPR.  (This is primarily
>    used to print informational notes after warnings.)
>
> 5) Finally, the patch factors code to dump each access_ref
>    cached by the pointer_query cache out of pointer_query::dump
>    and into access_ref::dump.  This helps with debugging.
>
> These changes should have no user-visible effect and other than
> a regression test for the typo (PR 103143) come with no tests.
> They've been tested on x86_64-linux.
Sigh.  You've identified 6 distinct changes above.  The 5 you've 
enumerated plus a typo fix somewhere.  There's no reason why they need 
to be a single patch and many reasons why they should be a series of 
independent patches.    Combining them into a single patch isn't how we 
do things and it hides the actual bugfix in here.

Please send a fix for the typo first since that should be able to 
trivially go forward.  Then  a patch for item #1.  That should be 
trivial to review when it's pulled out from teh rest of the patch. 
Beyond that, your choice on ordering, but you need to break this down.




Jeff
  

Patch

Improve compute_objsize() and fix PR 103143.

gcc/ChangeLog:

	PR  middle-end/103143
	* gimple-ssa-warn-access.cc (check_access): Use access_data members.
	(pass_waccess::check_stxncpy): Set access_data::ostype.
	(pass_waccess::check_strncmp): Use access_data members.
	* gimple-ssa-warn-restrict.c (builtin_access::builtin_access): Pass
	compute_objsize the call statement as an argument.
	* pointer-query.cc (compute_objsize_r): Add an argument.
	(gimple_call_return_array): Pass compute_objsize_r a new argument.
	(access_ref::access_ref): Move code to access_data::set_bound.
	(access_ref::merge_ref): New function.
	(access_ref::get_ref): Call it.
	(access_ref::inform_access): Add an argument.
	(access_ref::dump): New function.
	(access_data::access_data): Call set_bound.
	(access_data::set_bound): New function.
	(pointer_query::get_ref): Factor out code into merge_ref.  Handle
	MIN_EXPR and MAX_EXPR.
	(pointer_query::dump): Factor code out into access_ref::dump.
	(handle_min_max_size): Pass a new argument to compute_objsize_r.
	(handle_decl): New function.
	(handle_array_ref): Avoid incrementing deref.  Pass a new argument
	to compute_objsize_r.
	(set_component_ref_size): New function.
	(handle_component_ref): New function.
	(handle_mem_ref): Pass a new argument to compute_objsize_r.
	Increment deref after calling it, not before.
	(handle_ssa_name): New function.
	(compute_objsize): Call compute_objsize_r with a new argument.
	* pointer-query.h (struct access_ref): Move members to access_data.
	(struct access_data): Add members.
	(compute_objsize): Declare a new overload.

gcc/testsuite/ChangeLog:

	* c-c++-common/Wstringop-overflow-2.c: Avoid vectorization.

	PR  middle-end/103143
	* gcc.dg/Wstringop-overflow-83.c: New test.

diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 63fc27a1487..7f225a30a8c 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -1337,10 +1337,10 @@  check_access (GimpleOrTree exp, tree dstwrite,
   if (!dstsize)
     dstsize = maxobjsize;
 
-  /* Set RANGE to that of DSTWRITE if non-null, bounded by PAD->DST.BNDRNG
+  /* Set RANGE to that of DSTWRITE if non-null, bounded by PAD->DST_BNDRNG
      if valid.  */
   gimple *stmt = pad ? pad->stmt : nullptr;
-  get_size_range (rvals, dstwrite, stmt, range, pad ? pad->dst.bndrng : NULL);
+  get_size_range (rvals, dstwrite, stmt, range, pad ? pad->dst_bndrng : NULL);
 
   tree func = get_callee_fndecl (exp);
   /* Read vs write access by built-ins can be determined from the const
@@ -1419,7 +1419,7 @@  check_access (GimpleOrTree exp, tree dstwrite,
 	    {
 	      suppress_warning (exp, OPT_Wstringop_overflow_);
 	      if (pad)
-		pad->dst.inform_access (pad->mode);
+		pad->dst.inform_access (pad->mode, pad->ostype);
 	    }
 
 	  /* Return error when an overflow has been detected.  */
@@ -1432,9 +1432,9 @@  check_access (GimpleOrTree exp, tree dstwrite,
      of an object.  */
   if (maxread)
     {
-      /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
+      /* Set RANGE to that of MAXREAD, bounded by PAD->SRC_BNDRNG if
 	 PAD is nonnull and BNDRNG is valid.  */
-      get_size_range (rvals, maxread, stmt, range, pad ? pad->src.bndrng : NULL);
+      get_size_range (rvals, maxread, stmt, range, pad ? pad->src_bndrng : NULL);
 
       location_t loc = get_location (exp);
       tree size = dstsize;
@@ -1479,12 +1479,12 @@  check_access (GimpleOrTree exp, tree dstwrite,
       && (pad->src.offrng[1] < 0
 	  || pad->src.offrng[0] <= pad->src.offrng[1]))
     {
-      /* Set RANGE to that of MAXREAD, bounded by PAD->SRC.BNDRNG if
+      /* Set RANGE to that of MAXREAD, bounded by PAD->SRC_BNDRNG if
 	 PAD is nonnull and BNDRNG is valid.  */
-      get_size_range (rvals, maxread, stmt, range, pad ? pad->src.bndrng : NULL);
+      get_size_range (rvals, maxread, stmt, range, pad ? pad->src_bndrng : NULL);
       /* Set OVERREAD for reads starting just past the end of an object.  */
-      overread = pad->src.sizrng[1] - pad->src.offrng[0] < pad->src.bndrng[0];
-      range[0] = wide_int_to_tree (sizetype, pad->src.bndrng[0]);
+      overread = pad->src.sizrng[1] - pad->src.offrng[0] < pad->src_bndrng[0];
+      range[0] = wide_int_to_tree (sizetype, pad->src_bndrng[0]);
       slen = size_zero_node;
     }
 
@@ -2525,9 +2525,10 @@  pass_waccess::check_stxncpy (gcall *stmt)
   /* The number of bytes to write (not the maximum).  */
   tree len = call_arg (stmt, 2);
 
-  access_data data (m_ptr_qry.rvals, stmt, access_read_write, len, true, len,
-		    true);
   const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
+  access_data data (m_ptr_qry.rvals, stmt, access_read_write,
+		    len, true, len, true);
+  data.ostype = ost;
   compute_objsize (src, stmt, ost, &data.src, &m_ptr_qry);
   tree dstsize = compute_objsize (dst, stmt, ost, &data.dst, &m_ptr_qry);
 
@@ -2590,7 +2591,7 @@  pass_waccess::check_strncmp (gcall *stmt)
   /* Determine the range of the bound first and bail if it fails; it's
      cheaper than computing the size of the objects.  */
   tree bndrng[2] = { NULL_TREE, NULL_TREE };
-  get_size_range (m_ptr_qry.rvals, bound, stmt, bndrng, adata1.src.bndrng);
+  get_size_range (m_ptr_qry.rvals, bound, stmt, bndrng, adata1.src_bndrng);
   if (!bndrng[0] || integer_zerop (bndrng[0]))
     return;
 
diff --git a/gcc/gimple-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c
index d1df9ca8d4f..ca2d4c2c32e 100644
--- a/gcc/gimple-ssa-warn-restrict.c
+++ b/gcc/gimple-ssa-warn-restrict.c
@@ -777,7 +777,7 @@  builtin_access::builtin_access (range_query *query, gimple *call,
       if (!POINTER_TYPE_P (TREE_TYPE (addr)))
 	addr = build1 (ADDR_EXPR, (TREE_TYPE (addr)), addr);
 
-      if (tree dstsize = compute_objsize (addr, ostype))
+      if (tree dstsize = compute_objsize (addr, call, ostype))
 	dst.basesize = wi::to_offset (dstsize);
       else if (POINTER_TYPE_P (TREE_TYPE (addr)))
 	dst.basesize = HOST_WIDE_INT_MIN;
@@ -791,7 +791,7 @@  builtin_access::builtin_access (range_query *query, gimple *call,
       if (!POINTER_TYPE_P (TREE_TYPE (addr)))
 	addr = build1 (ADDR_EXPR, (TREE_TYPE (addr)), addr);
 
-      if (tree srcsize = compute_objsize (addr, ostype))
+      if (tree srcsize = compute_objsize (addr, call, ostype))
 	src.basesize = wi::to_offset (srcsize);
       else if (POINTER_TYPE_P (TREE_TYPE (addr)))
 	src.basesize = HOST_WIDE_INT_MIN;
diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index a0e4543d8a3..873cb66b17e 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -43,7 +43,7 @@ 
 #include "tree-ssanames.h"
 #include "target.h"
 
-static bool compute_objsize_r (tree, gimple *, int, access_ref *,
+static bool compute_objsize_r (tree, gimple *, bool, int, access_ref *,
 			       ssa_name_limit_t &, pointer_query *);
 
 /* Wrapper around the wide_int overload of get_range that accepts
@@ -199,7 +199,7 @@  gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
 	       of the source object.  */
 	    access_ref aref;
 	    tree src = gimple_call_arg (stmt, 1);
-	    if (compute_objsize (src, stmt, 1, &aref, qry)
+	    if (compute_objsize_r (src, stmt, false, 1, &aref, snlim, qry)
 		&& aref.sizrng[1] < offrng[1])
 	      offrng[1] = aref.sizrng[1];
 	  }
@@ -233,7 +233,7 @@  gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
       {
 	access_ref aref;
 	tree src = gimple_call_arg (stmt, 1);
-	if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry))
+	if (compute_objsize_r (src, stmt, false, 1, &aref, snlim, qry))
 	  offrng[1] = aref.sizrng[1] - 1;
 	else
 	  offrng[1] = HOST_WIDE_INT_M1U;
@@ -258,7 +258,7 @@  gimple_call_return_array (gimple *stmt, offset_int offrng[2], bool *past_end,
 	       of the source object.  */
 	    access_ref aref;
 	    tree src = gimple_call_arg (stmt, 1);
-	    if (compute_objsize_r (src, stmt, 1, &aref, snlim, qry)
+	    if (compute_objsize_r (src, stmt, false, 1, &aref, snlim, qry)
 		&& aref.sizrng[1] < offrng[1])
 	      offrng[1] = aref.sizrng[1];
 	  }
@@ -596,15 +596,7 @@  gimple_parm_array_size (tree ptr, wide_int rng[2],
   return var;
 }
 
-/* Given a statement STMT, set the bounds of the reference to at most
-   as many bytes as BOUND or unknown when null, and at least one when
-   the MINACCESS is true unless BOUND is a constant zero.  STMT is
-   used for context to get accurate range info.  */
-
-access_ref::access_ref (range_query *qry /* = nullptr */,
-			tree bound /* = NULL_TREE */,
-			gimple *stmt /* = nullptr */,
-			bool minaccess /* = false */)
+access_ref::access_ref ()
   : ref (), eval ([](tree x){ return x; }), deref (), trail1special (true),
     base0 (true), parmarray ()
 {
@@ -613,21 +605,6 @@  access_ref::access_ref (range_query *qry /* = nullptr */,
   offmax[0] = offmax[1] = 0;
   /* Invalidate.   */
   sizrng[0] = sizrng[1] = -1;
-
-  /* Set the default bounds of the access and adjust below.  */
-  bndrng[0] = minaccess ? 1 : 0;
-  bndrng[1] = HOST_WIDE_INT_M1U;
-
-  /* When BOUND is nonnull and a range can be extracted from it,
-     set the bounds of the access to reflect both it and MINACCESS.
-     BNDRNG[0] is the size of the minimum access.  */
-  tree rng[2];
-  if (bound && get_size_range (qry, bound, stmt, rng, SR_ALLOW_ZERO))
-    {
-      bndrng[0] = wi::to_offset (rng[0]);
-      bndrng[1] = wi::to_offset (rng[1]);
-      bndrng[0] = bndrng[0] > 0 && minaccess ? 1 : 0;
-    }
 }
 
 /* Return the PHI node REF refers to or null if it doesn't.  */
@@ -645,6 +622,97 @@  access_ref::phi () const
   return as_a <gphi *> (def_stmt);
 }
 
+/* Determine the size and offset for ARG, append it to ALL_REFS, and
+   merge the result with *THIS.  Ignore ARG if SKIP_NULL is set and
+   ARG refers to the null pointer.  Return true on success and false
+   on failure.  */
+
+bool
+access_ref::merge_ref (vec<access_ref> *all_refs, tree arg, gimple *stmt,
+		       int ostype, bool skip_null,
+		       ssa_name_limit_t &snlim, pointer_query &qry)
+{
+  access_ref aref;
+  if (!compute_objsize_r (arg, stmt, false, ostype, &aref, snlim, &qry)
+      || aref.sizrng[0] < 0)
+    /* This may be a PHI with all null pointer arguments.  */
+    return false;
+
+  if (all_refs)
+    {
+      access_ref dummy_ref;
+      aref.get_ref (all_refs, &dummy_ref, ostype, &snlim, &qry);
+    }
+
+  if (TREE_CODE (arg) == SSA_NAME)
+    qry.put_ref (arg, aref, ostype);
+
+  if (all_refs)
+    all_refs->safe_push (aref);
+
+  aref.deref += deref;
+
+  bool merged_parmarray = aref.parmarray;
+
+  const bool nullp = skip_null && integer_zerop (arg);
+  const offset_int maxobjsize = wi::to_offset (max_object_size ());
+  offset_int minsize = sizrng[0];
+
+  if (sizrng[0] < 0)
+    {
+      /* If *THIS doesn't contain a meaningful result yet set it to AREF
+	 unless the argument is null and it's okay to ignore it.  */
+      if (!nullp)
+	*this = aref;
+
+      /* Set if the current argument refers to one or more objects of
+	 known size (or range of sizes), as opposed to referring to
+	 one or more unknown object(s).  */
+      const bool arg_known_size = (aref.sizrng[0] != 0
+				   || aref.sizrng[1] != maxobjsize);
+      if (arg_known_size)
+	sizrng[0] = aref.sizrng[0];
+
+      return true;
+    }
+
+  /* Disregard null pointers in PHIs with two or more arguments.
+     TODO: Handle this better!  */
+  if (nullp)
+    return true;
+
+  const bool known_size = (sizrng[0] != 0 || sizrng[1] != maxobjsize);
+
+  if (known_size && aref.sizrng[0] < minsize)
+    minsize = aref.sizrng[0];
+
+  /* Determine the amount of remaining space in the argument.  */
+  offset_int argrem[2];
+  argrem[1] = aref.size_remaining (argrem);
+
+  /* Determine the amount of remaining space computed so far and
+     if the remaining space in the argument is more use it instead.  */
+  offset_int merged_rem[2];
+  merged_rem[1] = size_remaining (merged_rem);
+
+  /* Reset the PHI's BASE0 flag if any of the nonnull arguments
+     refers to an object at an unknown offset.  */
+  if (!aref.base0)
+    base0 = false;
+
+  if (merged_rem[1] < argrem[1]
+      || (merged_rem[1] == argrem[1]
+	  && sizrng[1] < aref.sizrng[1]))
+    /* Use the argument with the most space remaining as the result,
+       or the larger one if the space is equal.  */
+    *this = aref;
+
+  sizrng[0] = minsize;
+  parmarray = merged_parmarray;
+
+  return true;
+}
+
 /* Determine and return the largest object to which *THIS refers.  If
    *THIS refers to a PHI and PREF is nonnull, fill *PREF with the details
    of the object determined by compute_objsize(ARG, OSTYPE) for each PHI
@@ -657,9 +725,8 @@  access_ref::get_ref (vec<access_ref> *all_refs,
 		     ssa_name_limit_t *psnlim /* = NULL */,
 		     pointer_query *qry /* = NULL */) const
 {
-  gphi *phi_stmt = this->phi ();
-  if (!phi_stmt)
-    return ref;
+  if (!ref || TREE_CODE (ref) != SSA_NAME)
+    return NULL;
 
   /* FIXME: Calling get_ref() with a null PSNLIM is dangerous and might
      cause unbounded recursion.  */
@@ -667,13 +734,49 @@  access_ref::get_ref (vec<access_ref> *all_refs,
   if (!psnlim)
     psnlim = &snlim_buf;
 
-  if (!psnlim->visit_phi (ref))
-    return NULL_TREE;
-
   pointer_query empty_qry;
   if (!qry)
     qry = &empty_qry;
 
+  if (gimple *def_stmt = SSA_NAME_DEF_STMT (ref))
+    {
+      if (is_gimple_assign (def_stmt))
+	{
+	  tree_code code = gimple_assign_rhs_code (def_stmt);
+	  if (code != MIN_EXPR && code != MAX_EXPR)
+	    return NULL_TREE;
+
+	  access_ref aref;
+	  tree arg1 = gimple_assign_rhs1 (def_stmt);
+	  if (!aref.merge_ref (all_refs, arg1, def_stmt, ostype, false,
+			       *psnlim, *qry))
+	    return NULL_TREE;
+
+	  tree arg2 = gimple_assign_rhs2 (def_stmt);
+	  if (!aref.merge_ref (all_refs, arg2, def_stmt, ostype, false,
+			       *psnlim, *qry))
+	    return NULL_TREE;
+
+	  if (pref && pref != this)
+	    {
+	      tree ref = pref->ref;
+	      *pref = aref;
+	      pref->ref = ref;
+	    }
+
+	  return aref.ref;
+	}
+    }
+  else
+    return NULL_TREE;
+
+  gphi *phi_stmt = this->phi ();
+  if (!phi_stmt)
+    return ref;
+
+  if (!psnlim->visit_phi (ref))
+    return NULL_TREE;
+
   /* The conservative result of the PHI reflecting the offset and size
      of the largest PHI argument, regardless of whether or not they all
      refer to the same object.  */
@@ -691,91 +794,17 @@  access_ref::get_ref (vec<access_ref> *all_refs,
       phi_ref = *pref;
     }
 
-  /* Set if any argument is a function array (or VLA) parameter not
-     declared [static].  */
-  bool parmarray = false;
-  /* The size of the smallest object referenced by the PHI arguments.  */
-  offset_int minsize = 0;
-  const offset_int maxobjsize = wi::to_offset (max_object_size ());
-
   const unsigned nargs = gimple_phi_num_args (phi_stmt);
   for (unsigned i = 0; i < nargs; ++i)
     {
       access_ref phi_arg_ref;
+      bool skip_null = i || i + 1 < nargs;
       tree arg = gimple_phi_arg_def (phi_stmt, i);
-      if (!compute_objsize_r (arg, phi_stmt, ostype, &phi_arg_ref, *psnlim,
-			      qry)
-	  || phi_arg_ref.sizrng[0] < 0)
-	/* A PHI with all null pointer arguments.  */
+      if (!phi_ref.merge_ref (all_refs, arg, phi_stmt, ostype, skip_null,
+			      *psnlim, *qry))
 	return NULL_TREE;
-
-      if (TREE_CODE (arg) == SSA_NAME)
-	qry->put_ref (arg, phi_arg_ref);
-
-      if (all_refs)
-	all_refs->safe_push (phi_arg_ref);
-
-      parmarray |= phi_arg_ref.parmarray;
-
-      const bool nullp = integer_zerop (arg) && (i || i + 1 < nargs);
-
-      if (phi_ref.sizrng[0] < 0)
-	{
-	  /* If PHI_REF doesn't contain a meaningful result yet set it
-	     to the result for the first argument.  */
-	  if (!nullp)
-	    phi_ref = phi_arg_ref;
-
-	  /* Set if the current argument refers to one or more objects of
-	     known size (or range of sizes), as opposed to referring to
-	     one or more unknown object(s).  */
-	  const bool arg_known_size = (phi_arg_ref.sizrng[0] != 0
-				       || phi_arg_ref.sizrng[1] != maxobjsize);
-	  if (arg_known_size)
-	    minsize = phi_arg_ref.sizrng[0];
-
-	  continue;
-	}
-
-      const bool phi_known_size = (phi_ref.sizrng[0] != 0
-				   || phi_ref.sizrng[1] != maxobjsize);
-
-      if (phi_known_size && phi_arg_ref.sizrng[0] < minsize)
-	minsize = phi_arg_ref.sizrng[0];
-
-      /* Disregard null pointers in PHIs with two or more arguments.
-	 TODO: Handle this better!  */
-      if (nullp)
-	continue;
-
-      /* Determine the amount of remaining space in the argument.  */
-      offset_int argrem[2];
-      argrem[1] = phi_arg_ref.size_remaining (argrem);
-
-      /* Determine the amount of remaining space computed so far and
-	 if the remaining space in the argument is more use it instead.  */
-      offset_int phirem[2];
-      phirem[1] = phi_ref.size_remaining (phirem);
-
-      /* Reset the PHI's BASE0 flag if any of the nonnull arguments
-	 refers to an object at an unknown offset.  */
-      if (!phi_arg_ref.base0)
-	phi_ref.base0 = false;
-
-      if (phirem[1] < argrem[1]
-	  || (phirem[1] == argrem[1]
-	      && phi_ref.sizrng[1] < phi_arg_ref.sizrng[1]))
-	/* Use the argument with the most space remaining as the result,
-	   or the larger one if the space is equal.  */
-	phi_ref = phi_arg_ref;
     }
 
-  /* Replace the lower bound of the largest argument with the size
-     of the smallest argument, and set PARMARRAY if any argument
-     was one.  */
-  phi_ref.sizrng[0] = minsize;
-  phi_ref.parmarray = parmarray;
-
   if (phi_ref.sizrng[0] < 0)
     {
       /* Fail if none of the PHI's arguments resulted in updating PHI_REF
@@ -787,7 +816,14 @@  access_ref::get_ref (vec<access_ref> *all_refs,
 
   /* Avoid changing *THIS.  */
   if (pref && pref != this)
-    *pref = phi_ref;
+    {
+      /* Keep the SSA_NAME of the PHI unchanged so that all PHI arguments
+	 can be referred to later if necessary.  This is useful even if
+	 they all refer to the same object.  */
+      tree ref = pref->ref;
+      *pref = phi_ref;
+      pref->ref = ref;
+    }
 
   psnlim->leave_phi (ref);
 
@@ -959,42 +995,45 @@  void access_ref::add_offset (const offset_int &min, const offset_int &max)
    WRITE is set for a write access and clear for a read access.  */
 
 void
-access_ref::inform_access (access_mode mode) const
+access_ref::inform_access (access_mode mode, int ostype /* = 1 */) const
 {
   const access_ref &aref = *this;
   if (!aref.ref)
     return;
 
-  if (aref.phi ())
+  if (phi ())
     {
       /* Set MAXREF to refer to the largest object and fill ALL_REFS
 	 with data for all objects referenced by the PHI arguments.  */
       access_ref maxref;
       auto_vec<access_ref> all_refs;
-      if (!get_ref (&all_refs, &maxref))
+      if (!get_ref (&all_refs, &maxref, ostype))
 	return;
 
-      /* Except for MAXREF, the rest of the arguments' offsets need not
-	 reflect one added to the PHI itself.  Determine the latter from
-	 MAXREF on which the result is based.  */
-      const offset_int orng[] =
+      if (all_refs.length ())
 	{
-	  offrng[0] - maxref.offrng[0],
-	  wi::smax (offrng[1] - maxref.offrng[1], offrng[0]),
-	};
+	  /* Except for MAXREF, the rest of the arguments' offsets need not
+	     reflect one added to the PHI itself.  Determine the latter from
+	     MAXREF on which the result is based.  */
+	  const offset_int orng[] =
+	    {
+	     offrng[0] - maxref.offrng[0],
+	     wi::smax (offrng[1] - maxref.offrng[1], offrng[0]),
+	    };
 
-      /* Add the final PHI's offset to that of each of the arguments
-	 and recurse to issue an inform message for it.  */
-      for (unsigned i = 0; i != all_refs.length (); ++i)
-	{
-	  /* Skip any PHIs; those could lead to infinite recursion.  */
-	  if (all_refs[i].phi ())
-	    continue;
+	  /* Add the final PHI's offset to that of each of the arguments
+	     and recurse to issue an inform message for it.  */
+	  for (unsigned i = 0; i != all_refs.length (); ++i)
+	    {
+	      /* Skip any PHIs; those could lead to infinite recursion.  */
+	      if (all_refs[i].phi ())
+		continue;
 
-	  all_refs[i].add_offset (orng[0], orng[1]);
-	  all_refs[i].inform_access (mode);
+	      all_refs[i].add_offset (orng[0], orng[1]);
+	      all_refs[i].inform_access (mode, ostype);
+	    }
+	  return;
 	}
-      return;
     }
 
   /* Convert offset range and avoid including a zero range since it
@@ -1199,6 +1238,111 @@  access_ref::inform_access (access_mode mode) const
 	    sizestr, allocfn);
 }
 
+/* Dump *THIS to FILE.  */
+
+void
+access_ref::dump (FILE *file) const
+{
+  for (int i = deref; i < 0; ++i)
+    fputc ('&', file);
+
+  for (int i = 0; i < deref; ++i)
+    fputc ('*', file);
+
+  if (gphi *phi_stmt = phi ())
+    {
+      fputs ("PHI <", file);
+      unsigned nargs = gimple_phi_num_args (phi_stmt);
+      for (unsigned i = 0; i != nargs; ++i)
+	{
+	  tree arg = gimple_phi_arg_def (phi_stmt, i);
+	  print_generic_expr (file, arg);
+	  if (i + 1 < nargs)
+	    fputs (", ", file);
+	}
+      fputc ('>', file);
+    }
+  else
+    print_generic_expr (file, ref);
+
+  if (offrng[0] != offrng[1])
+    fprintf (file, " + [%lli, %lli]",
+	     (long long) offrng[0].to_shwi (),
+	     (long long) offrng[1].to_shwi ());
+  else if (offrng[0] != 0)
+    fprintf (file, " %c %lli",
+	     offrng[0] < 0 ? '-' : '+',
+	     (long long) offrng[0].to_shwi ());
+
+  if (base0)
+    fputs (" (base0)", file);
+
+  fputs ("; size: ", file);
+  if (sizrng[0] != sizrng[1])
+    {
+      offset_int maxsize = wi::to_offset (max_object_size ());
+      if (sizrng[0] == 0 && sizrng[1] >= maxsize)
+	fputs ("unknown", file);
+      else
+	fprintf (file, "[%llu, %llu]",
+		 (unsigned long long) sizrng[0].to_uhwi (),
+		 (unsigned long long) sizrng[1].to_uhwi ());
+    }
+  else if (sizrng[0] != 0)
+    fprintf (file, "%llu",
+	     (unsigned long long) sizrng[0].to_uhwi ());
+
+  fputc ('\n', file);
+}
+
+/* Set the access to at most MAXWRITE and MAXREAD bytes, and at least 1
+   when MINWRITE or MINREAD, respectively, is set.  */
+access_data::access_data (range_query *query, gimple *stmt, access_mode mode,
+			  tree maxwrite /* = NULL_TREE */,
+			  bool minwrite /* = false */,
+			  tree maxread /* = NULL_TREE */,
+			  bool minread /* = false */)
+  : stmt (stmt), call (), dst (), src (), mode (mode), ostype ()
+{
+  set_bound (dst_bndrng, maxwrite, minwrite, query, stmt);
+  set_bound (src_bndrng, maxread, minread, query, stmt);
+}
+
+/* Set the access to at most MAXWRITE and MAXREAD bytes, and at least 1
+   when MINWRITE or MINREAD, respectively, is set.  */
+access_data::access_data (range_query *query, tree expr, access_mode mode,
+			  tree maxwrite /* = NULL_TREE */,
+			  bool minwrite /* = false */,
+			  tree maxread /* = NULL_TREE */,
+			  bool minread /* = false */)
+  : stmt (), call (expr),  dst (), src (), mode (mode), ostype ()
+{
+  set_bound (dst_bndrng, maxwrite, minwrite, query, stmt);
+  set_bound (src_bndrng, maxread, minread, query, stmt);
+}
+
+/* Set BNDRNG to the range of BOUND for the statement STMT.  */
+
+void
+access_data::set_bound (offset_int bndrng[2], tree bound, bool minaccess,
+			range_query *query, gimple *stmt)
+{
+  /* Set the default bounds of the access and adjust below.  */
+  bndrng[0] = minaccess ? 1 : 0;
+  bndrng[1] = HOST_WIDE_INT_M1U;
+
+  /* When BOUND is nonnull and a range can be extracted from it,
+     set the bounds of the access to reflect both it and MINACCESS.
+     BNDRNG[0] is the size of the minimum access.  */
+  tree rng[2];
+  if (bound && get_size_range (query, bound, stmt, rng, SR_ALLOW_ZERO))
+    {
+      bndrng[0] = wi::to_offset (rng[0]);
+      bndrng[1] = wi::to_offset (rng[1]);
+      bndrng[0] = bndrng[0] > 0 && minaccess ? 1 : 0;
+    }
+}
+
 /* Set a bit for the PHI in VISITED and return true if it wasn't
    already set.  */
 
@@ -1320,7 +1464,8 @@  pointer_query::get_ref (tree ptr, int ostype /* = 1 */) const
    there or compute it and insert it into the cache if it's nonnonull.  */
 
 bool
-pointer_query::get_ref (tree ptr, gimple *stmt, access_ref *pref, int ostype /* = 1 */)
+pointer_query::get_ref (tree ptr, gimple *stmt, access_ref *pref,
+			int ostype /* = 1 */)
 {
   const unsigned version
     = TREE_CODE (ptr) == SSA_NAME ? SSA_NAME_VERSION (ptr) : 0;
@@ -1408,6 +1553,9 @@  pointer_query::flush_cache ()
 void
 pointer_query::dump (FILE *dump_file, bool contents /* = false */)
 {
+  if (!var_cache)
+    return;
+
   unsigned nused = 0, nrefs = 0;
   unsigned nidxs = var_cache->indices.length ();
   for (unsigned i = 0; i != nidxs; ++i)
@@ -1468,35 +1616,40 @@  pointer_query::dump (FILE *dump_file, bool contents /* = false */)
       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);
+      aref.dump (dump_file);
     }
 
   fputc ('\n', dump_file);
+
+  {
+    fputs ("\npointer_query cache contents (again):\n", dump_file);
+
+    tree var;
+    unsigned i;
+    FOR_EACH_SSA_NAME (i, var, cfun)
+      {
+	if (TREE_CODE (TREE_TYPE (var)) != POINTER_TYPE)
+	  continue;
+
+	for (unsigned ost = 0; ost != 2; ++ost)
+	  {
+	    if (const access_ref *cache_ref = get_ref (var, ost))
+	      {
+		unsigned ver = SSA_NAME_VERSION (var);
+		fprintf (dump_file, "  %u.%u: ", ver, ost);
+		if (tree name = ssa_name (ver))
+		  {
+		    print_generic_expr (dump_file, name);
+		    fputs (" = ", dump_file);
+		  }
+		else
+		  fprintf (dump_file, "  _%u = ", ver);
+
+		cache_ref->dump (dump_file);
+	      }
+	  }
+      }
+  }
 }
 
 /* A helper of compute_objsize_r() to determine the size from an assignment
@@ -1520,7 +1673,7 @@  handle_min_max_size (tree ptr, int ostype, access_ref *pref,
      for the expression.  */
   access_ref aref[2] = { *pref, *pref };
   tree arg1 = gimple_assign_rhs1 (stmt);
-  if (!compute_objsize_r (arg1, stmt, ostype, &aref[0], snlim, qry))
+  if (!compute_objsize_r (arg1, stmt, false, ostype, &aref[0], snlim, qry))
     {
       aref[0].base0 = false;
       aref[0].offrng[0] = aref[0].offrng[1] = 0;
@@ -1529,7 +1682,7 @@  handle_min_max_size (tree ptr, int ostype, access_ref *pref,
     }
 
   tree arg2 = gimple_assign_rhs2 (stmt);
-  if (!compute_objsize_r (arg2, stmt, ostype, &aref[1], snlim, qry))
+  if (!compute_objsize_r (arg2, stmt, false, ostype, &aref[1], snlim, qry))
     {
       aref[1].base0 = false;
       aref[1].offrng[0] = aref[1].offrng[1] = 0;
@@ -1592,6 +1745,46 @@  handle_min_max_size (tree ptr, int ostype, access_ref *pref,
   return true;
 }
 
+/* A helper of compute_objsize_r() to determine the size of a DECL.
+   Return true on success and (possibly in the future) false on failure.  */
+
+static bool
+handle_decl (tree decl, bool addr, access_ref *pref)
+{
+  tree decl_type = TREE_TYPE (decl);
+
+  pref->ref = decl;
+
+  /* Reset the offset in case it was set by a prior call and not
+     cleared by the caller.  The offset is only adjusted after
+     the identity of the object has been determined.  */
+  pref->offrng[0] = pref->offrng[1] = 0;
+
+  if (!addr && POINTER_TYPE_P (decl_type))
+    {
+      /* Set the maximum size if the reference is to the pointer
+	 itself (as opposed to what it points to), and clear
+	 BASE0 since the offset isn't necessarily zero-based.  */
+      pref->set_max_size_range ();
+      pref->base0 = false;
+      return true;
+    }
+
+  /* Valid offsets into the object are nonnegative.  */
+  pref->base0 = true;
+
+  if (tree size = decl_init_size (decl, false))
+    if (TREE_CODE (size) == INTEGER_CST)
+      {
+	pref->sizrng[0] = wi::to_offset (size);
+	pref->sizrng[1] = pref->sizrng[0];
+	return true;
+      }
+
+  pref->set_max_size_range ();
+  return true;
+}
+
 /* A helper of compute_objsize_r() to determine the size from ARRAY_REF
    AREF.  ADDR is true if PTR is the operand of ADDR_EXPR.  Return true
    on success and false on failure.  */
@@ -1603,8 +1796,6 @@  handle_array_ref (tree aref, gimple *stmt, bool addr, int ostype,
 {
   gcc_assert (TREE_CODE (aref) == ARRAY_REF);
 
-  ++pref->deref;
-
   tree arefop = TREE_OPERAND (aref, 0);
   tree reftype = TREE_TYPE (arefop);
   if (!addr && TREE_CODE (TREE_TYPE (reftype)) == POINTER_TYPE)
@@ -1612,13 +1803,13 @@  handle_array_ref (tree aref, gimple *stmt, bool addr, int ostype,
        of known bound.  */
     return false;
 
-  if (!compute_objsize_r (arefop, stmt, ostype, pref, snlim, qry))
+  if (!compute_objsize_r (arefop, stmt, addr, ostype, pref, snlim, qry))
     return false;
 
   offset_int orng[2];
   tree off = pref->eval (TREE_OPERAND (aref, 1));
   range_query *const rvals = qry ? qry->rvals : NULL;
-  if (!get_offset_range (off, NULL, orng, rvals))
+  if (!get_offset_range (off, stmt, orng, rvals))
     {
       /* Set ORNG to the maximum offset representable in ptrdiff_t.  */
       orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
@@ -1673,6 +1864,96 @@  handle_array_ref (tree aref, gimple *stmt, bool addr, int ostype,
   return true;
 }
 
+/* Given a COMPONENT_REF CREF, set *PREF size to the size of the referenced
+   member.  */
+
+static void
+set_component_ref_size (tree cref, access_ref *pref)
+{
+  const tree base = TREE_OPERAND (cref, 0);
+  const tree base_type = TREE_TYPE (base);
+
+  /* SAM is set for array members that might need special treatment.  */
+  special_array_member sam;
+  tree size = component_ref_size (cref, &sam);
+  if (sam == special_array_member::int_0)
+    pref->sizrng[0] = pref->sizrng[1] = 0;
+  else if (!pref->trail1special && sam == special_array_member::trail_1)
+    pref->sizrng[0] = pref->sizrng[1] = 1;
+  else if (size && TREE_CODE (size) == INTEGER_CST)
+    pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
+  else
+    {
+      /* When the size of the member is unknown it's either a flexible
+	 array member or a trailing special array member (either zero
+	 length or one-element).  Set the size to the maximum minus
+	 the constant size of the base object's type.  */
+      pref->sizrng[0] = 0;
+      pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
+      if (tree base_size = TYPE_SIZE_UNIT (base_type))
+	if (TREE_CODE (base_size) == INTEGER_CST)
+	  pref->sizrng[1] -= wi::to_offset (base_size);
+    }
+}
+
+/* A helper of compute_objsize_r() to determine the size from COMPONENT_REF
+   CREF.  Return true on success and false on failure.  */
+
+static bool
+handle_component_ref (tree cref, gimple *stmt, bool addr, int ostype,
+		      access_ref *pref, ssa_name_limit_t &snlim,
+		      pointer_query *qry)
+{
+  gcc_assert (TREE_CODE (cref) == COMPONENT_REF);
+
+  const tree base = TREE_OPERAND (cref, 0);
+  const tree base_type = TREE_TYPE (base);
+  if (TREE_CODE (base_type) == UNION_TYPE)
+    /* In accesses through union types consider the entire unions
+       rather than just their members.  */
+    ostype = 0;
+
+  tree field = TREE_OPERAND (cref, 1);
+
+  if (ostype == 0)
+    {
+      /* In OSTYPE zero (for raw memory functions like memcpy), use
+	 the maximum size instead if the identity of the enclosing
+	 object cannot be determined.  */
+      if (!compute_objsize_r (base, stmt, addr, ostype, pref, snlim, qry))
+	return false;
+
+      /* Otherwise, use the size of the enclosing object and add
+	 the offset of the member to the offset computed so far.  */
+      tree offset = byte_position (field);
+      if (TREE_CODE (offset) == INTEGER_CST)
+	pref->add_offset (wi::to_offset (offset));
+      else
+	pref->add_max_offset ();
+
+      if (!pref->ref)
+	/* PREF->REF may have been already set to an SSA_NAME earlier
+	   to provide better context for diagnostics.  In that case,
+	   leave it unchanged.  */
+	pref->ref = base;
+
+      return true;
+    }
+
+  pref->ref = field;
+
+  if (!addr && POINTER_TYPE_P (TREE_TYPE (field)))
+    {
+      /* Set maximum size if the reference is to the pointer member
+	 itself (as opposed to what it points to).  */
+      pref->set_max_size_range ();
+      return true;
+    }
+
+  set_component_ref_size (cref, pref);
+  return true;
+}
+
 /* A helper of compute_objsize_r() to determine the size from MEM_REF
    MREF.  Return true on success and false on failure.  */
 
@@ -1682,10 +1963,9 @@  handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref,
 {
   gcc_assert (TREE_CODE (mref) == MEM_REF);
 
-  ++pref->deref;
-
-  if (VECTOR_TYPE_P (TREE_TYPE (mref)))
-    {
+  tree mreftype = TYPE_MAIN_VARIANT (TREE_TYPE (mref));
+  if (VECTOR_TYPE_P (mreftype))
+      {
       /* Hack: Handle MEM_REFs of vector types as those to complete
 	 objects; those may be synthesized from multiple assignments
 	 to consecutive data members (see PR 93200 and 96963).
@@ -1699,13 +1979,15 @@  handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref,
     }
 
   tree mrefop = TREE_OPERAND (mref, 0);
-  if (!compute_objsize_r (mrefop, stmt, ostype, pref, snlim, qry))
+  if (!compute_objsize_r (mrefop, stmt, false, ostype, pref, snlim, qry))
     return false;
 
+  ++pref->deref;
+
   offset_int orng[2];
   tree off = pref->eval (TREE_OPERAND (mref, 1));
   range_query *const rvals = qry ? qry->rvals : NULL;
-  if (!get_offset_range (off, NULL, orng, rvals))
+  if (!get_offset_range (off, stmt, orng, rvals))
     {
       /* Set ORNG to the maximum offset representable in ptrdiff_t.  */
       orng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
@@ -1716,168 +1998,282 @@  handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref,
   return true;
 }
 
-/* Helper to compute the size of the object referenced by the PTR
-   expression which must have pointer type, using Object Size type
-   OSTYPE (only the least significant 2 bits are used).
-   On success, sets PREF->REF to the DECL of the referenced object
-   if it's unique, otherwise to null, PREF->OFFRNG to the range of
-   offsets into it, and PREF->SIZRNG to the range of sizes of
-   the object(s).
-   SNLIM is used to avoid visiting the same PHI operand multiple
-   times, and, when nonnull, RVALS to determine range information.
-   Returns true on success, false when a meaningful size (or range)
-   cannot be determined.
-
-   The function is intended for diagnostics and should not be used
-   to influence code generation or optimization.  */
+/* A helper of compute_objsize_r() to determine the size from SSA_NAME
+   PTR.  Return true on success and false on failure.  */
 
 static bool
-compute_objsize_r (tree ptr, gimple *stmt, int ostype, access_ref *pref,
-		   ssa_name_limit_t &snlim, pointer_query *qry)
+handle_ssa_name (tree ptr, bool addr, int ostype,
+		 access_ref *pref, ssa_name_limit_t &snlim,
+		 pointer_query *qry)
 {
-  STRIP_NOPS (ptr);
+  if (!snlim.next ())
+    return false;
 
-  const bool addr = TREE_CODE (ptr) == ADDR_EXPR;
-  if (addr)
+  /* Only process an SSA_NAME if the recursion limit has not yet
+     been reached.  */
+  if (qry)
     {
-      --pref->deref;
-      ptr = TREE_OPERAND (ptr, 0);
+      if (++qry->depth > qry->max_depth)
+	qry->max_depth = qry->depth;
+      if (const access_ref *cache_ref = qry->get_ref (ptr, ostype))
+	{
+	  /* Add the number of DEREFerences accummulated so far.  */
+	  const int deref = pref->deref;
+	  *pref = *cache_ref;
+	  pref->deref += deref;
+	  return true;
+	}
     }
 
-  if (DECL_P (ptr))
+  gimple *stmt = SSA_NAME_DEF_STMT (ptr);
+  if (is_gimple_call (stmt))
     {
-      pref->ref = ptr;
-
-      /* Reset the offset in case it was set by a prior call and not
-	 cleared by the caller.  The offset is only adjusted after
-	 the identity of the object has been determined.  */
-      pref->offrng[0] = pref->offrng[1] = 0;
+      /* If STMT is a call to an allocation function get the size
+	 from its argument(s).  If successful, also set *PREF->REF
+	 to PTR for the caller to include in diagnostics.  */
+      wide_int wr[2];
+      range_query *const rvals = qry ? qry->rvals : NULL;
+      if (gimple_call_alloc_size (stmt, wr, rvals))
+	{
+	  pref->ref = ptr;
+	  pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
+	  pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+	  /* Constrain both bounds to a valid size.  */
+	  offset_int maxsize = wi::to_offset (max_object_size ());
+	  if (pref->sizrng[0] > maxsize)
+	    pref->sizrng[0] = maxsize;
+	  if (pref->sizrng[1] > maxsize)
+	    pref->sizrng[1] = maxsize;
+	}
+      else
+	{
+	  /* For functions known to return one of their pointer arguments
+	     try to determine what the returned pointer points to, and on
+	     success add OFFRNG which was set to the offset added by
+	     the function (e.g., memchr) to the overall offset.  */
+	  bool past_end;
+	  offset_int offrng[2];
+	  if (tree ret = gimple_call_return_array (stmt, offrng, &past_end,
+						   snlim, qry))
+	    {
+	      if (!compute_objsize_r (ret, stmt, addr, ostype, pref, snlim, qry))
+		return false;
+
+	      /* Cap OFFRNG[1] to at most the remaining size of
+		 the object.  */
+	      offset_int remrng[2];
+	      remrng[1] = pref->size_remaining (remrng);
+	      if (remrng[1] != 0 && !past_end)
+		/* Decrement the size for functions that never return
+		   a past-the-end pointer.  */
+		remrng[1] -= 1;
+
+	      if (remrng[1] < offrng[1])
+		offrng[1] = remrng[1];
+	      pref->add_offset (offrng[0], offrng[1]);
+	    }
+	  else
+	    {
+	      /* For other calls that might return arbitrary pointers
+		 including into the middle of objects set the size
+		 range to maximum, clear PREF->BASE0, and also set
+		 PREF->REF to include in diagnostics.  */
+	      pref->set_max_size_range ();
+	      pref->base0 = false;
+	      pref->ref = ptr;
+	    }
+	}
+      qry->put_ref (ptr, *pref, ostype);
+      return true;
+    }
 
-      if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
+  if (gimple_nop_p (stmt))
+    {
+      /* For a function argument try to determine the byte size
+	 of the array from the current function declaratation
+	 (e.g., attribute access or related).  */
+      wide_int wr[2];
+      bool static_array = false;
+      if (tree ref = gimple_parm_array_size (ptr, wr, &static_array))
 	{
-	  /* Set the maximum size if the reference is to the pointer
-	     itself (as opposed to what it points to), and clear
-	     BASE0 since the offset isn't necessarily zero-based.  */
-	  pref->set_max_size_range ();
-	  pref->base0 = false;
+	  pref->parmarray = !static_array;
+	  pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
+	  pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
+	  pref->ref = ref;
+	  qry->put_ref (ptr, *pref, ostype);
 	  return true;
 	}
 
-      /* Valid offsets into the object are nonnegative.  */
-      pref->base0 = true;
-
-      if (tree size = decl_init_size (ptr, false))
-	if (TREE_CODE (size) == INTEGER_CST)
-	  {
-	    pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
-	    return true;
-	  }
-
       pref->set_max_size_range ();
+      pref->base0 = false;
+      pref->ref = ptr;
+      qry->put_ref (ptr, *pref, ostype);
       return true;
     }
 
-  const tree_code code = TREE_CODE (ptr);
-  range_query *const rvals = qry ? qry->rvals : NULL;
-
-  if (code == BIT_FIELD_REF)
+  if (gimple_code (stmt) == GIMPLE_PHI)
     {
-      tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
+      /* Pass PTR to get_ref() via PREF.  If all PHI arguments refer
+	 to the same object the function will replace it with it.  */
+      pref->ref = ptr;
+      access_ref phi_ref = *pref;
+      if (!pref->get_ref (NULL, &phi_ref, ostype, &snlim, qry))
 	return false;
+      *pref = phi_ref;
+      qry->put_ref (ptr, *pref, ostype);
+      return true;
+    }
 
-      offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
-      pref->add_offset (off / BITS_PER_UNIT);
+  if (!is_gimple_assign (stmt))
+    {
+      /* Clear BASE0 since the assigned pointer might point into
+	 the middle of the object, set the maximum size range and,
+	 if the SSA_NAME refers to a function argumnent, set
+	 PREF->REF to it.  */
+      pref->base0 = false;
+      pref->set_max_size_range ();
+      pref->ref = ptr;
       return true;
     }
 
-  if (code == COMPONENT_REF)
+  tree_code code = gimple_assign_rhs_code (stmt);
+
+  if (code == MAX_EXPR || code == MIN_EXPR)
     {
-      tree ref = TREE_OPERAND (ptr, 0);
-      if (TREE_CODE (TREE_TYPE (ref)) == UNION_TYPE)
-	/* In accesses through union types consider the entire unions
-	   rather than just their members.  */
-	ostype = 0;
-      tree field = TREE_OPERAND (ptr, 1);
+      if (!handle_min_max_size (ptr, ostype, pref, snlim, qry))
+	return false;
 
-      if (ostype == 0)
-	{
-	  /* In OSTYPE zero (for raw memory functions like memcpy), use
-	     the maximum size instead if the identity of the enclosing
-	     object cannot be determined.  */
-	  if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
-	    return false;
-
-	  /* Otherwise, use the size of the enclosing object and add
-	     the offset of the member to the offset computed so far.  */
-	  tree offset = byte_position (field);
-	  if (TREE_CODE (offset) == INTEGER_CST)
-	    pref->add_offset (wi::to_offset (offset));
-	  else
-	    pref->add_max_offset ();
+      qry->put_ref (ptr, *pref, ostype);
+      return true;
+    }
 
-	  if (!pref->ref)
-	    /* REF may have been already set to an SSA_NAME earlier
-	       to provide better context for diagnostics.  In that case,
-	       leave it unchanged.  */
-	    pref->ref = ref;
-	  return true;
-	}
+  tree rhs = gimple_assign_rhs1 (stmt);
 
-      pref->ref = field;
+  if (code == ASSERT_EXPR)
+    {
+      rhs = TREE_OPERAND (rhs, 0);
+      return compute_objsize_r (rhs, stmt, addr, ostype, pref, snlim, qry);
+    }
 
-      if (!addr && POINTER_TYPE_P (TREE_TYPE (field)))
-	{
-	  /* Set maximum size if the reference is to the pointer member
-	     itself (as opposed to what it points to).  */
-	  pref->set_max_size_range ();
-	  return true;
-	}
+  if (code == POINTER_PLUS_EXPR
+      && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE)
+    {
+      /* Compute the size of the object first. */
+      if (!compute_objsize_r (rhs, stmt, addr, ostype, pref, snlim, qry))
+	return false;
 
-      /* SAM is set for array members that might need special treatment.  */
-      special_array_member sam;
-      tree size = component_ref_size (ptr, &sam);
-      if (sam == special_array_member::int_0)
-	pref->sizrng[0] = pref->sizrng[1] = 0;
-      else if (!pref->trail1special && sam == special_array_member::trail_1)
-	pref->sizrng[0] = pref->sizrng[1] = 1;
-      else if (size && TREE_CODE (size) == INTEGER_CST)
-	pref->sizrng[0] = pref->sizrng[1] = wi::to_offset (size);
+      offset_int orng[2];
+      tree off = gimple_assign_rhs2 (stmt);
+      range_query *const rvals = qry ? qry->rvals : NULL;
+      if (get_offset_range (off, stmt, orng, rvals))
+	pref->add_offset (orng[0], orng[1]);
       else
-	{
-	  /* When the size of the member is unknown it's either a flexible
-	     array member or a trailing special array member (either zero
-	     length or one-element).  Set the size to the maximum minus
-	     the constant size of the type.  */
-	  pref->sizrng[0] = 0;
-	  pref->sizrng[1] = wi::to_offset (TYPE_MAX_VALUE (ptrdiff_type_node));
-	  if (tree recsize = TYPE_SIZE_UNIT (TREE_TYPE (ref)))
-	    if (TREE_CODE (recsize) == INTEGER_CST)
-	      pref->sizrng[1] -= wi::to_offset (recsize);
-	}
+	pref->add_max_offset ();
+
+      qry->put_ref (ptr, *pref, ostype);
       return true;
     }
 
-  if (code == ARRAY_REF)
-    return handle_array_ref (ptr, stmt, addr, ostype, pref, snlim, qry);
-
-  if (code == MEM_REF)
-    return handle_mem_ref (ptr, stmt, ostype, pref, snlim, qry);
+  if (code == ADDR_EXPR || code == SSA_NAME)
+    {
+      if (!compute_objsize_r (rhs, stmt, addr, ostype, pref, snlim, qry))
+	return false;
+      qry->put_ref (ptr, *pref, ostype);
+      return true;
+    }
 
-  if (code == TARGET_MEM_REF)
+  if (ostype > 1 && POINTER_TYPE_P (TREE_TYPE (rhs)))
     {
-      tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
+      /* When determining the qualifiers follow the pointer but
+	 avoid caching the result.  As the pointer is added to
+	 and/or dereferenced the computed size and offset need
+	 not be meaningful for other queries involving the same
+	 pointer.  */
+      if (!compute_objsize_r (rhs, stmt, addr, ostype, pref, snlim, qry))
 	return false;
 
-      /* TODO: Handle remaining operands.  Until then, add maximum offset.  */
-      pref->ref = ptr;
-      pref->add_max_offset ();
-      return true;
+      rhs = pref->ref;
     }
 
-  if (code == INTEGER_CST)
+  /* (This could also be an assignment from a nonlocal pointer.)  Save
+     PTR to mention in diagnostics but otherwise treat it as a pointer
+     to an unknown object.  */
+  pref->ref = rhs;
+  pref->base0 = false;
+  pref->set_max_size_range ();
+  return true;
+}
+
+/* Helper to compute the size of the object referenced by the PTR
+   expression which must have pointer type, using Object Size type
+   OSTYPE (only the least significant 2 bits are used).
+   On success, sets PREF->REF to the DECL of the referenced object
+   if it's unique, otherwise to null, PREF->OFFRNG to the range of
+   offsets into it, and PREF->SIZRNG to the range of sizes of
+   the object(s).
+   SNLIM is used to avoid visiting the same PHI operand multiple
+   times, and, when nonnull, RVALS to determine range information.
+   Returns true on success, false when a meaningful size (or range)
+   cannot be determined.
+
+   The function is intended for diagnostics and should not be used
+   to influence code generation or optimization.  */
+
+static bool
+compute_objsize_r (tree ptr, gimple *stmt, bool addr, int ostype,
+		   access_ref *pref, ssa_name_limit_t &snlim,
+		   pointer_query *qry)
+{
+  STRIP_NOPS (ptr);
+
+  if (DECL_P (ptr))
+    return handle_decl (ptr, addr, pref);
+
+  switch (TREE_CODE (ptr))
     {
+    case ADDR_EXPR:
+      {
+	tree ref = TREE_OPERAND (ptr, 0);
+	if (!compute_objsize_r (ref, stmt, true, ostype, pref, snlim, qry))
+	  return false;
+
+	--pref->deref;
+	return true;
+      }
+
+    case BIT_FIELD_REF:
+      {
+	tree ref = TREE_OPERAND (ptr, 0);
+	if (!compute_objsize_r (ref, stmt, addr, ostype, pref, snlim, qry))
+	  return false;
+
+	offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
+	pref->add_offset (off / BITS_PER_UNIT);
+	return true;
+      }
+
+    case ARRAY_REF:
+	return handle_array_ref (ptr, stmt, addr, ostype, pref, snlim, qry);
+
+    case COMPONENT_REF:
+      return handle_component_ref (ptr, stmt, addr, ostype, pref, snlim, qry);
+
+    case MEM_REF:
+      return handle_mem_ref (ptr, stmt, ostype, pref, snlim, qry);
+
+    case TARGET_MEM_REF:
+      {
+	tree ref = TREE_OPERAND (ptr, 0);
+	if (!compute_objsize_r (ref, stmt, addr, ostype, pref, snlim, qry))
+	  return false;
+
+	/* TODO: Handle remaining operands.  Until then, add maximum offset.  */
+	pref->ref = ptr;
+	pref->add_max_offset ();
+	return true;
+      }
+
+    case INTEGER_CST:
       /* Pointer constants other than null are most likely the result
 	 of erroneous null pointer addition/subtraction.  Unless zero
 	 is a valid address set size to zero.  For null pointers, set
@@ -1898,21 +2294,17 @@  compute_objsize_r (tree ptr, gimple *stmt, int ostype, access_ref *pref,
 	pref->sizrng[0] = pref->sizrng[1] = 0;
 
       pref->ref = ptr;
-
       return true;
-    }
 
-  if (code == STRING_CST)
-    {
+    case STRING_CST:
       pref->sizrng[0] = pref->sizrng[1] = TREE_STRING_LENGTH (ptr);
       pref->ref = ptr;
       return true;
-    }
 
-  if (code == POINTER_PLUS_EXPR)
+    case POINTER_PLUS_EXPR:
     {
       tree ref = TREE_OPERAND (ptr, 0);
-      if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
+      if (!compute_objsize_r (ref, stmt, addr, ostype, pref, snlim, qry))
 	return false;
 
       /* Clear DEREF since the offset is being applied to the target
@@ -1921,206 +2313,22 @@  compute_objsize_r (tree ptr, gimple *stmt, int ostype, access_ref *pref,
 
       offset_int orng[2];
       tree off = pref->eval (TREE_OPERAND (ptr, 1));
-      if (get_offset_range (off, NULL, orng, rvals))
+      if (get_offset_range (off, stmt, orng, qry->rvals))
 	pref->add_offset (orng[0], orng[1]);
       else
 	pref->add_max_offset ();
       return true;
     }
 
-  if (code == VIEW_CONVERT_EXPR)
-    {
+    case VIEW_CONVERT_EXPR:
       ptr = TREE_OPERAND (ptr, 0);
-      return compute_objsize_r (ptr, stmt, ostype, pref, snlim, qry);
-    }
+      return compute_objsize_r (ptr, stmt, addr, ostype, pref, snlim, qry);
 
-  if (code == SSA_NAME)
-    {
-      if (!snlim.next ())
-	return false;
+    case SSA_NAME:
+      return handle_ssa_name (ptr, addr, ostype, pref, snlim, qry);
 
-      /* Only process an SSA_NAME if the recursion limit has not yet
-	 been reached.  */
-      if (qry)
-	{
-	  if (++qry->depth)
-	    qry->max_depth = qry->depth;
-	  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.
-		 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;
-	    }
-	}
-
-      stmt = SSA_NAME_DEF_STMT (ptr);
-      if (is_gimple_call (stmt))
-	{
-	  /* If STMT is a call to an allocation function get the size
-	     from its argument(s).  If successful, also set *PREF->REF
-	     to PTR for the caller to include in diagnostics.  */
-	  wide_int wr[2];
-	  if (gimple_call_alloc_size (stmt, wr, rvals))
-	    {
-	      pref->ref = ptr;
-	      pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
-	      pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
-	      /* Constrain both bounds to a valid size.  */
-	      offset_int maxsize = wi::to_offset (max_object_size ());
-	      if (pref->sizrng[0] > maxsize)
-		pref->sizrng[0] = maxsize;
-	      if (pref->sizrng[1] > maxsize)
-		pref->sizrng[1] = maxsize;
-	    }
-	  else
-	    {
-	      /* For functions known to return one of their pointer arguments
-		 try to determine what the returned pointer points to, and on
-		 success add OFFRNG which was set to the offset added by
-		 the function (e.g., memchr) to the overall offset.  */
-	      bool past_end;
-	      offset_int offrng[2];
-	      if (tree ret = gimple_call_return_array (stmt, offrng,
-						       &past_end, snlim, qry))
-		{
-		  if (!compute_objsize_r (ret, stmt, ostype, pref, snlim, qry))
-		    return false;
-
-		  /* Cap OFFRNG[1] to at most the remaining size of
-		     the object.  */
-		  offset_int remrng[2];
-		  remrng[1] = pref->size_remaining (remrng);
-		  if (remrng[1] != 0 && !past_end)
-		    /* Decrement the size for functions that never return
-		       a past-the-end pointer.  */
-		    remrng[1] -= 1;
-
-		  if (remrng[1] < offrng[1])
-		    offrng[1] = remrng[1];
-		  pref->add_offset (offrng[0], offrng[1]);
-		}
-	      else
-		{
-		  /* For other calls that might return arbitrary pointers
-		     including into the middle of objects set the size
-		     range to maximum, clear PREF->BASE0, and also set
-		     PREF->REF to include in diagnostics.  */
-		  pref->set_max_size_range ();
-		  pref->base0 = false;
-		  pref->ref = ptr;
-		}
-	    }
-	  qry->put_ref (ptr, *pref);
-	  return true;
-	}
-
-      if (gimple_nop_p (stmt))
-	{
-	  /* For a function argument try to determine the byte size
-	     of the array from the current function declaratation
-	     (e.g., attribute access or related).  */
-	  wide_int wr[2];
-	  bool static_array = false;
-	  if (tree ref = gimple_parm_array_size (ptr, wr, &static_array))
-	    {
-	      pref->parmarray = !static_array;
-	      pref->sizrng[0] = offset_int::from (wr[0], UNSIGNED);
-	      pref->sizrng[1] = offset_int::from (wr[1], UNSIGNED);
-	      pref->ref = ref;
-	      qry->put_ref (ptr, *pref);
-	      return true;
-	    }
-
-	  pref->set_max_size_range ();
-	  pref->base0 = false;
-	  pref->ref = ptr;
-	  qry->put_ref (ptr, *pref);
-	  return true;
-	}
-
-      if (gimple_code (stmt) == GIMPLE_PHI)
-	{
-	  pref->ref = ptr;
-	  access_ref phi_ref = *pref;
-	  if (!pref->get_ref (NULL, &phi_ref, ostype, &snlim, qry))
-	    return false;
-	  *pref = phi_ref;
-	  pref->ref = ptr;
-	  qry->put_ref (ptr, *pref);
-	  return true;
-	}
-
-      if (!is_gimple_assign (stmt))
-	{
-	  /* Clear BASE0 since the assigned pointer might point into
-	     the middle of the object, set the maximum size range and,
-	     if the SSA_NAME refers to a function argumnent, set
-	     PREF->REF to it.  */
-	  pref->base0 = false;
-	  pref->set_max_size_range ();
-	  pref->ref = ptr;
-	  return true;
-	}
-
-      tree_code code = gimple_assign_rhs_code (stmt);
-
-      if (code == MAX_EXPR || code == MIN_EXPR)
-	{
-	  if (!handle_min_max_size (ptr, ostype, pref, snlim, qry))
-	    return false;
-
-	  qry->put_ref (ptr, *pref);
-	  return true;
-	}
-
-      tree rhs = gimple_assign_rhs1 (stmt);
-
-      if (code == ASSERT_EXPR)
-	{
-	  rhs = TREE_OPERAND (rhs, 0);
-	  return compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry);
-	}
-
-      if (code == POINTER_PLUS_EXPR
-	  && TREE_CODE (TREE_TYPE (rhs)) == POINTER_TYPE)
-	{
-	  /* Compute the size of the object first. */
-	  if (!compute_objsize_r (rhs, stmt, ostype, pref, snlim, qry))
-	    return false;
-
-	  offset_int orng[2];
-	  tree off = gimple_assign_rhs2 (stmt);
-	  if (get_offset_range (off, stmt, orng, rvals))
-	    pref->add_offset (orng[0], orng[1]);
-	  else
-	    pref->add_max_offset ();
-
-	  qry->put_ref (ptr, *pref);
-	  return true;
-	}
-
-      if (code == ADDR_EXPR || code == SSA_NAME)
-	{
-	  if (!compute_objsize_r (rhs, stmt, 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
-	 to an unknown object.  */
-      pref->ref = rhs;
-      pref->base0 = false;
-      pref->set_max_size_range ();
-      return true;
+    default:
+      break;
     }
 
   /* Assume all other expressions point into an unknown object
@@ -2151,7 +2359,7 @@  compute_objsize (tree ptr, gimple *stmt, int ostype, access_ref *pref,
   pref->sizrng[0] = pref->sizrng[1] = -1;
 
   ssa_name_limit_t snlim;
-  if (!compute_objsize_r (ptr, stmt, ostype, pref, snlim, ptr_qry))
+  if (!compute_objsize_r (ptr, stmt, false, ostype, pref, snlim, ptr_qry))
     return NULL_TREE;
 
   offset_int maxsize = pref->size_remaining ();
@@ -2176,13 +2384,13 @@  compute_objsize (tree ptr, gimple *stmt, int ostype, access_ref *pref,
    once callers transition to one of the two above.  */
 
 tree
-compute_objsize (tree ptr, int ostype, tree *pdecl /* = NULL */,
+compute_objsize (tree ptr, gimple *stmt, int ostype, tree *pdecl /* = NULL */,
 		 tree *poff /* = NULL */, range_query *rvals /* = NULL */)
 {
   /* Set the initial offsets to zero and size to negative to indicate
      none has been computed yet.  */
   access_ref ref;
-  tree size = compute_objsize (ptr, nullptr, ostype, &ref, rvals);
+  tree size = compute_objsize (ptr, stmt, ostype, &ref, rvals);
   if (!size || !ref.base0)
     return NULL_TREE;
 
diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h
index c8215b681ef..df19154a3d5 100644
--- a/gcc/pointer-query.h
+++ b/gcc/pointer-query.h
@@ -61,15 +61,19 @@  class pointer_query;
 struct access_ref
 {
   /* Set the bounds of the reference.  */
-  access_ref (range_query *query = nullptr, tree = NULL_TREE,
-	      gimple * = nullptr, bool = false);
+  access_ref ();
 
   /* Return the PHI node REF refers to or null if it doesn't.  */
   gphi *phi () const;
 
+  /* Merge the result for a pointer with *THIS.  */
+  bool merge_ref (vec<access_ref> *all_refs, tree, gimple *, int, bool,
+		  ssa_name_limit_t &, pointer_query &);
+
   /* Return the object to which REF refers.  */
-  tree get_ref (vec<access_ref> *, access_ref * = nullptr, int = 1,
-		ssa_name_limit_t * = nullptr, pointer_query * = nullptr) const;
+  tree get_ref (vec<access_ref> *, access_ref * = NULL, int = 1,
+		ssa_name_limit_t * = nullptr,
+		pointer_query * = nullptr) const;
 
   /* Return true if OFFRNG is the constant zero.  */
   bool offset_zero () const
@@ -119,7 +123,10 @@  struct access_ref
 
   /* Issue an informational message describing the target of an access
      with the given mode.  */
-  void inform_access (access_mode) const;
+  void inform_access (access_mode, int = 1) const;
+
+  /* Dump *THIS to file.  */
+  void dump (FILE *) const;
 
   /* Reference to the accessed object(s).  */
   tree ref;
@@ -129,11 +136,6 @@  struct access_ref
   offset_int sizrng[2];
   /* The minimum and maximum offset computed.  */
   offset_int offmax[2];
-  /* Range of the bound of the access: denotes that the access
-     is at least BNDRNG[0] bytes but no more than BNDRNG[1].
-     For string functions the size of the actual access is
-     further constrained by the length of the string.  */
-  offset_int bndrng[2];
 
   /* Used to fold integer expressions when called from front ends.  */
   tree (*eval)(tree);
@@ -206,23 +208,18 @@  struct access_data
 {
   /* Set the access to at most MAXWRITE and MAXREAD bytes, and
      at least 1 when MINWRITE or MINREAD, respectively, is set.  */
-  access_data (range_query *query, gimple *stmt, access_mode mode,
-	       tree maxwrite = NULL_TREE, bool minwrite = false,
-	       tree maxread = NULL_TREE, bool minread = false)
-    : stmt (stmt), call (),
-      dst (query, maxwrite, stmt, minwrite),
-      src (query, maxread, stmt, minread),
-      mode (mode) { }
+  access_data (range_query *, gimple *, access_mode,
+	       tree = NULL_TREE, bool = false,
+	       tree = NULL_TREE, bool = false);
 
   /* Set the access to at most MAXWRITE and MAXREAD bytes, and
      at least 1 when MINWRITE or MINREAD, respectively, is set.  */
-  access_data (range_query *query, tree expr, access_mode mode,
-	       tree maxwrite = NULL_TREE, bool minwrite = false,
-	       tree maxread = NULL_TREE, bool minread = false)
-    : stmt (), call (expr),
-      dst (query, maxwrite, nullptr, minwrite),
-      src (query, maxread, nullptr, minread),
-      mode (mode) { }
+  access_data (range_query *, tree, access_mode,
+	       tree = NULL_TREE, bool = false,
+	       tree = NULL_TREE, bool = false);
+
+  /* Constructor helper.  */
+  static void set_bound (offset_int[2], tree, bool, range_query *, gimple *);
 
   /* Access statement.  */
   gimple *stmt;
@@ -230,9 +227,19 @@  struct access_data
   tree call;
   /* Destination and source of the access.  */
   access_ref dst, src;
+
+  /* Range of the bound of the access: denotes that the access is at
+     least XXX_BNDRNG[0] bytes but no more than XXX_BNDRNG[1].  For
+     string functions the size of the actual access is further
+     constrained by the length of the string.  */
+  offset_int dst_bndrng[2];
+  offset_int src_bndrng[2];
+
   /* Read-only for functions like memcmp or strlen, write-only
      for memset, read-write for memcpy or strcat.  */
   access_mode mode;
+  /* The object size type.  */
+  int ostype;
 };
 
 enum size_range_flags
@@ -263,8 +270,13 @@  inline tree compute_objsize (tree ptr, int ostype, access_ref *pref)
 }
 
 /* Legacy/transitional API.  Should not be used in new code.  */
-extern tree compute_objsize (tree, int, tree * = nullptr, tree * = nullptr,
-			     range_query * = nullptr);
+extern tree compute_objsize (tree, gimple *, int, tree * = nullptr,
+			     tree * = nullptr, range_query * = nullptr);
+inline tree compute_objsize (tree ptr, int ostype, tree *pdecl = nullptr,
+			     tree *poff = nullptr, range_query *rvals = nullptr)
+{
+  return compute_objsize (ptr, nullptr, ostype, pdecl, poff, rvals);
+}
 
 /* Return the field at the constant offset.  */
 extern tree field_at_offset (tree, tree, HOST_WIDE_INT,
diff --git a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
index e5802613a9c..a25cc1fb2ca 100644
--- a/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
+++ b/gcc/testsuite/c-c++-common/Wstringop-overflow-2.c
@@ -287,9 +287,11 @@  void ga1i__ (void)
   a1i__.a[2] = 2;                // { dg-warning "\\\[-Wstringop-overflow" }
 
   struct A1i a = { 0 };
+  sink (&a);
+
   a.a[0] = 0;
   a.a[1] = 1;                    // { dg-warning "\\\[-Wstringop-overflow" }
-  a.a[2] = 2;                    // { dg-warning "\\\[-Wstringop-overflow" "pr102462" { xfail { vect_slp_v2qi_store_align } } }
+  a.a[2] = 2;                    // { dg-warning "\\\[-Wstringop-overflow" }
   sink (&a);
 }
 
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-83.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-83.c
new file mode 100644
index 00000000000..6928ee4d559
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-83.c
@@ -0,0 +1,19 @@ 
+/* PR  middle-end/103143 - ICE due to infinite recursion in pointer-query.cc
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void foo (size_t x)
+{
+  struct T { char buf[64]; char buf2[64]; } t;
+  char *p = &t.buf[8];
+  char *r = t.buf2;
+  size_t i;
+
+  for (i = 0; i < x; i++)
+    {
+      r = __builtin_mempcpy (r, p, i);
+      p = r + 1;
+    }
+}