Skip to content

Commit 6f3c16c

Browse files
Merge pull request #7894 from thomasspriggs/tas/smt_function_pointers2
Add function pointer support to incremental SMT decision procedure
2 parents c06b67c + afa2520 commit 6f3c16c

File tree

5 files changed

+144
-33
lines changed

5 files changed

+144
-33
lines changed

regression/cbmc/Function_Pointer8/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE
1+
CORE new-smt-backend
22
main.c
33

44
^EXIT=10$

src/solvers/smt2_incremental/convert_expr_to_smt.cpp

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <algorithm>
2525
#include <functional>
2626
#include <numeric>
27+
#include <stack>
2728

2829
/// Post order visitation is used in order to construct the the smt terms bottom
2930
/// upwards without using recursion to traverse the input `exprt`. Therefore
@@ -1875,6 +1876,46 @@ exprt lower_address_of_array_index(exprt expr)
18751876
return expr;
18761877
}
18771878

1879+
/// Post order traversal where the children of a node are only visited if
1880+
/// applying the \p filter function to that node returns true. Note that this
1881+
/// function is based on the `visit_post_template` function.
1882+
void filtered_visit_post(
1883+
const exprt &_expr,
1884+
std::function<bool(const exprt &)> filter,
1885+
std::function<void(const exprt &)> visitor)
1886+
{
1887+
struct stack_entryt
1888+
{
1889+
const exprt *e;
1890+
bool operands_pushed;
1891+
explicit stack_entryt(const exprt *_e) : e(_e), operands_pushed(false)
1892+
{
1893+
}
1894+
};
1895+
1896+
std::stack<stack_entryt> stack;
1897+
1898+
stack.emplace(&_expr);
1899+
1900+
while(!stack.empty())
1901+
{
1902+
auto &top = stack.top();
1903+
if(top.operands_pushed)
1904+
{
1905+
visitor(*top.e);
1906+
stack.pop();
1907+
}
1908+
else
1909+
{
1910+
// do modification of 'top' before pushing in case 'top' isn't stable
1911+
top.operands_pushed = true;
1912+
if(filter(*top.e))
1913+
for(auto &op : top.e->operands())
1914+
stack.emplace(&op);
1915+
}
1916+
}
1917+
}
1918+
18781919
smt_termt convert_expr_to_smt(
18791920
const exprt &expr,
18801921
const smt_object_mapt &object_map,
@@ -1894,18 +1935,30 @@ smt_termt convert_expr_to_smt(
18941935
#endif
18951936
sub_expression_mapt sub_expression_map;
18961937
const auto lowered_expr = lower_address_of_array_index(expr);
1897-
lowered_expr.visit_post([&](const exprt &expr) {
1898-
const auto find_result = sub_expression_map.find(expr);
1899-
if(find_result != sub_expression_map.cend())
1900-
return;
1901-
smt_termt term = dispatch_expr_to_smt_conversion(
1902-
expr,
1903-
sub_expression_map,
1904-
object_map,
1905-
pointer_sizes,
1906-
object_size,
1907-
is_dynamic_object);
1908-
sub_expression_map.emplace_hint(find_result, expr, std::move(term));
1909-
});
1938+
filtered_visit_post(
1939+
lowered_expr,
1940+
[](const exprt &expr) {
1941+
// Code values inside "address of" expressions do not need to be converted
1942+
// as the "address of" conversion only depends on the object identifier.
1943+
// Avoiding the conversion side steps a need to convert arbitrary code to
1944+
// SMT terms.
1945+
const auto address_of = expr_try_dynamic_cast<address_of_exprt>(expr);
1946+
if(!address_of)
1947+
return true;
1948+
return !can_cast_type<code_typet>(address_of->object().type());
1949+
},
1950+
[&](const exprt &expr) {
1951+
const auto find_result = sub_expression_map.find(expr);
1952+
if(find_result != sub_expression_map.cend())
1953+
return;
1954+
smt_termt term = dispatch_expr_to_smt_conversion(
1955+
expr,
1956+
sub_expression_map,
1957+
object_map,
1958+
pointer_sizes,
1959+
object_size,
1960+
is_dynamic_object);
1961+
sub_expression_map.emplace_hint(find_result, expr, std::move(term));
1962+
});
19101963
return std::move(sub_expression_map.at(lowered_expr));
19111964
}

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,17 @@ get_problem_messages(const smt_responset &response)
7070
/// returned by this`gather_dependent_expressions` function.
7171
/// \details `symbol_exprt`, `array_exprt` and `nondet_symbol_exprt` add
7272
/// dependant expressions.
73-
static std::vector<exprt> gather_dependent_expressions(const exprt &expr)
73+
static std::vector<exprt> gather_dependent_expressions(const exprt &root_expr)
7474
{
7575
std::vector<exprt> dependent_expressions;
76-
expr.visit_pre([&](const exprt &expr_node) {
76+
77+
std::stack<const exprt *> stack;
78+
stack.push(&root_expr);
79+
80+
while(!stack.empty())
81+
{
82+
const exprt &expr_node = *stack.top();
83+
stack.pop();
7784
if(
7885
can_cast_expr<symbol_exprt>(expr_node) ||
7986
can_cast_expr<array_exprt>(expr_node) ||
@@ -83,7 +90,17 @@ static std::vector<exprt> gather_dependent_expressions(const exprt &expr)
8390
{
8491
dependent_expressions.push_back(expr_node);
8592
}
86-
});
93+
// The decision procedure does not depend on the values inside address of
94+
// code typed expressions. We can build the address without knowing the
95+
// value at that memory location. In this case the hypothetical compiled
96+
// machine instructions at the address are not relevant to solving, only
97+
// representing *which* function a pointer points to is needed.
98+
const auto address_of = expr_try_dynamic_cast<address_of_exprt>(expr_node);
99+
if(address_of && can_cast_type<code_typet>(address_of->object().type()))
100+
continue;
101+
for(auto &operand : expr_node.operands())
102+
stack.push(&operand);
103+
}
87104
return dependent_expressions;
88105
}
89106

unit/solvers/smt2_incremental/convert_expr_to_smt.cpp

Lines changed: 33 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1477,29 +1477,46 @@ TEST_CASE(
14771477
const symbol_exprt bar{"bar", unsignedbv_typet{32}};
14781478
SECTION("Address of symbol")
14791479
{
1480-
const address_of_exprt address_of_foo{foo};
1481-
track_expression_objects(address_of_foo, ns, test.object_map);
1482-
INFO("Expression " + address_of_foo.pretty(1, 0));
1483-
SECTION("8 object bits")
1480+
SECTION("bit vector symbol")
1481+
{
1482+
const address_of_exprt address_of_foo{foo};
1483+
track_expression_objects(address_of_foo, ns, test.object_map);
1484+
INFO("Expression " + address_of_foo.pretty(1, 0));
1485+
SECTION("8 object bits")
1486+
{
1487+
config.bv_encoding.object_bits = 8;
1488+
const auto converted = test.convert(address_of_foo);
1489+
CHECK(test.object_map.at(foo).unique_id == 2);
1490+
CHECK(
1491+
converted == smt_bit_vector_theoryt::concat(
1492+
smt_bit_vector_constant_termt{2, 8},
1493+
smt_bit_vector_constant_termt{0, 56}));
1494+
}
1495+
SECTION("16 object bits")
1496+
{
1497+
config.bv_encoding.object_bits = 16;
1498+
const auto converted = test.convert(address_of_foo);
1499+
CHECK(test.object_map.at(foo).unique_id == 2);
1500+
CHECK(
1501+
converted == smt_bit_vector_theoryt::concat(
1502+
smt_bit_vector_constant_termt{2, 16},
1503+
smt_bit_vector_constant_termt{0, 48}));
1504+
}
1505+
}
1506+
SECTION("Code symbol")
14841507
{
14851508
config.bv_encoding.object_bits = 8;
1486-
const auto converted = test.convert(address_of_foo);
1487-
CHECK(test.object_map.at(foo).unique_id == 2);
1509+
const symbol_exprt function{"opaque", code_typet{{}, void_type()}};
1510+
const address_of_exprt function_pointer{function};
1511+
track_expression_objects(function_pointer, ns, test.object_map);
1512+
INFO("Expression " + function_pointer.pretty(1, 0));
1513+
const auto converted = test.convert(function_pointer);
1514+
CHECK(test.object_map.at(function).unique_id == 2);
14881515
CHECK(
14891516
converted == smt_bit_vector_theoryt::concat(
14901517
smt_bit_vector_constant_termt{2, 8},
14911518
smt_bit_vector_constant_termt{0, 56}));
14921519
}
1493-
SECTION("16 object bits")
1494-
{
1495-
config.bv_encoding.object_bits = 16;
1496-
const auto converted = test.convert(address_of_foo);
1497-
CHECK(test.object_map.at(foo).unique_id == 2);
1498-
CHECK(
1499-
converted == smt_bit_vector_theoryt::concat(
1500-
smt_bit_vector_constant_termt{2, 16},
1501-
smt_bit_vector_constant_termt{0, 48}));
1502-
}
15031520
}
15041521
SECTION("Invariant checks")
15051522
{

unit/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -731,6 +731,30 @@ TEST_CASE(
731731
REQUIRE(test.sent_commands == expected_commands);
732732
}
733733

734+
TEST_CASE(
735+
"smt2_incremental_decision_proceduret function pointer support.",
736+
"[core][smt2_incremental]")
737+
{
738+
auto test = decision_procedure_test_environmentt::make();
739+
const code_typet function_type{{}, void_type()};
740+
const symbolt function{"opaque", function_type, ID_C};
741+
test.symbol_table.insert(function);
742+
const address_of_exprt function_pointer{function.symbol_expr()};
743+
const equal_exprt equals_null{
744+
function_pointer, null_pointer_exprt{pointer_typet{function_type, 32}}};
745+
746+
test.sent_commands.clear();
747+
test.procedure.set_to(equals_null, false);
748+
749+
const std::vector<smt_commandt> expected_commands{
750+
smt_assert_commandt{smt_core_theoryt::make_not(smt_core_theoryt::equal(
751+
smt_bit_vector_theoryt::concat(
752+
smt_bit_vector_constant_termt{2, 8},
753+
smt_bit_vector_constant_termt{0, 24}),
754+
smt_bit_vector_constant_termt{0, 32}))}};
755+
REQUIRE(test.sent_commands == expected_commands);
756+
}
757+
734758
TEST_CASE(
735759
"smt2_incremental_decision_proceduret multi-ary with_exprt introduces "
736760
"correct number of indexes.",

0 commit comments

Comments
 (0)