mirror of
https://forge.sourceware.org/marek/gcc.git
synced 2026-02-22 03:47:02 -05:00
2527 lines
62 KiB
C++
2527 lines
62 KiB
C++
/* Regions of memory.
|
||
Copyright (C) 2019-2026 Free Software Foundation, Inc.
|
||
Contributed by David Malcolm <dmalcolm@redhat.com>.
|
||
|
||
This file is part of GCC.
|
||
|
||
GCC is free software; you can redistribute it and/or modify it
|
||
under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 3, or (at your option)
|
||
any later version.
|
||
|
||
GCC is distributed in the hope that it will be useful, but
|
||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||
General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with GCC; see the file COPYING3. If not see
|
||
<http://www.gnu.org/licenses/>. */
|
||
|
||
#include "analyzer/common.h"
|
||
|
||
#include "ordered-hash-map.h"
|
||
#include "options.h"
|
||
#include "cgraph.h"
|
||
#include "cfg.h"
|
||
#include "digraph.h"
|
||
#include "sbitmap.h"
|
||
#include "fold-const.h"
|
||
#include "tree-ssa.h"
|
||
|
||
#include "analyzer/analyzer-logging.h"
|
||
#include "analyzer/supergraph.h"
|
||
#include "analyzer/call-string.h"
|
||
#include "analyzer/program-point.h"
|
||
#include "analyzer/store.h"
|
||
#include "analyzer/region.h"
|
||
#include "analyzer/region-model.h"
|
||
#include "analyzer/sm.h"
|
||
#include "analyzer/program-state.h"
|
||
#include "text-art/dump.h"
|
||
|
||
#if ENABLE_ANALYZER
|
||
|
||
namespace ana {
|
||
|
||
region_offset
|
||
region_offset::make_byte_offset (const region *base_region,
|
||
const svalue *num_bytes_sval)
|
||
{
|
||
if (tree num_bytes_cst = num_bytes_sval->maybe_get_constant ())
|
||
{
|
||
gcc_assert (TREE_CODE (num_bytes_cst) == INTEGER_CST);
|
||
bit_offset_t num_bits = wi::to_offset (num_bytes_cst) * BITS_PER_UNIT;
|
||
return make_concrete (base_region, num_bits);
|
||
}
|
||
else
|
||
{
|
||
return make_symbolic (base_region, num_bytes_sval);
|
||
}
|
||
}
|
||
|
||
const svalue &
|
||
region_offset::calc_symbolic_bit_offset (region_model_manager *mgr) const
|
||
{
|
||
if (symbolic_p ())
|
||
{
|
||
const svalue *bits_per_byte
|
||
= mgr->get_or_create_int_cst (NULL_TREE, BITS_PER_UNIT);
|
||
return *mgr->get_or_create_binop (NULL_TREE, MULT_EXPR,
|
||
m_sym_offset, bits_per_byte);
|
||
}
|
||
else
|
||
return *mgr->get_or_create_int_cst (NULL_TREE, m_offset);
|
||
}
|
||
|
||
const svalue *
|
||
region_offset::calc_symbolic_byte_offset (region_model_manager *mgr) const
|
||
{
|
||
if (symbolic_p ())
|
||
return m_sym_offset;
|
||
else
|
||
{
|
||
byte_offset_t concrete_byte_offset;
|
||
if (get_concrete_byte_offset (&concrete_byte_offset))
|
||
return mgr->get_or_create_int_cst (size_type_node,
|
||
concrete_byte_offset);
|
||
else
|
||
/* Can't handle bitfields; return UNKNOWN. */
|
||
return mgr->get_or_create_unknown_svalue (size_type_node);
|
||
}
|
||
}
|
||
|
||
void
|
||
region_offset::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (symbolic_p ())
|
||
{
|
||
/* We don't bother showing the base region. */
|
||
pp_string (pp, "byte ");
|
||
m_sym_offset->dump_to_pp (pp, simple);
|
||
}
|
||
else
|
||
{
|
||
if (m_offset % BITS_PER_UNIT == 0)
|
||
{
|
||
pp_string (pp, "byte ");
|
||
pp_wide_int (pp, m_offset / BITS_PER_UNIT, SIGNED);
|
||
}
|
||
else
|
||
{
|
||
pp_string (pp, "bit ");
|
||
pp_wide_int (pp, m_offset, SIGNED);
|
||
}
|
||
}
|
||
}
|
||
|
||
DEBUG_FUNCTION void
|
||
region_offset::dump (bool simple) const
|
||
{
|
||
tree_dump_pretty_printer pp (stderr);
|
||
dump_to_pp (&pp, simple);
|
||
pp_newline (&pp);
|
||
}
|
||
|
||
/* An svalue that matches the pattern (BASE * FACTOR) + OFFSET
|
||
where FACTOR or OFFSET could be the identity (represented as nullptr). */
|
||
|
||
struct linear_op
|
||
{
|
||
linear_op (const svalue *base,
|
||
const svalue *factor,
|
||
const svalue *offset)
|
||
: m_base (base), m_factor (factor), m_offset (offset)
|
||
{
|
||
}
|
||
|
||
bool maybe_get_cst_factor (bit_offset_t *out) const
|
||
{
|
||
if (m_factor == nullptr)
|
||
{
|
||
*out = 1;
|
||
return true;
|
||
}
|
||
if (tree cst_factor = m_factor->maybe_get_constant ())
|
||
{
|
||
*out = wi::to_offset (cst_factor);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool maybe_get_cst_offset (bit_offset_t *out) const
|
||
{
|
||
if (m_offset == nullptr)
|
||
{
|
||
*out = 0;
|
||
return true;
|
||
}
|
||
if (tree cst_offset = m_offset->maybe_get_constant ())
|
||
{
|
||
*out = wi::to_offset (cst_offset);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
static tristate
|
||
less (const linear_op &a, const linear_op &b)
|
||
{
|
||
/* Same base. */
|
||
if (a.m_base == b.m_base)
|
||
{
|
||
bit_offset_t a_wi_factor;
|
||
bit_offset_t b_wi_factor;
|
||
if (a.maybe_get_cst_factor (&a_wi_factor)
|
||
&& b.maybe_get_cst_factor (&b_wi_factor))
|
||
{
|
||
if (a_wi_factor != b_wi_factor)
|
||
return tristate (a_wi_factor < b_wi_factor);
|
||
else
|
||
{
|
||
bit_offset_t a_wi_offset;
|
||
bit_offset_t b_wi_offset;
|
||
if (a.maybe_get_cst_offset (&a_wi_offset)
|
||
&& b.maybe_get_cst_offset (&b_wi_offset))
|
||
return tristate (a_wi_offset < b_wi_offset);
|
||
}
|
||
}
|
||
}
|
||
return tristate::unknown ();
|
||
}
|
||
|
||
static tristate
|
||
le (const linear_op &a, const linear_op &b)
|
||
{
|
||
/* Same base. */
|
||
if (a.m_base == b.m_base)
|
||
{
|
||
bit_offset_t a_wi_factor;
|
||
bit_offset_t b_wi_factor;
|
||
if (a.maybe_get_cst_factor (&a_wi_factor)
|
||
&& b.maybe_get_cst_factor (&b_wi_factor))
|
||
{
|
||
if (a_wi_factor != b_wi_factor)
|
||
return tristate (a_wi_factor <= b_wi_factor);
|
||
else
|
||
{
|
||
bit_offset_t a_wi_offset;
|
||
bit_offset_t b_wi_offset;
|
||
if (a.maybe_get_cst_offset (&a_wi_offset)
|
||
&& b.maybe_get_cst_offset (&b_wi_offset))
|
||
return tristate (a_wi_offset <= b_wi_offset);
|
||
}
|
||
}
|
||
}
|
||
return tristate::unknown ();
|
||
}
|
||
|
||
static bool
|
||
from_svalue (const svalue &sval, linear_op *out)
|
||
{
|
||
switch (sval.get_kind ())
|
||
{
|
||
default:
|
||
break;
|
||
case SK_BINOP:
|
||
{
|
||
const binop_svalue &binop_sval ((const binop_svalue &)sval);
|
||
if (binop_sval.get_op () == MULT_EXPR)
|
||
{
|
||
*out = linear_op (binop_sval.get_arg0 (),
|
||
binop_sval.get_arg1 (),
|
||
nullptr);
|
||
return true;
|
||
}
|
||
else if (binop_sval.get_op () == PLUS_EXPR)
|
||
{
|
||
if (binop_sval.get_arg0 ()->get_kind () == SK_BINOP)
|
||
{
|
||
const binop_svalue &inner_binop_sval
|
||
((const binop_svalue &)*binop_sval.get_arg0 ());
|
||
if (inner_binop_sval.get_op () == MULT_EXPR)
|
||
{
|
||
*out = linear_op (inner_binop_sval.get_arg0 (),
|
||
inner_binop_sval.get_arg1 (),
|
||
binop_sval.get_arg1 ());
|
||
return true;
|
||
}
|
||
}
|
||
|
||
*out = linear_op (binop_sval.get_arg0 (),
|
||
nullptr,
|
||
binop_sval.get_arg1 ());
|
||
return true;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
const svalue *m_base;
|
||
const svalue *m_factor;
|
||
const svalue *m_offset;
|
||
};
|
||
|
||
bool
|
||
operator< (const region_offset &a, const region_offset &b)
|
||
{
|
||
if (a.symbolic_p ())
|
||
{
|
||
if (b.symbolic_p ())
|
||
{
|
||
/* Symbolic vs symbolic. */
|
||
const svalue &a_sval = *a.get_symbolic_byte_offset ();
|
||
const svalue &b_sval = *b.get_symbolic_byte_offset ();
|
||
|
||
linear_op op_a (nullptr, nullptr, nullptr);
|
||
linear_op op_b (nullptr, nullptr, nullptr);
|
||
if (linear_op::from_svalue (a_sval, &op_a)
|
||
&& linear_op::from_svalue (b_sval, &op_b))
|
||
{
|
||
tristate ts = linear_op::less (op_a, op_b);
|
||
if (ts.is_true ())
|
||
return true;
|
||
else if (ts.is_false ())
|
||
return false;
|
||
}
|
||
/* Use svalue's deterministic order, for now. */
|
||
return (svalue::cmp_ptr (a.get_symbolic_byte_offset (),
|
||
b.get_symbolic_byte_offset ())
|
||
< 0);
|
||
}
|
||
else
|
||
/* Symbolic vs concrete: put all symbolic after all concrete. */
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if (b.symbolic_p ())
|
||
/* Concrete vs symbolic: put all concrete before all symbolic. */
|
||
return true;
|
||
else
|
||
/* Concrete vs concrete. */
|
||
return a.get_bit_offset () < b.get_bit_offset ();
|
||
}
|
||
}
|
||
|
||
bool
|
||
operator<= (const region_offset &a, const region_offset &b)
|
||
{
|
||
if (a.symbolic_p ())
|
||
{
|
||
if (b.symbolic_p ())
|
||
{
|
||
/* Symbolic vs symbolic. */
|
||
const svalue &a_sval = *a.get_symbolic_byte_offset ();
|
||
const svalue &b_sval = *b.get_symbolic_byte_offset ();
|
||
|
||
linear_op op_a (nullptr, nullptr, nullptr);
|
||
linear_op op_b (nullptr, nullptr, nullptr);
|
||
if (linear_op::from_svalue (a_sval, &op_a)
|
||
&& linear_op::from_svalue (b_sval, &op_b))
|
||
{
|
||
tristate ts = linear_op::le (op_a, op_b);
|
||
if (ts.is_true ())
|
||
return true;
|
||
else if (ts.is_false ())
|
||
return false;
|
||
}
|
||
/* Use svalue's deterministic order, for now. */
|
||
return (svalue::cmp_ptr (a.get_symbolic_byte_offset (),
|
||
b.get_symbolic_byte_offset ())
|
||
<= 0);
|
||
}
|
||
else
|
||
/* Symbolic vs concrete: put all symbolic after all concrete. */
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
if (b.symbolic_p ())
|
||
/* Concrete vs symbolic: put all concrete before all symbolic. */
|
||
return true;
|
||
else
|
||
/* Concrete vs concrete. */
|
||
return a.get_bit_offset () <= b.get_bit_offset ();
|
||
}
|
||
}
|
||
|
||
bool
|
||
operator> (const region_offset &a, const region_offset &b)
|
||
{
|
||
return b < a;
|
||
}
|
||
|
||
bool
|
||
operator>= (const region_offset &a, const region_offset &b)
|
||
{
|
||
return b <= a;
|
||
}
|
||
|
||
region_offset
|
||
strip_types (const region_offset &offset, region_model_manager &mgr)
|
||
{
|
||
if (offset.symbolic_p ())
|
||
return region_offset::make_symbolic
|
||
(offset.get_base_region (),
|
||
strip_types (offset.get_symbolic_byte_offset (),
|
||
mgr));
|
||
else
|
||
return offset;
|
||
}
|
||
|
||
/* class region and its various subclasses. */
|
||
|
||
/* class region. */
|
||
|
||
region::~region ()
|
||
{
|
||
delete m_cached_offset;
|
||
}
|
||
|
||
/* Determine the base region for this region: when considering bindings
|
||
for this region, the base region is the ancestor which identifies
|
||
which cluster they should be partitioned into.
|
||
Regions within the same struct/union/array are in the same cluster.
|
||
Different decls are in different clusters. */
|
||
|
||
const region *
|
||
region::get_base_region () const
|
||
{
|
||
const region *iter = this;
|
||
while (iter)
|
||
{
|
||
switch (iter->get_kind ())
|
||
{
|
||
case RK_FIELD:
|
||
case RK_ELEMENT:
|
||
case RK_OFFSET:
|
||
case RK_SIZED:
|
||
case RK_BIT_RANGE:
|
||
case RK_CAST:
|
||
iter = iter->get_parent_region ();
|
||
continue;
|
||
default:
|
||
return iter;
|
||
}
|
||
}
|
||
return iter;
|
||
}
|
||
|
||
/* Return true if get_base_region() == this for this region. */
|
||
|
||
bool
|
||
region::base_region_p () const
|
||
{
|
||
switch (get_kind ())
|
||
{
|
||
/* Region kinds representing a descendent of a base region. */
|
||
case RK_FIELD:
|
||
case RK_ELEMENT:
|
||
case RK_OFFSET:
|
||
case RK_SIZED:
|
||
case RK_CAST:
|
||
case RK_BIT_RANGE:
|
||
return false;
|
||
|
||
default:
|
||
return true;
|
||
}
|
||
}
|
||
|
||
/* Return true if this region is ELDER or one of its descendents. */
|
||
|
||
bool
|
||
region::descendent_of_p (const region *elder) const
|
||
{
|
||
const region *iter = this;
|
||
while (iter)
|
||
{
|
||
if (iter == elder)
|
||
return true;
|
||
iter = iter->get_parent_region ();
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/* If this region is a frame_region, or a descendent of one, return it.
|
||
Otherwise return nullptr. */
|
||
|
||
const frame_region *
|
||
region::maybe_get_frame_region () const
|
||
{
|
||
const region *iter = this;
|
||
while (iter)
|
||
{
|
||
if (const frame_region *frame_reg = iter->dyn_cast_frame_region ())
|
||
return frame_reg;
|
||
iter = iter->get_parent_region ();
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
/* Get the memory space of this region. */
|
||
|
||
enum memory_space
|
||
region::get_memory_space () const
|
||
{
|
||
const region *iter = this;
|
||
while (iter)
|
||
{
|
||
switch (iter->get_kind ())
|
||
{
|
||
default:
|
||
break;
|
||
case RK_GLOBALS:
|
||
return MEMSPACE_GLOBALS;
|
||
case RK_CODE:
|
||
case RK_FUNCTION:
|
||
case RK_LABEL:
|
||
return MEMSPACE_CODE;
|
||
case RK_FRAME:
|
||
case RK_STACK:
|
||
case RK_ALLOCA:
|
||
return MEMSPACE_STACK;
|
||
case RK_HEAP:
|
||
case RK_HEAP_ALLOCATED:
|
||
return MEMSPACE_HEAP;
|
||
case RK_STRING:
|
||
return MEMSPACE_READONLY_DATA;
|
||
case RK_PRIVATE:
|
||
return MEMSPACE_PRIVATE;
|
||
}
|
||
iter = iter->get_parent_region ();
|
||
}
|
||
return MEMSPACE_UNKNOWN;
|
||
}
|
||
|
||
/* Subroutine for use by region_model_manager::get_or_create_initial_value.
|
||
Return true if this region has an initial_svalue.
|
||
Return false if attempting to use INIT_VAL(this_region) should give
|
||
the "UNINITIALIZED" poison value. */
|
||
|
||
bool
|
||
region::can_have_initial_svalue_p () const
|
||
{
|
||
const region *base_reg = get_base_region ();
|
||
|
||
/* Check for memory spaces that are uninitialized by default. */
|
||
enum memory_space mem_space = base_reg->get_memory_space ();
|
||
switch (mem_space)
|
||
{
|
||
default:
|
||
gcc_unreachable ();
|
||
case MEMSPACE_UNKNOWN:
|
||
case MEMSPACE_CODE:
|
||
case MEMSPACE_GLOBALS:
|
||
case MEMSPACE_READONLY_DATA:
|
||
case MEMSPACE_PRIVATE:
|
||
/* Such regions have initial_svalues. */
|
||
return true;
|
||
|
||
case MEMSPACE_HEAP:
|
||
/* Heap allocations are uninitialized by default. */
|
||
return false;
|
||
|
||
case MEMSPACE_STACK:
|
||
if (tree decl = base_reg->maybe_get_decl ())
|
||
{
|
||
/* See the assertion in frame_region::get_region_for_local for the
|
||
tree codes we need to handle here. */
|
||
switch (TREE_CODE (decl))
|
||
{
|
||
default:
|
||
gcc_unreachable ();
|
||
|
||
case PARM_DECL:
|
||
/* Parameters have initial values. */
|
||
return true;
|
||
|
||
case VAR_DECL:
|
||
case RESULT_DECL:
|
||
/* Function locals don't have initial values. */
|
||
return false;
|
||
|
||
case SSA_NAME:
|
||
{
|
||
/* Some SSA names have an implicit default defined value. */
|
||
tree ssa_name = decl;
|
||
if (SSA_NAME_IS_DEFAULT_DEF (ssa_name))
|
||
return ssa_defined_default_def_p (ssa_name);
|
||
/* Others don't. */
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* If we have an on-stack region that isn't associated with a decl
|
||
or SSA name, then we have VLA/alloca, which is uninitialized. */
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/* For regions within a global decl, get the svalue for the initial
|
||
value of this region when the program starts, caching the result. */
|
||
|
||
const svalue *
|
||
region::get_initial_value_at_main (region_model_manager *mgr) const
|
||
{
|
||
if (!m_cached_init_sval_at_main)
|
||
m_cached_init_sval_at_main = calc_initial_value_at_main (mgr);
|
||
return m_cached_init_sval_at_main;
|
||
}
|
||
|
||
/* Implementation of region::get_initial_value_at_main. */
|
||
|
||
const svalue *
|
||
region::calc_initial_value_at_main (region_model_manager *mgr) const
|
||
{
|
||
const decl_region *base_reg = get_base_region ()->dyn_cast_decl_region ();
|
||
gcc_assert (base_reg);
|
||
|
||
/* Attempt to get the initializer value for base_reg. */
|
||
if (const svalue *base_reg_init
|
||
= base_reg->get_svalue_for_initializer (mgr))
|
||
{
|
||
if (this == base_reg)
|
||
return base_reg_init;
|
||
else
|
||
{
|
||
/* Get the value for REG within base_reg_init. */
|
||
binding_cluster c (*mgr->get_store_manager (), base_reg);
|
||
c.bind (mgr->get_store_manager (), base_reg, base_reg_init);
|
||
const svalue *sval
|
||
= c.get_any_binding (mgr->get_store_manager (), this);
|
||
if (sval)
|
||
{
|
||
if (get_type ())
|
||
sval = mgr->get_or_create_cast (get_type (), sval);
|
||
return sval;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Otherwise, return INIT_VAL(REG). */
|
||
return mgr->get_or_create_initial_value (this);
|
||
}
|
||
|
||
/* If this region is a decl_region, return the decl.
|
||
Otherwise return NULL_TREE. */
|
||
|
||
tree
|
||
region::maybe_get_decl () const
|
||
{
|
||
if (const decl_region *decl_reg = dyn_cast_decl_region ())
|
||
return decl_reg->get_decl ();
|
||
return NULL_TREE;
|
||
}
|
||
|
||
/* Get the region_offset for this region (calculating it on the
|
||
first call and caching it internally). */
|
||
|
||
region_offset
|
||
region::get_offset (region_model_manager *mgr) const
|
||
{
|
||
if(!m_cached_offset)
|
||
m_cached_offset = new region_offset (calc_offset (mgr));
|
||
return *m_cached_offset;
|
||
}
|
||
|
||
/* Get the region_offset for immediately beyond this region. */
|
||
|
||
region_offset
|
||
region::get_next_offset (region_model_manager *mgr) const
|
||
{
|
||
region_offset start = get_offset (mgr);
|
||
|
||
bit_size_t bit_size;
|
||
if (get_bit_size (&bit_size))
|
||
{
|
||
if (start.concrete_p ())
|
||
{
|
||
bit_offset_t next_bit_offset = start.get_bit_offset () + bit_size;
|
||
return region_offset::make_concrete (start.get_base_region (),
|
||
next_bit_offset);
|
||
}
|
||
}
|
||
|
||
const svalue *start_byte_offset_sval = start.calc_symbolic_byte_offset (mgr);
|
||
const svalue *byte_size_sval = get_byte_size_sval (mgr);
|
||
const svalue *sum_sval
|
||
= mgr->get_or_create_binop (size_type_node,
|
||
PLUS_EXPR,
|
||
start_byte_offset_sval,
|
||
byte_size_sval);
|
||
return region_offset::make_symbolic (start.get_base_region (),
|
||
sum_sval);
|
||
}
|
||
|
||
/* Base class implementation of region::get_byte_size vfunc.
|
||
If the size of this region (in bytes) is known statically, write it to *OUT
|
||
and return true.
|
||
Otherwise return false. */
|
||
|
||
bool
|
||
region::get_byte_size (byte_size_t *out) const
|
||
{
|
||
tree type = get_type ();
|
||
|
||
/* Bail out e.g. for heap-allocated regions. */
|
||
if (!type)
|
||
return false;
|
||
|
||
HOST_WIDE_INT bytes = int_size_in_bytes (type);
|
||
if (bytes == -1)
|
||
return false;
|
||
*out = bytes;
|
||
return true;
|
||
}
|
||
|
||
/* Base implementation of region::get_byte_size_sval vfunc. */
|
||
|
||
const svalue *
|
||
region::get_byte_size_sval (region_model_manager *mgr) const
|
||
{
|
||
tree type = get_type ();
|
||
|
||
/* Bail out e.g. for heap-allocated regions. */
|
||
if (!type)
|
||
return mgr->get_or_create_unknown_svalue (size_type_node);
|
||
|
||
HOST_WIDE_INT bytes = int_size_in_bytes (type);
|
||
if (bytes == -1)
|
||
return mgr->get_or_create_unknown_svalue (size_type_node);
|
||
|
||
tree byte_size = size_in_bytes (type);
|
||
if (TREE_TYPE (byte_size) != size_type_node)
|
||
byte_size = fold_build1 (NOP_EXPR, size_type_node, byte_size);
|
||
return mgr->get_or_create_constant_svalue (byte_size);
|
||
}
|
||
|
||
/* Attempt to get the size of TYPE in bits.
|
||
If successful, return true and write the size to *OUT.
|
||
Otherwise return false. */
|
||
|
||
bool
|
||
int_size_in_bits (const_tree type, bit_size_t *out)
|
||
{
|
||
if (INTEGRAL_TYPE_P (type))
|
||
{
|
||
*out = TYPE_PRECISION (type);
|
||
return true;
|
||
}
|
||
|
||
tree sz = TYPE_SIZE (type);
|
||
if (sz
|
||
&& tree_fits_uhwi_p (sz)
|
||
/* If the size is zero, then we may have a zero-sized
|
||
array; handle such cases by returning false. */
|
||
&& !integer_zerop (sz))
|
||
{
|
||
*out = TREE_INT_CST_LOW (sz);
|
||
return true;
|
||
}
|
||
else
|
||
return false;
|
||
}
|
||
|
||
/* Base implementation of region::get_bit_size_sval vfunc. */
|
||
|
||
const svalue *
|
||
region::get_bit_size_sval (region_model_manager *mgr) const
|
||
{
|
||
tree type = get_type ();
|
||
|
||
/* Bail out e.g. for heap-allocated regions. */
|
||
if (!type)
|
||
return mgr->get_or_create_unknown_svalue (size_type_node);
|
||
|
||
bit_size_t bits;
|
||
if (!int_size_in_bits (type, &bits))
|
||
return mgr->get_or_create_unknown_svalue (size_type_node);
|
||
|
||
return mgr->get_or_create_int_cst (size_type_node, bits);
|
||
}
|
||
|
||
/* If the size of this region (in bits) is known statically, write it to *OUT
|
||
and return true.
|
||
Otherwise return false. */
|
||
|
||
bool
|
||
region::get_bit_size (bit_size_t *out) const
|
||
{
|
||
tree type = get_type ();
|
||
|
||
/* Bail out e.g. for heap-allocated regions. */
|
||
if (!type)
|
||
return false;
|
||
|
||
return int_size_in_bits (type, out);
|
||
}
|
||
|
||
/* Get the field within RECORD_TYPE at BIT_OFFSET. */
|
||
|
||
tree
|
||
get_field_at_bit_offset (tree record_type, bit_offset_t bit_offset)
|
||
{
|
||
gcc_assert (TREE_CODE (record_type) == RECORD_TYPE);
|
||
if (bit_offset < 0)
|
||
return nullptr;
|
||
|
||
/* Find the first field that has an offset > BIT_OFFSET,
|
||
then return the one preceding it.
|
||
Skip other trees within the chain, such as FUNCTION_DECLs. */
|
||
tree last_field = NULL_TREE;
|
||
for (tree iter = TYPE_FIELDS (record_type); iter != NULL_TREE;
|
||
iter = DECL_CHAIN (iter))
|
||
{
|
||
if (TREE_CODE (iter) == FIELD_DECL)
|
||
{
|
||
int iter_field_offset = int_bit_position (iter);
|
||
if (bit_offset < iter_field_offset)
|
||
return last_field;
|
||
last_field = iter;
|
||
}
|
||
}
|
||
return last_field;
|
||
}
|
||
|
||
/* Populate *OUT with descendent regions of type TYPE that match
|
||
RELATIVE_BIT_OFFSET and SIZE_IN_BITS within this region. */
|
||
|
||
void
|
||
region::get_subregions_for_binding (region_model_manager *mgr,
|
||
bit_offset_t relative_bit_offset,
|
||
bit_size_t size_in_bits,
|
||
tree type,
|
||
auto_vec <const region *> *out) const
|
||
{
|
||
if (get_type () == NULL_TREE || type == NULL_TREE)
|
||
return;
|
||
if (relative_bit_offset == 0
|
||
&& types_compatible_p (get_type (), type))
|
||
{
|
||
out->safe_push (this);
|
||
return;
|
||
}
|
||
switch (TREE_CODE (get_type ()))
|
||
{
|
||
case ARRAY_TYPE:
|
||
{
|
||
tree element_type = TREE_TYPE (get_type ());
|
||
HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (element_type);
|
||
if (hwi_byte_size > 0)
|
||
{
|
||
HOST_WIDE_INT bits_per_element
|
||
= hwi_byte_size << LOG2_BITS_PER_UNIT;
|
||
HOST_WIDE_INT element_index
|
||
= (relative_bit_offset.to_shwi () / bits_per_element);
|
||
tree element_index_cst
|
||
= build_int_cst (integer_type_node, element_index);
|
||
HOST_WIDE_INT inner_bit_offset
|
||
= relative_bit_offset.to_shwi () % bits_per_element;
|
||
const region *subregion = mgr->get_element_region
|
||
(this, element_type,
|
||
mgr->get_or_create_constant_svalue (element_index_cst));
|
||
subregion->get_subregions_for_binding (mgr, inner_bit_offset,
|
||
size_in_bits, type, out);
|
||
}
|
||
}
|
||
break;
|
||
case RECORD_TYPE:
|
||
{
|
||
/* The bit offset might be *within* one of the fields (such as
|
||
with nested structs).
|
||
So we want to find the enclosing field, adjust the offset,
|
||
and repeat. */
|
||
if (tree field = get_field_at_bit_offset (get_type (),
|
||
relative_bit_offset))
|
||
{
|
||
int field_bit_offset = int_bit_position (field);
|
||
const region *subregion = mgr->get_field_region (this, field);
|
||
subregion->get_subregions_for_binding
|
||
(mgr, relative_bit_offset - field_bit_offset,
|
||
size_in_bits, type, out);
|
||
}
|
||
}
|
||
break;
|
||
case UNION_TYPE:
|
||
{
|
||
for (tree field = TYPE_FIELDS (get_type ()); field != NULL_TREE;
|
||
field = DECL_CHAIN (field))
|
||
{
|
||
if (TREE_CODE (field) != FIELD_DECL)
|
||
continue;
|
||
const region *subregion = mgr->get_field_region (this, field);
|
||
subregion->get_subregions_for_binding (mgr,
|
||
relative_bit_offset,
|
||
size_in_bits,
|
||
type,
|
||
out);
|
||
}
|
||
}
|
||
break;
|
||
default:
|
||
/* Do nothing. */
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Walk from this region up to the base region within its cluster, calculating
|
||
the offset relative to the base region, either as an offset in bits,
|
||
or a symbolic offset. */
|
||
|
||
region_offset
|
||
region::calc_offset (region_model_manager *mgr) const
|
||
{
|
||
const region *iter_region = this;
|
||
bit_offset_t accum_bit_offset = 0;
|
||
const svalue *accum_byte_sval = nullptr;
|
||
|
||
while (iter_region)
|
||
{
|
||
switch (iter_region->get_kind ())
|
||
{
|
||
case RK_FIELD:
|
||
case RK_ELEMENT:
|
||
case RK_OFFSET:
|
||
case RK_BIT_RANGE:
|
||
if (accum_byte_sval)
|
||
{
|
||
const svalue *sval
|
||
= iter_region->get_relative_symbolic_offset (mgr);
|
||
accum_byte_sval
|
||
= mgr->get_or_create_binop (ptrdiff_type_node, PLUS_EXPR,
|
||
accum_byte_sval, sval);
|
||
iter_region = iter_region->get_parent_region ();
|
||
}
|
||
else
|
||
{
|
||
bit_offset_t rel_bit_offset;
|
||
if (iter_region->get_relative_concrete_offset (&rel_bit_offset))
|
||
{
|
||
accum_bit_offset += rel_bit_offset;
|
||
iter_region = iter_region->get_parent_region ();
|
||
}
|
||
else
|
||
{
|
||
/* If the iter_region is not concrete anymore, convert the
|
||
accumulated bits to a svalue in bytes and revisit the
|
||
iter_region collecting the symbolic value. */
|
||
byte_offset_t byte_offset = accum_bit_offset / BITS_PER_UNIT;
|
||
tree offset_tree = wide_int_to_tree (ptrdiff_type_node,
|
||
byte_offset);
|
||
accum_byte_sval
|
||
= mgr->get_or_create_constant_svalue (offset_tree);
|
||
}
|
||
}
|
||
continue;
|
||
case RK_SIZED:
|
||
case RK_CAST:
|
||
iter_region = iter_region->get_parent_region ();
|
||
continue;
|
||
|
||
default:
|
||
return accum_byte_sval
|
||
? region_offset::make_symbolic (iter_region,
|
||
accum_byte_sval)
|
||
: region_offset::make_concrete (iter_region,
|
||
accum_bit_offset);
|
||
}
|
||
}
|
||
|
||
return accum_byte_sval ? region_offset::make_symbolic (iter_region,
|
||
accum_byte_sval)
|
||
: region_offset::make_concrete (iter_region,
|
||
accum_bit_offset);
|
||
}
|
||
|
||
/* Base implementation of region::get_relative_concrete_offset vfunc. */
|
||
|
||
bool
|
||
region::get_relative_concrete_offset (bit_offset_t *) const
|
||
{
|
||
return false;
|
||
}
|
||
|
||
/* Base implementation of region::get_relative_symbolic_offset vfunc. */
|
||
|
||
const svalue *
|
||
region::get_relative_symbolic_offset (region_model_manager *mgr) const
|
||
{
|
||
return mgr->get_or_create_unknown_svalue (ptrdiff_type_node);
|
||
}
|
||
|
||
/* Attempt to get the position and size of this region expressed as a
|
||
concrete range of bytes relative to its parent.
|
||
If successful, return true and write to *OUT.
|
||
Otherwise return false. */
|
||
|
||
bool
|
||
region::get_relative_concrete_byte_range (byte_range *out) const
|
||
{
|
||
/* We must have a concrete offset relative to the parent. */
|
||
bit_offset_t rel_bit_offset;
|
||
if (!get_relative_concrete_offset (&rel_bit_offset))
|
||
return false;
|
||
/* ...which must be a whole number of bytes. */
|
||
if (rel_bit_offset % BITS_PER_UNIT != 0)
|
||
return false;
|
||
byte_offset_t start_byte_offset = rel_bit_offset / BITS_PER_UNIT;
|
||
|
||
/* We must have a concrete size, which must be a whole number
|
||
of bytes. */
|
||
byte_size_t num_bytes;
|
||
if (!get_byte_size (&num_bytes))
|
||
return false;
|
||
|
||
/* Success. */
|
||
*out = byte_range (start_byte_offset, num_bytes);
|
||
return true;
|
||
}
|
||
|
||
/* Dump a description of this region to stderr. */
|
||
|
||
DEBUG_FUNCTION void
|
||
region::dump (bool simple) const
|
||
{
|
||
tree_dump_pretty_printer pp (stderr);
|
||
dump_to_pp (&pp, simple);
|
||
pp_newline (&pp);
|
||
}
|
||
|
||
/* Dump a tree-like representation of this region and its constituent symbols
|
||
to stderr, using global_dc's colorization and theming options.
|
||
|
||
For example:
|
||
. (gdb) call reg->dump()
|
||
. (26): ‘int’: decl_region(‘x_10(D)’)
|
||
. ╰─ parent: (9): frame_region(‘test_bitmask_2’, index: 0, depth: 1)
|
||
. ╰─ parent: (1): stack region
|
||
. ╰─ parent: (0): root region
|
||
*/
|
||
|
||
DEBUG_FUNCTION void
|
||
region::dump () const
|
||
{
|
||
text_art::dump (*this);
|
||
}
|
||
|
||
/* Return a new json::string describing the region. */
|
||
|
||
std::unique_ptr<json::value>
|
||
region::to_json () const
|
||
{
|
||
label_text desc = get_desc (true);
|
||
auto reg_js = std::make_unique<json::string> (desc.get ());
|
||
return reg_js;
|
||
}
|
||
|
||
bool
|
||
region::maybe_print_for_user (pretty_printer *pp,
|
||
const region_model &) const
|
||
{
|
||
switch (get_kind ())
|
||
{
|
||
default:
|
||
break;
|
||
case RK_DECL:
|
||
{
|
||
const decl_region *reg = (const decl_region *)this;
|
||
tree decl = reg->get_decl ();
|
||
if (TREE_CODE (decl) == SSA_NAME)
|
||
decl = SSA_NAME_VAR (decl);
|
||
print_expr_for_user (pp, decl);
|
||
return true;
|
||
}
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/* Use DWI to create a text_art::widget describing this region in
|
||
a tree-like form, using PREFIX as a prefix (e.g. for field names). */
|
||
|
||
std::unique_ptr<text_art::tree_widget>
|
||
region::make_dump_widget (const text_art::dump_widget_info &dwi,
|
||
const char *prefix) const
|
||
{
|
||
pretty_printer pp;
|
||
pp_format_decoder (&pp) = default_tree_printer;
|
||
pp_show_color (&pp) = true;
|
||
|
||
if (prefix)
|
||
pp_printf (&pp, "%s: ", prefix);
|
||
|
||
pp_printf (&pp, "(%i): ", get_id ());
|
||
if (get_type ())
|
||
pp_printf (&pp, "%qT: ", get_type ());
|
||
|
||
print_dump_widget_label (&pp);
|
||
|
||
std::unique_ptr<text_art::tree_widget> w
|
||
(text_art::tree_widget::make (dwi, &pp));
|
||
|
||
add_dump_widget_children (*w, dwi);
|
||
|
||
if (m_parent)
|
||
w->add_child (m_parent->make_dump_widget (dwi, "parent"));
|
||
|
||
return w;
|
||
}
|
||
|
||
void
|
||
region::add_dump_widget_children (text_art::tree_widget &,
|
||
const text_art::dump_widget_info &) const
|
||
{
|
||
/* By default, add nothing (parent is added in make_dump_widget). */
|
||
}
|
||
|
||
/* Generate a description of this region. */
|
||
|
||
DEBUG_FUNCTION label_text
|
||
region::get_desc (bool simple) const
|
||
{
|
||
pretty_printer pp;
|
||
pp_format_decoder (&pp) = default_tree_printer;
|
||
dump_to_pp (&pp, simple);
|
||
return label_text::take (xstrdup (pp_formatted_text (&pp)));
|
||
}
|
||
|
||
/* Base implementation of region::accept vfunc.
|
||
Subclass implementations should chain up to this. */
|
||
|
||
void
|
||
region::accept (visitor *v) const
|
||
{
|
||
v->visit_region (this);
|
||
if (m_parent)
|
||
m_parent->accept (v);
|
||
}
|
||
|
||
/* Return true if this is a symbolic region for deferencing an
|
||
unknown ptr.
|
||
We shouldn't attempt to bind values for this region (but
|
||
can unbind values for other regions). */
|
||
|
||
bool
|
||
region::symbolic_for_unknown_ptr_p () const
|
||
{
|
||
if (const symbolic_region *sym_reg = dyn_cast_symbolic_region ())
|
||
if (sym_reg->get_pointer ()->get_kind () == SK_UNKNOWN)
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
/* Return true if this is a symbolic region. */
|
||
|
||
bool
|
||
region::symbolic_p () const
|
||
{
|
||
return get_kind () == RK_SYMBOLIC;
|
||
}
|
||
|
||
/* Return true if this region is known to be zero bits in size. */
|
||
|
||
bool
|
||
region::empty_p () const
|
||
{
|
||
bit_size_t num_bits;
|
||
if (get_bit_size (&num_bits))
|
||
if (num_bits == 0)
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
/* Return true if this is a region for a decl with name DECL_NAME.
|
||
Intended for use when debugging (for assertions and conditional
|
||
breakpoints). */
|
||
|
||
DEBUG_FUNCTION bool
|
||
region::is_named_decl_p (const char *decl_name) const
|
||
{
|
||
if (tree decl = maybe_get_decl ())
|
||
if (DECL_NAME (decl)
|
||
&& !strcmp (IDENTIFIER_POINTER (DECL_NAME (decl)), decl_name))
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
/* region's ctor. */
|
||
|
||
region::region (complexity c, symbol::id_t id, const region *parent, tree type)
|
||
: symbol (c, id),
|
||
m_parent (parent), m_type (type),
|
||
m_cached_offset (nullptr), m_cached_init_sval_at_main (nullptr)
|
||
{
|
||
gcc_assert (type == NULL_TREE || TYPE_P (type));
|
||
}
|
||
|
||
/* Comparator for use by vec<const region *>::qsort,
|
||
using their IDs to order them. */
|
||
|
||
int
|
||
region::cmp_ptr_ptr (const void *p1, const void *p2)
|
||
{
|
||
const region * const *reg1 = (const region * const *)p1;
|
||
const region * const *reg2 = (const region * const *)p2;
|
||
|
||
return cmp_ids (*reg1, *reg2);
|
||
}
|
||
|
||
/* Determine if a pointer to this region must be non-NULL.
|
||
|
||
Generally, pointers to regions must be non-NULL, but pointers
|
||
to symbolic_regions might, in fact, be NULL.
|
||
|
||
This allows us to simulate functions like malloc and calloc with:
|
||
- only one "outcome" from each statement,
|
||
- the idea that the pointer is on the heap if non-NULL
|
||
- the possibility that the pointer could be NULL
|
||
- the idea that successive values returned from malloc are non-equal
|
||
- to be able to zero-fill for calloc. */
|
||
|
||
bool
|
||
region::non_null_p () const
|
||
{
|
||
switch (get_kind ())
|
||
{
|
||
default:
|
||
return true;
|
||
case RK_SYMBOLIC:
|
||
/* Are we within a symbolic_region? If so, it could be NULL, and we
|
||
have to fall back on the constraints. */
|
||
return false;
|
||
case RK_HEAP_ALLOCATED:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/* Return true iff this region is defined in terms of SVAL. */
|
||
|
||
bool
|
||
region::involves_p (const svalue *sval) const
|
||
{
|
||
if (const symbolic_region *symbolic_reg = dyn_cast_symbolic_region ())
|
||
{
|
||
if (symbolic_reg->get_pointer ()->involves_p (sval))
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
/* Comparator for trees to impose a deterministic ordering on
|
||
T1 and T2. */
|
||
|
||
static int
|
||
tree_cmp (const_tree t1, const_tree t2)
|
||
{
|
||
gcc_assert (t1);
|
||
gcc_assert (t2);
|
||
|
||
/* Test tree codes first. */
|
||
if (TREE_CODE (t1) != TREE_CODE (t2))
|
||
return TREE_CODE (t1) - TREE_CODE (t2);
|
||
|
||
/* From this point on, we know T1 and T2 have the same tree code. */
|
||
|
||
if (DECL_P (t1))
|
||
{
|
||
if (DECL_NAME (t1) && DECL_NAME (t2))
|
||
return strcmp (IDENTIFIER_POINTER (DECL_NAME (t1)),
|
||
IDENTIFIER_POINTER (DECL_NAME (t2)));
|
||
else
|
||
{
|
||
if (DECL_NAME (t1))
|
||
return -1;
|
||
else if (DECL_NAME (t2))
|
||
return 1;
|
||
else
|
||
return DECL_UID (t1) - DECL_UID (t2);
|
||
}
|
||
}
|
||
|
||
switch (TREE_CODE (t1))
|
||
{
|
||
case SSA_NAME:
|
||
{
|
||
if (SSA_NAME_VAR (t1) && SSA_NAME_VAR (t2))
|
||
{
|
||
int var_cmp = tree_cmp (SSA_NAME_VAR (t1), SSA_NAME_VAR (t2));
|
||
if (var_cmp)
|
||
return var_cmp;
|
||
return SSA_NAME_VERSION (t1) - SSA_NAME_VERSION (t2);
|
||
}
|
||
else
|
||
{
|
||
if (SSA_NAME_VAR (t1))
|
||
return -1;
|
||
else if (SSA_NAME_VAR (t2))
|
||
return 1;
|
||
else
|
||
return SSA_NAME_VERSION (t1) - SSA_NAME_VERSION (t2);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case INTEGER_CST:
|
||
return tree_int_cst_compare (t1, t2);
|
||
|
||
case REAL_CST:
|
||
{
|
||
const real_value *rv1 = TREE_REAL_CST_PTR (t1);
|
||
const real_value *rv2 = TREE_REAL_CST_PTR (t2);
|
||
if (real_compare (UNORDERED_EXPR, rv1, rv2))
|
||
{
|
||
/* Impose an arbitrary order on NaNs relative to other NaNs
|
||
and to non-NaNs. */
|
||
if (int cmp_isnan = real_isnan (rv1) - real_isnan (rv2))
|
||
return cmp_isnan;
|
||
if (int cmp_issignaling_nan
|
||
= real_issignaling_nan (rv1) - real_issignaling_nan (rv2))
|
||
return cmp_issignaling_nan;
|
||
return real_isneg (rv1) - real_isneg (rv2);
|
||
}
|
||
if (real_compare (LT_EXPR, rv1, rv2))
|
||
return -1;
|
||
if (real_compare (GT_EXPR, rv1, rv2))
|
||
return 1;
|
||
return 0;
|
||
}
|
||
|
||
case STRING_CST:
|
||
return strcmp (TREE_STRING_POINTER (t1),
|
||
TREE_STRING_POINTER (t2));
|
||
|
||
default:
|
||
gcc_unreachable ();
|
||
break;
|
||
}
|
||
|
||
gcc_unreachable ();
|
||
|
||
return 0;
|
||
}
|
||
|
||
/* qsort comparator for trees to impose a deterministic ordering on
|
||
P1 and P2. */
|
||
|
||
int
|
||
tree_cmp (const void *p1, const void *p2)
|
||
{
|
||
const_tree t1 = *(const_tree const *)p1;
|
||
const_tree t2 = *(const_tree const *)p2;
|
||
|
||
return tree_cmp (t1, t2);
|
||
}
|
||
|
||
/* class frame_region : public space_region. */
|
||
|
||
frame_region::~frame_region ()
|
||
{
|
||
for (map_t::iterator iter = m_locals.begin ();
|
||
iter != m_locals.end ();
|
||
++iter)
|
||
delete (*iter).second;
|
||
}
|
||
|
||
void
|
||
frame_region::accept (visitor *v) const
|
||
{
|
||
region::accept (v);
|
||
if (m_calling_frame)
|
||
m_calling_frame->accept (v);
|
||
}
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for frame_region. */
|
||
|
||
void
|
||
frame_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_printf (pp, "frame: %qs@%i", function_name (&m_fun), get_stack_depth ());
|
||
else
|
||
pp_printf (pp, "frame_region(%qs, index: %i, depth: %i)",
|
||
function_name (&m_fun), m_index, get_stack_depth ());
|
||
}
|
||
|
||
void
|
||
frame_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "frame_region(%qs, index: %i, depth: %i)",
|
||
function_name (&m_fun), m_index, get_stack_depth ());
|
||
}
|
||
|
||
const decl_region *
|
||
frame_region::get_region_for_local (region_model_manager *mgr,
|
||
tree expr,
|
||
const region_model_context *ctxt) const
|
||
{
|
||
if (CHECKING_P)
|
||
{
|
||
/* Verify that EXPR is a local or SSA name, and that it's for the
|
||
correct function for this stack frame. */
|
||
gcc_assert (TREE_CODE (expr) == PARM_DECL
|
||
|| TREE_CODE (expr) == VAR_DECL
|
||
|| TREE_CODE (expr) == SSA_NAME
|
||
|| TREE_CODE (expr) == RESULT_DECL);
|
||
switch (TREE_CODE (expr))
|
||
{
|
||
default:
|
||
gcc_unreachable ();
|
||
case VAR_DECL:
|
||
gcc_assert (!is_global_var (expr));
|
||
/* Fall through. */
|
||
case PARM_DECL:
|
||
case RESULT_DECL:
|
||
gcc_assert (DECL_CONTEXT (expr) == m_fun.decl);
|
||
break;
|
||
case SSA_NAME:
|
||
{
|
||
if (tree var = SSA_NAME_VAR (expr))
|
||
{
|
||
if (DECL_P (var))
|
||
gcc_assert (DECL_CONTEXT (var) == m_fun.decl);
|
||
}
|
||
else if (ctxt)
|
||
if (const extrinsic_state *ext_state = ctxt->get_ext_state ())
|
||
if (const supergraph *sg
|
||
= ext_state->get_engine ()->get_supergraph ())
|
||
{
|
||
const gimple *def_stmt = SSA_NAME_DEF_STMT (expr);
|
||
if (gimple_code (def_stmt) != GIMPLE_PHI)
|
||
{
|
||
const supernode *snode
|
||
= sg->get_supernode_for_stmt (def_stmt);
|
||
gcc_assert (snode->get_function () == &m_fun);
|
||
}
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Ideally we'd use mutable here. */
|
||
map_t &mutable_locals = const_cast <map_t &> (m_locals);
|
||
|
||
if (decl_region **slot = mutable_locals.get (expr))
|
||
return *slot;
|
||
decl_region *reg
|
||
= new decl_region (mgr->alloc_symbol_id (), this, expr);
|
||
mutable_locals.put (expr, reg);
|
||
return reg;
|
||
}
|
||
|
||
/* class globals_region : public space_region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for globals_region. */
|
||
|
||
void
|
||
globals_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_string (pp, "::");
|
||
else
|
||
pp_string (pp, "globals");
|
||
}
|
||
|
||
void
|
||
globals_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_string (pp, "globals");
|
||
}
|
||
|
||
/* class code_region : public map_region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for code_region. */
|
||
|
||
void
|
||
code_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_string (pp, "code region");
|
||
else
|
||
pp_string (pp, "code_region()");
|
||
}
|
||
|
||
void
|
||
code_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_string (pp, "code region");
|
||
}
|
||
|
||
/* class function_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for function_region. */
|
||
|
||
void
|
||
function_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
{
|
||
dump_quoted_tree (pp, m_fndecl);
|
||
}
|
||
else
|
||
{
|
||
pp_string (pp, "function_region(");
|
||
dump_quoted_tree (pp, m_fndecl);
|
||
pp_string (pp, ")");
|
||
}
|
||
}
|
||
|
||
void
|
||
function_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_string (pp, "function_region(");
|
||
dump_quoted_tree (pp, m_fndecl);
|
||
pp_string (pp, ")");
|
||
}
|
||
|
||
/* class label_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for label_region. */
|
||
|
||
void
|
||
label_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
{
|
||
dump_quoted_tree (pp, m_label);
|
||
}
|
||
else
|
||
{
|
||
pp_string (pp, "label_region(");
|
||
dump_quoted_tree (pp, m_label);
|
||
pp_string (pp, ")");
|
||
}
|
||
}
|
||
|
||
void
|
||
label_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_string (pp, "label_region(");
|
||
dump_quoted_tree (pp, m_label);
|
||
pp_string (pp, ")");
|
||
}
|
||
|
||
/* class stack_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for stack_region. */
|
||
|
||
void
|
||
stack_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_string (pp, "stack region");
|
||
else
|
||
pp_string (pp, "stack_region()");
|
||
}
|
||
|
||
void
|
||
stack_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_string (pp, "stack region");
|
||
}
|
||
|
||
/* class heap_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for heap_region. */
|
||
|
||
void
|
||
heap_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_string (pp, "heap region");
|
||
else
|
||
pp_string (pp, "heap_region()");
|
||
}
|
||
|
||
void
|
||
heap_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_string (pp, "heap_region");
|
||
}
|
||
|
||
/* class root_region : public region. */
|
||
|
||
/* root_region's ctor. */
|
||
|
||
root_region::root_region (symbol::id_t id)
|
||
: region (complexity (1, 1), id, nullptr, NULL_TREE)
|
||
{
|
||
}
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for root_region. */
|
||
|
||
void
|
||
root_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_string (pp, "root region");
|
||
else
|
||
pp_string (pp, "root_region()");
|
||
}
|
||
|
||
void
|
||
root_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_string (pp, "root region");
|
||
}
|
||
|
||
/* class thread_local_region : public space_region. */
|
||
|
||
void
|
||
thread_local_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_string (pp, "thread_local_region");
|
||
else
|
||
pp_string (pp, "thread_local_region()");
|
||
}
|
||
|
||
void
|
||
thread_local_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_string (pp, "thread_local_region");
|
||
}
|
||
|
||
/* class symbolic_region : public map_region. */
|
||
|
||
/* symbolic_region's ctor. */
|
||
|
||
symbolic_region::symbolic_region (symbol::id_t id, region *parent,
|
||
const svalue *sval_ptr)
|
||
: region (complexity::from_pair (parent, sval_ptr), id, parent,
|
||
(sval_ptr->get_type ()
|
||
? TREE_TYPE (sval_ptr->get_type ())
|
||
: NULL_TREE)),
|
||
m_sval_ptr (sval_ptr)
|
||
{
|
||
}
|
||
|
||
/* Implementation of region::accept vfunc for symbolic_region. */
|
||
|
||
void
|
||
symbolic_region::accept (visitor *v) const
|
||
{
|
||
region::accept (v);
|
||
m_sval_ptr->accept (v);
|
||
}
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for symbolic_region. */
|
||
|
||
void
|
||
symbolic_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
{
|
||
pp_string (pp, "(*");
|
||
m_sval_ptr->dump_to_pp (pp, simple);
|
||
pp_string (pp, ")");
|
||
}
|
||
else
|
||
{
|
||
pp_string (pp, "symbolic_region(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
if (get_type ())
|
||
{
|
||
pp_string (pp, ", ");
|
||
print_quoted_type (pp, get_type ());
|
||
}
|
||
pp_string (pp, ", ");
|
||
m_sval_ptr->dump_to_pp (pp, simple);
|
||
pp_string (pp, ")");
|
||
}
|
||
}
|
||
|
||
void
|
||
symbolic_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_string (pp, "symbolic_region: %<*%>");
|
||
}
|
||
|
||
void
|
||
symbolic_region::
|
||
add_dump_widget_children (text_art::tree_widget &w,
|
||
const text_art::dump_widget_info &dwi) const
|
||
{
|
||
w.add_child (m_sval_ptr->make_dump_widget (dwi, "m_sval_ptr"));
|
||
}
|
||
|
||
/* class decl_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for decl_region. */
|
||
|
||
void
|
||
decl_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_printf (pp, "%E", m_decl);
|
||
else
|
||
{
|
||
pp_string (pp, "decl_region(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, ", ");
|
||
print_quoted_type (pp, get_type ());
|
||
pp_printf (pp, ", %qE)", m_decl);
|
||
}
|
||
}
|
||
|
||
void
|
||
decl_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "decl_region(%qE)", m_decl);
|
||
}
|
||
|
||
/* Get the stack depth for the frame containing this decl, or 0
|
||
for a global. */
|
||
|
||
int
|
||
decl_region::get_stack_depth () const
|
||
{
|
||
if (get_parent_region () == nullptr)
|
||
return 0;
|
||
if (const frame_region *frame_reg
|
||
= get_parent_region ()->dyn_cast_frame_region ())
|
||
return frame_reg->get_stack_depth ();
|
||
return 0;
|
||
}
|
||
|
||
/* If the underlying decl is in the global constant pool,
|
||
return an svalue representing the constant value.
|
||
Otherwise return nullptr. */
|
||
|
||
const svalue *
|
||
decl_region::maybe_get_constant_value (region_model_manager *mgr) const
|
||
{
|
||
if (VAR_P (m_decl)
|
||
&& DECL_IN_CONSTANT_POOL (m_decl)
|
||
&& DECL_INITIAL (m_decl)
|
||
&& TREE_CODE (DECL_INITIAL (m_decl)) == CONSTRUCTOR)
|
||
return get_svalue_for_constructor (DECL_INITIAL (m_decl), mgr);
|
||
return nullptr;
|
||
}
|
||
|
||
/* Implementation of decl_region::get_svalue_for_constructor
|
||
for when the cached value hasn't yet been calculated. */
|
||
|
||
const svalue *
|
||
decl_region::calc_svalue_for_constructor (tree ctor,
|
||
region_model_manager *mgr) const
|
||
{
|
||
/* Create a binding map, applying ctor to it, using this
|
||
decl_region as the base region when building child regions
|
||
for offset calculations. */
|
||
binding_map map (*mgr->get_store_manager ());
|
||
if (!map.apply_ctor_to_region (this, ctor, mgr))
|
||
return mgr->get_or_create_unknown_svalue (get_type ());
|
||
|
||
/* Return a compound svalue for the map we built. */
|
||
return mgr->get_or_create_compound_svalue (get_type (), map);
|
||
}
|
||
|
||
/* Get an svalue for CTOR, a CONSTRUCTOR for this region's decl. */
|
||
|
||
const svalue *
|
||
decl_region::get_svalue_for_constructor (tree ctor,
|
||
region_model_manager *mgr) const
|
||
{
|
||
gcc_assert (!TREE_CLOBBER_P (ctor));
|
||
gcc_assert (ctor == DECL_INITIAL (m_decl));
|
||
|
||
if (!m_ctor_svalue)
|
||
m_ctor_svalue = calc_svalue_for_constructor (ctor, mgr);
|
||
|
||
return m_ctor_svalue;
|
||
}
|
||
|
||
/* For use on decl_regions for global variables.
|
||
|
||
Get an svalue for the initial value of this region at entry to
|
||
"main" (either based on DECL_INITIAL, or implicit initialization to
|
||
zero.
|
||
|
||
Return nullptr if there is a problem. */
|
||
|
||
const svalue *
|
||
decl_region::get_svalue_for_initializer (region_model_manager *mgr) const
|
||
{
|
||
tree init = DECL_INITIAL (m_decl);
|
||
if (!init)
|
||
{
|
||
/* If we have an "extern" decl then there may be an initializer in
|
||
another TU. */
|
||
if (DECL_EXTERNAL (m_decl))
|
||
return nullptr;
|
||
|
||
if (empty_p ())
|
||
return nullptr;
|
||
|
||
/* Implicit initialization to zero; use a compound_svalue for it.
|
||
Doing so requires that we have a concrete binding for this region,
|
||
which can fail if we have a region with unknown size
|
||
(e.g. "extern const char arr[];"). */
|
||
const binding_key *binding
|
||
= binding_key::make (mgr->get_store_manager (), this);
|
||
if (binding->symbolic_p ())
|
||
return nullptr;
|
||
|
||
/* If we don't care about tracking the content of this region, then
|
||
it's unused, and the value doesn't matter. */
|
||
if (!tracked_p ())
|
||
return nullptr;
|
||
|
||
binding_cluster c (*mgr->get_store_manager (), this);
|
||
c.zero_fill_region (mgr->get_store_manager (), this);
|
||
return mgr->get_or_create_compound_svalue (TREE_TYPE (m_decl),
|
||
c.get_map ());
|
||
}
|
||
|
||
/* LTO can write out error_mark_node as the DECL_INITIAL for simple scalar
|
||
values (to avoid writing out an extra section). */
|
||
if (init == error_mark_node)
|
||
return nullptr;
|
||
|
||
if (TREE_CODE (init) == CONSTRUCTOR)
|
||
return get_svalue_for_constructor (init, mgr);
|
||
|
||
/* Reuse the get_rvalue logic from region_model. */
|
||
region_model m (mgr);
|
||
return m.get_rvalue (path_var (init, 0), nullptr);
|
||
}
|
||
|
||
/* Subroutine of symnode_requires_tracking_p; return true if REF
|
||
might imply that we should be tracking the value of its decl. */
|
||
|
||
static bool
|
||
ipa_ref_requires_tracking (ipa_ref *ref)
|
||
{
|
||
/* If we have a load/store/alias of the symbol, then we'll track
|
||
the decl's value. */
|
||
if (ref->use != IPA_REF_ADDR)
|
||
return true;
|
||
|
||
if (ref->stmt == nullptr)
|
||
return true;
|
||
|
||
switch (ref->stmt->code)
|
||
{
|
||
default:
|
||
return true;
|
||
case GIMPLE_CALL:
|
||
{
|
||
cgraph_node *caller_cnode = dyn_cast <cgraph_node *> (ref->referring);
|
||
if (caller_cnode == nullptr)
|
||
return true;
|
||
cgraph_edge *edge = caller_cnode->get_edge (ref->stmt);
|
||
if (!edge)
|
||
return true;
|
||
if (edge->callee == nullptr)
|
||
return true; /* e.g. call through function ptr. */
|
||
if (edge->callee->definition)
|
||
return true;
|
||
/* If we get here, then this ref is a pointer passed to
|
||
a function we don't have the definition for. */
|
||
return false;
|
||
}
|
||
break;
|
||
case GIMPLE_ASM:
|
||
{
|
||
const gasm *asm_stmt = as_a <const gasm *> (ref->stmt);
|
||
if (gimple_asm_noutputs (asm_stmt) > 0)
|
||
return true;
|
||
if (gimple_asm_nclobbers (asm_stmt) > 0)
|
||
return true;
|
||
/* If we get here, then this ref is the decl being passed
|
||
by pointer to asm with no outputs. */
|
||
return false;
|
||
}
|
||
break;
|
||
}
|
||
}
|
||
|
||
/* Determine if the decl for SYMNODE should have binding_clusters
|
||
in our state objects; return false to optimize away tracking
|
||
certain decls in our state objects, as an optimization. */
|
||
|
||
static bool
|
||
symnode_requires_tracking_p (symtab_node *symnode)
|
||
{
|
||
gcc_assert (symnode);
|
||
if (symnode->externally_visible)
|
||
return true;
|
||
tree context_fndecl = DECL_CONTEXT (symnode->decl);
|
||
if (context_fndecl == nullptr)
|
||
return true;
|
||
if (TREE_CODE (context_fndecl) != FUNCTION_DECL)
|
||
return true;
|
||
for (auto ref : symnode->ref_list.referring)
|
||
if (ipa_ref_requires_tracking (ref))
|
||
return true;
|
||
|
||
/* If we get here, then we don't have uses of this decl that require
|
||
tracking; we never read from it or write to it explicitly. */
|
||
return false;
|
||
}
|
||
|
||
/* Subroutine of decl_region ctor: determine whether this decl_region
|
||
can have binding_clusters; return false to optimize away tracking
|
||
of certain decls in our state objects, as an optimization. */
|
||
|
||
bool
|
||
decl_region::calc_tracked_p (tree decl)
|
||
{
|
||
/* Precondition of symtab_node::get. */
|
||
if (TREE_CODE (decl) == VAR_DECL
|
||
&& (TREE_STATIC (decl) || DECL_EXTERNAL (decl) || in_lto_p))
|
||
if (symtab_node *symnode = symtab_node::get (decl))
|
||
return symnode_requires_tracking_p (symnode);
|
||
return true;
|
||
}
|
||
|
||
/* class field_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for field_region. */
|
||
|
||
void
|
||
field_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
{
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, ".");
|
||
pp_printf (pp, "%E", m_field);
|
||
}
|
||
else
|
||
{
|
||
pp_string (pp, "field_region(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, ", ");
|
||
print_quoted_type (pp, get_type ());
|
||
pp_printf (pp, ", %qE)", m_field);
|
||
}
|
||
}
|
||
|
||
void
|
||
field_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "field_region(%qE)", m_field);
|
||
}
|
||
|
||
/* Implementation of region::get_relative_concrete_offset vfunc
|
||
for field_region. */
|
||
|
||
bool
|
||
field_region::get_relative_concrete_offset (bit_offset_t *out) const
|
||
{
|
||
/* Compare with e.g. gimple-fold.cc's
|
||
fold_nonarray_ctor_reference. */
|
||
tree byte_offset = DECL_FIELD_OFFSET (m_field);
|
||
if (TREE_CODE (byte_offset) != INTEGER_CST)
|
||
return false;
|
||
tree field_offset = DECL_FIELD_BIT_OFFSET (m_field);
|
||
/* Compute bit offset of the field. */
|
||
offset_int bitoffset
|
||
= (wi::to_offset (field_offset)
|
||
+ (wi::to_offset (byte_offset) << LOG2_BITS_PER_UNIT));
|
||
*out = bitoffset;
|
||
return true;
|
||
}
|
||
|
||
|
||
/* Implementation of region::get_relative_symbolic_offset vfunc
|
||
for field_region.
|
||
If known, the returned svalue is equal to the offset converted to bytes and
|
||
rounded off. */
|
||
|
||
const svalue *
|
||
field_region::get_relative_symbolic_offset (region_model_manager *mgr) const
|
||
{
|
||
bit_offset_t out;
|
||
if (get_relative_concrete_offset (&out))
|
||
{
|
||
tree cst_tree
|
||
= wide_int_to_tree (ptrdiff_type_node, out / BITS_PER_UNIT);
|
||
return mgr->get_or_create_constant_svalue (cst_tree);
|
||
}
|
||
return mgr->get_or_create_unknown_svalue (ptrdiff_type_node);
|
||
}
|
||
|
||
/* class element_region : public region. */
|
||
|
||
/* Implementation of region::accept vfunc for element_region. */
|
||
|
||
void
|
||
element_region::accept (visitor *v) const
|
||
{
|
||
region::accept (v);
|
||
m_index->accept (v);
|
||
}
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for element_region. */
|
||
|
||
void
|
||
element_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
{
|
||
//pp_string (pp, "(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, "[");
|
||
m_index->dump_to_pp (pp, simple);
|
||
pp_string (pp, "]");
|
||
//pp_string (pp, ")");
|
||
}
|
||
else
|
||
{
|
||
pp_string (pp, "element_region(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, ", ");
|
||
print_quoted_type (pp, get_type ());
|
||
pp_string (pp, ", ");
|
||
m_index->dump_to_pp (pp, simple);
|
||
pp_printf (pp, ")");
|
||
}
|
||
}
|
||
|
||
void
|
||
element_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "element_region: %<[]%>");
|
||
}
|
||
|
||
void
|
||
element_region::
|
||
add_dump_widget_children (text_art::tree_widget &w,
|
||
const text_art::dump_widget_info &dwi) const
|
||
{
|
||
w.add_child (m_index->make_dump_widget (dwi, "m_index"));
|
||
}
|
||
|
||
/* Implementation of region::get_relative_concrete_offset vfunc
|
||
for element_region. */
|
||
|
||
bool
|
||
element_region::get_relative_concrete_offset (bit_offset_t *out) const
|
||
{
|
||
if (tree idx_cst = m_index->maybe_get_constant ())
|
||
{
|
||
gcc_assert (TREE_CODE (idx_cst) == INTEGER_CST);
|
||
|
||
tree elem_type = get_type ();
|
||
offset_int element_idx = wi::to_offset (idx_cst);
|
||
|
||
/* First, use int_size_in_bytes, to reject the case where we
|
||
have an incomplete type, or a non-constant value. */
|
||
HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (elem_type);
|
||
if (hwi_byte_size > 0)
|
||
{
|
||
offset_int element_bit_size
|
||
= hwi_byte_size << LOG2_BITS_PER_UNIT;
|
||
offset_int element_bit_offset
|
||
= element_idx * element_bit_size;
|
||
*out = element_bit_offset;
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/* Implementation of region::get_relative_symbolic_offset vfunc
|
||
for element_region. */
|
||
|
||
const svalue *
|
||
element_region::get_relative_symbolic_offset (region_model_manager *mgr) const
|
||
{
|
||
tree elem_type = get_type ();
|
||
|
||
/* First, use int_size_in_bytes, to reject the case where we
|
||
have an incomplete type, or a non-constant value. */
|
||
HOST_WIDE_INT hwi_byte_size = int_size_in_bytes (elem_type);
|
||
if (hwi_byte_size > 0)
|
||
{
|
||
tree byte_size_tree = wide_int_to_tree (ptrdiff_type_node,
|
||
hwi_byte_size);
|
||
const svalue *byte_size_sval
|
||
= mgr->get_or_create_constant_svalue (byte_size_tree);
|
||
return mgr->get_or_create_binop (NULL_TREE, MULT_EXPR,
|
||
m_index, byte_size_sval);
|
||
}
|
||
return mgr->get_or_create_unknown_svalue (ptrdiff_type_node);
|
||
}
|
||
|
||
/* class offset_region : public region. */
|
||
|
||
/* Implementation of region::accept vfunc for offset_region. */
|
||
|
||
void
|
||
offset_region::accept (visitor *v) const
|
||
{
|
||
region::accept (v);
|
||
m_byte_offset->accept (v);
|
||
}
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for offset_region. */
|
||
|
||
void
|
||
offset_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
{
|
||
//pp_string (pp, "(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, "+");
|
||
m_byte_offset->dump_to_pp (pp, simple);
|
||
//pp_string (pp, ")");
|
||
}
|
||
else
|
||
{
|
||
pp_string (pp, "offset_region(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, ", ");
|
||
print_quoted_type (pp, get_type ());
|
||
pp_string (pp, ", ");
|
||
m_byte_offset->dump_to_pp (pp, simple);
|
||
pp_printf (pp, ")");
|
||
}
|
||
}
|
||
|
||
void
|
||
offset_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "offset_region");
|
||
}
|
||
|
||
void
|
||
offset_region::
|
||
add_dump_widget_children (text_art::tree_widget &w,
|
||
const text_art::dump_widget_info &dwi) const
|
||
{
|
||
w.add_child (m_byte_offset->make_dump_widget (dwi, "m_byte_offset"));
|
||
}
|
||
|
||
const svalue *
|
||
offset_region::get_bit_offset (region_model_manager *mgr) const
|
||
{
|
||
const svalue *bits_per_byte_sval
|
||
= mgr->get_or_create_int_cst (NULL_TREE, BITS_PER_UNIT);
|
||
return mgr->get_or_create_binop (NULL_TREE, MULT_EXPR,
|
||
m_byte_offset, bits_per_byte_sval);
|
||
}
|
||
|
||
/* Implementation of region::get_relative_concrete_offset vfunc
|
||
for offset_region. */
|
||
|
||
bool
|
||
offset_region::get_relative_concrete_offset (bit_offset_t *out) const
|
||
{
|
||
if (tree byte_offset_cst = m_byte_offset->maybe_get_constant ())
|
||
{
|
||
gcc_assert (TREE_CODE (byte_offset_cst) == INTEGER_CST);
|
||
/* Use a signed value for the byte offset, to handle
|
||
negative offsets. */
|
||
HOST_WIDE_INT byte_offset
|
||
= wi::to_offset (byte_offset_cst).to_shwi ();
|
||
HOST_WIDE_INT bit_offset = byte_offset * BITS_PER_UNIT;
|
||
*out = bit_offset;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/* Implementation of region::get_relative_symbolic_offset vfunc
|
||
for offset_region. */
|
||
|
||
const svalue *
|
||
offset_region::get_relative_symbolic_offset (region_model_manager *mgr
|
||
ATTRIBUTE_UNUSED) const
|
||
{
|
||
return get_byte_offset ();
|
||
}
|
||
|
||
/* class sized_region : public region. */
|
||
|
||
/* Implementation of region::accept vfunc for sized_region. */
|
||
|
||
void
|
||
sized_region::accept (visitor *v) const
|
||
{
|
||
region::accept (v);
|
||
m_byte_size_sval->accept (v);
|
||
}
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for sized_region. */
|
||
|
||
void
|
||
sized_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
{
|
||
pp_string (pp, "SIZED_REG(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, ", ");
|
||
m_byte_size_sval->dump_to_pp (pp, simple);
|
||
pp_string (pp, ")");
|
||
}
|
||
else
|
||
{
|
||
pp_string (pp, "sized_region(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, ", ");
|
||
m_byte_size_sval->dump_to_pp (pp, simple);
|
||
pp_printf (pp, ")");
|
||
}
|
||
}
|
||
|
||
void
|
||
sized_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "sized_region");
|
||
}
|
||
|
||
void
|
||
sized_region::
|
||
add_dump_widget_children (text_art::tree_widget &w,
|
||
const text_art::dump_widget_info &dwi) const
|
||
{
|
||
w.add_child (m_byte_size_sval->make_dump_widget (dwi, "m_byte_size_sval"));
|
||
}
|
||
|
||
/* Implementation of region::get_byte_size vfunc for sized_region. */
|
||
|
||
bool
|
||
sized_region::get_byte_size (byte_size_t *out) const
|
||
{
|
||
if (tree cst = m_byte_size_sval->maybe_get_constant ())
|
||
{
|
||
gcc_assert (TREE_CODE (cst) == INTEGER_CST);
|
||
*out = tree_to_uhwi (cst);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/* Implementation of region::get_bit_size vfunc for sized_region. */
|
||
|
||
bool
|
||
sized_region::get_bit_size (bit_size_t *out) const
|
||
{
|
||
byte_size_t byte_size;
|
||
if (!get_byte_size (&byte_size))
|
||
return false;
|
||
*out = byte_size * BITS_PER_UNIT;
|
||
return true;
|
||
}
|
||
|
||
/* Implementation of region::get_bit_size_sval vfunc for sized_region. */
|
||
|
||
const svalue *
|
||
sized_region::get_bit_size_sval (region_model_manager *mgr) const
|
||
{
|
||
const svalue *bits_per_byte_sval
|
||
= mgr->get_or_create_int_cst (NULL_TREE, BITS_PER_UNIT);
|
||
return mgr->get_or_create_binop (NULL_TREE, MULT_EXPR,
|
||
m_byte_size_sval, bits_per_byte_sval);
|
||
}
|
||
|
||
/* class cast_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for cast_region. */
|
||
|
||
void
|
||
cast_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
{
|
||
pp_string (pp, "CAST_REG(");
|
||
print_quoted_type (pp, get_type ());
|
||
pp_string (pp, ", ");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, ")");
|
||
}
|
||
else
|
||
{
|
||
pp_string (pp, "cast_region(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, ", ");
|
||
print_quoted_type (pp, get_type ());
|
||
pp_printf (pp, ")");
|
||
}
|
||
}
|
||
|
||
void
|
||
cast_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "cast_region");
|
||
}
|
||
|
||
/* Implementation of region::get_relative_concrete_offset vfunc
|
||
for cast_region. */
|
||
|
||
bool
|
||
cast_region::get_relative_concrete_offset (bit_offset_t *out) const
|
||
{
|
||
*out = (int) 0;
|
||
return true;
|
||
}
|
||
|
||
/* class heap_allocated_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for heap_allocated_region. */
|
||
|
||
void
|
||
heap_allocated_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_printf (pp, "HEAP_ALLOCATED_REGION(%i)", get_id ());
|
||
else
|
||
pp_printf (pp, "heap_allocated_region(%i)", get_id ());
|
||
}
|
||
|
||
void
|
||
heap_allocated_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "heap_allocated_region");
|
||
}
|
||
|
||
/* class alloca_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for alloca_region. */
|
||
|
||
void
|
||
alloca_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_printf (pp, "ALLOCA_REGION(%i)", get_id ());
|
||
else
|
||
pp_printf (pp, "alloca_region(%i)", get_id ());
|
||
}
|
||
|
||
void
|
||
alloca_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "alloca_region");
|
||
}
|
||
|
||
/* class string_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for string_region. */
|
||
|
||
void
|
||
string_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
dump_tree (pp, m_string_cst);
|
||
else
|
||
{
|
||
pp_string (pp, "string_region(");
|
||
dump_tree (pp, m_string_cst);
|
||
if (!flag_dump_noaddr)
|
||
{
|
||
pp_string (pp, " (");
|
||
pp_pointer (pp, m_string_cst);
|
||
pp_string (pp, "))");
|
||
}
|
||
}
|
||
}
|
||
|
||
void
|
||
string_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_string (pp, "string_region(");
|
||
dump_tree (pp, m_string_cst);
|
||
pp_string (pp, ")");
|
||
}
|
||
|
||
/* class bit_range_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for bit_range_region. */
|
||
|
||
void
|
||
bit_range_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
{
|
||
pp_string (pp, "BIT_RANGE_REG(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, ", ");
|
||
m_bits.dump_to_pp (pp);
|
||
pp_string (pp, ")");
|
||
}
|
||
else
|
||
{
|
||
pp_string (pp, "bit_range_region(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_string (pp, ", ");
|
||
m_bits.dump_to_pp (pp);
|
||
pp_printf (pp, ")");
|
||
}
|
||
}
|
||
|
||
void
|
||
bit_range_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "bit_range_region(m_bits: ");
|
||
m_bits.dump_to_pp (pp);
|
||
pp_string (pp, ")");
|
||
}
|
||
|
||
/* Implementation of region::get_byte_size vfunc for bit_range_region. */
|
||
|
||
bool
|
||
bit_range_region::get_byte_size (byte_size_t *out) const
|
||
{
|
||
if (m_bits.m_size_in_bits % BITS_PER_UNIT == 0)
|
||
{
|
||
*out = m_bits.m_size_in_bits / BITS_PER_UNIT;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/* Implementation of region::get_bit_size vfunc for bit_range_region. */
|
||
|
||
bool
|
||
bit_range_region::get_bit_size (bit_size_t *out) const
|
||
{
|
||
*out = m_bits.m_size_in_bits;
|
||
return true;
|
||
}
|
||
|
||
/* Implementation of region::get_byte_size_sval vfunc for bit_range_region. */
|
||
|
||
const svalue *
|
||
bit_range_region::get_byte_size_sval (region_model_manager *mgr) const
|
||
{
|
||
if (m_bits.m_size_in_bits % BITS_PER_UNIT != 0)
|
||
return mgr->get_or_create_unknown_svalue (size_type_node);
|
||
|
||
HOST_WIDE_INT num_bytes = m_bits.m_size_in_bits.to_shwi () / BITS_PER_UNIT;
|
||
return mgr->get_or_create_int_cst (size_type_node, num_bytes);
|
||
}
|
||
|
||
/* Implementation of region::get_bit_size_sval vfunc for bit_range_region. */
|
||
|
||
const svalue *
|
||
bit_range_region::get_bit_size_sval (region_model_manager *mgr) const
|
||
{
|
||
return mgr->get_or_create_int_cst (size_type_node,
|
||
m_bits.m_size_in_bits);
|
||
}
|
||
|
||
/* Implementation of region::get_relative_concrete_offset vfunc for
|
||
bit_range_region. */
|
||
|
||
bool
|
||
bit_range_region::get_relative_concrete_offset (bit_offset_t *out) const
|
||
{
|
||
*out = m_bits.get_start_bit_offset ();
|
||
return true;
|
||
}
|
||
|
||
/* Implementation of region::get_relative_symbolic_offset vfunc for
|
||
bit_range_region.
|
||
The returned svalue is equal to the offset converted to bytes and
|
||
rounded off. */
|
||
|
||
const svalue *
|
||
bit_range_region::get_relative_symbolic_offset (region_model_manager *mgr)
|
||
const
|
||
{
|
||
byte_offset_t start_byte = m_bits.get_start_bit_offset () / BITS_PER_UNIT;
|
||
tree start_bit_tree = wide_int_to_tree (ptrdiff_type_node, start_byte);
|
||
return mgr->get_or_create_constant_svalue (start_bit_tree);
|
||
}
|
||
|
||
/* class var_arg_region : public region. */
|
||
|
||
void
|
||
var_arg_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
{
|
||
pp_string (pp, "VAR_ARG_REG(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_printf (pp, ", arg_idx: %d)", m_idx);
|
||
}
|
||
else
|
||
{
|
||
pp_string (pp, "var_arg_region(");
|
||
get_parent_region ()->dump_to_pp (pp, simple);
|
||
pp_printf (pp, ", arg_idx: %d)", m_idx);
|
||
}
|
||
}
|
||
|
||
void
|
||
var_arg_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "var_arg_region(arg_idx: %i)", m_idx);
|
||
}
|
||
|
||
/* Get the frame_region for this var_arg_region. */
|
||
|
||
const frame_region *
|
||
var_arg_region::get_frame_region () const
|
||
{
|
||
gcc_assert (get_parent_region ());
|
||
return as_a <const frame_region *> (get_parent_region ());
|
||
}
|
||
|
||
/* class errno_region : public region. */
|
||
|
||
void
|
||
errno_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_string (pp, "errno_region");
|
||
else
|
||
pp_string (pp, "errno_region()");
|
||
}
|
||
|
||
void
|
||
errno_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "errno_region");
|
||
}
|
||
|
||
/* class private_region : public region. */
|
||
|
||
void
|
||
private_region::dump_to_pp (pretty_printer *pp, bool simple) const
|
||
{
|
||
if (simple)
|
||
pp_printf (pp, "PRIVATE_REG(%qs)", m_desc);
|
||
else
|
||
pp_printf (pp, "private_region(%qs)", m_desc);
|
||
}
|
||
|
||
void
|
||
private_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "private_region(%qs)", m_desc);
|
||
}
|
||
|
||
/* class unknown_region : public region. */
|
||
|
||
/* Implementation of region::dump_to_pp vfunc for unknown_region. */
|
||
|
||
void
|
||
unknown_region::dump_to_pp (pretty_printer *pp, bool /*simple*/) const
|
||
{
|
||
pp_string (pp, "UNKNOWN_REGION");
|
||
}
|
||
|
||
void
|
||
unknown_region::print_dump_widget_label (pretty_printer *pp) const
|
||
{
|
||
pp_printf (pp, "unknown_region");
|
||
}
|
||
|
||
} // namespace ana
|
||
|
||
#endif /* #if ENABLE_ANALYZER */
|