Skip to content

Commit ca2a7b3

Browse files
committed
Value-set: add support for a base value-set
This is a simple copy-on-write base, intended to allow cheap local analysis on top of a potentially large, possibly mutable global state.
1 parent 7b5d5e5 commit ca2a7b3

File tree

2 files changed

+60
-2
lines changed

2 files changed

+60
-2
lines changed

src/pointer-analysis/value_set.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,10 @@ const value_sett::entryt *value_sett::find_entry(const value_sett::idt &id)
5555
const
5656
{
5757
auto found = values.find(id);
58-
return found == values.end() ? nullptr : &found->second;
58+
if(found != values.end())
59+
return &found->second;
60+
else
61+
return base_value_set ? base_value_set->find_entry(id) : nullptr;
5962
}
6063

6164
value_sett::entryt &value_sett::get_entry(
@@ -73,6 +76,14 @@ value_sett::entryt &value_sett::get_entry(
7376
std::pair<valuest::iterator, bool> r=
7477
values.insert(std::pair<idt, entryt>(index, e));
7578

79+
if(base_value_set && r.second)
80+
{
81+
// New entry; copy the existing base entry if found.
82+
const entryt *base_entry = base_value_set->find_entry(index);
83+
if(base_entry)
84+
r.first->second = *base_entry;
85+
}
86+
7687
return r.first->second;
7788
}
7889

@@ -188,6 +199,12 @@ void value_sett::output(
188199

189200
out << " } \n";
190201
}
202+
203+
if(base_value_set)
204+
{
205+
out << "NB. the base value-set (" << base_value_set << ") has a further "
206+
<< base_value_set->size() << " entries\n";
207+
}
191208
}
192209

193210
exprt value_sett::to_expr(const object_map_dt::value_type &it) const
@@ -212,6 +229,10 @@ exprt value_sett::to_expr(const object_map_dt::value_type &it) const
212229

213230
bool value_sett::make_union(const value_sett::valuest &new_values)
214231
{
232+
// Note this method makes no reference to base_value_set -- we assume that
233+
// we share the same base, if any, with new_values. This is enforced in the
234+
// make_union(const value_sett &) overload.
235+
215236
bool result=false;
216237

217238
valuest::iterator v_it=values.begin();
@@ -1666,3 +1687,13 @@ exprt value_sett::make_member(
16661687

16671688
return member_expr;
16681689
}
1690+
1691+
void value_sett::set_base_value_set(const value_sett *base)
1692+
{
1693+
// Simplifying assumptions -- this way we don't need to check for clashing
1694+
// keys, for example:
1695+
INVARIANT(size() == 0, "base_value_set cannot be set for a non-empty set");
1696+
INVARIANT(!base_value_set, "base_value_set cannot be reset");
1697+
1698+
base_value_set = base;
1699+
}

src/pointer-analysis/value_set.h

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,21 @@ class namespacet;
3838
/// pairs of an `exprt` and an optional offset if known (0 for both dynamic
3939
/// objects in the example given above). RHS expressions are represented using
4040
/// numbering to avoid storing redundant duplicate expressions.
41+
///
42+
/// A value_sett may have a base (stored in field base_value_set), in which case
43+
/// any lookup that fails in this value-set will check the base, and any write
44+
/// to this value-set will copy any entry in the base into this value-set before
45+
/// performing the write (i.e. we act as a copy-on-write layer on top of the
46+
/// base). This makes copying or union'ing value-sets with a common base
47+
/// cheaper -- the intended use case is for faster local analysis on top of a
48+
/// large, expensive-to-copy global context. value_sett::make_union requires
49+
/// that the value-sets being merged have the same base (or both have no base);
50+
/// it is up to the user to make sure this is true.
51+
4152
class value_sett
4253
{
4354
public:
44-
value_sett():location_number(0)
55+
value_sett():location_number(0), base_value_set(nullptr)
4556
{
4657
}
4758

@@ -340,6 +351,12 @@ class value_sett
340351
const entryt &e, const typet &type,
341352
const namespacet &ns);
342353

354+
/// Gets the number of entries in this value-set.
355+
valuest::size_type size() const
356+
{
357+
return values.size();
358+
}
359+
343360
/// Pretty-print this value-set
344361
/// \param ns: global namespace
345362
/// \param [out] out: stream to write to
@@ -366,6 +383,9 @@ class value_sett
366383
/// \return true if anything changed.
367384
bool make_union(const value_sett &new_values)
368385
{
386+
INVARIANT(
387+
new_values.base_value_set == base_value_set,
388+
"Unioned value-sets must have the same base");
369389
return make_union(new_values.values);
370390
}
371391

@@ -459,6 +479,11 @@ class value_sett
459479
exprt &expr,
460480
const namespacet &ns) const;
461481

482+
/// Set the base value-set, to which failing queries fall through. Note for
483+
/// simplicity's sake, at the moment this value-set must be empty when this
484+
/// is called, and the base value-set cannot be reset later.
485+
void set_base_value_set(const value_sett *base);
486+
462487
protected:
463488
/// Reads the set of objects pointed to by `expr`, including making
464489
/// recursive lookups for dereference operations etc.
@@ -550,6 +575,8 @@ class value_sett
550575
const namespacet &)
551576
{
552577
}
578+
579+
const value_sett *base_value_set;
553580
};
554581

555582
#endif // CPROVER_POINTER_ANALYSIS_VALUE_SET_H

0 commit comments

Comments
 (0)