gcc_assert (null_p () || m_idx < (int)model.get_num_regions ());
}
-/* class id_set. */
+/* class region_id_set. */
-/* id_set<region_id>'s ctor. */
-
-template<>
-id_set<region_id>::id_set (const region_model *model)
+region_id_set::region_id_set (const region_model *model)
: m_bitmap (model->get_num_regions ())
{
bitmap_clear (m_bitmap);
}
+/* class svalue_id_set. */
+
+svalue_id_set::svalue_id_set ()
+: m_bitmap (NULL)
+{
+ bitmap_clear (m_bitmap);
+}
+
/* class svalue and its various subclasses. */
/* class svalue. */
return svalue_id::null ();
}
+/* Copy from SRC_RID to DST_RID, using CTXT for any issues that occur.
+ Copy across any value for the region, and handle structs, unions
+ and arrays recursively. */
+
+void
+region_model::copy_region (region_id dst_rid, region_id src_rid,
+ region_model_context *ctxt)
+{
+ gcc_assert (!dst_rid.null_p ());
+ gcc_assert (!src_rid.null_p ());
+ if (dst_rid == src_rid)
+ return;
+ region *dst_reg = get_region (dst_rid);
+ region *src_reg = get_region (src_rid);
+
+ /* Copy across any value for the src region itself. */
+ svalue_id sid = src_reg->get_value (*this, true, ctxt);
+ set_value (dst_rid, sid, ctxt);
+
+ if (dst_reg->get_kind () != src_reg->get_kind ())
+ return;
+
+ /* Copy across child regions for structs, unions, and arrays. */
+ switch (dst_reg->get_kind ())
+ {
+ case RK_PRIMITIVE:
+ return;
+ case RK_STRUCT:
+ {
+ struct_region *dst_sub = as_a <struct_region *> (dst_reg);
+ struct_region *src_sub = as_a <struct_region *> (src_reg);
+ copy_struct_region (dst_rid, dst_sub, src_sub, ctxt);
+ }
+ return;
+ case RK_UNION:
+ {
+ union_region *src_sub = as_a <union_region *> (src_reg);
+ copy_union_region (dst_rid, src_sub, ctxt);
+ }
+ return;
+ case RK_FRAME:
+ case RK_GLOBALS:
+ case RK_CODE:
+ case RK_FUNCTION:
+ return;
+ case RK_ARRAY:
+ {
+ array_region *dst_sub = as_a <array_region *> (dst_reg);
+ array_region *src_sub = as_a <array_region *> (src_reg);
+ copy_array_region (dst_rid, dst_sub, src_sub, ctxt);
+ }
+ return;
+ case RK_STACK:
+ case RK_HEAP:
+ case RK_ROOT:
+ case RK_SYMBOLIC:
+ return;
+ }
+}
+
+/* Subroutine of region_model::copy_region for copying the child
+ regions for a struct. */
+
+void
+region_model::copy_struct_region (region_id dst_rid,
+ struct_region *dst_reg,
+ struct_region *src_reg,
+ region_model_context *ctxt)
+{
+ for (map_region::iterator_t iter = src_reg->begin ();
+ iter != src_reg->end (); ++iter)
+ {
+ tree src_key = (*iter).first;
+ region_id src_field_rid = (*iter).second;
+ region *src_field_reg = get_region (src_field_rid);
+ region_id dst_field_rid
+ = dst_reg->get_or_create (this, dst_rid, src_key,
+ src_field_reg->get_type (), ctxt);
+ copy_region (dst_field_rid, src_field_rid, ctxt);
+ }
+}
+
+/* Subroutine of region_model::copy_region for copying the active
+ child region for a union. */
+
+void
+region_model::copy_union_region (region_id dst_rid,
+ union_region *src_reg,
+ region_model_context *ctxt)
+{
+ region_id src_active_view_rid = src_reg->get_active_view ();
+ if (src_active_view_rid.null_p ())
+ return;
+ region *src_active_view = get_region (src_active_view_rid);
+ tree type = src_active_view->get_type ();
+ region_id dst_active_view_rid = get_or_create_view (dst_rid, type, ctxt);
+ copy_region (dst_active_view_rid, src_active_view_rid, ctxt);
+}
+
+/* Subroutine of region_model::copy_region for copying the child
+ regions for an array. */
+
+void
+region_model::copy_array_region (region_id dst_rid,
+ array_region *dst_reg,
+ array_region *src_reg,
+ region_model_context *ctxt)
+{
+ for (array_region::iterator_t iter = src_reg->begin ();
+ iter != src_reg->end (); ++iter)
+ {
+ array_region::key_t src_key = (*iter).first;
+ region_id src_field_rid = (*iter).second;
+ region *src_field_reg = get_region (src_field_rid);
+ region_id dst_field_rid
+ = dst_reg->get_or_create (this, dst_rid, src_key,
+ src_field_reg->get_type (), ctxt);
+ copy_region (dst_field_rid, src_field_rid, ctxt);
+ }
+}
+
/* Generate a hash value for this region. The work is done by the
add_to_hash vfunc. */
/* Pop the topmost frame_region from this stack.
+ If RESULT_DST_RID is non-null, copy any return value from the frame
+ into RESULT_DST_RID's region.
+
Purge the frame region and all its descendent regions.
Convert any pointers that point into such regions into
POISON_KIND_POPPED_STACK svalues.
- Return the ID of any return value from the frame.
-
If PURGE, then purge all unused svalues, with the exception of any
- return value for the frame, which is temporarily
- preserved in case no regions reference it, so it can
- be written into a region in the caller.
+ returned values.
Accumulate stats on purged entities into STATS. */
-svalue_id
-stack_region::pop_frame (region_model *model, bool purge, purge_stats *stats,
+void
+stack_region::pop_frame (region_model *model, region_id result_dst_rid,
+ bool purge, purge_stats *stats,
region_model_context *ctxt)
{
gcc_assert (m_frame_rids.length () > 0);
frame_region *frame = model->get_region<frame_region> (frame_rid);
/* Evaluate the result, within the callee frame. */
- svalue_id result_sid;
+ svalue_id_set returned_sids;
tree fndecl = frame->get_function ()->decl;
tree result = DECL_RESULT (fndecl);
if (result && TREE_TYPE (result) != void_type_node)
- result_sid = model->get_rvalue (result, ctxt);
+ {
+ if (!result_dst_rid.null_p ())
+ {
+ /* Copy the result to RESULT_DST_RID. */
+ model->copy_region (result_dst_rid, model->get_lvalue (result, ctxt),
+ ctxt);
+ }
+ if (purge)
+ {
+ /* Populate returned_sids, to avoid purging them. */
+ region_id return_rid = model->get_lvalue (result, NULL);
+ region_id_set returned_rids (model);
+ model->get_descendents (return_rid, &returned_rids,
+ region_id::null ());
+ for (unsigned i = 0; i < model->get_num_regions (); i++)
+ {
+ region_id rid = region_id::from_int (i);
+ if (returned_rids.region_p (rid))
+ {
+ svalue_id sid = model->get_region (rid)->get_value_direct ();
+ returned_sids.add_svalue (sid);
+ }
+ }
+ }
+ }
/* Pop the frame RID. */
m_frame_rids.pop ();
stats,
ctxt ? ctxt->get_logger () : NULL);
- /* Delete unused svalues, but don't delete the return value. */
+ /* Delete unused svalues, but don't delete the return value(s). */
if (purge)
- model->purge_unused_svalues (stats, ctxt, &result_sid);
+ model->purge_unused_svalues (stats, ctxt, &returned_sids);
model->validate ();
-
- return result_sid;
}
/* Implementation of region::add_to_hash vfunc for stack_region. */
/* Pop the topmost frame_region from this root_region's stack;
see the comment for stack_region::pop_frame. */
-svalue_id
-root_region::pop_frame (region_model *model, bool purge, purge_stats *out,
+void
+root_region::pop_frame (region_model *model, region_id result_dst_rid,
+ bool purge, purge_stats *out,
region_model_context *ctxt)
{
stack_region *stack = model->get_region <stack_region> (m_stack_rid);
- return stack->pop_frame (model, purge, out, ctxt);
+ stack->pop_frame (model, result_dst_rid, purge, out, ctxt);
}
/* Return the region_id of the stack region, creating it if doesn't
case PARM_DECL:
{
/* LHS = VAR; */
- svalue_id var_sid = get_rvalue (rhs1, ctxt);
- set_value (lhs_rid, var_sid, ctxt);
+ region_id rhs_rid = get_lvalue (rhs1, ctxt);
+ copy_region (lhs_rid, rhs_rid, ctxt);
}
break;
tree rhs = gimple_return_retval (return_stmt);
if (lhs && rhs)
- set_value (get_lvalue (lhs, ctxt), get_rvalue (rhs, ctxt), ctxt);
+ copy_region (get_lvalue (lhs, ctxt), get_lvalue (rhs, ctxt), ctxt);
}
/* Update this model for a call and return of setjmp/sigsetjmp at CALL within
while (get_stack_depth () > setjmp_stack_depth)
{
/* Don't purge unused svalues yet, as we're using fake_retval_sid. */
- pop_frame (false, NULL, ctxt);
+ pop_frame (region_id::null (), false, NULL, ctxt);
}
gcc_assert (get_stack_depth () == setjmp_stack_depth);
push_frame (call_edge.get_callee_function (), &arg_sids, ctxt);
}
-/* Pop the top-most frame_region from the stack, and store the svalue
- for any returned value into the region for the lvalue of the LHS of
+/* Pop the top-most frame_region from the stack, and copy the return
+ region's values (if any) into the region for the lvalue of the LHS of
the call (if any). */
void
region_model::update_for_return_superedge (const return_superedge &return_edge,
region_model_context *ctxt)
{
- purge_stats stats;
- svalue_id result_sid = pop_frame (true, &stats, ctxt);
- // TODO: do something with the stats?
-
- if (result_sid.null_p ())
- return;
+ region_id stack_rid = get_stack_region_id ();
+ stack_region *stack = get_region <stack_region> (stack_rid);
- /* Set the result of the call, within the caller frame. */
+ /* Get the region for the result of the call, within the caller frame. */
+ region_id result_dst_rid;
const gcall *call_stmt = return_edge.get_call_stmt ();
tree lhs = gimple_call_lhs (call_stmt);
if (lhs)
- set_value (get_lvalue (lhs, ctxt), result_sid, ctxt);
- else
+ {
+ /* Normally we access the top-level frame, which is:
+ path_var (expr, stack->get_num_frames () - 1)
+ whereas here we need the caller frame, hence "- 2" here. */
+ gcc_assert (stack->get_num_frames () >= 2);
+ result_dst_rid = get_lvalue (path_var (lhs, stack->get_num_frames () - 2),
+ ctxt);
+ }
+
+ purge_stats stats;
+ stack->pop_frame (this, result_dst_rid, true, &stats, ctxt);
+ // TODO: do something with the stats?
+
+ if (!lhs)
{
/* This could be a leak; try purging again, but this time,
- don't special-case the result_sid. */
- purge_stats stats;
+ don't special-case the result sids (as was done in pop_frame). */
purge_unused_svalues (&stats, ctxt);
}
}
/* Pop the topmost frame_region from this region_model's stack;
see the comment for stack_region::pop_frame. */
-svalue_id
-region_model::pop_frame (bool purge, purge_stats *out,
+void
+region_model::pop_frame (region_id result_dst_rid,
+ bool purge, purge_stats *out,
region_model_context *ctxt)
{
- return get_root_region ()->pop_frame (this, purge, out, ctxt);
+ get_root_region ()->pop_frame (this, result_dst_rid, purge, out, ctxt);
}
/* Get the number of frames in this region_model's stack. */
number of redundant unknown values could grow without bounds, and each
such model would be treated as distinct.
- If KNOWN_USED is non-NULL, treat *KNOWN_USED as used (this is for
+ If KNOWN_USED_SIDS is non-NULL, treat *KNOWN_USED_SIDS as used (this is for
handling values being returned from functions as their frame is popped,
since otherwise we'd have to simultaneously determine both the rvalue
of the return expr in the callee frame and the lvalue for the gcall's
assignment in the caller frame, and it seems cleaner to express all
- lvalue and rvalue lookups implicitly relative to a "current" frame). */
+ lvalue and rvalue lookups implicitly relative to a "current" frame).
+ The svalue_ids in *KNOWN_USED_SIDS are not remapped and hence this
+ call makes it invalid. */
void
region_model::purge_unused_svalues (purge_stats *stats,
region_model_context *ctxt,
- svalue_id *known_used_sid)
+ svalue_id_set *known_used_sids)
{
// TODO: might want to avoid a vfunc call just to do logging here:
logger *logger = ctxt ? ctxt->get_logger () : NULL;
auto_sbitmap used (m_svalues.length ());
bitmap_clear (used);
- if (known_used_sid)
- if (!known_used_sid->null_p ())
- bitmap_set_bit (used, known_used_sid->as_int ());
+ if (known_used_sids)
+ {
+ /* We can't use an sbitmap for known_used_sids as the number of
+ svalues could have grown since it was created. */
+ for (unsigned i = 0; i < get_num_svalues (); i++)
+ if (known_used_sids->svalue_p (svalue_id::from_int (i)))
+ bitmap_set_bit (used, i);
+ }
/* Walk the regions, marking sids that are used. */
unsigned i;
stats->m_num_svalues++;
}
- if (known_used_sid)
- map.update (known_used_sid);
-
validate ();
}
return t;
}
+/* Selftest fixture for creating the type "struct coord {int x; int y; };". */
+
+struct coord_test
+{
+ coord_test ()
+ {
+ auto_vec<tree> fields;
+ m_x_field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
+ get_identifier ("x"), integer_type_node);
+ fields.safe_push (m_x_field);
+ m_y_field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
+ get_identifier ("y"), integer_type_node);
+ fields.safe_push (m_y_field);
+ m_coord_type = make_test_compound_type ("coord", true, &fields);
+ }
+
+ tree m_x_field;
+ tree m_y_field;
+ tree m_coord_type;
+};
+
/* Verify that dumps can show struct fields. */
static void
test_dump_2 ()
{
- auto_vec<tree> fields;
- tree x_field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
- get_identifier ("x"), integer_type_node);
- fields.safe_push (x_field);
- tree y_field = build_decl (UNKNOWN_LOCATION, FIELD_DECL,
- get_identifier ("y"), integer_type_node);
- fields.safe_push (y_field);
- tree coord_type = make_test_compound_type ("coord", true, &fields);
-
- tree c = build_global_decl ("c", coord_type);
- tree c_x = build3 (COMPONENT_REF, TREE_TYPE (x_field),
- c, x_field, NULL_TREE);
- tree c_y = build3 (COMPONENT_REF, TREE_TYPE (y_field),
- c, y_field, NULL_TREE);
+ coord_test ct;
+
+ tree c = build_global_decl ("c", ct.m_coord_type);
+ tree c_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
+ c, ct.m_x_field, NULL_TREE);
+ tree c_y = build3 (COMPONENT_REF, TREE_TYPE (ct.m_y_field),
+ c, ct.m_y_field, NULL_TREE);
tree int_17 = build_int_cst (integer_type_node, 17);
tree int_m3 = build_int_cst (integer_type_node, -3);
ASSERT_DUMP_EQ (model, true, "y: 0, {x}: unknown, x == y");
}
+/* Verify that compound assignments work as expected. */
+
+static void
+test_compound_assignment ()
+{
+ coord_test ct;
+
+ tree c = build_global_decl ("c", ct.m_coord_type);
+ tree c_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
+ c, ct.m_x_field, NULL_TREE);
+ tree c_y = build3 (COMPONENT_REF, TREE_TYPE (ct.m_y_field),
+ c, ct.m_y_field, NULL_TREE);
+ tree d = build_global_decl ("d", ct.m_coord_type);
+ tree d_x = build3 (COMPONENT_REF, TREE_TYPE (ct.m_x_field),
+ d, ct.m_x_field, NULL_TREE);
+ tree d_y = build3 (COMPONENT_REF, TREE_TYPE (ct.m_y_field),
+ d, ct.m_y_field, NULL_TREE);
+
+ tree int_17 = build_int_cst (integer_type_node, 17);
+ tree int_m3 = build_int_cst (integer_type_node, -3);
+
+ region_model model;
+ model.set_value (c_x, int_17, NULL);
+ model.set_value (c_y, int_m3, NULL);
+
+ ASSERT_DUMP_EQ (model, true, "c.x: 17, c.y: -3");
+
+ /* Copy c to d. */
+ model.copy_region (model.get_lvalue (d, NULL), model.get_lvalue (c, NULL),
+ NULL);
+ /* Check that the fields have the same svalues. */
+ ASSERT_EQ (model.get_rvalue (c_x, NULL), model.get_rvalue (d_x, NULL));
+ ASSERT_EQ (model.get_rvalue (c_y, NULL), model.get_rvalue (d_y, NULL));
+}
+
/* Verify the details of pushing and popping stack frames. */
static void
/* Pop the "child_fn" frame from the stack. */
purge_stats purged;
- model.pop_frame (true, &purged, &ctxt);
+ model.pop_frame (region_id::null (), true, &purged, &ctxt);
/* We should have purged the unknown values for x and y. */
ASSERT_EQ (purged.m_num_svalues, 2);
test_purging_by_criteria ();
test_purge_unused_svalues ();
test_assignment ();
+ test_compound_assignment ();
test_stack_frames ();
test_get_representative_path_var ();
test_canonicalization_1 ();
*id = get_dst_for_src (*id);
}
-/* A set of IDs within a region_model (either svalue_id or region_id). */
+/* A set of region_ids within a region_model. */
-template <typename T>
-class id_set
+class region_id_set
{
public:
- id_set (const region_model *model);
+ region_id_set (const region_model *model);
- void add_region (T id)
+ void add_region (region_id rid)
{
- if (!id.null_p ())
- bitmap_set_bit (m_bitmap, id.as_int ());
+ if (!rid.null_p ())
+ bitmap_set_bit (m_bitmap, rid.as_int ());
}
- bool region_p (T id) const
+ bool region_p (region_id rid) const
{
- gcc_assert (!id.null_p ());
+ gcc_assert (!rid.null_p ());
return bitmap_bit_p (const_cast <auto_sbitmap &> (m_bitmap),
- id.as_int ());
+ rid.as_int ());
}
unsigned int num_regions ()
auto_sbitmap m_bitmap;
};
-typedef id_set<region_id> region_id_set;
+/* A set of svalue_ids within a region_model. */
+
+class svalue_id_set
+{
+public:
+ svalue_id_set ();
+
+ void add_svalue (svalue_id sid)
+ {
+ if (!sid.null_p ())
+ bitmap_set_bit (m_bitmap, sid.as_int ());
+ }
+
+ bool svalue_p (svalue_id sid) const
+ {
+ gcc_assert (!sid.null_p ());
+ return bitmap_bit_p (const_cast <auto_bitmap &> (m_bitmap),
+ sid.as_int ());
+ }
+
+private:
+ auto_bitmap m_bitmap;
+};
/* Various operations delete information from a region_model.
void add_view (region_id view_rid, region_model *model);
region_id get_view (tree type, region_model *model) const;
+ region_id get_active_view () const { return m_active_view_rid; }
bool is_view_p () const { return m_is_view; }
virtual void validate (const region_model &model) const;
void push_frame (region_id frame_rid);
region_id get_current_frame_id () const;
- svalue_id pop_frame (region_model *model, bool purge, purge_stats *stats,
- region_model_context *ctxt);
+ void pop_frame (region_model *model, region_id result_dst_rid,
+ bool purge, purge_stats *stats,
+ region_model_context *ctxt);
void remap_region_ids (const region_id_map &map) FINAL OVERRIDE;
vec<svalue_id> *arg_sids,
region_model_context *ctxt);
region_id get_current_frame_id (const region_model &model) const;
- svalue_id pop_frame (region_model *model, bool purge, purge_stats *stats,
- region_model_context *ctxt);
+ void pop_frame (region_model *model, region_id result_dst_rid,
+ bool purge, purge_stats *stats,
+ region_model_context *ctxt);
region_id ensure_stack_region (region_model *model);
region_id get_stack_region_id () const { return m_stack_rid; }
region_model_context *ctxt);
region_id get_current_frame_id () const;
function * get_current_function () const;
- svalue_id pop_frame (bool purge, purge_stats *stats,
- region_model_context *ctxt);
+ void pop_frame (region_id result_dst_rid,
+ bool purge, purge_stats *stats,
+ region_model_context *ctxt);
int get_stack_depth () const;
function *get_function_at_depth (unsigned depth) const;
svalue_id set_to_new_unknown_value (region_id dst_rid, tree type,
region_model_context *ctxt);
+ void copy_region (region_id dst_rid, region_id src_rid,
+ region_model_context *ctxt);
+
tristate eval_condition (svalue_id lhs,
enum tree_code op,
svalue_id rhs) const;
void purge_unused_svalues (purge_stats *out,
region_model_context *ctxt,
- svalue_id *known_used_sid = NULL);
+ svalue_id_set *known_used_sids = NULL);
void remap_svalue_ids (const svalue_id_map &map);
void remap_region_ids (const region_id_map &map);
region_id get_lvalue_1 (path_var pv, region_model_context *ctxt);
svalue_id get_rvalue_1 (path_var pv, region_model_context *ctxt);
+ void copy_struct_region (region_id dst_rid, struct_region *dst_reg,
+ struct_region *src_reg, region_model_context *ctxt);
+ void copy_union_region (region_id dst_rid, union_region *src_reg,
+ region_model_context *ctxt);
+ void copy_array_region (region_id dst_rid, array_region *dst_reg,
+ array_region *src_reg, region_model_context *ctxt);
+
region_id make_region_for_unexpected_tree_code (region_model_context *ctxt,
tree t,
const dump_location_t &loc);