Skip to content

Commit 0bfe1a9

Browse files
committed
Compact propositional encoding of OBJECT_SIZE(ptr)
For each OBJECT_SIZE expression to be encoded by the propositional back-end we previously created a select statement over all objects, mapping a match to the object's size. This would yield a number of constraints linear in the number of objects times the number of OBJECT_SIZE expressions. For a particular benchmark at hand these were 2047 OBJECT_SIZE expressions over 1669 objects. Post-processing exceeded 30GB of memory after several minutes of constructing constraints. For programs that number of distinct object sizes will be small. In the above benchmark there are only 8 distinct object sizes (over those 1669 objects). The new encoding groups objects by size by forming a disjunction of the object identifiers. BDDs are used to compute compact representations of these disjunctions. With this new approach, post-processing on the above benchmark completes in less than 10 seconds and memory never exceeds 3GB. While at it, use the same trick for compacting the encoding of IS_DYNAMIC_OBJECT(ptr).
1 parent cafbcc4 commit 0bfe1a9

File tree

2 files changed

+184
-74
lines changed

2 files changed

+184
-74
lines changed

src/solvers/flattening/bv_pointers.cpp

Lines changed: 179 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ Author: Daniel Kroening, [email protected]
1818
#include <util/pointer_expr.h>
1919
#include <util/pointer_offset_size.h>
2020
#include <util/pointer_predicates.h>
21+
#include <util/replace_expr.h>
22+
23+
#include <solvers/prop/bdd_expr.h>
24+
#include <solvers/prop/literal_expr.h>
2125

2226
/// Map bytes according to the configured endianness. The key difference to
2327
/// endianness_mapt is that bv_endianness_mapt is aware of the bit-level
@@ -898,112 +902,214 @@ bvt bv_pointerst::add_addr(const exprt &expr)
898902
return encode(a, type);
899903
}
900904

901-
void bv_pointerst::do_postponed(
902-
const postponedt &postponed)
905+
std::pair<exprt, exprt> bv_pointerst::prepare_postponed_is_dynamic_object(
906+
std::vector<symbol_exprt> &placeholders) const
903907
{
904-
if(postponed.expr.id() == ID_is_dynamic_object)
908+
PRECONDITION(placeholders.empty());
909+
910+
const auto &objects = pointer_logic.objects;
911+
std::size_t number = 0;
912+
913+
exprt::operandst dynamic_objects_ops, not_dynamic_objects_ops;
914+
dynamic_objects_ops.reserve(objects.size());
915+
not_dynamic_objects_ops.reserve(objects.size());
916+
917+
for(auto it = objects.cbegin(); it != objects.cend(); ++it, ++number)
905918
{
906-
const auto &type =
907-
to_pointer_type(to_unary_expr(postponed.expr).op().type());
908-
const auto &objects = pointer_logic.objects;
909-
std::size_t number=0;
919+
const exprt &expr = *it;
910920

911-
for(auto it = objects.cbegin(); it != objects.cend(); ++it, ++number)
921+
// only compare object part
922+
pointer_typet pt = pointer_type(expr.type());
923+
bvt bv = object_literals(encode(number, pt), pt);
924+
925+
exprt::operandst conjuncts;
926+
conjuncts.reserve(bv.size());
927+
placeholders.reserve(bv.size());
928+
for(std::size_t i = 0; i < bv.size(); ++i)
912929
{
913-
const exprt &expr=*it;
930+
if(placeholders.size() <= i)
931+
placeholders.push_back(symbol_exprt{std::to_string(i), bool_typet{}});
932+
933+
POSTCONDITION(bv[i].is_constant());
934+
if(bv[i].is_true())
935+
conjuncts.emplace_back(placeholders[i]);
936+
else
937+
conjuncts.emplace_back(not_exprt{placeholders[i]});
938+
}
914939

915-
bool is_dynamic=pointer_logic.is_dynamic_object(expr);
940+
if(pointer_logic.is_dynamic_object(expr))
941+
dynamic_objects_ops.push_back(conjunction(conjuncts));
942+
else
943+
not_dynamic_objects_ops.push_back(conjunction(conjuncts));
944+
}
916945

917-
// only compare object part
918-
pointer_typet pt = pointer_type(expr.type());
919-
bvt bv = object_literals(encode(number, pt), type);
946+
exprt dynamic_objects = disjunction(dynamic_objects_ops);
947+
exprt not_dynamic_objects = disjunction(not_dynamic_objects_ops);
920948

921-
bvt saved_bv = object_literals(postponed.op, type);
949+
bdd_exprt bdd_converter;
950+
bddt dyn_bdd = bdd_converter.from_expr(dynamic_objects);
951+
bddt not_dyn_bdd = bdd_converter.from_expr(not_dynamic_objects);
922952

923-
POSTCONDITION(bv.size()==saved_bv.size());
924-
PRECONDITION(postponed.bv.size()==1);
953+
return {bdd_converter.as_expr(dyn_bdd), bdd_converter.as_expr(not_dyn_bdd)};
954+
}
925955

926-
literalt l1=bv_utils.equal(bv, saved_bv);
927-
literalt l2=postponed.bv.front();
956+
std::unordered_map<exprt, exprt, irep_hash>
957+
bv_pointerst::prepare_postponed_object_size(
958+
std::vector<symbol_exprt> &placeholders) const
959+
{
960+
PRECONDITION(placeholders.empty());
928961

929-
if(!is_dynamic)
930-
l2=!l2;
962+
const auto &objects = pointer_logic.objects;
963+
std::size_t number = 0;
931964

932-
prop.l_set_to_true(prop.limplies(l1, l2));
933-
}
934-
}
935-
else if(
936-
const auto postponed_object_size =
937-
expr_try_dynamic_cast<object_size_exprt>(postponed.expr))
965+
std::unordered_map<exprt, exprt::operandst, irep_hash> per_size_object_ops;
966+
967+
for(auto it = objects.cbegin(); it != objects.cend(); ++it, ++number)
938968
{
939-
const auto &type = to_pointer_type(postponed_object_size->pointer().type());
940-
const auto &objects = pointer_logic.objects;
941-
std::size_t number=0;
969+
const exprt &expr = *it;
970+
971+
if(expr.id() != ID_symbol && expr.id() != ID_string_constant)
972+
continue;
942973

943-
for(auto it = objects.cbegin(); it != objects.cend(); ++it, ++number)
974+
const auto size_expr = size_of_expr(expr.type(), ns);
975+
if(!size_expr.has_value())
976+
continue;
977+
978+
// only compare object part
979+
pointer_typet pt = pointer_type(expr.type());
980+
bvt bv = object_literals(encode(number, pt), pt);
981+
982+
exprt::operandst conjuncts;
983+
conjuncts.reserve(bv.size());
984+
placeholders.reserve(bv.size());
985+
for(std::size_t i = 0; i < bv.size(); ++i)
944986
{
945-
const exprt &expr=*it;
987+
if(placeholders.size() <= i)
988+
placeholders.push_back(symbol_exprt{std::to_string(i), bool_typet{}});
989+
990+
POSTCONDITION(bv[i].is_constant());
991+
if(bv[i].is_true())
992+
conjuncts.emplace_back(placeholders[i]);
993+
else
994+
conjuncts.emplace_back(not_exprt{placeholders[i]});
995+
}
946996

947-
if(expr.id() != ID_symbol && expr.id() != ID_string_constant)
948-
continue;
997+
per_size_object_ops[size_expr.value()].push_back(conjunction(conjuncts));
998+
}
949999

950-
const auto size_expr = size_of_expr(expr.type(), ns);
1000+
std::unordered_map<exprt, exprt, irep_hash> result;
1001+
for(const auto &size_entry : per_size_object_ops)
1002+
{
1003+
exprt all_objects_this_size = disjunction(size_entry.second);
1004+
bdd_exprt bdd_converter;
1005+
bddt bdd = bdd_converter.from_expr(all_objects_this_size);
9511006

952-
if(!size_expr.has_value())
953-
continue;
1007+
result.emplace(size_entry.first, bdd_converter.as_expr(bdd));
1008+
}
9541009

955-
const exprt object_size = typecast_exprt::conditional_cast(
956-
size_expr.value(), postponed_object_size->type());
1010+
return result;
1011+
}
9571012

958-
// only compare object part
959-
pointer_typet pt = pointer_type(expr.type());
960-
bvt bv = object_literals(encode(number, pt), type);
1013+
void bv_pointerst::finish_eager_conversion()
1014+
{
1015+
// post-processing arrays may yield further objects, do this first
1016+
SUB::finish_eager_conversion();
9611017

962-
bvt saved_bv = object_literals(postponed.op, type);
1018+
// it would seem nicer to use `optionalt` here, but GCC >= 12 produces
1019+
// spurious warnings about accessing uninitialized objects
1020+
std::pair<exprt, exprt> is_dynamic_expr = {nil_exprt{}, nil_exprt{}};
1021+
std::vector<symbol_exprt> is_dynamic_placeholders;
9631022

964-
bvt size_bv = convert_bv(object_size);
1023+
std::unordered_map<exprt, exprt, irep_hash> object_sizes;
1024+
std::vector<symbol_exprt> object_size_placeholders;
9651025

966-
POSTCONDITION(bv.size()==saved_bv.size());
967-
PRECONDITION(postponed.bv.size()>=1);
968-
PRECONDITION(size_bv.size() == postponed.bv.size());
1026+
for(const postponedt &postponed : postponed_list)
1027+
{
1028+
if(postponed.expr.id() == ID_is_dynamic_object)
1029+
{
1030+
if(is_dynamic_expr.first.is_nil())
1031+
is_dynamic_expr =
1032+
prepare_postponed_is_dynamic_object(is_dynamic_placeholders);
9691033

970-
literalt l1=bv_utils.equal(bv, saved_bv);
971-
if(l1.is_true())
1034+
const auto &type =
1035+
to_pointer_type(to_unary_expr(postponed.expr).op().type());
1036+
bvt saved_bv = object_literals(postponed.op, type);
1037+
POSTCONDITION(saved_bv.size() == is_dynamic_placeholders.size());
1038+
replace_mapt replacements;
1039+
for(std::size_t i = 0; i < saved_bv.size(); ++i)
9721040
{
973-
for(std::size_t i = 0; i < postponed.bv.size(); ++i)
974-
prop.set_equal(postponed.bv[i], size_bv[i]);
975-
break;
1041+
replacements.emplace(
1042+
is_dynamic_placeholders[i], literal_exprt{saved_bv[i]});
9761043
}
977-
else if(l1.is_false())
1044+
exprt is_dyn = is_dynamic_expr.first;
1045+
replace_expr(replacements, is_dyn);
1046+
exprt is_not_dyn = is_dynamic_expr.second;
1047+
replace_expr(replacements, is_not_dyn);
1048+
1049+
PRECONDITION(postponed.bv.size() == 1);
1050+
prop.l_set_to_true(
1051+
prop.limplies(convert_bv(is_dyn)[0], postponed.bv.front()));
1052+
prop.l_set_to_true(
1053+
prop.limplies(convert_bv(is_not_dyn)[0], !postponed.bv.front()));
1054+
}
1055+
else if(
1056+
const auto postponed_object_size =
1057+
expr_try_dynamic_cast<object_size_exprt>(postponed.expr))
1058+
{
1059+
if(object_sizes.empty())
1060+
object_sizes = prepare_postponed_object_size(object_size_placeholders);
1061+
1062+
// we might not have any usable objects
1063+
if(object_size_placeholders.empty())
9781064
continue;
1065+
1066+
const auto &type =
1067+
to_pointer_type(postponed_object_size->pointer().type());
1068+
bvt saved_bv = object_literals(postponed.op, type);
1069+
POSTCONDITION(saved_bv.size() == object_size_placeholders.size());
1070+
replace_mapt replacements;
1071+
for(std::size_t i = 0; i < saved_bv.size(); ++i)
1072+
{
1073+
replacements.emplace(
1074+
object_size_placeholders[i], literal_exprt{saved_bv[i]});
1075+
}
1076+
1077+
for(const auto &object_size_entry : object_sizes)
1078+
{
1079+
const exprt object_size = typecast_exprt::conditional_cast(
1080+
object_size_entry.first, postponed_object_size->type());
1081+
bvt size_bv = convert_bv(object_size);
1082+
POSTCONDITION(size_bv.size() == postponed.bv.size());
1083+
1084+
exprt all_objects_this_size = object_size_entry.second;
1085+
replace_expr(replacements, all_objects_this_size);
1086+
1087+
literalt l1 = convert_bv(all_objects_this_size)[0];
1088+
if(l1.is_true())
1089+
{
1090+
for(std::size_t i = 0; i < postponed.bv.size(); ++i)
1091+
prop.set_equal(postponed.bv[i], size_bv[i]);
1092+
break;
1093+
}
1094+
else if(l1.is_false())
1095+
continue;
9791096
#define COMPACT_OBJECT_SIZE_EQ
9801097
#ifndef COMPACT_OBJECT_SIZE_EQ
981-
literalt l2=bv_utils.equal(postponed.bv, size_bv);
1098+
literalt l2 = bv_utils.equal(postponed.bv, size_bv);
9821099

983-
prop.l_set_to_true(prop.limplies(l1, l2));
1100+
prop.l_set_to_true(prop.limplies(l1, l2));
9841101
#else
985-
for(std::size_t i = 0; i < postponed.bv.size(); ++i)
986-
{
987-
prop.lcnf({!l1, !postponed.bv[i], size_bv[i]});
988-
prop.lcnf({!l1, postponed.bv[i], !size_bv[i]});
989-
}
1102+
for(std::size_t i = 0; i < postponed.bv.size(); ++i)
1103+
{
1104+
prop.lcnf({!l1, !postponed.bv[i], size_bv[i]});
1105+
prop.lcnf({!l1, postponed.bv[i], !size_bv[i]});
1106+
}
9901107
#endif
1108+
}
9911109
}
1110+
else
1111+
UNREACHABLE;
9921112
}
993-
else
994-
UNREACHABLE;
995-
}
996-
997-
void bv_pointerst::finish_eager_conversion()
998-
{
999-
// post-processing arrays may yield further objects, do this first
1000-
SUB::finish_eager_conversion();
1001-
1002-
for(postponed_listt::const_iterator
1003-
it=postponed_list.begin();
1004-
it!=postponed_list.end();
1005-
it++)
1006-
do_postponed(*it);
10071113

10081114
// Clear the list to avoid re-doing in case of incremental usage.
10091115
postponed_list.clear();

src/solvers/flattening/bv_pointers.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ class bv_pointerst:public boolbvt
9292
typedef std::list<postponedt> postponed_listt;
9393
postponed_listt postponed_list;
9494

95-
void do_postponed(const postponedt &postponed);
95+
std::pair<exprt, exprt> prepare_postponed_is_dynamic_object(
96+
std::vector<symbol_exprt> &placeholders) const;
97+
98+
std::unordered_map<exprt, exprt, irep_hash>
99+
prepare_postponed_object_size(std::vector<symbol_exprt> &placeholders) const;
96100

97101
/// Given a pointer encoded in \p bv, extract the literals identifying the
98102
/// object that the pointer points to.

0 commit comments

Comments
 (0)