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:
Martin Sebor 2021-12-06 09:33:32 -07:00
parent 10d185b984
commit 1334d889d4
2 changed files with 432 additions and 364 deletions

View 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,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 ();

View 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