Skip to content

Commit 908d76e

Browse files
committed
Add conversion of address_of(symbol) to SMT terms
1 parent eb2070a commit 908d76e

File tree

5 files changed

+191
-10
lines changed

5 files changed

+191
-10
lines changed

src/solvers/smt2_incremental/convert_expr_to_smt.cpp

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include <util/bitvector_expr.h>
55
#include <util/byte_operators.h>
66
#include <util/c_types.h>
7+
#include <util/config.h>
78
#include <util/expr.h>
89
#include <util/expr_cast.h>
910
#include <util/expr_util.h>
@@ -732,10 +733,53 @@ static smt_termt convert_expr_to_smt(
732733
}
733734
}
734735

736+
#ifndef CPROVER_INVARIANT_DO_NOT_CHECK
737+
static mp_integer power2(unsigned exponent)
738+
{
739+
mp_integer result;
740+
result.setPower2(exponent);
741+
return result;
742+
}
743+
#endif
744+
745+
/// \details
746+
/// This conversion constructs a bit vector representation of the memory
747+
/// address. This address is composed of 2 concatenated bit vector components.
748+
/// The first part is the base object's unique identifier. The second is the
749+
/// offset into that object. For address of symbols the offset will be 0. The
750+
/// offset may be non-zero for cases such as the address of a member field of a
751+
/// struct or a the address of a non-zero index into an array.
735752
static smt_termt convert_expr_to_smt(
736753
const address_of_exprt &address_of,
737-
const sub_expression_mapt &converted)
754+
const sub_expression_mapt &converted,
755+
const smt_object_mapt &object_map)
738756
{
757+
const auto type = type_try_dynamic_cast<pointer_typet>(address_of.type());
758+
INVARIANT(
759+
type, "Result of the address_of operator should have pointer type.");
760+
const auto base = find_object_base_expression(address_of);
761+
const auto object = object_map.find(base);
762+
INVARIANT(
763+
object != object_map.end(),
764+
"Objects should be tracked before converting their address to SMT terms");
765+
const std::size_t object_id = object->second.unique_id;
766+
INVARIANT(
767+
object_id < power2(config.bv_encoding.object_bits),
768+
"There should be sufficient bits to encode unique object identifier.");
769+
const smt_termt object_bit_vector =
770+
smt_bit_vector_constant_termt{object_id, config.bv_encoding.object_bits};
771+
INVARIANT(
772+
type->get_width() > config.bv_encoding.object_bits,
773+
"Pointer should be wider than object_bits in order to allow for offset "
774+
"encoding.");
775+
const size_t offset_bits = type->get_width() - config.bv_encoding.object_bits;
776+
if(
777+
const auto symbol =
778+
expr_try_dynamic_cast<symbol_exprt>(address_of.object()))
779+
{
780+
const smt_bit_vector_constant_termt offset{0, offset_bits};
781+
return smt_bit_vector_theoryt::concat(object_bit_vector, offset);
782+
}
739783
UNIMPLEMENTED_FEATURE(
740784
"Generation of SMT formula for address of expression: " +
741785
address_of.pretty());
@@ -1193,7 +1237,8 @@ static smt_termt convert_expr_to_smt(
11931237

11941238
static smt_termt dispatch_expr_to_smt_conversion(
11951239
const exprt &expr,
1196-
const sub_expression_mapt &converted)
1240+
const sub_expression_mapt &converted,
1241+
const smt_object_mapt &object_map)
11971242
{
11981243
if(const auto symbol = expr_try_dynamic_cast<symbol_exprt>(expr))
11991244
{
@@ -1347,7 +1392,7 @@ static smt_termt dispatch_expr_to_smt_conversion(
13471392
#endif
13481393
if(const auto address_of = expr_try_dynamic_cast<address_of_exprt>(expr))
13491394
{
1350-
return convert_expr_to_smt(*address_of, converted);
1395+
return convert_expr_to_smt(*address_of, converted, object_map);
13511396
}
13521397
if(const auto array_of = expr_try_dynamic_cast<array_of_exprt>(expr))
13531398
{
@@ -1547,7 +1592,8 @@ at_scope_exitt<functiont> at_scope_exit(functiont exit_function)
15471592
}
15481593
#endif
15491594

1550-
smt_termt convert_expr_to_smt(const exprt &expr)
1595+
smt_termt
1596+
convert_expr_to_smt(const exprt &expr, const smt_object_mapt &object_map)
15511597
{
15521598
#ifndef CPROVER_INVARIANT_DO_NOT_CHECK
15531599
static bool in_conversion = false;
@@ -1564,7 +1610,8 @@ smt_termt convert_expr_to_smt(const exprt &expr)
15641610
const auto find_result = sub_expression_map.find(expr);
15651611
if(find_result != sub_expression_map.cend())
15661612
return;
1567-
smt_termt term = dispatch_expr_to_smt_conversion(expr, sub_expression_map);
1613+
smt_termt term =
1614+
dispatch_expr_to_smt_conversion(expr, sub_expression_map, object_map);
15681615
sub_expression_map.emplace_hint(find_result, expr, std::move(term));
15691616
});
15701617
return std::move(sub_expression_map.at(expr));

src/solvers/smt2_incremental/convert_expr_to_smt.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#ifndef CPROVER_SOLVERS_SMT2_INCREMENTAL_CONVERT_EXPR_TO_SMT_H
44
#define CPROVER_SOLVERS_SMT2_INCREMENTAL_CONVERT_EXPR_TO_SMT_H
55

6+
#include <solvers/smt2_incremental/object_tracking.h>
67
#include <solvers/smt2_incremental/smt_sorts.h>
78
#include <solvers/smt2_incremental/smt_terms.h>
89

@@ -15,6 +16,7 @@ smt_sortt convert_type_to_smt_sort(const typet &type);
1516

1617
/// \brief Converts the \p expression to an smt encoding of the same expression
1718
/// stored as term ast (abstract syntax tree).
18-
smt_termt convert_expr_to_smt(const exprt &expression);
19+
smt_termt
20+
convert_expr_to_smt(const exprt &expression, const smt_object_mapt &object_map);
1921

2022
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_CONVERT_EXPR_TO_SMT_H

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ smt2_incremental_decision_proceduret::smt2_incremental_decision_proceduret(
132132
: ns{_ns},
133133
number_of_solver_calls{0},
134134
solver_process(std::move(_solver_process)),
135-
log{message_handler}
135+
log{message_handler},
136+
object_map{initial_smt_object_map()}
136137
{
137138
solver_process->send(
138139
smt_set_option_commandt{smt_option_produce_modelst{true}});
@@ -157,6 +158,13 @@ void smt2_incremental_decision_proceduret::ensure_handle_for_expr_defined(
157158
solver_process->send(function);
158159
}
159160

161+
smt_termt
162+
smt2_incremental_decision_proceduret::convert_expr_to_smt(const exprt &expr)
163+
{
164+
track_expression_objects(expr, ns, object_map);
165+
return ::convert_expr_to_smt(expr, object_map);
166+
}
167+
160168
exprt smt2_incremental_decision_proceduret::handle(const exprt &expr)
161169
{
162170
log.conditional_output(log.debug(), [&](messaget::mstreamt &debug) {
@@ -193,7 +201,11 @@ exprt smt2_incremental_decision_proceduret::get(const exprt &expr) const
193201
{
194202
if(gather_dependent_expressions(expr).empty())
195203
{
196-
descriptor = convert_expr_to_smt(expr);
204+
INVARIANT(
205+
objects_are_already_tracked(expr, object_map),
206+
"Objects in expressions being read should already be tracked from "
207+
"point of being set/handled.");
208+
descriptor = ::convert_expr_to_smt(expr, object_map);
197209
}
198210
else
199211
{

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@
66
#ifndef CPROVER_SOLVERS_SMT2_INCREMENTAL_SMT2_INCREMENTAL_DECISION_PROCEDURE_H
77
#define CPROVER_SOLVERS_SMT2_INCREMENTAL_SMT2_INCREMENTAL_DECISION_PROCEDURE_H
88

9-
#include <solvers/smt2_incremental/smt_terms.h>
10-
#include <solvers/stack_decision_procedure.h>
119
#include <util/message.h>
1210
#include <util/std_expr.h>
1311

12+
#include <solvers/smt2_incremental/object_tracking.h>
13+
#include <solvers/smt2_incremental/smt_terms.h>
14+
#include <solvers/stack_decision_procedure.h>
15+
1416
#include <memory>
1517
#include <unordered_map>
1618
#include <unordered_set>
@@ -55,6 +57,9 @@ class smt2_incremental_decision_proceduret final
5557
/// been defined, along with their dependencies in turn.
5658
void define_dependent_functions(const exprt &expr);
5759
void ensure_handle_for_expr_defined(const exprt &expr);
60+
/// \brief Add objects in \p expr to object_map if needed and convert to smt.
61+
/// \note This function is non-const because it mutates the object_map.
62+
smt_termt convert_expr_to_smt(const exprt &expr);
5863

5964
const namespacet &ns;
6065

@@ -78,6 +83,7 @@ class smt2_incremental_decision_proceduret final
7883
expression_handle_identifiers;
7984
std::unordered_map<exprt, smt_identifier_termt, irep_hash>
8085
expression_identifiers;
86+
smt_object_mapt object_map;
8187
};
8288

8389
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_SMT2_INCREMENTAL_DECISION_PROCEDURE_H

unit/solvers/smt2_incremental/convert_expr_to_smt.cpp

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,17 @@
77
#include <util/config.h>
88
#include <util/constructor_of.h>
99
#include <util/format.h>
10+
#include <util/namespace.h>
1011
#include <util/std_expr.h>
12+
#include <util/symbol_table.h>
1113

1214
#include <solvers/smt2_incremental/convert_expr_to_smt.h>
15+
#include <solvers/smt2_incremental/object_tracking.h>
1316
#include <solvers/smt2_incremental/smt_bit_vector_theory.h>
1417
#include <solvers/smt2_incremental/smt_core_theory.h>
1518
#include <solvers/smt2_incremental/smt_terms.h>
1619
#include <solvers/smt2_incremental/smt_to_smt2_string.h>
20+
#include <testing-utils/invariant.h>
1721
#include <testing-utils/use_catch.h>
1822

1923
TEST_CASE("\"typet\" to smt sort conversion", "[core][smt2_incremental]")
@@ -38,6 +42,11 @@ TEST_CASE("\"typet\" to smt sort conversion", "[core][smt2_incremental]")
3842
}
3943
}
4044

45+
smt_termt convert_expr_to_smt(const exprt &expression)
46+
{
47+
return convert_expr_to_smt(expression, initial_smt_object_map());
48+
}
49+
4150
TEST_CASE("\"symbol_exprt\" to smt term conversion", "[core][smt2_incremental]")
4251
{
4352
CHECK(
@@ -1072,3 +1081,108 @@ TEST_CASE("expr to smt conversion for type casts", "[core][smt2_incremental]")
10721081
}
10731082
}
10741083
}
1084+
1085+
TEST_CASE(
1086+
"expr to smt conversion for address of operator",
1087+
"[core][smt2_incremental]")
1088+
{
1089+
// The config lines are necessary to ensure that pointer width in configured.
1090+
config.ansi_c.mode = configt::ansi_ct::flavourt::GCC;
1091+
config.ansi_c.set_arch_spec_x86_64();
1092+
const symbol_tablet symbol_table;
1093+
const namespacet ns{symbol_table};
1094+
smt_object_mapt object_map = initial_smt_object_map();
1095+
const symbol_exprt foo{"foo", unsignedbv_typet{32}};
1096+
const symbol_exprt bar{"bar", unsignedbv_typet{32}};
1097+
SECTION("Address of symbol")
1098+
{
1099+
const address_of_exprt address_of_foo{foo};
1100+
track_expression_objects(address_of_foo, ns, object_map);
1101+
INFO("Expression " + address_of_foo.pretty(1, 0));
1102+
SECTION("8 object bits")
1103+
{
1104+
config.bv_encoding.object_bits = 8;
1105+
const auto converted = convert_expr_to_smt(address_of_foo, object_map);
1106+
CHECK(object_map.at(foo).unique_id == 1);
1107+
CHECK(
1108+
converted == smt_bit_vector_theoryt::concat(
1109+
smt_bit_vector_constant_termt{1, 8},
1110+
smt_bit_vector_constant_termt{0, 56}));
1111+
}
1112+
SECTION("16 object bits")
1113+
{
1114+
config.bv_encoding.object_bits = 16;
1115+
const auto converted = convert_expr_to_smt(address_of_foo, object_map);
1116+
CHECK(object_map.at(foo).unique_id == 1);
1117+
CHECK(
1118+
converted == smt_bit_vector_theoryt::concat(
1119+
smt_bit_vector_constant_termt{1, 16},
1120+
smt_bit_vector_constant_termt{0, 48}));
1121+
}
1122+
}
1123+
SECTION("Invariant checks")
1124+
{
1125+
const cbmc_invariants_should_throwt invariants_throw;
1126+
SECTION("Address of should result in a pointer")
1127+
{
1128+
exprt address_of = address_of_exprt{foo};
1129+
address_of.type() = bool_typet{};
1130+
REQUIRE_THROWS_MATCHES(
1131+
convert_expr_to_smt(address_of, object_map),
1132+
invariant_failedt,
1133+
invariant_failure_containing(
1134+
"Result of the address_of operator should have pointer type."));
1135+
}
1136+
SECTION("Objects should already be tracked")
1137+
{
1138+
REQUIRE_THROWS_MATCHES(
1139+
convert_expr_to_smt(address_of_exprt{foo}, object_map),
1140+
invariant_failedt,
1141+
invariant_failure_containing("Objects should be tracked before "
1142+
"converting their address to SMT terms"));
1143+
}
1144+
SECTION("There should be enough bits for object id")
1145+
{
1146+
config.bv_encoding.object_bits = 8;
1147+
const address_of_exprt address_of_foo{foo};
1148+
track_expression_objects(address_of_foo, ns, object_map);
1149+
object_map.at(foo).unique_id = 256;
1150+
REQUIRE_THROWS_MATCHES(
1151+
convert_expr_to_smt(address_of_exprt{foo}, object_map),
1152+
invariant_failedt,
1153+
invariant_failure_containing("There should be sufficient bits to "
1154+
"encode unique object identifier."));
1155+
}
1156+
SECTION("Pointer should be wide enough to encode offset")
1157+
{
1158+
config.bv_encoding.object_bits = 64;
1159+
const address_of_exprt address_of_foo{foo};
1160+
track_expression_objects(address_of_foo, ns, object_map);
1161+
object_map.at(foo).unique_id = 256;
1162+
REQUIRE_THROWS_MATCHES(
1163+
convert_expr_to_smt(address_of_exprt{foo}, object_map),
1164+
invariant_failedt,
1165+
invariant_failure_containing("Pointer should be wider than object_bits "
1166+
"in order to allow for offset encoding."));
1167+
}
1168+
}
1169+
SECTION("Comparison of address of operations.")
1170+
{
1171+
config.bv_encoding.object_bits = 8;
1172+
const exprt comparison =
1173+
notequal_exprt{address_of_exprt{foo}, address_of_exprt{bar}};
1174+
track_expression_objects(comparison, ns, object_map);
1175+
INFO("Expression " + comparison.pretty(1, 0));
1176+
const auto converted = convert_expr_to_smt(comparison, object_map);
1177+
CHECK(object_map.at(foo).unique_id == 2);
1178+
CHECK(object_map.at(bar).unique_id == 1);
1179+
CHECK(
1180+
converted == smt_core_theoryt::distinct(
1181+
smt_bit_vector_theoryt::concat(
1182+
smt_bit_vector_constant_termt{2, 8},
1183+
smt_bit_vector_constant_termt{0, 56}),
1184+
smt_bit_vector_theoryt::concat(
1185+
smt_bit_vector_constant_termt{1, 8},
1186+
smt_bit_vector_constant_termt{0, 56})));
1187+
}
1188+
}

0 commit comments

Comments
 (0)