[v2,4/5] fix up compute_objsize: refactor it into helpers

Message ID f19e40e9-488f-2de1-6c14-0dbee81c2a84@gmail.com
State New
Headers
Series [v2] fix PR 103143 |

Commit Message

Martin Sebor Dec. 6, 2021, 5:32 p.m. UTC
  Attached is the subset of the patch in part (2) below: refactor
compute_objsize_r into helpers.  It applies on top of patch 3/5.

On 12/3/21 5:00 PM, Jeff Law wrote:
> 
> 
> 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
>
  

Comments

Jeff Law Dec. 8, 2021, 7:12 p.m. UTC | #1
On 12/6/2021 10:32 AM, Martin Sebor wrote:
> Attached is the subset of the patch in part (2) below: refactor
> compute_objsize_r into helpers.  It applies on top of patch 3/5.
>
> On 12/3/21 5:00 PM, Jeff Law wrote:
>>
>>
>> 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
>>
>
>
> gcc-pointer_query-refactor-4.diff
>
> commit 7d1d8cc18678ccaec5f274919e0c9910ccfea86e
> Author: Martin Sebor <msebor@redhat.com>
> Date:   Mon Dec 6 09:33:32 2021 -0700
>
>      Refactor compute_objsize_r into helpers.
>      
>      gcc/ChangeLog:
>      
>              * pointer-query.cc (compute_objsize_r): Add an argument.
>              (gimple_call_return_array): Pass a new argument to compute_objsize_r.
>              (access_ref::merge_ref): Same.
>              (access_ref::inform_access): Add an argument and use it.
>              (access_data::access_data): Initialize new member.
>              (handle_min_max_size): Pass a new argument to compute_objsize_r.
>              (handle_decl): New function.
>              (handle_array_ref): Pass a new argument to compute_objsize_r.
>              Avoid incrementing deref.
>              (set_component_ref_size): New function.
>              (handle_component_ref): New function.
>              (handle_mem_ref): Pass a new argument to compute_objsize_r.
>              Only increment deref after successfully computing object size.
>              (handle_ssa_name): New function.
>              (compute_objsize_r): Move code into helpers and call them.
>              (compute_objsize): Pass a new argument to compute_objsize_r.
>              * pointer-query.h (access_ref::inform_access): Add an argument.
>              (access_data::ostype): New member.
Thanks.  This was a bit uglier than I expected to review -- but I think 
that largely stems from the somewhat chaotic nature of the original code 
where we have fragments like

   block A

   block B

   block C

Where blocks A & C are closely related and you've refactored them into a 
common function.  Parts of B may stay while other parts end up in a 
different refactored function.  In and of itself that usually isn't too 
bad, but in this case git diff made a bit of a mess out of things making 
the rearrangements harder to follow.  I chased most stuff around and it 
all looked sensible.

OK.

Thanks,
Jeff
  

Patch

commit 7d1d8cc18678ccaec5f274919e0c9910ccfea86e
Author: Martin Sebor <msebor@redhat.com>
Date:   Mon Dec 6 09:33:32 2021 -0700

    Refactor compute_objsize_r into helpers.
    
    gcc/ChangeLog:
    
            * pointer-query.cc (compute_objsize_r): Add an argument.
            (gimple_call_return_array): Pass a new argument to compute_objsize_r.
            (access_ref::merge_ref): Same.
            (access_ref::inform_access): Add an argument and use it.
            (access_data::access_data): Initialize new member.
            (handle_min_max_size): Pass a new argument to compute_objsize_r.
            (handle_decl): New function.
            (handle_array_ref): Pass a new argument to compute_objsize_r.
            Avoid incrementing deref.
            (set_component_ref_size): New function.
            (handle_component_ref): New function.
            (handle_mem_ref): Pass a new argument to compute_objsize_r.
            Only increment deref after successfully computing object size.
            (handle_ssa_name): New function.
            (compute_objsize_r): Move code into helpers and call them.
            (compute_objsize): Pass a new argument to compute_objsize_r.
            * pointer-query.h (access_ref::inform_access): Add an argument.
            (access_data::ostype): New member.

diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 24fbac84ec4..3f583110c71 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_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];
 	  }
@@ -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];
 	  }
@@ -635,7 +635,7 @@  access_ref::merge_ref (vec<access_ref> *all_refs, tree arg, gimple *stmt,
 		       ssa_name_limit_t &snlim, pointer_query &qry)
 {
   access_ref aref;
-  if (!compute_objsize_r (arg, stmt, ostype, &aref, snlim, &qry)
+  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;
@@ -997,42 +997,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
@@ -1244,7 +1247,7 @@  access_data::access_data (range_query *query, gimple *stmt, access_mode mode,
 			  bool minwrite /* = false */,
 			  tree maxread /* = NULL_TREE */,
 			  bool minread /* = false */)
-  : stmt (stmt), call (), dst (), src (), mode (mode)
+  : stmt (stmt), call (), dst (), src (), mode (mode), ostype ()
 {
   set_bound (dst_bndrng, maxwrite, minwrite, query, stmt);
   set_bound (src_bndrng, maxread, minread, query, stmt);
@@ -1257,7 +1260,7 @@  access_data::access_data (range_query *query, tree expr, access_mode mode,
 			  bool minwrite /* = false */,
 			  tree maxread /* = NULL_TREE */,
 			  bool minread /* = false */)
-  : stmt (), call (expr),  dst (), src (), mode (mode)
+  : stmt (), call (expr),  dst (), src (), mode (mode), ostype ()
 {
   set_bound (dst_bndrng, maxwrite, minwrite, query, stmt);
   set_bound (src_bndrng, maxread, minread, query, stmt);
@@ -1406,7 +1409,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;
@@ -1606,7 +1610,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;
@@ -1615,7 +1619,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;
@@ -1678,6 +1682,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.  */
@@ -1689,8 +1733,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)
@@ -1698,7 +1740,7 @@  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];
@@ -1759,6 +1801,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.  */
 
@@ -1768,10 +1900,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).
@@ -1785,9 +1916,11 @@  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;
@@ -1802,168 +1935,283 @@  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))
-    {
-      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;
+  gimple *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];
+      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).
+   ADDR is true for an enclosing ADDR_EXPR.
+   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
@@ -1984,21 +2232,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
@@ -2007,200 +2251,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, stmt, 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.  */
-	      *pref = *cache_ref;
-	      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
@@ -2231,7 +2297,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 ();
diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h
index fe46f711e96..a7ac7d34370 100644
--- a/gcc/pointer-query.h
+++ b/gcc/pointer-query.h
@@ -122,7 +122,7 @@  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;
 
   /* Reference to the accessed object(s).  */
   tree ref;
@@ -234,6 +234,8 @@  struct access_data
   /* 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