Skip to content

Commit fd81e72

Browse files
committed
Unit test of byte operator lowering
The test attempts to systematically explore combinations of types, and shows a number of current bugs or limitations.
1 parent 381459d commit fd81e72

File tree

3 files changed

+313
-0
lines changed

3 files changed

+313
-0
lines changed

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ SRC += analyses/ai/ai.cpp \
3333
path_strategies.cpp \
3434
pointer-analysis/value_set.cpp \
3535
solvers/floatbv/float_utils.cpp \
36+
solvers/lowering/byte_operators.cpp \
3637
solvers/miniBDD/miniBDD.cpp \
3738
solvers/strings/array_pool/array_pool.cpp \
3839
solvers/strings/string_constraint_generator_valueof/calculate_max_string_length.cpp \
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
/*******************************************************************\
2+
3+
Module: Unit tests for byte_extract/byte_update lowering
4+
5+
Author: Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
#include <testing-utils/use_catch.h>
10+
11+
#include <solvers/lowering/expr_lowering.h>
12+
13+
#include <util/arith_tools.h>
14+
#include <util/byte_operators.h>
15+
#include <util/c_types.h>
16+
#include <util/cmdline.h>
17+
#include <util/config.h>
18+
#include <util/expr_util.h>
19+
#include <util/namespace.h>
20+
#include <util/pointer_offset_size.h>
21+
#include <util/simplify_expr.h>
22+
#include <util/simplify_expr_class.h>
23+
#include <util/std_types.h>
24+
#include <util/symbol_table.h>
25+
26+
SCENARIO("byte_extract_lowering", "[core][solvers][lowering][byte_extract]")
27+
{
28+
// this test does require a proper architecture to be set so that byte extract
29+
// uses adequate endianness
30+
cmdlinet cmdline;
31+
config.set(cmdline);
32+
33+
const symbol_tablet symbol_table;
34+
const namespacet ns(symbol_table);
35+
36+
GIVEN("A byte_extract over a POD")
37+
{
38+
const exprt deadbeef = from_integer(0xdeadbeef, unsignedbv_typet(32));
39+
const byte_extract_exprt be1(
40+
ID_byte_extract_little_endian,
41+
deadbeef,
42+
from_integer(1, index_type()),
43+
signedbv_typet(8));
44+
45+
THEN("byte_extract lowering yields the expected value")
46+
{
47+
const exprt lower_be1 = lower_byte_extract(be1, ns);
48+
const exprt lower_be1_s = simplify_expr(lower_be1, ns);
49+
50+
REQUIRE(!has_subexpr(lower_be1, ID_byte_extract_little_endian));
51+
REQUIRE(lower_be1.type() == be1.type());
52+
REQUIRE(lower_be1_s == from_integer(0xbe, signedbv_typet(8)));
53+
54+
byte_extract_exprt be2 = be1;
55+
be2.id(ID_byte_extract_big_endian);
56+
const exprt lower_be2 = lower_byte_extract(be2, ns);
57+
const exprt lower_be2_s = simplify_expr(lower_be2, ns);
58+
59+
REQUIRE(!has_subexpr(lower_be2, ID_byte_extract_big_endian));
60+
REQUIRE(lower_be2.type() == be2.type());
61+
REQUIRE(lower_be2_s == from_integer(0xad, signedbv_typet(8)));
62+
}
63+
}
64+
65+
GIVEN("A collection of types")
66+
{
67+
unsignedbv_typet u8(8);
68+
unsignedbv_typet u32(32);
69+
unsignedbv_typet u64(64);
70+
71+
exprt size = from_integer(8, size_type());
72+
73+
std::vector<typet> types = {
74+
struct_typet({{"comp1", u32}, {"comp2", u64}}),
75+
#if 0
76+
// not currently handled: components not byte aligned
77+
struct_typet({{"comp1", u32},
78+
{"compX", c_bit_field_typet(u8, 4)},
79+
{"pad", c_bit_field_typet(u8, 4)},
80+
{"comp2", u8}}),
81+
#endif
82+
union_typet({{"compA", u32}, {"compB", u64}}),
83+
c_enum_typet(unsignedbv_typet(16)),
84+
array_typet(u8, size),
85+
array_typet(u64, size),
86+
unsignedbv_typet(24),
87+
signedbv_typet(128),
88+
ieee_float_spect::single_precision().to_type(),
89+
pointer_typet(u64, 64),
90+
vector_typet(u8, size),
91+
vector_typet(u64, size),
92+
complex_typet(u32)
93+
};
94+
95+
simplify_exprt simp(ns);
96+
97+
THEN("byte_extract lowering yields the expected value")
98+
{
99+
for(const auto &t1 : types)
100+
{
101+
std::ostringstream oss;
102+
for(int i = 0; i < 64; ++i)
103+
oss << integer2binary(i, 8);
104+
105+
const auto type_bits = pointer_offset_bits(t1, ns);
106+
REQUIRE(type_bits);
107+
const auto type_bits_int = numeric_cast_v<std::size_t>(*type_bits);
108+
REQUIRE(type_bits_int <= oss.str().size());
109+
const exprt s =
110+
simp.bits2expr(oss.str().substr(0, type_bits_int), t1, true);
111+
REQUIRE(s.is_not_nil());
112+
113+
for(const auto &t2 : types)
114+
{
115+
oss.str("");
116+
for(int i = 2; i < 66; ++i)
117+
oss << integer2binary(i, 8);
118+
119+
const auto type_bits_2 = pointer_offset_bits(t2, ns);
120+
REQUIRE(type_bits_2);
121+
const auto type_bits_2_int =
122+
numeric_cast_v<std::size_t>(*type_bits_2);
123+
REQUIRE(type_bits_2_int <= oss.str().size());
124+
const exprt r =
125+
simp.bits2expr(oss.str().substr(0, type_bits_2_int), t2, true);
126+
REQUIRE(r.is_not_nil());
127+
128+
const byte_extract_exprt be1(
129+
ID_byte_extract_little_endian,
130+
s,
131+
from_integer(2, index_type()),
132+
t2);
133+
134+
const exprt lower_be1 = lower_byte_extract(be1, ns);
135+
const exprt lower_be1_s = simplify_expr(lower_be1, ns);
136+
137+
// TODO: does not currently hold
138+
// REQUIRE(!has_subexpr(lower_be1, ID_byte_extract_little_endian));
139+
REQUIRE(lower_be1.type() == be1.type());
140+
// TODO: does not currently hold
141+
// REQUIRE(lower_be1 == r);
142+
// TODO: does not currently hold
143+
// REQUIRE(lower_be1_s == r);
144+
145+
const byte_extract_exprt be2(
146+
ID_byte_extract_big_endian, s, from_integer(2, index_type()), t2);
147+
148+
const exprt lower_be2 = lower_byte_extract(be2, ns);
149+
const exprt lower_be2_s = simplify_expr(lower_be2, ns);
150+
151+
// TODO: does not currently hold
152+
REQUIRE(!has_subexpr(lower_be2, ID_byte_extract_big_endian));
153+
REQUIRE(lower_be2.type() == be2.type());
154+
// TODO: does not currently hold
155+
// REQUIRE(lower_be2 == r);
156+
// TODO: does not currently hold
157+
// REQUIRE(lower_be2_s == r);
158+
}
159+
}
160+
}
161+
}
162+
}
163+
164+
SCENARIO("byte_update_lowering", "[core][solvers][lowering][byte_update]")
165+
{
166+
// this test does require a proper architecture to be set so that byte extract
167+
// uses adequate endianness
168+
cmdlinet cmdline;
169+
config.set(cmdline);
170+
171+
const symbol_tablet symbol_table;
172+
const namespacet ns(symbol_table);
173+
174+
GIVEN("A byte_update of a POD")
175+
{
176+
const exprt deadbeef = from_integer(0xdeadbeef, unsignedbv_typet(32));
177+
const byte_update_exprt bu1(
178+
ID_byte_update_little_endian,
179+
deadbeef,
180+
from_integer(1, index_type()),
181+
from_integer(0x42, unsignedbv_typet(8)));
182+
183+
THEN("byte_update lowering yields the expected value")
184+
{
185+
const exprt lower_bu1 = lower_byte_operators(bu1, ns);
186+
const exprt lower_bu1_s = simplify_expr(lower_bu1, ns);
187+
188+
REQUIRE(!has_subexpr(lower_bu1, ID_byte_extract_little_endian));
189+
REQUIRE(!has_subexpr(lower_bu1, ID_byte_update_little_endian));
190+
REQUIRE(lower_bu1.type() == bu1.type());
191+
REQUIRE(lower_bu1_s == from_integer(0xdead42ef, unsignedbv_typet(32)));
192+
193+
#if 0
194+
// this is currently broken, #2058 will fix it
195+
byte_update_exprt bu2 = bu1;
196+
bu2.id(ID_byte_update_big_endian);
197+
const exprt lower_bu2 = lower_byte_operators(bu2, ns);
198+
const exprt lower_bu2_s = simplify_expr(lower_bu2, ns);
199+
200+
REQUIRE(!has_subexpr(lower_bu2, ID_byte_extract_big_endian));
201+
REQUIRE(!has_subexpr(lower_bu2, ID_byte_update_big_endian));
202+
REQUIRE(lower_bu2.type() == bu2.type());
203+
REQUIRE(lower_bu2_s == from_integer(0xde42beef, unsignedbv_typet(32)));
204+
#endif
205+
}
206+
}
207+
208+
GIVEN("A collection of types")
209+
{
210+
unsignedbv_typet u8(8);
211+
unsignedbv_typet u32(32);
212+
unsignedbv_typet u64(64);
213+
214+
exprt size = from_integer(8, size_type());
215+
216+
std::vector<typet> types = {
217+
// TODO: only arrays and scalars
218+
// struct_typet({{"comp1", u32}, {"comp2", u64}}),
219+
#if 0
220+
// not currently handled: components not byte aligned
221+
struct_typet({{"comp1", u32},
222+
{"compX", c_bit_field_typet(u8, 4)},
223+
{"pad", c_bit_field_typet(u8, 4)},
224+
{"comp2", u8}}),
225+
#endif
226+
// TODO: only arrays and scalars
227+
// union_typet({{"compA", u32}, {"compB", u64}}),
228+
// c_enum_typet(unsignedbv_typet(16)),
229+
array_typet(u8, size),
230+
array_typet(u64, size),
231+
unsignedbv_typet(24),
232+
signedbv_typet(128),
233+
ieee_float_spect::single_precision().to_type(),
234+
pointer_typet(u64, 64),
235+
// TODO: only arrays and scalars
236+
// vector_typet(u8, size),
237+
// vector_typet(u64, size),
238+
// complex_typet(u32)
239+
};
240+
241+
simplify_exprt simp(ns);
242+
243+
THEN("byte_update lowering yields the expected value")
244+
{
245+
for(const auto &t1 : types)
246+
{
247+
std::ostringstream oss;
248+
for(int i = 0; i < 64; ++i)
249+
oss << integer2binary(i, 8);
250+
251+
const auto type_bits = pointer_offset_bits(t1, ns);
252+
REQUIRE(type_bits);
253+
const auto type_bits_int = numeric_cast_v<std::size_t>(*type_bits);
254+
REQUIRE(type_bits_int <= oss.str().size());
255+
const exprt s =
256+
simp.bits2expr(oss.str().substr(0, type_bits_int), t1, true);
257+
REQUIRE(s.is_not_nil());
258+
259+
for(const auto &t2 : types)
260+
{
261+
oss.str("");
262+
for(int i = 64; i < 128; ++i)
263+
oss << integer2binary(i, 8);
264+
265+
const auto type_bits_2 = pointer_offset_bits(t2, ns);
266+
REQUIRE(type_bits_2);
267+
const auto type_bits_2_int =
268+
numeric_cast_v<std::size_t>(*type_bits_2);
269+
REQUIRE(type_bits_2_int <= oss.str().size());
270+
const exprt u =
271+
simp.bits2expr(oss.str().substr(0, type_bits_2_int), t2, true);
272+
REQUIRE(u.is_not_nil());
273+
274+
// TODO: currently required
275+
if(type_bits < type_bits_2)
276+
continue;
277+
278+
const byte_update_exprt bu1(
279+
ID_byte_update_little_endian, s, from_integer(2, index_type()), u);
280+
281+
const exprt lower_bu1 = lower_byte_operators(bu1, ns);
282+
const exprt lower_bu1_s = simplify_expr(lower_bu1, ns);
283+
284+
REQUIRE(!has_subexpr(lower_bu1, ID_byte_update_little_endian));
285+
REQUIRE(!has_subexpr(lower_bu1, ID_byte_extract_little_endian));
286+
REQUIRE(lower_bu1.type() == bu1.type());
287+
// TODO: does not currently hold
288+
// REQUIRE(lower_bu1 == u);
289+
// TODO: does not currently hold
290+
// REQUIRE(lower_bu1_s == u);
291+
292+
const byte_update_exprt bu2(
293+
ID_byte_update_big_endian, s, from_integer(2, index_type()), u);
294+
295+
const exprt lower_bu2 = lower_byte_operators(bu2, ns);
296+
const exprt lower_bu2_s = simplify_expr(lower_bu2, ns);
297+
298+
REQUIRE(!has_subexpr(lower_bu2, ID_byte_update_big_endian));
299+
REQUIRE(!has_subexpr(lower_bu2, ID_byte_extract_big_endian));
300+
REQUIRE(lower_bu2.type() == bu2.type());
301+
// TODO: does not currently hold
302+
// REQUIRE(lower_bu2 == u);
303+
// TODO: does not currently hold
304+
// REQUIRE(lower_bu2_s == u);
305+
}
306+
}
307+
}
308+
}
309+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
solvers/lowering
2+
testing-utils
3+
util

0 commit comments

Comments
 (0)