Skip to content

Commit 9220c3a

Browse files
authored
Merge pull request #7702 from tautschnig/cleanup/object-size-encoding
Compact propositional encoding of OBJECT_SIZE(ptr)
2 parents 519a12b + c53b111 commit 9220c3a

File tree

2 files changed

+189
-74
lines changed

2 files changed

+189
-74
lines changed

src/solvers/flattening/bv_pointers.cpp

+179-73
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ Author: Daniel Kroening, kroening@kroening.com
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>
2122
#include <util/simplify_expr.h>
2223

24+
#include <solvers/prop/bdd_expr.h>
25+
#include <solvers/prop/literal_expr.h>
26+
2327
/// Map bytes according to the configured endianness. The key difference to
2428
/// endianness_mapt is that bv_endianness_mapt is aware of the bit-level
2529
/// encoding of types, which need not co-incide with the bit layout at
@@ -911,112 +915,214 @@ bvt bv_pointerst::add_addr(const exprt &expr)
911915
return encode(a, type);
912916
}
913917

914-
void bv_pointerst::do_postponed(
915-
const postponedt &postponed)
918+
std::pair<exprt, exprt> bv_pointerst::prepare_postponed_is_dynamic_object(
919+
std::vector<symbol_exprt> &placeholders) const
916920
{
917-
if(postponed.expr.id() == ID_is_dynamic_object)
921+
PRECONDITION(placeholders.empty());
922+
923+
const auto &objects = pointer_logic.objects;
924+
std::size_t number = 0;
925+
926+
exprt::operandst dynamic_objects_ops, not_dynamic_objects_ops;
927+
dynamic_objects_ops.reserve(objects.size());
928+
not_dynamic_objects_ops.reserve(objects.size());
929+
930+
for(auto it = objects.cbegin(); it != objects.cend(); ++it, ++number)
918931
{
919-
const auto &type =
920-
to_pointer_type(to_unary_expr(postponed.expr).op().type());
921-
const auto &objects = pointer_logic.objects;
922-
std::size_t number=0;
932+
const exprt &expr = *it;
923933

924-
for(auto it = objects.cbegin(); it != objects.cend(); ++it, ++number)
934+
// only compare object part
935+
pointer_typet pt = pointer_type(expr.type());
936+
bvt bv = object_literals(encode(number, pt), pt);
937+
938+
exprt::operandst conjuncts;
939+
conjuncts.reserve(bv.size());
940+
placeholders.reserve(bv.size());
941+
for(std::size_t i = 0; i < bv.size(); ++i)
925942
{
926-
const exprt &expr=*it;
943+
if(placeholders.size() <= i)
944+
placeholders.push_back(symbol_exprt{std::to_string(i), bool_typet{}});
945+
946+
POSTCONDITION(bv[i].is_constant());
947+
if(bv[i].is_true())
948+
conjuncts.emplace_back(placeholders[i]);
949+
else
950+
conjuncts.emplace_back(not_exprt{placeholders[i]});
951+
}
927952

928-
bool is_dynamic=pointer_logic.is_dynamic_object(expr);
953+
if(pointer_logic.is_dynamic_object(expr))
954+
dynamic_objects_ops.push_back(conjunction(conjuncts));
955+
else
956+
not_dynamic_objects_ops.push_back(conjunction(conjuncts));
957+
}
929958

930-
// only compare object part
931-
pointer_typet pt = pointer_type(expr.type());
932-
bvt bv = object_literals(encode(number, pt), type);
959+
exprt dynamic_objects = disjunction(dynamic_objects_ops);
960+
exprt not_dynamic_objects = disjunction(not_dynamic_objects_ops);
933961

934-
bvt saved_bv = object_literals(postponed.op, type);
962+
bdd_exprt bdd_converter;
963+
bddt dyn_bdd = bdd_converter.from_expr(dynamic_objects);
964+
bddt not_dyn_bdd = bdd_converter.from_expr(not_dynamic_objects);
935965

936-
POSTCONDITION(bv.size()==saved_bv.size());
937-
PRECONDITION(postponed.bv.size()==1);
966+
return {bdd_converter.as_expr(dyn_bdd), bdd_converter.as_expr(not_dyn_bdd)};
967+
}
938968

939-
literalt l1=bv_utils.equal(bv, saved_bv);
940-
literalt l2=postponed.bv.front();
969+
std::unordered_map<exprt, exprt, irep_hash>
970+
bv_pointerst::prepare_postponed_object_size(
971+
std::vector<symbol_exprt> &placeholders) const
972+
{
973+
PRECONDITION(placeholders.empty());
941974

942-
if(!is_dynamic)
943-
l2=!l2;
975+
const auto &objects = pointer_logic.objects;
976+
std::size_t number = 0;
944977

945-
prop.l_set_to_true(prop.limplies(l1, l2));
946-
}
947-
}
948-
else if(
949-
const auto postponed_object_size =
950-
expr_try_dynamic_cast<object_size_exprt>(postponed.expr))
978+
std::unordered_map<exprt, exprt::operandst, irep_hash> per_size_object_ops;
979+
980+
for(auto it = objects.cbegin(); it != objects.cend(); ++it, ++number)
951981
{
952-
const auto &type = to_pointer_type(postponed_object_size->pointer().type());
953-
const auto &objects = pointer_logic.objects;
954-
std::size_t number=0;
982+
const exprt &expr = *it;
983+
984+
if(expr.id() != ID_symbol && expr.id() != ID_string_constant)
985+
continue;
955986

956-
for(auto it = objects.cbegin(); it != objects.cend(); ++it, ++number)
987+
const auto size_expr = size_of_expr(expr.type(), ns);
988+
if(!size_expr.has_value())
989+
continue;
990+
991+
// only compare object part
992+
pointer_typet pt = pointer_type(expr.type());
993+
bvt bv = object_literals(encode(number, pt), pt);
994+
995+
exprt::operandst conjuncts;
996+
conjuncts.reserve(bv.size());
997+
placeholders.reserve(bv.size());
998+
for(std::size_t i = 0; i < bv.size(); ++i)
957999
{
958-
const exprt &expr=*it;
1000+
if(placeholders.size() <= i)
1001+
placeholders.push_back(symbol_exprt{std::to_string(i), bool_typet{}});
1002+
1003+
POSTCONDITION(bv[i].is_constant());
1004+
if(bv[i].is_true())
1005+
conjuncts.emplace_back(placeholders[i]);
1006+
else
1007+
conjuncts.emplace_back(not_exprt{placeholders[i]});
1008+
}
9591009

960-
if(expr.id() != ID_symbol && expr.id() != ID_string_constant)
961-
continue;
1010+
per_size_object_ops[size_expr.value()].push_back(conjunction(conjuncts));
1011+
}
9621012

963-
const auto size_expr = size_of_expr(expr.type(), ns);
1013+
std::unordered_map<exprt, exprt, irep_hash> result;
1014+
for(const auto &size_entry : per_size_object_ops)
1015+
{
1016+
exprt all_objects_this_size = disjunction(size_entry.second);
1017+
bdd_exprt bdd_converter;
1018+
bddt bdd = bdd_converter.from_expr(all_objects_this_size);
9641019

965-
if(!size_expr.has_value())
966-
continue;
1020+
result.emplace(size_entry.first, bdd_converter.as_expr(bdd));
1021+
}
9671022

968-
const exprt object_size = typecast_exprt::conditional_cast(
969-
size_expr.value(), postponed_object_size->type());
1023+
return result;
1024+
}
9701025

971-
// only compare object part
972-
pointer_typet pt = pointer_type(expr.type());
973-
bvt bv = object_literals(encode(number, pt), type);
1026+
void bv_pointerst::finish_eager_conversion()
1027+
{
1028+
// post-processing arrays may yield further objects, do this first
1029+
SUB::finish_eager_conversion();
9741030

975-
bvt saved_bv = object_literals(postponed.op, type);
1031+
// it would seem nicer to use `optionalt` here, but GCC >= 12 produces
1032+
// spurious warnings about accessing uninitialized objects
1033+
std::pair<exprt, exprt> is_dynamic_expr = {nil_exprt{}, nil_exprt{}};
1034+
std::vector<symbol_exprt> is_dynamic_placeholders;
9761035

977-
bvt size_bv = convert_bv(object_size);
1036+
std::unordered_map<exprt, exprt, irep_hash> object_sizes;
1037+
std::vector<symbol_exprt> object_size_placeholders;
9781038

979-
POSTCONDITION(bv.size()==saved_bv.size());
980-
PRECONDITION(postponed.bv.size()>=1);
981-
PRECONDITION(size_bv.size() == postponed.bv.size());
1039+
for(const postponedt &postponed : postponed_list)
1040+
{
1041+
if(postponed.expr.id() == ID_is_dynamic_object)
1042+
{
1043+
if(is_dynamic_expr.first.is_nil())
1044+
is_dynamic_expr =
1045+
prepare_postponed_is_dynamic_object(is_dynamic_placeholders);
9821046

983-
literalt l1=bv_utils.equal(bv, saved_bv);
984-
if(l1.is_true())
1047+
const auto &type =
1048+
to_pointer_type(to_unary_expr(postponed.expr).op().type());
1049+
bvt saved_bv = object_literals(postponed.op, type);
1050+
POSTCONDITION(saved_bv.size() == is_dynamic_placeholders.size());
1051+
replace_mapt replacements;
1052+
for(std::size_t i = 0; i < saved_bv.size(); ++i)
9851053
{
986-
for(std::size_t i = 0; i < postponed.bv.size(); ++i)
987-
prop.set_equal(postponed.bv[i], size_bv[i]);
988-
break;
1054+
replacements.emplace(
1055+
is_dynamic_placeholders[i], literal_exprt{saved_bv[i]});
9891056
}
990-
else if(l1.is_false())
1057+
exprt is_dyn = is_dynamic_expr.first;
1058+
replace_expr(replacements, is_dyn);
1059+
exprt is_not_dyn = is_dynamic_expr.second;
1060+
replace_expr(replacements, is_not_dyn);
1061+
1062+
PRECONDITION(postponed.bv.size() == 1);
1063+
prop.l_set_to_true(
1064+
prop.limplies(convert_bv(is_dyn)[0], postponed.bv.front()));
1065+
prop.l_set_to_true(
1066+
prop.limplies(convert_bv(is_not_dyn)[0], !postponed.bv.front()));
1067+
}
1068+
else if(
1069+
const auto postponed_object_size =
1070+
expr_try_dynamic_cast<object_size_exprt>(postponed.expr))
1071+
{
1072+
if(object_sizes.empty())
1073+
object_sizes = prepare_postponed_object_size(object_size_placeholders);
1074+
1075+
// we might not have any usable objects
1076+
if(object_size_placeholders.empty())
9911077
continue;
1078+
1079+
const auto &type =
1080+
to_pointer_type(postponed_object_size->pointer().type());
1081+
bvt saved_bv = object_literals(postponed.op, type);
1082+
POSTCONDITION(saved_bv.size() == object_size_placeholders.size());
1083+
replace_mapt replacements;
1084+
for(std::size_t i = 0; i < saved_bv.size(); ++i)
1085+
{
1086+
replacements.emplace(
1087+
object_size_placeholders[i], literal_exprt{saved_bv[i]});
1088+
}
1089+
1090+
for(const auto &object_size_entry : object_sizes)
1091+
{
1092+
const exprt object_size = typecast_exprt::conditional_cast(
1093+
object_size_entry.first, postponed_object_size->type());
1094+
bvt size_bv = convert_bv(object_size);
1095+
POSTCONDITION(size_bv.size() == postponed.bv.size());
1096+
1097+
exprt all_objects_this_size = object_size_entry.second;
1098+
replace_expr(replacements, all_objects_this_size);
1099+
1100+
literalt l1 = convert_bv(all_objects_this_size)[0];
1101+
if(l1.is_true())
1102+
{
1103+
for(std::size_t i = 0; i < postponed.bv.size(); ++i)
1104+
prop.set_equal(postponed.bv[i], size_bv[i]);
1105+
break;
1106+
}
1107+
else if(l1.is_false())
1108+
continue;
9921109
#define COMPACT_OBJECT_SIZE_EQ
9931110
#ifndef COMPACT_OBJECT_SIZE_EQ
994-
literalt l2=bv_utils.equal(postponed.bv, size_bv);
1111+
literalt l2 = bv_utils.equal(postponed.bv, size_bv);
9951112

996-
prop.l_set_to_true(prop.limplies(l1, l2));
1113+
prop.l_set_to_true(prop.limplies(l1, l2));
9971114
#else
998-
for(std::size_t i = 0; i < postponed.bv.size(); ++i)
999-
{
1000-
prop.lcnf({!l1, !postponed.bv[i], size_bv[i]});
1001-
prop.lcnf({!l1, postponed.bv[i], !size_bv[i]});
1002-
}
1115+
for(std::size_t i = 0; i < postponed.bv.size(); ++i)
1116+
{
1117+
prop.lcnf({!l1, !postponed.bv[i], size_bv[i]});
1118+
prop.lcnf({!l1, postponed.bv[i], !size_bv[i]});
1119+
}
10031120
#endif
1121+
}
10041122
}
1123+
else
1124+
UNREACHABLE;
10051125
}
1006-
else
1007-
UNREACHABLE;
1008-
}
1009-
1010-
void bv_pointerst::finish_eager_conversion()
1011-
{
1012-
// post-processing arrays may yield further objects, do this first
1013-
SUB::finish_eager_conversion();
1014-
1015-
for(postponed_listt::const_iterator
1016-
it=postponed_list.begin();
1017-
it!=postponed_list.end();
1018-
it++)
1019-
do_postponed(*it);
10201126

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

src/solvers/flattening/bv_pointers.h

+10-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,16 @@ class bv_pointerst:public boolbvt
8585
typedef std::list<postponedt> postponed_listt;
8686
postponed_listt postponed_list;
8787

88-
void do_postponed(const postponedt &postponed);
88+
/// Create Boolean functions describing all dynamic and all not-dynamic object
89+
/// encodings over \p placeholders as input Boolean variables representing
90+
/// object bits.
91+
std::pair<exprt, exprt> prepare_postponed_is_dynamic_object(
92+
std::vector<symbol_exprt> &placeholders) const;
93+
94+
/// Create Boolean functions describing all objects of each known object size
95+
/// over \p placeholders as input Boolean variables representing object bits.
96+
std::unordered_map<exprt, exprt, irep_hash>
97+
prepare_postponed_object_size(std::vector<symbol_exprt> &placeholders) const;
8998

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

0 commit comments

Comments
 (0)