]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Refactor compute_objsize_r into helpers.
authorMartin Sebor <msebor@redhat.com>
Mon, 6 Dec 2021 16:33:32 +0000 (09:33 -0700)
committerMartin Sebor <msebor@redhat.com>
Thu, 9 Dec 2021 16:51:48 +0000 (09:51 -0700)
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.

gcc/pointer-query.cc
gcc/pointer-query.h

index 24fbac84ec4629bb2ddd4974550a3ce7f7214428..3f583110c71df2aba6037d18707319f102f0897c 100644 (file)
@@ -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 ();
index fe46f711e96b415703aaa47f7ff917e499383ad0..a7ac7d34370c4d7e0d0dfc48e49a38f0d7a89eea 100644 (file)
@@ -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