Branch data Line data Source code
1 : : #include "backend/WasmDSL.hpp"
2 : :
3 : : #include "backend/WasmMacro.hpp"
4 : : #include <mutable/catalog/Catalog.hpp>
5 : : #include <mutable/catalog/Schema.hpp>
6 : : #ifdef __BMI2__
7 : : #include <immintrin.h>
8 : : #endif
9 : :
10 : :
11 : : using namespace m;
12 : : using namespace m::wasm;
13 : :
14 : :
15 : : namespace {
16 : :
17 : : __attribute__((constructor(201)))
18 : 1 : static void add_wasm_dsl_args()
19 : : {
20 : 1 : Catalog &C = Catalog::Get();
21 : :
22 : : #if !defined(NDEBUG) && defined(M_ENABLE_SANITY_FIELDS)
23 : : /*----- Command-line arguments -----*/
24 : : C.arg_parser().add<bool>(
25 : : /* group= */ "Wasm",
26 : : /* short= */ nullptr,
27 : : /* long= */ "--insist-no-ternary-logic",
28 : : /* description= */ "insist that there is no ternary logic, i.e. NULL value computation",
29 : : /* callback= */ [](bool){ dsl_options::insist_no_ternary_logic = true; }
30 : : );
31 : : #endif
32 : 1 : }
33 : :
34 : : }
35 : :
36 : :
37 : : /*======================================================================================================================
38 : : * ConstantFolding
39 : : *====================================================================================================================*/
40 : :
41 : : #define CASE(TYPE, BLOCK) \
42 : : case ::wasm::Expression::TYPE##Id: { \
43 : : auto _expr = expr; \
44 : : auto *expr = static_cast<const ::wasm::TYPE*>(_expr); \
45 : : BLOCK \
46 : : break; \
47 : : }
48 : :
49 : 7842 : ConstantFolding::boolean_result_t ConstantFolding::EvalBoolean(const ::wasm::Expression *expr)
50 : : {
51 [ + + + + ]: 7842 : switch (expr->_id) {
52 : : default:
53 : 14 : break;
54 : 6 : CASE(Const, {
55 : : M_insist(expr->value.type == ::wasm::Type::i32, "invalid boolean expression");
56 : : return expr->value.geti32() ? TRUE : FALSE;
57 : : })
58 [ - + - - : 4494 : CASE(Unary, {
+ - ]
59 : : if (expr->op == ::wasm::UnaryOp::EqZInt32) { // negation
60 : : switch (EvalBoolean(expr->value)) {
61 : : case UNDEF:
62 : : return UNDEF;
63 : : case TRUE:
64 : : return FALSE;
65 : : case FALSE:
66 : : return TRUE;
67 : 0 : }
68 : : }
69 : : })
70 [ + + - - : 6502 : CASE(Binary, {
+ - - + -
+ - # # #
# # # # ]
71 : : if (expr->op == ::wasm::BinaryOp::AndInt32) { // conjunction
72 : : switch (EvalBoolean(expr->left)) {
73 : : case UNDEF:
74 : 1 : switch (EvalBoolean(expr->right)) {
75 : : case UNDEF:
76 : : case TRUE:
77 : : return UNDEF;
78 : : case FALSE: // dominating element
79 : : return FALSE;
80 : : }
81 : : case TRUE: // neutral element
82 : : return EvalBoolean(expr->right);
83 : : case FALSE: // dominating element
84 : : return FALSE;
85 : : }
86 : : } else if (expr->op == ::wasm::BinaryOp::OrInt32) { // disjunction
87 : : switch (EvalBoolean(expr->left)) {
88 : : case UNDEF:
89 : : switch (EvalBoolean(expr->right)) {
90 : : case UNDEF:
91 : : case FALSE:
92 : : return UNDEF;
93 : : case TRUE: // dominating element
94 : : return TRUE;
95 : : }
96 : : case TRUE: // dominating element
97 : : return TRUE;
98 : : case FALSE: // neutral element
99 : : return EvalBoolean(expr->right);
100 : : }
101 : : }
102 : : })
103 : : }
104 : 3188 : return UNDEF;
105 : 7842 : }
106 : :
107 : : #undef CASE
108 : :
109 : :
110 : : /*======================================================================================================================
111 : : * MockInterface
112 : : *====================================================================================================================*/
113 : :
114 : : struct MockInterface final : ::wasm::ModuleRunner::ExternalInterface
115 : : {
116 : : using base_type = ::wasm::ModuleRunner::ExternalInterface;
117 : :
118 : : private:
119 : : ///> the underlying virtual address space used
120 : : const memory::AddressSpace &memory_;
121 : : ///> the given imports
122 : : ::wasm::GlobalValueSet imports_;
123 : :
124 : : public:
125 [ + - ]: 829 : MockInterface(const memory::AddressSpace &memory, ::wasm::GlobalValueSet imports = {})
126 : 829 : : memory_(memory), imports_(std::move(imports))
127 : 1658 : { }
128 : :
129 : 829 : void importGlobals(::wasm::GlobalValueSet &globals, ::wasm::Module&) override {
130 : 829 : M_insist(globals.empty(), "globals can only be imported once");
131 : 829 : globals = std::move(imports_);
132 : 829 : }
133 : 0 : ::wasm::Literals callImport(::wasm::Function *f, ::wasm::Literals &args) override {
134 [ # # ]: 0 : if (auto it = callback_functions.find(f->name); it != callback_functions.end())
135 : 0 : return it->second(args);
136 : : else
137 : 0 : M_unreachable("callback function not found");
138 : : }
139 : 0 : ::wasm::Literals callTable(::wasm::Name,
140 : : ::wasm::Index,
141 : : ::wasm::HeapType,
142 : : ::wasm::Literals&,
143 : : ::wasm::Type,
144 : 0 : ::wasm::ModuleRunner&) override { M_unreachable("not supported"); }
145 : 0 : bool growMemory(::wasm::Name, ::wasm::Address, ::wasm::Address) override { M_unreachable("not supported"); }
146 : 0 : bool growTable(::wasm::Name,
147 : : const ::wasm::Literal&,
148 : : ::wasm::Index,
149 : 0 : ::wasm::Index) override { M_unreachable("not implemented"); }
150 : 0 : ::wasm::Index tableSize(::wasm::Name) override { M_unreachable("not implemented"); }
151 : 0 : void trap(const char*) override { M_unreachable("not supported"); }
152 : 0 : void hostLimit(const char*) override { M_unreachable("not supported"); }
153 : 0 : void throwException(const ::wasm::WasmException&) override { M_unreachable("not supported"); }
154 : :
155 : : private:
156 : : template<typename T = void>
157 : 613 : T _load(::wasm::Address addr) {
158 : 613 : M_insist(addr.addr < memory_.size(), "invalid address");
159 : 613 : M_insist(addr.addr % alignof(T) == 0, "misaligned address");
160 : 613 : return *reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(memory_.addr()) + addr.addr);
161 : : }
162 : : template<>
163 : 1 : std::array<uint8_t, 16> _load<std::array<uint8_t, 16>>(::wasm::Address _addr) {
164 : 1 : M_insist(_addr.addr + 16 <= memory_.size(), "invalid address");
165 : 1 : auto addr = reinterpret_cast<uint8_t*>(memory_.addr()) + _addr.addr;
166 : 1 : return std::to_array<uint8_t, 16>(*reinterpret_cast<uint8_t(*)[16]>(addr));
167 : : }
168 : : template<typename T = void>
169 : 283 : void _store(::wasm::Address addr, T value) {
170 : 283 : M_insist(addr.addr < memory_.size(), "invalid address");
171 : 283 : M_insist(addr.addr % alignof(T) == 0, "misaligned address");
172 : 283 : *reinterpret_cast<T*>(reinterpret_cast<uint8_t*>(memory_.addr()) + addr.addr) = value;
173 : 283 : }
174 : : template<>
175 : 1 : void _store<const std::array<uint8_t, 16>&>(::wasm::Address addr, const std::array<uint8_t, 16> &value) {
176 : 1 : M_insist(addr.addr + 16 <= memory_.size(), "invalid address");
177 [ + + ]: 17 : for (uint32_t idx = 0; idx < 16; ++idx)
178 : 16 : *(reinterpret_cast<uint8_t*>(memory_.addr()) + addr.addr + idx) = value[idx];
179 : 1 : }
180 : :
181 : : public:
182 : : #define DECLARE_LOAD(BINARYEN_TYPE, C_TYPE) \
183 : : C_TYPE load##BINARYEN_TYPE(::wasm::Address addr, ::wasm::Name) override { \
184 : : return _load<C_TYPE>(addr); \
185 : : }
186 : 420 : DECLARE_LOAD(8s, int8_t)
187 : 141 : DECLARE_LOAD(8u, uint8_t)
188 : 0 : DECLARE_LOAD(16s, int16_t)
189 : 0 : DECLARE_LOAD(16u, uint16_t)
190 : 5 : DECLARE_LOAD(32s, int32_t)
191 : 23 : DECLARE_LOAD(32u, uint32_t)
192 : 23 : DECLARE_LOAD(64s, int64_t)
193 : 1 : DECLARE_LOAD(64u, uint64_t)
194 : 1 : DECLARE_LOAD(128, std::array<M_COMMA(uint8_t) 16>)
195 : : #undef DECLARE_LOAD
196 : :
197 : : #define DECLARE_STORE(BINARYEN_TYPE, C_TYPE) \
198 : : void store##BINARYEN_TYPE(::wasm::Address addr, C_TYPE value, ::wasm::Name) override { \
199 : : return _store<C_TYPE>(addr, value); \
200 : : }
201 : 275 : DECLARE_STORE(8, int8_t)
202 : 1 : DECLARE_STORE(16, int16_t)
203 : 6 : DECLARE_STORE(32, int32_t)
204 : 1 : DECLARE_STORE(64, int64_t)
205 : 1 : DECLARE_STORE(128, const std::array<M_COMMA(uint8_t) 16>&)
206 : : #undef DECLARE_STORE
207 : : };
208 : :
209 : :
210 : : /*======================================================================================================================
211 : : * LinearAllocator
212 : : *====================================================================================================================*/
213 : :
214 : : /** A simple linear allocator which keeps a global pointer to the next free memory address and advances it for
215 : : * allocation. Deallocation can only reclaim memory if all chronologically later allocations have been deallocated
216 : : * before. If possible, deallocate memory in the inverse order of allocation. */
217 : : struct LinearAllocator : Allocator
218 : : {
219 : : private:
220 : : ///> the underlying virtual address space used
221 : : const memory::AddressSpace &memory_;
222 : : ///> flag whether pre-allocations were already performed, i.e. `perform_pre_allocations()` was already called
223 : 1654 : bool pre_allocations_performed_ = false;
224 : : ///> compile-time size of the currently used memory, used as pointer to next free pre-allocation
225 : : uint32_t pre_alloc_addr_;
226 : : ///> runtime global size of the currently used memory, used as pointer to next free allocation
227 : : Global<U32x1> alloc_addr_;
228 : : ///> compile-time total memory consumption
229 : 1654 : uint32_t pre_alloc_total_mem_ = 0;
230 : : ///> runtime total memory consumption
231 : : Global<U32x1> alloc_total_mem_;
232 : : ///> runtime peak memory consumption
233 : : Global<U32x1> alloc_peak_mem_;
234 : :
235 : : public:
236 [ + - + - : 3308 : LinearAllocator(const memory::AddressSpace &memory, uint32_t start_addr)
+ - + - ]
237 : 1654 : : memory_(memory)
238 : 1654 : , pre_alloc_addr_(start_addr)
239 : 1654 : {
240 [ - + ]: 1654 : M_insist(start_addr != 0, "memory address 0 is reserved as `nullptr`");
241 : : #ifdef M_ENABLE_SANITY_FIELDS
242 : : alloc_addr_.val().discard(); // artificial use of `alloc_addr_` to silence diagnostics if allocator is not used
243 : : alloc_total_mem_.val().discard(); // artificial use of `alloc_total_mem_` to silence diagnostics if allocator is not used
244 : : alloc_peak_mem_.val().discard(); // artificial use of `alloc_peak_mem_` to silence diagnostics if allocator is not used
245 : : #endif
246 : 1654 : }
247 : :
248 : 3308 : ~LinearAllocator() {
249 [ + - ]: 1654 : M_insist(pre_allocations_performed_, "must call `perform_pre_allocations()` before destruction");
250 : 3308 : }
251 : :
252 : 0 : void * raw_allocate(uint32_t bytes, uint32_t alignment) override {
253 : 0 : M_insist(not pre_allocations_performed_,
254 : : "must not request a pre-allocation after `perform_pre_allocations()` was already called");
255 : 0 : M_insist(alignment);
256 : 0 : M_insist(is_pow_2(alignment), "alignment must be a power of 2");
257 [ # # ]: 0 : if (alignment != 1U)
258 : 0 : align_pre_memory(alignment);
259 : 0 : void *ptr = static_cast<uint8_t*>(memory_.addr()) + pre_alloc_addr_;
260 : 0 : pre_alloc_addr_ += bytes; // advance memory size by bytes
261 : 0 : pre_alloc_total_mem_ += bytes;
262 : 0 : M_insist(memory_.size() >= pre_alloc_addr_, "allocation must fit in memory");
263 : 0 : return ptr;
264 : : }
265 : 124 : Ptr<void> pre_allocate(uint32_t bytes, uint32_t alignment) override {
266 : 124 : M_insist(not pre_allocations_performed_,
267 : : "must not request a pre-allocation after `perform_pre_allocations()` was already called");
268 : 124 : M_insist(alignment);
269 : 124 : M_insist(is_pow_2(alignment), "alignment must be a power of 2");
270 [ + + ]: 124 : if (alignment != 1U)
271 : 56 : align_pre_memory(alignment);
272 [ + - ]: 124 : Ptr<void> ptr(U32x1(pre_alloc_addr_).template to<void*>());
273 : 124 : pre_alloc_addr_ += bytes; // advance memory size by bytes
274 : 124 : pre_alloc_total_mem_ += bytes;
275 [ + - ]: 124 : M_insist(memory_.size() >= pre_alloc_addr_, "allocation must fit in memory");
276 : 124 : return ptr;
277 [ + - ]: 124 : }
278 : 196 : Var<Ptr<void>> allocate(U32x1 bytes, uint32_t alignment) override {
279 : 196 : M_insist(alignment);
280 : 196 : M_insist(is_pow_2(alignment), "alignment must be a power of 2");
281 [ + + ]: 196 : if (alignment != 1U)
282 : 58 : align_memory(alignment);
283 [ + - ]: 196 : Var<Ptr<void>> ptr(alloc_addr_.template to<void*>());
284 [ + - + - ]: 196 : alloc_addr_ += bytes.clone(); // advance memory size by bytes
285 [ + - ]: 196 : alloc_total_mem_ += bytes;
286 [ + - + - : 196 : alloc_peak_mem_ = Select(alloc_peak_mem_ > alloc_addr_, alloc_peak_mem_, alloc_addr_);
+ - ]
287 [ + - + - : 196 : Wasm_insist(memory_.size() >= alloc_addr_, "allocation must fit in memory");
+ - ]
288 : 196 : return ptr;
289 [ + - ]: 196 : }
290 : :
291 : 2 : void deallocate(Ptr<void> ptr, U32x1 bytes) override {
292 [ + - + - : 2 : Wasm_insist(ptr.clone().template to<uint32_t>() < alloc_addr_, "must not try to free unallocated memory");
+ - ]
293 [ + - + - : 4 : IF (ptr.template to<uint32_t>() + bytes.clone() == alloc_addr_) { // last allocation can be freed
+ - - + ]
294 : 2 : alloc_addr_ -= bytes; // free by decreasing memory size
295 : 2 : };
296 : 2 : }
297 : :
298 : 1654 : uint32_t perform_pre_allocations() override {
299 : 1654 : M_insist(not pre_allocations_performed_,
300 : : "must not call `perform_pre_allocations()` multiple times");
301 [ + - ]: 1654 : alloc_addr_.init(Module::Get().get_global<uint32_t>("alloc_addr_init"));
302 : 1654 : pre_allocations_performed_ = true;
303 : 1654 : return pre_alloc_addr_;
304 : 0 : }
305 : :
306 : 0 : uint32_t pre_allocated_memory_consumption() const override { return pre_alloc_total_mem_; }
307 : 0 : U32x1 allocated_memory_consumption() const override { return alloc_total_mem_; }
308 : 0 : U32x1 allocated_memory_peak() const override { return alloc_peak_mem_; }
309 : :
310 : : private:
311 : : /** Aligns the memory for pre-allocations with alignment requirement `align`. */
312 : 56 : void align_pre_memory(uint32_t alignment) {
313 : 56 : M_insist(is_pow_2(alignment));
314 : 56 : pre_alloc_addr_ = (pre_alloc_addr_ + (alignment - 1U)) bitand ~(alignment - 1U);
315 : 56 : }
316 : : /** Aligns the memory for allocations with alignment requirement `align`. */
317 : 58 : void align_memory(uint32_t alignment) {
318 : 58 : M_insist(is_pow_2(alignment));
319 [ + - - + ]: 58 : alloc_addr_ = (alloc_addr_ + (alignment - 1U)) bitand ~(alignment - 1U);
320 : 58 : }
321 : : };
322 : :
323 : :
324 : : /*======================================================================================================================
325 : : * Module
326 : : *====================================================================================================================*/
327 : :
328 : : thread_local std::unique_ptr<Module> Module::the_module_;
329 : :
330 : 1658 : Module::Module()
331 : 1658 : : id_(NEXT_MODULE_ID_.fetch_add(1U, std::memory_order_relaxed))
332 : 1658 : , module_()
333 [ + - ]: 1658 : , builder_(module_)
334 : : {
335 : : /*----- Import functions from the environment. -----*/
336 [ + - ]: 1658 : emit_function_import<void(uint64_t)>("insist"); // to implement insist at Wasm site
337 [ + - ]: 1658 : emit_function_import<void(uint64_t, uint64_t)>("throw"); // to throw exceptions from Wasm site
338 : :
339 : : /*----- Create module memory. -----*/
340 [ + - ]: 1658 : ::wasm::Name memory_name = std::to_string(id_);
341 : : {
342 [ + - ]: 1658 : auto mem = std::make_unique<::wasm::Memory>();
343 [ + - ]: 1658 : mem->name = memory_name;
344 [ + - ]: 1658 : module_.addMemory(std::move(mem));
345 : 1658 : }
346 : :
347 : : /*----- Export the Wasm linear memory, s.t. it can be accessed from the environment (JavaScript). -----*/
348 [ + - ]: 1658 : memory_ = module_.getMemory(memory_name);
349 [ + - ]: 1658 : memory_->initial = 1; // otherwise the Binaryen interpreter traps
350 [ + - ]: 1658 : memory_->max = int32_t(WasmEngine::WASM_MAX_MEMORY / WasmEngine::WASM_PAGE_SIZE);
351 [ + - ]: 3316 : module_.exports.emplace_back(
352 [ + - + - ]: 1658 : builder_.makeExport("memory", memory_->name, ::wasm::ExternalKind::Memory)
353 : : );
354 : :
355 : : /*----- Set features. -----*/
356 [ + - ]: 1658 : module_.features.setBulkMemory(true);
357 [ + - ]: 1658 : module_.features.setSIMD(true);
358 : 1658 : }
359 : :
360 : 829 : ::wasm::ModuleRunner::ExternalInterface * Module::get_mock_interface(::wasm::GlobalValueSet imports)
361 : : {
362 [ - + ]: 829 : if (not interface_) [[unlikely]]
363 : 829 : interface_ = std::make_unique<MockInterface>(Memory(), std::move(imports));
364 : 829 : return interface_.get();
365 : : }
366 : :
367 : 1656 : bool Module::Validate(bool verbose, bool global)
368 : : {
369 : 1656 : ::wasm::WasmValidator::Flags flags(0);
370 [ + - ]: 1656 : if (not verbose) flags |= ::wasm::WasmValidator::Quiet;
371 [ - + ]: 1656 : if (global) flags |= ::wasm::WasmValidator::Globally;
372 : 1656 : return ::wasm::WasmValidator{}.validate(Get().module_, flags);
373 : : }
374 : :
375 : 0 : void Module::Optimize(int optimization_level)
376 : : {
377 : 0 : ::wasm::PassOptions options;
378 : 0 : options.optimizeLevel = optimization_level;
379 : 0 : options.shrinkLevel = 0; // shrinking not required
380 [ # # # # : 0 : ::wasm::PassRunner runner(&Get().module_, options);
# # ]
381 [ # # ]: 0 : runner.addDefaultOptimizationPasses();
382 [ # # ]: 0 : runner.run();
383 : 0 : }
384 : :
385 : 827 : std::pair<uint8_t*, std::size_t> Module::binary()
386 : : {
387 : 827 : ::wasm::BufferWithRandomAccess buffer;
388 [ + - ]: 827 : ::wasm::WasmBinaryWriter writer(&module_, buffer);
389 [ + - ]: 827 : writer.setNamesSection(false);
390 [ + - ]: 827 : writer.write();
391 : 827 : void *binary = malloc(buffer.size());
392 [ + - ]: 827 : std::copy_n(buffer.begin(), buffer.size(), static_cast<char*>(binary));
393 [ + - ]: 827 : return std::make_pair(reinterpret_cast<uint8_t*>(binary), buffer.size());
394 : 827 : }
395 : :
396 : : template<std::size_t L>
397 : 0 : void Module::emit_insist(PrimitiveExpr<bool, L> cond, const char *filename, unsigned line, const char *msg)
398 : : {
399 [ # # # # : 0 : emit_insist(cond.all_true(), filename, line, msg);
# # # # #
# ]
400 : 0 : }
401 : :
402 : : // explicit instantiations to prevent linker errors
403 : : template void Module::emit_insist(PrimitiveExpr<bool, 2>, const char*, unsigned, const char*);
404 : : template void Module::emit_insist(PrimitiveExpr<bool, 4>, const char*, unsigned, const char*);
405 : : template void Module::emit_insist(PrimitiveExpr<bool, 8>, const char*, unsigned, const char*);
406 : : template void Module::emit_insist(PrimitiveExpr<bool, 16>, const char*, unsigned, const char*);
407 : : template void Module::emit_insist(PrimitiveExpr<bool, 32>, const char*, unsigned, const char*);
408 : :
409 : : template<>
410 : 2076 : void Module::emit_insist(PrimitiveExpr<bool, 1> cond, const char *filename, unsigned line, const char *msg)
411 : : {
412 : : static thread_local struct {} _; // unique caller handle
413 : : struct data_t : GarbageCollectedData
414 : : {
415 : : public:
416 : : std::optional<FunctionProxy<void(uint64_t)>> delegate_insist;
417 : :
418 : 2076 : data_t(GarbageCollectedData &&d) : GarbageCollectedData(std::move(d)) { }
419 : : };
420 : 2076 : auto &d = add_garbage_collected_data<data_t>(&_); // garbage collect the `data_t` instance
421 : :
422 [ + + ]: 2076 : if (not d.delegate_insist) {
423 : : /*----- Create function to delegate to host (used for easier debugging since one can break in here). -----*/
424 [ + - + - : 526 : FUNCTION(delegate_insist, void(uint64_t))
+ - ]
425 : : {
426 [ + - + - : 263 : emit_call<void>("insist", PARAMETER(0).val());
- + ]
427 : : }
428 : 263 : d.delegate_insist = std::move(delegate_insist);
429 : 263 : }
430 : :
431 : 2076 : uint64_t idx = messages_.size();
432 : 2076 : messages_.emplace_back(filename, line, msg);
433 : :
434 : : /*----- Check condition and possibly delegate to host. --*/
435 : 2076 : M_insist(bool(d.delegate_insist));
436 [ + - ]: 4152 : IF (not cond) {
437 : 2076 : (*d.delegate_insist)(idx);
438 : 2076 : };
439 : 2076 : }
440 : :
441 : 580 : void Module::emit_throw(exception::exception_t type, const char *filename, unsigned line, const char *msg)
442 : : {
443 : 580 : uint64_t idx = messages_.size();
444 : 580 : messages_.emplace_back(filename, line, msg);
445 [ - + ]: 1160 : std::vector<::wasm::Expression*> args = {
446 [ + - ]: 580 : builder_.makeConst(::wasm::Literal(type)), // type id
447 [ + - + - ]: 580 : builder_.makeConst(::wasm::Literal(idx)) // message index
448 : : };
449 [ + - + - : 580 : active_block_->list.push_back(builder_.makeCall("throw", args, wasm_type<void, 1>()));
+ - + - ]
450 : 580 : }
451 : :
452 : : /** Emit an unconditional continue, continuing \p level levels above. */
453 : 212 : void Module::emit_continue(std::size_t level)
454 : : {
455 : 212 : M_insist(level > 0);
456 : 212 : M_insist(branch_target_stack_.size() >= level);
457 : 212 : auto &branch_targets = branch_target_stack_[branch_target_stack_.size() - level];
458 [ + + ]: 212 : if (branch_targets.condition) {
459 [ + - ]: 120 : PrimitiveExpr<bool, 1> condition(
460 : 120 : ::wasm::ExpressionManipulator::copy(branch_targets.condition, Module::Get().module_)
461 : : );
462 : : /* Continue if condition is satisfied, break otherwise. */
463 [ + - ]: 240 : IF (condition) {
464 : 120 : active_block_->list.push_back(builder_.makeBreak(branch_targets.continu));
465 : 360 : } ELSE {
466 : 120 : BREAK();
467 : 120 : };
468 : 120 : } else {
469 : : /* Continue unconditionally. */
470 : 92 : active_block_->list.push_back(builder_.makeBreak(branch_targets.continu));
471 : : }
472 : 212 : }
473 : :
474 : : /** Emit an *conditional* continue, continuing \p level levels above if \p cond evaluates to `true` *and* the original
475 : : * continue condition evaluates to `true`. */
476 : 20 : void Module::emit_continue(PrimitiveExpr<bool, 1> cond, std::size_t level)
477 : : {
478 : 20 : M_insist(level > 0);
479 : 20 : M_insist(branch_target_stack_.size() >= level);
480 : 40 : IF (cond) {
481 : 20 : emit_continue(level);
482 : 20 : };
483 : 20 : }
484 : :
485 : 2483 : memory::AddressSpace & Module::Memory()
486 : : {
487 [ + + ]: 2483 : if (WasmEngine::Has_Wasm_Context(ID())) {
488 : 139 : return WasmEngine::Get_Wasm_Context_By_ID(ID()).vm;
489 : : } else {
490 [ + + ]: 2344 : if (not Get().vm_) [[unlikely]] {
491 : 1522 : memory::AddressSpace vm(WasmEngine::WASM_MAX_MEMORY);
492 [ + - + - : 1522 : auto mem = Catalog::Get().allocator().allocate(vm.size());
+ - + - ]
493 [ + - + - ]: 1522 : mem.map(vm.size(), 0, vm, 0); // map backed memory into vm
494 [ + - - + ]: 1522 : Get().vm_ = std::make_unique<std::pair<memory::AddressSpace, memory::Memory>>(std::move(vm), std::move(mem));
495 : 1522 : }
496 : 2344 : return Get().vm_->first;
497 : : }
498 : 2483 : }
499 : :
500 : 1976 : Allocator & Module::Allocator()
501 : : {
502 [ + + ]: 1976 : if (not Get().allocator_) [[unlikely]] {
503 [ + + ]: 1654 : if (WasmEngine::Has_Wasm_Context(ID()))
504 [ + - ]: 134 : Get().allocator_ = std::make_unique<LinearAllocator>(Memory(), WasmEngine::Get_Wasm_Context_By_ID(ID()).heap);
505 : : else
506 [ + - ]: 1520 : Get().allocator_ = std::make_unique<LinearAllocator>(Memory(), 1); // reserve address 0 for `nullptr`
507 : 1654 : }
508 : 1976 : return *Get().allocator_;
509 : 0 : }
510 : :
511 : :
512 : : /*======================================================================================================================
513 : : * Callback functions
514 : : *====================================================================================================================*/
515 : :
516 : 0 : ::wasm::Literals m::wasm::insist_interpreter(::wasm::Literals &args)
517 : : {
518 : 0 : M_insist(args.size() == 1);
519 : 0 : auto idx = args[0].getUnsigned();
520 : 0 : auto [filename, line, msg] = Module::Get().get_message(idx);
521 : :
522 : 0 : std::cout.flush();
523 : 0 : std::cerr << filename << ':' << line << ": Wasm_insist failed.";
524 [ # # ]: 0 : if (msg)
525 : 0 : std::cerr << " " << msg << '.';
526 : 0 : std::cerr << std::endl;
527 : :
528 : 0 : abort();
529 : : }
530 : :
531 : 0 : ::wasm::Literals m::wasm::throw_interpreter(::wasm::Literals &args)
532 : : {
533 : 0 : M_insist(args.size() == 2);
534 : 0 : auto type = static_cast<exception::exception_t>(args[0].getUnsigned());
535 : 0 : auto idx = args[1].getUnsigned();
536 : 0 : auto [filename, line, msg] = Module::Get().get_message(idx);
537 : :
538 : 0 : std::ostringstream oss;
539 [ # # # # : 0 : oss << filename << ':' << line << ": Exception `" << exception::names_[type] << "` thrown.";
# # # # #
# # # ]
540 [ # # ]: 0 : if (*msg)
541 [ # # # # : 0 : oss << " " << msg << '.';
# # ]
542 [ # # ]: 0 : oss << std::endl;
543 : :
544 [ # # # # : 0 : throw exception(type, oss.str());
# # # # ]
545 : 0 : }
546 : :
547 : :
548 : : /*======================================================================================================================
549 : : * Control flow
550 : : *====================================================================================================================*/
551 : :
552 : : /*----- If -----------------------------------------------------------------------------------------------------------*/
553 : :
554 : 3040 : If::~If()
555 : : {
556 [ + - ]: 3040 : M_insist(bool(Then), "If must have a Then");
557 : :
558 [ + - + - ]: 3040 : Block then_block(name_ + ".then", false);
559 [ + - + - ]: 3040 : Block else_block(name_ + ".else", false);
560 : :
561 [ + - ]: 6080 : BLOCK_OPEN(then_block) {
562 [ + - ]: 3040 : Then();
563 : : }
564 : :
565 [ + + ]: 3040 : if (Else) {
566 [ + - ]: 464 : BLOCK_OPEN(else_block) {
567 [ + - ]: 232 : Else();
568 : : }
569 : 232 : }
570 : :
571 [ + - ]: 3040 : auto cond = cond_.expr();
572 [ + - + - : 3040 : switch (ConstantFolding::EvalBoolean(cond)) {
- - ]
573 : : case ConstantFolding::UNDEF:
574 : : /*----- If -----*/
575 [ + - + - ]: 6080 : Module::Block().list.push_back(
576 [ + - + - ]: 6080 : Module::Builder().makeIf(
577 : 3040 : cond,
578 [ + - ]: 3040 : &then_block.get(),
579 [ + + + - ]: 3040 : Else ? &else_block.get() : nullptr
580 : : )
581 : : );
582 : 3040 : break;
583 : : case ConstantFolding::TRUE:
584 : : /*----- Then-block -----*/
585 [ # # # # : 0 : Module::Block().list.push_back(&then_block.get());
# # ]
586 : 0 : break;
587 [ + - ]: 1 : case ConstantFolding::FALSE:
588 : : /*----- Else-block -----*/
589 [ + - # # ]: 1 : if (Else)
590 [ + - # # : 1 : Module::Block().list.push_back(&else_block.get());
# # # # ]
591 : 0 : break;
592 : : }
593 : 3040 : }
594 : :
595 : : /*----- Do-While -----------------------------------------------------------------------------------------------------*/
596 : :
597 : 100 : DoWhile::~DoWhile() {
598 [ + - + - ]: 200 : BLOCK_OPEN(body()) {
599 [ + - ]: 100 : CONTINUE(); // emit conditional branch back to loop header at the end of the loop's body
600 : : }
601 : 100 : }
602 : :
603 : : /*----- While --------------------------------------------------------------------------------------------------------*/
604 : :
605 : 60 : While::~While() {
606 [ + - ]: 120 : IF (cond_) {
607 : 60 : do_while_.reset(); // emit do-while code within IF
608 : 60 : };
609 : 60 : }
|