55#include < util/bitvector_expr.h>
66#include < util/bitvector_types.h>
77#include < util/make_unique.h>
8+ #include < util/namespace.h>
9+ #include < util/range.h>
10+ #include < util/simplify_expr.h>
811
912#include < solvers/flattening/boolbv_width.h>
1013
14+ #include < algorithm>
15+ #include < numeric>
1116#include < queue>
1217
1318struct_encodingt::struct_encodingt (const namespacet &ns)
14- : boolbv_width{util_make_unique<boolbv_widtht>(ns)}
19+ : boolbv_width{util_make_unique<boolbv_widtht>(ns)}, ns{ns}
1520{
1621}
1722
1823struct_encodingt::struct_encodingt (const struct_encodingt &other)
19- : boolbv_width{util_make_unique<boolbv_widtht>(*other.boolbv_width )}
24+ : boolbv_width{util_make_unique<boolbv_widtht>(*other.boolbv_width )},
25+ ns{other.ns }
2026{
2127}
2228
@@ -45,6 +51,57 @@ typet struct_encodingt::encode(typet type) const
4551 return type;
4652}
4753
54+ // / \brief Extracts the component/field names and new values from a `with_exprt`
55+ // / which expresses a new struct value where one or more members of a
56+ // / struct have had their values substituted with new values.
57+ // / \note This is implemented using direct access to the operands and other
58+ // / underlying irept interfaces, because the interface for `with_exprt`
59+ // / only supports a single `where` / `new_value` pair and does not
60+ // / support extracting the name from the `where` operand.
61+ static std::unordered_map<irep_idt, exprt>
62+ extricate_updates (const with_exprt &struct_expr)
63+ {
64+ std::unordered_map<irep_idt, exprt> pairs;
65+ auto current_operand = struct_expr.operands ().begin ();
66+ // Skip the struct being updated in order to begin with the pairs.
67+ current_operand++;
68+ while (current_operand != struct_expr.operands ().end ())
69+ {
70+ INVARIANT (
71+ current_operand->id () == ID_member_name,
72+ " operand is expected to be the name of a member" );
73+ auto name = current_operand->find (ID_component_name).id ();
74+ ++current_operand;
75+ INVARIANT (
76+ current_operand != struct_expr.operands ().end (),
77+ " every name is expected to be followed by a paired value" );
78+ pairs[name] = *current_operand;
79+ ++current_operand;
80+ }
81+ POSTCONDITION (!pairs.empty ());
82+ return pairs;
83+ }
84+
85+ static exprt encode (const with_exprt &with, const namespacet &ns)
86+ {
87+ const auto tag_type = type_checked_cast<struct_tag_typet>(with.type ());
88+ const auto struct_type =
89+ type_checked_cast<struct_typet>(ns.follow (with.type ()));
90+ const auto updates = extricate_updates (with);
91+ const auto components =
92+ make_range (struct_type.components ())
93+ .map ([&](const struct_union_typet::componentt &component) -> exprt {
94+ const auto &update = updates.find (component.get_name ());
95+ if (update != updates.end ())
96+ return update->second ;
97+ else
98+ return member_exprt{
99+ with.old (), component.get_name (), component.type ()};
100+ })
101+ .collect <exprt::operandst>();
102+ return struct_exprt{components, tag_type};
103+ }
104+
48105static exprt encode (const struct_exprt &struct_expr)
49106{
50107 INVARIANT (
@@ -55,6 +112,49 @@ static exprt encode(const struct_exprt &struct_expr)
55112 return concatenation_exprt{struct_expr.operands (), struct_expr.type ()};
56113}
57114
115+ static std::size_t count_trailing_bit_width (
116+ const struct_typet &type,
117+ const irep_idt name,
118+ const boolbv_widtht &boolbv_width)
119+ {
120+ auto member_component_rit = std::find_if (
121+ type.components ().rbegin (),
122+ type.components ().rend (),
123+ [&](const struct_union_typet::componentt &component) {
124+ return component.get_name () == name;
125+ });
126+ INVARIANT (
127+ member_component_rit != type.components ().rend (),
128+ " Definition of struct type should include named component." );
129+ const auto trailing_widths =
130+ make_range (type.components ().rbegin (), member_component_rit)
131+ .map ([&](const struct_union_typet::componentt &component) -> std::size_t {
132+ return boolbv_width (component.type ());
133+ });
134+ return std::accumulate (
135+ trailing_widths.begin (), trailing_widths.end (), std::size_t {0 });
136+ }
137+
138+ // / The member expression selects the value of a field from a struct. The
139+ // / struct is encoded as a single bitvector where the first field is stored
140+ // / in the highest bits. The second field is stored in the next highest set of
141+ // / bits and so on. As offsets are indexed from the lowest bit, any field can be
142+ // / selected by extracting the range of bits starting from an offset based on
143+ // / the combined width of the fields which follow the field being selected.
144+ exprt struct_encodingt::encode_member (const member_exprt &member_expr) const
145+ {
146+ const auto &struct_type = type_checked_cast<struct_typet>(
147+ ns.get ().follow (member_expr.compound ().type ()));
148+ const std::size_t offset_bits = count_trailing_bit_width (
149+ struct_type, member_expr.get_component_name (), *boolbv_width);
150+ const auto member_bits_width = (*boolbv_width)(member_expr.type ());
151+ return extractbits_exprt{
152+ member_expr.compound (),
153+ offset_bits + member_bits_width - 1 ,
154+ offset_bits,
155+ member_expr.type ()};
156+ }
157+
58158exprt struct_encodingt::encode (exprt expr) const
59159{
60160 std::queue<exprt *> work_queue;
@@ -63,11 +163,46 @@ exprt struct_encodingt::encode(exprt expr) const
63163 {
64164 exprt ¤t = *work_queue.front ();
65165 work_queue.pop ();
166+ // Note that "with" expressions are handled before type encoding in order to
167+ // facilitate checking that they are applied to structs rather than arrays.
168+ if (const auto with_expr = expr_try_dynamic_cast<with_exprt>(current))
169+ if (can_cast_type<struct_tag_typet>(current.type ()))
170+ current = ::encode (*with_expr, ns);
66171 current.type () = encode (current.type ());
67172 if (const auto struct_expr = expr_try_dynamic_cast<struct_exprt>(current))
68173 current = ::encode (*struct_expr);
174+ if (const auto member_expr = expr_try_dynamic_cast<member_exprt>(current))
175+ current = encode_member (*member_expr);
69176 for (auto &operand : current.operands ())
70177 work_queue.push (&operand);
71178 }
72179 return expr;
73180}
181+
182+ exprt struct_encodingt::decode (
183+ const exprt &encoded,
184+ const struct_tag_typet &original_type) const
185+ {
186+ // The algorithm below works by extracting each of the separate fields from
187+ // the combined encoded value using a `member_exprt` which is itself encoded
188+ // into a `bit_extract_exprt`. These separated fields can then be combined
189+ // using a `struct_exprt`. This is expected to duplicate the input encoded
190+ // expression for each of the fields. However for the case where the input
191+ // encoded expression is a `constant_exprt`, expression simplification will be
192+ // able simplify away the duplicated portions of the constant and the bit
193+ // extraction expressions. This yields a clean struct expression where each
194+ // field is a separate constant containing the data solely relevant to that
195+ // field.
196+ INVARIANT (
197+ can_cast_type<bv_typet>(encoded.type ()),
198+ " Structs are expected to be encoded into bit vectors." );
199+ const struct_typet definition = ns.get ().follow_tag (original_type);
200+ exprt::operandst encoded_fields;
201+ for (const auto &component : definition.components ())
202+ {
203+ encoded_fields.push_back (typecast_exprt::conditional_cast (
204+ encode (member_exprt{typecast_exprt{encoded, original_type}, component}),
205+ component.type ()));
206+ }
207+ return simplify_expr (struct_exprt{encoded_fields, original_type}, ns);
208+ }
0 commit comments