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.
This commit is contained in:
parent
10d185b984
commit
1334d889d4
2 changed files with 432 additions and 364 deletions
|
@ -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,6 +1935,212 @@ handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref,
|
|||
return true;
|
||||
}
|
||||
|
||||
/* A helper of compute_objsize_r() to determine the size from SSA_NAME
|
||||
PTR. Return true on success and false on failure. */
|
||||
|
||||
static bool
|
||||
handle_ssa_name (tree ptr, bool addr, int ostype,
|
||||
access_ref *pref, ssa_name_limit_t &snlim,
|
||||
pointer_query *qry)
|
||||
{
|
||||
if (!snlim.next ())
|
||||
return false;
|
||||
|
||||
/* Only process an SSA_NAME if the recursion limit has not yet
|
||||
been reached. */
|
||||
if (qry)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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 (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, ostype);
|
||||
return true;
|
||||
}
|
||||
|
||||
pref->set_max_size_range ();
|
||||
pref->base0 = false;
|
||||
pref->ref = ptr;
|
||||
qry->put_ref (ptr, *pref, ostype);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gimple_code (stmt) == GIMPLE_PHI)
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
|
||||
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, ostype);
|
||||
return true;
|
||||
}
|
||||
|
||||
tree rhs = gimple_assign_rhs1 (stmt);
|
||||
|
||||
if (code == ASSERT_EXPR)
|
||||
{
|
||||
rhs = TREE_OPERAND (rhs, 0);
|
||||
return compute_objsize_r (rhs, stmt, addr, 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, addr, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
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
|
||||
pref->add_max_offset ();
|
||||
|
||||
qry->put_ref (ptr, *pref, ostype);
|
||||
return true;
|
||||
}
|
||||
|
||||
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 (ostype > 1 && POINTER_TYPE_P (TREE_TYPE (rhs)))
|
||||
{
|
||||
/* 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;
|
||||
|
||||
rhs = pref->ref;
|
||||
}
|
||||
|
||||
/* (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).
|
||||
|
@ -1809,6 +2148,7 @@ handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref,
|
|||
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)
|
||||
|
@ -1818,152 +2158,60 @@ handle_mem_ref (tree mref, gimple *stmt, int ostype, access_ref *pref,
|
|||
to influence code generation or optimization. */
|
||||
|
||||
static bool
|
||||
compute_objsize_r (tree ptr, gimple *stmt, int ostype, access_ref *pref,
|
||||
ssa_name_limit_t &snlim, pointer_query *qry)
|
||||
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);
|
||||
|
||||
const bool addr = TREE_CODE (ptr) == ADDR_EXPR;
|
||||
if (addr)
|
||||
{
|
||||
--pref->deref;
|
||||
ptr = TREE_OPERAND (ptr, 0);
|
||||
}
|
||||
|
||||
if (DECL_P (ptr))
|
||||
return handle_decl (ptr, addr, pref);
|
||||
|
||||
switch (TREE_CODE (ptr))
|
||||
{
|
||||
pref->ref = ptr;
|
||||
case ADDR_EXPR:
|
||||
{
|
||||
tree ref = TREE_OPERAND (ptr, 0);
|
||||
if (!compute_objsize_r (ref, stmt, true, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
/* 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;
|
||||
--pref->deref;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!addr && POINTER_TYPE_P (TREE_TYPE (ptr)))
|
||||
{
|
||||
/* 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;
|
||||
}
|
||||
case BIT_FIELD_REF:
|
||||
{
|
||||
tree ref = TREE_OPERAND (ptr, 0);
|
||||
if (!compute_objsize_r (ref, stmt, addr, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
/* Valid offsets into the object are nonnegative. */
|
||||
pref->base0 = true;
|
||||
offset_int off = wi::to_offset (pref->eval (TREE_OPERAND (ptr, 2)));
|
||||
pref->add_offset (off / BITS_PER_UNIT);
|
||||
return 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;
|
||||
}
|
||||
case ARRAY_REF:
|
||||
return handle_array_ref (ptr, stmt, addr, ostype, pref, snlim, qry);
|
||||
|
||||
pref->set_max_size_range ();
|
||||
return true;
|
||||
}
|
||||
case COMPONENT_REF:
|
||||
return handle_component_ref (ptr, stmt, addr, ostype, pref, snlim, qry);
|
||||
|
||||
const tree_code code = TREE_CODE (ptr);
|
||||
range_query *const rvals = qry ? qry->rvals : NULL;
|
||||
case MEM_REF:
|
||||
return handle_mem_ref (ptr, stmt, ostype, pref, snlim, qry);
|
||||
|
||||
if (code == BIT_FIELD_REF)
|
||||
{
|
||||
tree ref = TREE_OPERAND (ptr, 0);
|
||||
if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
case TARGET_MEM_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;
|
||||
}
|
||||
/* TODO: Handle remaining operands. Until then, add maximum offset. */
|
||||
pref->ref = ptr;
|
||||
pref->add_max_offset ();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (code == COMPONENT_REF)
|
||||
{
|
||||
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 (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 ();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
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);
|
||||
}
|
||||
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 == TARGET_MEM_REF)
|
||||
{
|
||||
tree ref = TREE_OPERAND (ptr, 0);
|
||||
if (!compute_objsize_r (ref, stmt, ostype, pref, snlim, qry))
|
||||
return false;
|
||||
|
||||
/* TODO: Handle remaining operands. Until then, add maximum offset. */
|
||||
pref->ref = ptr;
|
||||
pref->add_max_offset ();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (code == INTEGER_CST)
|
||||
{
|
||||
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 ();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue