Skip to content

Commit 8e49ea6

Browse files
authored
Merge pull request #9119 from tautschnig/fix/smt2-incremental-deterministic-object-order
Emit SMT object properties in deterministic order
2 parents 2b17bea + 4745b9c commit 8e49ea6

2 files changed

Lines changed: 78 additions & 18 deletions

File tree

‎src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp‎

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include <stack>
2828
#include <unordered_set>
29+
#include <vector>
2930

3031
/// Issues a command to the solving process which is expected to optionally
3132
/// return a success status followed by the actual response of interest.
@@ -706,18 +707,34 @@ lookup_decision_procedure_result(
706707
void smt2_incremental_decision_proceduret::define_object_properties()
707708
{
708709
object_properties_defined.resize(object_map.size());
710+
// The object map is an unordered map, whose iteration order depends on the
711+
// hash values of the object base expressions. These hashes may differ
712+
// across platforms and toolchains, for example when the signedness of
713+
// `char` differs. Establish a deterministic ordering based on the objects'
714+
// unique identifiers before emitting the definitions, so that the solver
715+
// receives them in a stable order. The unique identifiers are dense in
716+
// [0, object_map.size()), as assigned by track_expression_objects.
717+
std::vector<const decision_procedure_objectt *> objects(
718+
object_map.size(), nullptr);
709719
for(const auto &key_value : object_map)
710720
{
711-
const decision_procedure_objectt &object = key_value.second;
712-
if(object_properties_defined[object.unique_id])
721+
const std::size_t unique_id = key_value.second.unique_id;
722+
INVARIANT(
723+
unique_id < objects.size() && objects[unique_id] == nullptr,
724+
"object identifiers are unique and dense");
725+
objects[unique_id] = &key_value.second;
726+
}
727+
for(const decision_procedure_objectt *object : objects)
728+
{
729+
if(object_properties_defined[object->unique_id])
713730
continue;
714731
else
715-
object_properties_defined[object.unique_id] = true;
716-
define_dependent_functions(object.size);
732+
object_properties_defined[object->unique_id] = true;
733+
define_dependent_functions(object->size);
717734
solver_process->send(object_size_function.make_definition(
718-
object.unique_id, convert_expr_to_smt(object.size)));
735+
object->unique_id, convert_expr_to_smt(object->size)));
719736
solver_process->send(is_dynamic_object_function.make_definition(
720-
object.unique_id, object.is_dynamic));
737+
object->unique_id, object->is_dynamic));
721738
}
722739
}
723740

‎unit/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp‎

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
#include <goto-symex/path_storage.h>
3333
#include <solvers/smt2_incremental/smt_to_smt2_string.h>
3434

35+
#include <algorithm>
3536
#include <deque>
3637

3738
class analysis_execption_with_messaget
@@ -480,22 +481,12 @@ TEST_CASE(
480481
std::vector<smt_commandt> expected_commands{
481482
smt_declare_function_commandt{foo_term, {}},
482483
smt_assert_commandt{smt_core_theoryt::equal(foo_term, term_42)},
483-
invalid_pointer_object_size_definition,
484484
null_object_size_definition,
485485
null_object_dynamic_definition,
486+
invalid_pointer_object_size_definition,
486487
invalid_pointer_object_dynamic_definition,
487488
smt_check_sat_commandt{}};
488-
REQUIRE(
489-
(test.sent_commands.size() == expected_commands.size() &&
490-
std::all_of(
491-
expected_commands.begin(),
492-
expected_commands.end(),
493-
[&](const smt_commandt &command) -> bool {
494-
return std::find(
495-
test.sent_commands.begin(),
496-
test.sent_commands.end(),
497-
command) != test.sent_commands.end();
498-
})));
489+
REQUIRE(test.sent_commands == expected_commands);
499490
SECTION("Get \"foo\" value back")
500491
{
501492
test.sent_commands.clear();
@@ -730,6 +721,58 @@ TEST_CASE(
730721
REQUIRE(test.sent_commands == expected_commands);
731722
}
732723

724+
TEST_CASE(
725+
"smt2_incremental_decision_proceduret defines object properties in order "
726+
"of object identifiers.",
727+
"[core][smt2_incremental]")
728+
{
729+
auto test = decision_procedure_test_environmentt::make();
730+
// Objects of distinct sizes, so that their definitions can be told apart.
731+
const symbolt a = make_test_symbol("a", signedbv_typet{8});
732+
const symbolt b = make_test_symbol("b", signedbv_typet{16});
733+
const symbolt c = make_test_symbol("c", signedbv_typet{32});
734+
const pointer_typet pointer_type{signedbv_typet{8}, 32};
735+
const auto address = [&](const symbolt &symbol)
736+
{
737+
return typecast_exprt::conditional_cast(
738+
address_of_exprt{symbol.symbol_expr()}, pointer_type);
739+
};
740+
test.procedure.set_to(
741+
or_exprt{
742+
equal_exprt{address(a), address(b)}, equal_exprt{address(b), address(c)}},
743+
false);
744+
test.sent_commands.clear();
745+
test.mock_responses.push_back(smt_check_sat_responset{smt_sat_responset{}});
746+
test.procedure();
747+
748+
// Following the null and invalid-pointer objects (identifiers 0 and 1), the
749+
// objects are identified in the (deterministic) order in which object
750+
// tracking encounters them, which is c, b, a for this expression. Their
751+
// properties must be defined in the order of their identifiers,
752+
// irrespective of the hash values of the objects' base expressions.
753+
std::vector<smt_commandt> expected_definitions;
754+
const std::vector<std::size_t> object_sizes{0, 0, 4, 2, 1};
755+
for(std::size_t id = 0; id < object_sizes.size(); ++id)
756+
{
757+
expected_definitions.push_back(test.object_size_function.make_definition(
758+
id, smt_bit_vector_constant_termt{object_sizes[id], 32}));
759+
expected_definitions.push_back(
760+
test.is_dynamic_object_function.make_definition(id, false));
761+
}
762+
std::vector<smt_commandt> sent_definitions;
763+
for(const auto &command : test.sent_commands)
764+
{
765+
if(
766+
std::find(
767+
expected_definitions.begin(), expected_definitions.end(), command) !=
768+
expected_definitions.end())
769+
{
770+
sent_definitions.push_back(command);
771+
}
772+
}
773+
REQUIRE(sent_definitions == expected_definitions);
774+
}
775+
733776
TEST_CASE(
734777
"smt2_incremental_decision_proceduret function pointer support.",
735778
"[core][smt2_incremental]")

0 commit comments

Comments
 (0)