Skip to content

Commit 557d749

Browse files
Conversion and load i64 (#97)
* support conversion from integer to fp * support int64 memory load & fix caching for symbolic concrete
1 parent 099a711 commit 557d749

File tree

8 files changed

+323
-57
lines changed

8 files changed

+323
-57
lines changed

benchmarks/wasm/fp_ops.wat

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,30 @@
115115
local.get 3
116116
f64.le
117117
i32.eqz
118+
call 0
119+
120+
i32.const -1
121+
f32.convert_i32_s
122+
f32.const -1.0
123+
f32.eq
124+
call 0
125+
126+
i32.const -1
127+
f32.convert_i32_u
128+
f32.const 0x1p+32 (;=4294967296;)
129+
f32.eq
130+
call 0
131+
132+
i32.const -1
133+
f64.convert_i32_u
134+
f64.const 4294967295.0
135+
f64.eq
136+
call 0
137+
138+
i64.const -1
139+
f64.convert_i64_s
140+
f64.const -1.0
141+
f64.eq
118142
call 0)
119143

120144
;; symbolic branch guard for f32

benchmarks/wasm/mem-sym.wat

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,48 @@
1212
i32.load
1313
i32.const 25
1414
i32.eq
15-
if (result i32) ;; if x == 25
16-
i32.const 0
15+
if ;; if x == 25
16+
i32.const 0
1717
call 0 ;; assert false
18-
i32.const 1 ;; to satisfy the type checker, this line will never be reached
19-
else
20-
i32.const 1
21-
i32.load
22-
i32.const 1
23-
i32.eq
24-
if (result i32) ;; if x >> 8 == 1
25-
i32.const 0
26-
call 0 ;; assert false
27-
i32.const 1 ;; to satisfy the type checker, this line will never be reached
28-
else
29-
i32.const 1
30-
end
31-
i32.const 1
3218
end
19+
i32.const 1
20+
i32.load
21+
i32.const 1
22+
i32.eq
23+
if ;; if x >> 8 == 1
24+
i32.const 0
25+
call 0 ;; assert false
26+
end
27+
i32.const 4
28+
i64.load
29+
i64.eqz
30+
i32.eqz
31+
if
32+
i32.const 0
33+
call 0 ;; assert false
34+
end
35+
i32.const 0
36+
i64.load
37+
i64.const 32
38+
i64.shr_u
39+
i64.eqz
40+
i32.eqz
41+
if
42+
i32.const 0
43+
call 0 ;; assert false
44+
end
45+
i32.const 8
46+
i64.const 0x0102030405060708
47+
i64.store
48+
i32.const 8
49+
i64.load
50+
i64.const 0x0102030405060708
51+
i64.ne
52+
if
53+
i32.const 0
54+
call 0 ;; assert false
55+
end
56+
i32.const 1
3357
)
3458
(func (;2;) (type 1)
3559
i32.const 0
@@ -40,4 +64,4 @@
4064
(memory (;0;) 2)
4165
(export "main" (func 1))
4266
(global (;0;) (mut i32) (i32.const 42))
43-
)
67+
)

headers/wasm/concrete_num.hpp

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,10 +236,18 @@ struct Num {
236236
}
237237

238238
// i64.extend_i32_s: sign-extend low 32 bits to i64
239-
inline Num i32_extend_to_i64() const {
239+
inline Num i32_extend_to_i64_s() const {
240240
int64_t result_s = static_cast<int64_t>(this->toInt());
241241
Num res(result_s);
242-
debug_print("i32.extend_to_i64", *this, *this, res);
242+
debug_print("i32.extend_to_i64_s", *this, *this, res);
243+
return res;
244+
}
245+
246+
// i64.extend_i32_u: zero-extend low 32 bits to i64
247+
inline Num i32_extend_to_i64_u() const {
248+
uint64_t result_u = static_cast<uint64_t>(this->toUInt());
249+
Num res(static_cast<int64_t>(result_u));
250+
debug_print("i32.extend_to_i64_u", *this, *this, res);
243251
return res;
244252
}
245253

@@ -611,6 +619,26 @@ struct Num {
611619
return res;
612620
}
613621

622+
inline Num convert_i32_to_f32_s() const {
623+
uint32_t r_bits = f32_to_bits(static_cast<float>(toInt()));
624+
return Num(static_cast<int32_t>(r_bits));
625+
}
626+
627+
inline Num convert_i32_to_f32_u() const {
628+
uint32_t r_bits = f32_to_bits(static_cast<float>(toUInt()));
629+
return Num(static_cast<int32_t>(r_bits));
630+
}
631+
632+
inline Num convert_i64_to_f32_s() const {
633+
uint32_t r_bits = f32_to_bits(static_cast<float>(toInt64()));
634+
return Num(static_cast<int32_t>(r_bits));
635+
}
636+
637+
inline Num convert_i64_to_f32_u() const {
638+
uint32_t r_bits = f32_to_bits(static_cast<float>(toUInt64()));
639+
return Num(static_cast<int32_t>(r_bits));
640+
}
641+
614642
// f32.min / f32.max: follow wasm-ish semantics: if either is NaN, return NaN
615643
// (propagate)
616644
inline Num f32_min(const Num &other) const {
@@ -829,6 +857,26 @@ struct Num {
829857
return res;
830858
}
831859

860+
inline Num convert_i32_to_f64_s() const {
861+
uint64_t r_bits = f64_to_bits(static_cast<double>(toInt()));
862+
return Num(static_cast<int64_t>(r_bits));
863+
}
864+
865+
inline Num convert_i32_to_f64_u() const {
866+
uint64_t r_bits = f64_to_bits(static_cast<double>(toUInt()));
867+
return Num(static_cast<int64_t>(r_bits));
868+
}
869+
870+
inline Num convert_i64_to_f64_s() const {
871+
uint64_t r_bits = f64_to_bits(static_cast<double>(toInt64()));
872+
return Num(static_cast<int64_t>(r_bits));
873+
}
874+
875+
inline Num convert_i64_to_f64_u() const {
876+
uint64_t r_bits = f64_to_bits(static_cast<double>(toUInt64()));
877+
return Num(static_cast<int64_t>(r_bits));
878+
}
879+
832880
// f64.min / f64.max: follow wasm-ish semantics: if either is NaN, return
833881
// NaN (propagate)
834882
inline Num f64_min(const Num &other) const {

headers/wasm/concrete_rt.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,18 @@ struct Memory_t {
280280
return result;
281281
}
282282

283+
int64_t loadLong(int32_t base, int32_t offset) {
284+
int32_t addr = base + offset;
285+
if (!(addr + 7 < memory.size()) || addr < 0) {
286+
throw std::runtime_error("Invalid memory access " + std::to_string(addr));
287+
}
288+
int64_t result = 0;
289+
for (int i = 0; i < 8; ++i) {
290+
result |= static_cast<int64_t>(memory[addr + i]) << (8 * i);
291+
}
292+
return result;
293+
}
294+
283295
std::monostate storeInt(int32_t base, int32_t offset, int32_t value) {
284296
int32_t addr = base + offset;
285297
// Ensure we don't write out of bounds
@@ -296,6 +308,17 @@ struct Memory_t {
296308
return std::monostate{};
297309
}
298310

311+
std::monostate storeLong(int32_t base, int32_t offset, int64_t value) {
312+
int32_t addr = base + offset;
313+
if (!(addr + 7 < memory.size()) || addr < 0) {
314+
throw std::runtime_error("Invalid memory access " + std::to_string(addr));
315+
}
316+
for (int i = 0; i < 8; ++i) {
317+
memory[addr + i] = static_cast<uint8_t>((static_cast<uint64_t>(value) >> (8 * i)) & 0xFF);
318+
}
319+
return std::monostate{};
320+
}
321+
299322
std::monostate store_byte(int32_t addr, uint8_t value) {
300323
#ifdef DEBUG
301324
std::cout << "[Debug] storing byte " << std::to_string(value)

headers/wasm/sym_rt.hpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,48 @@ class SymMemory_t {
315315
#endif
316316
}
317317

318+
SymVal loadSymLong(int32_t base, int32_t offset) {
319+
#ifdef USE_IMM
320+
int32_t addr = base + offset;
321+
auto it = memory.find(addr);
322+
SymVal s0 = it ? *it : SVFactory::ZeroByte;
323+
it = memory.find(addr + 1);
324+
SymVal s1 = it ? *it : SVFactory::ZeroByte;
325+
it = memory.find(addr + 2);
326+
SymVal s2 = it ? *it : SVFactory::ZeroByte;
327+
it = memory.find(addr + 3);
328+
SymVal s3 = it ? *it : SVFactory::ZeroByte;
329+
it = memory.find(addr + 4);
330+
SymVal s4 = it ? *it : SVFactory::ZeroByte;
331+
it = memory.find(addr + 5);
332+
SymVal s5 = it ? *it : SVFactory::ZeroByte;
333+
it = memory.find(addr + 6);
334+
SymVal s6 = it ? *it : SVFactory::ZeroByte;
335+
it = memory.find(addr + 7);
336+
SymVal s7 = it ? *it : SVFactory::ZeroByte;
337+
#else
338+
int32_t addr = base + offset;
339+
auto it = memory.find(addr);
340+
SymVal s0 = (it != memory.end()) ? it->second : SVFactory::ZeroByte;
341+
it = memory.find(addr + 1);
342+
SymVal s1 = (it != memory.end()) ? it->second : SVFactory::ZeroByte;
343+
it = memory.find(addr + 2);
344+
SymVal s2 = (it != memory.end()) ? it->second : SVFactory::ZeroByte;
345+
it = memory.find(addr + 3);
346+
SymVal s3 = (it != memory.end()) ? it->second : SVFactory::ZeroByte;
347+
it = memory.find(addr + 4);
348+
SymVal s4 = (it != memory.end()) ? it->second : SVFactory::ZeroByte;
349+
it = memory.find(addr + 5);
350+
SymVal s5 = (it != memory.end()) ? it->second : SVFactory::ZeroByte;
351+
it = memory.find(addr + 6);
352+
SymVal s6 = (it != memory.end()) ? it->second : SVFactory::ZeroByte;
353+
it = memory.find(addr + 7);
354+
SymVal s7 = (it != memory.end()) ? it->second : SVFactory::ZeroByte;
355+
#endif
356+
357+
return s7.concat(s6).concat(s5).concat(s4).concat(s3).concat(s2).concat(s1).concat(s0);
358+
}
359+
318360
// when loading a symval, we need to concat 4 symbolic values
319361
// This sounds terribly bad for SMT...
320362
// Load a 4-byte symbolic value from memory
@@ -333,6 +375,27 @@ class SymMemory_t {
333375
return std::monostate{};
334376
}
335377

378+
std::monostate storeSymLong(int32_t base, int32_t offset, SymVal value) {
379+
int32_t addr = base + offset;
380+
SymVal s0 = value.extract(1, 1);
381+
SymVal s1 = value.extract(2, 2);
382+
SymVal s2 = value.extract(3, 3);
383+
SymVal s3 = value.extract(4, 4);
384+
SymVal s4 = value.extract(5, 5);
385+
SymVal s5 = value.extract(6, 6);
386+
SymVal s6 = value.extract(7, 7);
387+
SymVal s7 = value.extract(8, 8);
388+
storeSymByte(addr, s0);
389+
storeSymByte(addr + 1, s1);
390+
storeSymByte(addr + 2, s2);
391+
storeSymByte(addr + 3, s3);
392+
storeSymByte(addr + 4, s4);
393+
storeSymByte(addr + 5, s5);
394+
storeSymByte(addr + 6, s6);
395+
storeSymByte(addr + 7, s7);
396+
return std::monostate{};
397+
}
398+
336399
std::monostate storeSymByte(int32_t addr, SymVal value) {
337400
// assume the input value is 8-bit symbolic value
338401
bool exists;

headers/wasm/symval_factory.hpp

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22
#define WASM_SYMVAL_FACTORY_HPP
33

44
#include "heap_mem_bookkeeper.hpp"
5-
#include "symval_decl.hpp"
65
#include "symbolic_decl.hpp"
6+
#include "symval_decl.hpp"
77

88
namespace SVFactory {
99

@@ -116,7 +116,7 @@ struct UnaryOpKeyHash {
116116

117117
// Caches.
118118
static std::unordered_map<int, SymVal> SymbolStore;
119-
static std::unordered_map<int64_t, SymVal> concrete_pool;
119+
static std::unordered_map<int64_t, SymVal> FPStore;
120120
static std::unordered_map<SmallBVKey, SymVal, SmallBVKeyHash> SmallBVStore;
121121
static std::unordered_map<ExtractKey, SymVal, ExtractKeyHash>
122122
ExtractOperationStore;
@@ -126,26 +126,27 @@ static std::unordered_map<UnaryOpKey, SymVal, UnaryOpKeyHash>
126126

127127
// Factory implementations.
128128
inline SymVal make_concrete_bv(Num num, int width) {
129-
auto it = concrete_pool.find(num.toInt());
130-
if (it != concrete_pool.end()) {
129+
auto key = SmallBVKey(width, num.toInt64());
130+
auto it = SmallBVStore.find(key);
131+
if (it != SmallBVStore.end()) {
131132
return it->second;
132133
}
133134

134135
auto new_val =
135136
SymVal(SymBookKeeper.allocate<SymConcrete>(num, KindBV, width));
136-
concrete_pool.insert({num.toInt(), new_val});
137+
SmallBVStore.insert({key, new_val});
137138
return new_val;
138139
}
139140

140141
inline SymVal make_concrete_fp(Num num, int width) {
141-
auto it = concrete_pool.find(num.toInt());
142-
if (it != concrete_pool.end()) {
142+
auto it = FPStore.find(num.toInt64());
143+
if (it != FPStore.end()) {
143144
return it->second;
144145
}
145146

146147
auto new_val =
147148
SymVal(SymBookKeeper.allocate<SymConcrete>(num, KindFP, width));
148-
concrete_pool.insert({num.toInt(), new_val});
149+
FPStore.insert({num.toInt64(), new_val});
149150
return new_val;
150151
}
151152

@@ -205,7 +206,8 @@ inline SymVal make_extract(const SymVal &value, int high, int low) {
205206

206207
if (auto concrete = std::dynamic_pointer_cast<SymConcrete>(value.symptr)) {
207208
if (concrete->kind != KindBV) {
208-
throw std::runtime_error("Extract only supports bitvector concrete values");
209+
throw std::runtime_error(
210+
"Extract only supports bitvector concrete values");
209211
}
210212
// extract from concrete bitvector value
211213
int64_t val = concrete->value.value;
@@ -520,7 +522,8 @@ inline SymVal make_binary(BinOperation op, const SymVal &lhs,
520522
}
521523
}
522524

523-
auto result = SymVal(SVFactory::SymBookKeeper.allocate<SymBinary>(op, lhs, rhs));
525+
auto result =
526+
SymVal(SVFactory::SymBookKeeper.allocate<SymBinary>(op, lhs, rhs));
524527
BinaryOperationStore.insert({key, result});
525528
return result;
526529
}
@@ -594,8 +597,8 @@ inline SymVal make_unary(UnaryOperation op, const SymVal &value) {
594597
break;
595598
}
596599
if (negated_op != inner_binary->op) {
597-
auto result =
598-
SVFactory::make_binary(negated_op, inner_binary->lhs, inner_binary->rhs);
600+
auto result = SVFactory::make_binary(negated_op, inner_binary->lhs,
601+
inner_binary->rhs);
599602
UnaryOperationStore.insert({key, result});
600603
return result;
601604
}
@@ -609,7 +612,8 @@ inline SymVal make_unary(UnaryOperation op, const SymVal &value) {
609612

610613
inline SymVal make_concat(const SymVal &lhs, const SymVal &rhs) {
611614
if (auto lhs_concrete = std::dynamic_pointer_cast<SymConcrete>(lhs.symptr)) {
612-
if (auto rhs_concrete = std::dynamic_pointer_cast<SymConcrete>(rhs.symptr)) {
615+
if (auto rhs_concrete =
616+
std::dynamic_pointer_cast<SymConcrete>(rhs.symptr)) {
613617
if (lhs_concrete->kind == KindBV && rhs_concrete->kind == KindBV) {
614618
int new_width = lhs_concrete->width() + rhs_concrete->width();
615619
int64_t new_value =

0 commit comments

Comments
 (0)