5
5
#include < util/bitvector_expr.h>
6
6
#include < util/bitvector_types.h>
7
7
#include < util/make_unique.h>
8
+ #include < util/namespace.h>
9
+ #include < util/range.h>
10
+ #include < util/simplify_expr.h>
8
11
9
12
#include < solvers/flattening/boolbv_width.h>
10
13
14
+ #include < algorithm>
15
+ #include < numeric>
11
16
#include < queue>
12
17
13
18
struct_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}
15
20
{
16
21
}
17
22
18
23
struct_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 }
20
26
{
21
27
}
22
28
@@ -45,6 +51,57 @@ typet struct_encodingt::encode(typet type) const
45
51
return type;
46
52
}
47
53
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
+
48
105
static exprt encode (const struct_exprt &struct_expr)
49
106
{
50
107
INVARIANT (
@@ -55,6 +112,49 @@ static exprt encode(const struct_exprt &struct_expr)
55
112
return concatenation_exprt{struct_expr.operands (), struct_expr.type ()};
56
113
}
57
114
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
+
58
158
exprt struct_encodingt::encode (exprt expr) const
59
159
{
60
160
std::queue<exprt *> work_queue;
@@ -63,11 +163,46 @@ exprt struct_encodingt::encode(exprt expr) const
63
163
{
64
164
exprt ¤t = *work_queue.front ();
65
165
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);
66
171
current.type () = encode (current.type ());
67
172
if (const auto struct_expr = expr_try_dynamic_cast<struct_exprt>(current))
68
173
current = ::encode (*struct_expr);
174
+ if (const auto member_expr = expr_try_dynamic_cast<member_exprt>(current))
175
+ current = encode_member (*member_expr);
69
176
for (auto &operand : current.operands ())
70
177
work_queue.push (&operand);
71
178
}
72
179
return expr;
73
180
}
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