LCOV - code coverage report
Current view: top level - src/backend - WasmDSL.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 242 310 78.1 %
Date: 2025-03-25 01:19:55 Functions: 63 88 71.6 %
Branches: 139 334 41.6 %

           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 : }

Generated by: LCOV version 1.16