Skip to content

Commit 12e58e5

Browse files
author
owen-jones-diffblue
authored
Merge pull request diffblue#275 from diffblue/smowton/docs/lvsa
Document LVSA
2 parents ef6b8d7 + b5cf665 commit 12e58e5

File tree

4 files changed

+272
-30
lines changed

4 files changed

+272
-30
lines changed

src/pointer-analysis/local_value_set.cpp

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313

1414
#include <boost/range/algorithm.hpp>
1515

16+
// For documentation of members of `local_value_sett`, see the corresponding
17+
// header file, `local_value_set.h`.
18+
1619
void local_value_sett::make_union_adjusting_evs_types(
1720
object_mapt &dest,
1821
const object_mapt &src,
@@ -73,14 +76,35 @@ void local_value_sett::get_value_set(
7376
baset::get_value_set(tmp, dest, ns, true);
7477
}
7578

79+
/// Value-set analysis accrues a `suffix` as it walks down an expression tree--
80+
/// for example, it transforms a `get_value_set_rec(x.y.z)` into
81+
/// `get_value_set_rec(x, suffix = "y.z")`. This function recovers the type
82+
/// information lost in so doing by examining the type of the root expression
83+
/// and each subsequent member.
84+
/// The suffix may also contain the string "[]", which indicates a pointer
85+
/// dereference or array indexing operation.
86+
/// \param original_type: Type of root object (in the above example, `x.type()`)
87+
/// \param suffix: Suffix, a sequence of member and array operators
88+
/// \param ns: global namespace
89+
/// \param [out] parent_type: Type of the object referred to excluding the
90+
/// suffix's last period-delimited element. For example, if suffix is
91+
/// `x.y[].z[][]`, then this gives the result for `x.y[]`. If there is only
92+
/// one element in `suffix` (e.g. `.a[]`) then `parent_type` is simply
93+
/// `original_type`. If suffix is empty then `parent_type` is not assigned.
94+
/// \param [out] suffix_without_supertypes: is assigned `suffix` with any
95+
/// leading `[email protected]` accessors removed. For example, if
96+
/// `suffix` is `[email protected][email protected]` then `suffix_without_supertypes`
97+
/// will be assigned `.z`.
98+
/// \return Type of expression `root.suffix`, where `root` has type
99+
/// `original_type`
76100
static const typet &type_from_suffix(
77101
const typet &original_type,
78102
const std::string &suffix,
79103
const namespacet &ns,
80104
typet &parent_type,
81-
std::string &suffix_without_subtypes)
105+
std::string &suffix_without_supertypes)
82106
{
83-
suffix_without_subtypes=suffix;
107+
suffix_without_supertypes=suffix;
84108
const typet* ret=&original_type;
85109
size_t next_member=1; // Skip initial '.'
86110
if(suffix.size()!=0)
@@ -93,7 +117,8 @@ static const typet &type_from_suffix(
93117
// of pending members or similar.
94118
if(suffix[next_member]=='@')
95119
{
96-
// This path is (believed to be, supposed to be) Java-specific.
120+
// This accesses either special fields @class_identifier or @lock,
121+
// or a superclass field, which has a name like @package.Classname.
97122
if(has_infix(suffix, "@lock", next_member) ||
98123
has_infix(suffix, "@class_identifier", next_member))
99124
return static_cast<const typet&>(get_nil_irep());
@@ -104,10 +129,12 @@ static const typet &type_from_suffix(
104129
assert(has_infix(suffix, subclass_name, next_member));
105130
ret=&subclass_comp.type();
106131
next_member+=(subclass_name.size()+1);
107-
suffix_without_subtypes=suffix.substr(next_member-1);
132+
suffix_without_supertypes=suffix.substr(next_member-1);
108133
}
109134
else
110135
{
136+
// This accesses a struct member and then dereferences it one or more
137+
// times (for example, .x[][] gets field x and then derefs it twice)
111138
size_t member_after=suffix.find('.', next_member);
112139
if(member_after==std::string::npos)
113140
member_after=suffix.size();
@@ -135,7 +162,6 @@ static const typet &type_from_suffix(
135162
return *ret;
136163
}
137164

138-
// Override value_sett to provide behaviour for external value sets
139165
void local_value_sett::get_value_set_rec(
140166
const exprt &expr,
141167
object_mapt &dest,
@@ -197,6 +223,10 @@ void local_value_sett::get_value_set_rec(
197223
"[]" :
198224
suffix_without_subtype;
199225
const auto &extinit=to_external_value_set(expr);
226+
227+
// If we're reading from `EVS(A.b)` then initialize an entry for that
228+
// EVS containing an initializer (e.g.
229+
// `external_objects.A.b <- { EVS(A.b) }`.
200230
access_path_entry_exprt newentry(
201231
access_path_suffix,
202232
function,
@@ -228,9 +258,6 @@ void local_value_sett::get_value_set_rec(
228258
}
229259
}
230260

231-
/// Applying side effects needed in Recency Analysis.
232-
/// \param rhs: Right-hand-side of an assignment code expression.
233-
/// \param ns: Namespace.
234261
void local_value_sett::apply_side_effects(
235262
const exprt &rhs,
236263
const namespacet &ns)

src/pointer-analysis/local_value_set.h

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,57 +15,100 @@ class local_value_sett:public value_sett
1515
public:
1616
typedef value_sett baset;
1717

18-
#ifdef USE_DSTRING
19-
typedef std::map<idt, typet> declared_on_type_mapt;
20-
#else
21-
typedef std::unordered_map<idt, typet, string_hash> declared_on_type_mapt;
22-
#endif
23-
24-
declared_on_type_mapt declared_on_types;
2518
mutable bool use_precise_evs=true;
2619

20+
/// Clone of `value_sett::make_union`, with the exception that any external
21+
/// value-set expressions in `src` have their type updated before insertion
22+
/// into `dest`. This is used because we need to keep track of typecasts
23+
/// and other implicit casts affecting EVSes in order to know the actual
24+
/// types accessed by future member accesses.
25+
/// For example, if we have `class A { Object o; }`, then in the expression
26+
/// `((B)some_parameter.o).bfield`, `some_parameter.o` evaluates to `EVS(A.o)`
27+
/// with its natural type `Object`, but `(B)some_parameter.o` evaluates to
28+
/// `(B)EVS(A.o)`, represented by changing the EVS' type to `B`. This is
29+
/// then used to interpret the access `.bfield` with respect to type `B`.
30+
/// \param [out] dest: value-set RHS to merge into
31+
/// \param src: value-set RHS to merge in
32+
/// \param new_evs_type: new type for EVS expressions entering `dest`
2733
void make_union_adjusting_evs_types(
2834
object_mapt &dest,
2935
const object_mapt &src,
3036
const typet&) const;
3137

38+
/// Gets or creates a value-set entry corresponding to an external value set
39+
/// expression, which initially contains the EVS itself. For example, given
40+
/// EVS(A.x), representing all `x` fields of objects of type `A`, this would
41+
/// create an entry `external_objects.A.x <- EVS(A.x)` if it didn't already
42+
/// exist and return an iterator pointing to it.
43+
/// \param evse: external value set whose entry to create
44+
/// \return a la `std::map::insert`, a pair of an iterator pointing to the
45+
/// relevant entry and a boolean indicating if it is new.
3246
std::pair<valuest::iterator, bool> init_external_value_set(
3347
const external_value_set_exprt& evse);
3448

49+
// For all the overrides below, see the base type for parameter-level docs
50+
// as their meanings are unchanged.
51+
52+
/// Overrides `value_sett::get_value_set` to ensure casts between identical
53+
/// structs (e.g. `(struct Empty*)&also_empty_struct_of_different_type`
54+
/// should result in values of type `Empty*`, not the RHS' type)
3555
void get_value_set(
3656
const exprt &expr,
3757
object_mapt &dest,
3858
const namespacet &ns,
3959
bool is_simplified) const override;
4060

61+
/// Overrides `value_sett::assign` to (a) tag all external-value-set
62+
/// expressions from `rhs` indicating they have been assigned, and (b)
63+
/// apply side-effects of this assignment to the whole domain, which currently
64+
/// means demoting recent dynamic object operations to general ones when
65+
/// passing over that same dynamic object's allocation site.
4166
void assign(
4267
const exprt &lhs,
4368
const exprt &rhs,
4469
const namespacet &ns,
4570
bool is_simplified,
4671
bool add_to_sets) override;
4772

73+
/// Overrides `value_sett::apply_code` to erase `dead` statements and ignore
74+
/// intrinsic instructions that manipulate abstract taint but don't cause
75+
/// any actual data flow.
4876
void apply_code(
4977
const codet &,
5078
const namespacet &) override;
5179

80+
/// Union data structures in this value-set besides the actual `values`
81+
/// with those in `other`.
5282
void make_union_bookkeeping_data_structures(const local_value_sett &other);
5383

5484
protected:
85+
/// Overrides `value_sett::get_value_set_rec` to (a) handle reads from
86+
/// external value sets and (b) ensure reads from Java allocation sites pick
87+
/// up the correct dynamic object ID.
5588
void get_value_set_rec(
5689
const exprt &expr,
5790
object_mapt &dest,
5891
const std::string &suffix,
5992
const typet &original_type,
6093
const namespacet &ns) const override;
6194

95+
/// Overrides `value_sett::assign_rec` to (a) handle writes to external value
96+
/// sets and (b) record `declared_on_type` entries for all symbols and
97+
/// dynamic objects, making it easier for external value sets to be matched
98+
/// against them when applying a function summary at a callsite.
6299
void assign_rec(
63100
const exprt &lhs,
64101
const object_mapt &values_rhs,
65102
const std::string &suffix,
66103
const namespacet &ns,
67104
bool add_to_sets) override;
68105

106+
/// Applies side-effects stemming from reading from `rhs`. At the moment this
107+
/// only happens when we pass an allocation site, at which recent dynamic
108+
/// objects (representing the most recent allocation at this site) are merged
109+
/// into the general dynamic object representing the same site.
110+
/// \param rhs: RHS of an assignment
111+
/// \param ns: global namespace
69112
void apply_side_effects(
70113
const exprt &rhs,
71114
const namespacet &ns);

0 commit comments

Comments
 (0)