@@ -18,6 +18,10 @@ 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>
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 ();
0 commit comments