Skip to content

Commit 79766cb

Browse files
committed
Add object tracking
1 parent 4879051 commit 79766cb

File tree

5 files changed

+281
-0
lines changed

5 files changed

+281
-0
lines changed

src/solvers/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ SRC = $(BOOLEFORCE_SRC) \
195195
smt2/smt2irep.cpp \
196196
smt2_incremental/construct_value_expr_from_smt.cpp \
197197
smt2_incremental/convert_expr_to_smt.cpp \
198+
smt2_incremental/object_tracking.cpp \
198199
smt2_incremental/smt_bit_vector_theory.cpp \
199200
smt2_incremental/smt_commands.cpp \
200201
smt2_incremental/smt_core_theory.cpp \
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// Author: Diffblue Ltd.
2+
3+
#include "object_tracking.h"
4+
5+
#include <util/c_types.h>
6+
#include <util/pointer_offset_size.h>
7+
#include <util/std_code.h>
8+
#include <util/std_expr.h>
9+
#include <util/string_constant.h>
10+
11+
exprt find_object_base_expression(const address_of_exprt &address_of)
12+
{
13+
auto current = std::ref(address_of.object());
14+
while(
15+
!(can_cast_expr<symbol_exprt>(current) ||
16+
can_cast_expr<constant_exprt>(current) ||
17+
can_cast_expr<string_constantt>(current) ||
18+
can_cast_expr<code_labelt>(current)))
19+
{
20+
if(const auto index = expr_try_dynamic_cast<index_exprt>(current.get()))
21+
{
22+
// For the case `my_array[bar]` the base expression is `my_array`.
23+
current = index->array();
24+
continue;
25+
}
26+
if(const auto member = expr_try_dynamic_cast<member_exprt>(current.get()))
27+
{
28+
// For the case `my_struct.field_name` the base expression is `my_struct`.
29+
current = member->compound();
30+
continue;
31+
}
32+
INVARIANT(
33+
false,
34+
"Unable to find base object of expression: " +
35+
current.get().pretty(1, 0));
36+
}
37+
return current.get();
38+
}
39+
40+
static decision_procedure_objectt make_null_object()
41+
{
42+
decision_procedure_objectt null_object;
43+
null_object.unique_id = 0;
44+
null_object.base_expression = null_pointer_exprt{pointer_type(void_type())};
45+
return null_object;
46+
}
47+
48+
smt_object_mapt initial_smt_object_map()
49+
{
50+
smt_object_mapt object_map;
51+
decision_procedure_objectt null_object = make_null_object();
52+
exprt base = null_object.base_expression;
53+
object_map.emplace(std::move(base), std::move(null_object));
54+
return object_map;
55+
}
56+
57+
void track_expression_objects(
58+
const exprt &expression,
59+
const namespacet &ns,
60+
smt_object_mapt &object_map)
61+
{
62+
find_object_base_expressions(
63+
expression, [&](const exprt &object_base) -> void {
64+
const auto find_result = object_map.find(object_base);
65+
if(find_result != object_map.cend())
66+
return;
67+
decision_procedure_objectt object;
68+
object.base_expression = object_base;
69+
object.unique_id = object_map.size();
70+
object.size = size_of_expr(object_base.type(), ns);
71+
object_map.emplace_hint(find_result, object_base, std::move(object));
72+
});
73+
}
74+
75+
bool objects_are_already_tracked(
76+
const exprt &expression,
77+
const smt_object_mapt &object_map)
78+
{
79+
bool all_objects_tracked = true;
80+
find_object_base_expressions(
81+
expression, [&](const exprt &object_base) -> void {
82+
const auto find_result = object_map.find(object_base);
83+
if(find_result != object_map.cend())
84+
return;
85+
all_objects_tracked = false;
86+
});
87+
return all_objects_tracked;
88+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Author: Diffblue Ltd.
2+
3+
#ifndef CPROVER_SOLVERS_SMT2_INCREMENTAL_OBJECT_TRACKING_H
4+
#define CPROVER_SOLVERS_SMT2_INCREMENTAL_OBJECT_TRACKING_H
5+
6+
#include <util/expr.h>
7+
#include <util/pointer_expr.h>
8+
9+
#include <unordered_map>
10+
11+
/// Information the decision procedure holds about each object.
12+
struct decision_procedure_objectt
13+
{
14+
/// The expression for the root of the object. This is expression equivalent
15+
/// to deferencing a pointer to this object with a zero offset.
16+
exprt base_expression;
17+
/// Number which uniquely identifies this particular object.
18+
std::size_t unique_id;
19+
/// Expression which evaluates to the size of the object in bytes.
20+
optionalt<exprt> size;
21+
};
22+
23+
/// The model of addresses we use consists of a unique object identifier and an
24+
/// offset. In order to encode the offset identifiers we need to assign unique
25+
/// identifiers to the objects. This function finds the base object expression
26+
/// in an address of expression for which a unique identifier needs to be
27+
/// assigned.
28+
exprt find_object_base_expression(const address_of_exprt &address_of);
29+
30+
/// Arbitary expressions passed to the decision procedure may have multiple
31+
/// address of operations as its sub expressions. This means the overall
32+
/// expression may contain multiple base objects which need to be assigned
33+
/// unique identifiers.
34+
/// \param expression
35+
/// The expression within which to find base objects.
36+
/// \param output_object
37+
/// This function is called with each of the base object expressions found, as
38+
/// they are found.
39+
/// \details
40+
/// The found objects are returned through an output function in order to
41+
/// separate the implementation of the storage and deduplication of the
42+
/// results from finding the object expressions. The type of \p output_object
43+
/// is a template parameter in order to eliminate potential performance
44+
/// overheads of using `std::function`.
45+
template <typename output_object_functiont>
46+
void find_object_base_expressions(
47+
const exprt &expression,
48+
const output_object_functiont &output_object)
49+
{
50+
expression.visit_pre([&](const exprt &node) {
51+
if(const auto address_of = expr_try_dynamic_cast<address_of_exprt>(node))
52+
{
53+
output_object(find_object_base_expression(*address_of));
54+
}
55+
});
56+
}
57+
58+
/// Mapping from an object's base expression to the set of information about it
59+
/// which we track.
60+
using smt_object_mapt =
61+
std::unordered_map<exprt, decision_procedure_objectt, irep_hash>;
62+
63+
/// Constructs an initial object map containing the null object. The null object
64+
/// must be added at construction in order to ensure it is allocated a unique
65+
/// identifier of 0.
66+
smt_object_mapt initial_smt_object_map();
67+
68+
/// \brief
69+
/// Finds all the object expressions in the given expression and adds them to
70+
/// the object map for cases where the map does not contain them already.
71+
/// \param expression
72+
/// The expression within which to find and base object expressions.
73+
/// \param ns
74+
/// The namespace used to look up the size of object types.
75+
/// \param object_map
76+
/// The map into which any new tracking information should be inserted.
77+
void track_expression_objects(
78+
const exprt &expression,
79+
const namespacet &ns,
80+
smt_object_mapt &object_map);
81+
82+
/// Finds whether all base object expressions in the given expression are
83+
/// already tracked in the given object map. This supports writing invariants
84+
/// on the base object expressions already being tracked in the map in contexts
85+
/// where the map is const.
86+
bool objects_are_already_tracked(
87+
const exprt &expression,
88+
const smt_object_mapt &object_map);
89+
90+
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_OBJECT_TRACKING_H

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ SRC += analyses/ai/ai.cpp \
102102
solvers/smt2/smt2irep.cpp \
103103
solvers/smt2_incremental/construct_value_expr_from_smt.cpp \
104104
solvers/smt2_incremental/convert_expr_to_smt.cpp \
105+
solvers/smt2_incremental/object_tracking.cpp \
105106
solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp \
106107
solvers/smt2_incremental/smt_bit_vector_theory.cpp \
107108
solvers/smt2_incremental/smt_commands.cpp \
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Author: Diffblue Ltd.
2+
3+
#include <util/c_types.h>
4+
#include <util/namespace.h>
5+
#include <util/optional.h>
6+
#include <util/std_expr.h>
7+
#include <util/symbol_table.h>
8+
9+
#include <solvers/smt2_incremental/object_tracking.h>
10+
#include <testing-utils/use_catch.h>
11+
12+
#include <string>
13+
#include <utility>
14+
15+
TEST_CASE("find_object_base_expression", "[core][smt2_incremental]")
16+
{
17+
const typet base_type = pointer_typet{unsignedbv_typet{8}, 18};
18+
const symbol_exprt object_base{"base", base_type};
19+
const symbol_exprt index{"index", base_type};
20+
const pointer_typet pointer_type{base_type, 12};
21+
std::string description;
22+
optionalt<address_of_exprt> address_of;
23+
using rowt = std::pair<std::string, address_of_exprt>;
24+
std::tie(description, address_of) = GENERATE_REF(
25+
rowt{"Address of symbol", {object_base, pointer_type}},
26+
rowt{"Address of index", {index_exprt{object_base, index}, pointer_type}},
27+
rowt{
28+
"Address of struct member",
29+
{member_exprt{object_base, "baz", unsignedbv_typet{8}}, pointer_type}},
30+
rowt{
31+
"Address of index of struct member",
32+
{index_exprt{
33+
member_exprt{object_base, "baz", unsignedbv_typet{8}}, index},
34+
pointer_type}},
35+
rowt{
36+
"Address of struct member at index",
37+
{member_exprt{
38+
index_exprt{object_base, index}, "baz", unsignedbv_typet{8}},
39+
pointer_type}});
40+
SECTION(description)
41+
{
42+
CHECK(find_object_base_expression(*address_of) == object_base);
43+
}
44+
}
45+
46+
TEST_CASE("Tracking object base expressions", "[core][smt2_incremental]")
47+
{
48+
const typet base_type = pointer_typet{signedbv_typet{16}, 18};
49+
const symbol_exprt foo{"foo", base_type};
50+
const symbol_exprt bar{"bar", base_type};
51+
const symbol_exprt index{"index", base_type};
52+
const pointer_typet pointer_type{base_type, 32};
53+
const exprt bar_address = address_of_exprt{bar, pointer_type};
54+
const exprt compound_expression = and_exprt{
55+
equal_exprt{
56+
address_of_exprt{index_exprt{foo, index}, pointer_type}, bar_address},
57+
notequal_exprt{
58+
address_of_exprt{
59+
member_exprt{foo, "baz", unsignedbv_typet{8}}, pointer_type},
60+
bar_address}};
61+
SECTION("Find base expressions")
62+
{
63+
std::vector<exprt> expressions;
64+
find_object_base_expressions(compound_expression, [&](const exprt &expr) {
65+
expressions.push_back(expr);
66+
});
67+
CHECK(expressions == std::vector<exprt>{bar, foo, bar, foo});
68+
}
69+
smt_object_mapt object_map = initial_smt_object_map();
70+
SECTION("Check initial object map has null pointer")
71+
{
72+
REQUIRE(object_map.size() == 1);
73+
const exprt null_pointer = null_pointer_exprt{::pointer_type(void_type())};
74+
CHECK(object_map.begin()->first == null_pointer);
75+
CHECK(object_map.begin()->second.unique_id == 0);
76+
CHECK(object_map.begin()->second.base_expression == null_pointer);
77+
}
78+
symbol_tablet symbol_table;
79+
namespacet ns{symbol_table};
80+
SECTION("Check objects of compound expression not yet tracked")
81+
{
82+
CHECK_FALSE(objects_are_already_tracked(compound_expression, object_map));
83+
}
84+
track_expression_objects(compound_expression, ns, object_map);
85+
SECTION("Tracking expression objects")
86+
{
87+
CHECK(object_map.size() == 3);
88+
const auto foo_object = object_map.find(foo);
89+
REQUIRE(foo_object != object_map.end());
90+
CHECK(foo_object->second.base_expression == foo);
91+
CHECK(foo_object->second.unique_id == 2);
92+
const auto bar_object = object_map.find(bar);
93+
REQUIRE(bar_object != object_map.end());
94+
CHECK(bar_object->second.base_expression == bar);
95+
CHECK(bar_object->second.unique_id == 1);
96+
}
97+
SECTION("Confirming objects are tracked.")
98+
{
99+
CHECK(objects_are_already_tracked(compound_expression, object_map));
100+
}
101+
}

0 commit comments

Comments
 (0)