diff options
author | Ben Smith <binji@chromium.org> | 2020-02-21 14:11:44 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-21 14:11:44 -0800 |
commit | 24a12ecfdde0246527506c87bd82460beb761c57 (patch) | |
tree | 242550e84d516926a71bef00342f4f57d011d4f6 /src | |
parent | bb4b65186668ff3ebd2f088778337608969a9567 (diff) | |
download | wabt-24a12ecfdde0246527506c87bd82460beb761c57.tar.gz wabt-24a12ecfdde0246527506c87bd82460beb761c57.tar.bz2 wabt-24a12ecfdde0246527506c87bd82460beb761c57.zip |
New interpreter (#1330)
It's modeled closely on the wasm-c-api. All runtime objects are
garbage-collected. The GC'd objects are subclasses of `Object` and all
stored in the `Store`. GC roots can be created using a RefPtr<T> struct.
The `Module` stores no runtime objects, only description structs (named
`*Desc`, e.g. `FuncDesc`). Instantiation takes these descriptors and
turns them into runtime objects (e.g. `FuncDesc` is used to create a
`Func`). Only import-matching occurs during instantiation; all other
validation is assumed to have occurred during Module compilation.
As with the previous interpreter, the wasm instructions are not executed
directly, and instead compiled to an instruction stream that is easier
to interpret (e.g. all branches become gotos). It's still a stack
machine, though.
The previous interpreter would always compile and instantiate in one
step, which means that it was always known whether an imported function
is a host function or wasm function. As a result, calls between modules
that stayed in wasm could be very cheap (just update PC). Now that the
module is compiled before instantiation, an imported function call
always has to check first, which may be a slight performance hit.
One major difference to the structure of the interpreter is that
floating-point values are passed directly, instead of using their
equivalent bit pattern. This is more convenient in general, but requires
annotating all functions with WABT_VECTORCALL so Visual Studio x86 works
properly (otherwise floats are passed in the x87 FP stack).
Instruction stream decoding/tracing/disassembling is now all handled in
the `Istream` class. This reduces a lot of duplication between the
three, which makes the logging-all-opcodes and tracing-all-opcodes less
valuable, so I've removed them.
Here are the changes to files:
binary-reader-metadata.{h,cc} -> [removed]
interp-{disassemble.trace}.cc -> istream.{h,cc}
There are new helper files:
interp-util.{h,cc}: Primarily print debugging functions
interp-math.h: Templates used for handling wasm math
Diffstat (limited to 'src')
-rw-r--r-- | src/common.h | 2 | ||||
-rw-r--r-- | src/config.h.in | 4 | ||||
-rw-r--r-- | src/interp/binary-reader-interp.cc | 1401 | ||||
-rw-r--r-- | src/interp/binary-reader-interp.h | 26 | ||||
-rw-r--r-- | src/interp/binary-reader-metadata.cc | 74 | ||||
-rw-r--r-- | src/interp/interp-disassemble.cc | 699 | ||||
-rw-r--r-- | src/interp/interp-inl.h | 911 | ||||
-rw-r--r-- | src/interp/interp-internal.h | 94 | ||||
-rw-r--r-- | src/interp/interp-math.h | 358 | ||||
-rw-r--r-- | src/interp/interp-util.cc | 117 | ||||
-rw-r--r-- | src/interp/interp-util.h (renamed from src/interp/binary-reader-metadata.h) | 37 | ||||
-rw-r--r-- | src/interp/interp-wasm-c-api.cc | 1571 | ||||
-rw-r--r-- | src/interp/interp.cc | 5364 | ||||
-rw-r--r-- | src/interp/interp.h | 1639 | ||||
-rw-r--r-- | src/interp/istream.cc (renamed from src/interp/interp-trace.cc) | 1318 | ||||
-rw-r--r-- | src/interp/istream.h | 155 | ||||
-rw-r--r-- | src/opcode.cc | 7 | ||||
-rw-r--r-- | src/opcode.def | 18 | ||||
-rw-r--r-- | src/opcode.h | 11 | ||||
-rw-r--r-- | src/test-interp.cc | 556 | ||||
-rw-r--r-- | src/tools/spectest-interp.cc | 567 | ||||
-rw-r--r-- | src/tools/wasm-interp.cc | 183 |
22 files changed, 7108 insertions, 8004 deletions
diff --git a/src/common.h b/src/common.h index 6437bb7a..a2aff1c7 100644 --- a/src/common.h +++ b/src/common.h @@ -123,7 +123,7 @@ static const Index kInvalidIndex = ~0; static const Offset kInvalidOffset = ~0; template <typename Dst, typename Src> -Dst Bitcast(Src&& value) { +Dst WABT_VECTORCALL Bitcast(Src&& value) { static_assert(sizeof(Src) == sizeof(Dst), "Bitcast sizes must match."); Dst result; memcpy(&result, &value, sizeof(result)); diff --git a/src/config.h.in b/src/config.h.in index fa79190f..97c1b101 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -67,6 +67,8 @@ #define WABT_UNLIKELY(x) __builtin_expect(!!(x), 0) #define WABT_LIKELY(x) __builtin_expect(!!(x), 1) +#define WABT_VECTORCALL + #if __MINGW32__ // mingw defaults to printf format specifier being ms_printf (which doesn't // understand 'llu', etc.) We always want gnu_printf, and force mingw to always @@ -104,6 +106,8 @@ #define WABT_LIKELY(x) (x) #define WABT_PRINTF_FORMAT(format_arg, first_arg) +#define WABT_VECTORCALL __vectorcall + #else #error unknown compiler diff --git a/src/interp/binary-reader-interp.cc b/src/interp/binary-reader-interp.cc index 8bee5f20..985d8cec 100644 --- a/src/interp/binary-reader-interp.cc +++ b/src/interp/binary-reader-interp.cc @@ -16,57 +16,63 @@ #include "src/interp/binary-reader-interp.h" -#include <cassert> -#include <cinttypes> -#include <cstdarg> -#include <cstdio> -#include <vector> +#include <map> +#include <set> #include "src/binary-reader-nop.h" -#include "src/cast.h" #include "src/feature.h" #include "src/interp/interp.h" -#include "src/interp/interp-internal.h" #include "src/stream.h" #include "src/type-checker.h" -namespace wabt { +// TODO: Remove wabt:: namespace everywhere. -using namespace interp; +namespace wabt { +namespace interp { namespace { -typedef std::vector<Index> IndexVector; -typedef std::vector<IstreamOffset> IstreamOffsetVector; -typedef std::vector<IstreamOffsetVector> IstreamOffsetVectorVector; +ValueTypes ToInterp(Index count, Type* types) { + return ValueTypes(&types[0], &types[count]); +} -struct Label { - Label(IstreamOffset offset, IstreamOffset fixup_offset); +Mutability ToMutability(bool mut) { + return mut ? Mutability::Var : Mutability::Const; +} - IstreamOffset offset; - IstreamOffset fixup_offset; -}; +SegmentMode ToSegmentMode(uint8_t flags) { + if ((flags & SegDeclared) == SegDeclared) { + return SegmentMode::Declared; + } else if ((flags & SegPassive) == SegPassive) { + return SegmentMode::Passive; + } else { + return SegmentMode::Active; + } +} -struct GlobalType { - Type type; - bool mutable_; +struct Label { + Istream::Offset offset; + Istream::Offset fixup_offset; }; -Label::Label(IstreamOffset offset, IstreamOffset fixup_offset) - : offset(offset), fixup_offset(fixup_offset) {} +struct FixupMap { + using Offset = Istream::Offset; + using Fixups = std::vector<Offset>; + + void Clear(); + void Append(Index, Offset); + void Resolve(Istream&, Index); + + std::map<Index, Fixups> map; +}; class BinaryReaderInterp : public BinaryReaderNop { public: - BinaryReaderInterp(Environment* env, - DefinedModule* module, - std::unique_ptr<OutputBuffer> istream, - const std::vector<Export*>& imports_, + BinaryReaderInterp(ModuleDesc* module, Errors* errors, const Features& features); - wabt::Result ReadBinary(DefinedModule* out_module); - - std::unique_ptr<OutputBuffer> ReleaseOutputBuffer(); + ValueType GetType(InitExpr); // Implement BinaryReader. bool OnError(const Error&) override; @@ -111,6 +117,7 @@ class BinaryReaderInterp : public BinaryReaderNop { Type elem_type, const Limits* elem_limits) override; + wabt::Result OnMemoryCount(Index count) override; wabt::Result OnMemory(Index index, const Limits* limits) override; wabt::Result OnGlobalCount(Index count) override; @@ -217,7 +224,6 @@ class BinaryReaderInterp : public BinaryReaderNop { Index table_index, uint8_t flags, Type elem_type) override; - wabt::Result BeginElemSegmentInitExpr(Index index) override; wabt::Result EndElemSegmentInitExpr(Index index) override; wabt::Result OnElemSegmentElemExprCount(Index index, Index count) override; wabt::Result OnElemSegmentElemExpr_RefNull(Index segment_index) override; @@ -225,10 +231,10 @@ class BinaryReaderInterp : public BinaryReaderNop { Index func_index) override; wabt::Result OnDataCount(Index count) override; + wabt::Result EndDataSegmentInitExpr(Index index) override; wabt::Result BeginDataSegment(Index index, Index memory_index, uint8_t flags) override; - wabt::Result BeginDataSegmentInitExpr(Index index) override; wabt::Result OnDataSegmentData(Index index, const void* data, Address size) override; @@ -246,45 +252,12 @@ class BinaryReaderInterp : public BinaryReaderNop { private: Label* GetLabel(Index depth); Label* TopLabel(); - void PushLabel(IstreamOffset offset, IstreamOffset fixup_offset); + void PushLabel(Istream::Offset offset = Istream::kInvalidOffset, + Istream::Offset fixup_offset = Istream::kInvalidOffset); void PopLabel(); void PrintError(const char* format, ...); - Index TranslateSigIndexToEnv(Index sig_index); - void GetBlockSignature(Type sig_type, - TypeVector* out_param_types, - TypeVector* out_result_types); - FuncSignature* GetSignatureByModuleIndex(Index sig_index); - Index TranslateFuncIndexToEnv(Index func_index); - Table* GetTableByModuleIndex(Index table_index); - Index TranslateTableIndexToEnv(Index table_index); - Index TranslateModuleFuncIndexToDefined(Index func_index); - Func* GetFuncByModuleIndex(Index func_index); - Index TranslateGlobalIndexToEnv(Index global_index); - Global* GetGlobalByModuleIndex(Index global_index); - Type GetGlobalTypeByModuleIndex(Index global_index); - Index TranslateLocalIndex(Index local_index); - Type GetLocalTypeByIndex(Func* func, Index local_index); - Index TranslateDataSegmentIndexToEnv(Index data_segment_index); - Index TranslateElemSegmentIndexToEnv(Index elem_segment_index); - - IstreamOffset GetIstreamOffset(); - - wabt::Result EmitDataAt(IstreamOffset offset, - const void* data, - IstreamOffset size); - wabt::Result EmitData(const void* data, IstreamOffset size); - wabt::Result EmitOpcode(Opcode opcode); - wabt::Result EmitI8(uint8_t value); - wabt::Result EmitI32(uint32_t value); - wabt::Result EmitI64(uint64_t value); - wabt::Result EmitV128(v128 value); - wabt::Result EmitI32At(IstreamOffset offset, uint32_t value); - wabt::Result EmitDropKeep(uint32_t drop, uint32_t keep); - wabt::Result AppendFixup(IstreamOffsetVectorVector* fixups_vector, - Index index); - wabt::Result EmitBrOffset(Index depth, IstreamOffset offset); wabt::Result GetDropCount(Index keep_count, size_t type_stack_limit, Index* out_drop_count); @@ -293,105 +266,95 @@ class BinaryReaderInterp : public BinaryReaderNop { Index* out_keep_count); wabt::Result GetReturnDropKeepCount(Index* out_drop_count, Index* out_keep_count); - wabt::Result GetReturnCallDropKeepCount(FuncSignature* sig, + wabt::Result GetReturnCallDropKeepCount(const FuncType&, Index keep_extra, Index* out_drop_count, Index* out_keep_count); - wabt::Result EmitBr(Index depth, Index drop_count, Index keep_count); - wabt::Result EmitBrTableOffset(Index depth); - wabt::Result FixupTopLabel(); - wabt::Result EmitFuncOffset(DefinedFunc* func, Index func_index); + void EmitBr(Index depth, Index drop_count, Index keep_count); + void FixupTopLabel(); + u32 GetFuncOffset(Index func_index); + + void GetBlockSignature(Type sig_type, + TypeVector* out_param_types, + TypeVector* out_result_types); + Index TranslateLocalIndex(Index local_index); + ValueType GetLocalType(Index local_index); wabt::Result CheckDeclaredFunc(Index func_index); wabt::Result CheckLocal(Index local_index); wabt::Result CheckGlobal(Index global_index); - wabt::Result CheckGlobalType(GlobalType actual, - GlobalType expected); wabt::Result CheckDataSegment(Index data_segment_index); wabt::Result CheckElemSegment(Index elem_segment_index); - wabt::Result CheckImportKind(string_view module, - string_view field, - ExternalKind kind, - ExternalKind actual_kind); - wabt::Result CheckImportLimits(const Limits* declared_limits, - const Limits* actual_limits); wabt::Result CheckHasMemory(wabt::Opcode opcode); wabt::Result CheckHasTable(wabt::Opcode opcode); wabt::Result CheckAlign(uint32_t alignment_log2, Address natural_alignment); wabt::Result CheckAtomicAlign(uint32_t alignment_log2, Address natural_alignment); - wabt::Result AppendExport(Module* module, - ExternalKind kind, - Index item_index, - string_view name); - wabt::Result FindRegisteredModule(string_view module_name, - Module** out_module); - wabt::Result ResolveImport(Index import_index, - ExternalKind kind, - string_view module_name, - string_view field_name, - Index sig_index, - Export** out_export); + Index num_func_imports() const; + Index num_global_imports() const; Features features_; - const std::vector<Export*>& imports_; Errors* errors_ = nullptr; - Environment* env_ = nullptr; - DefinedModule* module_ = nullptr; - DefinedFunc* current_func_ = nullptr; + ModuleDesc& module_; + Istream& istream_; + TypeChecker typechecker_; + + FuncDesc* func_; std::vector<Label> label_stack_; - IstreamOffsetVectorVector func_fixups_; - IstreamOffsetVectorVector depth_fixups_; - MemoryStream istream_; - IstreamOffset istream_offset_ = 0; - /* mappings from module index space to env index space; this won't just be a - * translation, because imported values will be resolved as well */ - IndexVector sig_index_mapping_; - IndexVector func_index_mapping_; - IndexVector table_index_mapping_; - IndexVector global_index_mapping_; - IndexVector data_segment_index_mapping_; - IndexVector elem_segment_index_mapping_; - - Index num_func_imports_ = 0; - Index num_global_imports_ = 0; - - // Values cached so they can be shared between callbacks. - TypedValue init_expr_value_; - IstreamOffset table_offset_ = 0; - SegmentFlags segment_flags_ = SegFlagsNone; - Index segment_table_index_ = kInvalidIndex; - ElemSegment* elem_segment_ = nullptr; - ElemSegmentInfo* elem_segment_info_ = nullptr; + ValueTypes param_and_local_types_; + FixupMap depth_fixups_; + FixupMap func_fixups_; + + InitExpr init_expr_; + u32 local_decl_count_; + u32 local_count_; + + std::vector<FuncType> func_types_; // Includes imported and defined. + std::vector<TableType> table_types_; // Includes imported and defined. + std::vector<MemoryType> memory_types_; // Includes imported and defined. + std::vector<GlobalType> global_types_; // Includes imported and defined. + std::vector<EventType> event_types_; // Includes imported and defined. + + std::set<std::string> export_names_; // Used to check for duplicates. std::vector<bool> declared_funcs_; std::vector<Index> init_expr_funcs_; - bool has_table = false; + + static const Index kMemoryIndex0 = 0; }; -BinaryReaderInterp::BinaryReaderInterp(Environment* env, - DefinedModule* module, - std::unique_ptr<OutputBuffer> istream, - const std::vector<Export*>& imports, +void FixupMap::Clear() { + map.clear(); +} + +void FixupMap::Append(Index index, Offset offset) { + map[index].push_back(offset); +} + +void FixupMap::Resolve(Istream& istream, Index index) { + auto iter = map.find(index); + if (iter == map.end()) { + return; + } + for (Offset offset : iter->second) { + istream.ResolveFixupU32(offset); + } + map.erase(iter); +} + +BinaryReaderInterp::BinaryReaderInterp(ModuleDesc* module, Errors* errors, const Features& features) : features_(features), - imports_(imports), errors_(errors), - env_(env), - module_(module), - typechecker_(features), - istream_(std::move(istream)), - istream_offset_(istream_.output_buffer().size()) { + module_(*module), + istream_(module->istream), + typechecker_(features) { typechecker_.set_error_callback( [this](const char* msg) { PrintError("%s", msg); }); } -std::unique_ptr<OutputBuffer> BinaryReaderInterp::ReleaseOutputBuffer() { - return istream_.ReleaseOutputBuffer(); -} - Label* BinaryReaderInterp::GetLabel(Index depth) { assert(depth < label_stack_.size()); return &label_stack_[label_stack_.size() - depth - 1]; @@ -407,156 +370,19 @@ void WABT_PRINTF_FORMAT(2, 3) BinaryReaderInterp::PrintError(const char* format, errors_->emplace_back(ErrorLevel::Error, Location(kInvalidOffset), buffer); } -Index BinaryReaderInterp::TranslateSigIndexToEnv(Index sig_index) { - assert(sig_index < sig_index_mapping_.size()); - return sig_index_mapping_[sig_index]; -} - void BinaryReaderInterp::GetBlockSignature(Type sig_type, TypeVector* out_param_types, TypeVector* out_result_types) { if (IsTypeIndex(sig_type)) { - FuncSignature* func_sig = GetSignatureByModuleIndex(GetTypeIndex(sig_type)); - *out_param_types = func_sig->param_types; - *out_result_types = func_sig->result_types; + FuncType& func_type = module_.func_types[GetTypeIndex(sig_type)]; + *out_param_types = func_type.params; + *out_result_types = func_type.results; } else { out_param_types->clear(); *out_result_types = GetInlineTypeVector(sig_type); } } -FuncSignature* BinaryReaderInterp::GetSignatureByModuleIndex(Index sig_index) { - return env_->GetFuncSignature(TranslateSigIndexToEnv(sig_index)); -} - -Index BinaryReaderInterp::TranslateFuncIndexToEnv(Index func_index) { - assert(func_index < func_index_mapping_.size()); - return func_index_mapping_[func_index]; -} - -Index BinaryReaderInterp::TranslateTableIndexToEnv(Index table_index) { - assert(table_index < table_index_mapping_.size()); - return table_index_mapping_[table_index]; -} - -Index BinaryReaderInterp::TranslateModuleFuncIndexToDefined(Index func_index) { - assert(func_index >= num_func_imports_); - return func_index - num_func_imports_; -} - -Func* BinaryReaderInterp::GetFuncByModuleIndex(Index func_index) { - return env_->GetFunc(TranslateFuncIndexToEnv(func_index)); -} - -Table* BinaryReaderInterp::GetTableByModuleIndex(Index table_index) { - return env_->GetTable(TranslateTableIndexToEnv(table_index)); -} - -Index BinaryReaderInterp::TranslateGlobalIndexToEnv(Index global_index) { - return global_index_mapping_[global_index]; -} - -Global* BinaryReaderInterp::GetGlobalByModuleIndex(Index global_index) { - return env_->GetGlobal(TranslateGlobalIndexToEnv(global_index)); -} - -Type BinaryReaderInterp::GetGlobalTypeByModuleIndex(Index global_index) { - return GetGlobalByModuleIndex(global_index)->typed_value.type; -} - -Type BinaryReaderInterp::GetLocalTypeByIndex(Func* func, Index local_index) { - assert(!func->is_host); - return cast<DefinedFunc>(func)->param_and_local_types[local_index]; -} - -Index BinaryReaderInterp::TranslateDataSegmentIndexToEnv(Index index) { - return data_segment_index_mapping_[index]; -} - -Index BinaryReaderInterp::TranslateElemSegmentIndexToEnv(Index index) { - return elem_segment_index_mapping_[index]; -} - -IstreamOffset BinaryReaderInterp::GetIstreamOffset() { - return istream_offset_; -} - -wabt::Result BinaryReaderInterp::EmitDataAt(IstreamOffset offset, - const void* data, - IstreamOffset size) { - istream_.WriteDataAt(offset, data, size); - return istream_.result(); -} - -wabt::Result BinaryReaderInterp::EmitData(const void* data, - IstreamOffset size) { - CHECK_RESULT(EmitDataAt(istream_offset_, data, size)); - istream_offset_ += size; - return wabt::Result::Ok; -} - -wabt::Result BinaryReaderInterp::EmitOpcode(Opcode opcode) { - return EmitI32(static_cast<uint32_t>(opcode)); -} - -wabt::Result BinaryReaderInterp::EmitI8(uint8_t value) { - return EmitData(&value, sizeof(value)); -} - -wabt::Result BinaryReaderInterp::EmitI32(uint32_t value) { - return EmitData(&value, sizeof(value)); -} - -wabt::Result BinaryReaderInterp::EmitI64(uint64_t value) { - return EmitData(&value, sizeof(value)); -} - -wabt::Result BinaryReaderInterp::EmitV128(v128 value) { - return EmitData(&value, sizeof(value)); -} - -wabt::Result BinaryReaderInterp::EmitI32At(IstreamOffset offset, - uint32_t value) { - return EmitDataAt(offset, &value, sizeof(value)); -} - -wabt::Result BinaryReaderInterp::EmitDropKeep(uint32_t drop, uint32_t keep) { - assert(drop != UINT32_MAX); - assert(keep != UINT32_MAX); - if (drop > 0) { - if (drop == 1 && keep == 0) { - CHECK_RESULT(EmitOpcode(Opcode::Drop)); - } else { - CHECK_RESULT(EmitOpcode(Opcode::InterpDropKeep)); - CHECK_RESULT(EmitI32(drop)); - CHECK_RESULT(EmitI32(keep)); - } - } - return wabt::Result::Ok; -} - -wabt::Result BinaryReaderInterp::AppendFixup( - IstreamOffsetVectorVector* fixups_vector, - Index index) { - if (index >= fixups_vector->size()) { - fixups_vector->resize(index + 1); - } - (*fixups_vector)[index].push_back(GetIstreamOffset()); - return wabt::Result::Ok; -} - -wabt::Result BinaryReaderInterp::EmitBrOffset(Index depth, - IstreamOffset offset) { - if (offset == kInvalidIstreamOffset) { - /* depth_fixups_ stores the depth counting up from zero, where zero is the - * top-level function scope. */ - depth = label_stack_.size() - 1 - depth; - CHECK_RESULT(AppendFixup(&depth_fixups_, depth)); - } - CHECK_RESULT(EmitI32(offset)); - return wabt::Result::Ok; -} - wabt::Result BinaryReaderInterp::GetDropCount(Index keep_count, size_t type_stack_limit, Index* out_drop_count) { @@ -586,63 +412,47 @@ wabt::Result BinaryReaderInterp::GetReturnDropKeepCount(Index* out_drop_count, Index* out_keep_count) { CHECK_RESULT(GetBrDropKeepCount(label_stack_.size() - 1, out_drop_count, out_keep_count)); - *out_drop_count += current_func_->param_and_local_types.size(); + *out_drop_count += param_and_local_types_.size(); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::GetReturnCallDropKeepCount( - FuncSignature* sig, + const FuncType& func_type, Index keep_extra, Index* out_drop_count, Index* out_keep_count) { - Index keep_count = static_cast<Index>(sig->param_types.size()) + keep_extra; + Index keep_count = static_cast<Index>(func_type.params.size()) + keep_extra; CHECK_RESULT(GetDropCount(keep_count, 0, out_drop_count)); - *out_drop_count += current_func_->param_and_local_types.size(); + *out_drop_count += param_and_local_types_.size(); *out_keep_count = keep_count; return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::EmitBr(Index depth, - Index drop_count, - Index keep_count) { - CHECK_RESULT(EmitDropKeep(drop_count, keep_count)); - CHECK_RESULT(EmitOpcode(Opcode::Br)); - CHECK_RESULT(EmitBrOffset(depth, GetLabel(depth)->offset)); - return wabt::Result::Ok; -} - -wabt::Result BinaryReaderInterp::EmitBrTableOffset(Index depth) { - Index drop_count, keep_count; - CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count)); - CHECK_RESULT(EmitBrOffset(depth, GetLabel(depth)->offset)); - CHECK_RESULT(EmitI32(drop_count)); - CHECK_RESULT(EmitI32(keep_count)); - return wabt::Result::Ok; -} - -wabt::Result BinaryReaderInterp::FixupTopLabel() { - IstreamOffset offset = GetIstreamOffset(); - Index top = label_stack_.size() - 1; - if (top >= depth_fixups_.size()) { - /* nothing to fixup */ - return wabt::Result::Ok; +void BinaryReaderInterp::EmitBr(Index depth, + Index drop_count, + Index keep_count) { + istream_.EmitDropKeep(drop_count, keep_count); + Istream::Offset offset = GetLabel(depth)->offset; + istream_.Emit(Opcode::Br); + if (offset == Istream::kInvalidOffset) { + // depth_fixups_ stores the depth counting up from zero, where zero is the + // top-level function scope. + depth_fixups_.Append(label_stack_.size() - 1 - depth, istream_.end()); } + istream_.Emit(offset); +} - IstreamOffsetVector& fixups = depth_fixups_[top]; - for (IstreamOffset fixup : fixups) - CHECK_RESULT(EmitI32At(fixup, offset)); - fixups.clear(); - return wabt::Result::Ok; +void BinaryReaderInterp::FixupTopLabel() { + depth_fixups_.Resolve(istream_, label_stack_.size() - 1); } -wabt::Result BinaryReaderInterp::EmitFuncOffset(DefinedFunc* func, - Index func_index) { - if (func->offset == kInvalidIstreamOffset) { - Index defined_index = TranslateModuleFuncIndexToDefined(func_index); - CHECK_RESULT(AppendFixup(&func_fixups_, defined_index)); +u32 BinaryReaderInterp::GetFuncOffset(Index func_index) { + assert(func_index >= num_func_imports()); + FuncDesc& func = module_.funcs[func_index - num_func_imports()]; + if (func.code_offset == Istream::kInvalidOffset) { + func_fixups_.Append(func_index, istream_.end()); } - CHECK_RESULT(EmitI32(func->offset)); - return wabt::Result::Ok; + return func.code_offset; } bool BinaryReaderInterp::OnError(const Error& error) { @@ -670,10 +480,7 @@ wabt::Result BinaryReaderInterp::EndModule() { } wabt::Result BinaryReaderInterp::OnTypeCount(Index count) { - Index sig_count = env_->GetFuncSignatureCount(); - sig_index_mapping_.resize(count); - for (Index i = 0; i < count; ++i) - sig_index_mapping_[i] = sig_count + i; + module_.func_types.reserve(count); return wabt::Result::Ok; } @@ -682,14 +489,13 @@ wabt::Result BinaryReaderInterp::OnType(Index index, Type* param_types, Index result_count, Type* result_types) { - assert(TranslateSigIndexToEnv(index) == env_->GetFuncSignatureCount()); - env_->EmplaceBackFuncSignature(param_count, param_types, result_count, - result_types); + module_.func_types.push_back(FuncType(ToInterp(param_count, param_types), + ToInterp(result_count, result_types))); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::CheckLocal(Index local_index) { - Index max_local_index = current_func_->param_and_local_types.size(); + Index max_local_index = param_and_local_types_.size(); if (local_index >= max_local_index) { PrintError("invalid local_index: %" PRIindex " (max %" PRIindex ")", local_index, max_local_index); @@ -699,7 +505,7 @@ wabt::Result BinaryReaderInterp::CheckLocal(Index local_index) { } wabt::Result BinaryReaderInterp::CheckGlobal(Index global_index) { - Index max_global_index = global_index_mapping_.size(); + Index max_global_index = global_types_.size(); if (global_index >= max_global_index) { PrintError("invalid global_index: %" PRIindex " (max %" PRIindex ")", global_index, max_global_index); @@ -709,8 +515,9 @@ wabt::Result BinaryReaderInterp::CheckGlobal(Index global_index) { } wabt::Result BinaryReaderInterp::CheckDataSegment(Index data_segment_index) { - Index max_data_segment_index = data_segment_index_mapping_.size(); + Index max_data_segment_index = module_.datas.capacity(); if (data_segment_index >= max_data_segment_index) { + // TODO: change to "data segment index" PrintError("invalid data_segment_index: %" PRIindex " (max %" PRIindex ")", data_segment_index, max_data_segment_index); return wabt::Result::Error; @@ -719,8 +526,9 @@ wabt::Result BinaryReaderInterp::CheckDataSegment(Index data_segment_index) { } wabt::Result BinaryReaderInterp::CheckElemSegment(Index elem_segment_index) { - Index max_elem_segment_index = elem_segment_index_mapping_.size(); + Index max_elem_segment_index = module_.elems.size(); if (elem_segment_index >= max_elem_segment_index) { + // TODO: change to "elem segment index" PrintError("invalid elem_segment_index: %" PRIindex " (max %" PRIindex ")", elem_segment_index, max_elem_segment_index); return wabt::Result::Error; @@ -728,126 +536,15 @@ wabt::Result BinaryReaderInterp::CheckElemSegment(Index elem_segment_index) { return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::CheckImportKind(string_view module_name, - string_view field_name, - ExternalKind kind, - ExternalKind actual_kind) { - if (kind != actual_kind) { - PrintError("expected import \"" PRIstringview "." PRIstringview - "\" to have kind %s, not %s", - WABT_PRINTF_STRING_VIEW_ARG(module_name), - WABT_PRINTF_STRING_VIEW_ARG(field_name), - GetKindName(kind), GetKindName(actual_kind)); - return wabt::Result::Error; - } - return wabt::Result::Ok; -} - -wabt::Result BinaryReaderInterp::CheckImportLimits( - const Limits* declared_limits, - const Limits* actual_limits) { - if (actual_limits->initial < declared_limits->initial) { - PrintError("actual size (%" PRIu64 ") smaller than declared (%" PRIu64 ")", - actual_limits->initial, declared_limits->initial); - return wabt::Result::Error; - } - - if (declared_limits->has_max) { - if (!actual_limits->has_max) { - PrintError("max size (unspecified) larger than declared (%" PRIu64 ")", - declared_limits->max); - return wabt::Result::Error; - } else if (actual_limits->max > declared_limits->max) { - PrintError("max size (%" PRIu64 ") larger than declared (%" PRIu64 ")", - actual_limits->max, declared_limits->max); - return wabt::Result::Error; - } - } - - return wabt::Result::Ok; -} - -wabt::Result BinaryReaderInterp::AppendExport(Module* module, - ExternalKind kind, - Index item_index, - string_view name) { - // Host modules are allowed to have duplicated exports; e.g. "spectest.print" - if (isa<DefinedModule>(module) && - module->export_bindings.FindIndex(name) != kInvalidIndex) { - PrintError("duplicate export \"" PRIstringview "\"", - WABT_PRINTF_STRING_VIEW_ARG(name)); - return wabt::Result::Error; - } - - module->AppendExport(kind, item_index, name); - return wabt::Result::Ok; -} - -wabt::Result BinaryReaderInterp::FindRegisteredModule(string_view module_name, - Module** out_module) { - Module* module = env_->FindRegisteredModule(module_name); - if (!module) { - PrintError("unknown import module \"" PRIstringview "\"", - WABT_PRINTF_STRING_VIEW_ARG(module_name)); - return wabt::Result::Error; - } - - *out_module = module; - return wabt::Result::Ok; -} - -wabt::Result BinaryReaderInterp::ResolveImport(Index import_index, - ExternalKind kind, - string_view module_name, - string_view field_name, - Index sig_index, - Export** out_export) { - Export* export_ = nullptr; - if (!imports_.empty()) { - export_ = imports_[import_index]; - } else { - Module* module; - CHECK_RESULT(FindRegisteredModule(module_name, &module)); - - // Func imports get special handled due to the face that they can be - // overloaded on signature. - if (kind == ExternalKind::Func) { - export_ = module->GetFuncExport(env_, field_name, sig_index); - } - if (!export_) { - export_ = module->GetExport(field_name); - if (!export_) { - PrintError("unknown module field \"" PRIstringview "\"", - WABT_PRINTF_STRING_VIEW_ARG(field_name)); - return wabt::Result::Error; - } - } - } - - CHECK_RESULT(CheckImportKind(module_name, field_name, kind, export_->kind)); - *out_export = export_; - return wabt::Result::Ok; -} - wabt::Result BinaryReaderInterp::OnImportFunc(Index import_index, string_view module_name, string_view field_name, Index func_index, Index sig_index) { - Index env_sig_index = TranslateSigIndexToEnv(sig_index); - - Export* export_; - CHECK_RESULT(ResolveImport(import_index, ExternalKind::Func, module_name, - field_name, env_sig_index, &export_)); - - Func* func = env_->GetFunc(export_->index); - if (!env_->FuncSignaturesAreEqual(env_sig_index, func->sig_index)) { - PrintError("import signature mismatch"); - return wabt::Result::Error; - } - - func_index_mapping_.push_back(export_->index); - num_func_imports_++; + FuncType& func_type = module_.func_types[sig_index]; + module_.imports.push_back(ImportDesc{ImportType( + module_name.to_string(), field_name.to_string(), func_type.Clone())}); + func_types_.push_back(func_type); return wabt::Result::Ok; } @@ -857,21 +554,10 @@ wabt::Result BinaryReaderInterp::OnImportTable(Index import_index, Index table_index, Type elem_type, const Limits* elem_limits) { - has_table = true; - Export* export_; - CHECK_RESULT(ResolveImport(import_index, ExternalKind::Table, module_name, - field_name, 0, &export_)); - - Table* table = env_->GetTable(export_->index); - if (elem_type != table->elem_type) { - PrintError("type mismatch in imported table, expected %s but got %s.", - GetTypeName(elem_type), GetTypeName(table->elem_type)); - return wabt::Result::Error; - } - - CHECK_RESULT(CheckImportLimits(elem_limits, &table->limits)); - - table_index_mapping_.push_back(export_->index); + TableType table_type{elem_type, *elem_limits}; + module_.imports.push_back(ImportDesc{ImportType( + module_name.to_string(), field_name.to_string(), table_type.Clone())}); + table_types_.push_back(table_type); return wabt::Result::Ok; } @@ -880,240 +566,207 @@ wabt::Result BinaryReaderInterp::OnImportMemory(Index import_index, string_view field_name, Index memory_index, const Limits* page_limits) { - if (module_->memory_index != kInvalidIndex) { - PrintError("only one memory allowed"); - return wabt::Result::Error; - } - - Export* export_; - CHECK_RESULT(ResolveImport(import_index, ExternalKind::Memory, module_name, - field_name, 0, &export_)); - - Memory* memory = env_->GetMemory(export_->index); - CHECK_RESULT(CheckImportLimits(page_limits, &memory->page_limits)); - - module_->memory_index = export_->index; + MemoryType memory_type{*page_limits}; + module_.imports.push_back(ImportDesc{ImportType( + module_name.to_string(), field_name.to_string(), memory_type.Clone())}); + memory_types_.push_back(memory_type); return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::CheckGlobalType(GlobalType actual, - GlobalType expected) { - if (actual.mutable_ != expected.mutable_) { - const char* kMutableNames[] = {"immutable", "mutable"}; - PrintError( - "mutability mismatch in imported global, expected %s but got %s.", - kMutableNames[actual.mutable_], kMutableNames[expected.mutable_]); - return wabt::Result::Error; - } - - if (actual.type == expected.type || - (!expected.mutable_ && - Succeeded(typechecker_.CheckType(actual.type, expected.type)))) { - return wabt::Result::Ok; - } - - PrintError("type mismatch in imported global, expected %s but got %s.", - GetTypeName(expected.type), GetTypeName(actual.type)); - return wabt::Result::Error; -} - wabt::Result BinaryReaderInterp::OnImportGlobal(Index import_index, string_view module_name, string_view field_name, Index global_index, Type type, bool mutable_) { - Export* export_; - CHECK_RESULT(ResolveImport(import_index, ExternalKind::Global, module_name, - field_name, 0, &export_)); - - Global* exported_global = env_->GetGlobal(export_->index); - GlobalType expected = {type, mutable_}; - GlobalType actual = {exported_global->type, exported_global->mutable_}; - if (Failed(CheckGlobalType(actual, expected))) { - return wabt::Result::Error; - } - - global_index_mapping_.push_back(export_->index); - num_global_imports_++; + GlobalType global_type{type, ToMutability(mutable_)}; + module_.imports.push_back(ImportDesc{ImportType( + module_name.to_string(), field_name.to_string(), global_type.Clone())}); + global_types_.push_back(global_type); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnFunctionCount(Index count) { - for (Index i = 0; i < count; ++i) - func_index_mapping_.push_back(env_->GetFuncCount() + i); - func_fixups_.resize(count); + module_.funcs.reserve(count); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnFunction(Index index, Index sig_index) { - env_->EmplaceBackFunc(new DefinedFunc(TranslateSigIndexToEnv(sig_index))); + FuncType& func_type = module_.func_types[sig_index]; + module_.funcs.push_back(FuncDesc{func_type, {}, 0}); + func_types_.push_back(func_type); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnTableCount(Index count) { - for (Index i = 0; i < count; ++i) - table_index_mapping_.push_back(env_->GetTableCount() + i); + module_.tables.reserve(count); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnTable(Index index, Type elem_type, const Limits* elem_limits) { - has_table = true; - env_->EmplaceBackTable(elem_type, *elem_limits); + TableType table_type{elem_type, *elem_limits}; + module_.tables.push_back(TableDesc{table_type}); + table_types_.push_back(table_type); return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnMemory(Index index, - const Limits* page_limits) { - if (module_->memory_index != kInvalidIndex) { - PrintError("only one memory allowed"); - return wabt::Result::Error; - } +wabt::Result BinaryReaderInterp::OnMemoryCount(Index count) { + module_.memories.reserve(count); + return wabt::Result::Ok; +} - env_->EmplaceBackMemory(*page_limits); - module_->memory_index = env_->GetMemoryCount() - 1; +wabt::Result BinaryReaderInterp::OnMemory(Index index, const Limits* limits) { + MemoryType memory_type{*limits}; + module_.memories.push_back(MemoryDesc{memory_type}); + memory_types_.push_back(memory_type); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnGlobalCount(Index count) { - for (Index i = 0; i < count; ++i) - global_index_mapping_.push_back(env_->GetGlobalCount() + i); + module_.globals.reserve(count); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::BeginGlobal(Index index, Type type, bool mutable_) { - assert(TranslateGlobalIndexToEnv(index) == env_->GetGlobalCount()); - env_->EmplaceBackGlobal(type, mutable_); - init_expr_value_.type = Type::Void; - return wabt::Result::Ok; + GlobalType global_type{type, ToMutability(mutable_)}; + module_.globals.push_back(GlobalDesc{global_type, InitExpr{}}); + global_types_.push_back(global_type); + init_expr_.kind = InitExprKind::None; + return wabt::Result::Ok; +} + +ValueType BinaryReaderInterp::GetType(InitExpr init) { + switch (init_expr_.kind) { + case InitExprKind::None: return ValueType::Void; + case InitExprKind::I32: return ValueType::I32; + case InitExprKind::I64: return ValueType::I64; + case InitExprKind::F32: return ValueType::F32; + case InitExprKind::F64: return ValueType::F64; + case InitExprKind::V128: return ValueType::V128; + case InitExprKind::GlobalGet: return global_types_[init.index_].type; + case InitExprKind::RefNull: return ValueType::Nullref; + case InitExprKind::RefFunc: return ValueType::Funcref; + default: WABT_UNREACHABLE; + } } wabt::Result BinaryReaderInterp::EndGlobalInitExpr(Index index) { - Global* global = GetGlobalByModuleIndex(index); - if (Failed(typechecker_.CheckType(init_expr_value_.type, global->type))) { + GlobalDesc& global = module_.globals.back(); + ValueType type = GetType(global.init); + if (!TypesMatch(global.type.type, type)) { PrintError("type mismatch in global, expected %s but got %s.", - GetTypeName(global->type), GetTypeName(init_expr_value_.type)); + GetName(global.type.type), GetName(type)); return wabt::Result::Error; } - global->typed_value = init_expr_value_; + + global.init = init_expr_; return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnInitExprF32ConstExpr(Index index, uint32_t value_bits) { - init_expr_value_.type = Type::F32; - init_expr_value_.value.f32_bits = value_bits; + init_expr_.kind = InitExprKind::F32; + init_expr_.f32_ = Bitcast<f32>(value_bits); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnInitExprF64ConstExpr(Index index, uint64_t value_bits) { - init_expr_value_.type = Type::F64; - init_expr_value_.value.f64_bits = value_bits; + init_expr_.kind = InitExprKind::F64; + init_expr_.f64_ = Bitcast<f64>(value_bits); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnInitExprV128ConstExpr(Index index, v128 value_bits) { - init_expr_value_.type = Type::V128; - init_expr_value_.value.vec128 = value_bits; + init_expr_.kind = InitExprKind::V128; + init_expr_.v128_ = Bitcast<v128>(value_bits); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnInitExprGlobalGetExpr(Index index, Index global_index) { - if (global_index >= num_global_imports_) { + if (global_index >= num_global_imports()) { PrintError("initializer expression can only reference an imported global"); return wabt::Result::Error; } - Global* ref_global = GetGlobalByModuleIndex(global_index); - if (ref_global->mutable_) { + if (global_types_[global_index].mut == Mutability::Var) { PrintError("initializer expression cannot reference a mutable global"); return wabt::Result::Error; } - init_expr_value_ = ref_global->typed_value; + + init_expr_.kind = InitExprKind::GlobalGet; + init_expr_.index_ = global_index; return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnInitExprI32ConstExpr(Index index, uint32_t value) { - init_expr_value_.type = Type::I32; - init_expr_value_.value.i32 = value; + init_expr_.kind = InitExprKind::I32; + init_expr_.i32_ = value; return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnInitExprI64ConstExpr(Index index, uint64_t value) { - init_expr_value_.type = Type::I64; - init_expr_value_.value.i64 = value; + init_expr_.kind = InitExprKind::I64; + init_expr_.i64_ = value; return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnInitExprRefNull(Index index) { - init_expr_value_.type = Type::Nullref; - init_expr_value_.set_ref({RefType::Null, kInvalidIndex}); + init_expr_.kind = InitExprKind::RefNull; return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnInitExprRefFunc(Index index, Index func_index) { - init_expr_value_.type = Type::Funcref; +wabt::Result BinaryReaderInterp::OnInitExprRefFunc(Index index, + Index func_index) { + init_expr_.kind = InitExprKind::RefFunc; + init_expr_.index_ = func_index; init_expr_funcs_.push_back(func_index); - init_expr_value_.set_ref({RefType::Func, TranslateFuncIndexToEnv(func_index)}); return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnExport(Index index, - ExternalKind kind, - Index item_index, - string_view name) { +Result BinaryReaderInterp::OnExport(Index index, + ExternalKind kind, + Index item_index, + string_view name) { + auto name_str = name.to_string(); + if (export_names_.find(name_str) != export_names_.end()) { + PrintError("duplicate export \"" PRIstringview "\"", + WABT_PRINTF_STRING_VIEW_ARG(name)); + return Result::Error; + } + export_names_.insert(name_str); + + std::unique_ptr<ExternType> type; switch (kind) { - case ExternalKind::Func: - item_index = TranslateFuncIndexToEnv(item_index); - break; - - case ExternalKind::Table: - item_index = TranslateTableIndexToEnv(item_index); - break; - - case ExternalKind::Memory: - item_index = module_->memory_index; - break; - - case ExternalKind::Global: { - item_index = TranslateGlobalIndexToEnv(item_index); - Global* global = env_->GetGlobal(item_index); - if (global->mutable_ && !features_.mutable_globals_enabled()) { - PrintError("mutable globals cannot be exported"); - return wabt::Result::Error; - } - break; - } - - case ExternalKind::Event: - // TODO(karlschimpf) Define - WABT_FATAL("BinaryReaderInterp::OnExport(event) not implemented"); - break; + case ExternalKind::Func: type = func_types_[item_index].Clone(); break; + case ExternalKind::Table: type = table_types_[item_index].Clone(); break; + case ExternalKind::Memory: type = memory_types_[item_index].Clone(); break; + case ExternalKind::Global: type = global_types_[item_index].Clone(); break; + case ExternalKind::Event: type = event_types_[item_index].Clone(); break; } - return AppendExport(module_, kind, item_index, name); + module_.exports.push_back( + ExportDesc{ExportType(name_str, std::move(type)), item_index}); + return Result::Ok; } wabt::Result BinaryReaderInterp::OnStartFunction(Index func_index) { - Index start_func_index = TranslateFuncIndexToEnv(func_index); - Func* start_func = env_->GetFunc(start_func_index); - FuncSignature* sig = env_->GetFuncSignature(start_func->sig_index); - if (sig->param_types.size() != 0) { + FuncType& func_type = func_types_[func_index]; + if (func_type.params.size() != 0) { PrintError("start function must be nullary"); return wabt::Result::Error; } - if (sig->result_types.size() != 0) { + if (func_type.results.size() != 0) { PrintError("start function must not return anything"); return wabt::Result::Error; } - module_->start_func_index = start_func_index; + module_.starts.push_back(StartDesc{func_index}); return wabt::Result::Ok; } @@ -1121,15 +774,13 @@ wabt::Result BinaryReaderInterp::BeginElemSection(Offset size) { // Delay resizing `declared_funcs_` until we we know the total range of // function indexes (not until after imports sections is read) and that // an elem section exists (therefore the possiblity of declared functions). - Index max_func_index = func_index_mapping_.size(); + Index max_func_index = func_types_.size(); declared_funcs_.resize(max_func_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnElemSegmentCount(Index count) { - for (Index i = 0; i < count; ++i) { - elem_segment_index_mapping_.push_back(env_->GetElemSegmentCount() + i); - } + module_.elems.reserve(count); return wabt::Result::Ok; } @@ -1137,168 +788,126 @@ wabt::Result BinaryReaderInterp::BeginElemSegment(Index index, Index table_index, uint8_t flags, Type elem_type) { - segment_flags_ = static_cast<SegmentFlags>(flags); - segment_table_index_ = table_index; - return wabt::Result::Ok; -} - -wabt::Result BinaryReaderInterp::BeginElemSegmentInitExpr(Index index) { - init_expr_value_.type = Type::Void; + ElemDesc desc; + desc.type = elem_type; + desc.mode = ToSegmentMode(flags); + desc.table_index = table_index; + module_.elems.push_back(desc); + init_expr_.kind = InitExprKind::None; return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::EndElemSegmentInitExpr(Index index) { - assert(!(segment_flags_ & SegPassive)); - - if (init_expr_value_.type != Type::I32) { + ValueType type = GetType(init_expr_); + if (type != ValueType::I32) { PrintError( "type mismatch in elem segment initializer expression, expected i32 " "but got %s", - GetTypeName(init_expr_value_.type)); + GetName(type)); return wabt::Result::Error; } - table_offset_ = init_expr_value_.value.i32; + ElemDesc& elem = module_.elems.back(); + elem.offset = init_expr_; return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnElemSegmentElemExprCount(Index index, Index count) { - elem_segment_ = env_->EmplaceBackElemSegment(segment_flags_); - if (segment_flags_ & SegPassive) { - elem_segment_info_ = nullptr; - } else { - assert(segment_table_index_ != kInvalidIndex); - Table* table = GetTableByModuleIndex(segment_table_index_); - module_->active_elem_segments_.emplace_back(table, table_offset_); - elem_segment_info_ = &module_->active_elem_segments_.back(); - } + ElemDesc& elem = module_.elems.back(); + elem.elements.reserve(count); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnElemSegmentElemExpr_RefNull( Index segment_index) { - assert(segment_flags_ & SegUseElemExprs); - if (segment_flags_ & SegPassive) { - elem_segment_->elems.push_back({RefType::Null, kInvalidIndex}); - } else { - elem_segment_info_->src.push_back({RefType::Null, kInvalidIndex}); - } + ElemDesc& elem = module_.elems.back(); + elem.elements.push_back(ElemExpr{ElemKind::RefNull, 0}); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnElemSegmentElemExpr_RefFunc( - Index index, + Index segment_index, Index func_index) { - Index max_func_index = func_index_mapping_.size(); + Index max_func_index = func_types_.size(); if (func_index >= max_func_index) { PrintError("invalid func_index: %" PRIindex " (max %" PRIindex ")", func_index, max_func_index); return wabt::Result::Error; } - + ElemDesc& elem = module_.elems.back(); + elem.elements.push_back(ElemExpr{ElemKind::RefFunc, func_index}); declared_funcs_[func_index] = true; - func_index = TranslateFuncIndexToEnv(func_index); - - if (segment_flags_ & SegPassive) { - elem_segment_->elems.push_back({RefType::Func, func_index}); - } else { - elem_segment_info_->src.push_back({RefType::Func, func_index}); - } return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnDataCount(Index count) { - for (Index i = 0; i < count; ++i) { - data_segment_index_mapping_.push_back(env_->GetDataSegmentCount() + i); + module_.datas.reserve(count); + return wabt::Result::Ok; +} + +wabt::Result BinaryReaderInterp::EndDataSegmentInitExpr(Index index) { + ValueType type = GetType(init_expr_); + if (type != ValueType::I32) { + PrintError( + "type mismatch in data segment initializer expression, expected i32 " + "but got %s", + GetName(type)); + return wabt::Result::Error; } + + DataDesc& data = module_.datas.back(); + data.offset = init_expr_; return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::BeginDataSegment(Index index, Index memory_index, uint8_t flags) { - segment_flags_ = static_cast<SegmentFlags>(flags); - return wabt::Result::Ok; -} - -wabt::Result BinaryReaderInterp::BeginDataSegmentInitExpr(Index index) { - init_expr_value_.type = Type::Void; + DataDesc desc; + desc.mode = ToSegmentMode(flags); + desc.memory_index = memory_index; + module_.datas.push_back(desc); + init_expr_.kind = InitExprKind::None; return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnDataSegmentData(Index index, const void* src_data, Address size) { - DataSegment* segment = env_->EmplaceBackDataSegment(); - if (segment_flags_ & SegPassive) { - segment->data.resize(size); - if (size > 0) { - memcpy(segment->data.data(), src_data, size); - } - } else { - if (init_expr_value_.type != Type::I32) { - PrintError( - "type mismatch in data segment initializer expression, expected i32 " - "but got %s", - GetTypeName(init_expr_value_.type)); - return wabt::Result::Error; - } - - assert(module_->memory_index != kInvalidIndex); - Memory* memory = env_->GetMemory(module_->memory_index); - Address address = init_expr_value_.value.i32; - module_->active_data_segments_.emplace_back(memory, address); - auto& segment = module_->active_data_segments_.back(); - if (size > 0) { - segment.data.resize(size); - memcpy(segment.data.data(), src_data, size); - } + DataDesc& dst_data = module_.datas.back(); + if (size > 0) { + dst_data.data.resize(size); + memcpy(dst_data.data.data(), src_data, size); } return wabt::Result::Ok; } -void BinaryReaderInterp::PushLabel(IstreamOffset offset, - IstreamOffset fixup_offset) { - label_stack_.emplace_back(offset, fixup_offset); +void BinaryReaderInterp::PushLabel(Istream::Offset offset, + Istream::Offset fixup_offset) { + label_stack_.push_back(Label{offset, fixup_offset}); } void BinaryReaderInterp::PopLabel() { label_stack_.pop_back(); - /* reduce the depth_fixups_ stack as well, but it may be smaller than - * label_stack_ so only do it conditionally. */ - if (depth_fixups_.size() > label_stack_.size()) { - depth_fixups_.erase(depth_fixups_.begin() + label_stack_.size(), - depth_fixups_.end()); - } } wabt::Result BinaryReaderInterp::BeginFunctionBody(Index index, Offset size) { - auto* func = cast<DefinedFunc>(GetFuncByModuleIndex(index)); - FuncSignature* sig = env_->GetFuncSignature(func->sig_index); - - func->offset = GetIstreamOffset(); - func->local_decl_count = 0; - func->local_count = 0; + Index defined_index = index - num_func_imports(); + func_ = &module_.funcs[defined_index]; + func_->code_offset = istream_.end(); - current_func_ = func; - depth_fixups_.clear(); + depth_fixups_.Clear(); label_stack_.clear(); - /* fixup function references */ - Index defined_index = TranslateModuleFuncIndexToDefined(index); - IstreamOffsetVector& fixups = func_fixups_[defined_index]; - for (IstreamOffset fixup : fixups) - CHECK_RESULT(EmitI32At(fixup, func->offset)); + func_fixups_.Resolve(istream_, defined_index); - /* append param types */ - for (Type param_type : sig->param_types) - func->param_and_local_types.push_back(param_type); + param_and_local_types_ = func_->type.params; - CHECK_RESULT(typechecker_.BeginFunction(sig->result_types)); + CHECK_RESULT(typechecker_.BeginFunction(func_->type.results)); - /* push implicit func label (equivalent to return) */ - PushLabel(kInvalidIstreamOffset, kInvalidIstreamOffset); + // Push implicit func label (equivalent to return). + PushLabel(); return wabt::Result::Ok; } @@ -1307,36 +916,37 @@ wabt::Result BinaryReaderInterp::EndFunctionBody(Index index) { Index drop_count, keep_count; CHECK_RESULT(GetReturnDropKeepCount(&drop_count, &keep_count)); CHECK_RESULT(typechecker_.EndFunction()); - CHECK_RESULT(EmitDropKeep(drop_count, keep_count)); - CHECK_RESULT(EmitOpcode(Opcode::Return)); + istream_.EmitDropKeep(drop_count, keep_count); + istream_.Emit(Opcode::Return); PopLabel(); - current_func_ = nullptr; + func_ = nullptr; return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnLocalDeclCount(Index count) { - current_func_->local_decl_count = count; + local_decl_count_ = count; + local_count_ = 0; return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnLocalDecl(Index decl_index, Index count, Type type) { - current_func_->local_count += count; + local_count_ += count; + func_->locals.push_back(LocalDesc{type, count, local_count_}); - for (Index i = 0; i < count; ++i) - current_func_->param_and_local_types.push_back(type); + for (Index i = 0; i < count; ++i) { + param_and_local_types_.push_back(type); + } - if (decl_index == current_func_->local_decl_count - 1) { - /* last local declaration, allocate space for all locals. */ - CHECK_RESULT(EmitOpcode(Opcode::InterpAlloca)); - CHECK_RESULT(EmitI32(current_func_->local_count)); + if (decl_index == local_decl_count_ - 1) { + istream_.Emit(Opcode::InterpAlloca, local_count_); } return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::CheckHasMemory(wabt::Opcode opcode) { - if (module_->memory_index == kInvalidIndex) { + if (memory_types_.empty()) { PrintError("%s requires an imported or defined memory.", opcode.GetName()); return wabt::Result::Error; } @@ -1344,8 +954,8 @@ wabt::Result BinaryReaderInterp::CheckHasMemory(wabt::Opcode opcode) { } wabt::Result BinaryReaderInterp::CheckHasTable(wabt::Opcode opcode) { - if (!has_table) { - PrintError("%s requires an imported or defined table.", opcode.GetName()); + if (table_types_.empty()) { + PrintError("found %s operator, but no table", opcode.GetName()); return wabt::Result::Error; } return wabt::Result::Ok; @@ -1371,51 +981,55 @@ wabt::Result BinaryReaderInterp::CheckAtomicAlign(uint32_t alignment_log2, return wabt::Result::Ok; } +Index BinaryReaderInterp::num_func_imports() const { + return func_types_.size() - module_.funcs.size(); +} + +Index BinaryReaderInterp::num_global_imports() const { + return global_types_.size() - module_.globals.size(); +} + wabt::Result BinaryReaderInterp::OnOpcode(Opcode opcode) { - if (current_func_ == nullptr || label_stack_.size() == 0) { + if (func_ == nullptr || label_stack_.empty()) { PrintError("Unexpected instruction after end of function"); return wabt::Result::Error; } return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnUnaryExpr(wabt::Opcode opcode) { +wabt::Result BinaryReaderInterp::OnUnaryExpr(Opcode opcode) { CHECK_RESULT(typechecker_.OnUnary(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); + istream_.Emit(opcode); return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnTernaryExpr(wabt::Opcode opcode) { +wabt::Result BinaryReaderInterp::OnTernaryExpr(Opcode opcode) { CHECK_RESULT(typechecker_.OnTernary(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); + istream_.Emit(opcode); return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnSimdLaneOpExpr(wabt::Opcode opcode, +wabt::Result BinaryReaderInterp::OnSimdLaneOpExpr(Opcode opcode, uint64_t value) { CHECK_RESULT(typechecker_.OnSimdLaneOp(opcode, value)); - CHECK_RESULT(EmitOpcode(opcode)); - CHECK_RESULT(EmitI8(static_cast<uint8_t>(value))); + istream_.Emit(opcode, static_cast<u8>(value)); return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnSimdShuffleOpExpr(wabt::Opcode opcode, +wabt::Result BinaryReaderInterp::OnSimdShuffleOpExpr(Opcode opcode, v128 value) { CHECK_RESULT(typechecker_.OnSimdShuffleOp(opcode, value)); - CHECK_RESULT(EmitOpcode(opcode)); - CHECK_RESULT(EmitV128(value)); + istream_.Emit(opcode, value); return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnLoadSplatExpr(wabt::Opcode opcode, +wabt::Result BinaryReaderInterp::OnLoadSplatExpr(Opcode opcode, uint32_t alignment_log2, Address offset) { CHECK_RESULT(CheckHasMemory(opcode)); CHECK_RESULT(CheckAlign(alignment_log2, opcode.GetMemorySize())); CHECK_RESULT(typechecker_.OnLoad(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); - CHECK_RESULT(EmitI32(module_->memory_index)); - CHECK_RESULT(EmitI32(offset)); + istream_.Emit(opcode, kMemoryIndex0, offset); return wabt::Result::Ok; } @@ -1425,9 +1039,7 @@ wabt::Result BinaryReaderInterp::OnAtomicLoadExpr(Opcode opcode, CHECK_RESULT(CheckHasMemory(opcode)); CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize())); CHECK_RESULT(typechecker_.OnAtomicLoad(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); - CHECK_RESULT(EmitI32(module_->memory_index)); - CHECK_RESULT(EmitI32(offset)); + istream_.Emit(opcode, kMemoryIndex0, offset); return wabt::Result::Ok; } @@ -1437,9 +1049,7 @@ wabt::Result BinaryReaderInterp::OnAtomicStoreExpr(Opcode opcode, CHECK_RESULT(CheckHasMemory(opcode)); CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize())); CHECK_RESULT(typechecker_.OnAtomicStore(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); - CHECK_RESULT(EmitI32(module_->memory_index)); - CHECK_RESULT(EmitI32(offset)); + istream_.Emit(opcode, kMemoryIndex0, offset); return wabt::Result::Ok; } @@ -1449,9 +1059,7 @@ wabt::Result BinaryReaderInterp::OnAtomicRmwExpr(Opcode opcode, CHECK_RESULT(CheckHasMemory(opcode)); CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize())); CHECK_RESULT(typechecker_.OnAtomicRmw(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); - CHECK_RESULT(EmitI32(module_->memory_index)); - CHECK_RESULT(EmitI32(offset)); + istream_.Emit(opcode, kMemoryIndex0, offset); return wabt::Result::Ok; } @@ -1461,15 +1069,13 @@ wabt::Result BinaryReaderInterp::OnAtomicRmwCmpxchgExpr(Opcode opcode, CHECK_RESULT(CheckHasMemory(opcode)); CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize())); CHECK_RESULT(typechecker_.OnAtomicRmwCmpxchg(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); - CHECK_RESULT(EmitI32(module_->memory_index)); - CHECK_RESULT(EmitI32(offset)); + istream_.Emit(opcode, kMemoryIndex0, offset); return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnBinaryExpr(wabt::Opcode opcode) { +wabt::Result BinaryReaderInterp::OnBinaryExpr(Opcode opcode) { CHECK_RESULT(typechecker_.OnBinary(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); + istream_.Emit(opcode); return wabt::Result::Ok; } @@ -1477,7 +1083,7 @@ wabt::Result BinaryReaderInterp::OnBlockExpr(Type sig_type) { TypeVector param_types, result_types; GetBlockSignature(sig_type, ¶m_types, &result_types); CHECK_RESULT(typechecker_.OnBlock(param_types, result_types)); - PushLabel(kInvalidIstreamOffset, kInvalidIstreamOffset); + PushLabel(); return wabt::Result::Ok; } @@ -1485,7 +1091,7 @@ wabt::Result BinaryReaderInterp::OnLoopExpr(Type sig_type) { TypeVector param_types, result_types; GetBlockSignature(sig_type, ¶m_types, &result_types); CHECK_RESULT(typechecker_.OnLoop(param_types, result_types)); - PushLabel(GetIstreamOffset(), kInvalidIstreamOffset); + PushLabel(istream_.end()); return wabt::Result::Ok; } @@ -1493,21 +1099,19 @@ wabt::Result BinaryReaderInterp::OnIfExpr(Type sig_type) { TypeVector param_types, result_types; GetBlockSignature(sig_type, ¶m_types, &result_types); CHECK_RESULT(typechecker_.OnIf(param_types, result_types)); - CHECK_RESULT(EmitOpcode(Opcode::InterpBrUnless)); - IstreamOffset fixup_offset = GetIstreamOffset(); - CHECK_RESULT(EmitI32(kInvalidIstreamOffset)); - PushLabel(kInvalidIstreamOffset, fixup_offset); + istream_.Emit(Opcode::InterpBrUnless); + auto fixup = istream_.EmitFixupU32(); + PushLabel(Istream::kInvalidOffset, fixup); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnElseExpr() { CHECK_RESULT(typechecker_.OnElse()); Label* label = TopLabel(); - IstreamOffset fixup_cond_offset = label->fixup_offset; - CHECK_RESULT(EmitOpcode(Opcode::Br)); - label->fixup_offset = GetIstreamOffset(); - CHECK_RESULT(EmitI32(kInvalidIstreamOffset)); - CHECK_RESULT(EmitI32At(fixup_cond_offset, GetIstreamOffset())); + Istream::Offset fixup_cond_offset = label->fixup_offset; + istream_.Emit(Opcode::Br); + label->fixup_offset = istream_.EmitFixupU32(); + istream_.ResolveFixupU32(fixup_cond_offset); return wabt::Result::Ok; } @@ -1517,7 +1121,7 @@ wabt::Result BinaryReaderInterp::OnEndExpr() { LabelType label_type = label->label_type; CHECK_RESULT(typechecker_.OnEnd()); if (label_type == LabelType::If || label_type == LabelType::Else) { - CHECK_RESULT(EmitI32At(TopLabel()->fixup_offset, GetIstreamOffset())); + istream_.ResolveFixupU32(TopLabel()->fixup_offset); } FixupTopLabel(); PopLabel(); @@ -1528,7 +1132,7 @@ wabt::Result BinaryReaderInterp::OnBrExpr(Index depth) { Index drop_count, keep_count; CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count)); CHECK_RESULT(typechecker_.OnBr(depth)); - CHECK_RESULT(EmitBr(depth, drop_count, keep_count)); + EmitBr(depth, drop_count, keep_count); return wabt::Result::Ok; } @@ -1536,12 +1140,11 @@ wabt::Result BinaryReaderInterp::OnBrIfExpr(Index depth) { Index drop_count, keep_count; CHECK_RESULT(typechecker_.OnBrIf(depth)); CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count)); - /* flip the br_if so if <cond> is true it can drop values from the stack */ - CHECK_RESULT(EmitOpcode(Opcode::InterpBrUnless)); - IstreamOffset fixup_br_offset = GetIstreamOffset(); - CHECK_RESULT(EmitI32(kInvalidIstreamOffset)); - CHECK_RESULT(EmitBr(depth, drop_count, keep_count)); - CHECK_RESULT(EmitI32At(fixup_br_offset, GetIstreamOffset())); + // Flip the br_if so if <cond> is true it can drop values from the stack. + istream_.Emit(Opcode::InterpBrUnless); + auto fixup = istream_.EmitFixupU32(); + EmitBr(depth, drop_count, keep_count); + istream_.ResolveFixupU32(fixup); return wabt::Result::Ok; } @@ -1549,37 +1152,38 @@ wabt::Result BinaryReaderInterp::OnBrTableExpr(Index num_targets, Index* target_depths, Index default_target_depth) { CHECK_RESULT(typechecker_.BeginBrTable()); - CHECK_RESULT(EmitOpcode(Opcode::BrTable)); - CHECK_RESULT(EmitI32(num_targets)); - IstreamOffset fixup_table_offset = GetIstreamOffset(); - CHECK_RESULT(EmitI32(kInvalidIstreamOffset)); - /* not necessary for the interp, but it makes it easier to disassemble. - * This opcode specifies how many bytes of data follow. */ - CHECK_RESULT(EmitOpcode(Opcode::InterpData)); - CHECK_RESULT(EmitI32((num_targets + 1) * WABT_TABLE_ENTRY_SIZE)); - CHECK_RESULT(EmitI32At(fixup_table_offset, GetIstreamOffset())); - - for (Index i = 0; i <= num_targets; ++i) { - Index depth = i != num_targets ? target_depths[i] : default_target_depth; + Index drop_count, keep_count; + istream_.Emit(Opcode::BrTable, num_targets); + + for (Index i = 0; i < num_targets; ++i) { + Index depth = target_depths[i]; CHECK_RESULT(typechecker_.OnBrTableTarget(depth)); - CHECK_RESULT(EmitBrTableOffset(depth)); + CHECK_RESULT(GetBrDropKeepCount(depth, &drop_count, &keep_count)); + // Emit DropKeep directly (instead of using EmitDropKeep) so the + // instruction has a fixed size. + istream_.Emit(Opcode::InterpDropKeep, drop_count, keep_count); + EmitBr(depth, 0, 0); } + CHECK_RESULT(typechecker_.OnBrTableTarget(default_target_depth)); + CHECK_RESULT( + GetBrDropKeepCount(default_target_depth, &drop_count, &keep_count)); + // The default case doesn't need a fixed size, since it is never jumped over. + istream_.EmitDropKeep(drop_count, keep_count); + EmitBr(default_target_depth, 0, 0); CHECK_RESULT(typechecker_.EndBrTable()); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnCallExpr(Index func_index) { - Func* func = GetFuncByModuleIndex(func_index); - FuncSignature* sig = env_->GetFuncSignature(func->sig_index); - CHECK_RESULT(typechecker_.OnCall(sig->param_types, sig->result_types)); + FuncType& func_type = func_types_[func_index]; + CHECK_RESULT(typechecker_.OnCall(func_type.params, func_type.results)); - if (func->is_host) { - CHECK_RESULT(EmitOpcode(Opcode::InterpCallHost)); - CHECK_RESULT(EmitI32(TranslateFuncIndexToEnv(func_index))); + if (func_index >= num_func_imports()) { + istream_.Emit(Opcode::Call, func_index); } else { - CHECK_RESULT(EmitOpcode(Opcode::Call)); - CHECK_RESULT(EmitFuncOffset(cast<DefinedFunc>(func), func_index)); + // TODO: rename CallImport + istream_.Emit(Opcode::InterpCallHost, func_index); } return wabt::Result::Ok; @@ -1587,38 +1191,32 @@ wabt::Result BinaryReaderInterp::OnCallExpr(Index func_index) { wabt::Result BinaryReaderInterp::OnCallIndirectExpr(Index sig_index, Index table_index) { - if (!has_table) { - PrintError("found call_indirect operator, but no table"); - return wabt::Result::Error; - } - FuncSignature* sig = GetSignatureByModuleIndex(sig_index); + CHECK_RESULT(CheckHasTable(Opcode::CallIndirect)); + FuncType& func_type = module_.func_types[sig_index]; CHECK_RESULT( - typechecker_.OnCallIndirect(sig->param_types, sig->result_types)); + typechecker_.OnCallIndirect(func_type.params, func_type.results)); - CHECK_RESULT(EmitOpcode(Opcode::CallIndirect)); - CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index))); - CHECK_RESULT(EmitI32(TranslateSigIndexToEnv(sig_index))); + istream_.Emit(Opcode::CallIndirect, table_index, sig_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnReturnCallExpr(Index func_index) { - Func* func = GetFuncByModuleIndex(func_index); - FuncSignature* sig = env_->GetFuncSignature(func->sig_index); + FuncType& func_type = func_types_[func_index]; Index drop_count, keep_count; - CHECK_RESULT(GetReturnCallDropKeepCount(sig, 0, &drop_count, &keep_count)); + CHECK_RESULT( + GetReturnCallDropKeepCount(func_type, 0, &drop_count, &keep_count)); // The typechecker must be run after we get the drop/keep counts, since it // will change the type stack. - CHECK_RESULT(typechecker_.OnReturnCall(sig->param_types, sig->result_types)); - CHECK_RESULT(EmitDropKeep(drop_count, keep_count)); + CHECK_RESULT(typechecker_.OnReturnCall(func_type.params, func_type.results)); + istream_.EmitDropKeep(drop_count, keep_count); - if (func->is_host) { - CHECK_RESULT(EmitOpcode(Opcode::InterpCallHost)); - CHECK_RESULT(EmitI32(TranslateFuncIndexToEnv(func_index))); - CHECK_RESULT(EmitOpcode(Opcode::Return)); + if (func_index >= num_func_imports()) { + istream_.Emit(Opcode::Br, GetFuncOffset(func_index)); } else { - CHECK_RESULT(EmitOpcode(Opcode::ReturnCall)); - CHECK_RESULT(EmitFuncOffset(cast<DefinedFunc>(func), func_index)); + // TODO: rename CallImport + istream_.Emit(Opcode::InterpCallHost, func_index); + istream_.Emit(Opcode::Return); } return wabt::Result::Ok; @@ -1626,215 +1224,193 @@ wabt::Result BinaryReaderInterp::OnReturnCallExpr(Index func_index) { wabt::Result BinaryReaderInterp::OnReturnCallIndirectExpr(Index sig_index, Index table_index) { - if (!has_table) { - PrintError("found return_call_indirect operator, but no table"); - return wabt::Result::Error; - } - FuncSignature* sig = GetSignatureByModuleIndex(sig_index); + CHECK_RESULT(CheckHasTable(Opcode::ReturnCallIndirect)); + FuncType& func_type = module_.func_types[sig_index]; Index drop_count, keep_count; // +1 to include the index of the function. - CHECK_RESULT(GetReturnCallDropKeepCount(sig, +1, &drop_count, &keep_count)); + CHECK_RESULT( + GetReturnCallDropKeepCount(func_type, +1, &drop_count, &keep_count)); // The typechecker must be run after we get the drop/keep counts, since it // changes the type stack. CHECK_RESULT( - typechecker_.OnReturnCallIndirect(sig->param_types, sig->result_types)); - CHECK_RESULT(EmitDropKeep(drop_count, keep_count)); - - CHECK_RESULT(EmitOpcode(Opcode::ReturnCallIndirect)); - CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index))); - CHECK_RESULT(EmitI32(TranslateSigIndexToEnv(sig_index))); + typechecker_.OnReturnCallIndirect(func_type.params, func_type.results)); + istream_.EmitDropKeep(drop_count, keep_count); + istream_.Emit(Opcode::ReturnCallIndirect, table_index, sig_index); return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnCompareExpr(wabt::Opcode opcode) { +wabt::Result BinaryReaderInterp::OnCompareExpr(Opcode opcode) { return OnBinaryExpr(opcode); } -wabt::Result BinaryReaderInterp::OnConvertExpr(wabt::Opcode opcode) { +wabt::Result BinaryReaderInterp::OnConvertExpr(Opcode opcode) { return OnUnaryExpr(opcode); } wabt::Result BinaryReaderInterp::OnDropExpr() { CHECK_RESULT(typechecker_.OnDrop()); - CHECK_RESULT(EmitOpcode(Opcode::Drop)); + istream_.Emit(Opcode::Drop); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnI32ConstExpr(uint32_t value) { CHECK_RESULT(typechecker_.OnConst(Type::I32)); - CHECK_RESULT(EmitOpcode(Opcode::I32Const)); - CHECK_RESULT(EmitI32(value)); + istream_.Emit(Opcode::I32Const, value); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnI64ConstExpr(uint64_t value) { CHECK_RESULT(typechecker_.OnConst(Type::I64)); - CHECK_RESULT(EmitOpcode(Opcode::I64Const)); - CHECK_RESULT(EmitI64(value)); + istream_.Emit(Opcode::I64Const, value); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnF32ConstExpr(uint32_t value_bits) { CHECK_RESULT(typechecker_.OnConst(Type::F32)); - CHECK_RESULT(EmitOpcode(Opcode::F32Const)); - CHECK_RESULT(EmitI32(value_bits)); + istream_.Emit(Opcode::F32Const, value_bits); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnF64ConstExpr(uint64_t value_bits) { CHECK_RESULT(typechecker_.OnConst(Type::F64)); - CHECK_RESULT(EmitOpcode(Opcode::F64Const)); - CHECK_RESULT(EmitI64(value_bits)); + istream_.Emit(Opcode::F64Const, value_bits); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnV128ConstExpr(v128 value_bits) { CHECK_RESULT(typechecker_.OnConst(Type::V128)); - CHECK_RESULT(EmitOpcode(Opcode::V128Const)); - CHECK_RESULT(EmitV128(value_bits)); + istream_.Emit(Opcode::V128Const, value_bits); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnGlobalGetExpr(Index global_index) { CHECK_RESULT(CheckGlobal(global_index)); - Type type = GetGlobalTypeByModuleIndex(global_index); - CHECK_RESULT(typechecker_.OnGlobalGet(type)); - CHECK_RESULT(EmitOpcode(Opcode::GlobalGet)); - CHECK_RESULT(EmitI32(TranslateGlobalIndexToEnv(global_index))); + GlobalType& global_type = global_types_[global_index]; + CHECK_RESULT(typechecker_.OnGlobalGet(global_type.type)); + istream_.Emit(Opcode::GlobalGet, global_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnGlobalSetExpr(Index global_index) { CHECK_RESULT(CheckGlobal(global_index)); - Global* global = GetGlobalByModuleIndex(global_index); - if (!global->mutable_) { + GlobalType& global_type = global_types_[global_index]; + if (global_type.mut == Mutability::Const) { PrintError("can't global.set on immutable global at index %" PRIindex ".", global_index); return wabt::Result::Error; } - CHECK_RESULT(typechecker_.OnGlobalSet(global->type)); - CHECK_RESULT(EmitOpcode(Opcode::GlobalSet)); - CHECK_RESULT(EmitI32(TranslateGlobalIndexToEnv(global_index))); + CHECK_RESULT(typechecker_.OnGlobalSet(global_type.type)); + istream_.Emit(Opcode::GlobalSet, global_index); return wabt::Result::Ok; } +ValueType BinaryReaderInterp::GetLocalType(Index local_index) { + return param_and_local_types_[local_index]; +} + Index BinaryReaderInterp::TranslateLocalIndex(Index local_index) { - return typechecker_.type_stack_size() + - current_func_->param_and_local_types.size() - local_index; + return typechecker_.type_stack_size() + param_and_local_types_.size() - + local_index; } wabt::Result BinaryReaderInterp::OnLocalGetExpr(Index local_index) { CHECK_RESULT(CheckLocal(local_index)); - Type type = GetLocalTypeByIndex(current_func_, local_index); // Get the translated index before calling typechecker_.OnLocalGet because it // will update the type stack size. We need the index to be relative to the // old stack size. Index translated_local_index = TranslateLocalIndex(local_index); - CHECK_RESULT(typechecker_.OnLocalGet(type)); - CHECK_RESULT(EmitOpcode(Opcode::LocalGet)); - CHECK_RESULT(EmitI32(translated_local_index)); + CHECK_RESULT(typechecker_.OnLocalGet(GetLocalType(local_index))); + istream_.Emit(Opcode::LocalGet, translated_local_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnLocalSetExpr(Index local_index) { CHECK_RESULT(CheckLocal(local_index)); - Type type = GetLocalTypeByIndex(current_func_, local_index); - CHECK_RESULT(typechecker_.OnLocalSet(type)); - CHECK_RESULT(EmitOpcode(Opcode::LocalSet)); - CHECK_RESULT(EmitI32(TranslateLocalIndex(local_index))); + // See comment in OnLocalGetExpr above. + Index translated_local_index = TranslateLocalIndex(local_index); + CHECK_RESULT(typechecker_.OnLocalSet(GetLocalType(local_index))); + istream_.Emit(Opcode::LocalSet, translated_local_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnLocalTeeExpr(Index local_index) { CHECK_RESULT(CheckLocal(local_index)); - Type type = GetLocalTypeByIndex(current_func_, local_index); - CHECK_RESULT(typechecker_.OnLocalTee(type)); - CHECK_RESULT(EmitOpcode(Opcode::LocalTee)); - CHECK_RESULT(EmitI32(TranslateLocalIndex(local_index))); + CHECK_RESULT(typechecker_.OnLocalTee(GetLocalType(local_index))); + istream_.Emit(Opcode::LocalTee, TranslateLocalIndex(local_index)); return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnLoadExpr(wabt::Opcode opcode, +wabt::Result BinaryReaderInterp::OnLoadExpr(Opcode opcode, uint32_t alignment_log2, Address offset) { CHECK_RESULT(CheckHasMemory(opcode)); CHECK_RESULT(CheckAlign(alignment_log2, opcode.GetMemorySize())); CHECK_RESULT(typechecker_.OnLoad(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); - CHECK_RESULT(EmitI32(module_->memory_index)); - CHECK_RESULT(EmitI32(offset)); + istream_.Emit(opcode, kMemoryIndex0, offset); return wabt::Result::Ok; } -wabt::Result BinaryReaderInterp::OnStoreExpr(wabt::Opcode opcode, +wabt::Result BinaryReaderInterp::OnStoreExpr(Opcode opcode, uint32_t alignment_log2, Address offset) { CHECK_RESULT(CheckHasMemory(opcode)); CHECK_RESULT(CheckAlign(alignment_log2, opcode.GetMemorySize())); CHECK_RESULT(typechecker_.OnStore(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); - CHECK_RESULT(EmitI32(module_->memory_index)); - CHECK_RESULT(EmitI32(offset)); + istream_.Emit(opcode, kMemoryIndex0, offset); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnMemoryGrowExpr() { - CHECK_RESULT(CheckHasMemory(wabt::Opcode::MemoryGrow)); + CHECK_RESULT(CheckHasMemory(Opcode::MemoryGrow)); CHECK_RESULT(typechecker_.OnMemoryGrow()); - CHECK_RESULT(EmitOpcode(Opcode::MemoryGrow)); - CHECK_RESULT(EmitI32(module_->memory_index)); + istream_.Emit(Opcode::MemoryGrow, kMemoryIndex0); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnMemorySizeExpr() { - CHECK_RESULT(CheckHasMemory(wabt::Opcode::MemorySize)); + CHECK_RESULT(CheckHasMemory(Opcode::MemorySize)); CHECK_RESULT(typechecker_.OnMemorySize()); - CHECK_RESULT(EmitOpcode(Opcode::MemorySize)); - CHECK_RESULT(EmitI32(module_->memory_index)); + istream_.Emit(Opcode::MemorySize, kMemoryIndex0); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnTableGrowExpr(Index table_index) { - Table* table = GetTableByModuleIndex(table_index); - CHECK_RESULT(typechecker_.OnTableGrow(table->elem_type)); - CHECK_RESULT(EmitOpcode(Opcode::TableGrow)); - CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index))); + TableType& table_type = table_types_[table_index]; + CHECK_RESULT(typechecker_.OnTableGrow(table_type.element)); + istream_.Emit(Opcode::TableGrow, table_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnTableSizeExpr(Index table_index) { CHECK_RESULT(typechecker_.OnTableSize()); - CHECK_RESULT(EmitOpcode(Opcode::TableSize)); - CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index))); + istream_.Emit(Opcode::TableSize, table_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnTableFillExpr(Index table_index) { - Table* table = GetTableByModuleIndex(table_index); - CHECK_RESULT(typechecker_.OnTableFill(table->elem_type)); - CHECK_RESULT(EmitOpcode(Opcode::TableFill)); - CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index))); + TableType& table_type = table_types_[table_index]; + CHECK_RESULT(typechecker_.OnTableFill(table_type.element)); + istream_.Emit(Opcode::TableFill, table_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnRefFuncExpr(Index func_index) { CHECK_RESULT(CheckDeclaredFunc(func_index)); CHECK_RESULT(typechecker_.OnRefFuncExpr(func_index)); - CHECK_RESULT(EmitOpcode(Opcode::RefFunc)); - CHECK_RESULT(EmitI32(TranslateFuncIndexToEnv(func_index))); + istream_.Emit(Opcode::RefFunc, func_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnRefNullExpr() { CHECK_RESULT(typechecker_.OnRefNullExpr()); - CHECK_RESULT(EmitOpcode(Opcode::RefNull)); + istream_.Emit(Opcode::RefNull); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnRefIsNullExpr() { CHECK_RESULT(typechecker_.OnRefIsNullExpr()); - CHECK_RESULT(EmitOpcode(Opcode::RefIsNull)); + istream_.Emit(Opcode::RefIsNull); return wabt::Result::Ok; } @@ -1846,20 +1422,20 @@ wabt::Result BinaryReaderInterp::OnReturnExpr() { Index drop_count, keep_count; CHECK_RESULT(GetReturnDropKeepCount(&drop_count, &keep_count)); CHECK_RESULT(typechecker_.OnReturn()); - CHECK_RESULT(EmitDropKeep(drop_count, keep_count)); - CHECK_RESULT(EmitOpcode(Opcode::Return)); + istream_.EmitDropKeep(drop_count, keep_count); + istream_.Emit(Opcode::Return); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnSelectExpr(Type result_type) { CHECK_RESULT(typechecker_.OnSelect(result_type)); - CHECK_RESULT(EmitOpcode(Opcode::Select)); + istream_.Emit(Opcode::Select); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnUnreachableExpr() { CHECK_RESULT(typechecker_.OnUnreachable()); - CHECK_RESULT(EmitOpcode(Opcode::Unreachable)); + istream_.Emit(Opcode::Unreachable); return wabt::Result::Ok; } @@ -1869,9 +1445,7 @@ wabt::Result BinaryReaderInterp::OnAtomicWaitExpr(Opcode opcode, CHECK_RESULT(CheckHasMemory(opcode)); CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize())); CHECK_RESULT(typechecker_.OnAtomicWait(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); - CHECK_RESULT(EmitI32(module_->memory_index)); - CHECK_RESULT(EmitI32(offset)); + istream_.Emit(opcode, kMemoryIndex0, offset); return wabt::Result::Ok; } @@ -1881,33 +1455,28 @@ wabt::Result BinaryReaderInterp::OnAtomicNotifyExpr(Opcode opcode, CHECK_RESULT(CheckHasMemory(opcode)); CHECK_RESULT(CheckAtomicAlign(alignment_log2, opcode.GetMemorySize())); CHECK_RESULT(typechecker_.OnAtomicNotify(opcode)); - CHECK_RESULT(EmitOpcode(opcode)); - CHECK_RESULT(EmitI32(module_->memory_index)); - CHECK_RESULT(EmitI32(offset)); + istream_.Emit(opcode, kMemoryIndex0, offset); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnMemoryCopyExpr() { CHECK_RESULT(CheckHasMemory(Opcode::MemoryCopy)); CHECK_RESULT(typechecker_.OnMemoryCopy()); - CHECK_RESULT(EmitOpcode(Opcode::MemoryCopy)); - CHECK_RESULT(EmitI32(module_->memory_index)); + istream_.Emit(Opcode::MemoryCopy, kMemoryIndex0, kMemoryIndex0); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnDataDropExpr(Index segment_index) { CHECK_RESULT(CheckDataSegment(segment_index)); CHECK_RESULT(typechecker_.OnDataDrop(segment_index)); - CHECK_RESULT(EmitOpcode(Opcode::DataDrop)); - CHECK_RESULT(EmitI32(TranslateDataSegmentIndexToEnv(segment_index))); + istream_.Emit(Opcode::DataDrop, segment_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnMemoryFillExpr() { CHECK_RESULT(CheckHasMemory(Opcode::MemoryFill)); CHECK_RESULT(typechecker_.OnMemoryFill()); - CHECK_RESULT(EmitOpcode(Opcode::MemoryFill)); - CHECK_RESULT(EmitI32(module_->memory_index)); + istream_.Emit(Opcode::MemoryFill, kMemoryIndex0); return wabt::Result::Ok; } @@ -1915,25 +1484,21 @@ wabt::Result BinaryReaderInterp::OnMemoryInitExpr(Index segment_index) { CHECK_RESULT(CheckHasMemory(Opcode::MemoryInit)); CHECK_RESULT(CheckDataSegment(segment_index)); CHECK_RESULT(typechecker_.OnMemoryInit(segment_index)); - CHECK_RESULT(EmitOpcode(Opcode::MemoryInit)); - CHECK_RESULT(EmitI32(module_->memory_index)); - CHECK_RESULT(EmitI32(TranslateDataSegmentIndexToEnv(segment_index))); + istream_.Emit(Opcode::MemoryInit, kMemoryIndex0, segment_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnTableGetExpr(Index table_index) { - const Table* table = GetTableByModuleIndex(table_index); - CHECK_RESULT(typechecker_.OnTableGet(table->elem_type)); - CHECK_RESULT(EmitOpcode(Opcode::TableGet)); - CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index))); + TableType& table_type = table_types_[table_index]; + CHECK_RESULT(typechecker_.OnTableGet(table_type.element)); + istream_.Emit(Opcode::TableGet, table_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnTableSetExpr(Index table_index) { - const Table* table = GetTableByModuleIndex(table_index); - CHECK_RESULT(typechecker_.OnTableSet(table->elem_type)); - CHECK_RESULT(EmitOpcode(Opcode::TableSet)); - CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index))); + TableType& table_type = table_types_[table_index]; + CHECK_RESULT(typechecker_.OnTableSet(table_type.element)); + istream_.Emit(Opcode::TableSet, table_index); return wabt::Result::Ok; } @@ -1941,17 +1506,14 @@ wabt::Result BinaryReaderInterp::OnTableCopyExpr(Index dst_index, Index src_index) { CHECK_RESULT(CheckHasTable(Opcode::TableCopy)); CHECK_RESULT(typechecker_.OnTableCopy()); - CHECK_RESULT(EmitOpcode(Opcode::TableCopy)); - CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(dst_index))); - CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(src_index))); + istream_.Emit(Opcode::TableCopy, dst_index, src_index); return wabt::Result::Ok; } wabt::Result BinaryReaderInterp::OnElemDropExpr(Index segment_index) { CHECK_RESULT(CheckElemSegment(segment_index)); CHECK_RESULT(typechecker_.OnElemDrop(segment_index)); - CHECK_RESULT(EmitOpcode(Opcode::ElemDrop)); - CHECK_RESULT(EmitI32(TranslateElemSegmentIndexToEnv(segment_index))); + istream_.Emit(Opcode::ElemDrop, segment_index); return wabt::Result::Ok; } @@ -1960,55 +1522,20 @@ wabt::Result BinaryReaderInterp::OnTableInitExpr(Index segment_index, CHECK_RESULT(CheckHasTable(Opcode::TableInit)); CHECK_RESULT(CheckElemSegment(segment_index)); CHECK_RESULT(typechecker_.OnTableInit(table_index, segment_index)); - CHECK_RESULT(EmitOpcode(Opcode::TableInit)); - CHECK_RESULT(EmitI32(TranslateTableIndexToEnv(table_index))); - CHECK_RESULT(EmitI32(TranslateElemSegmentIndexToEnv(segment_index))); + istream_.Emit(Opcode::TableInit, table_index, segment_index); return wabt::Result::Ok; } -} // end anonymous namespace - -wabt::Result ReadBinaryInterp(Environment* env, - const void* data, - size_t size, - const ReadBinaryOptions& options, - const std::vector<Export*>& imports, - Errors* errors, - DefinedModule** out_module) { - // Need to mark before taking ownership of env->istream. - Environment::MarkPoint mark = env->Mark(); - - std::unique_ptr<OutputBuffer> istream = env->ReleaseIstream(); - IstreamOffset istream_offset = istream->size(); - DefinedModule* module = new DefinedModule(env); - - BinaryReaderInterp reader(env, module, std::move(istream), imports, errors, - options.features); - env->EmplaceBackModule(module); - - wabt::Result result = ReadBinary(data, size, &reader, options); - env->SetIstream(reader.ReleaseOutputBuffer()); - - if (Failed(result)) { - env->ResetToMarkPoint(mark); - return result; - } - - *out_module = module; - module->istream_start = istream_offset; - module->istream_end = env->istream().size(); - return result; -} +} // namespace -wabt::Result ReadBinaryInterp(Environment* env, - const void* data, +wabt::Result ReadBinaryInterp(const void* data, size_t size, const ReadBinaryOptions& options, Errors* errors, - DefinedModule** out_module) { - std::vector<Export*> empty_imports; - return ReadBinaryInterp(env, data, size, options, empty_imports, errors, - out_module); + ModuleDesc* out_module) { + BinaryReaderInterp reader(out_module, errors, options.features); + return ReadBinary(data, size, &reader, options); } +} // namespace interp } // namespace wabt diff --git a/src/interp/binary-reader-interp.h b/src/interp/binary-reader-interp.h index e6110f5e..0197e9aa 100644 --- a/src/interp/binary-reader-interp.h +++ b/src/interp/binary-reader-interp.h @@ -19,37 +19,21 @@ #include "src/common.h" #include "src/error.h" +#include "src/interp/interp.h" namespace wabt { -namespace interp { - -struct Export; -struct DefinedModule; -class Environment; - -} // namespace interp - struct ReadBinaryOptions; -// Read and instantiate a module in the given environment. -Result ReadBinaryInterp(interp::Environment* env, - const void* data, - size_t size, - const ReadBinaryOptions& options, - Errors*, - interp::DefinedModule** out_module); +namespace interp { -// Read and instantiate a module in the given environment. Similar to above but -// using using a fixed set of exports to resolve the module exports. -Result ReadBinaryInterp(interp::Environment* env, - const void* data, +Result ReadBinaryInterp(const void* data, size_t size, const ReadBinaryOptions& options, - const std::vector<interp::Export*>& imports, Errors*, - interp::DefinedModule** out_module); + ModuleDesc* out_module); +} // namespace interp } // namespace wabt #endif /* WABT_BINARY_READER_INTERP_H_ */ diff --git a/src/interp/binary-reader-metadata.cc b/src/interp/binary-reader-metadata.cc deleted file mode 100644 index 8cbed78e..00000000 --- a/src/interp/binary-reader-metadata.cc +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2020 WebAssembly Community Group participants - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/interp/binary-reader-metadata.h" - -#include "src/binary-reader-nop.h" -#include "src/binary-reader.h" -#include "src/interp/interp.h" -#include "src/string-view.h" - -namespace wabt { - -using namespace interp; - -namespace { - -class BinaryReaderMetadata : public BinaryReaderNop { - public: - BinaryReaderMetadata(ModuleMetadata* metadata, - Errors* errors, - const Features& features) - : metadata_(metadata) {} - wabt::Result OnImport(Index index, - ExternalKind kind, - string_view module_name, - string_view field_name) override { - metadata_->imports.emplace_back(kind, module_name, field_name); - return wabt::Result::Ok; - } - - wabt::Result OnExport(Index index, - ExternalKind kind, - Index item_index, - string_view name) override { - metadata_->exports.emplace_back(name, kind, item_index); - return wabt::Result::Ok; - } - - private: - ModuleMetadata* metadata_; -}; - -} // end anonymous namespace - -wabt::Result ReadBinaryMetadata(const void* data, - size_t size, - const ReadBinaryOptions& options, - Errors* errors, - ModuleMetadata** out_metadata) { - ModuleMetadata* metadata = new ModuleMetadata(); - BinaryReaderMetadata reader(metadata, errors, options.features); - wabt::Result result = ReadBinary(data, size, &reader, options); - if (!Succeeded(result)) { - delete metadata; - return result; - } - *out_metadata = metadata; - return result; -} - -} // namespace wabt diff --git a/src/interp/interp-disassemble.cc b/src/interp/interp-disassemble.cc deleted file mode 100644 index 729b15fa..00000000 --- a/src/interp/interp-disassemble.cc +++ /dev/null @@ -1,699 +0,0 @@ -/* - * Copyright 2018 WebAssembly Community Group participants - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "src/interp/interp.h" - -#include <cinttypes> - -#include "src/cast.h" -#include "src/interp/interp-internal.h" - -namespace wabt { -namespace interp { - -void Environment::Disassemble(Stream* stream, - IstreamOffset from, - IstreamOffset to) { - /* TODO(binji): mark function entries */ - /* TODO(binji): track value stack size */ - if (from >= istream_->data.size()) { - return; - } - to = std::min<IstreamOffset>(to, istream_->data.size()); - const uint8_t* istream = istream_->data.data(); - const uint8_t* pc = &istream[from]; - - while (static_cast<IstreamOffset>(pc - istream) < to) { - stream->Writef("%4" PRIzd "| ", pc - istream); - - Opcode opcode = ReadOpcode(&pc); - assert(!opcode.IsInvalid()); - switch (opcode) { - case Opcode::Select: - case Opcode::SelectT: - case Opcode::V128BitSelect: - stream->Writef("%s %%[-3], %%[-2], %%[-1]\n", opcode.GetName()); - break; - - case Opcode::Br: - stream->Writef("%s @%u\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::BrIf: - stream->Writef("%s @%u, %%[-1]\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::BrTable: { - const Index num_targets = ReadU32(&pc); - const IstreamOffset table_offset = ReadU32(&pc); - stream->Writef("%s %%[-1], $#%" PRIindex ", table:$%u\n", - opcode.GetName(), num_targets, table_offset); - break; - } - - case Opcode::Nop: - case Opcode::Return: - case Opcode::Unreachable: - case Opcode::Drop: - stream->Writef("%s\n", opcode.GetName()); - break; - - case Opcode::MemorySize: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex "\n", opcode.GetName(), memory_index); - break; - } - - case Opcode::I32Const: - stream->Writef("%s %u\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::I64Const: - stream->Writef("%s %" PRIu64 "\n", opcode.GetName(), ReadU64(&pc)); - break; - - case Opcode::F32Const: - stream->Writef("%s %g\n", opcode.GetName(), - Bitcast<float>(ReadU32(&pc))); - break; - - case Opcode::F64Const: - stream->Writef("%s %g\n", opcode.GetName(), - Bitcast<double>(ReadU64(&pc))); - break; - - case Opcode::LocalGet: - case Opcode::GlobalGet: - stream->Writef("%s $%u\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::LocalSet: - case Opcode::GlobalSet: - case Opcode::LocalTee: - stream->Writef("%s $%u, %%[-1]\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::Call: - case Opcode::ReturnCall: - stream->Writef("%s @%u\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::CallIndirect: - case Opcode::ReturnCallIndirect: { - const Index table_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%u, %%[-1]\n", opcode.GetName(), - table_index, ReadU32(&pc)); - break; - } - - case Opcode::InterpCallHost: - stream->Writef("%s $%u\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::I32AtomicLoad: - case Opcode::I64AtomicLoad: - case Opcode::I32AtomicLoad8U: - case Opcode::I32AtomicLoad16U: - case Opcode::I64AtomicLoad8U: - case Opcode::I64AtomicLoad16U: - case Opcode::I64AtomicLoad32U: - case Opcode::I32Load8S: - case Opcode::I32Load8U: - case Opcode::I32Load16S: - case Opcode::I32Load16U: - case Opcode::I64Load8S: - case Opcode::I64Load8U: - case Opcode::I64Load16S: - case Opcode::I64Load16U: - case Opcode::I64Load32S: - case Opcode::I64Load32U: - case Opcode::I32Load: - case Opcode::I64Load: - case Opcode::F32Load: - case Opcode::F64Load: - case Opcode::V128Load: - case Opcode::V8X16LoadSplat: - case Opcode::V16X8LoadSplat: - case Opcode::V32X4LoadSplat: - case Opcode::V64X2LoadSplat: - case Opcode::I16X8Load8X8S: - case Opcode::I16X8Load8X8U: - case Opcode::I32X4Load16X4S: - case Opcode::I32X4Load16X4U: - case Opcode::I64X2Load32X2S: - case Opcode::I64X2Load32X2U: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%%[-1]+$%u\n", opcode.GetName(), - memory_index, ReadU32(&pc)); - break; - } - - case Opcode::AtomicNotify: - case Opcode::I32AtomicStore: - case Opcode::I64AtomicStore: - case Opcode::I32AtomicStore8: - case Opcode::I32AtomicStore16: - case Opcode::I64AtomicStore8: - case Opcode::I64AtomicStore16: - case Opcode::I64AtomicStore32: - case Opcode::I32AtomicRmwAdd: - case Opcode::I64AtomicRmwAdd: - case Opcode::I32AtomicRmw8AddU: - case Opcode::I32AtomicRmw16AddU: - case Opcode::I64AtomicRmw8AddU: - case Opcode::I64AtomicRmw16AddU: - case Opcode::I64AtomicRmw32AddU: - case Opcode::I32AtomicRmwSub: - case Opcode::I64AtomicRmwSub: - case Opcode::I32AtomicRmw8SubU: - case Opcode::I32AtomicRmw16SubU: - case Opcode::I64AtomicRmw8SubU: - case Opcode::I64AtomicRmw16SubU: - case Opcode::I64AtomicRmw32SubU: - case Opcode::I32AtomicRmwAnd: - case Opcode::I64AtomicRmwAnd: - case Opcode::I32AtomicRmw8AndU: - case Opcode::I32AtomicRmw16AndU: - case Opcode::I64AtomicRmw8AndU: - case Opcode::I64AtomicRmw16AndU: - case Opcode::I64AtomicRmw32AndU: - case Opcode::I32AtomicRmwOr: - case Opcode::I64AtomicRmwOr: - case Opcode::I32AtomicRmw8OrU: - case Opcode::I32AtomicRmw16OrU: - case Opcode::I64AtomicRmw8OrU: - case Opcode::I64AtomicRmw16OrU: - case Opcode::I64AtomicRmw32OrU: - case Opcode::I32AtomicRmwXor: - case Opcode::I64AtomicRmwXor: - case Opcode::I32AtomicRmw8XorU: - case Opcode::I32AtomicRmw16XorU: - case Opcode::I64AtomicRmw8XorU: - case Opcode::I64AtomicRmw16XorU: - case Opcode::I64AtomicRmw32XorU: - case Opcode::I32AtomicRmwXchg: - case Opcode::I64AtomicRmwXchg: - case Opcode::I32AtomicRmw8XchgU: - case Opcode::I32AtomicRmw16XchgU: - case Opcode::I64AtomicRmw8XchgU: - case Opcode::I64AtomicRmw16XchgU: - case Opcode::I64AtomicRmw32XchgU: - case Opcode::I32Store8: - case Opcode::I32Store16: - case Opcode::I32Store: - case Opcode::I64Store8: - case Opcode::I64Store16: - case Opcode::I64Store32: - case Opcode::I64Store: - case Opcode::F32Store: - case Opcode::F64Store: - case Opcode::V128Store: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%%[-2]+$%u, %%[-1]\n", - opcode.GetName(), memory_index, ReadU32(&pc)); - break; - } - - case Opcode::I32AtomicWait: - case Opcode::I64AtomicWait: - case Opcode::I32AtomicRmwCmpxchg: - case Opcode::I64AtomicRmwCmpxchg: - case Opcode::I32AtomicRmw8CmpxchgU: - case Opcode::I32AtomicRmw16CmpxchgU: - case Opcode::I64AtomicRmw8CmpxchgU: - case Opcode::I64AtomicRmw16CmpxchgU: - case Opcode::I64AtomicRmw32CmpxchgU: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%%[-3]+$%u, %%[-2], %%[-1]\n", - opcode.GetName(), memory_index, ReadU32(&pc)); - break; - } - - case Opcode::I32Add: - case Opcode::I32Sub: - case Opcode::I32Mul: - case Opcode::I32DivS: - case Opcode::I32DivU: - case Opcode::I32RemS: - case Opcode::I32RemU: - case Opcode::I32And: - case Opcode::I32Or: - case Opcode::I32Xor: - case Opcode::I32Shl: - case Opcode::I32ShrU: - case Opcode::I32ShrS: - case Opcode::I32Eq: - case Opcode::I32Ne: - case Opcode::I32LtS: - case Opcode::I32LeS: - case Opcode::I32LtU: - case Opcode::I32LeU: - case Opcode::I32GtS: - case Opcode::I32GeS: - case Opcode::I32GtU: - case Opcode::I32GeU: - case Opcode::I32Rotr: - case Opcode::I32Rotl: - case Opcode::F32Add: - case Opcode::F32Sub: - case Opcode::F32Mul: - case Opcode::F32Div: - case Opcode::F32Min: - case Opcode::F32Max: - case Opcode::F32Copysign: - case Opcode::F32Eq: - case Opcode::F32Ne: - case Opcode::F32Lt: - case Opcode::F32Le: - case Opcode::F32Gt: - case Opcode::F32Ge: - case Opcode::I64Add: - case Opcode::I64Sub: - case Opcode::I64Mul: - case Opcode::I64DivS: - case Opcode::I64DivU: - case Opcode::I64RemS: - case Opcode::I64RemU: - case Opcode::I64And: - case Opcode::I64Or: - case Opcode::I64Xor: - case Opcode::I64Shl: - case Opcode::I64ShrU: - case Opcode::I64ShrS: - case Opcode::I64Eq: - case Opcode::I64Ne: - case Opcode::I64LtS: - case Opcode::I64LeS: - case Opcode::I64LtU: - case Opcode::I64LeU: - case Opcode::I64GtS: - case Opcode::I64GeS: - case Opcode::I64GtU: - case Opcode::I64GeU: - case Opcode::I64Rotr: - case Opcode::I64Rotl: - case Opcode::F64Add: - case Opcode::F64Sub: - case Opcode::F64Mul: - case Opcode::F64Div: - case Opcode::F64Min: - case Opcode::F64Max: - case Opcode::F64Copysign: - case Opcode::F64Eq: - case Opcode::F64Ne: - case Opcode::F64Lt: - case Opcode::F64Le: - case Opcode::F64Gt: - case Opcode::F64Ge: - case Opcode::I8X16Add: - case Opcode::I16X8Add: - case Opcode::I32X4Add: - case Opcode::I64X2Add: - case Opcode::I8X16Sub: - case Opcode::I16X8Sub: - case Opcode::I32X4Sub: - case Opcode::I64X2Sub: - case Opcode::I16X8Mul: - case Opcode::I32X4Mul: - case Opcode::I64X2Mul: - case Opcode::I8X16AddSaturateS: - case Opcode::I8X16AddSaturateU: - case Opcode::I16X8AddSaturateS: - case Opcode::I16X8AddSaturateU: - case Opcode::I8X16SubSaturateS: - case Opcode::I8X16SubSaturateU: - case Opcode::I16X8SubSaturateS: - case Opcode::I16X8SubSaturateU: - case Opcode::I8X16MinS: - case Opcode::I16X8MinS: - case Opcode::I32X4MinS: - case Opcode::I8X16MinU: - case Opcode::I16X8MinU: - case Opcode::I32X4MinU: - case Opcode::I8X16MaxS: - case Opcode::I16X8MaxS: - case Opcode::I32X4MaxS: - case Opcode::I8X16MaxU: - case Opcode::I16X8MaxU: - case Opcode::I32X4MaxU: - case Opcode::I8X16Shl: - case Opcode::I16X8Shl: - case Opcode::I32X4Shl: - case Opcode::I64X2Shl: - case Opcode::I8X16ShrS: - case Opcode::I8X16ShrU: - case Opcode::I16X8ShrS: - case Opcode::I16X8ShrU: - case Opcode::I32X4ShrS: - case Opcode::I32X4ShrU: - case Opcode::I64X2ShrS: - case Opcode::I64X2ShrU: - case Opcode::V128And: - case Opcode::V128Or: - case Opcode::V128Xor: - case Opcode::I8X16Eq: - case Opcode::I16X8Eq: - case Opcode::I32X4Eq: - case Opcode::F32X4Eq: - case Opcode::F64X2Eq: - case Opcode::I8X16Ne: - case Opcode::I16X8Ne: - case Opcode::I32X4Ne: - case Opcode::F32X4Ne: - case Opcode::F64X2Ne: - case Opcode::I8X16LtS: - case Opcode::I8X16LtU: - case Opcode::I16X8LtS: - case Opcode::I16X8LtU: - case Opcode::I32X4LtS: - case Opcode::I32X4LtU: - case Opcode::F32X4Lt: - case Opcode::F64X2Lt: - case Opcode::I8X16LeS: - case Opcode::I8X16LeU: - case Opcode::I16X8LeS: - case Opcode::I16X8LeU: - case Opcode::I32X4LeS: - case Opcode::I32X4LeU: - case Opcode::F32X4Le: - case Opcode::F64X2Le: - case Opcode::I8X16GtS: - case Opcode::I8X16GtU: - case Opcode::I16X8GtS: - case Opcode::I16X8GtU: - case Opcode::I32X4GtS: - case Opcode::I32X4GtU: - case Opcode::F32X4Gt: - case Opcode::F64X2Gt: - case Opcode::I8X16GeS: - case Opcode::I8X16GeU: - case Opcode::I16X8GeS: - case Opcode::I16X8GeU: - case Opcode::I32X4GeS: - case Opcode::I32X4GeU: - case Opcode::F32X4Ge: - case Opcode::F64X2Ge: - case Opcode::F32X4Min: - case Opcode::F64X2Min: - case Opcode::F32X4Max: - case Opcode::F64X2Max: - case Opcode::F32X4Add: - case Opcode::F64X2Add: - case Opcode::F32X4Sub: - case Opcode::F64X2Sub: - case Opcode::F32X4Div: - case Opcode::F64X2Div: - case Opcode::F32X4Mul: - case Opcode::F64X2Mul: - case Opcode::V8X16Swizzle: - case Opcode::I8X16NarrowI16X8S: - case Opcode::I8X16NarrowI16X8U: - case Opcode::I16X8NarrowI32X4S: - case Opcode::I16X8NarrowI32X4U: - case Opcode::V128Andnot: - case Opcode::I8X16AvgrU: - case Opcode::I16X8AvgrU: - stream->Writef("%s %%[-2], %%[-1]\n", opcode.GetName()); - break; - - case Opcode::I32Clz: - case Opcode::I32Ctz: - case Opcode::I32Popcnt: - case Opcode::I32Eqz: - case Opcode::I64Clz: - case Opcode::I64Ctz: - case Opcode::I64Popcnt: - case Opcode::I64Eqz: - case Opcode::F32Abs: - case Opcode::F32Neg: - case Opcode::F32Ceil: - case Opcode::F32Floor: - case Opcode::F32Trunc: - case Opcode::F32Nearest: - case Opcode::F32Sqrt: - case Opcode::F64Abs: - case Opcode::F64Neg: - case Opcode::F64Ceil: - case Opcode::F64Floor: - case Opcode::F64Trunc: - case Opcode::F64Nearest: - case Opcode::F64Sqrt: - case Opcode::I32TruncF32S: - case Opcode::I32TruncF32U: - case Opcode::I64TruncF32S: - case Opcode::I64TruncF32U: - case Opcode::F64PromoteF32: - case Opcode::I32ReinterpretF32: - case Opcode::I32TruncF64S: - case Opcode::I32TruncF64U: - case Opcode::I64TruncF64S: - case Opcode::I64TruncF64U: - case Opcode::F32DemoteF64: - case Opcode::I64ReinterpretF64: - case Opcode::I32WrapI64: - case Opcode::F32ConvertI64S: - case Opcode::F32ConvertI64U: - case Opcode::F64ConvertI64S: - case Opcode::F64ConvertI64U: - case Opcode::F64ReinterpretI64: - case Opcode::I64ExtendI32S: - case Opcode::I64ExtendI32U: - case Opcode::F32ConvertI32S: - case Opcode::F32ConvertI32U: - case Opcode::F32ReinterpretI32: - case Opcode::F64ConvertI32S: - case Opcode::F64ConvertI32U: - case Opcode::I32TruncSatF32S: - case Opcode::I32TruncSatF32U: - case Opcode::I64TruncSatF32S: - case Opcode::I64TruncSatF32U: - case Opcode::I32TruncSatF64S: - case Opcode::I32TruncSatF64U: - case Opcode::I64TruncSatF64S: - case Opcode::I64TruncSatF64U: - case Opcode::I32Extend16S: - case Opcode::I32Extend8S: - case Opcode::I64Extend16S: - case Opcode::I64Extend32S: - case Opcode::I64Extend8S: - case Opcode::I8X16Splat: - case Opcode::I16X8Splat: - case Opcode::I32X4Splat: - case Opcode::I64X2Splat: - case Opcode::F32X4Splat: - case Opcode::F64X2Splat: - case Opcode::I8X16Neg: - case Opcode::I16X8Neg: - case Opcode::I32X4Neg: - case Opcode::I64X2Neg: - case Opcode::V128Not: - case Opcode::I8X16AnyTrue: - case Opcode::I16X8AnyTrue: - case Opcode::I32X4AnyTrue: - case Opcode::I8X16AllTrue: - case Opcode::I16X8AllTrue: - case Opcode::I32X4AllTrue: - case Opcode::F32X4Neg: - case Opcode::F64X2Neg: - case Opcode::F32X4Abs: - case Opcode::F64X2Abs: - case Opcode::F32X4Sqrt: - case Opcode::F64X2Sqrt: - case Opcode::F32X4ConvertI32X4S: - case Opcode::F32X4ConvertI32X4U: - case Opcode::I32X4TruncSatF32X4S: - case Opcode::I32X4TruncSatF32X4U: - case Opcode::I16X8WidenLowI8X16S: - case Opcode::I16X8WidenHighI8X16S: - case Opcode::I16X8WidenLowI8X16U: - case Opcode::I16X8WidenHighI8X16U: - case Opcode::I32X4WidenLowI16X8S: - case Opcode::I32X4WidenHighI16X8S: - case Opcode::I32X4WidenLowI16X8U: - case Opcode::I32X4WidenHighI16X8U: - stream->Writef("%s %%[-1]\n", opcode.GetName()); - break; - - case Opcode::I8X16ExtractLaneS: - case Opcode::I8X16ExtractLaneU: - case Opcode::I16X8ExtractLaneS: - case Opcode::I16X8ExtractLaneU: - case Opcode::I32X4ExtractLane: - case Opcode::I64X2ExtractLane: - case Opcode::F32X4ExtractLane: - case Opcode::F64X2ExtractLane: { - stream->Writef("%s %%[-1] : (Lane imm: %d)\n", opcode.GetName(), - ReadU8(&pc)); - break; - } - - case Opcode::I8X16ReplaceLane: - case Opcode::I16X8ReplaceLane: - case Opcode::I32X4ReplaceLane: - case Opcode::I64X2ReplaceLane: - case Opcode::F32X4ReplaceLane: - case Opcode::F64X2ReplaceLane: { - stream->Writef("%s %%[-1], %%[-2] : (Lane imm: %d)\n", - opcode.GetName(), ReadU8(&pc)); - break; - } - - case Opcode::V8X16Shuffle: - stream->Writef( - "%s %%[-2], %%[-1] : (Lane imm: $0x%08x 0x%08x 0x%08x 0x%08x )\n", - opcode.GetName(), ReadU32(&pc), ReadU32(&pc), ReadU32(&pc), - ReadU32(&pc)); - break; - - case Opcode::MemoryGrow: { - Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%%[-1]\n", opcode.GetName(), - memory_index); - break; - } - - case Opcode::InterpAlloca: - stream->Writef("%s $%u\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::InterpBrUnless: - stream->Writef("%s @%u, %%[-1]\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::InterpDropKeep: { - const uint32_t drop = ReadU32(&pc); - const uint32_t keep = ReadU32(&pc); - stream->Writef("%s $%u $%u\n", opcode.GetName(), drop, keep); - break; - } - - case Opcode::InterpData: { - const uint32_t num_bytes = ReadU32(&pc); - stream->Writef("%s $%u\n", opcode.GetName(), num_bytes); - /* for now, the only reason this is emitted is for br_table, so display - * it as a list of table entries */ - if (num_bytes % WABT_TABLE_ENTRY_SIZE == 0) { - Index num_entries = num_bytes / WABT_TABLE_ENTRY_SIZE; - for (Index i = 0; i < num_entries; ++i) { - stream->Writef("%4" PRIzd "| ", pc - istream); - IstreamOffset offset; - uint32_t drop; - uint32_t keep; - ReadTableEntryAt(pc, &offset, &drop, &keep); - stream->Writef(" entry %" PRIindex - ": offset: %u drop: %u keep: %u\n", - i, offset, drop, keep); - pc += WABT_TABLE_ENTRY_SIZE; - } - } else { - /* just skip those data bytes */ - pc += num_bytes; - } - - break; - } - - case Opcode::V128Const: { - stream->Writef("%s i32x4 0x%08x 0x%08x 0x%08x 0x%08x\n", opcode.GetName(), - ReadU32(&pc), ReadU32(&pc), ReadU32(&pc), ReadU32(&pc)); - - break; - } - - case Opcode::TableGet: - stream->Writef("%s %u %%[-1]\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::TableSet: - stream->Writef("%s $%u %%[-1] %%[-2]\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::TableGrow: - case Opcode::TableSize: - break; - - case Opcode::TableFill: - stream->Writef("%s %u %%[-3], %%[-2], %%[-1]\n", opcode.GetName(), - ReadU32(&pc)); - break; - - case Opcode::RefNull: - stream->Writef("%s\n", opcode.GetName()); - break; - - case Opcode::RefFunc: - stream->Writef("%s $%u\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::RefIsNull: - stream->Writef("%s %%[-1]\n", opcode.GetName()); - break; - - case Opcode::MemoryInit: - case Opcode::TableInit: { - Index index = ReadU32(&pc); - Index segment_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ", $%" PRIindex - ", %%[-3], %%[-2], %%[-1]\n", - opcode.GetName(), index, segment_index); - break; - } - - case Opcode::DataDrop: - case Opcode::ElemDrop: - stream->Writef("%s $%u\n", opcode.GetName(), ReadU32(&pc)); - break; - - case Opcode::MemoryCopy: - case Opcode::MemoryFill: - stream->Writef("%s $%u, %%[-3], %%[-2], %%[-1]\n", opcode.GetName(), - ReadU32(&pc)); - break; - - case Opcode::TableCopy: - stream->Writef("%s $%u, $%u, %%[-3], %%[-2], %%[-1]\n", - opcode.GetName(), ReadU32(&pc), ReadU32(&pc)); - break; - - // The following opcodes are either never generated or should never be - // executed. - case Opcode::Block: - case Opcode::BrOnExn: - case Opcode::Catch: - case Opcode::Else: - case Opcode::End: - case Opcode::If: - case Opcode::Invalid: - case Opcode::Loop: - case Opcode::Rethrow: - case Opcode::Throw: - case Opcode::Try: - fprintf(stderr, "unknown opcode: %#x\n", static_cast<uint32_t>(opcode)); - WABT_UNREACHABLE; - break; - } - } -} - -void Environment::DisassembleModule(Stream* stream, Module* module) { - assert(!module->is_host); - auto* defined_module = cast<DefinedModule>(module); - Disassemble(stream, defined_module->istream_start, - defined_module->istream_end); -} - -} // namespace interp -} // namespace wabt diff --git a/src/interp/interp-inl.h b/src/interp/interp-inl.h new file mode 100644 index 00000000..8699438d --- /dev/null +++ b/src/interp/interp-inl.h @@ -0,0 +1,911 @@ +/* + * Copyright 2020 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <cassert> +#include <limits> +#include <string> + +namespace wabt { +namespace interp { + +//// Ref //// +inline Ref::Ref(size_t index) : index(index) {} + +inline bool operator==(Ref lhs, Ref rhs) { + return lhs.index == rhs.index; +} + +inline bool operator!=(Ref lhs, Ref rhs) { + return lhs.index != rhs.index; +} + +//// ExternType //// +inline ExternType::ExternType(ExternKind kind) : kind(kind) {} + +//// FuncType //// +// static +inline bool FuncType::classof(const ExternType* type) { + return type->kind == skind; +} + +inline FuncType::FuncType(ValueTypes params, ValueTypes results) + : ExternType(ExternKind::Func), params(params), results(results) {} + +//// TableType //// +// static +inline bool TableType::classof(const ExternType* type) { + return type->kind == skind; +} + +inline TableType::TableType(ValueType element, Limits limits) + : ExternType(ExternKind::Table), element(element), limits(limits) { + // Always set max. + if (!limits.has_max) { + this->limits.max = std::numeric_limits<u32>::max(); + } +} + +//// MemoryType //// +// static +inline bool MemoryType::classof(const ExternType* type) { + return type->kind == skind; +} + +inline MemoryType::MemoryType(Limits limits) + : ExternType(ExternKind::Memory), limits(limits) { + // Always set max. + if (!limits.has_max) { + this->limits.max = WABT_MAX_PAGES; + } +} + +//// GlobalType //// +// static +inline bool GlobalType::classof(const ExternType* type) { + return type->kind == skind; +} + +inline GlobalType::GlobalType(ValueType type, Mutability mut) + : ExternType(ExternKind::Global), type(type), mut(mut) {} + +//// EventType //// +// static +inline bool EventType::classof(const ExternType* type) { + return type->kind == skind; +} + +inline EventType::EventType(EventAttr attr, const ValueTypes& signature) + : ExternType(ExternKind::Event), attr(attr), signature(signature) {} + +//// ImportType //// +inline ImportType::ImportType(std::string module, + std::string name, + std::unique_ptr<ExternType> type) + : module(module), name(name), type(std::move(type)) {} + +inline ImportType::ImportType(const ImportType& other) + : module(other.module), name(other.name), type(other.type->Clone()) {} + +inline ImportType& ImportType::operator=(const ImportType& other) { + if (this != &other) { + module = other.module; + name = other.name; + type = other.type->Clone(); + } + return *this; +} + +//// ExportType //// +inline ExportType::ExportType(std::string name, + std::unique_ptr<ExternType> type) + : name(name), type(std::move(type)) {} + +inline ExportType::ExportType(const ExportType& other) + : name(other.name), type(other.type->Clone()) {} + +inline ExportType& ExportType::operator=(const ExportType& other) { + if (this != &other) { + name = other.name; + type = other.type->Clone(); + } + return *this; +} + +//// Frame //// +inline Frame::Frame(Ref func, + u32 values, + u32 offset, + Instance* inst, + Module* mod) + : func(func), values(values), offset(offset), inst(inst), mod(mod) {} + +//// FreeList //// +template <typename T> +bool FreeList<T>::IsValid(Index index) const { + return index < list.size(); +} + +template <typename T> +template <typename... Args> +typename FreeList<T>::Index FreeList<T>::New(Args&&... args) { + if (!free.empty()) { + Index index = free.back(); + free.pop_back(); + list[index] = T(std::forward<Args>(args)...); + return index; + } + list.emplace_back(std::forward<Args>(args)...); + return list.size() - 1; +} + +template <typename T> +void FreeList<T>::Delete(Index index) { + assert(IsValid(index)); + list[index].~T(); + free.push_back(index); +} + +template <typename T> +const T& FreeList<T>::Get(Index index) const { + assert(IsValid(index)); + return list[index]; +} + +template <typename T> +T& FreeList<T>::Get(Index index) { + assert(IsValid(index)); + return list[index]; +} + +//// RefPtr //// +template <typename T> +RefPtr<T>::RefPtr() : obj_(nullptr), store_(nullptr), root_index_(0) {} + +template <typename T> +RefPtr<T>::RefPtr(Store& store, Ref ref) { +#ifndef NDEBUG + if (!store.Is<T>(ref)) { + ObjectKind ref_kind; + if (ref == Ref::Null) { + ref_kind = ObjectKind::Null; + } else { + ref_kind = store.objects_.Get(ref.index)->kind(); + } + fprintf(stderr, "Invalid conversion from Ref (%s) to RefPtr<%s>!\n", + GetName(ref_kind), T::GetTypeName()); + abort(); + } +#endif + root_index_ = store.NewRoot(ref); + obj_ = static_cast<T*>(store.objects_.Get(ref.index).get()); + store_ = &store; +} + +template <typename T> +RefPtr<T>::RefPtr(const RefPtr& other) + : obj_(other.obj_), store_(other.store_) { + root_index_ = store_ ? store_->CopyRoot(other.root_index_) : 0; +} + +template <typename T> +RefPtr<T>& RefPtr<T>::operator=(const RefPtr& other) { + obj_ = other.obj_; + store_ = other.store_; + root_index_ = store_ ? store_->CopyRoot(other.root_index_) : 0; + return *this; +} + +template <typename T> +RefPtr<T>::RefPtr(RefPtr&& other) + : obj_(other.obj_), store_(other.store_), root_index_(other.root_index_) { + other.obj_ = nullptr; + other.store_ = nullptr; + other.root_index_ = 0; +} + +template <typename T> +RefPtr<T>& RefPtr<T>::operator=(RefPtr&& other) { + obj_ = other.obj_; + store_ = other.store_; + root_index_ = other.root_index_; + other.obj_ = nullptr; + other.store_ = nullptr; + other.root_index_ = 0; + return *this; +} + +template <typename T> +RefPtr<T>::~RefPtr() { + reset(); +} + +template <typename T> +template <typename U> +RefPtr<T>::RefPtr(const RefPtr<U>& other) + : obj_(other.obj_), store_(other.store_) { + root_index_ = store_ ? store_->CopyRoot(other.root_index_) : 0; +} + +template <typename T> +template <typename U> +RefPtr<T>& RefPtr<T>::operator=(const RefPtr<U>& other){ + obj_ = other.obj_; + store_ = other.store_; + root_index_ = store_ ? store_->CopyRoot(other.root_index_) : 0; + return *this; +} + +template <typename T> +template <typename U> +RefPtr<T>::RefPtr(RefPtr&& other) + : obj_(other.obj_), store_(other.store_), root_index_(other.root_index_) { + other.obj_ = nullptr; + other.store_ = nullptr; + other.root_index_ = 0; +} + +template <typename T> +template <typename U> +RefPtr<T>& RefPtr<T>::operator=(RefPtr&& other) { + obj_ = other.obj_; + store_ = other.store_; + root_index_ = other.root_index_; + other.obj_ = nullptr; + other.store_ = nullptr; + other.root_index_ = 0; + return *this; +} + +template <typename T> +template <typename U> +RefPtr<U> RefPtr<T>::As() { + static_assert(std::is_base_of<T, U>::value, "T must be base class of U"); + assert(store_->Is<U>(obj_->self())); + RefPtr<U> result; + result.obj_ = static_cast<U*>(obj_); + result.store_ = store_; + result.root_index_ = store_->CopyRoot(root_index_); + return result; +} + +template <typename T> +bool RefPtr<T>::empty() const { + return obj_ != nullptr; +} + +template <typename T> +void RefPtr<T>::reset() { + if (obj_) { + store_->DeleteRoot(root_index_); + obj_ = nullptr; + root_index_ = 0; + store_ = nullptr; + } +} + +template <typename T> +T* RefPtr<T>::get() const { + return obj_; +} + +template <typename T> +T* RefPtr<T>::operator->() const { + return obj_; +} + +template <typename T> +T& RefPtr<T>::operator*() const { + return *obj_; +} + +template <typename T> +RefPtr<T>::operator bool() const { + return obj_ != nullptr; +} + +template <typename T> +Ref RefPtr<T>::ref() const { + return store_ ? store_->roots_.Get(root_index_) : Ref::Null; +} + +template <typename T> +Store* RefPtr<T>::store() const { + return store_; +} + +template <typename U, typename V> +bool operator==(const RefPtr<U>& lhs, const RefPtr<V>& rhs) { + return lhs.obj_->self() == rhs.obj_->self(); +} + +template <typename U, typename V> +bool operator!=(const RefPtr<U>& lhs, const RefPtr<V>& rhs) { + return lhs.obj_->self() != rhs.obj_->self(); +} + +//// ValueType //// +inline bool IsReference(ValueType type) { return IsRefType(type); } +template <> inline bool HasType<s32>(ValueType type) { return type == ValueType::I32; } +template <> inline bool HasType<u32>(ValueType type) { return type == ValueType::I32; } +template <> inline bool HasType<s64>(ValueType type) { return type == ValueType::I64; } +template <> inline bool HasType<u64>(ValueType type) { return type == ValueType::I64; } +template <> inline bool HasType<f32>(ValueType type) { return type == ValueType::F32; } +template <> inline bool HasType<f64>(ValueType type) { return type == ValueType::F64; } +template <> inline bool HasType<Ref>(ValueType type) { return IsReference(type); } + +template <typename T> void RequireType(ValueType type) { + assert(HasType<T>(type)); +} + +inline bool TypesMatch(ValueType expected, ValueType actual) { + if (expected == actual) { + return true; + } + if (!IsReference(expected)) { + return false; + } + if (expected == ValueType::Anyref || actual == ValueType::Nullref) { + return true; + } + return false; +} + +//// Value //// +inline Value WABT_VECTORCALL Value::Make(s32 val) { Value res; res.i32_ = val; return res; } +inline Value WABT_VECTORCALL Value::Make(u32 val) { Value res; res.i32_ = val; return res; } +inline Value WABT_VECTORCALL Value::Make(s64 val) { Value res; res.i64_ = val; return res; } +inline Value WABT_VECTORCALL Value::Make(u64 val) { Value res; res.i64_ = val; return res; } +inline Value WABT_VECTORCALL Value::Make(f32 val) { Value res; res.f32_ = val; return res; } +inline Value WABT_VECTORCALL Value::Make(f64 val) { Value res; res.f64_ = val; return res; } +inline Value WABT_VECTORCALL Value::Make(v128 val) { Value res; res.v128_ = val; return res; } +inline Value WABT_VECTORCALL Value::Make(Ref val) { Value res; res.ref_ = val; return res; } +template <typename T, u8 L> +Value WABT_VECTORCALL Value::Make(Simd<T, L> val) { + Value res; + res.v128_ = Bitcast<v128>(val); + return res; +} + +template <> inline s8 WABT_VECTORCALL Value::Get<s8>() const { return i32_; } +template <> inline u8 WABT_VECTORCALL Value::Get<u8>() const { return i32_; } +template <> inline s16 WABT_VECTORCALL Value::Get<s16>() const { return i32_; } +template <> inline u16 WABT_VECTORCALL Value::Get<u16>() const { return i32_; } +template <> inline s32 WABT_VECTORCALL Value::Get<s32>() const { return i32_; } +template <> inline u32 WABT_VECTORCALL Value::Get<u32>() const { return i32_; } +template <> inline s64 WABT_VECTORCALL Value::Get<s64>() const { return i64_; } +template <> inline u64 WABT_VECTORCALL Value::Get<u64>() const { return i64_; } +template <> inline f32 WABT_VECTORCALL Value::Get<f32>() const { return f32_; } +template <> inline f64 WABT_VECTORCALL Value::Get<f64>() const { return f64_; } +template <> inline v128 WABT_VECTORCALL Value::Get<v128>() const { return v128_; } +template <> inline Ref WABT_VECTORCALL Value::Get<Ref>() const { return ref_; } + +template <> inline s8x16 WABT_VECTORCALL Value::Get<s8x16>() const { return Bitcast<s8x16>(v128_); } +template <> inline u8x16 WABT_VECTORCALL Value::Get<u8x16>() const { return Bitcast<u8x16>(v128_); } +template <> inline s16x8 WABT_VECTORCALL Value::Get<s16x8>() const { return Bitcast<s16x8>(v128_); } +template <> inline u16x8 WABT_VECTORCALL Value::Get<u16x8>() const { return Bitcast<u16x8>(v128_); } +template <> inline s32x4 WABT_VECTORCALL Value::Get<s32x4>() const { return Bitcast<s32x4>(v128_); } +template <> inline u32x4 WABT_VECTORCALL Value::Get<u32x4>() const { return Bitcast<u32x4>(v128_); } +template <> inline s64x2 WABT_VECTORCALL Value::Get<s64x2>() const { return Bitcast<s64x2>(v128_); } +template <> inline u64x2 WABT_VECTORCALL Value::Get<u64x2>() const { return Bitcast<u64x2>(v128_); } +template <> inline f32x4 WABT_VECTORCALL Value::Get<f32x4>() const { return Bitcast<f32x4>(v128_); } +template <> inline f64x2 WABT_VECTORCALL Value::Get<f64x2>() const { return Bitcast<f64x2>(v128_); } + +template <> inline void WABT_VECTORCALL Value::Set<s32>(s32 val) { i32_ = val; } +template <> inline void WABT_VECTORCALL Value::Set<u32>(u32 val) { i32_ = val; } +template <> inline void WABT_VECTORCALL Value::Set<s64>(s64 val) { i64_ = val; } +template <> inline void WABT_VECTORCALL Value::Set<u64>(u64 val) { i64_ = val; } +template <> inline void WABT_VECTORCALL Value::Set<f32>(f32 val) { f32_ = val; } +template <> inline void WABT_VECTORCALL Value::Set<f64>(f64 val) { f64_ = val; } +template <> inline void WABT_VECTORCALL Value::Set<v128>(v128 val) { v128_ = val; } +template <> inline void WABT_VECTORCALL Value::Set<Ref>(Ref val) { ref_ = val; } + +//// Store //// +inline bool Store::IsValid(Ref ref) const { + return objects_.IsValid(ref.index) && objects_.Get(ref.index); +} + +template <typename T> +bool Store::Is(Ref ref) const { + return objects_.IsValid(ref.index) && isa<T>(objects_.Get(ref.index).get()); +} + +template <typename T> +Result Store::Get(Ref ref, RefPtr<T>* out) { + if (Is<T>(ref)) { + *out = RefPtr<T>(*this, ref); + return Result::Ok; + } + return Result::Error; +} + +template <typename T> +RefPtr<T> Store::UnsafeGet(Ref ref) { + return RefPtr<T>(*this, ref); +} + +template <typename T, typename... Args> +RefPtr<T> Store::Alloc(Args&&... args) { + Ref ref{objects_.New(new T(std::forward<Args>(args)...))}; + RefPtr<T> ptr{*this, ref}; + ptr->self_ = ref; + return ptr; +} + +inline const Features& Store::features() const { + return features_; +} + +//// Object //// +// static +inline bool Object::classof(const Object* obj) { + return true; +} + +inline Object::Object(ObjectKind kind) : kind_(kind) {} + +inline ObjectKind Object::kind() const { + return kind_; +} + +inline Ref Object::self() const { + return self_; +} + +inline void* Object::host_info() const { + return host_info_; +} + +inline void Object::set_host_info(void* host_info) { + host_info_ = host_info; +} + +inline Finalizer Object::get_finalizer() const { + return finalizer_; +} + +inline void Object::set_finalizer(Finalizer finalizer) { + finalizer_ = finalizer; +} + +//// Foreign //// +// static +inline bool Foreign::classof(const Object* obj) { + return obj->kind() == skind; +} + +// static +inline Foreign::Ptr Foreign::New(Store& store, void* ptr) { + return store.Alloc<Foreign>(store, ptr); +} + +inline void* Foreign::ptr() { + return ptr_; +} + +//// Trap //// +// static +inline bool Trap::classof(const Object* obj) { + return obj->kind() == skind; +} + +// static +inline Trap::Ptr Trap::New(Store& store, + const std::string& msg, + const std::vector<Frame>& trace) { + return store.Alloc<Trap>(store, msg, trace); +} + +inline std::string Trap::message() const { + return message_; +} + +//// Extern //// +// static +inline bool Extern::classof(const Object* obj) { + switch (obj->kind()) { + case ObjectKind::DefinedFunc: + case ObjectKind::HostFunc: + case ObjectKind::Table: + case ObjectKind::Memory: + case ObjectKind::Global: + case ObjectKind::Event: + return true; + default: + return false; + } +} + +inline Extern::Extern(ObjectKind kind) : Object(kind) {} + +//// Func //// +// static +inline bool Func::classof(const Object* obj) { + switch (obj->kind()) { + case ObjectKind::DefinedFunc: + case ObjectKind::HostFunc: + return true; + default: + return false; + } +} + +inline const ExternType& Func::extern_type() { + return type_; +} + +inline const FuncType& Func::type() const { + return type_; +} + +//// DefinedFunc //// +// static +inline bool DefinedFunc::classof(const Object* obj) { + return obj->kind() == skind; +} + +// static +inline DefinedFunc::Ptr DefinedFunc::New(Store& store, + Ref instance, + FuncDesc desc) { + return store.Alloc<DefinedFunc>(store, instance, desc); +} + +inline Ref DefinedFunc::instance() const { + return instance_; +} + +inline const FuncDesc& DefinedFunc::desc() const { + return desc_; +} + +//// HostFunc //// +// static +inline bool HostFunc::classof(const Object* obj) { + return obj->kind() == skind; +} + +// static +inline HostFunc::Ptr HostFunc::New(Store& store, FuncType type, Callback cb) { + return store.Alloc<HostFunc>(store, type, cb); +} + +//// Table //// +// static +inline bool Table::classof(const Object* obj) { + return obj->kind() == skind; +} + +// static +inline Table::Ptr Table::New(Store& store, TableType type) { + return store.Alloc<Table>(store, type); +} + +inline const ExternType& Table::extern_type() { + return type_; +} + +inline const TableType& Table::type() const { + return type_; +} + +inline const RefVec& Table::elements() const { + return elements_; +} + +inline u32 Table::size() const { + return static_cast<u32>(elements_.size()); +} + +//// Memory //// +// static +inline bool Memory::classof(const Object* obj) { + return obj->kind() == skind; +} + +// static +inline Memory::Ptr Memory::New(interp::Store& store, MemoryType type) { + return store.Alloc<Memory>(store, type); +} + +inline bool Memory::IsValidAccess(u32 offset, u32 addend, size_t size) const { + return u64{offset} + addend + size <= data_.size(); +} + +inline bool Memory::IsValidAtomicAccess(u32 offset, + u32 addend, + size_t size) const { + return IsValidAccess(offset, addend, size) && + ((offset + addend) & (size - 1)) == 0; +} + +template <typename T> +Result Memory::Load(u32 offset, u32 addend, T* out) const { + if (!IsValidAccess(offset, addend, sizeof(T))) { + return Result::Error; + } + memcpy(out, data_.data() + offset + addend, sizeof(T)); + return Result::Ok; +} + +template <typename T> +T WABT_VECTORCALL Memory::UnsafeLoad(u32 offset, u32 addend) const { + assert(IsValidAccess(offset, addend, sizeof(T))); + T val; + memcpy(&val, data_.data() + offset + addend, sizeof(T)); + return val; +} + +template <typename T> +Result WABT_VECTORCALL Memory::Store(u32 offset, u32 addend, T val) { + if (!IsValidAccess(offset, addend, sizeof(T))) { + return Result::Error; + } + memcpy(data_.data() + offset + addend, &val, sizeof(T)); + return Result::Ok; +} + +template <typename T> +Result Memory::AtomicLoad(u32 offset, u32 addend, T* out) const { + if (!IsValidAtomicAccess(offset, addend, sizeof(T))) { + return Result::Error; + } + memcpy(out, data_.data() + offset + addend, sizeof(T)); + return Result::Ok; +} + +template <typename T> +Result Memory::AtomicStore(u32 offset, u32 addend, T val) { + if (!IsValidAtomicAccess(offset, addend, sizeof(T))) { + return Result::Error; + } + memcpy(data_.data() + offset + addend, &val, sizeof(T)); + return Result::Ok; +} + +template <typename T, typename F> +Result Memory::AtomicRmw(u32 offset, u32 addend, T rhs, F&& func, T* out) { + T lhs; + CHECK_RESULT(AtomicLoad(offset, addend, &lhs)); + CHECK_RESULT(AtomicStore(offset, addend, func(lhs, rhs))); + *out = lhs; + return Result::Ok; +} + +template <typename T> +Result Memory::AtomicRmwCmpxchg(u32 offset, + u32 addend, + T expect, + T replace, + T* out) { + T read; + CHECK_RESULT(AtomicLoad(offset, addend, &read)); + if (read == expect) { + CHECK_RESULT(AtomicStore(offset, addend, replace)); + } + *out = read; + return Result::Ok; +} + +inline u8* Memory::UnsafeData() { + return data_.data(); +} + +inline u32 Memory::ByteSize() const { + return data_.size(); +} + +inline u32 Memory::PageSize() const { + return pages_; +} + +inline const ExternType& Memory::extern_type() { + return type_; +} + +inline const MemoryType& Memory::type() const { + return type_; +} + +//// Global //// +// static +inline bool Global::classof(const Object* obj) { + return obj->kind() == skind; +} + +// static +inline Global::Ptr Global::New(Store& store, GlobalType type, Value value) { + return store.Alloc<Global>(store, type, value); +} + +inline Value Global::Get() const { + return value_; +} + +template <typename T> +Result Global::Get(T* out) const { + if (HasType<T>(type_.type)) { + *out = value_.Get<T>(); + return Result::Ok; + } + return Result::Error; +} + +template <typename T> +T WABT_VECTORCALL Global::UnsafeGet() const { + RequireType<T>(type_.type); + return value_.Get<T>(); +} + +template <typename T> +Result WABT_VECTORCALL Global::Set(T val) { + if (type_.mut == Mutability::Var && HasType<T>(type_.type)) { + value_.Set(val); + return Result::Ok; + } + return Result::Error; +} + +inline const ExternType& Global::extern_type() { + return type_; +} + +inline const GlobalType& Global::type() const { + return type_; +} + +//// Event //// +// static +inline bool Event::classof(const Object* obj) { + return obj->kind() == skind; +} + +// static +inline Event::Ptr Event::New(Store& store, EventType type) { + return store.Alloc<Event>(store, type); +} + +inline const ExternType& Event::extern_type() { + return type_; +} + +inline const EventType& Event::type() const { + return type_; +} + +//// ElemSegment //// +inline void ElemSegment::Drop() { + elements_.clear(); +} + +inline const ElemDesc& ElemSegment::desc() const { + return *desc_; +} + +inline const RefVec& ElemSegment::elements() const { + return elements_; +} + +inline u32 ElemSegment::size() const { + return elements_.size(); +} + +//// DataSegment //// +inline void DataSegment::Drop() { + size_ = 0; +} + +inline const DataDesc& DataSegment::desc() const { + return *desc_; +} + +inline u32 DataSegment::size() const { + return size_; +} + +//// Module //// +// static +inline bool Module::classof(const Object* obj) { + return obj->kind() == skind; +} + +// static +inline Module::Ptr Module::New(Store& store, ModuleDesc desc) { + return store.Alloc<Module>(store, std::move(desc)); +} + +inline const ModuleDesc& Module::desc() const { + return desc_; +} + +inline const std::vector<ImportType>& Module::import_types() const { + return import_types_; +} + +inline const std::vector<ExportType>& Module::export_types() const { + return export_types_; +} + +//// Instance //// +// static +inline bool Instance::classof(const Object* obj) { + return obj->kind() == skind; +} + +inline Ref Instance::module() const { + return module_; +} + +inline const RefVec& Instance::imports() const { + return imports_; +} + +inline const RefVec& Instance::funcs() const { + return funcs_; +} + +inline const RefVec& Instance::tables() const { + return tables_; +} + +inline const RefVec& Instance::memories() const { + return memories_; +} + +inline const RefVec& Instance::globals() const { + return globals_; +} + +inline const RefVec& Instance::events() const { + return events_; +} + +inline const RefVec& Instance::exports() const { + return exports_; +} + +inline const std::vector<ElemSegment>& Instance::elems() const { + return elems_; +} + +inline std::vector<ElemSegment>& Instance::elems() { + return elems_; +} + +inline const std::vector<DataSegment>& Instance::datas() const { + return datas_; +} + +inline std::vector<DataSegment>& Instance::datas() { + return datas_; +} + +//// Thread //// +// static +inline bool Thread::classof(const Object* obj) { + return obj->kind() == skind; +} + +// static +inline Thread::Ptr Thread::New(Store& store, const Options& options) { + return store.Alloc<Thread>(store, options); +} + +inline Store& Thread::store() { + return store_; +} + +} // namespace interp +} // namespace wabt diff --git a/src/interp/interp-internal.h b/src/interp/interp-internal.h deleted file mode 100644 index f983d0b2..00000000 --- a/src/interp/interp-internal.h +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright 2018 WebAssembly Community Group participants - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -namespace wabt { -namespace interp { - -// A table entry has the following packed layout: -// -// struct { -// IstreamOffset offset; -// uint32_t drop_count; -// uint32_t keep_count; -// }; -#define WABT_TABLE_ENTRY_SIZE \ - (sizeof(IstreamOffset) + sizeof(uint32_t) + sizeof(uint32_t)) -#define WABT_TABLE_ENTRY_OFFSET_OFFSET 0 -#define WABT_TABLE_ENTRY_DROP_OFFSET sizeof(IstreamOffset) -#define WABT_TABLE_ENTRY_KEEP_OFFSET (sizeof(IstreamOffset) + sizeof(uint32_t)) - -template <typename T> -inline T ReadUxAt(const uint8_t* pc) { - T result; - memcpy(&result, pc, sizeof(T)); - return result; -} - -template <typename T> -inline T ReadUx(const uint8_t** pc) { - T result = ReadUxAt<T>(*pc); - *pc += sizeof(T); - return result; -} - -inline uint8_t ReadU8At(const uint8_t* pc) { - return ReadUxAt<uint8_t>(pc); -} - -inline uint8_t ReadU8(const uint8_t** pc) { - return ReadUx<uint8_t>(pc); -} - -inline uint32_t ReadU32At(const uint8_t* pc) { - return ReadUxAt<uint32_t>(pc); -} - -inline uint32_t ReadU32(const uint8_t** pc) { - return ReadUx<uint32_t>(pc); -} - -inline uint64_t ReadU64At(const uint8_t* pc) { - return ReadUxAt<uint64_t>(pc); -} - -inline uint64_t ReadU64(const uint8_t** pc) { - return ReadUx<uint64_t>(pc); -} - -inline v128 ReadV128At(const uint8_t* pc) { - return ReadUxAt<v128>(pc); -} - -inline v128 ReadV128(const uint8_t** pc) { - return ReadUx<v128>(pc); -} - -inline Opcode ReadOpcode(const uint8_t** pc) { - uint32_t value = ReadU32(pc); - return Opcode(static_cast<Opcode::Enum>(value)); -} - -inline void ReadTableEntryAt(const uint8_t* pc, - IstreamOffset* out_offset, - uint32_t* out_drop, - uint32_t* out_keep) { - *out_offset = ReadU32At(pc + WABT_TABLE_ENTRY_OFFSET_OFFSET); - *out_drop = ReadU32At(pc + WABT_TABLE_ENTRY_DROP_OFFSET); - *out_keep = ReadU32At(pc + WABT_TABLE_ENTRY_KEEP_OFFSET); -} - -} // namespace interp -} // namespace wabt diff --git a/src/interp/interp-math.h b/src/interp/interp-math.h new file mode 100644 index 00000000..ccbc9cd6 --- /dev/null +++ b/src/interp/interp-math.h @@ -0,0 +1,358 @@ +/* + * Copyright 2020 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WABT_INTERP_MATH_H_ +#define WABT_INTERP_MATH_H_ + +#include <cmath> +#include <limits> +#include <string> +#include <type_traits> + +#if COMPILER_IS_MSVC +#include <emmintrin.h> +#include <immintrin.h> +#endif + +#include "src/common.h" +#include "src/interp/interp.h" + +namespace wabt { +namespace interp { + +template < + typename T, + typename std::enable_if<!std::is_floating_point<T>::value, int>::type = 0> +bool WABT_VECTORCALL IsNaN(T val) { + return false; +} + +template < + typename T, + typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0> +bool WABT_VECTORCALL IsNaN(T val) { + return std::isnan(val); +} + +template < + typename T, + typename std::enable_if<!std::is_floating_point<T>::value, int>::type = 0> +T WABT_VECTORCALL CanonNaN(T val) { + return val; +} + +template < + typename T, + typename std::enable_if<std::is_floating_point<T>::value, int>::type = 0> +T WABT_VECTORCALL CanonNaN(T val) { + if (WABT_UNLIKELY(std::isnan(val))) { + return std::numeric_limits<f32>::quiet_NaN(); + } + return val; +} + +template <typename T> T ShiftMask(T val) { return val & (sizeof(T)*8-1); } + +template <typename T> bool WABT_VECTORCALL IntEqz(T val) { return val == 0; } +template <typename T> bool WABT_VECTORCALL Eq(T lhs, T rhs) { return lhs == rhs; } +template <typename T> bool WABT_VECTORCALL Ne(T lhs, T rhs) { return lhs != rhs; } +template <typename T> bool WABT_VECTORCALL Lt(T lhs, T rhs) { return lhs < rhs; } +template <typename T> bool WABT_VECTORCALL Le(T lhs, T rhs) { return lhs <= rhs; } +template <typename T> bool WABT_VECTORCALL Gt(T lhs, T rhs) { return lhs > rhs; } +template <typename T> bool WABT_VECTORCALL Ge(T lhs, T rhs) { return lhs >= rhs; } +template <typename T> T WABT_VECTORCALL IntClz(T val) { return Clz(val); } +template <typename T> T WABT_VECTORCALL IntCtz(T val) { return Ctz(val); } +template <typename T> T WABT_VECTORCALL IntPopcnt(T val) { return Popcount(val); } +template <typename T> T WABT_VECTORCALL IntNot(T val) { return ~val; } +template <typename T> T WABT_VECTORCALL IntNeg(T val) { return ~val + 1; } +template <typename T> T WABT_VECTORCALL Add(T lhs, T rhs) { return CanonNaN(lhs + rhs); } +template <typename T> T WABT_VECTORCALL Sub(T lhs, T rhs) { return CanonNaN(lhs - rhs); } +template <typename T> T WABT_VECTORCALL Mul(T lhs, T rhs) { return CanonNaN(lhs * rhs); } +template <typename T> T WABT_VECTORCALL IntAnd(T lhs, T rhs) { return lhs & rhs; } +template <typename T> T WABT_VECTORCALL IntOr(T lhs, T rhs) { return lhs | rhs; } +template <typename T> T WABT_VECTORCALL IntXor(T lhs, T rhs) { return lhs ^ rhs; } +template <typename T> T WABT_VECTORCALL IntShl(T lhs, T rhs) { return lhs << ShiftMask(rhs); } +template <typename T> T WABT_VECTORCALL IntShr(T lhs, T rhs) { return lhs >> ShiftMask(rhs); } +template <typename T> T WABT_VECTORCALL IntMin(T lhs, T rhs) { return std::min(lhs, rhs); } +template <typename T> T WABT_VECTORCALL IntMax(T lhs, T rhs) { return std::max(lhs, rhs); } +template <typename T> T WABT_VECTORCALL IntAndNot(T lhs, T rhs) { return lhs & ~rhs; } +template <typename T> T WABT_VECTORCALL IntAvgr(T lhs, T rhs) { return (lhs + rhs + 1) / 2; } +template <typename T> T WABT_VECTORCALL Xchg(T lhs, T rhs) { return rhs; } + +template <typename T> struct Mask { using Type = T; }; +template <> struct Mask<f32> { using Type = u32; }; +template <> struct Mask<f64> { using Type = u64; }; + +template <typename T> typename Mask<T>::Type WABT_VECTORCALL EqMask(T lhs, T rhs) { return lhs == rhs ? -1 : 0; } +template <typename T> typename Mask<T>::Type WABT_VECTORCALL NeMask(T lhs, T rhs) { return lhs != rhs ? -1 : 0; } +template <typename T> typename Mask<T>::Type WABT_VECTORCALL LtMask(T lhs, T rhs) { return lhs < rhs ? -1 : 0; } +template <typename T> typename Mask<T>::Type WABT_VECTORCALL LeMask(T lhs, T rhs) { return lhs <= rhs ? -1 : 0; } +template <typename T> typename Mask<T>::Type WABT_VECTORCALL GtMask(T lhs, T rhs) { return lhs > rhs ? -1 : 0; } +template <typename T> typename Mask<T>::Type WABT_VECTORCALL GeMask(T lhs, T rhs) { return lhs >= rhs ? -1 : 0; } + +template <typename T> +T WABT_VECTORCALL IntRotl(T lhs, T rhs) { + return (lhs << ShiftMask(rhs)) | (lhs >> ShiftMask<T>(0 - rhs)); +} + +template <typename T> +T WABT_VECTORCALL IntRotr(T lhs, T rhs) { + return (lhs >> ShiftMask(rhs)) | (lhs << ShiftMask<T>(0 - rhs)); +} + +// i{32,64}.{div,rem}_s are special-cased because they trap when dividing the +// max signed value by -1. The modulo operation on x86 uses the same +// instruction to generate the quotient and the remainder. +template <typename T, + typename std::enable_if<std::is_signed<T>::value, int>::type = 0> +bool IsNormalDivRem(T lhs, T rhs) { + return !(lhs == std::numeric_limits<T>::min() && rhs == -1); +} + +template <typename T, + typename std::enable_if<!std::is_signed<T>::value, int>::type = 0> +bool IsNormalDivRem(T lhs, T rhs) { + return true; +} + +template <typename T> +RunResult WABT_VECTORCALL IntDiv(T lhs, T rhs, T* out, std::string* out_msg) { + if (WABT_UNLIKELY(rhs == 0)) { + *out_msg = "integer divide by zero"; + return RunResult::Trap; + } + if (WABT_LIKELY(IsNormalDivRem(lhs, rhs))) { + *out = lhs / rhs; + return RunResult::Ok; + } else { + *out_msg = "integer overflow"; + return RunResult::Trap; + } +} + +template <typename T> +RunResult WABT_VECTORCALL IntRem(T lhs, T rhs, T* out, std::string* out_msg) { + if (WABT_UNLIKELY(rhs == 0)) { + *out_msg = "integer divide by zero"; + return RunResult::Trap; + } + if (WABT_LIKELY(IsNormalDivRem(lhs, rhs))) { + *out = lhs % rhs; + } else { + *out = 0; + } + return RunResult::Ok; +} + +#if COMPILER_IS_MSVC +template <typename T> T WABT_VECTORCALL FloatAbs(T val); +template <typename T> T WABT_VECTORCALL FloatCopysign(T lhs, T rhs); + +// Don't use std::{abs,copysign} directly on MSVC, since that seems to lose +// the NaN tag. +template <> +inline f32 WABT_VECTORCALL FloatAbs(f32 val) { + return _mm_cvtss_f32(_mm_and_ps( + _mm_set1_ps(val), _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)))); +} + +template <> +inline f64 WABT_VECTORCALL FloatAbs(f64 val) { + return _mm_cvtsd_f64( + _mm_and_pd(_mm_set1_pd(val), + _mm_castsi128_pd(_mm_set1_epi64x(0x7fffffffffffffffull)))); +} + +template <> +inline f32 WABT_VECTORCALL FloatCopysign(f32 lhs, f32 rhs) { + return _mm_cvtss_f32( + _mm_or_ps( + _mm_and_ps(_mm_set1_ps(lhs), _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff))), + _mm_and_ps(_mm_set1_ps(rhs), _mm_castsi128_ps(_mm_set1_epi32(0x80000000))))); +} + +template <> +inline f64 WABT_VECTORCALL FloatCopysign(f64 lhs, f64 rhs) { + return _mm_cvtsd_f64( + _mm_or_pd( + _mm_and_pd(_mm_set1_pd(lhs), _mm_castsi128_pd(_mm_set1_epi64x(0x7fffffffffffffffull))), + _mm_and_pd(_mm_set1_pd(rhs), _mm_castsi128_pd(_mm_set1_epi64x(0x8000000000000000ull))))); +} + +#else +template <typename T> +T WABT_VECTORCALL FloatAbs(T val) { + return std::abs(val); +} + +template <typename T> +T WABT_VECTORCALL FloatCopysign(T lhs, T rhs) { + return std::copysign(lhs, rhs); +} +#endif + +#if COMPILER_IS_MSVC +#else +#endif + +template <typename T> T WABT_VECTORCALL FloatNeg(T val) { return -val; } +template <typename T> T WABT_VECTORCALL FloatCeil(T val) { return CanonNaN(std::ceil(val)); } +template <typename T> T WABT_VECTORCALL FloatFloor(T val) { return CanonNaN(std::floor(val)); } +template <typename T> T WABT_VECTORCALL FloatTrunc(T val) { return CanonNaN(std::trunc(val)); } +template <typename T> T WABT_VECTORCALL FloatNearest(T val) { return CanonNaN(std::nearbyint(val)); } +template <typename T> T WABT_VECTORCALL FloatSqrt(T val) { return CanonNaN(std::sqrt(val)); } + +template <typename T> +T WABT_VECTORCALL FloatDiv(T lhs, T rhs) { + // IEE754 specifies what should happen when dividing a float by zero, but + // C/C++ says it is undefined behavior. + if (WABT_UNLIKELY(rhs == 0)) { + return std::isnan(lhs) || lhs == 0 + ? std::numeric_limits<T>::quiet_NaN() + : ((std::signbit(lhs) ^ std::signbit(rhs)) + ? -std::numeric_limits<T>::infinity() + : std::numeric_limits<T>::infinity()); + } + return CanonNaN(lhs / rhs); +} + +template <typename T> +T WABT_VECTORCALL FloatMin(T lhs, T rhs) { + if (WABT_UNLIKELY(std::isnan(lhs) || std::isnan(rhs))) { + return std::numeric_limits<T>::quiet_NaN(); + } else if (WABT_UNLIKELY(lhs == 0 && rhs == 0)) { + return std::signbit(lhs) ? lhs : rhs; + } else { + return std::min(lhs, rhs); + } +} + +template <typename T> +T WABT_VECTORCALL FloatMax(T lhs, T rhs) { + if (WABT_UNLIKELY(std::isnan(lhs) || std::isnan(rhs))) { + return std::numeric_limits<T>::quiet_NaN(); + } else if (WABT_UNLIKELY(lhs == 0 && rhs == 0)) { + return std::signbit(lhs) ? rhs : lhs; + } else { + return std::max(lhs, rhs); + } +} + +template <typename R, typename T> bool WABT_VECTORCALL CanConvert(T val) { return true; } +template <> inline bool WABT_VECTORCALL CanConvert<s32, f32>(f32 val) { return val >= -2147483648.f && val < 2147483648.f; } +template <> inline bool WABT_VECTORCALL CanConvert<s32, f64>(f64 val) { return val >= -2147483648. && val <= 2147483647.; } +template <> inline bool WABT_VECTORCALL CanConvert<u32, f32>(f32 val) { return val > -1.f && val < 4294967296.f; } +template <> inline bool WABT_VECTORCALL CanConvert<u32, f64>(f64 val) { return val > -1. && val <= 4294967295.; } +template <> inline bool WABT_VECTORCALL CanConvert<s64, f32>(f32 val) { return val >= -9223372036854775808.f && val < 9223372036854775808.f; } +template <> inline bool WABT_VECTORCALL CanConvert<s64, f64>(f64 val) { return val >= -9223372036854775808. && val < 9223372036854775808.; } +template <> inline bool WABT_VECTORCALL CanConvert<u64, f32>(f32 val) { return val > -1.f && val < 18446744073709551616.f; } +template <> inline bool WABT_VECTORCALL CanConvert<u64, f64>(f64 val) { return val > -1. && val < 18446744073709551616.; } + +template <typename R, typename T> +R WABT_VECTORCALL Convert(T val) { + assert((CanConvert<R, T>(val))); + return static_cast<R>(val); +} + +template <> +inline f32 WABT_VECTORCALL Convert(f64 val) { + // The WebAssembly rounding mode means that these values (which are > F32_MAX) + // should be rounded to F32_MAX and not set to infinity. Unfortunately, UBSAN + // complains that the value is not representable as a float, so we'll special + // case them. + const f64 kMin = 3.4028234663852886e38; + const f64 kMax = 3.4028235677973366e38; + if (WABT_LIKELY(val >= -kMin && val <= kMin)) { + return val; + } else if (WABT_UNLIKELY(val > kMin && val < kMax)) { + return std::numeric_limits<f32>::max(); + } else if (WABT_UNLIKELY(val > -kMax && val < -kMin)) { + return -std::numeric_limits<f32>::max(); + } else if (WABT_UNLIKELY(std::isnan(val))) { + return std::numeric_limits<f32>::quiet_NaN(); + } else { + return std::copysign(std::numeric_limits<f32>::infinity(), val); + } +} + +template <> +inline f32 WABT_VECTORCALL Convert(u64 val) { + return wabt_convert_uint64_to_float(val); +} + +template <> +inline f64 WABT_VECTORCALL Convert(u64 val) { + return wabt_convert_uint64_to_double(val); +} + +template <> +inline f32 WABT_VECTORCALL Convert(s64 val) { + return wabt_convert_int64_to_float(val); +} + +template <> +inline f64 WABT_VECTORCALL Convert(s64 val) { + return wabt_convert_int64_to_double(val); +} + +template <typename T, int N> +T WABT_VECTORCALL IntExtend(T val) { + // Hacker's delight 2.6 - sign extension + auto bit = T{1} << N; + auto mask = (bit << 1) - 1; + return ((val & mask) ^ bit) - bit; +} + +template <typename R, typename T> +R WABT_VECTORCALL IntTruncSat(T val) { + if (WABT_UNLIKELY(std::isnan(val))) { + return 0; + } else if (WABT_UNLIKELY(!CanConvert<R>(val))) { + return std::signbit(val) ? std::numeric_limits<R>::min() + : std::numeric_limits<R>::max(); + } else { + return static_cast<R>(val); + } +} + +template <typename T> struct SatPromote; +template <> struct SatPromote<s8> { using type = s32; }; +template <> struct SatPromote<s16> { using type = s32; }; +template <> struct SatPromote<u8> { using type = s32; }; +template <> struct SatPromote<u16> { using type = s32; }; + +template <typename R, typename T> +R WABT_VECTORCALL Saturate(T val) { + static_assert(sizeof(R) < sizeof(T), "Incorrect types for Saturate"); + const T min = std::numeric_limits<R>::min(); + const T max = std::numeric_limits<R>::max(); + return val > max ? max : val < min ? min : val; +} + +template <typename T, typename U = typename SatPromote<T>::type> +T WABT_VECTORCALL IntAddSat(T lhs, T rhs) { + return Saturate<T, U>(lhs + rhs); +} + +template <typename T, typename U = typename SatPromote<T>::type> +T WABT_VECTORCALL IntSubSat(T lhs, T rhs) { + return Saturate<T, U>(lhs - rhs); +} + +} // namespace interp +} // namespace wabt + +#endif // WABT_INTERP_MATH_H_ diff --git a/src/interp/interp-util.cc b/src/interp/interp-util.cc new file mode 100644 index 00000000..b12544c4 --- /dev/null +++ b/src/interp/interp-util.cc @@ -0,0 +1,117 @@ +/* + * Copyright 2020 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "src/interp/interp-util.h" + +#include <cinttypes> + +#include "src/stream.h" + +namespace wabt { +namespace interp { + +std::string TypedValueToString(const TypedValue& tv) { + switch (tv.type) { + case Type::I32: + return StringPrintf("i32:%u", tv.value.Get<s32>()); + + case Type::I64: + return StringPrintf("i64:%" PRIu64, tv.value.Get<s64>()); + + case Type::F32: + return StringPrintf("f32:%f", tv.value.Get<f32>()); + + case Type::F64: + return StringPrintf("f64:%f", tv.value.Get<f64>()); + + case Type::V128: { + v128 simd = tv.value.Get<v128>(); + return StringPrintf("v128 i32x4:0x%08x 0x%08x 0x%08x 0x%08x", simd.v[0], + simd.v[1], simd.v[2], simd.v[3]); + } + + case Type::Nullref: + return StringPrintf("nullref"); + + case Type::Hostref: + return StringPrintf("hostref:%" PRIzd, tv.value.Get<Ref>().index); + + case Type::Funcref: + return StringPrintf("funcref:%" PRIzd, tv.value.Get<Ref>().index); + + case Type::Exnref: + return StringPrintf("exnref:%" PRIzd, tv.value.Get<Ref>().index); + + case Type::Anyref: + return StringPrintf("anyref:%" PRIzd, tv.value.Get<Ref>().index); + + case Type::Func: + case Type::Void: + case Type::Any: + case Type::I8: + case Type::I8U: + case Type::I16: + case Type::I16U: + case Type::I32U: + // These types are not concrete types and should never exist as a value + WABT_UNREACHABLE; + } + WABT_UNREACHABLE; +} + +void WriteValue(Stream* stream, const TypedValue& tv) { + std::string s = TypedValueToString(tv); + stream->WriteData(s.data(), s.size()); +} + +void WriteValues(Stream* stream, + const ValueTypes& types, + const Values& values) { + assert(types.size() == values.size()); + for (size_t i = 0; i < values.size(); ++i) { + WriteValue(stream, TypedValue{types[i], values[i]}); + if (i != values.size() - 1) { + stream->Writef(", "); + } + } +} + +void WriteTrap(Stream* stream, const char* desc, const Trap::Ptr& trap) { + stream->Writef("%s: %s\n", desc, trap->message().c_str()); +} + +void WriteCall(Stream* stream, + string_view name, + const FuncType& func_type, + const Values& params, + const Values& results, + const Trap::Ptr& trap) { + stream->Writef(PRIstringview "(", WABT_PRINTF_STRING_VIEW_ARG(name)); + WriteValues(stream, func_type.params, params); + stream->Writef(") =>"); + if (!trap) { + if (!results.empty()) { + stream->Writef(" "); + WriteValues(stream, func_type.results, results); + } + stream->Writef("\n"); + } else { + WriteTrap(stream, " error", trap); + } +} + +} // namespace interp +} // namespace wabt diff --git a/src/interp/binary-reader-metadata.h b/src/interp/interp-util.h index 13834560..d05b34de 100644 --- a/src/interp/binary-reader-metadata.h +++ b/src/interp/interp-util.h @@ -14,30 +14,37 @@ * limitations under the License. */ -#ifndef WABT_BINARY_READER_METADATA_H_ -#define WABT_BINARY_READER_METADATA_H_ +#ifndef WABT_INTERP_UTIL_H_ +#define WABT_INTERP_UTIL_H_ -#include "src/common.h" -#include "src/error.h" +#include <string> +#include <vector> + +#include "src/interp/interp.h" +#include "src/string-view.h" namespace wabt { +class Stream; + namespace interp { -struct ModuleMetadata; +std::string TypedValueToString(const TypedValue&); -} // namespace interp +void WriteValue(Stream* stream, const TypedValue&); -struct ReadBinaryOptions; +void WriteValues(Stream* stream, const ValueTypes&, const Values&); -// Reads just the binary metadata, used by C-API to get module imports names -// (and potentially other metadata) before instantiation time. -Result ReadBinaryMetadata(const void* data, - size_t size, - const ReadBinaryOptions& options, - Errors*, - interp::ModuleMetadata** out_metadata); +void WriteTrap(Stream* stream, const char* desc, const Trap::Ptr&); +void WriteCall(Stream* stream, + string_view name, + const FuncType& func_type, + const Values& params, + const Values& results, + const Trap::Ptr& trap); + +} // namespace interp } // namespace wabt -#endif /* WABT_BINARY_READER_METADATA_H_ */ +#endif // WABT_INTERP_UTIL_H_ diff --git a/src/interp/interp-wasm-c-api.cc b/src/interp/interp-wasm-c-api.cc index d5ff9ae3..111df5ab 100644 --- a/src/interp/interp-wasm-c-api.cc +++ b/src/interp/interp-wasm-c-api.cc @@ -17,564 +17,487 @@ #include <wasm.h> #include "src/binary-reader.h" +#include "src/cast.h" #include "src/error-formatter.h" -#include "src/error.h" #include "src/interp/binary-reader-interp.h" -#include "src/interp/binary-reader-metadata.h" +#include "src/interp/interp-util.h" #include "src/interp/interp.h" -#include "src/ir.h" -#include "src/stream.h" using namespace wabt; using namespace wabt::interp; +#define own + #ifndef NDEBUG -#define TRACE(str, ...) fprintf(stderr, "CAPI: " str, ##__VA_ARGS__) +#define TRACE0() TRACE("") +#define TRACE(str, ...) \ + fprintf(stderr, "CAPI: [%s] " str "\n", __func__, ##__VA_ARGS__) +#define TRACE_NO_NL(str, ...) \ + fprintf(stderr, "CAPI: [%s] " str, __func__, ##__VA_ARGS__) #else +#define TRACE0(...) #define TRACE(...) +#define TRACE_NO_NL(...) #endif static Features s_features; -static Stream* s_trace_stream; -static Thread::Options s_thread_options; static std::unique_ptr<FileStream> s_log_stream; static std::unique_ptr<FileStream> s_stdout_stream; -struct wasm_valtype_t { - wasm_valkind_t kind; -}; +// Type conversion utilities +static ValueType ToWabtValueType(wasm_valkind_t); +static wasm_valkind_t FromWabtValueType(ValueType); -struct wasm_config_t {}; +static wasm_externkind_t FromWabtExternKind(ExternKind ); -struct wasm_externtype_t { - wasm_externkind_t kind; +static ValueTypes ToWabtValueTypes(const wasm_valtype_vec_t* types); +static void FromWabtValueTypes(const ValueTypes&, wasm_valtype_vec_t* out); - protected: - wasm_externtype_t(wasm_externkind_t kind) : kind(kind) {} -}; +static wasm_mutability_t FromWabtMutability(Mutability); +static Mutability ToWabtMutability(wasm_mutability_t); -struct wasm_globaltype_t : wasm_externtype_t { - wasm_globaltype_t(wasm_valtype_t type, bool mutable_) - : wasm_externtype_t(WASM_EXTERN_GLOBAL), type(type), mutable_(mutable_) {} - wasm_valtype_t type; - bool mutable_; -}; +static Limits ToWabtLimits(const wasm_limits_t&); +static wasm_limits_t FromWabtLimits(const Limits&); -struct wasm_tabletype_t : wasm_externtype_t { - wasm_tabletype_t(wasm_valtype_t elemtype, wasm_limits_t limits) - : wasm_externtype_t(WASM_EXTERN_TABLE), - elemtype(elemtype), - limits(limits) {} - wasm_valtype_t elemtype; - wasm_limits_t limits; -}; - -struct wasm_memorytype_t : wasm_externtype_t { - wasm_memorytype_t(wasm_limits_t limits) - : wasm_externtype_t(WASM_EXTERN_MEMORY), limits(limits) {} - wasm_limits_t limits; -}; +// Value conversion utilities +static TypedValue ToWabtValue(const wasm_val_t&); +static wasm_val_t FromWabtValue(Store&, const TypedValue&); -struct wasm_importtype_t {}; +static Values ToWabtValues(const wasm_val_t values[], size_t count); +static void FromWabtValues(Store& store, + wasm_val_t values[], + const ValueTypes& types, + const Values& wabt_values); -struct wasm_exporttype_t { - wasm_exporttype_t(wasm_name_t name, wasm_externtype_t* type) - : name(name), type(type) {} +// Structs +struct wasm_config_t {}; - wasm_exporttype_t(const wasm_exporttype_t& other) { - name = other.name; - type = MakeUnique<wasm_externtype_t>(*other.type.get()); - } +struct wasm_engine_t {}; - wasm_name_t name; - std::unique_ptr<wasm_externtype_t> type; +struct wasm_valtype_t { + ValueType I; }; -struct wasm_engine_t {}; - -struct wasm_store_t { - wasm_store_t(Environment* env, Executor* executor) - : env(env), executor(executor) {} +struct wasm_externtype_t { + static std::unique_ptr<wasm_externtype_t> New(std::unique_ptr<ExternType>); - ~wasm_store_t() { - TRACE("~store\n"); + std::unique_ptr<wasm_externtype_t> Clone() const { + return New(I->Clone()); } - Environment* env; - std::unique_ptr<Executor> executor; -}; + virtual ~wasm_externtype_t() {} -enum class WasmRefType { Extern, Module, Instance, Trap, Foreign }; - -std::string WasmRefTypeToString(WasmRefType t) { - switch (t) { - case WasmRefType::Extern: - return "extern"; - case WasmRefType::Module: - return "module"; - case WasmRefType::Instance: - return "instance"; - case WasmRefType::Trap: - return "trap"; - case WasmRefType::Foreign: - return "foreign"; - } - WABT_UNREACHABLE; -} - -struct wasm_ref_t { - wasm_ref_t(WasmRefType kind) : kind(kind) {} - wasm_ref_t(const wasm_ref_t& other) - : kind(other.kind), - host_info(other.host_info), - finalizer(other.finalizer) {} - - virtual ~wasm_ref_t() { - if (finalizer) { - finalizer(host_info); - } - } + wasm_externtype_t(const wasm_externtype_t& other) = delete; + wasm_externtype_t& operator=(const wasm_externtype_t& other) = delete; - virtual wasm_ref_t* Copy() const { return new wasm_ref_t(kind); } + template <typename T> + T* As() const { return cast<T>(I.get()); } - bool Same(const wasm_ref_t& other) const; + std::unique_ptr<ExternType> I; - WasmRefType kind; - void* host_info = nullptr; - void (*finalizer)(void*) = nullptr; + protected: + wasm_externtype_t(std::unique_ptr<ExternType> et) : I(std::move(et)) {} }; -struct wasm_extern_ref_t : wasm_ref_t { - wasm_extern_ref_t(ExternalKind kind, Index index) - : wasm_ref_t(WasmRefType::Extern), kind(kind), index(index) {} - - wasm_extern_ref_t(const wasm_extern_ref_t& other) - : wasm_ref_t(other), kind(other.kind), index(other.index) {} +struct wasm_functype_t : wasm_externtype_t { + wasm_functype_t(own wasm_valtype_vec_t* params, + own wasm_valtype_vec_t* results) + : wasm_externtype_t{MakeUnique<FuncType>(ToWabtValueTypes(params), + ToWabtValueTypes(results))}, + params(*params), + results(*results) {} - wasm_ref_t* Copy() const override { return new wasm_extern_ref_t(*this); } + wasm_functype_t(FuncType ft) : wasm_externtype_t{MakeUnique<FuncType>(ft)} { + FromWabtValueTypes(ft.params, ¶ms); + FromWabtValueTypes(ft.results, &results); + } - bool Same(const wasm_extern_ref_t& other) const { - return kind == other.kind && index == other.index; + ~wasm_functype_t() { + wasm_valtype_vec_delete(¶ms); + wasm_valtype_vec_delete(&results); } - ExternalKind kind; - Index index; + // Stored here because API requires returning pointers. + wasm_valtype_vec_t params; + wasm_valtype_vec_t results; }; -struct wasm_foreign_ref_t : wasm_ref_t { - wasm_foreign_ref_t(Index index) : wasm_ref_t(WasmRefType::Foreign), index(index) {} - wasm_foreign_ref_t(const wasm_foreign_ref_t& other) : wasm_ref_t(other), index(other.index) {} +struct wasm_globaltype_t : wasm_externtype_t { + wasm_globaltype_t(own wasm_valtype_t* type, wasm_mutability_t mut) + : wasm_externtype_t{MakeUnique<GlobalType>(type->I, + ToWabtMutability(mut))}, + valtype{*type} { + wasm_valtype_delete(type); + } - wasm_ref_t* Copy() const override { return new wasm_foreign_ref_t(*this); } + wasm_globaltype_t(GlobalType gt) + : wasm_externtype_t{MakeUnique<GlobalType>(gt)}, valtype{gt.type} {} - Index index; + // Stored here because API requires returning pointers. + wasm_valtype_t valtype; }; -struct wasm_foreign_t : wasm_foreign_ref_t { - wasm_foreign_t(Index index) : wasm_foreign_ref_t(index) {} - - bool Same(const wasm_foreign_t& other) const { - TRACE("wasm_foreign_t %u %u\n", index, other.index); - return index == other.index; +struct wasm_tabletype_t : wasm_externtype_t { + wasm_tabletype_t(own wasm_valtype_t* type, const wasm_limits_t* limits) + : wasm_externtype_t{MakeUnique<TableType>(type->I, + ToWabtLimits(*limits))}, + elemtype(*type), + limits(*limits) { + wasm_valtype_delete(type); } + + wasm_tabletype_t(TableType tt) + : wasm_externtype_t{MakeUnique<TableType>(tt)}, + elemtype{tt.element}, + limits{FromWabtLimits(tt.limits)} {} + + // Stored here because API requires returning pointers. + wasm_valtype_t elemtype; + wasm_limits_t limits; }; -struct WasmInstance { - WasmInstance(wasm_store_t* store, DefinedModule* module) - : store(store), module(module) {} +struct wasm_memorytype_t : wasm_externtype_t { + wasm_memorytype_t(const wasm_limits_t* limits) + : wasm_externtype_t{MakeUnique<MemoryType>(ToWabtLimits(*limits))}, + limits{*limits} {} - ~WasmInstance() { - TRACE("~WasmInstance\n"); - } + wasm_memorytype_t(MemoryType mt) + : wasm_externtype_t{MakeUnique<MemoryType>(mt)}, + limits{FromWabtLimits(mt.limits)} {} - wasm_store_t* store; - std::unique_ptr<DefinedModule> module; + // Stored here because API requires returning pointers. + wasm_limits_t limits; }; -struct wasm_instance_t : wasm_ref_t { - wasm_instance_t(wasm_store_t* store, DefinedModule* module) - : wasm_ref_t(WasmRefType::Instance), - ptr(std::make_shared<WasmInstance>(store, module)) {} +// static +std::unique_ptr<wasm_externtype_t> wasm_externtype_t::New( + std::unique_ptr<ExternType> ptr) { + switch (ptr->kind) { + case ExternKind::Func: + return MakeUnique<wasm_functype_t>(*cast<FuncType>(ptr.get())); - wasm_instance_t(std::shared_ptr<WasmInstance> ptr) - : wasm_ref_t(WasmRefType::Instance), ptr(ptr) {} + case ExternKind::Table: + return MakeUnique<wasm_tabletype_t>(*cast<TableType>(ptr.get())); - wasm_instance_t(const wasm_instance_t& other) - : wasm_ref_t(other), ptr(other.ptr) {} + case ExternKind::Memory: + return MakeUnique<wasm_memorytype_t>(*cast<MemoryType>(ptr.get())); - wasm_ref_t* Copy() const override { return new wasm_instance_t(*this); } + case ExternKind::Global: + return MakeUnique<wasm_globaltype_t>(*cast<GlobalType>(ptr.get())); - bool Same(const wasm_instance_t& other) const { return ptr == other.ptr; } + case ExternKind::Event: + break; + } - std::shared_ptr<WasmInstance> ptr; -}; + assert(false); + return {}; +} -struct wasm_module_t : wasm_ref_t { - wasm_module_t(wasm_store_t* store, - const wasm_byte_vec_t* in, - ModuleMetadata* metadata) - : wasm_ref_t(WasmRefType::Module), store(store), metadata(metadata) { - wasm_byte_vec_copy(&binary, in); - } +struct wasm_importtype_t { + ImportType I; +}; - bool Same(const wasm_module_t& other) const { - assert(false); - return true; +struct wasm_exporttype_t { + wasm_exporttype_t(ExportType et) + : I(et), + extern_{wasm_externtype_t::New(et.type->Clone())}, + name{I.name.size(), const_cast<wasm_byte_t*>(I.name.data())} {} + + wasm_exporttype_t(const wasm_exporttype_t& other) + : wasm_exporttype_t(other.I) {} + + wasm_exporttype_t& operator=(const wasm_exporttype_t& other) { + wasm_exporttype_t copy(other); + std::swap(I, copy.I); + std::swap(extern_, copy.extern_); + std::swap(name, copy.name); + return *this; } - ~wasm_module_t() { - TRACE("~module\n"); - wasm_byte_vec_delete(&binary); - } + ExportType I; + // Stored here because API requires returning pointers. + std::unique_ptr<wasm_externtype_t> extern_; + wasm_name_t name; +}; - wasm_store_t* store; - wasm_byte_vec_t binary; - std::unique_ptr<ModuleMetadata> metadata; +struct wasm_store_t { + wasm_store_t(const Features& features) : I(features) {} + Store I; }; -struct wasm_shared_module_t : wasm_module_t {}; +struct wasm_ref_t { + wasm_ref_t(RefPtr<Object> ptr) : I(ptr) {} -bool wasm_ref_t::Same(const wasm_ref_t& other) const { - TRACE("wasm_ref_t::Same kind=%d other=%d\n", static_cast<int>(kind), - static_cast<int>(other.kind)); - if (kind != other.kind) - return false; - TRACE("wasm_ref_t::Same x\n"); - if (other.host_info != host_info && other.finalizer != finalizer) - return false; - TRACE("wasm_ref_t::Same 2\n"); - switch (kind) { - case WasmRefType::Extern: - return static_cast<const wasm_extern_ref_t*>(this)->Same( - static_cast<const wasm_extern_ref_t&>(other)); - case WasmRefType::Instance: - return static_cast<const wasm_instance_t*>(this)->Same( - static_cast<const wasm_instance_t&>(other)); - case WasmRefType::Module: - return static_cast<const wasm_module_t*>(this)->Same( - static_cast<const wasm_module_t&>(other)); - case WasmRefType::Foreign: - return static_cast<const wasm_foreign_t*>(this)->Same( - static_cast<const wasm_foreign_t&>(other)); - case WasmRefType::Trap: - return true; - } - WABT_UNREACHABLE; -} + template <typename T> + T* As() const { return cast<T>(I.get()); } -struct wasm_frame_t { - wasm_instance_t* instance; - size_t offset; - uint32_t func_index; + RefPtr<Object> I; }; -// Type conversion utilities -static Type ToWabtType(wasm_valkind_t kind); -static wasm_valkind_t FromWabtType(Type type); - -Limits ToWabtLimits(const wasm_limits_t& limits) { - return Limits(limits.min, limits.max); -} +struct wasm_frame_t { + Frame I; +}; -wasm_limits_t FromWabtLimits(const Limits& limits) { - return wasm_limits_t{(uint32_t)limits.initial, (uint32_t)limits.max}; -} +struct wasm_trap_t : wasm_ref_t { + wasm_trap_t(RefPtr<Trap> ptr) : wasm_ref_t(ptr) {} +}; -// Value conversion utilities -static TypedValue ToWabtValue(const wasm_val_t& value); -static void ToWabtValues(TypedValues& out_values, - const wasm_val_t values[], - size_t count); -static wasm_val_t FromWabtValue(std::shared_ptr<WasmInstance> instance, - const TypedValue& value); -static void FromWabtValues(std::shared_ptr<WasmInstance> instance, - wasm_val_t values[], - const TypedValues& wabt_values); +struct wasm_foreign_t : wasm_ref_t { + wasm_foreign_t(RefPtr<Foreign> ptr) : wasm_ref_t(ptr) {} +}; -struct wasm_functype_t : wasm_externtype_t { - wasm_functype_t(interp::FuncSignature& sig) - : wasm_externtype_t(WASM_EXTERN_FUNC) { - TRACE("new wasm_functype_t %" PRIzx " -> %" PRIzx "\n", sig.param_types.size(), - sig.result_types.size()); - wasm_valtype_vec_new_uninitialized(¶ms, sig.param_types.size()); - wasm_valtype_vec_new_uninitialized(&results, sig.result_types.size()); - - for (size_t i = 0; i < sig.param_types.size(); i++) { - params.data[i] = new wasm_valtype_t{FromWabtType(sig.param_types[i])}; - } - for (size_t i = 0; i < sig.result_types.size(); i++) { - results.data[i] = new wasm_valtype_t{FromWabtType(sig.result_types[i])}; - } +struct wasm_module_t : wasm_ref_t { + wasm_module_t(RefPtr<Module> ptr, const wasm_byte_vec_t* in) + : wasm_ref_t(ptr) { + wasm_byte_vec_copy(&binary, in); } - wasm_functype_t(wasm_valtype_vec_t* params, wasm_valtype_vec_t* results) - : wasm_externtype_t(WASM_EXTERN_FUNC), - params(*params), - results(*results) {} - - ~wasm_functype_t() { - TRACE("~wasm_functype_t\n"); - wasm_valtype_vec_delete(¶ms); - wasm_valtype_vec_delete(&results); + wasm_module_t(const wasm_module_t& other) : wasm_ref_t(other.I) { + wasm_byte_vec_copy(&binary, &other.binary); } - interp::FuncSignature Sig() const { - std::vector<Type> param_vec; - std::vector<Type> result_vec; - for (size_t i = 0; i < params.size; i++) { - param_vec.push_back(ToWabtType(wasm_valtype_kind(params.data[i]))); - } - for (size_t i = 0; i < results.size; i++) { - result_vec.push_back(ToWabtType(wasm_valtype_kind(results.data[i]))); - } - return interp::FuncSignature{param_vec, result_vec}; + wasm_module_t& operator=(const wasm_module_t& other) { + wasm_module_t copy(other); + std::swap(I, copy.I); + std::swap(binary, copy.binary); + return *this; } - wasm_valtype_vec_t params; - wasm_valtype_vec_t results; + ~wasm_module_t() { + wasm_byte_vec_delete(&binary); + } + // TODO: This is used for wasm_module_serialize/wasm_module_deserialize. + // Currently the standard wasm binary bytes are cached here, but it would be + // better to have a serialization of ModuleDesc instead. + wasm_byte_vec_t binary; }; -struct wasm_extern_t : wasm_extern_ref_t { - virtual ~wasm_extern_t() = default; - - interp::Environment* Env() const { return instance.get()->store->env; } - - wasm_extern_t(const wasm_extern_t& other) - : wasm_extern_ref_t(other), instance(other.instance) {} - - virtual wasm_externtype_t* ExternType() const = 0; - - std::shared_ptr<WasmInstance> instance; +struct wasm_shared_module_t : wasm_module_t {}; - protected: - wasm_extern_t(std::shared_ptr<WasmInstance> instance, - ExternalKind kind, - Index index) - : wasm_extern_ref_t(kind, index), instance(instance) {} +struct wasm_extern_t : wasm_ref_t { + wasm_extern_t(RefPtr<Extern> ptr) : wasm_ref_t(ptr) {} }; struct wasm_func_t : wasm_extern_t { - wasm_func_t(std::shared_ptr<WasmInstance> instance, Index index) - : wasm_extern_t(instance, ExternalKind::Func, index) {} - - interp::Func* GetFunc() const { return Env()->GetFunc(index); } - - interp::FuncSignature* GetSig() const { - return Env()->GetFuncSignature(GetFunc()->sig_index); - } - - wasm_externtype_t* ExternType() const override { - return new wasm_functype_t(*GetSig()); - } + wasm_func_t(RefPtr<Func> ptr) : wasm_extern_t(ptr) {} }; struct wasm_global_t : wasm_extern_t { - wasm_global_t(std::shared_ptr<WasmInstance> instance, Index index) - : wasm_extern_t(instance, ExternalKind::Global, index) {} - - interp::Global* GetGlobal() const { return Env()->GetGlobal(index); } - - wasm_externtype_t* ExternType() const override { - auto* g = GetGlobal(); - return new wasm_globaltype_t({FromWabtType(g->type)}, g->mutable_); - } + wasm_global_t(RefPtr<Global> ptr) : wasm_extern_t(ptr) {} }; struct wasm_table_t : wasm_extern_t { - wasm_table_t(std::shared_ptr<WasmInstance> instance, Index index) - : wasm_extern_t(instance, ExternalKind::Table, index) {} - - interp::Table* GetTable() const { return Env()->GetTable(index); } - - wasm_externtype_t* ExternType() const override { - auto* t = GetTable(); - return new wasm_tabletype_t({FromWabtType(t->elem_type)}, - FromWabtLimits(t->limits)); - } + wasm_table_t(RefPtr<Table> ptr) : wasm_extern_t(ptr) {} }; struct wasm_memory_t : wasm_extern_t { - wasm_memory_t(std::shared_ptr<WasmInstance> instance, Index index) - : wasm_extern_t(instance, ExternalKind::Memory, index) {} - - interp::Memory* GetMemory() const { - auto* env = instance.get()->store->env; - return env->GetMemory(index); - } - - wasm_externtype_t* ExternType() const override { - auto* mem = GetMemory(); - return new wasm_memorytype_t(FromWabtLimits(mem->page_limits)); - } + wasm_memory_t(RefPtr<Memory> ptr) : wasm_extern_t(ptr) {} }; -struct wasm_trap_t : wasm_ref_t { - wasm_trap_t(const wasm_message_t* msg) : wasm_ref_t(WasmRefType::Trap) { - wasm_name_copy(&message, msg); - } - wasm_message_t message; +struct wasm_instance_t : wasm_ref_t { + wasm_instance_t(RefPtr<Instance> ptr) : wasm_ref_t(ptr) {} }; -// Helper functions - -static Type ToWabtType(wasm_valkind_t kind) { +// Type conversion utilities +static ValueType ToWabtValueType(wasm_valkind_t kind) { switch (kind) { case WASM_I32: - return Type::I32; + return ValueType::I32; case WASM_I64: - return Type::I64; + return ValueType::I64; case WASM_F32: - return Type::F32; + return ValueType::F32; case WASM_F64: - return Type::F64; + return ValueType::F64; case WASM_ANYREF: - return Type::Anyref; + return ValueType::Anyref; case WASM_FUNCREF: - return Type::Funcref; + return ValueType::Funcref; default: - TRACE("unexpected wasm_valkind_t: %d\n", kind); + TRACE("unexpected wasm_valkind_t: %d", kind); WABT_UNREACHABLE; } WABT_UNREACHABLE; } -static wasm_valkind_t FromWabtType(Type type) { +static wasm_valkind_t FromWabtValueType(ValueType type) { switch (type) { - case Type::I32: + case ValueType::I32: return WASM_I32; - case Type::I64: + case ValueType::I64: return WASM_I64; - case Type::F32: + case ValueType::F32: return WASM_F32; - case Type::F64: + case ValueType::F64: return WASM_F64; - case Type::Anyref: + case ValueType::Anyref: + case ValueType::Hostref: + case ValueType::Nullref: return WASM_ANYREF; - case Type::Funcref: + case ValueType::Funcref: return WASM_FUNCREF; default: WABT_UNREACHABLE; } } +static wasm_externkind_t FromWabtExternKind(ExternKind kind) { + switch (kind) { + case ExternalKind::Func: + return WASM_EXTERN_FUNC; + case ExternalKind::Global: + return WASM_EXTERN_GLOBAL; + case ExternalKind::Table: + return WASM_EXTERN_TABLE; + case ExternalKind::Memory: + return WASM_EXTERN_MEMORY; + case ExternalKind::Event: + WABT_UNREACHABLE; + } + WABT_UNREACHABLE; +} + +static ValueTypes ToWabtValueTypes(const wasm_valtype_vec_t* types) { + ValueTypes result; + for (size_t i = 0; i < types->size; ++i) { + result.push_back(types->data[i]->I); + } + return result; +} + +static void FromWabtValueTypes(const ValueTypes& types, + wasm_valtype_vec_t* out) { + wasm_valtype_vec_new_uninitialized(out, types.size()); + for (size_t i = 0; i < types.size(); ++i) { + out->data[i] = wasm_valtype_new(FromWabtValueType(types[i])); + } +} + +static wasm_mutability_t FromWabtMutability(Mutability mut) { + return mut == Mutability::Var ? WASM_VAR : WASM_CONST; +} + +static Mutability ToWabtMutability(wasm_mutability_t mut) { + return mut == WASM_VAR ? Mutability::Var : Mutability::Const; +} + +// Value conversion utilities + static TypedValue ToWabtValue(const wasm_val_t& value) { - TypedValue out(ToWabtType(value.kind)); + TypedValue out; + out.type = ToWabtValueType(value.kind); switch (value.kind) { case WASM_I32: - out.set_i32(value.of.i32); + out.value.Set(value.of.i32); break; case WASM_I64: - out.set_i64(value.of.i64); + out.value.Set(value.of.i64); break; case WASM_F32: - out.set_f32(value.of.f32); + out.value.Set(value.of.f32); break; case WASM_F64: - out.set_f64(value.of.f64); - break; - case WASM_FUNCREF: { - auto* ext = static_cast<wasm_extern_t*>(value.of.ref); - assert(ext->kind == ExternalKind::Func); - out.set_ref(Ref{RefType::Func, ext->index}); + out.value.Set(value.of.f64); break; - } - case WASM_ANYREF: { - wasm_ref_t* ref = value.of.ref; - if (!ref) { - out.set_ref(Ref{RefType::Null, kInvalidIndex}); - break; - } - if (ref->kind == WasmRefType::Foreign) { - auto* f = static_cast<wasm_foreign_t*>(ref); - out.set_ref(Ref{RefType::Host, f->index}); - out.type = Type::Hostref; - break; - } - printf("unexpected WASM_ANYREF in ToWabtValue: %d\n", - static_cast<int>(ref->kind)); - WABT_UNREACHABLE; + case WASM_ANYREF: + case WASM_FUNCREF: + out.value.Set(value.of.ref ? value.of.ref->I->self() : Ref::Null); break; - } default: - TRACE("unexpected wasm type: %d\n", value.kind); + TRACE("unexpected wasm type: %d", value.kind); assert(false); } - TRACE("ToWabtValue -> %s\n", TypedValueToString(out).c_str()); + TRACE("-> %s", TypedValueToString(out).c_str()); return out; } -static void ToWabtValues(TypedValues& wabt_values, - const wasm_val_t values[], - size_t count) { - for (size_t i = 0; i < count; i++) { - wabt_values.push_back(ToWabtValue(values[i])); - } -} - -// wasm_byte_vec - -static wasm_val_t FromWabtValue(std::shared_ptr<WasmInstance> instance, - const TypedValue& value) { - TRACE("FromWabtValue: %s\n", TypedValueToString(value).c_str()); +static wasm_val_t FromWabtValue(Store& store, const TypedValue& tv) { + TRACE("%s", TypedValueToString(tv).c_str()); wasm_val_t out_value; - switch (value.type) { + switch (tv.type) { case Type::I32: out_value.kind = WASM_I32; - out_value.of.i32 = value.get_i32(); + out_value.of.i32 = tv.value.Get<s32>(); break; case Type::I64: out_value.kind = WASM_I64; - out_value.of.i64 = value.get_i64(); + out_value.of.i64 = tv.value.Get<s64>(); break; case Type::F32: out_value.kind = WASM_F32; - out_value.of.f32 = value.get_f32(); + out_value.of.f32 = tv.value.Get<f32>(); break; case Type::F64: out_value.kind = WASM_F64; - out_value.of.f64 = value.get_f64(); + out_value.of.f64 = tv.value.Get<f64>(); break; + case Type::Anyref: case Type::Funcref: - out_value.kind = WASM_FUNCREF; - out_value.of.ref = new wasm_func_t(instance, value.get_ref().index); - break; case Type::Hostref: - out_value.kind = WASM_ANYREF; - out_value.of.ref = new wasm_foreign_t(value.get_ref().index); - break; - case Type::Nullref: - out_value.kind = WASM_ANYREF; - out_value.of.ref = nullptr; + case Type::Nullref: { + Ref ref = tv.value.Get<Ref>(); + // Get the actual type for this reference; tv.type uses the function + // signature, which may be to general (e.g. anyref). + Type type = store.GetValueType(ref); + out_value.kind = FromWabtValueType(type); + switch (type) { + case Type::Funcref: + out_value.of.ref = new wasm_func_t(store.UnsafeGet<Func>(ref)); + break; + + case Type::Hostref: + out_value.of.ref = new wasm_foreign_t(store.UnsafeGet<Foreign>(ref)); + break; + + case Type::Nullref: + out_value.of.ref = nullptr; + break; + + default: WABT_UNREACHABLE; + } break; + } default: - TRACE("unexpected wabt type: %d\n", static_cast<int>(value.type)); + TRACE("unexpected wabt type: %d", static_cast<int>(tv.type)); assert(false); } return out_value; } -static void FromWabtValues(std::shared_ptr<WasmInstance> instance, - wasm_val_t values[], - const TypedValues& wabt_values) { - for (size_t i = 0; i < wabt_values.size(); i++) { - values[i] = FromWabtValue(instance, wabt_values[i]); +static Limits ToWabtLimits(const wasm_limits_t& limits) { + return Limits(limits.min, limits.max); +} + +static wasm_limits_t FromWabtLimits(const Limits& limits) { + return wasm_limits_t{(uint32_t)limits.initial, (uint32_t)limits.max}; +} + +static Values ToWabtValues(const wasm_val_t values[], size_t count) { + Values result; + for (size_t i = 0; i < count; ++i) { + result.push_back(ToWabtValue(values[i]).value); } + return result; } -static wasm_externkind_t FromWabtExternalKind(ExternalKind kind) { - switch (kind) { - case ExternalKind::Func: - return WASM_EXTERN_FUNC; - case ExternalKind::Global: - return WASM_EXTERN_GLOBAL; - case ExternalKind::Table: - return WASM_EXTERN_TABLE; - case ExternalKind::Memory: - return WASM_EXTERN_MEMORY; - case ExternalKind::Event: - WABT_UNREACHABLE; +static void FromWabtValues(Store& store, + wasm_val_t values[], + const ValueTypes& types, + const Values& wabt_values) { + assert(types.size() == wabt_values.size()); + for (size_t i = 0; i < types.size(); ++i) { + values[i] = FromWabtValue(store, TypedValue{types[i], wabt_values[i]}); } - WABT_UNREACHABLE; +} + +static std::string ToString(const wasm_message_t* msg) { + return std::string(msg->data, msg->size); +} + +static wasm_message_t FromString(const std::string& s) { + wasm_message_t result; + wasm_byte_vec_new(&result, s.size() + 1, s.c_str()); + return result; } static ReadBinaryOptions GetOptions() { @@ -593,22 +516,22 @@ extern "C" { // wasm_valtype -wasm_valtype_t* wasm_valtype_new(wasm_valkind_t kind) { - return new wasm_valtype_t{kind}; +own wasm_valtype_t* wasm_valtype_new(wasm_valkind_t kind) { + return new wasm_valtype_t{ToWabtValueType(kind)}; } wasm_valkind_t wasm_valtype_kind(const wasm_valtype_t* type) { assert(type); - return type->kind; + return FromWabtValueType(type->I); } // Helpers -static void print_sig(const interp::FuncSignature& sig) { +static void print_sig(const FuncType& sig) { #ifndef NDEBUG fprintf(stderr, "("); bool first = true; - for (auto Type : sig.param_types) { + for (auto Type : sig.params) { if (!first) { fprintf(stderr, ", "); } @@ -617,7 +540,7 @@ static void print_sig(const interp::FuncSignature& sig) { } fprintf(stderr, ") -> ("); first = true; - for (auto Type : sig.result_types) { + for (auto Type : sig.results) { if (!first) { fprintf(stderr, ", "); } @@ -630,7 +553,7 @@ static void print_sig(const interp::FuncSignature& sig) { // wasm_val -void wasm_val_delete(wasm_val_t* val) { +void wasm_val_delete(own wasm_val_t* val) { assert(val); if (wasm_valkind_is_ref(val->kind)) { delete val->of.ref; @@ -638,134 +561,100 @@ void wasm_val_delete(wasm_val_t* val) { } } -void wasm_val_copy(wasm_val_t* out, const wasm_val_t* in) { - TRACE("wasm_val_copy %s\n", TypedValueToString(ToWabtValue(*in)).c_str()); - *out = *in; - TRACE("wasm_val_copy -> %p %s\n", out, - TypedValueToString(ToWabtValue(*out)).c_str()); +void wasm_val_copy(own wasm_val_t* out, const wasm_val_t* in) { + TRACE("%s", TypedValueToString(ToWabtValue(*in)).c_str()); + if (wasm_valkind_is_ref(in->kind)) { + out->kind = in->kind; + out->of.ref = in->of.ref ? new wasm_ref_t{*in->of.ref} : nullptr; + } else { + *out = *in; + } + TRACE("-> %p %s", out, TypedValueToString(ToWabtValue(*out)).c_str()); } // wasm_trap -wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t* msg) { - return new wasm_trap_t(msg); +own wasm_trap_t* wasm_trap_new(wasm_store_t* store, const wasm_message_t* msg) { + return new wasm_trap_t{Trap::New(store->I, ToString(msg))}; } -void wasm_trap_message(const wasm_trap_t* trap, wasm_message_t* out) { +void wasm_trap_message(const wasm_trap_t* trap, own wasm_message_t* out) { assert(trap); - wasm_name_copy(out, &trap->message); + *out = FromString(trap->As<Trap>()->message()); } -wasm_frame_t* wasm_trap_origin(const wasm_trap_t* trap) { +own wasm_frame_t* wasm_trap_origin(const wasm_trap_t* trap) { // TODO(sbc): Implement stack traces return nullptr; } -void wasm_trap_trace(const wasm_trap_t* trap, wasm_frame_vec_t* out) { +void wasm_trap_trace(const wasm_trap_t* trap, own wasm_frame_vec_t* out) { assert(trap); assert(out); wasm_frame_vec_new_empty(out); // std::string msg(trap->message.data, trap->message.size); - // fTRACE(stderr, "error: %s\n", msg.c_str()); - return; + // TRACE(stderr, "error: %s\n", msg.c_str()); } // wasm_engine -wasm_engine_t* wasm_engine_new() { +own wasm_engine_t* wasm_engine_new() { return new wasm_engine_t(); } -wasm_engine_t* wasm_engine_new_with_config(wasm_config_t*) { +own wasm_engine_t* wasm_engine_new_with_config(own wasm_config_t*) { assert(false); return nullptr; } // wasm_store -wasm_store_t* wasm_store_new(wasm_engine_t* engine) { +own wasm_store_t* wasm_store_new(wasm_engine_t* engine) { assert(engine); - if (!s_trace_stream) { - s_trace_stream = s_stdout_stream.get(); - } - Environment* env = new Environment(s_features); - Executor* executor = new Executor(env, s_trace_stream, s_thread_options); - return new wasm_store_t(env, executor); + return new wasm_store_t(s_features); } // wasm_module -wasm_module_t* wasm_module_new(wasm_store_t* store, - const wasm_byte_vec_t* binary) { +own wasm_module_t* wasm_module_new(wasm_store_t* store, + const wasm_byte_vec_t* binary) { Errors errors; - ModuleMetadata* metadata = nullptr; - wabt::Result result = ReadBinaryMetadata(binary->data, binary->size, - GetOptions(), &errors, &metadata); - if (!Succeeded(result)) { + ModuleDesc module_desc; + if (Failed(ReadBinaryInterp(binary->data, binary->size, GetOptions(), &errors, + &module_desc))) { + FormatErrorsToFile(errors, Location::Type::Binary); return nullptr; } - return new wasm_module_t(store, binary, metadata); + + return new wasm_module_t{Module::New(store->I, module_desc), binary}; } void wasm_module_exports(const wasm_module_t* module, - wasm_exporttype_vec_t* out) { - size_t num_exports = module->metadata->exports.size(); - TRACE("wasm_module_exports: %" PRIzx "\n", num_exports); - wasm_exporttype_vec_new_uninitialized(out, num_exports); - auto* env = module->store->env; - for (size_t i = 0; i < num_exports; i++) { - interp::Export& exp = module->metadata->exports[i]; - wasm_name_t name; - wasm_name_new_from_string(&name, exp.name.c_str()); - TRACE("module_export: %s\n", exp.name.c_str()); - switch (exp.kind) { - case ExternalKind::Func: { - interp::Func* func = env->GetFunc(exp.index); - auto* type = - new wasm_functype_t(*env->GetFuncSignature(func->sig_index)); - out->data[i] = new wasm_exporttype_t{name, type}; - break; - } - case ExternalKind::Global: { - auto* g = env->GetGlobal(exp.index); - auto* type = - new wasm_globaltype_t({FromWabtType(g->type)}, g->mutable_); - out->data[i] = new wasm_exporttype_t{name, type}; - break; - } - case ExternalKind::Table: { - auto* t = env->GetTable(exp.index); - auto* type = new wasm_tabletype_t({FromWabtType(t->elem_type)}, - FromWabtLimits(t->limits)); - out->data[i] = new wasm_exporttype_t{name, type}; - break; - } - case ExternalKind::Memory: { - auto* mem = env->GetMemory(exp.index); - auto* type = new wasm_memorytype_t(FromWabtLimits(mem->page_limits)); - out->data[i] = new wasm_exporttype_t{name, type}; - break; - } - case ExternalKind::Event: - WABT_UNREACHABLE; - } + own wasm_exporttype_vec_t* out) { + auto&& export_types = module->As<Module>()->export_types(); + TRACE("%" PRIzx, export_types.size()); + wasm_exporttype_vec_new_uninitialized(out, export_types.size()); + + for (size_t i = 0; i < export_types.size(); ++i) { + out->data[i] = new wasm_exporttype_t{export_types[i]}; } } -void wasm_module_serialize(const wasm_module_t* module, wasm_byte_vec_t* out) { +void wasm_module_serialize(const wasm_module_t* module, + own wasm_byte_vec_t* out) { wasm_byte_vec_copy(out, &module->binary); } -wasm_module_t* wasm_module_deserialize(wasm_store_t* store, - const wasm_byte_vec_t* bytes) { +own wasm_module_t* wasm_module_deserialize(wasm_store_t* store, + const wasm_byte_vec_t* bytes) { return wasm_module_new(store, bytes); } // wasm_exporttype -wasm_exporttype_t* wasm_exporttype_new(wasm_name_t* name, - wasm_externtype_t* type) { - return new wasm_exporttype_t{*name, type}; +own wasm_exporttype_t* wasm_exporttype_new(own wasm_name_t* name, + wasm_externtype_t* type) { + return new wasm_exporttype_t{ExportType{ToString(name), type->I->Clone()}}; } const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t* ex) { @@ -773,103 +662,62 @@ const wasm_name_t* wasm_exporttype_name(const wasm_exporttype_t* ex) { } const wasm_externtype_t* wasm_exporttype_type(const wasm_exporttype_t* ex) { - TRACE("wasm_exporttype_type %p\n", ex); + TRACE("%p", ex); assert(ex); - return ex->type.get(); + return ex->extern_.get(); } // wasm_instance -wasm_instance_t* wasm_instance_new(wasm_store_t* store, - const wasm_module_t* module, - const wasm_extern_t* const imports[], - wasm_trap_t** trap_out) { - TRACE("wasm_instance_new: %p %p\n", store, module); +own wasm_instance_t* wasm_instance_new(wasm_store_t* store, + const wasm_module_t* module, + const wasm_extern_t* const imports[], + own wasm_trap_t** trap_out) { + TRACE("%p %p", store, module); assert(module); - assert(module->metadata); assert(store); - assert(store->env); - Errors errors; - DefinedModule* interp_module = nullptr; - std::vector<interp::Export> wabt_imports; - std::vector<interp::Export*> wabt_import_ptrs; - - for (size_t i = 0; i < module->metadata->imports.size(); i++) { - wabt_imports.emplace_back("", imports[i]->kind, imports[i]->index); + RefVec import_refs; + for (size_t i = 0; i < module->As<Module>()->import_types().size(); i++) { + import_refs.push_back(imports[i]->I->self()); } - for (auto& i : wabt_imports) { - wabt_import_ptrs.push_back(&i); - } - - wabt::Result result = - ReadBinaryInterp(store->env, module->binary.data, module->binary.size, - GetOptions(), wabt_import_ptrs, &errors, &interp_module); - - FormatErrorsToFile(errors, Location::Type::Binary); - if (!Succeeded(result)) { - TRACE("ReadBinaryInterp failed!\n"); + Trap::Ptr trap; + auto instance = + Instance::Instantiate(store->I, module->I->self(), import_refs, &trap); + if (trap) { + *trap_out = new wasm_trap_t{trap}; return nullptr; } - ExecResult exec_result = store->executor->Initialize(interp_module); - if (!exec_result.ok()) { - TRACE("Initialize failed!\n"); - wasm_name_t msg; - wasm_name_new_from_string(&msg, ResultToString(exec_result.result).c_str()); - wasm_trap_t* trap = wasm_trap_new(store, &msg); - *trap_out = trap; - return nullptr; - } - return new wasm_instance_t(store, interp_module); + return new wasm_instance_t{instance}; } void wasm_instance_exports(const wasm_instance_t* instance, - wasm_extern_vec_t* out) { - WasmInstance& wasm_instance = *instance->ptr.get(); - DefinedModule* module = wasm_instance.module.get(); - size_t num_exports = module->exports.size(); - wasm_extern_vec_new_uninitialized(out, num_exports); - TRACE("wasm_instance_exports: %" PRIzx "\n", num_exports); - - for (size_t i = 0; i < num_exports; i++) { - interp::Export* export_ = &module->exports[i]; - TRACE("getexport: '%s' %d\n", export_->name.c_str(), - static_cast<int>(export_->kind)); - switch (export_->kind) { - case ExternalKind::Func: - out->data[i] = new wasm_func_t(instance->ptr, export_->index); - break; - case ExternalKind::Global: - out->data[i] = new wasm_global_t(instance->ptr, export_->index); - break; - case ExternalKind::Table: - out->data[i] = new wasm_table_t(instance->ptr, export_->index); - break; - case ExternalKind::Memory: - out->data[i] = new wasm_memory_t(instance->ptr, export_->index); - break; - case ExternalKind::Event: - WABT_UNREACHABLE; - } + own wasm_extern_vec_t* out) { + auto&& exports = instance->As<Instance>()->exports(); + wasm_extern_vec_new_uninitialized(out, exports.size()); + TRACE("%" PRIzx, exports.size()); + + for (size_t i = 0; i < exports.size(); ++i) { + out->data[i] = + new wasm_extern_t{instance->I.store()->UnsafeGet<Extern>(exports[i])}; } } // wasm_functype -wasm_functype_t* wasm_functype_new(wasm_valtype_vec_t* params, - wasm_valtype_vec_t* results) { - TRACE("wasm_functype_new params=%" PRIzx " args=%" PRIzx "\n", params->size, results->size); - auto* res = new wasm_functype_t(params, results); - interp::FuncSignature sig = res->Sig(); - TRACE("wasm_functype_new -> "); - print_sig(sig); +own wasm_functype_t* wasm_functype_new(own wasm_valtype_vec_t* params, + own wasm_valtype_vec_t* results) { + TRACE("params=%" PRIzx " args=%" PRIzx, params->size, results->size); + auto* res = new wasm_functype_t{params, results}; + TRACE_NO_NL(""); + print_sig(*res->As<FuncType>()); return res; } const wasm_valtype_vec_t* wasm_functype_params(const wasm_functype_t* f) { - TRACE("wasm_functype_params: %" PRIzx "\n", f->params.size); + TRACE("%" PRIzx, f->params.size); return &f->params; } @@ -878,145 +726,120 @@ const wasm_valtype_vec_t* wasm_functype_results(const wasm_functype_t* f) { } // wasm_func - -static wasm_func_t* do_wasm_func_new( - wasm_store_t* store, - const wasm_functype_t* type, - wasm_func_callback_t host_callback, - wasm_func_callback_with_env_t host_callback_with_env, - void* env, - void (*finalizer)(void*)) { - TRACE("do_wasm_func_new\n"); - type->Sig(); - auto instance = std::make_shared<WasmInstance>(store, nullptr); - - HostFunc::Callback callback = - [instance, host_callback, host_callback_with_env, env]( - const HostFunc* func, const interp::FuncSignature* sig, - const TypedValues& args, TypedValues& results) -> interp::Result { - TRACE("calling host function: "); - print_sig(*sig); - wasm_val_t* host_args = new wasm_val_t[args.size()]; - wasm_val_t* host_results = new wasm_val_t[sig->result_types.size()]; - FromWabtValues(instance, host_args, args); - wasm_trap_t* trap; - if (host_callback) { - trap = host_callback(host_args, host_results); - } else { - assert(host_callback_with_env); - trap = host_callback_with_env(env, host_args, host_results); - } +own wasm_func_t* wasm_func_new(wasm_store_t* store, + const wasm_functype_t* type, + wasm_func_callback_t callback) { + FuncType wabt_type = *type->As<FuncType>(); + auto lambda = [=](const Values& wabt_params, Values& wabt_results, + Trap::Ptr* out_trap) -> Result { + wasm_val_vec_t params, results; + wasm_val_vec_new_uninitialized(¶ms, wabt_params.size()); + wasm_val_vec_new_uninitialized(&results, wabt_results.size()); + FromWabtValues(store->I, params.data, wabt_type.params, wabt_params); + auto trap = callback(params.data, results.data); + wasm_val_vec_delete(¶ms); if (trap) { - TRACE("host function trapped\n"); - delete[] host_args; - delete[] host_results; - return interp::ResultType::TrapHostTrapped; + *out_trap = trap->I.As<Trap>(); + wasm_trap_delete(trap); + // Can't use wasm_val_vec_delete since it wasn't populated. + delete[] results.data; + return Result::Error; } - TRACE("adding result types: %" PRIzx "\n", sig->result_types.size()); - for (size_t i = 0; i < sig->result_types.size(); i++) { - TRACE("result: %p\n", &host_results[i]); - results[i] = ToWabtValue(host_results[i]); - TRACE("added result value: %s\n", TypedValueToString(results[i]).c_str()); - } - delete[] host_args; - delete[] host_results; - return interp::ResultType::Ok; + wabt_results = ToWabtValues(results.data, results.size); + wasm_val_vec_delete(&results); + return Result::Ok; }; - type->Sig(); - type->Sig(); - static int function_count = 0; - std::string name = std::string("function") + std::to_string(function_count++); - store->env->EmplaceBackFuncSignature(type->Sig()); - Index sig_index = store->env->GetFuncSignatureCount() - 1; - auto* host_func = new HostFunc("extern", name, sig_index, callback); - store->env->EmplaceBackFunc(host_func); - Index func_index = store->env->GetFuncCount() - 1; - return new wasm_func_t(instance, func_index); -} - -wasm_func_t* wasm_func_new(wasm_store_t* store, - const wasm_functype_t* type, - wasm_func_callback_t callback) { - return do_wasm_func_new(store, type, callback, nullptr, nullptr, nullptr); -} + return new wasm_func_t{HostFunc::New(store->I, wabt_type, lambda)}; +} + +own wasm_func_t* wasm_func_new_with_env(wasm_store_t* store, + const wasm_functype_t* type, + wasm_func_callback_with_env_t callback, + void* env, + void (*finalizer)(void*)) { + FuncType wabt_type = *type->As<FuncType>(); + auto lambda = [=](const Values& wabt_params, Values& wabt_results, + Trap::Ptr* out_trap) -> Result { + wasm_val_vec_t params, results; + wasm_val_vec_new_uninitialized(¶ms, wabt_params.size()); + wasm_val_vec_new_uninitialized(&results, wabt_results.size()); + FromWabtValues(store->I, params.data, wabt_type.params, wabt_params); + auto trap = callback(env, params.data, results.data); + wasm_val_vec_delete(¶ms); + if (trap) { + *out_trap = trap->I.As<Trap>(); + wasm_trap_delete(trap); + // Can't use wasm_val_vec_delete since it wasn't populated. + delete[] results.data; + return Result::Error; + } + wabt_results = ToWabtValues(results.data, results.size); + wasm_val_vec_delete(&results); + return Result::Ok; + }; -wasm_func_t* wasm_func_new_with_env(wasm_store_t* store, - const wasm_functype_t* type, - wasm_func_callback_with_env_t callback, - void* env, - void (*finalizer)(void*)) { - return do_wasm_func_new(store, type, nullptr, callback, env, finalizer); + // TODO: This finalizer is different from the host_info finalizer. + return new wasm_func_t{HostFunc::New(store->I, wabt_type, lambda)}; } -wasm_functype_t* wasm_func_type(const wasm_func_t* func) { - TRACE("wasm_func_type\n"); - return new wasm_functype_t(*func->GetSig()); +own wasm_functype_t* wasm_func_type(const wasm_func_t* func) { + TRACE0(); + return new wasm_functype_t{func->As<Func>()->type()}; } size_t wasm_func_result_arity(const wasm_func_t* func) { - interp::Func* wabt_func = func->GetFunc(); - auto* env = func->instance.get()->store->env; - interp::FuncSignature* sig = env->GetFuncSignature(wabt_func->sig_index); - return sig->result_types.size(); + return func->As<Func>()->type().results.size(); } size_t wasm_func_param_arity(const wasm_func_t* func) { - interp::Func* wabt_func = func->GetFunc(); - auto* env = func->instance.get()->store->env; - interp::FuncSignature* sig = env->GetFuncSignature(wabt_func->sig_index); - return sig->param_types.size(); -} - -wasm_trap_t* wasm_func_call(const wasm_func_t* f, - const wasm_val_t args[], - wasm_val_t results[]) { - TRACE("wasm_func_call %d\n", f->index); - assert(f); - wasm_store_t* store = f->instance.get()->store; - assert(store); - wasm_functype_t* functype = wasm_func_type(f); - TypedValues wabt_args; - ToWabtValues(wabt_args, args, functype->params.size); - wasm_functype_delete(functype); - assert(f->kind == ExternalKind::Func); - ExecResult res = store->executor->RunFunction(f->index, wabt_args); - if (!res.ok()) { - std::string msg = ResultToString(res.result); - TRACE("wasm_func_call failed: %s\n", msg.c_str()); - wasm_name_t message; - wasm_name_new_from_string(&message, msg.c_str()); - wasm_trap_t* trap = wasm_trap_new(store, &message); - wasm_name_delete(&message); - return trap; + return func->As<Func>()->type().params.size(); +} + +own wasm_trap_t* wasm_func_call(const wasm_func_t* f, + const wasm_val_t args[], + wasm_val_t results[]) { + // TODO: get some information about the function; name/index + // TRACE("%d", f->index); + + auto&& func_type = f->As<Func>()->type(); + Values wabt_args = ToWabtValues(args, func_type.params.size()); + Values wabt_results; + Trap::Ptr trap; + if (Failed( + f->As<Func>()->Call(*f->I.store(), wabt_args, wabt_results, &trap))) { + return new wasm_trap_t{trap}; } - FromWabtValues(f->instance, results, res.values); + FromWabtValues(*f->I.store(), results, func_type.results, wabt_results); return nullptr; } // wasm_globaltype -wasm_globaltype_t* wasm_globaltype_new(wasm_valtype_t* type, - wasm_mutability_t mut) { +own wasm_globaltype_t* wasm_globaltype_new(own wasm_valtype_t* type, + wasm_mutability_t mut) { assert(type); - return new wasm_globaltype_t(*type, mut == WASM_VAR); + auto* result = new wasm_globaltype_t{GlobalType{ + type->I, mut == WASM_CONST ? Mutability::Const : Mutability::Var}}; + wasm_valtype_delete(type); + return result; } wasm_mutability_t wasm_globaltype_mutability(const wasm_globaltype_t* type) { assert(type); - return type->mutable_; + return FromWabtMutability(type->As<GlobalType>()->mut); } const wasm_valtype_t* wasm_globaltype_content(const wasm_globaltype_t* type) { assert(type); - return &type->type; + return &type->valtype; } // wasm_tabletype -wasm_tabletype_t* wasm_tabletype_new(wasm_valtype_t* type, - const wasm_limits_t* limits) { - return new wasm_tabletype_t(*type, *limits); +own wasm_tabletype_t* wasm_tabletype_new(own wasm_valtype_t* type, + const wasm_limits_t* limits) { + return new wasm_tabletype_t{type, limits}; } const wasm_valtype_t* wasm_tabletype_element(const wasm_tabletype_t* type) { @@ -1029,8 +852,8 @@ const wasm_limits_t* wasm_tabletype_limits(const wasm_tabletype_t* type) { // wasm_memorytype -wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t* limits) { - return new wasm_memorytype_t(*limits); +own wasm_memorytype_t* wasm_memorytype_new(const wasm_limits_t* limits) { + return new wasm_memorytype_t(limits); } const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t* t) { @@ -1039,236 +862,160 @@ const wasm_limits_t* wasm_memorytype_limits(const wasm_memorytype_t* t) { // wasm_global -wasm_global_t* wasm_global_new(wasm_store_t* store, - const wasm_globaltype_t* type, - const wasm_val_t* val) { - assert(store && store && type); +own wasm_global_t* wasm_global_new(wasm_store_t* store, + const wasm_globaltype_t* type, + const wasm_val_t* val) { + assert(store && type); TypedValue value = ToWabtValue(*val); - TRACE("wasm_global_new: %s\n", TypedValueToString(value).c_str()); - interp::Global* g = store->env->EmplaceBackGlobal(value.type, type->mutable_); - g->typed_value = value; - Index global_index = store->env->GetGlobalCount() - 1; - auto instance = std::make_shared<WasmInstance>(store, nullptr); - return new wasm_global_t(instance, global_index); + TRACE("%s", TypedValueToString(value).c_str()); + return new wasm_global_t{ + Global::New(store->I, *type->As<GlobalType>(), value.value)}; } -wasm_globaltype_t* wasm_global_type(const wasm_global_t* global) { - assert(global); - assert(false); - WABT_UNREACHABLE; +own wasm_globaltype_t* wasm_global_type(const wasm_global_t* global) { + return new wasm_globaltype_t{global->As<Global>()->type()}; } -void wasm_global_get(const wasm_global_t* global, wasm_val_t* out) { +void wasm_global_get(const wasm_global_t* global, own wasm_val_t* out) { assert(global); - TRACE("wasm_global_get"); - interp::Global* interp_global = global->GetGlobal(); - TRACE(" -> %s\n", TypedValueToString(interp_global->typed_value).c_str()); - *out = FromWabtValue(global->instance, interp_global->typed_value); - return; + TRACE0(); + TypedValue tv{global->As<Global>()->type().type, global->As<Global>()->Get()}; + TRACE(" -> %s", TypedValueToString(tv).c_str()); + *out = FromWabtValue(*global->I.store(), tv); } void wasm_global_set(wasm_global_t* global, const wasm_val_t* val) { - TRACE("wasm_global_set\n"); - interp::Global* g = global->GetGlobal(); - g->typed_value = ToWabtValue(*val); + TRACE0(); + if (wasm_valkind_is_ref(val->kind)) { + global->As<Global>()->Set(*global->I.store(), val->of.ref->I->self()); + } else { + global->As<Global>()->UnsafeSet(ToWabtValue(*val).value); + } } // wasm_table -wasm_table_t* wasm_table_new(wasm_store_t* store, - const wasm_tabletype_t* type, - wasm_ref_t* init) { - store->env->EmplaceBackTable(ToWabtType(type->elemtype.kind), - ToWabtLimits(type->limits)); - Index index = store->env->GetTableCount() - 1; - auto instance = std::make_shared<WasmInstance>(store, nullptr); - return new wasm_table_t(instance, index); +own wasm_table_t* wasm_table_new(wasm_store_t* store, + const wasm_tabletype_t* type, + wasm_ref_t* init) { + return new wasm_table_t{Table::New(store->I, *type->As<TableType>())}; } -wasm_tabletype_t* wasm_table_type(const wasm_table_t*) { - assert(false); - return nullptr; +own wasm_tabletype_t* wasm_table_type(const wasm_table_t* table) { + return new wasm_tabletype_t{table->As<Table>()->type()}; } wasm_table_size_t wasm_table_size(const wasm_table_t* table) { - return table->GetTable()->size(); + return table->As<Table>()->size(); } -wasm_ref_t* wasm_table_get(const wasm_table_t* t, wasm_table_size_t index) { - interp::Table* table = t->GetTable(); - // TODO(sbc): This duplicates code from the CallIndirect handler. I imagine - // we will refactor this when we implment the TableGet opcode. - if (index >= table->size()) +own wasm_ref_t* wasm_table_get(const wasm_table_t* table, + wasm_table_size_t index) { + Ref ref; + if (Failed(table->As<Table>()->Get(index, &ref))) { return nullptr; - switch (table->entries[index].kind) { - case RefType::Func: { - Index func_index = table->entries[index].index; - if (func_index == kInvalidIndex) - return nullptr; - TRACE("wasm_table_get: %u -> %u\n", index, func_index); - return new wasm_extern_ref_t(ExternalKind::Func, func_index); - } - case RefType::Host: - return new wasm_foreign_ref_t(table->entries[index].index); - case RefType::Null: - return nullptr; } - WABT_UNREACHABLE; + if (ref == Ref::Null) { + return nullptr; + } + return new wasm_ref_t{table->I.store()->UnsafeGet<Object>(ref)}; } -bool wasm_table_set(wasm_table_t* t, wasm_table_size_t index, wasm_ref_t* ref) { - interp::Table* table = t->GetTable(); - if (index >= table->size()) { - return false; - } - TRACE("wasm_table_set %p\n", ref); - if (!ref) { - table->entries[index] = Ref{RefType::Null, kInvalidIndex}; - return true; - } - TRACE("wasm_table_set %s\n", WasmRefTypeToString(ref->kind).c_str()); - switch (ref->kind) { - case WasmRefType::Extern: { - auto* ext = static_cast<wasm_extern_ref_t*>(ref); - switch (ext->kind) { - case ExternalKind::Func: - table->entries[index] = Ref{RefType::Func, ext->index}; - break; - case ExternalKind::Table: - case ExternalKind::Memory: - case ExternalKind::Global: - case ExternalKind::Event: - return false; - } - break; - } - case WasmRefType::Foreign: { - auto* f = static_cast<wasm_foreign_ref_t*>(ref); - table->entries[index] = Ref{RefType::Host, f->index}; - break; - } - case WasmRefType::Module: - case WasmRefType::Instance: - case WasmRefType::Trap: - return false; - } - return true; +bool wasm_table_set(wasm_table_t* table, + wasm_table_size_t index, + wasm_ref_t* ref) { + return Succeeded(table->As<Table>()->Set(*table->I.store(), index, + ref ? ref->I->self() : Ref::Null)); } -bool wasm_table_grow(wasm_table_t* t, +bool wasm_table_grow(wasm_table_t* table, wasm_table_size_t delta, wasm_ref_t* init) { - interp::Table* table = t->GetTable(); - size_t cursize = table->size(); - size_t newsize = cursize + delta; - if (newsize > table->limits.max) { - return false; - } - TRACE("wasm_table_grow %" PRIzx " -> %" PRIzx "\n", cursize, newsize); - auto* ext = static_cast<wasm_extern_ref_t*>(init); - if (ext && ext->kind != ExternalKind::Func) { - return false; - } - Ref init_ref{RefType::Null, kInvalidIndex}; - if (ext) { - init_ref = Ref{RefType::Func, ext->index}; - } - table->entries.resize(newsize, init_ref); - return true; + return Succeeded(table->As<Table>()->Grow( + *table->I.store(), delta, init ? init->I->self() : Ref::Null)); } // wams_memory -wasm_memory_t* wasm_memory_new(wasm_store_t* store, - const wasm_memorytype_t* type) { - TRACE("wasm_memory_new\n"); - store->env->EmplaceBackMemory(Limits(type->limits.min, type->limits.max)); - Index index = store->env->GetMemoryCount() - 1; - auto instance = std::make_shared<WasmInstance>(store, nullptr); - return new wasm_memory_t(instance, index); +own wasm_memory_t* wasm_memory_new(wasm_store_t* store, + const wasm_memorytype_t* type) { + TRACE0(); + return new wasm_memory_t{Memory::New(store->I, *type->As<MemoryType>())}; } -byte_t* wasm_memory_data(wasm_memory_t* m) { - interp::Memory* memory = m->GetMemory(); - return memory->data.data(); +byte_t* wasm_memory_data(wasm_memory_t* memory) { + return reinterpret_cast<byte_t*>(memory->As<Memory>()->UnsafeData()); } -wasm_memory_pages_t wasm_memory_size(const wasm_memory_t* m) { - interp::Memory* memory = m->GetMemory(); - return memory->data.size() / WABT_PAGE_SIZE; +wasm_memory_pages_t wasm_memory_size(const wasm_memory_t* memory) { + return memory->As<Memory>()->PageSize(); } -size_t wasm_memory_data_size(const wasm_memory_t* m) { - interp::Memory* memory = m->GetMemory(); - return memory->data.size(); +size_t wasm_memory_data_size(const wasm_memory_t* memory) { + return memory->As<Memory>()->ByteSize(); } -bool wasm_memory_grow(wasm_memory_t* m, wasm_memory_pages_t delta) { - interp::Memory* memory = m->GetMemory(); - size_t cursize = memory->data.size() / WABT_PAGE_SIZE; - size_t newsize = cursize + delta; - if (newsize > memory->page_limits.max) { - return false; - } - TRACE("wasm_memory_grow %" PRIzx " -> %" PRIzx "\n", cursize, newsize); - memory->data.resize(newsize * WABT_PAGE_SIZE); - return true; +bool wasm_memory_grow(wasm_memory_t* memory, wasm_memory_pages_t delta) { + return Succeeded(memory->As<Memory>()->Grow(delta)); } // wasm_frame +own wasm_frame_t* wasm_frame_copy(const wasm_frame_t* frame) { + return new wasm_frame_t{*frame}; +} + wasm_instance_t* wasm_frame_instance(const wasm_frame_t* frame) { - assert(frame); - return frame->instance; + // TODO + return nullptr; } size_t wasm_frame_module_offset(const wasm_frame_t* frame) { - assert(frame); - return frame->offset; + // TODO + return 0; } size_t wasm_frame_func_offset(const wasm_frame_t* frame) { - assert(false); + // TODO return 0; } uint32_t wasm_frame_func_index(const wasm_frame_t* frame) { - assert(frame); - return frame->func_index; + // TODO + return 0; } // wasm_externtype wasm_externkind_t wasm_externtype_kind(const wasm_externtype_t* type) { assert(type); - return type->kind; + return FromWabtExternKind(type->I->kind); } // wasm_extern -wasm_externtype_t* wasm_extern_type(const wasm_extern_t* extern_) { - return extern_->ExternType(); +own wasm_externtype_t* wasm_extern_type(const wasm_extern_t* extern_) { + return wasm_externtype_t::New(extern_->As<Extern>()->extern_type().Clone()) + .release(); } wasm_externkind_t wasm_extern_kind(const wasm_extern_t* extern_) { - return FromWabtExternalKind(extern_->kind); + return FromWabtExternKind(extern_->As<Extern>()->extern_type().kind); } // wasm_foreign -wasm_foreign_t* wasm_foreign_new(wasm_store_t* store) { - store->env->EmplaceBackHostObject(); - Index new_index = store->env->GetHostCount() - 1; - return new wasm_foreign_t(new_index); +own wasm_foreign_t* wasm_foreign_new(wasm_store_t* store) { + return new wasm_foreign_t{Foreign::New(store->I, nullptr)}; } // vector types -#define WASM_IMPL_OWN(name) \ - void wasm_##name##_delete(wasm_##name##_t* t) { \ - assert(t); \ - TRACE("wasm_" #name "_delete\n"); \ - delete t; \ +#define WASM_IMPL_OWN(name) \ + void wasm_##name##_delete(own wasm_##name##_t* t) { \ + assert(t); \ + TRACE0(); \ + delete t; \ } WASM_IMPL_OWN(frame); @@ -1276,84 +1023,146 @@ WASM_IMPL_OWN(config); WASM_IMPL_OWN(engine); WASM_IMPL_OWN(store); -#define WASM_IMPL_VEC(name, ptr_or_none) \ - void wasm_##name##_vec_new(wasm_##name##_vec_t* vec, size_t size, \ - wasm_##name##_t ptr_or_none const src[]) { \ - TRACE("wasm_" #name "_vec_new\n"); \ +#define WASM_IMPL_VEC_BASE(name, ptr_or_none) \ + void wasm_##name##_vec_new_empty(own wasm_##name##_vec_t* out) { \ + TRACE0(); \ + wasm_##name##_vec_new_uninitialized(out, 0); \ + } \ + void wasm_##name##_vec_new_uninitialized(own wasm_##name##_vec_t* vec, \ + size_t size) { \ + TRACE("%" PRIzx, size); \ + vec->size = size; \ + vec->data = size ? new wasm_##name##_t ptr_or_none[size] : nullptr; \ + } + +#define WASM_IMPL_VEC_PLAIN(name) \ + WASM_IMPL_VEC_BASE(name, ) \ + void wasm_##name##_vec_new(own wasm_##name##_vec_t* vec, size_t size, \ + own wasm_##name##_t const src[]) { \ + TRACE0(); \ wasm_##name##_vec_new_uninitialized(vec, size); \ - memcpy(vec->data, src, size * sizeof(wasm_##name##_t ptr_or_none)); \ + memcpy(vec->data, src, size * sizeof(wasm_##name##_t)); \ } \ - void wasm_##name##_vec_new_empty(wasm_##name##_vec_t* out) { \ - TRACE("wasm_" #name "_vec_new_empty\n"); \ - out->data = nullptr; \ - out->size = 0; \ + void wasm_##name##_vec_copy(own wasm_##name##_vec_t* out, \ + const wasm_##name##_vec_t* vec) { \ + TRACE("%" PRIzx, vec->size); \ + wasm_##name##_vec_new_uninitialized(out, vec->size); \ + memcpy(out->data, vec->data, vec->size * sizeof(wasm_##name##_t)); \ } \ - void wasm_##name##_vec_new_uninitialized(wasm_##name##_vec_t* vec, \ - size_t size) { \ - TRACE("wasm_" #name "_vec_new_uninitialized %" PRIzx "\n", size); \ - vec->size = size; \ - if (size) \ - vec->data = new wasm_##name##_t ptr_or_none[size]; \ - else \ - vec->data = nullptr; \ + void wasm_##name##_vec_delete(own wasm_##name##_vec_t* vec) { \ + TRACE0(); \ + delete[] vec->data; \ + vec->size = 0; \ + } + +WASM_IMPL_VEC_PLAIN(byte); + +// Special implementation for wasm_val_t, since it's weird. +WASM_IMPL_VEC_BASE(val, ) +void wasm_val_vec_new(own wasm_val_vec_t* vec, + size_t size, + own wasm_val_t const src[]) { + TRACE0(); + wasm_val_vec_new_uninitialized(vec, size); + for (size_t i = 0; i < size; ++i) { + vec->data[i] = src[i]; + } +} + +void wasm_val_vec_copy(own wasm_val_vec_t* out, const wasm_val_vec_t* vec) { + TRACE("%" PRIzx, vec->size); + wasm_val_vec_new_uninitialized(out, vec->size); + for (size_t i = 0; i < vec->size; ++i) { + wasm_val_copy(&out->data[i], &vec->data[i]); + } +} + +void wasm_val_vec_delete(own wasm_val_vec_t* vec) { + TRACE0(); + for (size_t i = 0; i < vec->size; ++i) { + wasm_val_delete(&vec->data[i]); + } + delete[] vec->data; + vec->size = 0; +} + +#define WASM_IMPL_VEC_OWN(name) \ + WASM_IMPL_VEC_BASE(name, *) \ + void wasm_##name##_vec_new(own wasm_##name##_vec_t* vec, size_t size, \ + own wasm_##name##_t* const src[]) { \ + TRACE0(); \ + wasm_##name##_vec_new_uninitialized(vec, size); \ + for (size_t i = 0; i < size; ++i) { \ + vec->data[i] = src[i]; \ + } \ } \ - void wasm_##name##_vec_copy(wasm_##name##_vec_t* out, \ + void wasm_##name##_vec_copy(own wasm_##name##_vec_t* out, \ const wasm_##name##_vec_t* vec) { \ - TRACE("wasm_" #name "_vec_copy %" PRIzx "\n", vec->size); \ + TRACE("%" PRIzx, vec->size); \ wasm_##name##_vec_new_uninitialized(out, vec->size); \ - memcpy(out->data, vec->data, \ - vec->size * sizeof(wasm_##name##_t ptr_or_none)); \ + for (size_t i = 0; i < vec->size; ++i) { \ + out->data[i] = wasm_##name##_copy(vec->data[i]); \ + } \ } \ void wasm_##name##_vec_delete(wasm_##name##_vec_t* vec) { \ - TRACE("wasm_" #name "_vec_delete\n"); \ - if (vec->data) { \ - delete vec->data; \ + TRACE0(); \ + for (size_t i = 0; i < vec->size; ++i) { \ + delete vec->data[i]; \ } \ + delete[] vec->data; \ vec->size = 0; \ } -WASM_IMPL_VEC(byte, ); -WASM_IMPL_VEC(val, ); -WASM_IMPL_VEC(frame, *); -WASM_IMPL_VEC(extern, *); +WASM_IMPL_VEC_OWN(frame); +WASM_IMPL_VEC_OWN(extern); -#define WASM_IMPL_TYPE(name) \ - WASM_IMPL_OWN(name) \ - WASM_IMPL_VEC(name, *) \ - wasm_##name##_t* wasm_##name##_copy(wasm_##name##_t* other) { \ - return new wasm_##name##_t(*other); \ +#define WASM_IMPL_TYPE(name) \ + WASM_IMPL_OWN(name) \ + WASM_IMPL_VEC_OWN(name) \ + own wasm_##name##_t* wasm_##name##_copy(wasm_##name##_t* other) { \ + TRACE0(); \ + return new wasm_##name##_t(*other); \ } WASM_IMPL_TYPE(valtype); -WASM_IMPL_TYPE(functype); -WASM_IMPL_TYPE(globaltype); -WASM_IMPL_TYPE(tabletype); -WASM_IMPL_TYPE(memorytype); -WASM_IMPL_TYPE(externtype); WASM_IMPL_TYPE(importtype); WASM_IMPL_TYPE(exporttype); -#define WASM_IMPL_REF_BASE(name) \ - WASM_IMPL_OWN(name) \ - wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t* ref) { \ - TRACE("wasm_" #name "_copy\n"); \ - return static_cast<wasm_##name##_t*>(ref->Copy()); \ - } \ - bool wasm_##name##_same(const wasm_##name##_t* ref, \ - const wasm_##name##_t* other) { \ - TRACE("wasm_" #name "_same\n"); \ - return ref->Same(*other); \ - } \ - void* wasm_##name##_get_host_info(const wasm_##name##_t* ref) { \ - return ref->host_info; \ - } \ - void wasm_##name##_set_host_info(wasm_##name##_t* ref, void* info) { \ - ref->host_info = info; \ - } \ - void wasm_##name##_set_host_info_with_finalizer( \ - wasm_##name##_t* ref, void* info, void (*finalizer)(void*)) { \ - ref->host_info = info; \ - ref->finalizer = finalizer; \ +#define WASM_IMPL_TYPE_CLONE(name) \ + WASM_IMPL_OWN(name) \ + WASM_IMPL_VEC_OWN(name) \ + own wasm_##name##_t* wasm_##name##_copy(wasm_##name##_t* other) { \ + TRACE0(); \ + return static_cast<wasm_##name##_t*>(other->Clone().release()); \ + } + +WASM_IMPL_TYPE_CLONE(functype); +WASM_IMPL_TYPE_CLONE(globaltype); +WASM_IMPL_TYPE_CLONE(tabletype); +WASM_IMPL_TYPE_CLONE(memorytype); +WASM_IMPL_TYPE_CLONE(externtype); + +#define WASM_IMPL_REF_BASE(name) \ + WASM_IMPL_OWN(name) \ + own wasm_##name##_t* wasm_##name##_copy(const wasm_##name##_t* ref) { \ + TRACE0(); \ + return new wasm_##name##_t(*ref); \ + } \ + bool wasm_##name##_same(const wasm_##name##_t* ref, \ + const wasm_##name##_t* other) { \ + TRACE0(); \ + return ref->I == other->I; \ + } \ + void* wasm_##name##_get_host_info(const wasm_##name##_t* ref) { \ + return ref->I->host_info(); \ + } \ + void wasm_##name##_set_host_info(wasm_##name##_t* ref, void* info) { \ + ref->I->set_host_info(info); \ + } \ + void wasm_##name##_set_host_info_with_finalizer( \ + wasm_##name##_t* ref, void* info, void (*finalizer)(void*)) { \ + ref->I->set_host_info(info); \ + ref->I->set_finalizer([=](Object* o) { finalizer(o->host_info()); }); \ } #define WASM_IMPL_REF(name) \ @@ -1383,17 +1192,17 @@ WASM_IMPL_REF(memory); WASM_IMPL_REF(table); WASM_IMPL_REF(trap); -#define WASM_IMPL_SHARABLE_REF(name) \ - WASM_IMPL_REF(name) \ - WASM_IMPL_OWN(shared_##name) \ - wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t* t) { \ - return static_cast<wasm_shared_##name##_t*>( \ - const_cast<wasm_##name##_t*>(t)); \ - } \ - wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, \ - const wasm_shared_##name##_t* t) { \ - return static_cast<wasm_##name##_t*>( \ - const_cast<wasm_shared_##name##_t*>(t)); \ +#define WASM_IMPL_SHARABLE_REF(name) \ + WASM_IMPL_REF(name) \ + WASM_IMPL_OWN(shared_##name) \ + own wasm_shared_##name##_t* wasm_##name##_share(const wasm_##name##_t* t) { \ + return static_cast<wasm_shared_##name##_t*>( \ + const_cast<wasm_##name##_t*>(t)); \ + } \ + own wasm_##name##_t* wasm_##name##_obtain(wasm_store_t*, \ + const wasm_shared_##name##_t* t) { \ + return static_cast<wasm_##name##_t*>( \ + const_cast<wasm_shared_##name##_t*>(t)); \ } WASM_IMPL_SHARABLE_REF(module) diff --git a/src/interp/interp.cc b/src/interp/interp.cc index cf871312..ad4e00fa 100644 --- a/src/interp/interp.cc +++ b/src/interp/interp.cc @@ -1,5 +1,5 @@ /* - * Copyright 2016 WebAssembly Community Group participants + * Copyright 2020 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,3851 +19,2211 @@ #include <algorithm> #include <cassert> #include <cinttypes> -#include <cmath> -#include <limits> -#include <type_traits> -#include <vector> -#include "src/interp/interp-internal.h" - -#include "src/cast.h" -#include "src/stream.h" -#include "src/type-checker.h" +#include "src/interp/interp-math.h" +#include "src/make-unique.h" namespace wabt { namespace interp { -// Differs from the normal CHECK_RESULT because this one is meant to return the -// interp Result type. -#undef CHECK_RESULT -#define CHECK_RESULT(expr) \ - do { \ - if (WABT_FAILED(expr)) { \ - return ResultType::Error; \ - } \ - } while (0) - -// Differs from CHECK_RESULT since it can return different traps, not just -// Error. Also uses __VA_ARGS__ so templates can be passed without surrounding -// parentheses. -#define CHECK_TRAP(...) \ - do { \ - Result result = (__VA_ARGS__); \ - if (!result.ok()) { \ - return result; \ - } \ - } while (0) - -std::string RefTypeToString(RefType t) { - switch (t) { - case RefType::Null: - return "null"; - case RefType::Func: - return "func"; - case RefType::Host: - return "host"; - } - WABT_UNREACHABLE; -} - -std::string TypedValueToString(const TypedValue& tv) { - switch (tv.type) { - case Type::I32: - return StringPrintf("i32:%u", tv.get_i32()); - - case Type::I64: - return StringPrintf("i64:%" PRIu64, tv.get_i64()); - - case Type::F32: - return StringPrintf("f32:%f", tv.get_f32()); - - case Type::F64: - return StringPrintf("f64:%f", tv.get_f64()); - - case Type::V128: - return StringPrintf("v128 i32x4:0x%08x 0x%08x 0x%08x 0x%08x", - tv.value.vec128.v[0], tv.value.vec128.v[1], - tv.value.vec128.v[2], tv.value.vec128.v[3]); - - case Type::Nullref: - return StringPrintf("nullref"); - - case Type::Hostref: - return StringPrintf("hostref:%" PRIindex, tv.get_ref().index); - - case Type::Funcref: - return StringPrintf("funcref:%" PRIindex, tv.get_ref().index); - - case Type::Exnref: - return StringPrintf("exnref:%" PRIindex, tv.get_ref().index); - - case Type::Func: - case Type::Void: - case Type::Any: - case Type::Anyref: - case Type::I8: - case Type::I8U: - case Type::I16: - case Type::I16U: - case Type::I32U: - // These types are not concrete types and should never exist as a value - WABT_UNREACHABLE; - } - WABT_UNREACHABLE; -} - -void WriteTypedValue(Stream* stream, const TypedValue& tv) { - std::string s = TypedValueToString(tv); - stream->WriteData(s.data(), s.size()); +const char* GetName(Mutability mut) { + static const char* kNames[] = {"immutable", "mutable"}; + return kNames[int(mut)]; } -void WriteTypedValues(Stream* stream, const TypedValues& values) { - for (size_t i = 0; i < values.size(); ++i) { - WriteTypedValue(stream, values[i]); - if (i != values.size() - 1) { - stream->Writef(", "); - } - } +const char* GetName(ValueType type) { + return GetTypeName(type); } -#define V(name, str) str, -static const char* s_trap_strings[] = {FOREACH_INTERP_RESULT(V)}; -#undef V - -const char* ResultTypeToString(ResultType result) { - return s_trap_strings[static_cast<size_t>(result)]; +const char* GetName(ExternKind kind) { + return GetKindName(kind); } -std::string ResultToString(Result result) { - std::string rtn = ResultTypeToString(result.type); - if (!result.message.empty()) { - rtn += ": "; - rtn += result.message; - } - return rtn; +const char* GetName(ObjectKind kind) { + static const char* kNames[] = { + "Null", "Foreign", "Trap", "DefinedFunc", "HostFunc", "Table", + "Memory", "Global", "Event", "Module", "Instance", "Thread", + }; + return kNames[int(kind)]; } -void WriteResult(Stream* stream, const char* desc, Result result) { - stream->Writef("%s: %s\n", desc, ResultToString(result).c_str()); -} +//// Refs //// +// static +const Ref Ref::Null{0}; -void WriteCall(Stream* stream, - string_view module_name, - string_view func_name, - const TypedValues& args, - const TypedValues& results, - Result result) { - if (!module_name.empty()) { - stream->Writef(PRIstringview ".", WABT_PRINTF_STRING_VIEW_ARG(module_name)); +//// Limits //// +Result Match(const Limits& expected, + const Limits& actual, + std::string* out_msg) { + if (actual.initial < expected.initial) { + *out_msg = StringPrintf("actual size (%" PRIu64 + ") smaller than declared (%" PRIu64 ")", + actual.initial, expected.initial); + return Result::Error; } - stream->Writef(PRIstringview "(", WABT_PRINTF_STRING_VIEW_ARG(func_name)); - WriteTypedValues(stream, args); - stream->Writef(") =>"); - if (result.ok()) { - if (results.size() > 0) { - stream->Writef(" "); - WriteTypedValues(stream, results); + + if (expected.has_max) { + if (!actual.has_max) { + *out_msg = StringPrintf( + "max size (unspecified) larger than declared (%" PRIu64 ")", + expected.max); + return Result::Error; + } else if (actual.max > expected.max) { + *out_msg = StringPrintf("max size (%" PRIu64 + ") larger than declared (%" PRIu64 ")", + actual.max, expected.max); + return Result::Error; } - stream->Writef("\n"); - } else { - WriteResult(stream, " error", result); } -} -Environment::Environment(const Features& features) - : features_(features), istream_(new OutputBuffer()) {} - -Index Environment::FindModuleIndex(string_view name) const { - auto iter = module_bindings_.find(name.to_string()); - if (iter == module_bindings_.end()) { - return kInvalidIndex; - } - return iter->second.index; + return Result::Ok; } -Module* Environment::FindModule(string_view name) { - Index index = FindModuleIndex(name); - return index == kInvalidIndex ? nullptr : modules_[index].get(); +//// FuncType //// +std::unique_ptr<ExternType> FuncType::Clone() const { + return MakeUnique<FuncType>(*this); } -Module* Environment::FindRegisteredModule(string_view name) { - bool retry = false; - while (true) { - auto iter = registered_module_bindings_.find(name.to_string()); - if (iter != registered_module_bindings_.end()) { - return modules_[iter->second.index].get(); - } - - if (retry) { - // If you return true from on_unknown_module, you must add the module - // using AppendHostModule(). - assert(false); - break; - } - - if (on_unknown_module && on_unknown_module(this, name)) { - retry = true; - continue; - } - break; - } - return nullptr; -} - -Thread::Options::Options(uint32_t value_stack_size, uint32_t call_stack_size) - : value_stack_size(value_stack_size), call_stack_size(call_stack_size) {} - -Thread::Thread(Environment* env, const Options& options) - : env_(env), - value_stack_(options.value_stack_size), - call_stack_(options.call_stack_size) {} - -FuncSignature::FuncSignature(std::vector<Type> param_types, - std::vector<Type> result_types) - : param_types(param_types), result_types(result_types) {} - -FuncSignature::FuncSignature(Index param_count, - Type* param_types, - Index result_count, - Type* result_types) - : param_types(param_types, param_types + param_count), - result_types(result_types, result_types + result_count) {} - -Module::Module(Environment* env, bool is_host) - : memory_index(kInvalidIndex), - is_host(is_host), - env(env) {} - -Module::Module(Environment* env, string_view name, bool is_host) - : name(name.to_string()), - memory_index(kInvalidIndex), - is_host(is_host), - env(env) {} - -Export* Module::GetFuncExport(Environment* env, - string_view name, - Index sig_index) { - auto range = export_bindings.equal_range(name.to_string()); - for (auto iter = range.first; iter != range.second; ++iter) { - const Binding& binding = iter->second; - Export* export_ = &exports[binding.index]; - if (export_->kind == ExternalKind::Func) { - const Func* func = env->GetFunc(export_->index); - if (env->FuncSignaturesAreEqual(sig_index, func->sig_index)) { - return export_; - } +Result Match(const FuncType& expected, + const FuncType& actual, + std::string* out_msg) { + if (expected.params != actual.params || expected.results != actual.results) { + if (out_msg) { + *out_msg = "import signature mismatch"; } + return Result::Error; } + return Result::Ok; +} - // No match; check whether the module wants to spontaneously create a - // function of this name and signature. - Index index = OnUnknownFuncExport(name, sig_index); - if (index != kInvalidIndex) { - Export* export_ = &exports[index]; - assert(export_->kind == ExternalKind::Func); - const Func* func = env->GetFunc(export_->index); - WABT_USE(func); - assert(env->FuncSignaturesAreEqual(sig_index, func->sig_index)); - return export_; - } - - return nullptr; +//// TableType //// +std::unique_ptr<ExternType> TableType::Clone() const { + return MakeUnique<TableType>(*this); } -Export* Module::GetExport(string_view name) { - int field_index = export_bindings.FindIndex(name); - if (field_index < 0) { - return nullptr; +Result Match(const TableType& expected, + const TableType& actual, + std::string* out_msg) { + if (expected.element != actual.element) { + *out_msg = + StringPrintf("type mismatch in imported table, expected %s but got %s.", + GetName(expected.element), GetName(actual.element)); + return Result::Error; } - return &exports[field_index]; -} -Index Module::AppendExport(ExternalKind kind, - Index item_index, - string_view name) { - exports.emplace_back(name, kind, item_index); - Export* export_ = &exports.back(); - export_bindings.emplace(export_->name, Binding(exports.size() - 1)); - return exports.size() - 1; -} - -DefinedModule::DefinedModule(Environment* env) - : Module(env, false), - start_func_index(kInvalidIndex), - istream_start(kInvalidIstreamOffset), - istream_end(kInvalidIstreamOffset) {} - -HostModule::HostModule(Environment* env, string_view name) - : Module(env, name, true) {} - -Index HostModule::OnUnknownFuncExport(string_view name, Index sig_index) { - if (on_unknown_func_export) { - return on_unknown_func_export(env, this, name, sig_index); - } - return kInvalidIndex; -} - -std::pair<HostFunc*, Index> HostModule::AppendFuncExport( - string_view name, - const FuncSignature& sig, - HostFunc::Callback callback) { - // TODO(binji): dedupe signature? - env->EmplaceBackFuncSignature(sig); - Index sig_index = env->GetFuncSignatureCount() - 1; - return AppendFuncExport(name, sig_index, callback); -} - -std::pair<HostFunc*, Index> HostModule::AppendFuncExport( - string_view name, - Index sig_index, - HostFunc::Callback callback) { - auto* host_func = new HostFunc(this->name, name, sig_index, callback); - env->EmplaceBackFunc(host_func); - Index func_env_index = env->GetFuncCount() - 1; - Index export_index = AppendExport(ExternalKind::Func, func_env_index, name); - return {host_func, export_index}; -} - -std::pair<Table*, Index> HostModule::AppendTableExport(string_view name, - Type elem_type, - const Limits& limits) { - Table* table = env->EmplaceBackTable(elem_type, limits); - Index table_env_index = env->GetTableCount() - 1; - Index export_index = AppendExport(ExternalKind::Table, table_env_index, name); - return {table, export_index}; -} - -std::pair<Memory*, Index> HostModule::AppendMemoryExport(string_view name, - const Limits& limits) { - Memory* memory = env->EmplaceBackMemory(limits); - Index memory_env_index = env->GetMemoryCount() - 1; - Index export_index = - AppendExport(ExternalKind::Memory, memory_env_index, name); - return {memory, export_index}; -} - -std::pair<Global*, Index> HostModule::AppendGlobalExport(string_view name, - Type type, - bool mutable_) { - Global* global = env->EmplaceBackGlobal(type, mutable_); - Index global_env_index = env->GetGlobalCount() - 1; - Index export_index = - AppendExport(ExternalKind::Global, global_env_index, name); - return {global, export_index}; -} - -std::pair<Global*, Index> HostModule::AppendGlobalExport(string_view name, - bool mutable_, - uint32_t value) { - std::pair<Global*, Index> pair = - AppendGlobalExport(name, Type::I32, mutable_); - pair.first->typed_value.set_i32(value); - return pair; -} - -std::pair<Global*, Index> HostModule::AppendGlobalExport(string_view name, - bool mutable_, - uint64_t value) { - std::pair<Global*, Index> pair = - AppendGlobalExport(name, Type::I64, mutable_); - pair.first->typed_value.set_i64(value); - return pair; -} - -std::pair<Global*, Index> HostModule::AppendGlobalExport(string_view name, - bool mutable_, - float value) { - std::pair<Global*, Index> pair = - AppendGlobalExport(name, Type::F32, mutable_); - pair.first->typed_value.set_f32(value); - return pair; -} - -std::pair<Global*, Index> HostModule::AppendGlobalExport(string_view name, - bool mutable_, - double value) { - std::pair<Global*, Index> pair = - AppendGlobalExport(name, Type::F64, mutable_); - pair.first->typed_value.set_f64(value); - return pair; -} - -Environment::MarkPoint Environment::Mark() { - MarkPoint mark; - mark.modules_size = modules_.size(); - mark.sigs_size = sigs_.size(); - mark.funcs_size = funcs_.size(); - mark.memories_size = memories_.size(); - mark.tables_size = tables_.size(); - mark.globals_size = globals_.size(); - mark.data_segments_size = data_segments_.size(); - mark.elem_segments_size = elem_segments_.size(); - mark.istream_size = istream_->data.size(); - return mark; -} - -void Environment::ResetToMarkPoint(const MarkPoint& mark) { - // Destroy entries in the binding hash. - for (size_t i = mark.modules_size; i < modules_.size(); ++i) { - std::string name = modules_[i]->name; - if (!name.empty()) { - module_bindings_.erase(name); - } + if (Failed(Match(expected.limits, actual.limits, out_msg))) { + return Result::Error; } - // registered_module_bindings_ maps from an arbitrary name to a module index, - // so we have to iterate through the entire table to find entries to remove. - auto iter = registered_module_bindings_.begin(); - while (iter != registered_module_bindings_.end()) { - if (iter->second.index >= mark.modules_size) { - iter = registered_module_bindings_.erase(iter); - } else { - ++iter; - } - } - - modules_.erase(modules_.begin() + mark.modules_size, modules_.end()); - sigs_.erase(sigs_.begin() + mark.sigs_size, sigs_.end()); - funcs_.erase(funcs_.begin() + mark.funcs_size, funcs_.end()); - memories_.erase(memories_.begin() + mark.memories_size, memories_.end()); - tables_.erase(tables_.begin() + mark.tables_size, tables_.end()); - globals_.erase(globals_.begin() + mark.globals_size, globals_.end()); - data_segments_.erase(data_segments_.begin() + mark.data_segments_size, - data_segments_.end()); - elem_segments_.erase(elem_segments_.begin() + mark.elem_segments_size, - elem_segments_.end()); - istream_->data.resize(mark.istream_size); -} - -HostModule* Environment::AppendHostModule(string_view name) { - HostModule* module = new HostModule(this, name); - modules_.emplace_back(module); - registered_module_bindings_.emplace(name.to_string(), - Binding(modules_.size() - 1)); - return module; -} - -uint32_t ToRep(bool x) { return x ? 1 : 0; } -uint32_t ToRep(uint32_t x) { return x; } -uint64_t ToRep(uint64_t x) { return x; } -uint32_t ToRep(int32_t x) { return Bitcast<uint32_t>(x); } -uint64_t ToRep(int64_t x) { return Bitcast<uint64_t>(x); } -uint32_t ToRep(float x) { return Bitcast<uint32_t>(x); } -uint64_t ToRep(double x) { return Bitcast<uint64_t>(x); } -v128 ToRep(v128 x) { return x; } -Ref ToRep(Ref x) { return x; } - -template <typename Dst, typename Src> -Dst FromRep(Src x); - -template <> -uint32_t FromRep<uint32_t>(uint32_t x) { return x; } -template <> -uint64_t FromRep<uint64_t>(uint64_t x) { return x; } -template <> -int32_t FromRep<int32_t>(uint32_t x) { return Bitcast<int32_t>(x); } -template <> -int64_t FromRep<int64_t>(uint64_t x) { return Bitcast<int64_t>(x); } -template <> -float FromRep<float>(uint32_t x) { return Bitcast<float>(x); } -template <> -double FromRep<double>(uint64_t x) { return Bitcast<double>(x); } -template <> -v128 FromRep<v128>(v128 x) { return x; } -template <> -Ref FromRep<Ref>(Ref x) { return x; } - -template <typename T> -struct FloatTraits; - -template <typename R, typename T> -bool IsConversionInRange(ValueTypeRep<T> bits); - -/* 3 32222222 222...00 - * 1 09876543 210...10 - * ------------------- - * 0 00000000 000...00 => 0x00000000 => 0 - * 0 10011101 111...11 => 0x4effffff => 2147483520 (~INT32_MAX) - * 0 10011110 000...00 => 0x4f000000 => 2147483648 - * 0 10011110 111...11 => 0x4f7fffff => 4294967040 (~UINT32_MAX) - * 0 10111110 111...11 => 0x5effffff => 9223371487098961920 (~INT64_MAX) - * 0 10111110 000...00 => 0x5f000000 => 9223372036854775808 - * 0 10111111 111...11 => 0x5f7fffff => 18446742974197923840 (~UINT64_MAX) - * 0 10111111 000...00 => 0x5f800000 => 18446744073709551616 - * 0 11111111 000...00 => 0x7f800000 => inf - * 0 11111111 000...01 => 0x7f800001 => nan(0x1) - * 0 11111111 111...11 => 0x7fffffff => nan(0x7fffff) - * 1 00000000 000...00 => 0x80000000 => -0 - * 1 01111110 111...11 => 0xbf7fffff => -1 + ulp (~UINT32_MIN, ~UINT64_MIN) - * 1 01111111 000...00 => 0xbf800000 => -1 - * 1 10011110 000...00 => 0xcf000000 => -2147483648 (INT32_MIN) - * 1 10111110 000...00 => 0xdf000000 => -9223372036854775808 (INT64_MIN) - * 1 11111111 000...00 => 0xff800000 => -inf - * 1 11111111 000...01 => 0xff800001 => -nan(0x1) - * 1 11111111 111...11 => 0xffffffff => -nan(0x7fffff) - */ - -template <> -struct FloatTraits<float> { - static const uint32_t kMax = 0x7f7fffffU; - static const uint32_t kInf = 0x7f800000U; - static const uint32_t kNegMax = 0xff7fffffU; - static const uint32_t kNegInf = 0xff800000U; - static const uint32_t kNegOne = 0xbf800000U; - static const uint32_t kNegZero = 0x80000000U; - static const uint32_t kQuietNan = 0x7fc00000U; - static const uint32_t kQuietNegNan = 0xffc00000U; - static const int kSigBits = 23; - static const uint32_t kSigMask = 0x7fffff; - static const uint32_t kSignMask = 0x80000000U; + return Result::Ok; +} - static bool IsNan(uint32_t bits) { - return (bits > kInf && bits < kNegZero) || (bits > kNegInf); - } +//// MemoryType //// +std::unique_ptr<ExternType> MemoryType::Clone() const { + return MakeUnique<MemoryType>(*this); +} - static bool IsZero(uint32_t bits) { return bits == 0 || bits == kNegZero; } +Result Match(const MemoryType& expected, + const MemoryType& actual, + std::string* out_msg) { + return Match(expected.limits, actual.limits, out_msg); +} - static bool IsCanonicalNan(uint32_t bits) { - return bits == kQuietNan || bits == kQuietNegNan; - } +//// GlobalType //// +std::unique_ptr<ExternType> GlobalType::Clone() const { + return MakeUnique<GlobalType>(*this); +} - static bool IsArithmeticNan(uint32_t bits) { - return (bits & kQuietNan) == kQuietNan; +Result Match(const GlobalType& expected, + const GlobalType& actual, + std::string* out_msg) { + if (actual.mut != expected.mut) { + *out_msg = StringPrintf( + "mutability mismatch in imported global, expected %s but got %s.", + GetName(actual.mut), GetName(expected.mut)); + return Result::Error; } - static uint32_t CanonicalizeNan(uint32_t bits) { - return WABT_UNLIKELY(IsNan(bits)) ? kQuietNan : bits; + if (actual.type != expected.type && + (expected.mut == Mutability::Var || + !TypesMatch(expected.type, actual.type))) { + *out_msg = StringPrintf( + "type mismatch in imported global, expected %s but got %s.", + GetName(expected.type), GetName(actual.type)); + return Result::Error; } -}; -bool IsCanonicalNan(uint32_t bits) { - return FloatTraits<float>::IsCanonicalNan(bits); + return Result::Ok; } -bool IsArithmeticNan(uint32_t bits) { - return FloatTraits<float>::IsArithmeticNan(bits); +//// EventType //// +std::unique_ptr<ExternType> EventType::Clone() const { + return MakeUnique<EventType>(*this); } -template <> -bool IsConversionInRange<int32_t, float>(uint32_t bits) { - return (bits < 0x4f000000U) || - (bits >= FloatTraits<float>::kNegZero && bits <= 0xcf000000U); +Result Match(const EventType& expected, + const EventType& actual, + std::string* out_msg) { + // TODO signature + return Result::Ok; } -template <> -bool IsConversionInRange<int64_t, float>(uint32_t bits) { - return (bits < 0x5f000000U) || - (bits >= FloatTraits<float>::kNegZero && bits <= 0xdf000000U); +//// Limits //// +bool CanGrow(const Limits& limits, u32 old_size, u32 delta, u32* new_size) { + if (limits.max >= delta && old_size <= limits.max - delta) { + *new_size = old_size + delta; + return true; + } + return false; } -template <> -bool IsConversionInRange<uint32_t, float>(uint32_t bits) { - return (bits < 0x4f800000U) || (bits >= FloatTraits<float>::kNegZero && - bits < FloatTraits<float>::kNegOne); -} +//// FuncDesc //// -template <> -bool IsConversionInRange<uint64_t, float>(uint32_t bits) { - return (bits < 0x5f800000U) || (bits >= FloatTraits<float>::kNegZero && - bits < FloatTraits<float>::kNegOne); -} +ValueType FuncDesc::GetLocalType(Index index) const { + if (index < type.params.size()) { + return type.params[index]; + } + index -= type.params.size(); -/* - * 6 66655555555 5544..2..222221...000 - * 3 21098765432 1098..9..432109...210 - * ----------------------------------- - * 0 00000000000 0000..0..000000...000 0x0000000000000000 => 0 - * 0 10000011101 1111..1..111000...000 0x41dfffffffc00000 => 2147483647 (INT32_MAX) - * 0 10000011110 1111..1..111100...000 0x41efffffffe00000 => 4294967295 (UINT32_MAX) - * 0 10000111101 1111..1..111111...111 0x43dfffffffffffff => 9223372036854774784 (~INT64_MAX) - * 0 10000111110 0000..0..000000...000 0x43e0000000000000 => 9223372036854775808 - * 0 10000111110 1111..1..111111...111 0x43efffffffffffff => 18446744073709549568 (~UINT64_MAX) - * 0 10000111111 0000..0..000000...000 0x43f0000000000000 => 18446744073709551616 - * 0 10001111110 1111..1..000000...000 0x47efffffe0000000 => 3.402823e+38 (FLT_MAX) - * 0 11111111111 0000..0..000000...000 0x7ff0000000000000 => inf - * 0 11111111111 0000..0..000000...001 0x7ff0000000000001 => nan(0x1) - * 0 11111111111 1111..1..111111...111 0x7fffffffffffffff => nan(0xfff...) - * 1 00000000000 0000..0..000000...000 0x8000000000000000 => -0 - * 1 01111111110 1111..1..111111...111 0xbfefffffffffffff => -1 + ulp (~UINT32_MIN, ~UINT64_MIN) - * 1 01111111111 0000..0..000000...000 0xbff0000000000000 => -1 - * 1 10000011110 0000..0..000000...000 0xc1e0000000000000 => -2147483648 (INT32_MIN) - * 1 10000111110 0000..0..000000...000 0xc3e0000000000000 => -9223372036854775808 (INT64_MIN) - * 1 10001111110 1111..1..000000...000 0xc7efffffe0000000 => -3.402823e+38 (-FLT_MAX) - * 1 11111111111 0000..0..000000...000 0xfff0000000000000 => -inf - * 1 11111111111 0000..0..000000...001 0xfff0000000000001 => -nan(0x1) - * 1 11111111111 1111..1..111111...111 0xffffffffffffffff => -nan(0xfff...) - */ + auto iter = std::lower_bound( + locals.begin(), locals.end(), index + 1, + [](const LocalDesc& lhs, Index rhs) { return lhs.end < rhs; }); + assert(iter != locals.end()); + return iter->type; +} -template <> -struct FloatTraits<double> { - static const uint64_t kInf = 0x7ff0000000000000ULL; - static const uint64_t kNegInf = 0xfff0000000000000ULL; - static const uint64_t kNegOne = 0xbff0000000000000ULL; - static const uint64_t kNegZero = 0x8000000000000000ULL; - static const uint64_t kQuietNan = 0x7ff8000000000000ULL; - static const uint64_t kQuietNegNan = 0xfff8000000000000ULL; - static const int kSigBits = 52; - static const uint64_t kSigMask = 0xfffffffffffffULL; - static const uint64_t kSignMask = 0x8000000000000000ULL; +//// Store //// +Store::Store(const Features& features) : features_(features) { + Ref ref{objects_.New(new Object(ObjectKind::Null))}; + assert(ref == Ref::Null); + roots_.New(ref); +} - static bool IsNan(uint64_t bits) { - return (bits > kInf && bits < kNegZero) || (bits > kNegInf); +bool Store::HasValueType(Ref ref, ValueType type) const { + // TODO opt? + if (!IsValid(ref)) { + return false; } - - static bool IsZero(uint64_t bits) { return bits == 0 || bits == kNegZero; } - - static bool IsCanonicalNan(uint64_t bits) { - return bits == kQuietNan || bits == kQuietNegNan; + if (type == ValueType::Anyref) { + return true; } - - static bool IsArithmeticNan(uint64_t bits) { - return (bits & kQuietNan) == kQuietNan; + if (ref == Ref::Null) { + return true; } - static uint64_t CanonicalizeNan(uint64_t bits) { - return WABT_UNLIKELY(IsNan(bits)) ? kQuietNan : bits; + Object* obj = objects_.Get(ref.index).get(); + switch (type) { + case ValueType::Funcref: + return obj->kind() == ObjectKind::DefinedFunc || + obj->kind() == ObjectKind::HostFunc; + case ValueType::Nullref: + return ref.index == 0; + case ValueType::Exnref: // TODO + return false; + default: + return false; } -}; - -bool IsCanonicalNan(uint64_t bits) { - return FloatTraits<double>::IsCanonicalNan(bits); } -bool IsArithmeticNan(uint64_t bits) { - return FloatTraits<double>::IsArithmeticNan(bits); +ValueType Store::GetValueType(Ref ref) const { + Object* obj = objects_.Get(ref.index).get(); + switch (obj->kind()) { + case ObjectKind::Null: return ValueType::Nullref; + case ObjectKind::Foreign: return ValueType::Hostref; + case ObjectKind::DefinedFunc: + case ObjectKind::HostFunc: return ValueType::Funcref; + default: return ValueType::Anyref; + } } -template <> -bool IsConversionInRange<int32_t, double>(uint64_t bits) { - return (bits <= 0x41dfffffffc00000ULL) || - (bits >= FloatTraits<double>::kNegZero && - bits <= 0xc1e0000000000000ULL); +Store::RootList::Index Store::NewRoot(Ref ref) { + return roots_.New(ref); } -template <> -bool IsConversionInRange<int64_t, double>(uint64_t bits) { - return (bits < 0x43e0000000000000ULL) || - (bits >= FloatTraits<double>::kNegZero && - bits <= 0xc3e0000000000000ULL); +Store::RootList::Index Store::CopyRoot(RootList::Index index) { + // roots_.Get() returns a reference to an element in an internal vector, and + // roots_.New() might forward its arguments to emplace_back on the same + // vector. This seems to "work" in most environments, but fails on Visual + // Studio 2015 Win64. Copying it to a value fixes the issue. + auto obj_index = roots_.Get(index); + return roots_.New(obj_index); } -template <> -bool IsConversionInRange<uint32_t, double>(uint64_t bits) { - return (bits <= 0x41efffffffe00000ULL) || - (bits >= FloatTraits<double>::kNegZero && - bits < FloatTraits<double>::kNegOne); +void Store::DeleteRoot(RootList::Index index) { + roots_.Delete(index); } -template <> -bool IsConversionInRange<uint64_t, double>(uint64_t bits) { - return (bits < 0x43f0000000000000ULL) || - (bits >= FloatTraits<double>::kNegZero && - bits < FloatTraits<double>::kNegOne); +void Store::Collect() { + std::fill(marks_.begin(), marks_.end(), false); + Mark(roots_.list); + for (size_t i = 0; i < marks_.size(); ++i) { + if (!marks_[i]) { + objects_.Delete(i); + } + } } -template <> -bool IsConversionInRange<float, double>(uint64_t bits) { - return (bits <= 0x47efffffe0000000ULL) || - (bits >= FloatTraits<double>::kNegZero && - bits <= 0xc7efffffe0000000ULL); -} - -// The WebAssembly rounding mode means that these values (which are > F32_MAX) -// should be rounded to F32_MAX and not set to infinity. Unfortunately, UBSAN -// complains that the value is not representable as a float, so we'll special -// case them. -bool IsInRangeF64DemoteF32RoundToF32Max(uint64_t bits) { - return bits > 0x47efffffe0000000ULL && bits < 0x47effffff0000000ULL; -} - -bool IsInRangeF64DemoteF32RoundToNegF32Max(uint64_t bits) { - return bits > 0xc7efffffe0000000ULL && bits < 0xc7effffff0000000ULL; -} - -template <typename T, typename MemType> struct ExtendMemType; -template<> struct ExtendMemType<uint32_t, uint8_t> { typedef uint32_t type; }; -template<> struct ExtendMemType<uint32_t, int8_t> { typedef int32_t type; }; -template<> struct ExtendMemType<uint32_t, uint16_t> { typedef uint32_t type; }; -template<> struct ExtendMemType<uint32_t, int16_t> { typedef int32_t type; }; -template<> struct ExtendMemType<uint32_t, uint32_t> { typedef uint32_t type; }; -template<> struct ExtendMemType<uint32_t, int32_t> { typedef int32_t type; }; -template<> struct ExtendMemType<uint64_t, uint8_t> { typedef uint64_t type; }; -template<> struct ExtendMemType<uint64_t, int8_t> { typedef int64_t type; }; -template<> struct ExtendMemType<uint64_t, uint16_t> { typedef uint64_t type; }; -template<> struct ExtendMemType<uint64_t, int16_t> { typedef int64_t type; }; -template<> struct ExtendMemType<uint64_t, uint32_t> { typedef uint64_t type; }; -template<> struct ExtendMemType<uint64_t, int32_t> { typedef int64_t type; }; -template<> struct ExtendMemType<uint64_t, uint64_t> { typedef uint64_t type; }; -template<> struct ExtendMemType<uint64_t, int64_t> { typedef int64_t type; }; -template<> struct ExtendMemType<float, float> { typedef float type; }; -template<> struct ExtendMemType<double, double> { typedef double type; }; -template<> struct ExtendMemType<v128, v128> { typedef v128 type; }; - -template <typename T, typename MemType> struct WrapMemType; -template<> struct WrapMemType<uint32_t, uint8_t> { typedef uint8_t type; }; -template<> struct WrapMemType<uint32_t, uint16_t> { typedef uint16_t type; }; -template<> struct WrapMemType<uint32_t, uint32_t> { typedef uint32_t type; }; -template<> struct WrapMemType<uint64_t, uint8_t> { typedef uint8_t type; }; -template<> struct WrapMemType<uint64_t, uint16_t> { typedef uint16_t type; }; -template<> struct WrapMemType<uint64_t, uint32_t> { typedef uint32_t type; }; -template<> struct WrapMemType<uint64_t, uint64_t> { typedef uint64_t type; }; -template<> struct WrapMemType<float, float> { typedef uint32_t type; }; -template<> struct WrapMemType<double, double> { typedef uint64_t type; }; -template<> struct WrapMemType<v128, v128> { typedef v128 type; }; - -template <typename T> -Value MakeValue(ValueTypeRep<T>); - -template <> -Value MakeValue<uint32_t>(uint32_t v) { - Value result; - result.i32 = v; - return result; +void Store::Mark(Ref ref) { + marks_[ref.index] = true; } -template <> -Value MakeValue<int32_t>(uint32_t v) { - Value result; - result.i32 = v; - return result; +void Store::Mark(const RefVec& refs) { + for (auto&& ref: refs) { + Mark(ref); + } } -template <> -Value MakeValue<uint64_t>(uint64_t v) { - Value result; - result.i64 = v; - return result; +//// Object //// +Object::~Object() { + if (finalizer_) { + finalizer_(this); + } } -template <> -Value MakeValue<int64_t>(uint64_t v) { - Value result; - result.i64 = v; - return result; -} +//// Foreign //// +Foreign::Foreign(Store&, void* ptr) : Object(skind), ptr_(ptr) {} -template <> -Value MakeValue<float>(uint32_t v) { - Value result; - result.f32_bits = v; - return result; -} +void Foreign::Mark(Store&) {} -template <> -Value MakeValue<double>(uint64_t v) { - Value result; - result.f64_bits = v; - return result; +//// Frame //// +void Frame::Mark(Store& store) { + store.Mark(func); } -template <> -Value MakeValue<v128>(v128 v) { - Value result; - result.vec128 = v; - return result; -} +//// Trap //// +Trap::Trap(Store& store, + const std::string& msg, + const std::vector<Frame>& trace) + : Object(skind), message_(msg), trace_(trace) {} -template <> -Value MakeValue<Ref>(Ref v) { - Value result; - result.ref = v; - return result; +void Trap::Mark(Store& store) { + for (auto&& frame : trace_) { + frame.Mark(store); + } } -template <typename T> ValueTypeRep<T> GetValue(Value); -template<> uint32_t GetValue<int32_t>(Value v) { return v.i32; } -template<> uint32_t GetValue<uint32_t>(Value v) { return v.i32; } -template<> uint64_t GetValue<int64_t>(Value v) { return v.i64; } -template<> uint64_t GetValue<uint64_t>(Value v) { return v.i64; } -template<> uint32_t GetValue<float>(Value v) { return v.f32_bits; } -template<> uint64_t GetValue<double>(Value v) { return v.f64_bits; } -template<> v128 GetValue<v128>(Value v) { return v.vec128; } -template<> Ref GetValue<Ref>(Value v) { return v.ref; } - +//// Extern //// template <typename T> -ValueTypeRep<T> CanonicalizeNan(ValueTypeRep<T> rep) { - return rep; -} - -template <> -ValueTypeRep<float> CanonicalizeNan<float>(ValueTypeRep<float> rep) { - return FloatTraits<float>::CanonicalizeNan(rep); -} - -template <> -ValueTypeRep<double> CanonicalizeNan<double>(ValueTypeRep<double> rep) { - return FloatTraits<double>::CanonicalizeNan(rep); -} - -#define TRAP(type) return ResultType::Trap##type -#define TRAP_UNLESS(cond, type) TRAP_IF(!(cond), type) -#define TRAP_IF(cond, type) \ - do { \ - if (WABT_UNLIKELY(cond)) { \ - TRAP(type); \ - } \ - } while (0) - -#define TRAP_MSG(type, ...) \ - return Result(ResultType::Trap##type, StringPrintf(__VA_ARGS__)) - -#define CHECK_STACK() \ - TRAP_IF(value_stack_top_ >= value_stack_.size(), ValueStackExhausted) - -#define PUSH_NEG_1_AND_BREAK_IF(cond) \ - if (WABT_UNLIKELY(cond)) { \ - CHECK_TRAP(Push<int32_t>(-1)); \ - break; \ +Result Extern::MatchImpl(Store& store, + const ImportType& import_type, + const T& actual, + Trap::Ptr* out_trap) { + const T* extern_type = dyn_cast<T>(import_type.type.get()); + if (!extern_type) { + *out_trap = Trap::New( + store, + StringPrintf("expected import \"%s.%s\" to have kind %s, not %s", + import_type.module.c_str(), import_type.name.c_str(), + GetName(import_type.type->kind), GetName(T::skind))); + return Result::Error; } -#define GOTO(offset) pc = &istream[offset] + std::string msg; + if (Failed(interp::Match(*extern_type, actual, &msg))) { + *out_trap = Trap::New(store, msg); + return Result::Error; + } -Memory* Thread::ReadMemory(const uint8_t** pc) { - Index memory_index = ReadU32(pc); - return &env_->memories_[memory_index]; + return Result::Ok; } -Table* Thread::ReadTable(const uint8_t** pc) { - Index table_index = ReadU32(pc); - return &env_->tables_[table_index]; -} +//// Func //// +Func::Func(ObjectKind kind, FuncType type) : Extern(kind), type_(type) {} -template <typename MemType> -Result Thread::GetAccessAddress(const uint8_t** pc, void** out_address) { - Memory* memory = ReadMemory(pc); - uint64_t addr = static_cast<uint64_t>(Pop<uint32_t>()) + ReadU32(pc); - if (addr + sizeof(MemType) > memory->data.size()) { - TRAP_MSG(MemoryAccessOutOfBounds, - "access at %" PRIu64 "+%" PRIzd " >= max value %" PRIzd, addr, - sizeof(MemType), memory->data.size()); - } - *out_address = memory->data.data() + static_cast<IstreamOffset>(addr); - return ResultType::Ok; +Result Func::Call(Store& store, + const Values& params, + Values& results, + Trap::Ptr* out_trap, + Stream* trace_stream) { + Thread::Options options; + options.trace_stream = trace_stream; + Thread::Ptr thread = Thread::New(store, options); + return DoCall(*thread, params, results, out_trap); } -template <typename MemType> -Result Thread::GetAtomicAccessAddress(const uint8_t** pc, void** out_address) { - Memory* memory = ReadMemory(pc); - uint64_t addr = static_cast<uint64_t>(Pop<uint32_t>()) + ReadU32(pc); - if (addr + sizeof(MemType) > memory->data.size()) { - TRAP_MSG(MemoryAccessOutOfBounds, - "atomic access at %" PRIu64 "+%" PRIzd " >= max value %" PRIzd, - addr, sizeof(MemType), memory->data.size()); - } - TRAP_IF((addr & (sizeof(MemType) - 1)) != 0, AtomicMemoryAccessUnaligned); - *out_address = memory->data.data() + static_cast<IstreamOffset>(addr); - return ResultType::Ok; +Result Func::Call(Thread& thread, + const Values& params, + Values& results, + Trap::Ptr* out_trap) { + return DoCall(thread, params, results, out_trap); } -DataSegment* Thread::ReadDataSegment(const uint8_t** pc) { - Index index = ReadU32(pc); - assert(index < env_->data_segments_.size()); - return &env_->data_segments_[index]; -} +//// DefinedFunc //// +DefinedFunc::DefinedFunc(Store& store, Ref instance, FuncDesc desc) + : Func(skind, desc.type), instance_(instance), desc_(desc) {} -ElemSegment* Thread::ReadElemSegment(const uint8_t** pc) { - Index index = ReadU32(pc); - assert(index < env_->elem_segments_.size()); - return &env_->elem_segments_[index]; +void DefinedFunc::Mark(Store& store) { + store.Mark(instance_); } -Value& Thread::Top() { - return Pick(1); +Result DefinedFunc::Match(Store& store, + const ImportType& import_type, + Trap::Ptr* out_trap) { + return MatchImpl(store, import_type, type_, out_trap); } -Value& Thread::Pick(Index depth) { - return value_stack_[value_stack_top_ - depth]; +Result DefinedFunc::DoCall(Thread& thread, + const Values& params, + Values& results, + Trap::Ptr* out_trap) { + assert(params.size() == type_.params.size()); + thread.PushValues(type_.params, params); + RunResult result = thread.PushCall(*this, out_trap); + if (result == RunResult::Trap) { + return Result::Error; + } + result = thread.Run(out_trap); + if (result == RunResult::Trap) { + return Result::Error; + } + thread.PopValues(type_.results, &results); + return Result::Ok; } -void Thread::Reset() { - pc_ = 0; - value_stack_top_ = 0; - call_stack_top_ = 0; -} +//// HostFunc //// +HostFunc::HostFunc(Store&, FuncType type, Callback callback) + : Func(skind, type), callback_(callback) {} -Result Thread::Push(Value value) { - CHECK_STACK(); - value_stack_[value_stack_top_++] = value; - return ResultType::Ok; -} +void HostFunc::Mark(Store&) {} -Value Thread::Pop() { - return value_stack_[--value_stack_top_]; +Result HostFunc::Match(Store& store, + const ImportType& import_type, + Trap::Ptr* out_trap) { + return MatchImpl(store, import_type, type_, out_trap); } -Value Thread::ValueAt(Index at) const { - assert(at < value_stack_top_); - return value_stack_[at]; +Result HostFunc::DoCall(Thread& thread, + const Values& params, + Values& results, + Trap::Ptr* out_trap) { + return callback_(params, results, out_trap); } -template <typename T> -Result Thread::Push(T value) { - return PushRep<T>(ToRep(value)); +//// Table //// +Table::Table(Store&, TableType type) : Extern(skind), type_(type) { + elements_.resize(type.limits.initial); } -template <typename T> -T Thread::Pop() { - return FromRep<T>(PopRep<T>()); +void Table::Mark(Store& store) { + store.Mark(elements_); } -template <typename T> -Result Thread::PushRep(ValueTypeRep<T> value) { - return Push(MakeValue<T>(value)); +Result Table::Match(Store& store, + const ImportType& import_type, + Trap::Ptr* out_trap) { + return MatchImpl(store, import_type, type_, out_trap); } -template <typename T> -ValueTypeRep<T> Thread::PopRep() { - return GetValue<T>(Pop()); +bool Table::IsValidRange(u32 offset, u32 size) const { + size_t elem_size = elements_.size(); + return size <= elem_size && offset <= elem_size - size; } -void Thread::DropKeep(uint32_t drop_count, uint32_t keep_count) { - // Copy backward to avoid clobbering when the regions overlap. - for (uint32_t i = keep_count; i > 0; --i) { - Pick(drop_count + i) = Pick(i); +Result Table::Get(u32 offset, Ref* out) const { + if (IsValidRange(offset, 1)) { + *out = elements_[offset]; + return Result::Ok; } - value_stack_top_ -= drop_count; + return Result::Error; } -Result Thread::PushCall(const uint8_t* pc) { - TRAP_IF(call_stack_top_ >= call_stack_.size(), CallStackExhausted); - call_stack_[call_stack_top_++] = pc - GetIstream(); - return ResultType::Ok; +Ref Table::UnsafeGet(u32 offset) const { + assert(IsValidRange(offset, 1)); + return elements_[offset]; } -IstreamOffset Thread::PopCall() { - return call_stack_[--call_stack_top_]; -} - -template <typename T> -void LoadFromMemory(T* dst, const void* src) { - memcpy(dst, src, sizeof(T)); -} - -template <typename T> -void StoreToMemory(void* dst, T value) { - memcpy(dst, &value, sizeof(T)); -} - -template <typename MemType, typename ResultValueType> -Result Thread::Load(const uint8_t** pc) { - typedef typename ExtendMemType<ResultValueType, MemType>::type ExtendedType; - static_assert(std::is_floating_point<MemType>::value == - std::is_floating_point<ExtendedType>::value, - "Extended type should be float iff MemType is float"); - - void* src; - CHECK_TRAP(GetAccessAddress<MemType>(pc, &src)); - MemType value; - LoadFromMemory<MemType>(&value, src); - return Push<ResultValueType>(static_cast<ExtendedType>(value)); -} - -template <typename MemType, typename ResultValueType> -Result Thread::Store(const uint8_t** pc) { - typedef typename WrapMemType<ResultValueType, MemType>::type WrappedType; - WrappedType value = PopRep<ResultValueType>(); - void* dst; - CHECK_TRAP(GetAccessAddress<MemType>(pc, &dst)); - StoreToMemory<WrappedType>(dst, value); - return ResultType::Ok; -} - -template <typename MemType, typename ResultValueType> -Result Thread::AtomicLoad(const uint8_t** pc) { - typedef typename ExtendMemType<ResultValueType, MemType>::type ExtendedType; - static_assert(!std::is_floating_point<MemType>::value, - "AtomicLoad type can't be float"); - void* src; - CHECK_TRAP(GetAtomicAccessAddress<MemType>(pc, &src)); - MemType value; - LoadFromMemory<MemType>(&value, src); - return Push<ResultValueType>(static_cast<ExtendedType>(value)); -} - -template <typename MemType, typename ResultValueType> -Result Thread::AtomicStore(const uint8_t** pc) { - typedef typename WrapMemType<ResultValueType, MemType>::type WrappedType; - WrappedType value = PopRep<ResultValueType>(); - void* dst; - CHECK_TRAP(GetAtomicAccessAddress<MemType>(pc, &dst)); - StoreToMemory<WrappedType>(dst, value); - return ResultType::Ok; -} - -template <typename MemType, typename ResultValueType> -Result Thread::AtomicRmw(BinopFunc<ResultValueType, ResultValueType> func, - const uint8_t** pc) { - typedef typename ExtendMemType<ResultValueType, MemType>::type ExtendedType; - MemType rhs = PopRep<ResultValueType>(); - void* addr; - CHECK_TRAP(GetAtomicAccessAddress<MemType>(pc, &addr)); - MemType read; - LoadFromMemory<MemType>(&read, addr); - StoreToMemory<MemType>(addr, func(read, rhs)); - return Push<ResultValueType>(static_cast<ExtendedType>(read)); -} - -template <typename MemType, typename ResultValueType> -Result Thread::AtomicRmwCmpxchg(const uint8_t** pc) { - typedef typename ExtendMemType<ResultValueType, MemType>::type ExtendedType; - MemType replace = PopRep<ResultValueType>(); - MemType expect = PopRep<ResultValueType>(); - void* addr; - CHECK_TRAP(GetAtomicAccessAddress<MemType>(pc, &addr)); - MemType read; - LoadFromMemory<MemType>(&read, addr); - if (read == expect) { - StoreToMemory<MemType>(addr, replace); - } - return Push<ResultValueType>(static_cast<ExtendedType>(read)); -} - -static bool CheckBounds(uint32_t start, uint32_t length, uint32_t max) { - if (start > max) { - return false; +Result Table::Set(Store& store, u32 offset, Ref ref) { + if (IsValidRange(offset, 1) && store.HasValueType(ref, type_.element)) { + elements_[offset] = ref; + return Result::Ok; } - uint32_t avail = max - start; - return length <= avail; + return Result::Error; } -Result Thread::MemoryInit(const uint8_t** pc) { - Memory* memory = ReadMemory(pc); - DataSegment* segment = ReadDataSegment(pc); - uint32_t memory_size = memory->data.size(); - uint32_t segment_size = segment->data.size(); - uint32_t size = Pop<uint32_t>(); - uint32_t src = Pop<uint32_t>(); - uint32_t dst = Pop<uint32_t>(); - bool ok = CheckBounds(dst, size, memory_size); - ok &= CheckBounds(src, size, segment_size); - if (!ok) { - TRAP_MSG(MemoryAccessOutOfBounds, "memory.init out of bounds"); - } - if (size > 0) { - memcpy(memory->data.data() + dst, segment->data.data() + src, size); +Result Table::Grow(Store& store, u32 count, Ref ref) { + size_t old_size = elements_.size(); + u32 new_size; + if (store.HasValueType(ref, type_.element) && + CanGrow(type_.limits, old_size, count, &new_size)) { + elements_.resize(new_size); + Fill(store, old_size, ref, new_size - old_size); + return Result::Ok; } - return ResultType::Ok; + return Result::Error; } -Result Thread::DataDrop(const uint8_t** pc) { - DataSegment* segment = ReadDataSegment(pc); - segment->data.clear(); - return ResultType::Ok; +Result Table::Fill(Store& store, u32 offset, Ref ref, u32 size) { + if (IsValidRange(offset, size) && + store.HasValueType(ref, type_.element)) { + std::fill(elements_.begin() + offset, elements_.begin() + offset + size, + ref); + return Result::Ok; + } + return Result::Error; } -Result Thread::MemoryCopy(const uint8_t** pc) { - Memory* memory = ReadMemory(pc); - uint32_t memory_size = memory->data.size(); - uint32_t size = Pop<uint32_t>(); - uint32_t src = Pop<uint32_t>(); - uint32_t dst = Pop<uint32_t>(); - bool ok = CheckBounds(dst, size, memory_size); - ok &= CheckBounds(src, size, memory_size); - if (!ok) { - TRAP_MSG(MemoryAccessOutOfBounds, "memory.copy out of bound"); - } - if (size > 0) { - char* data = memory->data.data(); - memmove(data + dst, data + src, size); +Result Table::Init(Store& store, + u32 dst_offset, + const ElemSegment& src, + u32 src_offset, + u32 size) { + if (IsValidRange(dst_offset, size) && src.IsValidRange(src_offset, size) && + TypesMatch(type_.element, src.desc().type)) { + std::copy(src.elements().begin() + src_offset, + src.elements().begin() + src_offset + size, + elements_.begin() + dst_offset); + return Result::Ok; } - return ResultType::Ok; + return Result::Error; } -Result Thread::MemoryFill(const uint8_t** pc) { - Memory* memory = ReadMemory(pc); - uint32_t memory_size = memory->data.size(); - uint32_t size = Pop<uint32_t>(); - uint8_t value = static_cast<uint8_t>(Pop<uint32_t>()); - uint32_t dst = Pop<uint32_t>(); - if (!CheckBounds(dst, size, memory_size)) { - TRAP_MSG(MemoryAccessOutOfBounds, "memory.fill out of bounds"); - } - if (size > 0) { - memset(memory->data.data() + dst, value, size); +// static +Result Table::Copy(Store& store, + Table& dst, + u32 dst_offset, + const Table& src, + u32 src_offset, + u32 size) { + if (dst.IsValidRange(dst_offset, size) && + src.IsValidRange(src_offset, size) && + TypesMatch(dst.type_.element, src.type_.element)) { + auto src_begin = src.elements_.begin() + src_offset; + auto src_end = src_begin + size; + auto dst_begin = dst.elements_.begin() + dst_offset; + auto dst_end = dst_begin + size; + if (dst.self() == src.self() && src_begin < dst_begin) { + std::move_backward(src_begin, src_end, dst_end); + } else { + std::move(src_begin, src_end, dst_begin); + } + return Result::Ok; } - return ResultType::Ok; + return Result::Error; } -Result Thread::TableInit(const uint8_t** pc) { - Table* table = ReadTable(pc); - ElemSegment* segment = ReadElemSegment(pc); - if (segment->flags == SegDeclared) { - TRAP_MSG(TableAccessOutOfBounds, "table.init out of bounds"); - } - uint32_t segment_size = segment->elems.size(); - uint32_t size = Pop<uint32_t>(); - uint32_t src = Pop<uint32_t>(); - uint32_t dst = Pop<uint32_t>(); - bool ok = CheckBounds(dst, size, table->size()); - ok &= CheckBounds(src, size, segment_size); - if (!ok) { - TRAP_MSG(TableAccessOutOfBounds, "table.init out of bounds"); - } - if (size > 0) { - memcpy(table->entries.data() + dst, segment->elems.data() + src, - size * sizeof(table->entries[0])); - } - return ResultType::Ok; +//// Memory //// +Memory::Memory(class Store&, MemoryType type) + : Extern(skind), type_(type), pages_(type.limits.initial) { + data_.resize(pages_ * WABT_PAGE_SIZE); } -Result Thread::TableSet(const uint8_t** pc) { - Table* table = ReadTable(pc); - Ref ref = Pop<Ref>(); - uint32_t index = Pop<uint32_t>(); - if (index >= table->size()) { - TRAP_MSG(TableAccessOutOfBounds, "table.set at %u >= max value %" PRIzx, - index, table->size()); - } - table->entries[index] = ref; - return ResultType::Ok; +void Memory::Mark(class Store&) {} + +Result Memory::Match(class Store& store, + const ImportType& import_type, + Trap::Ptr* out_trap) { + return MatchImpl(store, import_type, type_, out_trap); } -Result Thread::TableGet(const uint8_t** pc) { - Table* table = ReadTable(pc); - uint32_t index = Pop<uint32_t>(); - if (index >= table->size()) { - TRAP_MSG(TableAccessOutOfBounds, "table.get at %u >= max value %" PRIzx, - index, table->size()); +Result Memory::Grow(u32 count) { + u32 new_pages; + if (CanGrow(type_.limits, pages_, count, &new_pages)) { + pages_ = new_pages; + data_.resize(new_pages * WABT_PAGE_SIZE); + return Result::Ok; } - return Push(table->entries[index]); + return Result::Error; } -Result Thread::TableFill(const uint8_t** pc) { - Table* table = ReadTable(pc); - uint32_t size = Pop<uint32_t>(); - Ref value = static_cast<Ref>(Pop<Ref>()); - uint32_t dst = Pop<uint32_t>(); - bool ok = CheckBounds(dst, size, table->size()); - if (!ok) { - TRAP_MSG(TableAccessOutOfBounds, "table.fill out of bounds"); - } - for (uint32_t i = 0; i < size; i++) { - table->entries[dst+i] = value; +Result Memory::Fill(u32 offset, u8 value, u32 size) { + if (IsValidAccess(offset, 0, size)) { + std::fill(data_.begin() + offset, data_.begin() + offset + size, value); + return Result::Ok; } - return ResultType::Ok; + return Result::Error; } -Result Thread::ElemDrop(const uint8_t** pc) { - ElemSegment* segment = ReadElemSegment(pc); - segment->elems.clear(); - return ResultType::Ok; +Result Memory::Init(u32 dst_offset, + const DataSegment& src, + u32 src_offset, + u32 size) { + if (IsValidAccess(dst_offset, 0, size) && + src.IsValidRange(src_offset, size)) { + std::copy(src.desc().data.begin() + src_offset, + src.desc().data.begin() + src_offset + size, + data_.begin() + dst_offset); + return Result::Ok; + } + return Result::Error; } -Result Thread::TableCopy(const uint8_t** pc) { - Table* src_table = ReadTable(pc); - Table* dst_table = ReadTable(pc); - assert(src_table == dst_table); - uint32_t size = Pop<uint32_t>(); - uint32_t src = Pop<uint32_t>(); - uint32_t dst = Pop<uint32_t>(); - bool ok = CheckBounds(dst, size, dst_table->size()); - ok &= CheckBounds(src, size, dst_table->size()); - if (!ok) { - TRAP_MSG(TableAccessOutOfBounds, "table.copy out of bounds"); - } - if (size > 0) { - Ref* data_src = src_table->entries.data(); - Ref* data_dst = dst_table->entries.data(); - memmove(data_dst + dst, data_src + src, size * sizeof(Ref)); +// static +Result Memory::Copy(Memory& dst, + u32 dst_offset, + const Memory& src, + u32 src_offset, + u32 size) { + if (dst.IsValidAccess(dst_offset, 0, size) && + src.IsValidAccess(src_offset, 0, size)) { + auto src_begin = src.data_.begin() + src_offset; + auto src_end = src_begin + size; + auto dst_begin = dst.data_.begin() + dst_offset; + auto dst_end = dst_begin + size; + if (src.self() == dst.self() && src_begin < dst_begin) { + std::move_backward(src_begin, src_end, dst_end); + } else { + std::move(src_begin, src_end, dst_begin); + } + return Result::Ok; } - return ResultType::Ok; + return Result::Error; } -Result Thread::RefFunc(const uint8_t** pc) { - uint32_t index = ReadU32(pc); - assert(index < env_->GetFuncCount()); - return Push(Ref{RefType::Func, index}); -} +Value Instance::ResolveInitExpr(Store& store, InitExpr init) { + Value result; + switch (init.kind) { + case InitExprKind::I32: result.Set(init.i32_); break; + case InitExprKind::I64: result.Set(init.i64_); break; + case InitExprKind::F32: result.Set(init.f32_); break; + case InitExprKind::F64: result.Set(init.f64_); break; + case InitExprKind::V128: result.Set(init.v128_); break; + case InitExprKind::GlobalGet: { + Global::Ptr global{store, globals_[init.index_]}; + result = global->Get(); + break; + } + case InitExprKind::RefFunc: { + result.Set(funcs_[init.index_]); + break; + } + case InitExprKind::RefNull: + result.Set(Ref::Null); + break; -template <typename R, typename T> -Result Thread::Unop(UnopFunc<R, T> func) { - auto value = PopRep<T>(); - return PushRep<R>(func(value)); + case InitExprKind::None: + WABT_UNREACHABLE; + } + return result; } -// {i8, i16, 132, i64}{16, 8, 4, 2}.(neg) -template <typename T, typename L, typename R, typename P> -Result Thread::SimdUnop(UnopFunc<R, P> func) { - auto value = PopRep<T>(); - - // Calculate how many Lanes according to input lane data type. - constexpr int32_t lanes = sizeof(T) / sizeof(L); - - // Define SIMD data array for Simd add by Lanes. - L simd_data_ret[lanes]; - L simd_data_0[lanes]; +//// Global //// +Global::Global(Store& store, GlobalType type, Value value) + : Extern(skind), type_(type), value_(value) {} - // Convert intput SIMD data to array. - memcpy(simd_data_0, &value, sizeof(T)); - - // Constuct the Simd value by Lane data and Lane nums. - for (int32_t i = 0; i < lanes; i++) { - simd_data_ret[i] = static_cast<L>(func(simd_data_0[i])); +void Global::Mark(Store& store) { + if (IsReference(type_.type)) { + store.Mark(value_.ref_); } +} - return PushRep<T>(Bitcast<T>(simd_data_ret)); +Result Global::Match(Store& store, + const ImportType& import_type, + Trap::Ptr* out_trap) { + return MatchImpl(store, import_type, type_, out_trap); } -template <typename R, typename T> -Result Thread::UnopTrap(UnopTrapFunc<R, T> func) { - auto value = PopRep<T>(); - ValueTypeRep<R> result_value; - CHECK_TRAP(func(value, &result_value)); - return PushRep<R>(result_value); +Result Global::Set(Store& store, Ref ref) { + if (store.HasValueType(ref, type_.type)) { + value_.Set(ref); + return Result::Ok; + } + return Result::Error; } -template <typename R, typename T> -Result Thread::Binop(BinopFunc<R, T> func) { - auto rhs_rep = PopRep<T>(); - auto lhs_rep = PopRep<T>(); - return PushRep<R>(func(lhs_rep, rhs_rep)); +void Global::UnsafeSet(Value value) { + value_ = value; } -// {i8, i16, 132, i64}{16, 8, 4, 2}.(add/sub/mul) -template <typename T, typename L, typename R, typename P> -Result Thread::SimdBinop(BinopFunc<R, P> func) { - auto rhs_rep = PopRep<T>(); - auto lhs_rep = PopRep<T>(); +//// Event //// +Event::Event(Store&, EventType type) : Extern(skind), type_(type) {} - // Calculate how many Lanes according to input lane data type. - constexpr int32_t lanes = sizeof(T) / sizeof(L); +void Event::Mark(Store&) {} - // Define SIMD data array for Simd add by Lanes. - L simd_data_ret[lanes]; - L simd_data_0[lanes]; - L simd_data_1[lanes]; +Result Event::Match(Store& store, + const ImportType& import_type, + Trap::Ptr* out_trap) { + return MatchImpl(store, import_type, type_, out_trap); +} - // Convert intput SIMD data to array. - memcpy(simd_data_0, &lhs_rep, sizeof(T)); - memcpy(simd_data_1, &rhs_rep, sizeof(T)); +//// ElemSegment //// +ElemSegment::ElemSegment(const ElemDesc* desc, Instance::Ptr& inst) + : desc_(desc) { + elements_.reserve(desc->elements.size()); + for (auto&& elem_expr : desc->elements) { + switch (elem_expr.kind) { + case ElemKind::RefNull: + elements_.emplace_back(Ref::Null); + break; - // Constuct the Simd value by Lane data and Lane nums. - for (int32_t i = 0; i < lanes; i++) { - simd_data_ret[i] = static_cast<L>(func(simd_data_0[i], simd_data_1[i])); + case ElemKind::RefFunc: + elements_.emplace_back(inst->funcs_[elem_expr.index]); + break; + } } - - return PushRep<T>(Bitcast<T>(simd_data_ret)); } -// {i8, i16, 132, i64, f32, f64}{16, 8, 4, 2}.(eq/ne/lt/le/gt/ge) -template <typename T, typename L, typename R, typename P> -Result Thread::SimdRelBinop(BinopFunc<R, P> func) { - auto rhs_rep = PopRep<T>(); - auto lhs_rep = PopRep<T>(); - - // Calculate how many Lanes according to input lane data type. - constexpr int32_t lanes = sizeof(T) / sizeof(L); +bool ElemSegment::IsValidRange(u32 offset, u32 size) const { + u32 elem_size = this->size(); + return size <= elem_size && offset <= elem_size - size; +} - // Define SIMD data array for Simd add by Lanes. - L simd_data_ret[lanes]; - L simd_data_0[lanes]; - L simd_data_1[lanes]; +//// DataSegment //// +DataSegment::DataSegment(const DataDesc* desc) + : desc_(desc), size_(desc->data.size()) {} - // Convert intput SIMD data to array. - memcpy(simd_data_0, &lhs_rep, sizeof(T)); - memcpy(simd_data_1, &rhs_rep, sizeof(T)); +bool DataSegment::IsValidRange(u32 offset, u32 size) const { + u32 data_size = size_; + return size <= data_size && offset <= data_size - size; +} - // Constuct the Simd value by Lane data and Lane nums. - for (int32_t i = 0; i < lanes; i++) { - simd_data_ret[i] = static_cast<L>( - func(simd_data_0[i], simd_data_1[i]) == 0? 0 : -1 - ); +//// Module //// +Module::Module(Store&, ModuleDesc desc) + : Object(skind), desc_(std::move(desc)) { + for (auto&& import: desc_.imports) { + import_types_.emplace_back(import.type); } - return PushRep<T>(Bitcast<T>(simd_data_ret)); + for (auto&& export_: desc_.exports) { + export_types_.emplace_back(export_.type); + } } -template <typename R, typename T> -Result Thread::BinopTrap(BinopTrapFunc<R, T> func) { - auto rhs_rep = PopRep<T>(); - auto lhs_rep = PopRep<T>(); - ValueTypeRep<R> result_value; - CHECK_TRAP(func(lhs_rep, rhs_rep, &result_value)); - return PushRep<R>(result_value); -} +void Module::Mark(Store&) {} -// {i,f}{32,64}.add -template <typename T> -ValueTypeRep<T> Add(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return CanonicalizeNan<T>(ToRep(FromRep<T>(lhs_rep) + FromRep<T>(rhs_rep))); +//// ElemSegment //// +void ElemSegment::Mark(Store& store) { + store.Mark(elements_); } -template <typename T, typename R> -ValueTypeRep<T> AddSaturate(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - T max = std::numeric_limits<R>::max(); - T min = std::numeric_limits<R>::min(); - T result = static_cast<T>(lhs_rep) + static_cast<T>(rhs_rep); - - if (result < min) { - return ToRep(min); - } else if (result > max) { - return ToRep(max); - } else { - return ToRep(result); - } +//// Instance //// +Instance::Instance(Store& store, Ref module) : Object(skind), module_(module) { + assert(store.Is<Module>(module)); } -template <typename T, typename R> -ValueTypeRep<T> SubSaturate(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - T max = std::numeric_limits<R>::max(); - T min = std::numeric_limits<R>::min(); - T result = static_cast<T>(lhs_rep) - static_cast<T>(rhs_rep); +// static +Instance::Ptr Instance::Instantiate(Store& store, + Ref module, + const RefVec& imports, + Trap::Ptr* out_trap) { + Module::Ptr mod{store, module}; + Instance::Ptr inst = store.Alloc<Instance>(store, module); - if (result < min) { - return ToRep(min); - } else if (result > max) { - return ToRep(max); - } else { - return ToRep(result); + size_t import_desc_count = mod->desc().imports.size(); + if (imports.size() < import_desc_count) { + *out_trap = Trap::New(store, "not enough imports!"); + return {}; } -} -template <typename T, typename L> -int32_t SimdIsLaneTrue(ValueTypeRep<T> value, int32_t true_cond) { - int true_count = 0; - - // Calculate how many Lanes according to input lane data type. - constexpr int32_t lanes = sizeof(T) / sizeof(L); + // Imports. + for (size_t i = 0; i < import_desc_count; ++i) { + auto&& import_desc = mod->desc().imports[i]; + Ref extern_ref = imports[i]; + if (extern_ref == Ref::Null) { + *out_trap = Trap::New(store, StringPrintf("invalid import \"%s.%s\"", + import_desc.type.module.c_str(), + import_desc.type.name.c_str())); + return {}; + } - // Define SIMD data array for Simd Lanes. - L simd_data_0[lanes]; + Extern::Ptr extern_{store, extern_ref}; + if (Failed(extern_->Match(store, import_desc.type, out_trap))) { + return {}; + } - // Convert intput SIMD data to array. - memcpy(simd_data_0, &value, sizeof(T)); + inst->imports_.push_back(extern_ref); - // Constuct the Simd value by Lane data and Lane nums. - for (int32_t i = 0; i < lanes; i++) { - if (simd_data_0[i] != 0) - true_count++; + switch (import_desc.type.type->kind) { + case ExternKind::Func: inst->funcs_.push_back(extern_ref); break; + case ExternKind::Table: inst->tables_.push_back(extern_ref); break; + case ExternKind::Memory: inst->memories_.push_back(extern_ref); break; + case ExternKind::Global: inst->globals_.push_back(extern_ref); break; + case ExternKind::Event: inst->events_.push_back(extern_ref); break; + } } - return (true_count >= true_cond) ? 1 : 0; -} - -// {i,f}{32,64}.sub -template <typename T> -ValueTypeRep<T> Sub(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return CanonicalizeNan<T>(ToRep(FromRep<T>(lhs_rep) - FromRep<T>(rhs_rep))); -} - -// {i,f}{32,64}.mul -template <typename T> -ValueTypeRep<T> Mul(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return CanonicalizeNan<T>(ToRep(FromRep<T>(lhs_rep) * FromRep<T>(rhs_rep))); -} + // Funcs. + for (auto&& desc : mod->desc().funcs) { + inst->funcs_.push_back(DefinedFunc::New(store, inst.ref(), desc).ref()); + } -// i{32,64}.{div,rem}_s are special-cased because they trap when dividing the -// max signed value by -1. The modulo operation on x86 uses the same -// instruction to generate the quotient and the remainder. -template <typename T> -bool IsNormalDivRemS(T lhs, T rhs) { - static_assert(std::is_signed<T>::value, "T should be a signed type."); - return !(lhs == std::numeric_limits<T>::min() && rhs == -1); -} + // Tables. + for (auto&& desc : mod->desc().tables) { + inst->tables_.push_back(Table::New(store, desc.type).ref()); + } -// i{32,64}.div_s -template <typename T> -Result IntDivS(ValueTypeRep<T> lhs_rep, - ValueTypeRep<T> rhs_rep, - ValueTypeRep<T>* out_result) { - auto lhs = FromRep<T>(lhs_rep); - auto rhs = FromRep<T>(rhs_rep); - TRAP_IF(rhs == 0, IntegerDivideByZero); - TRAP_UNLESS(IsNormalDivRemS(lhs, rhs), IntegerOverflow); - *out_result = ToRep(lhs / rhs); - return ResultType::Ok; -} - -// i{32,64}.rem_s -template <typename T> -Result IntRemS(ValueTypeRep<T> lhs_rep, - ValueTypeRep<T> rhs_rep, - ValueTypeRep<T>* out_result) { - auto lhs = FromRep<T>(lhs_rep); - auto rhs = FromRep<T>(rhs_rep); - TRAP_IF(rhs == 0, IntegerDivideByZero); - if (WABT_LIKELY(IsNormalDivRemS(lhs, rhs))) { - *out_result = ToRep(lhs % rhs); - } else { - *out_result = 0; + // Memories. + for (auto&& desc : mod->desc().memories) { + inst->memories_.push_back(Memory::New(store, desc.type).ref()); } - return ResultType::Ok; -} -// i{32,64}.div_u -template <typename T> -Result IntDivU(ValueTypeRep<T> lhs_rep, - ValueTypeRep<T> rhs_rep, - ValueTypeRep<T>* out_result) { - auto lhs = FromRep<T>(lhs_rep); - auto rhs = FromRep<T>(rhs_rep); - TRAP_IF(rhs == 0, IntegerDivideByZero); - *out_result = ToRep(lhs / rhs); - return ResultType::Ok; -} + // Globals. + for (auto&& desc : mod->desc().globals) { + inst->globals_.push_back( + Global::New(store, desc.type, inst->ResolveInitExpr(store, desc.init)) + .ref()); + } -// i{32,64}.rem_u -template <typename T> -Result IntRemU(ValueTypeRep<T> lhs_rep, - ValueTypeRep<T> rhs_rep, - ValueTypeRep<T>* out_result) { - auto lhs = FromRep<T>(lhs_rep); - auto rhs = FromRep<T>(rhs_rep); - TRAP_IF(rhs == 0, IntegerDivideByZero); - *out_result = ToRep(lhs % rhs); - return ResultType::Ok; -} + // Events. + for (auto&& desc : mod->desc().events) { + inst->events_.push_back(Event::New(store, desc.type).ref()); + } -// f{32,64}.div -template <typename T> -ValueTypeRep<T> FloatDiv(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - typedef FloatTraits<T> Traits; - ValueTypeRep<T> result; - if (WABT_UNLIKELY(Traits::IsZero(rhs_rep))) { - if (Traits::IsNan(lhs_rep) || Traits::IsZero(lhs_rep)) { - result = Traits::kQuietNan; - } else { - auto sign = (lhs_rep & Traits::kSignMask) ^ (rhs_rep & Traits::kSignMask); - result = sign | Traits::kInf; + // Exports. + for (auto&& desc : mod->desc().exports){ + Ref ref; + switch (desc.type.type->kind) { + case ExternKind::Func: ref = inst->funcs_[desc.index]; break; + case ExternKind::Table: ref = inst->tables_[desc.index]; break; + case ExternKind::Memory: ref = inst->memories_[desc.index]; break; + case ExternKind::Global: ref = inst->globals_[desc.index]; break; + case ExternKind::Event: ref = inst->events_[desc.index]; break; } - } else { - result = - CanonicalizeNan<T>(ToRep(FromRep<T>(lhs_rep) / FromRep<T>(rhs_rep))); + inst->exports_.push_back(ref); } - return result; -} -// i{32,64}.and -template <typename T> -ValueTypeRep<T> IntAnd(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return ToRep(FromRep<T>(lhs_rep) & FromRep<T>(rhs_rep)); -} + // Elems. + for (auto&& desc : mod->desc().elems) { + inst->elems_.emplace_back(&desc, inst); + } -// i{32,64}.or -template <typename T> -ValueTypeRep<T> IntOr(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return ToRep(FromRep<T>(lhs_rep) | FromRep<T>(rhs_rep)); -} + // Datas. + for (auto&& desc : mod->desc().datas) { + inst->datas_.emplace_back(&desc); + } -// i{32,64}.xor -template <typename T> -ValueTypeRep<T> IntXor(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return ToRep(FromRep<T>(lhs_rep) ^ FromRep<T>(rhs_rep)); -} + // Initialization. + enum Pass { Check, Init }; + int pass = store.features().bulk_memory_enabled() ? Init : Check; + for (; pass <= Init; ++pass) { + // Elems. + for (auto&& segment : inst->elems_) { + auto&& desc = segment.desc(); + if (desc.mode == SegmentMode::Active) { + Result result; + Table::Ptr table{store, inst->tables_[desc.table_index]}; + u32 offset = inst->ResolveInitExpr(store, desc.offset).Get<u32>(); + if (pass == Check) { + result = table->IsValidRange(offset, segment.size()) ? Result::Ok + : Result::Error; + } else { + result = table->Init(store, offset, segment, 0, segment.size()); + segment.Drop(); + } -// i{32,64}.shl -template <typename T> -ValueTypeRep<T> IntShl(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - const int mask = sizeof(T) * 8 - 1; - return ToRep(FromRep<T>(lhs_rep) << (FromRep<T>(rhs_rep) & mask)); -} + if (Failed(result)) { + *out_trap = Trap::New( + store, StringPrintf( + "out of bounds table access: elem segment is " + "out of bounds: [%u, %" PRIu64 ") >= max value %u", + offset, u64{offset} + segment.size(), table->size())); + return {}; + } + } else if (desc.mode == SegmentMode::Declared) { + segment.Drop(); + } + } -// i{32,64}.shr_{s,u} -template <typename T> -ValueTypeRep<T> IntShr(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - const int mask = sizeof(T) * 8 - 1; - return ToRep(FromRep<T>(lhs_rep) >> (FromRep<T>(rhs_rep) & mask)); -} + // Data. + for (auto&& segment : inst->datas_) { + auto&& desc = segment.desc(); + if (desc.mode == SegmentMode::Active) { + Result result; + Memory::Ptr memory{store, inst->memories_[desc.memory_index]}; + u32 offset = inst->ResolveInitExpr(store, desc.offset).Get<u32>(); + if (pass == Check) { + result = memory->IsValidAccess(offset, 0, segment.size()) + ? Result::Ok + : Result::Error; + } else { + result = memory->Init(offset, segment, 0, segment.size()); + segment.Drop(); + } -// i{32,64}.rotl -template <typename T> -ValueTypeRep<T> IntRotl(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - const int mask = sizeof(T) * 8 - 1; - int amount = FromRep<T>(rhs_rep) & mask; - auto lhs = FromRep<T>(lhs_rep); - if (amount == 0) { - return ToRep(lhs); - } else { - return ToRep((lhs << amount) | (lhs >> (mask + 1 - amount))); + if (Failed(result)) { + *out_trap = Trap::New( + store, + StringPrintf("out of bounds memory access: data segment is " + "out of bounds: [%u, %" PRIu64 ") >= max value %u", + offset, u64{offset} + segment.size(), + memory->ByteSize())); + return {}; + } + } else if (desc.mode == SegmentMode::Declared) { + segment.Drop(); + } + } } -} -// i{32,64}.rotr -template <typename T> -ValueTypeRep<T> IntRotr(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - const int mask = sizeof(T) * 8 - 1; - int amount = FromRep<T>(rhs_rep) & mask; - auto lhs = FromRep<T>(lhs_rep); - if (amount == 0) { - return ToRep(lhs); - } else { - return ToRep((lhs >> amount) | (lhs << (mask + 1 - amount))); + // Start. + for (auto&& start : mod->desc().starts) { + Func::Ptr func{store, inst->funcs_[start.func_index]}; + Values results; + if (Failed(func->Call(store, {}, results, out_trap))) { + return {}; + } } -} -// i{32,64}.eqz -template <typename R, typename T> -ValueTypeRep<R> IntEqz(ValueTypeRep<T> v_rep) { - return ToRep(v_rep == 0); + return inst; } -ValueTypeRep<uint32_t> RefIsNull(ValueTypeRep<Ref> v_rep) { - return ToRep(v_rep.kind == RefType::Null); -} - -template <typename T> -ValueTypeRep<T> IntNeg(ValueTypeRep<T> v_rep) { - T tmp = static_cast<T>(v_rep); - return ToRep(-tmp); -} - -template <typename T> -ValueTypeRep<T> IntNot(ValueTypeRep<T> v_rep) { - T tmp = static_cast<T>(v_rep); - return ToRep(~tmp); +void Instance::Mark(Store& store) { + store.Mark(module_); + store.Mark(imports_); + store.Mark(funcs_); + store.Mark(memories_); + store.Mark(tables_); + store.Mark(globals_); + store.Mark(events_); + store.Mark(exports_); + for (auto&& elem : elems_) { + elem.Mark(store); + } } -// f{32,64}.abs -template <typename T> -ValueTypeRep<T> FloatAbs(ValueTypeRep<T> v_rep) { - return v_rep & ~FloatTraits<T>::kSignMask; +//// Thread //// +Thread::Thread(Store& store, const Options& options) + : Object(skind), store_(store) { + frames_.reserve(options.call_stack_size); + values_.reserve(options.value_stack_size); + trace_stream_ = options.trace_stream; + if (options.trace_stream) { + trace_source_ = MakeUnique<TraceSource>(this); + } } -// f{32,64}.neg -template <typename T> -ValueTypeRep<T> FloatNeg(ValueTypeRep<T> v_rep) { - return v_rep ^ FloatTraits<T>::kSignMask; +void Thread::Mark(Store& store) { + for (auto&& frame : frames_) { + frame.Mark(store); + } + for (auto index: refs_) { + store.Mark(values_[index].ref_); + } } -// f{32,64}.ceil -template <typename T> -ValueTypeRep<T> FloatCeil(ValueTypeRep<T> v_rep) { - return CanonicalizeNan<T>(ToRep(std::ceil(FromRep<T>(v_rep)))); +void Thread::PushValues(const ValueTypes& types, const Values& values) { + assert(types.size() == values.size()); + for (size_t i = 0; i < types.size(); ++i) { + if (IsReference(types[i])) { + refs_.push_back(values_.size()); + } + values_.push_back(values[i]); + } } -// f{32,64}.floor -template <typename T> -ValueTypeRep<T> FloatFloor(ValueTypeRep<T> v_rep) { - return CanonicalizeNan<T>(ToRep(std::floor(FromRep<T>(v_rep)))); -} +#define TRAP(msg) *out_trap = Trap::New(store_, (msg), frames_), RunResult::Trap +#define TRAP_IF(cond, msg) \ + if (WABT_UNLIKELY((cond))) { \ + return TRAP(msg); \ + } +#define TRAP_UNLESS(cond, msg) TRAP_IF(!(cond), msg) -// f{32,64}.trunc -template <typename T> -ValueTypeRep<T> FloatTrunc(ValueTypeRep<T> v_rep) { - return CanonicalizeNan<T>(ToRep(std::trunc(FromRep<T>(v_rep)))); +RunResult Thread::PushCall(Ref func, u32 offset, Trap::Ptr* out_trap) { + TRAP_IF(frames_.size() == frames_.capacity(), "call stack exhausted"); + frames_.emplace_back(func, values_.size(), offset, inst_, mod_); + return RunResult::Ok; } -// f{32,64}.nearest -template <typename T> -ValueTypeRep<T> FloatNearest(ValueTypeRep<T> v_rep) { - return CanonicalizeNan<T>(ToRep(std::nearbyint(FromRep<T>(v_rep)))); +RunResult Thread::PushCall(const DefinedFunc& func, Trap::Ptr* out_trap) { + TRAP_IF(frames_.size() == frames_.capacity(), "call stack exhausted"); + inst_ = store_.UnsafeGet<Instance>(func.instance()).get(); + mod_ = store_.UnsafeGet<Module>(inst_->module()).get(); + frames_.emplace_back(func.self(), values_.size(), func.desc().code_offset, + inst_, mod_); + return RunResult::Ok; } -// f{32,64}.sqrt -template <typename T> -ValueTypeRep<T> FloatSqrt(ValueTypeRep<T> v_rep) { - return CanonicalizeNan<T>(ToRep(std::sqrt(FromRep<T>(v_rep)))); +RunResult Thread::PushCall(const HostFunc& func, Trap::Ptr* out_trap) { + TRAP_IF(frames_.size() == frames_.capacity(), "call stack exhausted"); + inst_ = nullptr; + mod_ = nullptr; + frames_.emplace_back(func.self(), values_.size(), 0, inst_, mod_); + return RunResult::Ok; } -// f{32,64}.min -template <typename T> -ValueTypeRep<T> FloatMin(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - typedef FloatTraits<T> Traits; - - if (WABT_UNLIKELY(Traits::IsNan(lhs_rep) || Traits::IsNan(rhs_rep))) { - return Traits::kQuietNan; - } else if (WABT_UNLIKELY(Traits::IsZero(lhs_rep) && - Traits::IsZero(rhs_rep))) { - // min(0.0, -0.0) == -0.0, but std::min won't produce the correct result. - // We can instead compare using the unsigned integer representation, but - // just max instead (since the sign bit makes the value larger). - return std::max(lhs_rep, rhs_rep); - } else { - return ToRep(std::min(FromRep<T>(lhs_rep), FromRep<T>(rhs_rep))); +RunResult Thread::PopCall() { + frames_.pop_back(); + if (frames_.empty()) { + return RunResult::Return; } -} -// f{32,64}.max -template <typename T> -ValueTypeRep<T> FloatMax(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - typedef FloatTraits<T> Traits; - - if (WABT_UNLIKELY(Traits::IsNan(lhs_rep) || Traits::IsNan(rhs_rep))) { - return Traits::kQuietNan; - } else if (WABT_UNLIKELY(Traits::IsZero(lhs_rep) && - Traits::IsZero(rhs_rep))) { - // min(0.0, -0.0) == -0.0, but std::min won't produce the correct result. - // We can instead compare using the unsigned integer representation, but - // just max instead (since the sign bit makes the value larger). - return std::min(lhs_rep, rhs_rep); - } else { - return ToRep(std::max(FromRep<T>(lhs_rep), FromRep<T>(rhs_rep))); + auto& frame = frames_.back(); + if (!frame.inst) { + // Returning to a HostFunc called on this thread. + return RunResult::Return; } -} - -// f{32,64}.copysign -template <typename T> -ValueTypeRep<T> FloatCopySign(ValueTypeRep<T> lhs_rep, - ValueTypeRep<T> rhs_rep) { - typedef FloatTraits<T> Traits; - return (lhs_rep & ~Traits::kSignMask) | (rhs_rep & Traits::kSignMask); -} - -// {i,f}{32,64}.eq -template <typename T> -uint32_t Eq(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return ToRep(FromRep<T>(lhs_rep) == FromRep<T>(rhs_rep)); -} -// {i,f}{32,64}.ne -template <typename T> -uint32_t Ne(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return ToRep(FromRep<T>(lhs_rep) != FromRep<T>(rhs_rep)); + inst_ = frame.inst; + mod_ = frame.mod; + return RunResult::Ok; } -// f{32,64}.lt | i{32,64}.lt_{s,u} -template <typename T> -uint32_t Lt(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return ToRep(FromRep<T>(lhs_rep) < FromRep<T>(rhs_rep)); +RunResult Thread::DoReturnCall(const Func::Ptr& func, Trap::Ptr* out_trap) { + PopCall(); + DoCall(func, out_trap); + return frames_.empty() ? RunResult::Return : RunResult::Ok; } -// f{32,64}.le | i{32,64}.le_{s,u} -template <typename T> -uint32_t Le(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return ToRep(FromRep<T>(lhs_rep) <= FromRep<T>(rhs_rep)); +void Thread::PopValues(const ValueTypes& types, Values* out_values) { + assert(values_.size() >= types.size()); + out_values->resize(types.size()); + std::copy(values_.end() - types.size(), values_.end(), out_values->begin()); + values_.resize(values_.size() - types.size()); } -// f{32,64}.gt | i{32,64}.gt_{s,u} -template <typename T> -uint32_t Gt(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return ToRep(FromRep<T>(lhs_rep) > FromRep<T>(rhs_rep)); +RunResult Thread::Run(Trap::Ptr* out_trap) { + const int kDefaultInstructionCount = 1000; + RunResult result; + do { + result = Run(kDefaultInstructionCount, out_trap); + } while (result == RunResult::Ok); + return result; } -// f{32,64}.ge | i{32,64}.ge_{s,u} -template <typename T> -uint32_t Ge(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return ToRep(FromRep<T>(lhs_rep) >= FromRep<T>(rhs_rep)); +RunResult Thread::Run(int num_instructions, Trap::Ptr* out_trap) { + DefinedFunc::Ptr func{store_, frames_.back().func}; + for (;num_instructions > 0; --num_instructions) { + auto result = StepInternal(out_trap); + if (result != RunResult::Ok) { + return result; + } + } + return RunResult::Ok; } -// f32x4.convert_{s,u}/i32x4 and f64x2.convert_s/i64x2. -template <typename R, typename T> -ValueTypeRep<R> SimdConvert(ValueTypeRep<T> v_rep) { - return ToRep(static_cast<R>(static_cast<T>(v_rep))); +RunResult Thread::Step(Trap::Ptr* out_trap) { + DefinedFunc::Ptr func{store_, frames_.back().func}; + return StepInternal(out_trap); } -// f64x2.convert_u/i64x2 use this instance due to MSVC issue. -template <> -ValueTypeRep<double> SimdConvert<double, uint64_t>( - ValueTypeRep<uint64_t> v_rep) { - return ToRep(wabt_convert_uint64_to_double(v_rep)); +Value& Thread::Pick(Index index) { + assert(index > 0 && index <= values_.size()); + return values_[values_.size() - index]; } -// i{32,64}.trunc_{s,u}/f{32,64} -template <typename R, typename T> -Result IntTrunc(ValueTypeRep<T> v_rep, ValueTypeRep<R>* out_result) { - TRAP_IF(FloatTraits<T>::IsNan(v_rep), InvalidConversionToInteger); - TRAP_UNLESS((IsConversionInRange<R, T>(v_rep)), IntegerOverflow); - *out_result = ToRep(static_cast<R>(FromRep<T>(v_rep))); - return ResultType::Ok; +template <typename T> +T WABT_VECTORCALL Thread::Pop() { + return Pop().Get<T>(); } -// i{32,64}.trunc_{s,u}:sat/f{32,64} -template <typename R, typename T> -ValueTypeRep<R> IntTruncSat(ValueTypeRep<T> v_rep) { - typedef FloatTraits<T> Traits; - if (WABT_UNLIKELY(Traits::IsNan(v_rep))) { - return 0; - } else if (WABT_UNLIKELY((!IsConversionInRange<R, T>(v_rep)))) { - if (v_rep & Traits::kSignMask) { - return ToRep(std::numeric_limits<R>::min()); - } else { - return ToRep(std::numeric_limits<R>::max()); - } - } else { - return ToRep(static_cast<R>(FromRep<T>(v_rep))); +Value Thread::Pop() { + if (!refs_.empty() && refs_.back() >= values_.size()) { + refs_.pop_back(); } + auto value = values_.back(); + values_.pop_back(); + return value; } -// i{32,64}.extend{8,16,32}_s -template <typename T, typename E> -ValueTypeRep<T> IntExtendS(ValueTypeRep<T> v_rep) { - // To avoid undefined/implementation-defined behavior, convert from unsigned - // type (T), to an unsigned value of the smaller size (EU), then bitcast from - // unsigned to signed, then cast from the smaller signed type to the larger - // signed type (TS) to sign extend. ToRep then will bitcast back from signed - // to unsigned. - static_assert(std::is_unsigned<ValueTypeRep<T>>::value, "T must be unsigned"); - static_assert(std::is_signed<E>::value, "E must be signed"); - typedef typename std::make_unsigned<E>::type EU; - typedef typename std::make_signed<T>::type TS; - return ToRep(static_cast<TS>(Bitcast<E>(static_cast<EU>(v_rep)))); -} - -// i{32,64}.atomic.rmw(8,16,32}_u.xchg template <typename T> -ValueTypeRep<T> Xchg(ValueTypeRep<T> lhs_rep, ValueTypeRep<T> rhs_rep) { - return rhs_rep; +void WABT_VECTORCALL Thread::Push(T value) { + Push(Value::Make(value)); } -// i(8,16,32,64) f(32,64) X (2,4,8,16) splat ==> v128 -template <typename T, typename V> -ValueTypeRep<T> SimdSplat(V lane_data) { - // Calculate how many Lanes according to input lane data type. - int32_t lanes = sizeof(T) / sizeof(V); - - // Define SIMD data array by Lanes. - V simd_data[sizeof(T) / sizeof(V)]; - - // Constuct the Simd value by Land data and Lane nums. - for (int32_t i = 0; i < lanes; i++) { - simd_data[i] = lane_data; - } - - return ToRep(Bitcast<T>(simd_data)); +template <> +void Thread::Push<bool>(bool value) { + Push(Value::Make(static_cast<u32>(value ? 1 : 0))); } -// Simd instructions of Lane extract. -// value: input v128 value. -// typename T: lane data type. -template <typename R, typename V, typename T> -ValueTypeRep<R> SimdExtractLane(V value, uint32_t laneidx) { - // Calculate how many Lanes according to input lane data type. - constexpr int32_t lanes = sizeof(V) / sizeof(T); - - // Define SIMD data array for Simd add by Lanes. - T simd_data_0[lanes]; - - // Convert intput SIMD data to array. - memcpy(simd_data_0, &value, sizeof(V)); - - return ToRep(static_cast<R>(simd_data_0[laneidx])); +void Thread::Push(Value value) { + values_.push_back(value); } -// Simd instructions of Lane replace. -// value: input v128 value. lane_val: input lane data. -// typename T: lane data type. -template <typename R, typename V, typename T> -ValueTypeRep<R> SimdReplaceLane(V value, uint32_t lane_idx, T lane_val) { - // Calculate how many Lanes according to input lane data type. - constexpr int32_t lanes = sizeof(V) / sizeof(T); - - // Define SIMD data array for Simd add by Lanes. - T simd_data_0[lanes]; - - // Convert intput SIMD data to array. - memcpy(simd_data_0, &value, sizeof(V)); - - // Replace the indicated lane. - simd_data_0[lane_idx] = lane_val; - - return ToRep(Bitcast<R>(simd_data_0)); -} - -bool Environment::FuncSignaturesAreEqual(Index sig_index_0, - Index sig_index_1) const { - if (sig_index_0 == sig_index_1) { - return true; - } - const FuncSignature* sig_0 = &sigs_[sig_index_0]; - const FuncSignature* sig_1 = &sigs_[sig_index_1]; - return sig_0->param_types == sig_1->param_types && - sig_0->result_types == sig_1->result_types; +void Thread::Push(Ref ref) { + refs_.push_back(values_.size()); + values_.push_back(Value::Make(ref)); } -Result Thread::CallHost(HostFunc* func) { - FuncSignature* sig = &env_->sigs_[func->sig_index]; +RunResult Thread::StepInternal(Trap::Ptr* out_trap) { + using O = Opcode; - size_t num_params = sig->param_types.size(); - size_t num_results = sig->result_types.size(); - TypedValues params(num_params); - TypedValues results(num_results); + u32& pc = frames_.back().offset; + auto& istream = mod_->desc().istream; - for (size_t i = num_params; i > 0; --i) - params[i - 1] = {sig->param_types[i - 1], Pop()}; - - for (size_t i = 0; i < num_results; ++i) { - results[i].type = sig->result_types[i]; - results[i].SetZero(); - } - - Result call_result = func->callback(func, sig, params, results); - TRAP_UNLESS(call_result.ok(), HostTrapped); - - if (results.size() != num_results) { - TRAP_MSG(HostResultTypeMismatch, - "expected %" PRIzd " results but got %" PRIzd, num_results, - results.size()); - } - for (size_t i = 0; i < num_results; ++i) { - if (TypeChecker::CheckType(results[i].type, sig->result_types[i]) != - wabt::Result::Ok) { - TRAP_MSG(HostResultTypeMismatch, "result mistmatch at %" PRIzx, i); - } - CHECK_TRAP(Push(results[i].value)); + if (trace_stream_) { + istream.Trace(trace_stream_, pc, trace_source_.get()); } - return ResultType::Ok; -} - -Result Thread::Run(int num_instructions) { - Result result = ResultType::Ok; - - const uint8_t* istream = GetIstream(); - const uint8_t* pc = &istream[pc_]; - for (int i = 0; i < num_instructions; ++i) { - Opcode opcode = ReadOpcode(&pc); - assert(!opcode.IsInvalid()); - switch (opcode) { - case Opcode::SelectT: - case Opcode::Select: { - uint32_t cond = Pop<uint32_t>(); - Value false_ = Pop(); - Value true_ = Pop(); - CHECK_TRAP(Push(cond ? true_ : false_)); - break; - } - - case Opcode::Br: - GOTO(ReadU32(&pc)); - break; + auto instr = istream.Read(&pc); + switch (instr.op) { + case O::Unreachable: + return TRAP("unreachable executed"); - case Opcode::BrIf: { - IstreamOffset new_pc = ReadU32(&pc); - if (Pop<uint32_t>()) { - GOTO(new_pc); - } - break; - } - - case Opcode::BrTable: { - Index num_targets = ReadU32(&pc); - IstreamOffset table_offset = ReadU32(&pc); - uint32_t key = Pop<uint32_t>(); - IstreamOffset key_offset = - (key >= num_targets ? num_targets : key) * WABT_TABLE_ENTRY_SIZE; - const uint8_t* entry = istream + table_offset + key_offset; - IstreamOffset new_pc; - uint32_t drop_count; - uint32_t keep_count; - ReadTableEntryAt(entry, &new_pc, &drop_count, &keep_count); - DropKeep(drop_count, keep_count); - GOTO(new_pc); - break; - } - - case Opcode::Return: - if (call_stack_top_ == 0) { - result = ResultType::Returned; - goto exit_loop; - } - GOTO(PopCall()); - break; - - case Opcode::Unreachable: - TRAP(Unreachable); - break; - - case Opcode::I32Const: - CHECK_TRAP(Push<uint32_t>(ReadU32(&pc))); - break; - - case Opcode::I64Const: - CHECK_TRAP(Push<uint64_t>(ReadU64(&pc))); - break; - - case Opcode::F32Const: - CHECK_TRAP(PushRep<float>(ReadU32(&pc))); - break; - - case Opcode::F64Const: - CHECK_TRAP(PushRep<double>(ReadU64(&pc))); - break; - - case Opcode::GlobalGet: { - Index index = ReadU32(&pc); - assert(index < env_->globals_.size()); - CHECK_TRAP(Push(env_->globals_[index].typed_value.value)); - break; - } - - case Opcode::GlobalSet: { - Index index = ReadU32(&pc); - assert(index < env_->globals_.size()); - env_->globals_[index].typed_value = {env_->globals_[index].type, Pop()}; - break; - } - - case Opcode::LocalGet: { - Value value = Pick(ReadU32(&pc)); - CHECK_TRAP(Push(value)); - break; - } - - case Opcode::LocalSet: { - Value value = Pop(); - Pick(ReadU32(&pc)) = value; - break; - } - - case Opcode::LocalTee: - Pick(ReadU32(&pc)) = Top(); - break; + case O::Br: + pc = instr.imm_u32; + break; - case Opcode::Call: { - IstreamOffset offset = ReadU32(&pc); - CHECK_TRAP(PushCall(pc)); - GOTO(offset); - break; - } - - case Opcode::CallIndirect: { - Table* table = ReadTable(&pc); - Index sig_index = ReadU32(&pc); - Index entry_index = Pop<uint32_t>(); - TRAP_IF(entry_index >= table->size(), UndefinedTableIndex); - Ref ref = table->entries[entry_index]; - TRAP_IF(ref.kind == RefType::Null, UninitializedTableElement); - assert(ref.kind == RefType::Func && ref.index != kInvalidIndex); - Func* func = env_->GetFunc(ref.index); - TRAP_UNLESS(env_->FuncSignaturesAreEqual(func->sig_index, sig_index), - IndirectCallSignatureMismatch); - if (func->is_host) { - CHECK_TRAP(CallHost(cast<HostFunc>(func))); - } else { - CHECK_TRAP(PushCall(pc)); - GOTO(cast<DefinedFunc>(func)->offset); - } - break; + case O::BrIf: + if (Pop<u32>()) { + pc = instr.imm_u32; } + break; - case Opcode::InterpCallHost: { - Index func_index = ReadU32(&pc); - CHECK_TRAP(CallHost(cast<HostFunc>(env_->funcs_[func_index].get()))); - break; + case O::BrTable: { + auto key = Pop<u32>(); + if (key >= instr.imm_u32) { + key = instr.imm_u32; } + pc += key * Istream::kBrTableEntrySize; + break; + } - case Opcode::ReturnCall: { - IstreamOffset offset = ReadU32(&pc); - GOTO(offset); + case O::Return: + return PopCall(); - break; + case O::Call: { + Ref new_func_ref = inst_->funcs()[instr.imm_u32]; + DefinedFunc::Ptr new_func{store_, new_func_ref}; + if (PushCall(new_func_ref, new_func->desc().code_offset, out_trap) == + RunResult::Trap) { + return RunResult::Trap; } + break; + } - case Opcode::ReturnCallIndirect:{ - Table* table = ReadTable(&pc); - Index sig_index = ReadU32(&pc); - Index entry_index = Pop<uint32_t>(); - TRAP_IF(entry_index >= table->size(), UndefinedTableIndex); - Ref ref = table->entries[entry_index]; - assert(ref.kind == RefType::Func); - TRAP_IF(ref.kind == RefType::Null, UninitializedTableElement); - assert(ref.kind == RefType::Func && ref.index != kInvalidIndex); - Func* func = env_->GetFunc(ref.index); - TRAP_UNLESS(env_->FuncSignaturesAreEqual(func->sig_index, sig_index), - IndirectCallSignatureMismatch); - if (func->is_host) { // Emulate a call/return for imported functions - CHECK_TRAP(CallHost(cast<HostFunc>(func))); - if (call_stack_top_ == 0) { - result = ResultType::Returned; - goto exit_loop; - } - GOTO(PopCall()); - } else { - GOTO(cast<DefinedFunc>(func)->offset); - } - break; + case O::CallIndirect: + case O::ReturnCallIndirect: { + Table::Ptr table{store_, inst_->tables()[instr.imm_u32x2.fst]}; + auto&& func_type = mod_->desc().func_types[instr.imm_u32x2.snd]; + auto entry = Pop<u32>(); + TRAP_IF(entry >= table->elements().size(), "undefined table index"); + auto new_func_ref = table->elements()[entry]; + TRAP_IF(new_func_ref == Ref::Null, "uninitialized table element"); + Func::Ptr new_func{store_, new_func_ref}; + TRAP_IF( + Failed(Match(new_func->type(), func_type, nullptr)), + "indirect call signature mismatch"); // TODO: don't use "signature" + if (instr.op == O::ReturnCallIndirect) { + return DoReturnCall(new_func, out_trap); + } else { + return DoCall(new_func, out_trap); } + } - case Opcode::I32Load8S: - CHECK_TRAP(Load<int8_t, uint32_t>(&pc)); - break; - - case Opcode::I32Load8U: - CHECK_TRAP(Load<uint8_t, uint32_t>(&pc)); - break; - - case Opcode::I32Load16S: - CHECK_TRAP(Load<int16_t, uint32_t>(&pc)); - break; - - case Opcode::I32Load16U: - CHECK_TRAP(Load<uint16_t, uint32_t>(&pc)); - break; - - case Opcode::I64Load8S: - CHECK_TRAP(Load<int8_t, uint64_t>(&pc)); - break; - - case Opcode::I64Load8U: - CHECK_TRAP(Load<uint8_t, uint64_t>(&pc)); - break; - - case Opcode::I64Load16S: - CHECK_TRAP(Load<int16_t, uint64_t>(&pc)); - break; - - case Opcode::I64Load16U: - CHECK_TRAP(Load<uint16_t, uint64_t>(&pc)); - break; - - case Opcode::I64Load32S: - CHECK_TRAP(Load<int32_t, uint64_t>(&pc)); - break; - - case Opcode::I64Load32U: - CHECK_TRAP(Load<uint32_t, uint64_t>(&pc)); - break; - - case Opcode::I32Load: - CHECK_TRAP(Load<uint32_t>(&pc)); - break; - - case Opcode::I64Load: - CHECK_TRAP(Load<uint64_t>(&pc)); - break; - - case Opcode::F32Load: - CHECK_TRAP(Load<float>(&pc)); - break; - - case Opcode::F64Load: - CHECK_TRAP(Load<double>(&pc)); - break; - - case Opcode::I32Store8: - CHECK_TRAP(Store<uint8_t, uint32_t>(&pc)); - break; - - case Opcode::I32Store16: - CHECK_TRAP(Store<uint16_t, uint32_t>(&pc)); - break; - - case Opcode::I64Store8: - CHECK_TRAP(Store<uint8_t, uint64_t>(&pc)); - break; - - case Opcode::I64Store16: - CHECK_TRAP(Store<uint16_t, uint64_t>(&pc)); - break; - - case Opcode::I64Store32: - CHECK_TRAP(Store<uint32_t, uint64_t>(&pc)); - break; - - case Opcode::I32Store: - CHECK_TRAP(Store<uint32_t>(&pc)); - break; - - case Opcode::I64Store: - CHECK_TRAP(Store<uint64_t>(&pc)); - break; - - case Opcode::F32Store: - CHECK_TRAP(Store<float>(&pc)); - break; - - case Opcode::F64Store: - CHECK_TRAP(Store<double>(&pc)); - break; - - case Opcode::I32AtomicLoad8U: - CHECK_TRAP(AtomicLoad<uint8_t, uint32_t>(&pc)); - break; - - case Opcode::I32AtomicLoad16U: - CHECK_TRAP(AtomicLoad<uint16_t, uint32_t>(&pc)); - break; - - case Opcode::I64AtomicLoad8U: - CHECK_TRAP(AtomicLoad<uint8_t, uint64_t>(&pc)); - break; - - case Opcode::I64AtomicLoad16U: - CHECK_TRAP(AtomicLoad<uint16_t, uint64_t>(&pc)); - break; - - case Opcode::I64AtomicLoad32U: - CHECK_TRAP(AtomicLoad<uint32_t, uint64_t>(&pc)); - break; - - case Opcode::I32AtomicLoad: - CHECK_TRAP(AtomicLoad<uint32_t>(&pc)); - break; - - case Opcode::I64AtomicLoad: - CHECK_TRAP(AtomicLoad<uint64_t>(&pc)); - break; - - case Opcode::I32AtomicStore8: - CHECK_TRAP(AtomicStore<uint8_t, uint32_t>(&pc)); - break; - - case Opcode::I32AtomicStore16: - CHECK_TRAP(AtomicStore<uint16_t, uint32_t>(&pc)); - break; - - case Opcode::I64AtomicStore8: - CHECK_TRAP(AtomicStore<uint8_t, uint64_t>(&pc)); - break; - - case Opcode::I64AtomicStore16: - CHECK_TRAP(AtomicStore<uint16_t, uint64_t>(&pc)); - break; - - case Opcode::I64AtomicStore32: - CHECK_TRAP(AtomicStore<uint32_t, uint64_t>(&pc)); - break; - - case Opcode::I32AtomicStore: - CHECK_TRAP(AtomicStore<uint32_t>(&pc)); - break; - - case Opcode::I64AtomicStore: - CHECK_TRAP(AtomicStore<uint64_t>(&pc)); - break; - -#define ATOMIC_RMW(rmwop, func) \ - case Opcode::I32AtomicRmw##rmwop: \ - CHECK_TRAP(AtomicRmw<uint32_t, uint32_t>(func<uint32_t>, &pc)); \ - break; \ - case Opcode::I64AtomicRmw##rmwop: \ - CHECK_TRAP(AtomicRmw<uint64_t, uint64_t>(func<uint64_t>, &pc)); \ - break; \ - case Opcode::I32AtomicRmw8##rmwop##U: \ - CHECK_TRAP(AtomicRmw<uint8_t, uint32_t>(func<uint32_t>, &pc)); \ - break; \ - case Opcode::I32AtomicRmw16##rmwop##U: \ - CHECK_TRAP(AtomicRmw<uint16_t, uint32_t>(func<uint32_t>, &pc)); \ - break; \ - case Opcode::I64AtomicRmw8##rmwop##U: \ - CHECK_TRAP(AtomicRmw<uint8_t, uint64_t>(func<uint64_t>, &pc)); \ - break; \ - case Opcode::I64AtomicRmw16##rmwop##U: \ - CHECK_TRAP(AtomicRmw<uint16_t, uint64_t>(func<uint64_t>, &pc)); \ - break; \ - case Opcode::I64AtomicRmw32##rmwop##U: \ - CHECK_TRAP(AtomicRmw<uint32_t, uint64_t>(func<uint64_t>, &pc)); \ - break /* no semicolon */ - - ATOMIC_RMW(Add, Add); - ATOMIC_RMW(Sub, Sub); - ATOMIC_RMW(And, IntAnd); - ATOMIC_RMW(Or, IntOr); - ATOMIC_RMW(Xor, IntXor); - ATOMIC_RMW(Xchg, Xchg); - -#undef ATOMIC_RMW - - case Opcode::I32AtomicRmwCmpxchg: - CHECK_TRAP(AtomicRmwCmpxchg<uint32_t, uint32_t>(&pc)); - break; + case O::Drop: + Pop(); + break; - case Opcode::I64AtomicRmwCmpxchg: - CHECK_TRAP(AtomicRmwCmpxchg<uint64_t, uint64_t>(&pc)); - break; + case O::Select: { + auto cond = Pop<u32>(); + Value false_ = Pop(); + Value true_ = Pop(); + Push(cond ? true_ : false_); + break; + } - case Opcode::I32AtomicRmw8CmpxchgU: - CHECK_TRAP(AtomicRmwCmpxchg<uint8_t, uint32_t>(&pc)); - break; + case O::LocalGet: + // TODO: need to mark whether this is a ref. + Push(Pick(instr.imm_u32)); + break; - case Opcode::I32AtomicRmw16CmpxchgU: - CHECK_TRAP(AtomicRmwCmpxchg<uint16_t, uint32_t>(&pc)); - break; + case O::LocalSet: { + Pick(instr.imm_u32) = Pick(1); + Pop(); + break; + } - case Opcode::I64AtomicRmw8CmpxchgU: - CHECK_TRAP(AtomicRmwCmpxchg<uint8_t, uint64_t>(&pc)); - break; + case O::LocalTee: + Pick(instr.imm_u32) = Pick(1); + break; - case Opcode::I64AtomicRmw16CmpxchgU: - CHECK_TRAP(AtomicRmwCmpxchg<uint16_t, uint64_t>(&pc)); - break; + case O::GlobalGet: { + // TODO: need to mark whether this is a ref. + Global::Ptr global{store_, inst_->globals()[instr.imm_u32]}; + Push(global->Get()); + break; + } - case Opcode::I64AtomicRmw32CmpxchgU: - CHECK_TRAP(AtomicRmwCmpxchg<uint32_t, uint64_t>(&pc)); - break; + case O::GlobalSet: { + Global::Ptr global{store_, inst_->globals()[instr.imm_u32]}; + global->UnsafeSet(Pop()); + break; + } - case Opcode::MemorySize: - CHECK_TRAP(Push<uint32_t>(ReadMemory(&pc)->page_limits.initial)); - break; + case O::I32Load: return DoLoad<u32>(instr, out_trap); + case O::I64Load: return DoLoad<u64>(instr, out_trap); + case O::F32Load: return DoLoad<f32>(instr, out_trap); + case O::F64Load: return DoLoad<f64>(instr, out_trap); + case O::I32Load8S: return DoLoad<s32, s8>(instr, out_trap); + case O::I32Load8U: return DoLoad<u32, u8>(instr, out_trap); + case O::I32Load16S: return DoLoad<s32, s16>(instr, out_trap); + case O::I32Load16U: return DoLoad<u32, u16>(instr, out_trap); + case O::I64Load8S: return DoLoad<s64, s8>(instr, out_trap); + case O::I64Load8U: return DoLoad<u64, u8>(instr, out_trap); + case O::I64Load16S: return DoLoad<s64, s16>(instr, out_trap); + case O::I64Load16U: return DoLoad<u64, u16>(instr, out_trap); + case O::I64Load32S: return DoLoad<s64, s32>(instr, out_trap); + case O::I64Load32U: return DoLoad<u64, u32>(instr, out_trap); + + case O::I32Store: return DoStore<u32>(instr, out_trap); + case O::I64Store: return DoStore<u64>(instr, out_trap); + case O::F32Store: return DoStore<f32>(instr, out_trap); + case O::F64Store: return DoStore<f64>(instr, out_trap); + case O::I32Store8: return DoStore<u32, u8>(instr, out_trap); + case O::I32Store16: return DoStore<u32, u16>(instr, out_trap); + case O::I64Store8: return DoStore<u64, u8>(instr, out_trap); + case O::I64Store16: return DoStore<u64, u16>(instr, out_trap); + case O::I64Store32: return DoStore<u64, u32>(instr, out_trap); + + case O::MemorySize: { + Memory::Ptr memory{store_, inst_->memories()[instr.imm_u32]}; + Push(memory->PageSize()); + break; + } - case Opcode::MemoryGrow: { - Memory* memory = ReadMemory(&pc); - uint32_t old_page_size = memory->page_limits.initial; - uint32_t grow_pages = Pop<uint32_t>(); - uint32_t new_page_size = old_page_size + grow_pages; - uint32_t max_page_size = memory->page_limits.has_max - ? memory->page_limits.max - : WABT_MAX_PAGES; - PUSH_NEG_1_AND_BREAK_IF(new_page_size > max_page_size); - PUSH_NEG_1_AND_BREAK_IF( - static_cast<uint64_t>(new_page_size) * WABT_PAGE_SIZE > UINT32_MAX); - memory->data.resize(new_page_size * WABT_PAGE_SIZE); - memory->page_limits.initial = new_page_size; - CHECK_TRAP(Push<uint32_t>(old_page_size)); - break; + case O::MemoryGrow: { + Memory::Ptr memory{store_, inst_->memories()[instr.imm_u32]}; + u32 old_size = memory->PageSize(); + if (Failed(memory->Grow(Pop<u32>()))) { + Push<s32>(-1); + } else { + Push<u32>(old_size); } + break; + } - case Opcode::I32Add: - CHECK_TRAP(Binop(Add<uint32_t>)); - break; - - case Opcode::I32Sub: - CHECK_TRAP(Binop(Sub<uint32_t>)); - break; - - case Opcode::I32Mul: - CHECK_TRAP(Binop(Mul<uint32_t>)); - break; - - case Opcode::I32DivS: - CHECK_TRAP(BinopTrap(IntDivS<int32_t>)); - break; - - case Opcode::I32DivU: - CHECK_TRAP(BinopTrap(IntDivU<uint32_t>)); - break; - - case Opcode::I32RemS: - CHECK_TRAP(BinopTrap(IntRemS<int32_t>)); - break; - - case Opcode::I32RemU: - CHECK_TRAP(BinopTrap(IntRemU<uint32_t>)); - break; - - case Opcode::I32And: - CHECK_TRAP(Binop(IntAnd<uint32_t>)); - break; - - case Opcode::I32Or: - CHECK_TRAP(Binop(IntOr<uint32_t>)); - break; - - case Opcode::I32Xor: - CHECK_TRAP(Binop(IntXor<uint32_t>)); - break; - - case Opcode::I32Shl: - CHECK_TRAP(Binop(IntShl<uint32_t>)); - break; - - case Opcode::I32ShrU: - CHECK_TRAP(Binop(IntShr<uint32_t>)); - break; - - case Opcode::I32ShrS: - CHECK_TRAP(Binop(IntShr<int32_t>)); - break; - - case Opcode::I32Eq: - CHECK_TRAP(Binop(Eq<uint32_t>)); - break; - - case Opcode::I32Ne: - CHECK_TRAP(Binop(Ne<uint32_t>)); - break; - - case Opcode::I32LtS: - CHECK_TRAP(Binop(Lt<int32_t>)); - break; - - case Opcode::I32LeS: - CHECK_TRAP(Binop(Le<int32_t>)); - break; - - case Opcode::I32LtU: - CHECK_TRAP(Binop(Lt<uint32_t>)); - break; - - case Opcode::I32LeU: - CHECK_TRAP(Binop(Le<uint32_t>)); - break; - - case Opcode::I32GtS: - CHECK_TRAP(Binop(Gt<int32_t>)); - break; - - case Opcode::I32GeS: - CHECK_TRAP(Binop(Ge<int32_t>)); - break; - - case Opcode::I32GtU: - CHECK_TRAP(Binop(Gt<uint32_t>)); - break; - - case Opcode::I32GeU: - CHECK_TRAP(Binop(Ge<uint32_t>)); - break; - - case Opcode::I32Clz: - CHECK_TRAP(Push<uint32_t>(Clz(Pop<uint32_t>()))); - break; - - case Opcode::I32Ctz: - CHECK_TRAP(Push<uint32_t>(Ctz(Pop<uint32_t>()))); - break; - - case Opcode::I32Popcnt: - CHECK_TRAP(Push<uint32_t>(Popcount(Pop<uint32_t>()))); - break; - - case Opcode::I32Eqz: - CHECK_TRAP(Unop(IntEqz<uint32_t, uint32_t>)); - break; - - case Opcode::I64Add: - CHECK_TRAP(Binop(Add<uint64_t>)); - break; - - case Opcode::I64Sub: - CHECK_TRAP(Binop(Sub<uint64_t>)); - break; - - case Opcode::I64Mul: - CHECK_TRAP(Binop(Mul<uint64_t>)); - break; - - case Opcode::I64DivS: - CHECK_TRAP(BinopTrap(IntDivS<int64_t>)); - break; - - case Opcode::I64DivU: - CHECK_TRAP(BinopTrap(IntDivU<uint64_t>)); - break; - - case Opcode::I64RemS: - CHECK_TRAP(BinopTrap(IntRemS<int64_t>)); - break; - - case Opcode::I64RemU: - CHECK_TRAP(BinopTrap(IntRemU<uint64_t>)); - break; - - case Opcode::I64And: - CHECK_TRAP(Binop(IntAnd<uint64_t>)); - break; - - case Opcode::I64Or: - CHECK_TRAP(Binop(IntOr<uint64_t>)); - break; - - case Opcode::I64Xor: - CHECK_TRAP(Binop(IntXor<uint64_t>)); - break; - - case Opcode::I64Shl: - CHECK_TRAP(Binop(IntShl<uint64_t>)); - break; - - case Opcode::I64ShrU: - CHECK_TRAP(Binop(IntShr<uint64_t>)); - break; - - case Opcode::I64ShrS: - CHECK_TRAP(Binop(IntShr<int64_t>)); - break; - - case Opcode::I64Eq: - CHECK_TRAP(Binop(Eq<uint64_t>)); - break; - - case Opcode::I64Ne: - CHECK_TRAP(Binop(Ne<uint64_t>)); - break; - - case Opcode::I64LtS: - CHECK_TRAP(Binop(Lt<int64_t>)); - break; - - case Opcode::I64LeS: - CHECK_TRAP(Binop(Le<int64_t>)); - break; - - case Opcode::I64LtU: - CHECK_TRAP(Binop(Lt<uint64_t>)); - break; - - case Opcode::I64LeU: - CHECK_TRAP(Binop(Le<uint64_t>)); - break; - - case Opcode::I64GtS: - CHECK_TRAP(Binop(Gt<int64_t>)); - break; - - case Opcode::I64GeS: - CHECK_TRAP(Binop(Ge<int64_t>)); - break; - - case Opcode::I64GtU: - CHECK_TRAP(Binop(Gt<uint64_t>)); - break; - - case Opcode::I64GeU: - CHECK_TRAP(Binop(Ge<uint64_t>)); - break; - - case Opcode::I64Clz: - CHECK_TRAP(Push<uint64_t>(Clz(Pop<uint64_t>()))); - break; - - case Opcode::I64Ctz: - CHECK_TRAP(Push<uint64_t>(Ctz(Pop<uint64_t>()))); - break; - - case Opcode::I64Popcnt: - CHECK_TRAP(Push<uint64_t>(Popcount(Pop<uint64_t>()))); - break; - - case Opcode::F32Add: - CHECK_TRAP(Binop(Add<float>)); - break; - - case Opcode::F32Sub: - CHECK_TRAP(Binop(Sub<float>)); - break; - - case Opcode::F32Mul: - CHECK_TRAP(Binop(Mul<float>)); - break; - - case Opcode::F32Div: - CHECK_TRAP(Binop(FloatDiv<float>)); - break; - - case Opcode::F32Min: - CHECK_TRAP(Binop(FloatMin<float>)); - break; - - case Opcode::F32Max: - CHECK_TRAP(Binop(FloatMax<float>)); - break; - - case Opcode::F32Abs: - CHECK_TRAP(Unop(FloatAbs<float>)); - break; - - case Opcode::F32Neg: - CHECK_TRAP(Unop(FloatNeg<float>)); - break; - - case Opcode::F32Copysign: - CHECK_TRAP(Binop(FloatCopySign<float>)); - break; - - case Opcode::F32Ceil: - CHECK_TRAP(Unop(FloatCeil<float>)); - break; - - case Opcode::F32Floor: - CHECK_TRAP(Unop(FloatFloor<float>)); - break; - - case Opcode::F32Trunc: - CHECK_TRAP(Unop(FloatTrunc<float>)); - break; - - case Opcode::F32Nearest: - CHECK_TRAP(Unop(FloatNearest<float>)); - break; - - case Opcode::F32Sqrt: - CHECK_TRAP(Unop(FloatSqrt<float>)); - break; - - case Opcode::F32Eq: - CHECK_TRAP(Binop(Eq<float>)); - break; - - case Opcode::F32Ne: - CHECK_TRAP(Binop(Ne<float>)); - break; - - case Opcode::F32Lt: - CHECK_TRAP(Binop(Lt<float>)); - break; - - case Opcode::F32Le: - CHECK_TRAP(Binop(Le<float>)); - break; - - case Opcode::F32Gt: - CHECK_TRAP(Binop(Gt<float>)); - break; - - case Opcode::F32Ge: - CHECK_TRAP(Binop(Ge<float>)); - break; - - case Opcode::F64Add: - CHECK_TRAP(Binop(Add<double>)); - break; - - case Opcode::F64Sub: - CHECK_TRAP(Binop(Sub<double>)); - break; - - case Opcode::F64Mul: - CHECK_TRAP(Binop(Mul<double>)); - break; - - case Opcode::F64Div: - CHECK_TRAP(Binop(FloatDiv<double>)); - break; - - case Opcode::F64Min: - CHECK_TRAP(Binop(FloatMin<double>)); - break; - - case Opcode::F64Max: - CHECK_TRAP(Binop(FloatMax<double>)); - break; - - case Opcode::F64Abs: - CHECK_TRAP(Unop(FloatAbs<double>)); - break; - - case Opcode::F64Neg: - CHECK_TRAP(Unop(FloatNeg<double>)); - break; - - case Opcode::F64Copysign: - CHECK_TRAP(Binop(FloatCopySign<double>)); - break; - - case Opcode::F64Ceil: - CHECK_TRAP(Unop(FloatCeil<double>)); - break; - - case Opcode::F64Floor: - CHECK_TRAP(Unop(FloatFloor<double>)); - break; - - case Opcode::F64Trunc: - CHECK_TRAP(Unop(FloatTrunc<double>)); - break; - - case Opcode::F64Nearest: - CHECK_TRAP(Unop(FloatNearest<double>)); - break; - - case Opcode::F64Sqrt: - CHECK_TRAP(Unop(FloatSqrt<double>)); - break; - - case Opcode::F64Eq: - CHECK_TRAP(Binop(Eq<double>)); - break; - - case Opcode::F64Ne: - CHECK_TRAP(Binop(Ne<double>)); - break; - - case Opcode::F64Lt: - CHECK_TRAP(Binop(Lt<double>)); - break; - - case Opcode::F64Le: - CHECK_TRAP(Binop(Le<double>)); - break; - - case Opcode::F64Gt: - CHECK_TRAP(Binop(Gt<double>)); - break; - - case Opcode::F64Ge: - CHECK_TRAP(Binop(Ge<double>)); - break; - - case Opcode::I32TruncF32S: - CHECK_TRAP(UnopTrap(IntTrunc<int32_t, float>)); - break; - - case Opcode::I32TruncSatF32S: - CHECK_TRAP(Unop(IntTruncSat<int32_t, float>)); - break; - - case Opcode::I32TruncF64S: - CHECK_TRAP(UnopTrap(IntTrunc<int32_t, double>)); - break; - - case Opcode::I32TruncSatF64S: - CHECK_TRAP(Unop(IntTruncSat<int32_t, double>)); - break; - - case Opcode::I32TruncF32U: - CHECK_TRAP(UnopTrap(IntTrunc<uint32_t, float>)); - break; - - case Opcode::I32TruncSatF32U: - CHECK_TRAP(Unop(IntTruncSat<uint32_t, float>)); - break; - - case Opcode::I32TruncF64U: - CHECK_TRAP(UnopTrap(IntTrunc<uint32_t, double>)); - break; - - case Opcode::I32TruncSatF64U: - CHECK_TRAP(Unop(IntTruncSat<uint32_t, double>)); - break; - - case Opcode::I32WrapI64: - CHECK_TRAP(Push<uint32_t>(Pop<uint64_t>())); - break; - - case Opcode::I64TruncF32S: - CHECK_TRAP(UnopTrap(IntTrunc<int64_t, float>)); - break; - - case Opcode::I64TruncSatF32S: - CHECK_TRAP(Unop(IntTruncSat<int64_t, float>)); - break; - - case Opcode::I64TruncF64S: - CHECK_TRAP(UnopTrap(IntTrunc<int64_t, double>)); - break; - - case Opcode::I64TruncSatF64S: - CHECK_TRAP(Unop(IntTruncSat<int64_t, double>)); - break; - - case Opcode::I64TruncF32U: - CHECK_TRAP(UnopTrap(IntTrunc<uint64_t, float>)); - break; - - case Opcode::I64TruncSatF32U: - CHECK_TRAP(Unop(IntTruncSat<uint64_t, float>)); - break; - - case Opcode::I64TruncF64U: - CHECK_TRAP(UnopTrap(IntTrunc<uint64_t, double>)); - break; - - case Opcode::I64TruncSatF64U: - CHECK_TRAP(Unop(IntTruncSat<uint64_t, double>)); - break; - - case Opcode::I64ExtendI32S: - CHECK_TRAP(Push<uint64_t>(Pop<int32_t>())); - break; - - case Opcode::I64ExtendI32U: - CHECK_TRAP(Push<uint64_t>(Pop<uint32_t>())); - break; - - case Opcode::F32ConvertI32S: - CHECK_TRAP(Push<float>(Pop<int32_t>())); - break; - - case Opcode::F32ConvertI32U: - CHECK_TRAP(Push<float>(Pop<uint32_t>())); - break; - - case Opcode::F32ConvertI64S: - CHECK_TRAP(Push<float>(wabt_convert_int64_to_float(Pop<int64_t>()))); - break; - - case Opcode::F32ConvertI64U: - CHECK_TRAP(Push<float>(wabt_convert_uint64_to_float(Pop<uint64_t>()))); - break; + case O::I32Const: Push(instr.imm_u32); break; + case O::F32Const: Push(instr.imm_f32); break; + case O::I64Const: Push(instr.imm_u64); break; + case O::F64Const: Push(instr.imm_f64); break; + + case O::I32Eqz: return DoUnop(IntEqz<u32>); + case O::I32Eq: return DoBinop(Eq<u32>); + case O::I32Ne: return DoBinop(Ne<u32>); + case O::I32LtS: return DoBinop(Lt<s32>); + case O::I32LtU: return DoBinop(Lt<u32>); + case O::I32GtS: return DoBinop(Gt<s32>); + case O::I32GtU: return DoBinop(Gt<u32>); + case O::I32LeS: return DoBinop(Le<s32>); + case O::I32LeU: return DoBinop(Le<u32>); + case O::I32GeS: return DoBinop(Ge<s32>); + case O::I32GeU: return DoBinop(Ge<u32>); + + case O::I64Eqz: return DoUnop(IntEqz<u64>); + case O::I64Eq: return DoBinop(Eq<u64>); + case O::I64Ne: return DoBinop(Ne<u64>); + case O::I64LtS: return DoBinop(Lt<s64>); + case O::I64LtU: return DoBinop(Lt<u64>); + case O::I64GtS: return DoBinop(Gt<s64>); + case O::I64GtU: return DoBinop(Gt<u64>); + case O::I64LeS: return DoBinop(Le<s64>); + case O::I64LeU: return DoBinop(Le<u64>); + case O::I64GeS: return DoBinop(Ge<s64>); + case O::I64GeU: return DoBinop(Ge<u64>); + + case O::F32Eq: return DoBinop(Eq<f32>); + case O::F32Ne: return DoBinop(Ne<f32>); + case O::F32Lt: return DoBinop(Lt<f32>); + case O::F32Gt: return DoBinop(Gt<f32>); + case O::F32Le: return DoBinop(Le<f32>); + case O::F32Ge: return DoBinop(Ge<f32>); + + case O::F64Eq: return DoBinop(Eq<f64>); + case O::F64Ne: return DoBinop(Ne<f64>); + case O::F64Lt: return DoBinop(Lt<f64>); + case O::F64Gt: return DoBinop(Gt<f64>); + case O::F64Le: return DoBinop(Le<f64>); + case O::F64Ge: return DoBinop(Ge<f64>); + + case O::I32Clz: return DoUnop(IntClz<u32>); + case O::I32Ctz: return DoUnop(IntCtz<u32>); + case O::I32Popcnt: return DoUnop(IntPopcnt<u32>); + case O::I32Add: return DoBinop(Add<u32>); + case O::I32Sub: return DoBinop(Sub<u32>); + case O::I32Mul: return DoBinop(Mul<u32>); + case O::I32DivS: return DoBinop(IntDiv<s32>, out_trap); + case O::I32DivU: return DoBinop(IntDiv<u32>, out_trap); + case O::I32RemS: return DoBinop(IntRem<s32>, out_trap); + case O::I32RemU: return DoBinop(IntRem<u32>, out_trap); + case O::I32And: return DoBinop(IntAnd<u32>); + case O::I32Or: return DoBinop(IntOr<u32>); + case O::I32Xor: return DoBinop(IntXor<u32>); + case O::I32Shl: return DoBinop(IntShl<u32>); + case O::I32ShrS: return DoBinop(IntShr<s32>); + case O::I32ShrU: return DoBinop(IntShr<u32>); + case O::I32Rotl: return DoBinop(IntRotl<u32>); + case O::I32Rotr: return DoBinop(IntRotr<u32>); + + case O::I64Clz: return DoUnop(IntClz<u64>); + case O::I64Ctz: return DoUnop(IntCtz<u64>); + case O::I64Popcnt: return DoUnop(IntPopcnt<u64>); + case O::I64Add: return DoBinop(Add<u64>); + case O::I64Sub: return DoBinop(Sub<u64>); + case O::I64Mul: return DoBinop(Mul<u64>); + case O::I64DivS: return DoBinop(IntDiv<s64>, out_trap); + case O::I64DivU: return DoBinop(IntDiv<u64>, out_trap); + case O::I64RemS: return DoBinop(IntRem<s64>, out_trap); + case O::I64RemU: return DoBinop(IntRem<u64>, out_trap); + case O::I64And: return DoBinop(IntAnd<u64>); + case O::I64Or: return DoBinop(IntOr<u64>); + case O::I64Xor: return DoBinop(IntXor<u64>); + case O::I64Shl: return DoBinop(IntShl<u64>); + case O::I64ShrS: return DoBinop(IntShr<s64>); + case O::I64ShrU: return DoBinop(IntShr<u64>); + case O::I64Rotl: return DoBinop(IntRotl<u64>); + case O::I64Rotr: return DoBinop(IntRotr<u64>); + + case O::F32Abs: return DoUnop(FloatAbs<f32>); + case O::F32Neg: return DoUnop(FloatNeg<f32>); + case O::F32Ceil: return DoUnop(FloatCeil<f32>); + case O::F32Floor: return DoUnop(FloatFloor<f32>); + case O::F32Trunc: return DoUnop(FloatTrunc<f32>); + case O::F32Nearest: return DoUnop(FloatNearest<f32>); + case O::F32Sqrt: return DoUnop(FloatSqrt<f32>); + case O::F32Add: return DoBinop(Add<f32>); + case O::F32Sub: return DoBinop(Sub<f32>); + case O::F32Mul: return DoBinop(Mul<f32>); + case O::F32Div: return DoBinop(FloatDiv<f32>); + case O::F32Min: return DoBinop(FloatMin<f32>); + case O::F32Max: return DoBinop(FloatMax<f32>); + case O::F32Copysign: return DoBinop(FloatCopysign<f32>); + + case O::F64Abs: return DoUnop(FloatAbs<f64>); + case O::F64Neg: return DoUnop(FloatNeg<f64>); + case O::F64Ceil: return DoUnop(FloatCeil<f64>); + case O::F64Floor: return DoUnop(FloatFloor<f64>); + case O::F64Trunc: return DoUnop(FloatTrunc<f64>); + case O::F64Nearest: return DoUnop(FloatNearest<f64>); + case O::F64Sqrt: return DoUnop(FloatSqrt<f64>); + case O::F64Add: return DoBinop(Add<f64>); + case O::F64Sub: return DoBinop(Sub<f64>); + case O::F64Mul: return DoBinop(Mul<f64>); + case O::F64Div: return DoBinop(FloatDiv<f64>); + case O::F64Min: return DoBinop(FloatMin<f64>); + case O::F64Max: return DoBinop(FloatMax<f64>); + case O::F64Copysign: return DoBinop(FloatCopysign<f64>); + + case O::I32WrapI64: return DoConvert<u32, u64>(out_trap); + case O::I32TruncF32S: return DoConvert<s32, f32>(out_trap); + case O::I32TruncF32U: return DoConvert<u32, f32>(out_trap); + case O::I32TruncF64S: return DoConvert<s32, f64>(out_trap); + case O::I32TruncF64U: return DoConvert<u32, f64>(out_trap); + case O::I64ExtendI32S: return DoConvert<s64, s32>(out_trap); + case O::I64ExtendI32U: return DoConvert<u64, u32>(out_trap); + case O::I64TruncF32S: return DoConvert<s64, f32>(out_trap); + case O::I64TruncF32U: return DoConvert<u64, f32>(out_trap); + case O::I64TruncF64S: return DoConvert<s64, f64>(out_trap); + case O::I64TruncF64U: return DoConvert<u64, f64>(out_trap); + case O::F32ConvertI32S: return DoConvert<f32, s32>(out_trap); + case O::F32ConvertI32U: return DoConvert<f32, u32>(out_trap); + case O::F32ConvertI64S: return DoConvert<f32, s64>(out_trap); + case O::F32ConvertI64U: return DoConvert<f32, u64>(out_trap); + case O::F32DemoteF64: return DoConvert<f32, f64>(out_trap); + case O::F64ConvertI32S: return DoConvert<f64, s32>(out_trap); + case O::F64ConvertI32U: return DoConvert<f64, u32>(out_trap); + case O::F64ConvertI64S: return DoConvert<f64, s64>(out_trap); + case O::F64ConvertI64U: return DoConvert<f64, u64>(out_trap); + case O::F64PromoteF32: return DoConvert<f64, f32>(out_trap); + + case O::I32ReinterpretF32: return DoReinterpret<u32, f32>(); + case O::F32ReinterpretI32: return DoReinterpret<f32, u32>(); + case O::I64ReinterpretF64: return DoReinterpret<u64, f64>(); + case O::F64ReinterpretI64: return DoReinterpret<f64, u64>(); + + case O::I32Extend8S: return DoUnop(IntExtend<u32, 7>); + case O::I32Extend16S: return DoUnop(IntExtend<u32, 15>); + case O::I64Extend8S: return DoUnop(IntExtend<u64, 7>); + case O::I64Extend16S: return DoUnop(IntExtend<u64, 15>); + case O::I64Extend32S: return DoUnop(IntExtend<u64, 31>); + + case O::InterpAlloca: + values_.resize(values_.size() + instr.imm_u32); + // refs_ doesn't need to be updated; We may be allocating space for + // references, but they will be initialized to null, so it is OK if we + // don't mark them. + break; - case Opcode::F32DemoteF64: { - typedef FloatTraits<float> F32Traits; - - uint64_t value = PopRep<double>(); - if (WABT_LIKELY((IsConversionInRange<float, double>(value)))) { - CHECK_TRAP(Push<float>(FromRep<double>(value))); - } else if (IsInRangeF64DemoteF32RoundToF32Max(value)) { - CHECK_TRAP(PushRep<float>(F32Traits::kMax)); - } else if (IsInRangeF64DemoteF32RoundToNegF32Max(value)) { - CHECK_TRAP(PushRep<float>(F32Traits::kNegMax)); - } else if (FloatTraits<double>::IsNan(value)) { - CHECK_TRAP(PushRep<float>(F32Traits::kQuietNan)); - } else { - // Infinity. - uint32_t sign = (value >> 32) & F32Traits::kSignMask; - CHECK_TRAP(PushRep<float>(sign | F32Traits::kInf)); - } - break; + case O::InterpBrUnless: + if (!Pop<u32>()) { + pc = instr.imm_u32; } + break; - case Opcode::F32ReinterpretI32: - CHECK_TRAP(PushRep<float>(Pop<uint32_t>())); - break; - - case Opcode::F64ConvertI32S: - CHECK_TRAP(Push<double>(Pop<int32_t>())); - break; - - case Opcode::F64ConvertI32U: - CHECK_TRAP(Push<double>(Pop<uint32_t>())); - break; - - case Opcode::F64ConvertI64S: - CHECK_TRAP(Push<double>(wabt_convert_int64_to_double(Pop<int64_t>()))); - break; - - case Opcode::F64ConvertI64U: - CHECK_TRAP( - Push<double>(wabt_convert_uint64_to_double(Pop<uint64_t>()))); - break; + case O::InterpCallHost: { + Ref new_func_ref = inst_->funcs()[instr.imm_u32]; + Func::Ptr new_func{store_, new_func_ref}; + return DoCall(new_func, out_trap); + } - case Opcode::F64PromoteF32: { - uint32_t value = PopRep<float>(); - if (WABT_UNLIKELY(FloatTraits<float>::IsNan(value))) { - CHECK_TRAP(PushRep<double>(FloatTraits<double>::kQuietNan)); + case O::InterpDropKeep: { + auto drop = instr.imm_u32x2.fst; + auto keep = instr.imm_u32x2.snd; + // Shift kept refs down. + for (auto iter = refs_.rbegin(); iter != refs_.rend(); ++iter) { + if (*iter >= values_.size() - keep) { + *iter -= drop; } else { - CHECK_TRAP(Push<double>(Bitcast<float>(value))); + break; } - break; } + std::move(values_.end() - keep, values_.end(), + values_.end() - drop - keep); + values_.resize(values_.size() - drop); + break; + } - case Opcode::F64ReinterpretI64: - CHECK_TRAP(PushRep<double>(Pop<uint64_t>())); - break; - - case Opcode::I32ReinterpretF32: - CHECK_TRAP(Push<uint32_t>(PopRep<float>())); - break; - - case Opcode::I64ReinterpretF64: - CHECK_TRAP(Push<uint64_t>(PopRep<double>())); - break; - - case Opcode::I32Rotr: - CHECK_TRAP(Binop(IntRotr<uint32_t>)); - break; - - case Opcode::I32Rotl: - CHECK_TRAP(Binop(IntRotl<uint32_t>)); - break; - - case Opcode::I64Rotr: - CHECK_TRAP(Binop(IntRotr<uint64_t>)); - break; - - case Opcode::I64Rotl: - CHECK_TRAP(Binop(IntRotl<uint64_t>)); - break; - - case Opcode::I64Eqz: - CHECK_TRAP(Unop(IntEqz<uint32_t, uint64_t>)); - break; - - case Opcode::I32Extend8S: - CHECK_TRAP(Unop(IntExtendS<uint32_t, int8_t>)); - break; - - case Opcode::I32Extend16S: - CHECK_TRAP(Unop(IntExtendS<uint32_t, int16_t>)); - break; - - case Opcode::I64Extend8S: - CHECK_TRAP(Unop(IntExtendS<uint64_t, int8_t>)); - break; - - case Opcode::I64Extend16S: - CHECK_TRAP(Unop(IntExtendS<uint64_t, int16_t>)); - break; - - case Opcode::I64Extend32S: - CHECK_TRAP(Unop(IntExtendS<uint64_t, int32_t>)); - break; - - case Opcode::InterpAlloca: { - uint32_t old_value_stack_top = value_stack_top_; - size_t count = ReadU32(&pc); - value_stack_top_ += count; - CHECK_STACK(); - memset(&value_stack_[old_value_stack_top], 0, count * sizeof(Value)); - break; - } - - case Opcode::InterpBrUnless: { - IstreamOffset new_pc = ReadU32(&pc); - if (!Pop<uint32_t>()) { - GOTO(new_pc); - } - break; - } - - case Opcode::Drop: - (void)Pop(); - break; - - case Opcode::InterpDropKeep: { - uint32_t drop_count = ReadU32(&pc); - uint32_t keep_count = ReadU32(&pc); - DropKeep(drop_count, keep_count); - break; - } - - case Opcode::Nop: - break; - - case Opcode::I32AtomicWait: - case Opcode::I64AtomicWait: - case Opcode::AtomicNotify: - // TODO(binji): Implement. - TRAP(Unreachable); - break; - - case Opcode::V128Const: { - CHECK_TRAP(PushRep<v128>(ReadV128(&pc))); - break; - } - - case Opcode::V128Load: - CHECK_TRAP(Load<v128>(&pc)); - break; - - case Opcode::V128Store: - CHECK_TRAP(Store<v128>(&pc)); - break; - - case Opcode::I8X16Splat: { - uint8_t lane_data = Pop<uint32_t>(); - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint8_t>(lane_data))); - break; - } - - case Opcode::I16X8Splat: { - uint16_t lane_data = Pop<uint32_t>(); - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint16_t>(lane_data))); - break; - } - - case Opcode::I32X4Splat: { - uint32_t lane_data = Pop<uint32_t>(); - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint32_t>(lane_data))); - break; - } - - case Opcode::I64X2Splat: { - uint64_t lane_data = Pop<uint64_t>(); - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint64_t>(lane_data))); - break; - } - - case Opcode::F32X4Splat: { - float lane_data = Pop<float>(); - CHECK_TRAP(Push<v128>(SimdSplat<v128, float>(lane_data))); - break; - } - - case Opcode::F64X2Splat: { - double lane_data = Pop<double>(); - CHECK_TRAP(Push<v128>(SimdSplat<v128, double>(lane_data))); - break; - } - - case Opcode::I8X16ExtractLaneS: { - v128 lane_val = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(PushRep<int32_t>( - SimdExtractLane<int32_t, v128, int8_t>(lane_val, lane_idx))); - break; - } - - case Opcode::I8X16ExtractLaneU: { - v128 lane_val = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(PushRep<int32_t>( - SimdExtractLane<int32_t, v128, uint8_t>(lane_val, lane_idx))); - break; - } - - case Opcode::I16X8ExtractLaneS: { - v128 lane_val = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(PushRep<int32_t>( - SimdExtractLane<int32_t, v128, int16_t>(lane_val, lane_idx))); - break; - } - - case Opcode::I16X8ExtractLaneU: { - v128 lane_val = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(PushRep<int32_t>( - SimdExtractLane<int32_t, v128, uint16_t>(lane_val, lane_idx))); - break; - } - - case Opcode::I32X4ExtractLane: { - v128 lane_val = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(PushRep<int32_t>( - SimdExtractLane<int32_t, v128, int32_t>(lane_val, lane_idx))); - break; - } - - case Opcode::I64X2ExtractLane: { - v128 lane_val = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(PushRep<int64_t>( - SimdExtractLane<int64_t, v128, int64_t>(lane_val, lane_idx))); - break; - } - - case Opcode::F32X4ExtractLane: { - v128 lane_val = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(PushRep<float>( - SimdExtractLane<int32_t, v128, int32_t>(lane_val, lane_idx))); - break; - } - - case Opcode::F64X2ExtractLane: { - v128 lane_val = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(PushRep<double>( - SimdExtractLane<int64_t, v128, int64_t>(lane_val, lane_idx))); - break; - } - - case Opcode::I8X16ReplaceLane: { - int8_t lane_val = static_cast<int8_t>(Pop<int32_t>()); - v128 value = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(Push<v128>( - SimdReplaceLane<v128, v128, int8_t>(value, lane_idx, lane_val))); - break; - } - - case Opcode::I16X8ReplaceLane: { - int16_t lane_val = static_cast<int16_t>(Pop<int32_t>()); - v128 value = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(Push<v128>( - SimdReplaceLane<v128, v128, int16_t>(value, lane_idx, lane_val))); - break; - } - - case Opcode::I32X4ReplaceLane: { - int32_t lane_val = Pop<int32_t>(); - v128 value = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(Push<v128>( - SimdReplaceLane<v128, v128, int32_t>(value, lane_idx, lane_val))); - break; - } - - case Opcode::I64X2ReplaceLane: { - int64_t lane_val = Pop<int64_t>(); - v128 value = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(Push<v128>( - SimdReplaceLane<v128, v128, int64_t>(value, lane_idx, lane_val))); - break; - } - - case Opcode::F32X4ReplaceLane: { - float lane_val = Pop<float>(); - v128 value = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(Push<v128>( - SimdReplaceLane<v128, v128, float>(value, lane_idx, lane_val))); - break; - } - - case Opcode::F64X2ReplaceLane: { - double lane_val = Pop<double>(); - v128 value = static_cast<v128>(Pop<v128>()); - uint32_t lane_idx = ReadU8(&pc); - CHECK_TRAP(Push<v128>( - SimdReplaceLane<v128, v128, double>(value, lane_idx, lane_val))); - break; - } - - case Opcode::V8X16Swizzle: { - const int32_t lanes = 16; - // Define SIMD data array for SIMD add by lanes. - int8_t simd_data_ret[lanes]; - int8_t simd_data[lanes]; - int8_t simd_swizzle[lanes]; - - v128 v2 = PopRep<v128>(); - v128 v1 = PopRep<v128>(); - - // Convert input SIMD data to array. - memcpy(simd_data, &v1, sizeof(v128)); - memcpy(simd_swizzle, &v2, sizeof(v128)); - - // Construct the SIMD value by lane data and lane nums. - for (int32_t i = 0; i < lanes; i++) { - uint8_t lane_idx = simd_swizzle[i]; - simd_data_ret[i] = lane_idx < lanes ? simd_data[lane_idx] : 0; - } - - CHECK_TRAP(PushRep<v128>(Bitcast<v128>(simd_data_ret))); - break; - } - - case Opcode::V8X16Shuffle: { - const int32_t lanes = 16; - // Define SIMD data array for SIMD add by lanes. - int8_t simd_data_ret[lanes]; - int8_t simd_data_0[lanes]; - int8_t simd_data_1[lanes]; - int8_t simd_shuffle[lanes]; - - v128 v2 = PopRep<v128>(); - v128 v1 = PopRep<v128>(); - v128 shuffle_imm = ReadV128(&pc); - - // Convert input SIMD data to array. - memcpy(simd_data_0, &v1, sizeof(v128)); - memcpy(simd_data_1, &v2, sizeof(v128)); - memcpy(simd_shuffle, &shuffle_imm, sizeof(v128)); - - // Constuct the SIMD value by lane data and lane nums. - for (int32_t i = 0; i < lanes; i++) { - int8_t lane_idx = simd_shuffle[i]; - simd_data_ret[i] = (lane_idx < lanes) ? simd_data_0[lane_idx] - : simd_data_1[lane_idx - lanes]; - } - - CHECK_TRAP(PushRep<v128>(Bitcast<v128>(simd_data_ret))); - break; - } - - case Opcode::V8X16LoadSplat: { - CHECK_TRAP(Load<uint8_t, uint32_t>(&pc)); - uint8_t lane_data = Pop<uint32_t>(); - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint8_t>(lane_data))); - break; - } - - case Opcode::V16X8LoadSplat: { - CHECK_TRAP(Load<uint16_t, uint32_t>(&pc)); - uint16_t lane_data = Pop<uint32_t>(); - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint16_t>(lane_data))); - break; - } - - case Opcode::V32X4LoadSplat: { - CHECK_TRAP(Load<uint32_t, uint32_t>(&pc)); - uint32_t lane_data = Pop<uint32_t>(); - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint32_t>(lane_data))); - break; - } - - case Opcode::V64X2LoadSplat: { - CHECK_TRAP(Load<uint64_t, uint64_t>(&pc)); - uint64_t lane_data = Pop<uint64_t>(); - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint64_t>(lane_data))); - break; - } - - case Opcode::I8X16Add: - CHECK_TRAP(SimdBinop<v128, uint8_t>(Add<uint32_t>)); - break; - - case Opcode::I16X8Add: - CHECK_TRAP(SimdBinop<v128, uint16_t>(Add<uint32_t>)); - break; - - case Opcode::I32X4Add: - CHECK_TRAP(SimdBinop<v128, uint32_t>(Add<uint32_t>)); - break; - - case Opcode::I64X2Add: - CHECK_TRAP(SimdBinop<v128, uint64_t>(Add<uint64_t>)); - break; - - case Opcode::I8X16Sub: - CHECK_TRAP(SimdBinop<v128, uint8_t>(Sub<uint32_t>)); - break; - - case Opcode::I16X8Sub: - CHECK_TRAP(SimdBinop<v128, uint16_t>(Sub<uint32_t>)); - break; - - case Opcode::I32X4Sub: - CHECK_TRAP(SimdBinop<v128, uint32_t>(Sub<uint32_t>)); - break; - - case Opcode::I64X2Sub: - CHECK_TRAP(SimdBinop<v128, uint64_t>(Sub<uint64_t>)); - break; - - case Opcode::I16X8Mul: - CHECK_TRAP(SimdBinop<v128, uint16_t>(Mul<uint32_t>)); - break; - - case Opcode::I32X4Mul: - CHECK_TRAP(SimdBinop<v128, uint32_t>(Mul<uint32_t>)); - break; - - case Opcode::I64X2Mul: - CHECK_TRAP(SimdBinop<v128, uint64_t>(Mul<uint64_t>)); - break; - - case Opcode::I8X16Neg: - CHECK_TRAP(SimdUnop<v128, int8_t>(IntNeg<int32_t>)); - break; - - case Opcode::I16X8Neg: - CHECK_TRAP(SimdUnop<v128, int16_t>(IntNeg<int32_t>)); - break; - - case Opcode::I32X4Neg: - CHECK_TRAP(SimdUnop<v128, int32_t>(IntNeg<int32_t>)); - break; - - case Opcode::I64X2Neg: - CHECK_TRAP(SimdUnop<v128, int64_t>(IntNeg<int64_t>)); - break; - - case Opcode::I8X16AddSaturateS: - CHECK_TRAP(SimdBinop<v128, int8_t>(AddSaturate<int32_t, int8_t>)); - break; - - case Opcode::I8X16AddSaturateU: - CHECK_TRAP(SimdBinop<v128, uint8_t>(AddSaturate<uint32_t, uint8_t>)); - break; - - case Opcode::I16X8AddSaturateS: - CHECK_TRAP(SimdBinop<v128, int16_t>(AddSaturate<int32_t, int16_t>)); - break; - - case Opcode::I16X8AddSaturateU: - CHECK_TRAP(SimdBinop<v128, uint16_t>(AddSaturate<uint32_t, uint16_t>)); - break; - - case Opcode::I8X16SubSaturateS: - CHECK_TRAP(SimdBinop<v128, int8_t>(SubSaturate<int32_t, int8_t>)); - break; - - case Opcode::I8X16SubSaturateU: - CHECK_TRAP(SimdBinop<v128, uint8_t>(SubSaturate<int32_t, uint8_t>)); - break; - - case Opcode::I16X8SubSaturateS: - CHECK_TRAP(SimdBinop<v128, int16_t>(SubSaturate<int32_t, int16_t>)); - break; - - case Opcode::I16X8SubSaturateU: - CHECK_TRAP(SimdBinop<v128, uint16_t>(SubSaturate<int32_t, uint16_t>)); - break; - - case Opcode::I8X16Shl: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 8; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint8_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, uint8_t>(IntShl<uint32_t>)); - break; - } - - case Opcode::I16X8Shl: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 16; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint16_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, uint16_t>(IntShl<uint32_t>)); - break; - } - - case Opcode::I32X4Shl: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 32; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint32_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, uint32_t>(IntShl<uint32_t>)); - break; - } - - case Opcode::I64X2Shl: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 64; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint64_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, uint64_t>(IntShl<uint64_t>)); - break; - } - - case Opcode::I8X16ShrS: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 8; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint8_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, int8_t>(IntShr<int32_t>)); - break; - } - - case Opcode::I8X16ShrU: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 8; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint8_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, uint8_t>(IntShr<uint32_t>)); - break; - } - - case Opcode::I16X8ShrS: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 16; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint16_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, int16_t>(IntShr<int32_t>)); - break; - } - - case Opcode::I16X8ShrU: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 16; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint16_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, uint16_t>(IntShr<uint32_t>)); - break; - } - - case Opcode::I32X4ShrS: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 32; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint32_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, int32_t>(IntShr<int32_t>)); - break; - } - - case Opcode::I32X4ShrU: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 32; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint32_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, uint32_t>(IntShr<uint32_t>)); - break; - } - - case Opcode::I64X2ShrS: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 64; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint64_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, int64_t>(IntShr<int64_t>)); - break; - } - - case Opcode::I64X2ShrU: { - uint32_t shift_count = Pop<uint32_t>(); - shift_count = shift_count % 64; - CHECK_TRAP(Push<v128>(SimdSplat<v128, uint64_t>(shift_count))); - CHECK_TRAP(SimdBinop<v128, uint64_t>(IntShr<uint64_t>)); - break; - } - - case Opcode::V128And: - CHECK_TRAP(SimdBinop<v128, uint64_t>(IntAnd<uint64_t>)); - break; - - case Opcode::V128Or: - CHECK_TRAP(SimdBinop<v128, uint64_t>(IntOr<uint64_t>)); - break; - - case Opcode::V128Xor: - CHECK_TRAP(SimdBinop<v128, uint64_t>(IntXor<uint64_t>)); - break; - - case Opcode::V128Not: - CHECK_TRAP(SimdUnop<v128, uint64_t>(IntNot<uint64_t>)); - break; - - case Opcode::V128BitSelect: { - // Follow Wasm Simd spec to compute V128BitSelect: - // v128.or(v128.and(v1, c), v128.and(v2, v128.not(c))) - v128 c_mask = PopRep<v128>(); - v128 v2 = PopRep<v128>(); - v128 v1 = PopRep<v128>(); - // 1. v128.and(v1, c) - CHECK_TRAP(Push<v128>(v1)); - CHECK_TRAP(Push<v128>(c_mask)); - CHECK_TRAP(SimdBinop<v128, uint64_t>(IntAnd<uint64_t>)); - // 2. v128.and(v2, v128.not(c)) - CHECK_TRAP(Push<v128>(v2)); - CHECK_TRAP(Push<v128>(c_mask)); - CHECK_TRAP(SimdUnop<v128, uint64_t>(IntNot<uint64_t>)); - CHECK_TRAP(SimdBinop<v128, uint64_t>(IntAnd<uint64_t>)); - // 3. v128.or( 1 , 2) - CHECK_TRAP(SimdBinop<v128, uint64_t>(IntOr<uint64_t>)); - break; - } - - case Opcode::I8X16AnyTrue: { - v128 value = PopRep<v128>(); - CHECK_TRAP(Push<int32_t>(SimdIsLaneTrue<v128, uint8_t>(value, 1))); - break; - } - - case Opcode::I16X8AnyTrue: { - v128 value = PopRep<v128>(); - CHECK_TRAP(Push<int32_t>(SimdIsLaneTrue<v128, uint16_t>(value, 1))); - break; - } - - case Opcode::I32X4AnyTrue: { - v128 value = PopRep<v128>(); - CHECK_TRAP(Push<int32_t>(SimdIsLaneTrue<v128, uint32_t>(value, 1))); - break; - } - - case Opcode::I8X16AllTrue: { - v128 value = PopRep<v128>(); - CHECK_TRAP(Push<int32_t>(SimdIsLaneTrue<v128, uint8_t>(value, 16))); - break; - } - - case Opcode::I16X8AllTrue: { - v128 value = PopRep<v128>(); - CHECK_TRAP(Push<int32_t>(SimdIsLaneTrue<v128, uint16_t>(value, 8))); - break; - } - - case Opcode::I32X4AllTrue: { - v128 value = PopRep<v128>(); - CHECK_TRAP(Push<int32_t>(SimdIsLaneTrue<v128, uint32_t>(value, 4))); - break; - } - - case Opcode::I8X16Eq: - CHECK_TRAP(SimdRelBinop<v128, int8_t>(Eq<int32_t>)); - break; - - case Opcode::I16X8Eq: - CHECK_TRAP(SimdRelBinop<v128, int16_t>(Eq<int32_t>)); - break; - - case Opcode::I32X4Eq: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Eq<int32_t>)); - break; - - case Opcode::F32X4Eq: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Eq<float>)); - break; - - case Opcode::F64X2Eq: - CHECK_TRAP(SimdRelBinop<v128, int64_t>(Eq<double>)); - break; - - case Opcode::I8X16Ne: - CHECK_TRAP(SimdRelBinop<v128, int8_t>(Ne<int32_t>)); - break; - - case Opcode::I16X8Ne: - CHECK_TRAP(SimdRelBinop<v128, int16_t>(Ne<int32_t>)); - break; - - case Opcode::I32X4Ne: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Ne<int32_t>)); - break; - - case Opcode::F32X4Ne: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Ne<float>)); - break; - - case Opcode::F64X2Ne: - CHECK_TRAP(SimdRelBinop<v128, int64_t>(Ne<double>)); - break; - - case Opcode::I8X16LtS: - CHECK_TRAP(SimdRelBinop<v128, int8_t>(Lt<int32_t>)); - break; - - case Opcode::I8X16LtU: - CHECK_TRAP(SimdRelBinop<v128, uint8_t>(Lt<uint32_t>)); - break; - - case Opcode::I16X8LtS: - CHECK_TRAP(SimdRelBinop<v128, int16_t>(Lt<int32_t>)); - break; - - case Opcode::I16X8LtU: - CHECK_TRAP(SimdRelBinop<v128, uint16_t>(Lt<uint32_t>)); - break; - - case Opcode::I32X4LtS: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Lt<int32_t>)); - break; - - case Opcode::I32X4LtU: - CHECK_TRAP(SimdRelBinop<v128, uint32_t>(Lt<uint32_t>)); - break; - - case Opcode::F32X4Lt: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Lt<float>)); - break; - - case Opcode::F64X2Lt: - CHECK_TRAP(SimdRelBinop<v128, int64_t>(Lt<double>)); - break; - - case Opcode::I8X16LeS: - CHECK_TRAP(SimdRelBinop<v128, int8_t>(Le<int32_t>)); - break; - - case Opcode::I8X16LeU: - CHECK_TRAP(SimdRelBinop<v128, uint8_t>(Le<uint32_t>)); - break; - - case Opcode::I16X8LeS: - CHECK_TRAP(SimdRelBinop<v128, int16_t>(Le<int32_t>)); - break; - - case Opcode::I16X8LeU: - CHECK_TRAP(SimdRelBinop<v128, uint16_t>(Le<uint32_t>)); - break; - - case Opcode::I32X4LeS: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Le<int32_t>)); - break; - - case Opcode::I32X4LeU: - CHECK_TRAP(SimdRelBinop<v128, uint32_t>(Le<uint32_t>)); - break; - - case Opcode::F32X4Le: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Le<float>)); - break; - - case Opcode::F64X2Le: - CHECK_TRAP(SimdRelBinop<v128, int64_t>(Le<double>)); - break; - - case Opcode::I8X16GtS: - CHECK_TRAP(SimdRelBinop<v128, int8_t>(Gt<int32_t>)); - break; - - case Opcode::I8X16GtU: - CHECK_TRAP(SimdRelBinop<v128, uint8_t>(Gt<uint32_t>)); - break; - - case Opcode::I16X8GtS: - CHECK_TRAP(SimdRelBinop<v128, int16_t>(Gt<int32_t>)); - break; - - case Opcode::I16X8GtU: - CHECK_TRAP(SimdRelBinop<v128, uint16_t>(Gt<uint32_t>)); - break; - - case Opcode::I32X4GtS: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Gt<int32_t>)); - break; - - case Opcode::I32X4GtU: - CHECK_TRAP(SimdRelBinop<v128, uint32_t>(Gt<uint32_t>)); - break; - - case Opcode::F32X4Gt: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Gt<float>)); - break; - - case Opcode::F64X2Gt: - CHECK_TRAP(SimdRelBinop<v128, int64_t>(Gt<double>)); - break; - - case Opcode::I8X16GeS: - CHECK_TRAP(SimdRelBinop<v128, int8_t>(Ge<int32_t>)); - break; - - case Opcode::I8X16GeU: - CHECK_TRAP(SimdRelBinop<v128, uint8_t>(Ge<uint32_t>)); - break; - - case Opcode::I16X8GeS: - CHECK_TRAP(SimdRelBinop<v128, int16_t>(Ge<int32_t>)); - break; - - case Opcode::I16X8GeU: - CHECK_TRAP(SimdRelBinop<v128, uint16_t>(Ge<uint32_t>)); - break; - - case Opcode::I32X4GeS: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Ge<int32_t>)); - break; - - case Opcode::I32X4GeU: - CHECK_TRAP(SimdRelBinop<v128, uint32_t>(Ge<uint32_t>)); - break; - - case Opcode::F32X4Ge: - CHECK_TRAP(SimdRelBinop<v128, int32_t>(Ge<float>)); - break; - - case Opcode::F64X2Ge: - CHECK_TRAP(SimdRelBinop<v128, int64_t>(Ge<double>)); - break; - - case Opcode::F32X4Neg: - CHECK_TRAP(SimdUnop<v128, int32_t>(FloatNeg<float>)); - break; - - case Opcode::F64X2Neg: - CHECK_TRAP(SimdUnop<v128, int64_t>(FloatNeg<double>)); - break; - - case Opcode::F32X4Abs: - CHECK_TRAP(SimdUnop<v128, int32_t>(FloatAbs<float>)); - break; - - case Opcode::F64X2Abs: - CHECK_TRAP(SimdUnop<v128, int64_t>(FloatAbs<double>)); - break; - - case Opcode::F32X4Min: - CHECK_TRAP(SimdBinop<v128, int32_t>(FloatMin<float>)); - break; - - case Opcode::F64X2Min: - CHECK_TRAP(SimdBinop<v128, int64_t>(FloatMin<double>)); - break; - - case Opcode::F32X4Max: - CHECK_TRAP(SimdBinop<v128, int32_t>(FloatMax<float>)); - break; - - case Opcode::F64X2Max: - CHECK_TRAP(SimdBinop<v128, int64_t>(FloatMax<double>)); - break; - - case Opcode::F32X4Add: - CHECK_TRAP(SimdBinop<v128, int32_t>(Add<float>)); - break; - - case Opcode::F64X2Add: - CHECK_TRAP(SimdBinop<v128, int64_t>(Add<double>)); - break; - - case Opcode::F32X4Sub: - CHECK_TRAP(SimdBinop<v128, int32_t>(Sub<float>)); - break; - - case Opcode::F64X2Sub: - CHECK_TRAP(SimdBinop<v128, int64_t>(Sub<double>)); - break; - - case Opcode::F32X4Div: - CHECK_TRAP(SimdBinop<v128, int32_t>(FloatDiv<float>)); - break; + case O::I32TruncSatF32S: return DoUnop(IntTruncSat<s32, f32>); + case O::I32TruncSatF32U: return DoUnop(IntTruncSat<u32, f32>); + case O::I32TruncSatF64S: return DoUnop(IntTruncSat<s32, f64>); + case O::I32TruncSatF64U: return DoUnop(IntTruncSat<u32, f64>); + case O::I64TruncSatF32S: return DoUnop(IntTruncSat<s64, f32>); + case O::I64TruncSatF32U: return DoUnop(IntTruncSat<u64, f32>); + case O::I64TruncSatF64S: return DoUnop(IntTruncSat<s64, f64>); + case O::I64TruncSatF64U: return DoUnop(IntTruncSat<u64, f64>); + + case O::MemoryInit: return DoMemoryInit(instr, out_trap); + case O::DataDrop: return DoDataDrop(instr); + case O::MemoryCopy: return DoMemoryCopy(instr, out_trap); + case O::MemoryFill: return DoMemoryFill(instr, out_trap); + + case O::TableInit: return DoTableInit(instr, out_trap); + case O::ElemDrop: return DoElemDrop(instr); + case O::TableCopy: return DoTableCopy(instr, out_trap); + case O::TableGet: return DoTableGet(instr, out_trap); + case O::TableSet: return DoTableSet(instr, out_trap); + case O::TableGrow: return DoTableGrow(instr, out_trap); + case O::TableSize: return DoTableSize(instr); + case O::TableFill: return DoTableFill(instr, out_trap); + + case O::RefNull: + Push(Ref::Null); + break; - case Opcode::F64X2Div: - CHECK_TRAP(SimdBinop<v128, int64_t>(FloatDiv<double>)); - break; + case O::RefIsNull: + Push(Pop<Ref>() == Ref::Null); + break; - case Opcode::F32X4Mul: - CHECK_TRAP(SimdBinop<v128, int32_t>(Mul<float>)); - break; + case O::RefFunc: + Push(inst_->funcs()[instr.imm_u32]); + break; - case Opcode::F64X2Mul: - CHECK_TRAP(SimdBinop<v128, int64_t>(Mul<double>)); - break; + case O::V128Load: return DoLoad<v128>(instr, out_trap); + case O::V128Store: return DoStore<v128>(instr, out_trap); - case Opcode::F32X4Sqrt: - CHECK_TRAP(SimdUnop<v128, int32_t>(FloatSqrt<float>)); - break; + case O::V128Const: + Push<v128>(instr.imm_v128); + break; - case Opcode::F64X2Sqrt: - CHECK_TRAP(SimdUnop<v128, int64_t>(FloatSqrt<double>)); - break; + case O::I8X16Splat: return DoSimdSplat<u8x16, u32>(); + case O::I8X16ExtractLaneS: return DoSimdExtract<s8x16, s32>(instr); + case O::I8X16ExtractLaneU: return DoSimdExtract<u8x16, u32>(instr); + case O::I8X16ReplaceLane: return DoSimdReplace<u8x16, u32>(instr); + case O::I16X8Splat: return DoSimdSplat<u16x8, u32>(); + case O::I16X8ExtractLaneS: return DoSimdExtract<s16x8, s32>(instr); + case O::I16X8ExtractLaneU: return DoSimdExtract<u16x8, u32>(instr); + case O::I16X8ReplaceLane: return DoSimdReplace<u16x8, u32>(instr); + case O::I32X4Splat: return DoSimdSplat<u32x4, u32>(); + case O::I32X4ExtractLane: return DoSimdExtract<s32x4, u32>(instr); + case O::I32X4ReplaceLane: return DoSimdReplace<u32x4, u32>(instr); + case O::I64X2Splat: return DoSimdSplat<u64x2, u64>(); + case O::I64X2ExtractLane: return DoSimdExtract<u64x2, u64>(instr); + case O::I64X2ReplaceLane: return DoSimdReplace<u64x2, u64>(instr); + case O::F32X4Splat: return DoSimdSplat<f32x4, f32>(); + case O::F32X4ExtractLane: return DoSimdExtract<f32x4, f32>(instr); + case O::F32X4ReplaceLane: return DoSimdReplace<f32x4, f32>(instr); + case O::F64X2Splat: return DoSimdSplat<f64x2, f64>(); + case O::F64X2ExtractLane: return DoSimdExtract<f64x2, f64>(instr); + case O::F64X2ReplaceLane: return DoSimdReplace<f64x2, f64>(instr); + + case O::I8X16Eq: return DoSimdBinop(EqMask<u8>); + case O::I8X16Ne: return DoSimdBinop(NeMask<u8>); + case O::I8X16LtS: return DoSimdBinop(LtMask<s8>); + case O::I8X16LtU: return DoSimdBinop(LtMask<u8>); + case O::I8X16GtS: return DoSimdBinop(GtMask<s8>); + case O::I8X16GtU: return DoSimdBinop(GtMask<u8>); + case O::I8X16LeS: return DoSimdBinop(LeMask<s8>); + case O::I8X16LeU: return DoSimdBinop(LeMask<u8>); + case O::I8X16GeS: return DoSimdBinop(GeMask<s8>); + case O::I8X16GeU: return DoSimdBinop(GeMask<u8>); + case O::I16X8Eq: return DoSimdBinop(EqMask<u16>); + case O::I16X8Ne: return DoSimdBinop(NeMask<u16>); + case O::I16X8LtS: return DoSimdBinop(LtMask<s16>); + case O::I16X8LtU: return DoSimdBinop(LtMask<u16>); + case O::I16X8GtS: return DoSimdBinop(GtMask<s16>); + case O::I16X8GtU: return DoSimdBinop(GtMask<u16>); + case O::I16X8LeS: return DoSimdBinop(LeMask<s16>); + case O::I16X8LeU: return DoSimdBinop(LeMask<u16>); + case O::I16X8GeS: return DoSimdBinop(GeMask<s16>); + case O::I16X8GeU: return DoSimdBinop(GeMask<u16>); + case O::I32X4Eq: return DoSimdBinop(EqMask<u32>); + case O::I32X4Ne: return DoSimdBinop(NeMask<u32>); + case O::I32X4LtS: return DoSimdBinop(LtMask<s32>); + case O::I32X4LtU: return DoSimdBinop(LtMask<u32>); + case O::I32X4GtS: return DoSimdBinop(GtMask<s32>); + case O::I32X4GtU: return DoSimdBinop(GtMask<u32>); + case O::I32X4LeS: return DoSimdBinop(LeMask<s32>); + case O::I32X4LeU: return DoSimdBinop(LeMask<u32>); + case O::I32X4GeS: return DoSimdBinop(GeMask<s32>); + case O::I32X4GeU: return DoSimdBinop(GeMask<u32>); + case O::F32X4Eq: return DoSimdBinop(EqMask<f32>); + case O::F32X4Ne: return DoSimdBinop(NeMask<f32>); + case O::F32X4Lt: return DoSimdBinop(LtMask<f32>); + case O::F32X4Gt: return DoSimdBinop(GtMask<f32>); + case O::F32X4Le: return DoSimdBinop(LeMask<f32>); + case O::F32X4Ge: return DoSimdBinop(GeMask<f32>); + case O::F64X2Eq: return DoSimdBinop(EqMask<f64>); + case O::F64X2Ne: return DoSimdBinop(NeMask<f64>); + case O::F64X2Lt: return DoSimdBinop(LtMask<f64>); + case O::F64X2Gt: return DoSimdBinop(GtMask<f64>); + case O::F64X2Le: return DoSimdBinop(LeMask<f64>); + case O::F64X2Ge: return DoSimdBinop(GeMask<f64>); + + case O::V128Not: return DoSimdUnop(IntNot<u64>); + case O::V128And: return DoSimdBinop(IntAnd<u64>); + case O::V128Or: return DoSimdBinop(IntOr<u64>); + case O::V128Xor: return DoSimdBinop(IntXor<u64>); + case O::V128BitSelect: return DoSimdBitSelect(); + + case O::I8X16Neg: return DoSimdUnop(IntNeg<u8>); + case O::I8X16AnyTrue: return DoSimdIsTrue<u8x16, 1>(); + case O::I8X16AllTrue: return DoSimdIsTrue<u8x16, 16>(); + case O::I8X16Shl: return DoSimdShift(IntShl<u8>); + case O::I8X16ShrS: return DoSimdShift(IntShr<s8>); + case O::I8X16ShrU: return DoSimdShift(IntShr<u8>); + case O::I8X16Add: return DoSimdBinop(Add<u8>); + case O::I8X16AddSaturateS: return DoSimdBinop(IntAddSat<s8>); + case O::I8X16AddSaturateU: return DoSimdBinop(IntAddSat<u8>); + case O::I8X16Sub: return DoSimdBinop(Sub<u8>); + case O::I8X16SubSaturateS: return DoSimdBinop(IntSubSat<s8>); + case O::I8X16SubSaturateU: return DoSimdBinop(IntSubSat<u8>); + case O::I8X16MinS: return DoSimdBinop(IntMin<s8>); + case O::I8X16MinU: return DoSimdBinop(IntMin<u8>); + case O::I8X16MaxS: return DoSimdBinop(IntMax<s8>); + case O::I8X16MaxU: return DoSimdBinop(IntMax<u8>); + + case O::I16X8Neg: return DoSimdUnop(IntNeg<u16>); + case O::I16X8AnyTrue: return DoSimdIsTrue<u16x8, 1>(); + case O::I16X8AllTrue: return DoSimdIsTrue<u16x8, 8>(); + case O::I16X8Shl: return DoSimdShift(IntShl<u16>); + case O::I16X8ShrS: return DoSimdShift(IntShr<s16>); + case O::I16X8ShrU: return DoSimdShift(IntShr<u16>); + case O::I16X8Add: return DoSimdBinop(Add<u16>); + case O::I16X8AddSaturateS: return DoSimdBinop(IntAddSat<s16>); + case O::I16X8AddSaturateU: return DoSimdBinop(IntAddSat<u16>); + case O::I16X8Sub: return DoSimdBinop(Sub<u16>); + case O::I16X8SubSaturateS: return DoSimdBinop(IntSubSat<s16>); + case O::I16X8SubSaturateU: return DoSimdBinop(IntSubSat<u16>); + case O::I16X8Mul: return DoSimdBinop(Mul<u16>); + case O::I16X8MinS: return DoSimdBinop(IntMin<s16>); + case O::I16X8MinU: return DoSimdBinop(IntMin<u16>); + case O::I16X8MaxS: return DoSimdBinop(IntMax<s16>); + case O::I16X8MaxU: return DoSimdBinop(IntMax<u16>); + + case O::I32X4Neg: return DoSimdUnop(IntNeg<u32>); + case O::I32X4AnyTrue: return DoSimdIsTrue<u32x4, 1>(); + case O::I32X4AllTrue: return DoSimdIsTrue<u32x4, 4>(); + case O::I32X4Shl: return DoSimdShift(IntShl<u32>); + case O::I32X4ShrS: return DoSimdShift(IntShr<s32>); + case O::I32X4ShrU: return DoSimdShift(IntShr<u32>); + case O::I32X4Add: return DoSimdBinop(Add<u32>); + case O::I32X4Sub: return DoSimdBinop(Sub<u32>); + case O::I32X4Mul: return DoSimdBinop(Mul<u32>); + case O::I32X4MinS: return DoSimdBinop(IntMin<s32>); + case O::I32X4MinU: return DoSimdBinop(IntMin<u32>); + case O::I32X4MaxS: return DoSimdBinop(IntMax<s32>); + case O::I32X4MaxU: return DoSimdBinop(IntMax<u32>); + + case O::I64X2Neg: return DoSimdUnop(IntNeg<u64>); + case O::I64X2Shl: return DoSimdShift(IntShl<u64>); + case O::I64X2ShrS: return DoSimdShift(IntShr<s64>); + case O::I64X2ShrU: return DoSimdShift(IntShr<u64>); + case O::I64X2Add: return DoSimdBinop(Add<u64>); + case O::I64X2Sub: return DoSimdBinop(Sub<u64>); + case O::I64X2Mul: return DoSimdBinop(Mul<u64>); + + case O::F32X4Abs: return DoSimdUnop(FloatAbs<f32>); + case O::F32X4Neg: return DoSimdUnop(FloatNeg<f32>); + case O::F32X4Sqrt: return DoSimdUnop(FloatSqrt<f32>); + case O::F32X4Add: return DoSimdBinop(Add<f32>); + case O::F32X4Sub: return DoSimdBinop(Sub<f32>); + case O::F32X4Mul: return DoSimdBinop(Mul<f32>); + case O::F32X4Div: return DoSimdBinop(FloatDiv<f32>); + case O::F32X4Min: return DoSimdBinop(FloatMin<f32>); + case O::F32X4Max: return DoSimdBinop(FloatMax<f32>); + + case O::F64X2Abs: return DoSimdUnop(FloatAbs<f64>); + case O::F64X2Neg: return DoSimdUnop(FloatNeg<f64>); + case O::F64X2Sqrt: return DoSimdUnop(FloatSqrt<f64>); + case O::F64X2Add: return DoSimdBinop(Add<f64>); + case O::F64X2Sub: return DoSimdBinop(Sub<f64>); + case O::F64X2Mul: return DoSimdBinop(Mul<f64>); + case O::F64X2Div: return DoSimdBinop(FloatDiv<f64>); + case O::F64X2Min: return DoSimdBinop(FloatMin<f64>); + case O::F64X2Max: return DoSimdBinop(FloatMax<f64>); + + case O::I32X4TruncSatF32X4S: return DoSimdUnop(IntTruncSat<s32, f32>); + case O::I32X4TruncSatF32X4U: return DoSimdUnop(IntTruncSat<u32, f32>); + case O::F32X4ConvertI32X4S: return DoSimdUnop(Convert<f32, s32>); + case O::F32X4ConvertI32X4U: return DoSimdUnop(Convert<f32, u32>); + + case O::V8X16Swizzle: return DoSimdSwizzle(); + case O::V8X16Shuffle: return DoSimdShuffle(instr); + + case O::V8X16LoadSplat: return DoSimdLoadSplat<u8x16, u32>(instr, out_trap); + case O::V16X8LoadSplat: return DoSimdLoadSplat<u16x8, u32>(instr, out_trap); + case O::V32X4LoadSplat: return DoSimdLoadSplat<u32x4, u32>(instr, out_trap); + case O::V64X2LoadSplat: return DoSimdLoadSplat<u64x2, u64>(instr, out_trap); + + case O::I8X16NarrowI16X8S: return DoSimdNarrow<s8x16, s16x8>(); + case O::I8X16NarrowI16X8U: return DoSimdNarrow<u8x16, u16x8>(); + case O::I16X8NarrowI32X4S: return DoSimdNarrow<s16x8, s32x4>(); + case O::I16X8NarrowI32X4U: return DoSimdNarrow<u16x8, u32x4>(); + case O::I16X8WidenLowI8X16S: return DoSimdWiden<s16x8, s8x16, true>(); + case O::I16X8WidenHighI8X16S: return DoSimdWiden<s16x8, s8x16, false>(); + case O::I16X8WidenLowI8X16U: return DoSimdWiden<u16x8, u8x16, true>(); + case O::I16X8WidenHighI8X16U: return DoSimdWiden<u16x8, u8x16, false>(); + case O::I32X4WidenLowI16X8S: return DoSimdWiden<s32x4, s16x8, true>(); + case O::I32X4WidenHighI16X8S: return DoSimdWiden<s32x4, s16x8, false>(); + case O::I32X4WidenLowI16X8U: return DoSimdWiden<u32x4, u16x8, true>(); + case O::I32X4WidenHighI16X8U: return DoSimdWiden<u32x4, u16x8, false>(); + + case O::I16X8Load8X8S: return DoSimdLoadExtend<s16x8, s8x8>(instr, out_trap); + case O::I16X8Load8X8U: return DoSimdLoadExtend<u16x8, u8x8>(instr, out_trap); + case O::I32X4Load16X4S: return DoSimdLoadExtend<s32x4, s16x4>(instr, out_trap); + case O::I32X4Load16X4U: return DoSimdLoadExtend<u32x4, u16x4>(instr, out_trap); + case O::I64X2Load32X2S: return DoSimdLoadExtend<s64x2, s32x2>(instr, out_trap); + case O::I64X2Load32X2U: return DoSimdLoadExtend<s64x2, s32x2>(instr, out_trap); + + case O::V128Andnot: return DoSimdBinop(IntAndNot<u64>); + case O::I8X16AvgrU: return DoSimdBinop(IntAvgr<u8>); + case O::I16X8AvgrU: return DoSimdBinop(IntAvgr<u16>); + + case O::AtomicNotify: + case O::I32AtomicWait: + case O::I64AtomicWait: + return TRAP("not implemented"); + + case O::I32AtomicLoad: return DoAtomicLoad<u32>(instr, out_trap); + case O::I64AtomicLoad: return DoAtomicLoad<u64>(instr, out_trap); + case O::I32AtomicLoad8U: return DoAtomicLoad<u32, u8>(instr, out_trap); + case O::I32AtomicLoad16U: return DoAtomicLoad<u32, u16>(instr, out_trap); + case O::I64AtomicLoad8U: return DoAtomicLoad<u64, u8>(instr, out_trap); + case O::I64AtomicLoad16U: return DoAtomicLoad<u64, u16>(instr, out_trap); + case O::I64AtomicLoad32U: return DoAtomicLoad<u64, u32>(instr, out_trap); + case O::I32AtomicStore: return DoAtomicStore<u32>(instr, out_trap); + case O::I64AtomicStore: return DoAtomicStore<u64>(instr, out_trap); + case O::I32AtomicStore8: return DoAtomicStore<u32, u8>(instr, out_trap); + case O::I32AtomicStore16: return DoAtomicStore<u32, u16>(instr, out_trap); + case O::I64AtomicStore8: return DoAtomicStore<u64, u8>(instr, out_trap); + case O::I64AtomicStore16: return DoAtomicStore<u64, u16>(instr, out_trap); + case O::I64AtomicStore32: return DoAtomicStore<u64, u32>(instr, out_trap); + case O::I32AtomicRmwAdd: return DoAtomicRmw<u32>(Add<u32>, instr, out_trap); + case O::I64AtomicRmwAdd: return DoAtomicRmw<u64>(Add<u64>, instr, out_trap); + case O::I32AtomicRmw8AddU: return DoAtomicRmw<u32>(Add<u8>, instr, out_trap); + case O::I32AtomicRmw16AddU: return DoAtomicRmw<u32>(Add<u16>, instr, out_trap); + case O::I64AtomicRmw8AddU: return DoAtomicRmw<u64>(Add<u8>, instr, out_trap); + case O::I64AtomicRmw16AddU: return DoAtomicRmw<u64>(Add<u16>, instr, out_trap); + case O::I64AtomicRmw32AddU: return DoAtomicRmw<u64>(Add<u32>, instr, out_trap); + case O::I32AtomicRmwSub: return DoAtomicRmw<u32>(Sub<u32>, instr, out_trap); + case O::I64AtomicRmwSub: return DoAtomicRmw<u64>(Sub<u64>, instr, out_trap); + case O::I32AtomicRmw8SubU: return DoAtomicRmw<u32>(Sub<u8>, instr, out_trap); + case O::I32AtomicRmw16SubU: return DoAtomicRmw<u32>(Sub<u16>, instr, out_trap); + case O::I64AtomicRmw8SubU: return DoAtomicRmw<u64>(Sub<u8>, instr, out_trap); + case O::I64AtomicRmw16SubU: return DoAtomicRmw<u64>(Sub<u16>, instr, out_trap); + case O::I64AtomicRmw32SubU: return DoAtomicRmw<u64>(Sub<u32>, instr, out_trap); + case O::I32AtomicRmwAnd: return DoAtomicRmw<u32>(IntAnd<u32>, instr, out_trap); + case O::I64AtomicRmwAnd: return DoAtomicRmw<u64>(IntAnd<u64>, instr, out_trap); + case O::I32AtomicRmw8AndU: return DoAtomicRmw<u32>(IntAnd<u8>, instr, out_trap); + case O::I32AtomicRmw16AndU: return DoAtomicRmw<u32>(IntAnd<u16>, instr, out_trap); + case O::I64AtomicRmw8AndU: return DoAtomicRmw<u64>(IntAnd<u8>, instr, out_trap); + case O::I64AtomicRmw16AndU: return DoAtomicRmw<u64>(IntAnd<u16>, instr, out_trap); + case O::I64AtomicRmw32AndU: return DoAtomicRmw<u64>(IntAnd<u32>, instr, out_trap); + case O::I32AtomicRmwOr: return DoAtomicRmw<u32>(IntOr<u32>, instr, out_trap); + case O::I64AtomicRmwOr: return DoAtomicRmw<u64>(IntOr<u64>, instr, out_trap); + case O::I32AtomicRmw8OrU: return DoAtomicRmw<u32>(IntOr<u8>, instr, out_trap); + case O::I32AtomicRmw16OrU: return DoAtomicRmw<u32>(IntOr<u16>, instr, out_trap); + case O::I64AtomicRmw8OrU: return DoAtomicRmw<u64>(IntOr<u8>, instr, out_trap); + case O::I64AtomicRmw16OrU: return DoAtomicRmw<u64>(IntOr<u16>, instr, out_trap); + case O::I64AtomicRmw32OrU: return DoAtomicRmw<u64>(IntOr<u32>, instr, out_trap); + case O::I32AtomicRmwXor: return DoAtomicRmw<u32>(IntXor<u32>, instr, out_trap); + case O::I64AtomicRmwXor: return DoAtomicRmw<u64>(IntXor<u64>, instr, out_trap); + case O::I32AtomicRmw8XorU: return DoAtomicRmw<u32>(IntXor<u8>, instr, out_trap); + case O::I32AtomicRmw16XorU: return DoAtomicRmw<u32>(IntXor<u16>, instr, out_trap); + case O::I64AtomicRmw8XorU: return DoAtomicRmw<u64>(IntXor<u8>, instr, out_trap); + case O::I64AtomicRmw16XorU: return DoAtomicRmw<u64>(IntXor<u16>, instr, out_trap); + case O::I64AtomicRmw32XorU: return DoAtomicRmw<u64>(IntXor<u32>, instr, out_trap); + case O::I32AtomicRmwXchg: return DoAtomicRmw<u32>(Xchg<u32>, instr, out_trap); + case O::I64AtomicRmwXchg: return DoAtomicRmw<u64>(Xchg<u64>, instr, out_trap); + case O::I32AtomicRmw8XchgU: return DoAtomicRmw<u32>(Xchg<u8>, instr, out_trap); + case O::I32AtomicRmw16XchgU: return DoAtomicRmw<u32>(Xchg<u16>, instr, out_trap); + case O::I64AtomicRmw8XchgU: return DoAtomicRmw<u64>(Xchg<u8>, instr, out_trap); + case O::I64AtomicRmw16XchgU: return DoAtomicRmw<u64>(Xchg<u16>, instr, out_trap); + case O::I64AtomicRmw32XchgU: return DoAtomicRmw<u64>(Xchg<u32>, instr, out_trap); + + case O::I32AtomicRmwCmpxchg: return DoAtomicRmwCmpxchg<u32>(instr, out_trap); + case O::I64AtomicRmwCmpxchg: return DoAtomicRmwCmpxchg<u64>(instr, out_trap); + case O::I32AtomicRmw8CmpxchgU: return DoAtomicRmwCmpxchg<u32, u8>(instr, out_trap); + case O::I32AtomicRmw16CmpxchgU: return DoAtomicRmwCmpxchg<u32, u16>(instr, out_trap); + case O::I64AtomicRmw8CmpxchgU: return DoAtomicRmwCmpxchg<u64, u8>(instr, out_trap); + case O::I64AtomicRmw16CmpxchgU: return DoAtomicRmwCmpxchg<u64, u16>(instr, out_trap); + case O::I64AtomicRmw32CmpxchgU: return DoAtomicRmwCmpxchg<u64, u32>(instr, out_trap); + + // The following opcodes are either never generated or should never be + // executed. + case O::Nop: + case O::Block: + case O::Loop: + case O::If: + case O::Else: + case O::End: + case O::ReturnCall: + case O::SelectT: + + case O::Try: + case O::Catch: + case O::Throw: + case O::Rethrow: + case O::BrOnExn: + case O::InterpData: + case O::Invalid: + WABT_UNREACHABLE; + break; + } - case Opcode::F32X4ConvertI32X4S: - CHECK_TRAP(SimdUnop<v128, int32_t>(SimdConvert<float, int32_t>)); - break; + return RunResult::Ok; +} - case Opcode::F32X4ConvertI32X4U: - CHECK_TRAP(SimdUnop<v128, uint32_t>(SimdConvert<float, uint32_t>)); - break; +RunResult Thread::DoCall(const Func::Ptr& func, Trap::Ptr* out_trap) { + if (auto* host_func = dyn_cast<HostFunc>(func.get())) { + auto& func_type = host_func->type(); - case Opcode::I32X4TruncSatF32X4S: - CHECK_TRAP(SimdUnop<v128, int32_t>(IntTruncSat<int32_t, float>)); - break; + Values params; + PopValues(func_type.params, ¶ms); + if (PushCall(*host_func, out_trap) == RunResult::Trap) { + return RunResult::Trap; + } - case Opcode::I32X4TruncSatF32X4U: - CHECK_TRAP(SimdUnop<v128, uint32_t>(IntTruncSat<uint32_t, float>)); - break; + Values results(func_type.results.size()); + if (Failed(host_func->Call(*this, params, results, out_trap))) { + return RunResult::Trap; + } - case Opcode::RefIsNull: - CHECK_TRAP(Unop(RefIsNull)); - break; + PopCall(); + PushValues(func_type.results, results); + } else { + if (PushCall(*cast<DefinedFunc>(func.get()), out_trap) == RunResult::Trap) { + return RunResult::Ok; + } + } + return RunResult::Ok; +} - case Opcode::TableGet: - CHECK_TRAP(TableGet(&pc)); - break; +template <typename T> +RunResult Thread::Load(Instr instr, T* out, Trap::Ptr* out_trap) { + Memory::Ptr memory{store_, inst_->memories()[instr.imm_u32x2.fst]}; + u32 offset = Pop<u32>(); + TRAP_IF(Failed(memory->Load(offset, instr.imm_u32x2.snd, out)), + StringPrintf("out of bounds memory access: access at %" PRIu64 + "+%" PRIzd " >= max value %u", + u64{offset} + instr.imm_u32x2.snd, sizeof(T), + memory->ByteSize())); + return RunResult::Ok; +} - case Opcode::TableSet: - CHECK_TRAP(TableSet(&pc)); - break; +template <typename T, typename V> +RunResult Thread::DoLoad(Instr instr, Trap::Ptr* out_trap) { + V val; + if (Load<V>(instr, &val, out_trap) != RunResult::Ok) { + return RunResult::Trap; + } + Push(static_cast<T>(val)); + return RunResult::Ok; +} - case Opcode::RefFunc: - CHECK_TRAP(RefFunc(&pc)); - break; +template <typename T, typename V> +RunResult Thread::DoStore(Instr instr, Trap::Ptr* out_trap) { + Memory::Ptr memory{store_, inst_->memories()[instr.imm_u32x2.fst]}; + V val = static_cast<V>(Pop<T>()); + u32 offset = Pop<u32>(); + TRAP_IF(Failed(memory->Store(offset, instr.imm_u32x2.snd, val)), + StringPrintf("out of bounds memory access: access at %" PRIu64 + "+%" PRIzd " >= max value %u", + u64{offset} + instr.imm_u32x2.snd, sizeof(V), + memory->ByteSize())); + return RunResult::Ok; +} - case Opcode::RefNull: - CHECK_TRAP(Push(Ref{RefType::Null, kInvalidIndex})); - break; +template <typename R, typename T> +RunResult Thread::DoUnop(UnopFunc<R, T> f) { + Push<R>(f(Pop<T>())); + return RunResult::Ok; +} - case Opcode::TableGrow: { - Table* table = ReadTable(&pc); - uint32_t increment = Pop<uint32_t>(); - Ref ref = Pop<Ref>(); - uint32_t old_size = table->size(); - uint32_t max = table->limits.has_max ? table->limits.max : UINT32_MAX; - PUSH_NEG_1_AND_BREAK_IF(int64_t(old_size) + increment > max); - uint32_t new_size = old_size + increment; - table->resize(new_size, ref); - CHECK_TRAP(Push<uint32_t>(old_size)); - break; - } +template <typename R, typename T> +RunResult Thread::DoUnop(UnopTrapFunc<R, T> f, Trap::Ptr* out_trap) { + T out; + std::string msg; + TRAP_IF(f(Pop<T>(), &out, &msg) == RunResult::Trap, msg); + Push<R>(out); + return RunResult::Ok; +} - case Opcode::TableSize: { - Table* table = ReadTable(&pc); - CHECK_TRAP(Push<uint32_t>(table->entries.size())); - break; - } +template <typename R, typename T> +RunResult Thread::DoBinop(BinopFunc<R, T> f) { + auto rhs = Pop<T>(); + auto lhs = Pop<T>(); + Push<R>(f(lhs, rhs)); + return RunResult::Ok; +} - case Opcode::TableFill: - CHECK_TRAP(TableFill(&pc)); - break; +template <typename R, typename T> +RunResult Thread::DoBinop(BinopTrapFunc<R, T> f, Trap::Ptr* out_trap) { + auto rhs = Pop<T>(); + auto lhs = Pop<T>(); + T out; + std::string msg; + TRAP_IF(f(lhs, rhs, &out, &msg) == RunResult::Trap, msg); + Push<R>(out); + return RunResult::Ok; +} - case Opcode::MemoryInit: - CHECK_TRAP(MemoryInit(&pc)); - break; +template <typename R, typename T> +RunResult Thread::DoConvert(Trap::Ptr* out_trap) { + auto val = Pop<T>(); + if (std::is_integral<R>::value && std::is_floating_point<T>::value) { + // Don't use std::isnan here because T may be a non-floating-point type. + TRAP_IF(IsNaN(val), "invalid conversion to integer"); + } + TRAP_UNLESS(CanConvert<R>(val), "integer overflow"); + Push<R>(Convert<R>(val)); + return RunResult::Ok; +} - case Opcode::DataDrop: - CHECK_TRAP(DataDrop(&pc)); - break; +template <typename R, typename T> +RunResult Thread::DoReinterpret() { + Push(Bitcast<R>(Pop<T>())); + return RunResult::Ok; +} + +RunResult Thread::DoMemoryInit(Instr instr, Trap::Ptr* out_trap) { + Memory::Ptr memory{store_, inst_->memories()[instr.imm_u32x2.fst]}; + auto&& data = inst_->datas()[instr.imm_u32x2.snd]; + auto size = Pop<u32>(); + auto src = Pop<u32>(); + auto dst = Pop<u32>(); + TRAP_IF(Failed(memory->Init(dst, data, src, size)), + "out of bounds memory access: memory.init out of bounds"); + return RunResult::Ok; +} + +RunResult Thread::DoDataDrop(Instr instr) { + inst_->datas()[instr.imm_u32].Drop(); + return RunResult::Ok; +} + +RunResult Thread::DoMemoryCopy(Instr instr, Trap::Ptr* out_trap) { + Memory::Ptr mem_dst{store_, inst_->memories()[instr.imm_u32x2.fst]}; + Memory::Ptr mem_src{store_, inst_->memories()[instr.imm_u32x2.snd]}; + auto size = Pop<u32>(); + auto src = Pop<u32>(); + auto dst = Pop<u32>(); + // TODO: change to "out of bounds" + TRAP_IF(Failed(Memory::Copy(*mem_dst, dst, *mem_src, src, size)), + "out of bounds memory access: memory.copy out of bound"); + return RunResult::Ok; +} + +RunResult Thread::DoMemoryFill(Instr instr, Trap::Ptr* out_trap) { + Memory::Ptr memory{store_, inst_->memories()[instr.imm_u32]}; + auto size = Pop<u32>(); + auto value = Pop<u32>(); + auto dst = Pop<u32>(); + TRAP_IF(Failed(memory->Fill(dst, value, size)), + "out of bounds memory access: memory.fill out of bounds"); + return RunResult::Ok; +} + +RunResult Thread::DoTableInit(Instr instr, Trap::Ptr* out_trap) { + Table::Ptr table{store_, inst_->tables()[instr.imm_u32x2.fst]}; + auto&& elem = inst_->elems()[instr.imm_u32x2.snd]; + auto size = Pop<u32>(); + auto src = Pop<u32>(); + auto dst = Pop<u32>(); + TRAP_IF(Failed(table->Init(store_, dst, elem, src, size)), + "out of bounds table access: table.init out of bounds"); + return RunResult::Ok; +} + +RunResult Thread::DoElemDrop(Instr instr) { + inst_->elems()[instr.imm_u32].Drop(); + return RunResult::Ok; +} + +RunResult Thread::DoTableCopy(Instr instr, Trap::Ptr* out_trap) { + Table::Ptr table_dst{store_, inst_->tables()[instr.imm_u32x2.fst]}; + Table::Ptr table_src{store_, inst_->tables()[instr.imm_u32x2.snd]}; + auto size = Pop<u32>(); + auto src = Pop<u32>(); + auto dst = Pop<u32>(); + TRAP_IF(Failed(Table::Copy(store_, *table_dst, dst, *table_src, src, size)), + "out of bounds table access: table.copy out of bounds"); + return RunResult::Ok; +} + +RunResult Thread::DoTableGet(Instr instr, Trap::Ptr* out_trap) { + Table::Ptr table{store_, inst_->tables()[instr.imm_u32]}; + auto index = Pop<u32>(); + Ref ref; + TRAP_IF(Failed(table->Get(index, &ref)), + StringPrintf( + "out of bounds table access: table.get at %u >= max value %u", + index, table->size())); + Push(ref); + return RunResult::Ok; +} + +RunResult Thread::DoTableSet(Instr instr, Trap::Ptr* out_trap) { + Table::Ptr table{store_, inst_->tables()[instr.imm_u32]}; + auto ref = Pop<Ref>(); + auto index = Pop<u32>(); + TRAP_IF(Failed(table->Set(store_, index, ref)), + StringPrintf( + "out of bounds table access: table.set at %u >= max value %u", + index, table->size())); + return RunResult::Ok; +} + +RunResult Thread::DoTableGrow(Instr instr, Trap::Ptr* out_trap) { + Table::Ptr table{store_, inst_->tables()[instr.imm_u32]}; + u32 old_size = table->size(); + auto delta = Pop<u32>(); + auto ref = Pop<Ref>(); + if (Failed(table->Grow(store_, delta, ref))) { + Push<s32>(-1); + } else { + Push<u32>(old_size); + } + return RunResult::Ok; +} - case Opcode::MemoryCopy: - CHECK_TRAP(MemoryCopy(&pc)); - break; +RunResult Thread::DoTableSize(Instr instr) { + Table::Ptr table{store_, inst_->tables()[instr.imm_u32]}; + Push<u32>(table->size()); + return RunResult::Ok; +} - case Opcode::MemoryFill: - CHECK_TRAP(MemoryFill(&pc)); - break; +RunResult Thread::DoTableFill(Instr instr, Trap::Ptr* out_trap) { + Table::Ptr table{store_, inst_->tables()[instr.imm_u32]}; + auto size = Pop<u32>(); + auto value = Pop<Ref>(); + auto dst = Pop<u32>(); + TRAP_IF(Failed(table->Fill(store_, dst, value, size)), + "out of bounds table access: table.fill out of bounds"); + return RunResult::Ok; +} - case Opcode::TableInit: - CHECK_TRAP(TableInit(&pc)); - break; +template <typename R, typename T> +RunResult Thread::DoSimdSplat() { + auto val = Pop<T>(); + R result; + std::fill(std::begin(result.v), std::end(result.v), val); + Push(result); + return RunResult::Ok; +} - case Opcode::ElemDrop: - CHECK_TRAP(ElemDrop(&pc)); - break; +template <typename R, typename T> +RunResult Thread::DoSimdExtract(Instr instr) { + Push<T>(Pop<R>().v[instr.imm_u8]); + return RunResult::Ok; +} - case Opcode::TableCopy: - CHECK_TRAP(TableCopy(&pc)); - break; +template <typename R, typename T> +RunResult Thread::DoSimdReplace(Instr instr) { + auto val = Pop<T>(); + auto simd = Pop<R>(); + simd.v[instr.imm_u8] = val; + Push(simd); + return RunResult::Ok; +} + +template <typename T> struct Simd128; +template <> struct Simd128<s8> { using Type = s8x16; }; +template <> struct Simd128<u8> { using Type = u8x16; }; +template <> struct Simd128<s16> { using Type = s16x8; }; +template <> struct Simd128<u16> { using Type = u16x8; }; +template <> struct Simd128<s32> { using Type = s32x4; }; +template <> struct Simd128<u32> { using Type = u32x4; }; +template <> struct Simd128<s64> { using Type = s64x2; }; +template <> struct Simd128<u64> { using Type = u64x2; }; +template <> struct Simd128<f32> { using Type = f32x4; }; +template <> struct Simd128<f64> { using Type = f64x2; }; - case Opcode::I8X16NarrowI16X8S: - case Opcode::I8X16NarrowI16X8U: - case Opcode::I16X8NarrowI32X4S: - case Opcode::I16X8NarrowI32X4U: - case Opcode::I16X8WidenLowI8X16S: - case Opcode::I16X8WidenHighI8X16S: - case Opcode::I16X8WidenLowI8X16U: - case Opcode::I16X8WidenHighI8X16U: - case Opcode::I32X4WidenLowI16X8S: - case Opcode::I32X4WidenHighI16X8S: - case Opcode::I32X4WidenLowI16X8U: - case Opcode::I32X4WidenHighI16X8U: - case Opcode::I16X8Load8X8S: - case Opcode::I16X8Load8X8U: - case Opcode::I32X4Load16X4S: - case Opcode::I32X4Load16X4U: - case Opcode::I64X2Load32X2S: - case Opcode::I64X2Load32X2U: - case Opcode::V128Andnot: - case Opcode::I8X16AvgrU: - case Opcode::I16X8AvgrU: - case Opcode::I8X16MinS: - case Opcode::I8X16MinU: - case Opcode::I8X16MaxS: - case Opcode::I8X16MaxU: - case Opcode::I16X8MinS: - case Opcode::I16X8MinU: - case Opcode::I16X8MaxS: - case Opcode::I16X8MaxU: - case Opcode::I32X4MinS: - case Opcode::I32X4MinU: - case Opcode::I32X4MaxS: - case Opcode::I32X4MaxU: - return ResultType::NotImplemented; - - // The following opcodes are either never generated or should never be - // executed. - case Opcode::Block: - case Opcode::BrOnExn: - case Opcode::Catch: - case Opcode::Else: - case Opcode::End: - case Opcode::If: - case Opcode::InterpData: - case Opcode::Invalid: - case Opcode::Loop: - case Opcode::Rethrow: - case Opcode::Throw: - case Opcode::Try: - WABT_UNREACHABLE; - break; - } - } +template <typename R, typename T> +RunResult Thread::DoSimdUnop(UnopFunc<R, T> f) { + using ST = typename Simd128<T>::Type; + using SR = typename Simd128<R>::Type; + auto val = Pop<ST>(); + SR result; + std::transform(std::begin(val.v), std::end(val.v), std::begin(result.v), f); + Push(result); + return RunResult::Ok; +} -exit_loop: - pc_ = pc - istream; - return result; +template <typename R, typename T> +RunResult Thread::DoSimdBinop(BinopFunc<R, T> f) { + using ST = typename Simd128<T>::Type; + using SR = typename Simd128<R>::Type; + static_assert(ST::lanes == SR::lanes, "SIMD lanes don't match"); + auto rhs = Pop<ST>(); + auto lhs = Pop<ST>(); + SR result; + for (u8 i = 0; i < SR::lanes; ++i) { + result.v[i] = f(lhs.v[i], rhs.v[i]); + } + Push(result); + return RunResult::Ok; +} + +RunResult Thread::DoSimdBitSelect() { + using S = u64x2; + auto c = Pop<S>(); + auto rhs = Pop<S>(); + auto lhs = Pop<S>(); + S result; + for (u8 i = 0; i < S::lanes; ++i) { + result.v[i] = (lhs.v[i] & c.v[i]) | (rhs.v[i] & ~c.v[i]); + } + Push(result); + return RunResult::Ok; +} + +template <typename S, u8 count> +RunResult Thread::DoSimdIsTrue() { + using L = typename S::LaneType; + auto val = Pop<S>(); + Push(std::count_if(std::begin(val.v), std::end(val.v), + [](L x) { return x != 0; }) >= count); + return RunResult::Ok; } -Executor::Executor(Environment* env, - Stream* trace_stream, - const Thread::Options& options) - : env_(env), trace_stream_(trace_stream), thread_(env, options) {} - -ExecResult Executor::RunFunction(Index func_index, const TypedValues& args) { - ExecResult exec_result; - Func* func = env_->GetFunc(func_index); - FuncSignature* sig = env_->GetFuncSignature(func->sig_index); - - thread_.Reset(); - exec_result.result = PushArgs(sig, args); - if (exec_result.ok()) { - exec_result.result = - func->is_host ? thread_.CallHost(cast<HostFunc>(func)) - : RunDefinedFunction(cast<DefinedFunc>(func)->offset); - if (exec_result.ok()) { - CopyResults(sig, &exec_result.values); - } +template <typename R, typename T> +RunResult Thread::DoSimdShift(BinopFunc<R, T> f) { + using ST = typename Simd128<T>::Type; + using SR = typename Simd128<R>::Type; + static_assert(ST::lanes == SR::lanes, "SIMD lanes don't match"); + auto amount = Pop<T>(); + auto lhs = Pop<ST>(); + SR result; + for (u8 i = 0; i < SR::lanes; ++i) { + result.v[i] = f(lhs.v[i], amount); } - - return exec_result; + Push(result); + return RunResult::Ok; } -ExecResult Executor::Initialize(DefinedModule* module) { - ExecResult exec_result; - exec_result.result = InitializeSegments(module); - if (!exec_result.ok()) - return exec_result; - - return RunStartFunction(module); +template <typename S, typename T> +RunResult Thread::DoSimdLoadSplat(Instr instr, Trap::Ptr* out_trap) { + using L = typename S::LaneType; + L val; + if (Load<L>(instr, &val, out_trap) != RunResult::Ok) { + return RunResult::Trap; + } + S result; + std::fill(std::begin(result.v), std::end(result.v), val); + Push(result); + return RunResult::Ok; } -ExecResult Executor::RunStartFunction(DefinedModule* module) { - if (module->start_func_index == kInvalidIndex) { - return ExecResult(ResultType::Ok); +RunResult Thread::DoSimdSwizzle() { + using S = u8x16; + auto rhs = Pop<S>(); + auto lhs = Pop<S>(); + S result; + for (u8 i = 0; i < S::lanes; ++i) { + result.v[i] = rhs.v[i] < S::lanes ? lhs.v[rhs.v[i]] : 0; } + Push(result); + return RunResult::Ok; +} - if (trace_stream_) { - trace_stream_->Writef(">>> running start function:\n"); +RunResult Thread::DoSimdShuffle(Instr instr) { + using S = u8x16; + auto sel = Bitcast<S>(instr.imm_v128); + auto rhs = Pop<S>(); + auto lhs = Pop<S>(); + S result; + for (u8 i = 0; i < S::lanes; ++i) { + result.v[i] = + sel.v[i] < S::lanes ? lhs.v[sel.v[i]] : rhs.v[sel.v[i] - S::lanes]; } - - TypedValues args; - ExecResult exec_result = RunFunction(module->start_func_index, args); - assert(exec_result.values.size() == 0); - return exec_result; + Push(result); + return RunResult::Ok; } -Result Executor::InitializeSegments(DefinedModule* module) { - // The MVP requires that all segments are bounds-checked before being copied - // into the table or memory. The bulk memory proposal changes this behavior; - // instead, each segment is copied in order. If any segment fails, then no - // further segments are copied. Any data that was written persists. - enum Pass { Check = 0, Init = 1 }; - int pass = env_->features_.bulk_memory_enabled() ? Init : Check; - - if (trace_stream_) { - trace_stream_->Writef(">>> initializing segments\n"); +template <typename S, typename T> +RunResult Thread::DoSimdNarrow() { + using SL = typename S::LaneType; + using TL = typename T::LaneType; + auto rhs = Pop<T>(); + auto lhs = Pop<T>(); + S result; + for (u8 i = 0; i < T::lanes; ++i) { + result.v[i] = Saturate<SL, TL>(lhs.v[i]); } - - for (; pass <= Init; ++pass) { - for (const ElemSegmentInfo& info : module->active_elem_segments_) { - uint32_t table_size = info.table->size(); - uint32_t segment_size = info.src.size(); - uint32_t copy_size = segment_size; - if (!CheckBounds(info.dst, copy_size, table_size)) { - TRAP_MSG(TableAccessOutOfBounds, - "elem segment is out of bounds: [%u, %" PRIu64 - ") >= max value %u", - info.dst, static_cast<uint64_t>(info.dst) + segment_size, - table_size); - } - - if (pass == Init && copy_size > 0) { - std::copy(info.src.begin(), info.src.begin() + copy_size, - info.table->entries.begin() + info.dst); - } - } - - for (const DataSegmentInfo& info : module->active_data_segments_) { - uint32_t memory_size = info.memory->data.size(); - uint32_t segment_size = info.data.size(); - uint32_t copy_size = segment_size; - if (!CheckBounds(info.dst, copy_size, memory_size)) { - TRAP_MSG(MemoryAccessOutOfBounds, - "data segment is out of bounds: [%u, %" PRIu64 - ") >= max value %u", - info.dst, static_cast<uint64_t>(info.dst) + segment_size, - memory_size); - } - - if (pass == Init && copy_size > 0) { - std::copy(info.data.begin(), info.data.begin() + copy_size, - info.memory->data.begin() + info.dst); - } - } + for (u8 i = 0; i < T::lanes; ++i) { + result.v[T::lanes + i] = Saturate<SL, TL>(rhs.v[i]); } - - return ResultType::Ok; + Push(result); + return RunResult::Ok; } -ExecResult Executor::RunExport(const Export* export_, const TypedValues& args) { - if (trace_stream_) { - trace_stream_->Writef(">>> running export \"" PRIstringview "\":\n", - WABT_PRINTF_STRING_VIEW_ARG(export_->name)); +template <typename S, typename T, bool low> +RunResult Thread::DoSimdWiden() { + auto val = Pop<T>(); + S result; + for (u8 i = 0; i < S::lanes; ++i) { + result.v[i] = val.v[(low ? 0 : S::lanes) + i]; } - - assert(export_->kind == ExternalKind::Func); - return RunFunction(export_->index, args); + Push(result); + return RunResult::Ok; } -ExecResult Executor::RunExportByName(Module* module, - string_view name, - const TypedValues& args) { - Export* export_ = module->GetExport(name); - if (!export_) { - return ExecResult(ResultType::UnknownExport); +template <typename S, typename T> +RunResult Thread::DoSimdLoadExtend(Instr instr, Trap::Ptr* out_trap) { + T val; + if (Load<T>(instr, &val, out_trap) != RunResult::Ok) { + return RunResult::Trap; } - if (export_->kind != ExternalKind::Func) { - return ExecResult(ResultType::ExportKindMismatch); + S result; + for (u8 i = 0; i < S::lanes; ++i) { + result.v[i] = val.v[i]; } - return RunExport(export_, args); + Push(result); + return RunResult::Ok; } -Result Executor::RunDefinedFunction(IstreamOffset function_offset) { - Result result = ResultType::Ok; - thread_.set_pc(function_offset); - if (trace_stream_) { - const int kNumInstructions = 1; - while (result.ok()) { - thread_.Trace(trace_stream_); - result = thread_.Run(kNumInstructions); - } - } else { - const int kNumInstructions = 1000; - while (result.ok()) { - result = thread_.Run(kNumInstructions); - } - } - if (result.type != ResultType::Returned) { - return result; - } - // Use OK instead of RETURNED for consistency. - return ResultType::Ok; +template <typename T, typename V> +RunResult Thread::DoAtomicLoad(Instr instr, Trap::Ptr* out_trap) { + Memory::Ptr memory{store_, inst_->memories()[instr.imm_u32x2.fst]}; + u32 offset = Pop<u32>(); + V val; + TRAP_IF(Failed(memory->AtomicLoad(offset, instr.imm_u32x2.snd, &val)), + StringPrintf("invalid atomic access at %u+%u", offset, + instr.imm_u32x2.snd)); + Push(static_cast<T>(val)); + return RunResult::Ok; } -Result Executor::PushArgs(const FuncSignature* sig, const TypedValues& args) { - if (sig->param_types.size() != args.size()) { - return ResultType::ArgumentTypeMismatch; - } +template <typename T, typename V> +RunResult Thread::DoAtomicStore(Instr instr, Trap::Ptr* out_trap) { + Memory::Ptr memory{store_, inst_->memories()[instr.imm_u32x2.fst]}; + V val = static_cast<V>(Pop<T>()); + u32 offset = Pop<u32>(); + TRAP_IF(Failed(memory->AtomicStore(offset, instr.imm_u32x2.snd, val)), + StringPrintf("invalid atomic access at %u+%u", offset, + instr.imm_u32x2.snd)); + return RunResult::Ok; +} - for (size_t i = 0; i < sig->param_types.size(); ++i) { - if (TypeChecker::CheckType(args[i].type, sig->param_types[i]) != - wabt::Result::Ok) { - return ResultType::ArgumentTypeMismatch; - } +template <typename R, typename T> +RunResult Thread::DoAtomicRmw(BinopFunc<T, T> f, + Instr instr, + Trap::Ptr* out_trap) { + Memory::Ptr memory{store_, inst_->memories()[instr.imm_u32x2.fst]}; + T val = Pop<T>(); + u32 offset = Pop<u32>(); + T old; + TRAP_IF(Failed(memory->AtomicRmw(offset, instr.imm_u32x2.snd, val, f, &old)), + StringPrintf("invalid atomic access at %u+%u", offset, + instr.imm_u32x2.snd)); + Push(static_cast<R>(old)); + return RunResult::Ok; +} - Result result = thread_.Push(args[i].value); - if (!result.ok()) { - return result; +template <typename T, typename V> +RunResult Thread::DoAtomicRmwCmpxchg(Instr instr, Trap::Ptr* out_trap) { + Memory::Ptr memory{store_, inst_->memories()[instr.imm_u32x2.fst]}; + V replace = static_cast<V>(Pop<T>()); + V expect = static_cast<V>(Pop<T>()); + V old; + u32 offset = Pop<u32>(); + TRAP_IF(Failed(memory->AtomicRmwCmpxchg(offset, instr.imm_u32x2.snd, expect, + replace, &old)), + StringPrintf("invalid atomic access at %u+%u", offset, + instr.imm_u32x2.snd)); + Push(static_cast<T>(old)); + return RunResult::Ok; +} + +Thread::TraceSource::TraceSource(Thread* thread) : thread_(thread) {} + +std::string Thread::TraceSource::Header(Istream::Offset offset) { + return StringPrintf("#%" PRIzd ". %4u: V:%-3" PRIzd, + thread_->frames_.size() - 1, offset, + thread_->values_.size()); +} + +std::string Thread::TraceSource::Pick(Index index, Instr instr) { + Value val = thread_->Pick(index); + const char* reftype; + // TODO: the opcode index and pick index go in opposite directions. + auto type = instr.op.GetParamType(index); + if (type == ValueType::Void) { + // Void should never be displayed normally; we only expect to see it when + // the stack may have different a different type. This is likely to occur + // with an index; try to see which type we should expect. + switch (instr.op) { + case Opcode::GlobalSet: type = GetGlobalType(instr.imm_u32); break; + case Opcode::LocalSet: + case Opcode::LocalTee: type = GetLocalType(instr.imm_u32); break; + case Opcode::TableSet: + case Opcode::TableGrow: + case Opcode::TableFill: type = GetTableElementType(instr.imm_u32); break; + default: return "?"; } } - return ResultType::Ok; -} -void Executor::CopyResults(const FuncSignature* sig, TypedValues* out_results) { - size_t expected_results = sig->result_types.size(); - assert(expected_results == thread_.NumValues()); + switch (type) { + case ValueType::I32: return StringPrintf("%u", val.Get<u32>()); + case ValueType::I64: return StringPrintf("%" PRIu64, val.Get<u64>()); + case ValueType::F32: return StringPrintf("%g", val.Get<f32>()); + case ValueType::F64: return StringPrintf("%g", val.Get<f64>()); + case ValueType::V128: { + auto v = val.Get<v128>(); + return StringPrintf("0x%08x 0x%08x 0x%08x 0x%08x", v.v[0], v.v[1], v.v[2], + v.v[3]); + } - out_results->clear(); - for (size_t i = 0; i < expected_results; ++i) { - out_results->emplace_back(sig->result_types[i], thread_.ValueAt(i)); + case ValueType::Nullref: reftype = "nullref"; break; + case ValueType::Funcref: reftype = "funcref"; break; + case ValueType::Exnref: reftype = "exnref"; break; + case ValueType::Anyref: reftype = "anyref"; break; + + default: + WABT_UNREACHABLE; + break; } + + // Handle ref types. + return StringPrintf("%s:%" PRIzd, reftype, val.Get<Ref>().index); +} + +ValueType Thread::TraceSource::GetLocalType(Index stack_slot) { + const Frame& frame = thread_->frames_.back(); + DefinedFunc::Ptr func{thread_->store_, frame.func}; + // When a function is called, the arguments are first pushed on the stack by + // the caller, then the new call frame is pushed (which caches the current + // height of the value stack). At that point, any local variables will be + // allocated on the stack. For example, a function that has two parameters + // and one local variable will have a stack like this: + // + // frame.values top of stack + // v v + // param1 param2 | local1 .......... + // + // When the instruction stream is generated, all local variable access is + // translated into a value relative to the top of the stack, counting up from + // 1. So in the previous example, if there are three values above the local + // variable, the stack looks like: + // + // param1 param2 | local1 value1 value2 value3 + // stack slot: 6 5 4 3 2 1 + // local index: 0 1 2 + // + // local1 can be accessed with stack_slot 4, and param1 can be accessed with + // stack_slot 6. The formula below takes these values into account to convert + // the stack_slot into a local index. + Index local_index = + (thread_->values_.size() - frame.values + func->type().params.size()) - + stack_slot; + return func->desc().GetLocalType(local_index); +} + +ValueType Thread::TraceSource::GetGlobalType(Index index) { + return thread_->mod_->desc().globals[index].type.type; +} + +ValueType Thread::TraceSource::GetTableElementType(Index index) { + return thread_->mod_->desc().tables[index].type.element; } } // namespace interp diff --git a/src/interp/interp.h b/src/interp/interp.h index ee323395..f008b10e 100644 --- a/src/interp/interp.h +++ b/src/interp/interp.h @@ -1,5 +1,5 @@ /* - * Copyright 2016 WebAssembly Community Group participants + * Copyright 2020 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,784 +17,1125 @@ #ifndef WABT_INTERP_H_ #define WABT_INTERP_H_ -#include <stdint.h> - +#include <cstdint> #include <functional> -#include <map> #include <memory> -#include <utility> +#include <string> #include <vector> -#include "src/binding-hash.h" +#include "src/cast.h" #include "src/common.h" #include "src/feature.h" #include "src/opcode.h" -#include "src/stream.h" +#include "src/result.h" +#include "src/string-view.h" -namespace wabt { +#include "src/interp/istream.h" +namespace wabt { namespace interp { -#define FOREACH_INTERP_RESULT(V) \ - V(Ok, "ok") \ - /* returned from the top-most function */ \ - V(Returned, "returned") \ - /* memory access is out of bounds */ \ - V(TrapMemoryAccessOutOfBounds, "out of bounds memory access") \ - /* atomic memory access is unaligned */ \ - V(TrapAtomicMemoryAccessUnaligned, "atomic memory access is unaligned") \ - /* converting from float -> int would overflow int */ \ - V(TrapIntegerOverflow, "integer overflow") \ - /* dividend is zero in integer divide */ \ - V(TrapIntegerDivideByZero, "integer divide by zero") \ - /* converting from float -> int where float is nan */ \ - V(TrapInvalidConversionToInteger, "invalid conversion to integer") \ - /* function table index is out of bounds */ \ - V(TrapUndefinedTableIndex, "undefined table index") \ - /* function table element is uninitialized */ \ - V(TrapUninitializedTableElement, "uninitialized table element") \ - /* unreachable instruction executed */ \ - V(TrapUnreachable, "unreachable executed") \ - /* call indirect signature doesn't match function table signature */ \ - V(TrapIndirectCallSignatureMismatch, "indirect call signature mismatch") \ - /* ran out of call stack frames (probably infinite recursion) */ \ - V(TrapCallStackExhausted, "call stack exhausted") \ - /* ran out of value stack space */ \ - V(TrapValueStackExhausted, "value stack exhausted") \ - /* we called a host function, but the return value didn't match the */ \ - /* expected type */ \ - V(TrapHostResultTypeMismatch, "host result type mismatch") \ - /* we called an import function, but it didn't complete succesfully */ \ - V(TrapHostTrapped, "host function trapped") \ - /* table access is out of bounds */ \ - V(TrapTableAccessOutOfBounds, "out of bounds table access") \ - /* we attempted to call a function with the an argument list that doesn't \ - * match the function signature */ \ - V(ArgumentTypeMismatch, "argument type mismatch") \ - /* we tried to get an export by name that doesn't exist */ \ - V(UnknownExport, "unknown export") \ - /* the expected export kind doesn't match. */ \ - V(ExportKindMismatch, "export kind mismatch") \ - /* instruction not implemented in the interpreter */ \ - V(NotImplemented, "not implemented") - -enum class ResultType { -#define V(Name, str) Name, - FOREACH_INTERP_RESULT(V) -#undef V -}; - -struct Result { - Result(ResultType type) : type(type) {} - Result(ResultType type, std::string msg) : type(type), message(msg) {} - bool ok() const { return type == ResultType::Ok; } - ResultType type; - std::string message; -}; - -typedef uint32_t IstreamOffset; -static const IstreamOffset kInvalidIstreamOffset = ~0; - -struct FuncSignature { - FuncSignature() = default; - FuncSignature(TypeVector param_types, TypeVector result_types); - FuncSignature(Index param_count, - Type* param_types, - Index result_count, - Type* result_types); - - TypeVector param_types; - TypeVector result_types; -}; - -enum class RefType { - Func, +class Store; +class Object; +class Trap; +class DataSegment; +class ElemSegment; +class Module; +class Instance; +class Thread; +template <typename T> class RefPtr; + +using s8 = int8_t; +using u8 = uint8_t; +using s16 = int16_t; +using u16 = uint16_t; +using s32 = int32_t; +using u32 = uint32_t; +using Index = uint32_t; +using s64 = int64_t; +using u64 = uint64_t; +using f32 = float; +using f64 = double; + +using Buffer = std::vector<u8>; + +using ValueType = wabt::Type; +using ValueTypes = std::vector<ValueType>; + +template <typename T> bool HasType(ValueType); +template <typename T> void RequireType(ValueType); +bool IsReference(ValueType); +bool TypesMatch(ValueType expected, ValueType actual); + +using ExternKind = ExternalKind; +enum class Mutability { Const, Var }; +enum class EventAttr { Exception }; +using SegmentMode = SegmentKind; +enum class ElemKind { RefNull, RefFunc }; + +enum class ObjectKind { Null, - Host, + Foreign, + Trap, + DefinedFunc, + HostFunc, + Table, + Memory, + Global, + Event, + Module, + Instance, + Thread, +}; + +const char* GetName(Mutability); +const char* GetName(ValueType); +const char* GetName(ExternKind); +const char* GetName(ObjectKind); + +enum class InitExprKind { + None, + I32, + I64, + F32, + F64, + V128, + GlobalGet, + RefNull, + RefFunc +}; + +struct InitExpr { + InitExprKind kind; + union { + u32 i32_; + u64 i64_; + f32 f32_; + f64 f64_; + v128 v128_; + Index index_; + }; }; struct Ref { - RefType kind; - Index index; // Not meaningful for RefType::Null + static const Ref Null; + + Ref() = default; + explicit Ref(size_t index); + + friend bool operator==(Ref, Ref); + friend bool operator!=(Ref, Ref); + + size_t index; +}; +using RefVec = std::vector<Ref>; + +template <typename T, u8 L> +struct Simd { + using LaneType = T; + static const u8 lanes = L; + + T v[L]; +}; +using s8x16 = Simd<s8, 16>; +using u8x16 = Simd<u8, 16>; +using s16x8 = Simd<s16, 8>; +using u16x8 = Simd<u16, 8>; +using s32x4 = Simd<s32, 4>; +using u32x4 = Simd<u32, 4>; +using s64x2 = Simd<s64, 2>; +using u64x2 = Simd<u64, 2>; +using f32x4 = Simd<f32, 4>; +using f64x2 = Simd<f64, 2>; + +// Used for load extend instructions. +using s8x8 = Simd<s8, 8>; +using u8x8 = Simd<u8, 8>; +using s16x4 = Simd<s16, 4>; +using u16x4 = Simd<u16, 4>; +using s32x2 = Simd<s32, 2>; +using u32x2 = Simd<u32, 2>; + +//// Types //// + +bool CanGrow(const Limits&, u32 old_size, u32 delta, u32* new_size); +Result Match(const Limits& expected, + const Limits& actual, + std::string* out_msg); + +struct ExternType { + explicit ExternType(ExternKind); + virtual ~ExternType() {} + virtual std::unique_ptr<ExternType> Clone() const = 0; + + ExternKind kind; +}; + +struct FuncType : ExternType { + static const ExternKind skind = ExternKind::Func; + static bool classof(const ExternType* type); + + explicit FuncType(ValueTypes params, ValueTypes results); + + std::unique_ptr<ExternType> Clone() const override; + + friend Result Match(const FuncType& expected, + const FuncType& actual, + std::string* out_msg); + + ValueTypes params; + ValueTypes results; }; -struct Table { - explicit Table(Type elem_type, const Limits& limits) - : elem_type(elem_type), limits(limits) { - resize(limits.initial, {RefType::Null, kInvalidIndex}); - } +struct TableType : ExternType { + static const ExternKind skind = ExternKind::Table; + static bool classof(const ExternType* type); - size_t size() const { return entries.size(); } + explicit TableType(ValueType, Limits); - void resize(size_t new_size, Ref init_elem) { - entries.resize(new_size, init_elem); - } + std::unique_ptr<ExternType> Clone() const override; - Type elem_type; + friend Result Match(const TableType& expected, + const TableType& actual, + std::string* out_msg); + + ValueType element; Limits limits; - std::vector<Ref> entries; }; -struct Memory { - Memory() = default; - explicit Memory(const Limits& limits) - : page_limits(limits), data(limits.initial * WABT_PAGE_SIZE) {} +struct MemoryType : ExternType { + static const ExternKind skind = ExternKind::Memory; + static bool classof(const ExternType* type); + + explicit MemoryType(Limits); + + std::unique_ptr<ExternType> Clone() const override; + + friend Result Match(const MemoryType& expected, + const MemoryType& actual, + std::string* out_msg); - Limits page_limits; - std::vector<char> data; + Limits limits; }; -struct DataSegment { - DataSegment() = default; +struct GlobalType : ExternType { + static const ExternKind skind = ExternKind::Global; + static bool classof(const ExternType* type); + + explicit GlobalType(ValueType, Mutability); + + std::unique_ptr<ExternType> Clone() const override; - std::vector<char> data; + friend Result Match(const GlobalType& expected, + const GlobalType& actual, + std::string* out_msg); + + ValueType type; + Mutability mut; +}; + +struct EventType : ExternType { + static const ExternKind skind = ExternKind::Event; + static bool classof(const ExternType* type); + + explicit EventType(EventAttr, const ValueTypes&); + + std::unique_ptr<ExternType> Clone() const override; + + friend Result Match(const EventType& expected, + const EventType& actual, + std::string* out_msg); + + EventAttr attr; + ValueTypes signature; }; -struct ElemSegment { - explicit ElemSegment(SegmentFlags flags) : flags(flags) {} +struct ImportType { + explicit ImportType(std::string module, + std::string name, + std::unique_ptr<ExternType>); + ImportType(const ImportType&); + ImportType& operator=(const ImportType&); - SegmentFlags flags = SegFlagsNone; - std::vector<Ref> elems; + std::string module; + std::string name; + std::unique_ptr<ExternType> type; }; -struct ElemSegmentInfo { - ElemSegmentInfo(Table* table, Index dst) : table(table), dst(dst) {} +struct ExportType { + explicit ExportType(std::string name, std::unique_ptr<ExternType>); + ExportType(const ExportType&); + ExportType& operator=(const ExportType&); - Table* table; - Index dst; - std::vector<Ref> src; + std::string name; + std::unique_ptr<ExternType> type; }; -struct DataSegmentInfo { - DataSegmentInfo(Memory* memory, - Address dst) - : memory(memory), dst(dst) {} +//// Structure //// - Memory* memory; - Address dst; - std::vector<char> data; +struct ImportDesc { + ImportType type; }; -// Opaque handle to a host object. -struct HostObject {}; +struct LocalDesc { + ValueType type; + u32 count; + // One past the last local index that has this type. For example, a vector of + // LocalDesc might look like: + // + // {{I32, 2, 2}, {I64, 3, 5}, {F32, 1, 6}, ...} + // + // This makes it possible to use a binary search to find the type of a local + // at a given index. + u32 end; +}; + +struct FuncDesc { + // Includes params. + ValueType GetLocalType(Index) const; + + FuncType type; + std::vector<LocalDesc> locals; + u32 code_offset; +}; + +struct TableDesc { + TableType type; +}; + +struct MemoryDesc { + MemoryType type; +}; + +struct GlobalDesc { + GlobalType type; + InitExpr init; +}; + +struct EventDesc { + EventType type; +}; + +struct ExportDesc { + ExportType type; + Index index; +}; + +struct StartDesc { + Index func_index; +}; + +struct DataDesc { + Buffer data; + SegmentMode mode; + Index memory_index; + InitExpr offset; +}; + +struct ElemExpr { + ElemKind kind; + Index index; +}; + +struct ElemDesc { + std::vector<ElemExpr> elements; + ValueType type; + SegmentMode mode; + Index table_index; + InitExpr offset; +}; + +struct ModuleDesc { + std::vector<FuncType> func_types; + std::vector<ImportDesc> imports; + std::vector<FuncDesc> funcs; + std::vector<TableDesc> tables; + std::vector<MemoryDesc> memories; + std::vector<GlobalDesc> globals; + std::vector<EventDesc> events; + std::vector<ExportDesc> exports; + std::vector<StartDesc> starts; + std::vector<ElemDesc> elems; + std::vector<DataDesc> datas; + Istream istream; +}; + +//// Runtime //// + +struct Frame { + explicit Frame(Ref func, u32 values, u32 offset, Instance*, Module*); + + void Mark(Store&); + + Ref func; + u32 values; // Height of the value stack at this activation. + u32 offset; // Istream offset; either the return PC, or the current PC. + + // Cached for convenience. Both are null if func is a HostFunc. + Instance* inst; + Module* mod; +}; -// ValueTypeRep converts from one type to its representation on the -// stack. For example, float -> uint32_t. See Value below. template <typename T> -struct ValueTypeRepT; +struct FreeList { + using Index = size_t; + + bool IsValid(Index) const; + + template <typename... Args> + Index New(Args&&...); + void Delete(Index); + + const T& Get(Index) const; + T& Get(Index); + + std::vector<T> list; + std::vector<size_t> free; +}; + +class Store { + public: + using ObjectList = FreeList<std::unique_ptr<Object>>; + using RootList = FreeList<Ref>; + + explicit Store(const Features& = Features{}); + + bool IsValid(Ref) const; + bool HasValueType(Ref, ValueType) const; + ValueType GetValueType(Ref) const; + template <typename T> + bool Is(Ref) const; -template <> struct ValueTypeRepT<Ref> { typedef Ref type; }; -template <> struct ValueTypeRepT<int32_t> { typedef uint32_t type; }; -template <> struct ValueTypeRepT<uint32_t> { typedef uint32_t type; }; -template <> struct ValueTypeRepT<int64_t> { typedef uint64_t type; }; -template <> struct ValueTypeRepT<uint64_t> { typedef uint64_t type; }; -template <> struct ValueTypeRepT<float> { typedef uint32_t type; }; -template <> struct ValueTypeRepT<double> { typedef uint64_t type; }; -template <> struct ValueTypeRepT<v128> { typedef v128 type; }; + template <typename T, typename... Args> + RefPtr<T> Alloc(Args&&...); + template <typename T> + Result Get(Ref, RefPtr<T>* out); + template <typename T> + RefPtr<T> UnsafeGet(Ref); + + RootList::Index NewRoot(Ref); + RootList::Index CopyRoot(RootList::Index); + void DeleteRoot(RootList::Index); + + void Collect(); + void Mark(Ref); + void Mark(const RefVec&); + + const Features& features() const; + + private: + template <typename T> + friend class RefPtr; + + Features features_; + ObjectList objects_; + RootList roots_; + std::vector<bool> marks_; +}; template <typename T> -using ValueTypeRep = typename ValueTypeRepT<T>::type; +class RefPtr { + public: + RefPtr(); + RefPtr(Store&, Ref); + RefPtr(const RefPtr&); + RefPtr& operator=(const RefPtr&); + RefPtr(RefPtr&&); + RefPtr& operator=(RefPtr&&); + ~RefPtr(); + + template <typename U> + RefPtr(const RefPtr<U>&); + template <typename U> + RefPtr& operator=(const RefPtr<U>&); + template <typename U> + RefPtr(RefPtr&&); + template <typename U> + RefPtr& operator=(RefPtr&&); + + template <typename U> + RefPtr<U> As(); + + bool empty() const; + void reset(); + + T* get() const; + T* operator->() const; + T& operator*() const; + explicit operator bool() const; + + Ref ref() const; + Store* store() const; + + template <typename U, typename V> + friend bool operator==(const RefPtr<U>& lhs, const RefPtr<V>& rhs); + template <typename U, typename V> + friend bool operator!=(const RefPtr<U>& lhs, const RefPtr<V>& rhs); + + private: + template <typename U> + friend class RefPtr; + + T* obj_; + Store* store_; + Store::RootList::Index root_index_; +}; union Value { - uint32_t i32; - uint64_t i64; - ValueTypeRep<float> f32_bits; - ValueTypeRep<double> f64_bits; - ValueTypeRep<v128> vec128; - Ref ref; + Value() = default; + static Value WABT_VECTORCALL Make(s32); + static Value WABT_VECTORCALL Make(u32); + static Value WABT_VECTORCALL Make(s64); + static Value WABT_VECTORCALL Make(u64); + static Value WABT_VECTORCALL Make(f32); + static Value WABT_VECTORCALL Make(f64); + static Value WABT_VECTORCALL Make(v128); + static Value WABT_VECTORCALL Make(Ref); + template <typename T, u8 L> + static Value WABT_VECTORCALL Make(Simd<T, L>); + + template <typename T> + T WABT_VECTORCALL Get() const; + template <typename T> + void WABT_VECTORCALL Set(T); + + u32 i32_; + u64 i64_; + f32 f32_; + f64 f64_; + v128 v128_; + Ref ref_; }; +using Values = std::vector<Value>; struct TypedValue { - TypedValue() = default; - explicit TypedValue(Type type) : type(type) {} - TypedValue(Type basetype, const Value& value) : type(basetype), value(value) { - UpdateType(); - } - - void UpdateType() { - if (!IsRefType(type)) { - return; - } - - // For references types, the concrete type of TypedValue can change as it - // gets its value changed. For example assigning a RefType::Func value to - // an Anyref will cause type of the TypedValue to be changed into a Funcref. - switch (value.ref.kind) { - case RefType::Func: - type = Type::Funcref; - break; - case RefType::Host: - type = Type::Hostref; - break; - case RefType::Null: - type = Type::Nullref; - break; - } - } - - void SetZero() { ZeroMemory(value); } - void set_ref(Ref x) { - value.ref = x; - UpdateType(); - } - void set_i32(uint32_t x) { value.i32 = x; } - void set_i64(uint64_t x) { value.i64 = x; } - void set_f32(float x) { memcpy(&value.f32_bits, &x, sizeof(x)); } - void set_f64(double x) { memcpy(&value.f64_bits, &x, sizeof(x)); } - - Ref get_ref() const { return value.ref; } - uint32_t get_i32() const { return value.i32; } - uint64_t get_i64() const { return value.i64; } - float get_f32() const { - float x; - memcpy(&x, &value.f32_bits, sizeof(x)); - return x; - } - double get_f64() const { - double x; - memcpy(&x, &value.f64_bits, sizeof(x)); - return x; - } - - Type type; + ValueType type; Value value; }; +using TypedValues = std::vector<TypedValue>; -typedef std::vector<TypedValue> TypedValues; +using Finalizer = std::function<void(Object*)>; -struct Global { - Global() : mutable_(false), import_index(kInvalidIndex) {} - Global(Type& type, bool mutable_) : type(type), mutable_(mutable_) { - typed_value.type = type; - } +class Object { + public: + static bool classof(const Object* obj); + static const char* GetTypeName() { return "Object"; } + using Ptr = RefPtr<Object>; - Type type; - TypedValue typed_value; - bool mutable_; - Index import_index = kInvalidIndex; /* kInvalidIndex if not imported */ -}; + Object(const Object&) = delete; + Object& operator=(const Object&) = delete; -struct Import { - Import(ExternalKind kind, string_view module_name, string_view field_name) - : kind(kind), - module_name(module_name.to_string()), - field_name(field_name.to_string()) {} + virtual ~Object(); - ExternalKind kind; - std::string module_name; - std::string field_name; + ObjectKind kind() const; + Ref self() const; + + void* host_info() const; + void set_host_info(void*); + + Finalizer get_finalizer() const; + void set_finalizer(Finalizer); + + protected: + friend Store; + explicit Object(ObjectKind); + virtual void Mark(Store&) {} + + ObjectKind kind_; + Finalizer finalizer_ = nullptr; + void* host_info_ = nullptr; + Ref self_ = Ref::Null; }; -struct Func { - WABT_DISALLOW_COPY_AND_ASSIGN(Func); - Func(Index sig_index, bool is_host) - : sig_index(sig_index), is_host(is_host) {} - virtual ~Func() {} +class Foreign : public Object { + public: + static const ObjectKind skind = ObjectKind::Foreign; + static bool classof(const Object* obj); + static const char* GetTypeName() { return "Foreign"; } + using Ptr = RefPtr<Foreign>; + + static Foreign::Ptr New(Store&, void*); - Index sig_index; - bool is_host; + void* ptr(); + + private: + friend Store; + explicit Foreign(Store&, void*); + void Mark(Store&) override; + + void* ptr_; }; -struct DefinedFunc : Func { - DefinedFunc(Index sig_index) - : Func(sig_index, false), - offset(kInvalidIstreamOffset), - local_decl_count(0), - local_count(0) {} +class Trap : public Object { + public: + static const ObjectKind skind = ObjectKind::Trap; + static bool classof(const Object* obj); + using Ptr = RefPtr<Trap>; - static bool classof(const Func* func) { return !func->is_host; } + static Trap::Ptr New(Store&, + const std::string& msg, + const std::vector<Frame>& trace = std::vector<Frame>()); - IstreamOffset offset; - Index local_decl_count; - Index local_count; - std::vector<Type> param_and_local_types; + std::string message() const; + + private: + friend Store; + explicit Trap(Store&, + const std::string& msg, + const std::vector<Frame>& trace = std::vector<Frame>()); + void Mark(Store&) override; + + std::string message_; + std::vector<Frame> trace_; }; -struct HostFunc : Func { - using Callback = std::function<Result(const HostFunc*, - const FuncSignature*, - const TypedValues& args, - TypedValues& results)>; +class Extern : public Object { + public: + static bool classof(const Object* obj); + static const char* GetTypeName() { return "Foreign"; } + using Ptr = RefPtr<Extern>; - HostFunc(string_view module_name, - string_view field_name, - Index sig_index, - Callback callback) - : Func(sig_index, true), - module_name(module_name.to_string()), - field_name(field_name.to_string()), - callback(callback) {} + virtual Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) = 0; + virtual const ExternType& extern_type() = 0; - static bool classof(const Func* func) { return func->is_host; } + protected: + friend Store; + explicit Extern(ObjectKind); - std::string module_name; - std::string field_name; - Callback callback; + template <typename T> + Result MatchImpl(Store&, + const ImportType&, + const T& actual, + Trap::Ptr* out_trap); }; -struct Export { - Export(string_view name, ExternalKind kind, Index index) - : name(name.to_string()), kind(kind), index(index) {} +class Func : public Extern { + public: + static bool classof(const Object* obj); + using Ptr = RefPtr<Func>; - std::string name; - ExternalKind kind; - Index index; + Result Call(Thread& thread, + const Values& params, + Values& results, + Trap::Ptr* out_trap); + + // Convenience function that creates new Thread. + Result Call(Store&, + const Values& params, + Values& results, + Trap::Ptr* out_trap, + Stream* = nullptr); + + const ExternType& extern_type() override; + const FuncType& type() const; + + protected: + explicit Func(ObjectKind, FuncType); + virtual Result DoCall(Thread& thread, + const Values& params, + Values& results, + Trap::Ptr* out_trap) = 0; + + FuncType type_; }; -class Environment; +class DefinedFunc : public Func { + public: + static bool classof(const Object* obj); + static const ObjectKind skind = ObjectKind::DefinedFunc; + static const char* GetTypeName() { return "DefinedFunc"; } + using Ptr = RefPtr<DefinedFunc>; + + static DefinedFunc::Ptr New(Store&, Ref instance, FuncDesc); + + Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) override; + + Ref instance() const; + const FuncDesc& desc() const; -struct ModuleMetadata { - WABT_DISALLOW_COPY_AND_ASSIGN(ModuleMetadata); - ModuleMetadata() = default; - std::vector<Import> imports; - std::vector<Export> exports; + protected: + Result DoCall(Thread& thread, + const Values& params, + Values& results, + Trap::Ptr* out_trap) override; + + private: + friend Store; + explicit DefinedFunc(Store&, Ref instance, FuncDesc); + void Mark(Store&) override; + + Ref instance_; + FuncDesc desc_; }; -struct Module { - WABT_DISALLOW_COPY_AND_ASSIGN(Module); - Module(Environment* env, bool is_host); - Module(Environment* env, string_view name, bool is_host); - virtual ~Module() = default; +class HostFunc : public Func { + public: + static bool classof(const Object* obj); + static const ObjectKind skind = ObjectKind::HostFunc; + static const char* GetTypeName() { return "HostFunc"; } + using Ptr = RefPtr<HostFunc>; - // Function exports are special-cased to allow for overloading functions by - // name. - Export* GetFuncExport(Environment*, string_view name, Index sig_index); - Export* GetExport(string_view name); - virtual Index OnUnknownFuncExport(string_view name, Index sig_index) = 0; + using Callback = std::function< + Result(const Values& params, Values& results, Trap::Ptr* out_trap)>; - // Returns export index. - Index AppendExport(ExternalKind kind, Index item_index, string_view name); + static HostFunc::Ptr New(Store&, FuncType, Callback); - std::string name; - std::vector<Export> exports; - BindingHash export_bindings; - Index memory_index; /* kInvalidIndex if not defined */ - bool is_host; + Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) override; protected: - Environment* env; + Result DoCall(Thread& thread, + const Values& params, + Values& results, + Trap::Ptr* out_trap) override; + + private: + friend Store; + friend Thread; + explicit HostFunc(Store&, FuncType, Callback); + void Mark(Store&) override; + + Callback callback_; }; -struct DefinedModule : Module { - explicit DefinedModule(Environment* env); - static bool classof(const Module* module) { return !module->is_host; } +class Table : public Extern { + public: + static bool classof(const Object* obj); + static const ObjectKind skind = ObjectKind::Table; + static const char* GetTypeName() { return "Table"; } + using Ptr = RefPtr<Table>; + + static Table::Ptr New(Store&, TableType); + + Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) override; + + bool IsValidRange(u32 offset, u32 size) const; + + Result Get(u32 offset, Ref* out) const; + Result Set(Store&, u32 offset, Ref); + Result Grow(Store&, u32 count, Ref); + Result Fill(Store&, u32 offset, Ref, u32 size); + Result Init(Store&, + u32 dst_offset, + const ElemSegment&, + u32 src_offset, + u32 size); + static Result Copy(Store&, + Table& dst, + u32 dst_offset, + const Table& src, + u32 src_offset, + u32 size); + + // Unsafe API. + Ref UnsafeGet(u32 offset) const; + + const ExternType& extern_type() override; + const TableType& type() const; + const RefVec& elements() const; + u32 size() const; - Index OnUnknownFuncExport(string_view name, Index sig_index) override { - return kInvalidIndex; - } + private: + friend Store; + explicit Table(Store&, TableType); + void Mark(Store&) override; - Index start_func_index; /* kInvalidIndex if not defined */ - IstreamOffset istream_start; - IstreamOffset istream_end; + TableType type_; + RefVec elements_; +}; - // Changes to linear memory and tables should not apply if a validation error - // occurs; these vectors cache the changes that must be applied after we know - // that there are no validation errors. - // - // Note that this behavior changed after the bulk memory proposal; in that - // case each segment is initialized as it is encountered. If one fails, then - // no further segments are processed. - std::vector<ElemSegmentInfo> active_elem_segments_; - std::vector<DataSegmentInfo> active_data_segments_; -}; - -struct HostModule : Module { - HostModule(Environment* env, string_view name); - static bool classof(const Module* module) { return module->is_host; } - - Index OnUnknownFuncExport(string_view name, Index sig_index) override; - - std::pair<HostFunc*, Index> AppendFuncExport(string_view name, - const FuncSignature&, - HostFunc::Callback); - std::pair<HostFunc*, Index> AppendFuncExport(string_view name, - Index sig_index, - HostFunc::Callback); - std::pair<Table*, Index> AppendTableExport(string_view name, - Type elem_type, - const Limits&); - std::pair<Memory*, Index> AppendMemoryExport(string_view name, const Limits&); - std::pair<Global*, Index> AppendGlobalExport(string_view name, - Type, - bool mutable_); - - // Convenience functions. - std::pair<Global*, Index> AppendGlobalExport(string_view name, - bool mutable_, - uint32_t); - std::pair<Global*, Index> AppendGlobalExport(string_view name, - bool mutable_, - uint64_t); - std::pair<Global*, Index> AppendGlobalExport(string_view name, - bool mutable_, - float); - std::pair<Global*, Index> AppendGlobalExport(string_view name, - bool mutable_, - double); - - // Should return an Export index if a new function was created via - // AppendFuncExport, or kInvalidIndex if no function was created. - std::function< - Index(Environment*, HostModule*, string_view name, Index sig_index)> - on_unknown_func_export; -}; - -class Environment { +class Memory : public Extern { public: - // Used to track and reset the state of the environment. - struct MarkPoint { - size_t modules_size = 0; - size_t sigs_size = 0; - size_t funcs_size = 0; - size_t memories_size = 0; - size_t tables_size = 0; - size_t globals_size = 0; - size_t data_segments_size = 0; - size_t elem_segments_size = 0; - size_t istream_size = 0; - }; + static bool classof(const Object* obj); + static const ObjectKind skind = ObjectKind::Memory; + static const char* GetTypeName() { return "Memory"; } + using Ptr = RefPtr<Memory>; - explicit Environment(const Features& features); - - OutputBuffer& istream() { return *istream_; } - void SetIstream(std::unique_ptr<OutputBuffer> istream) { - istream_ = std::move(istream); - } - std::unique_ptr<OutputBuffer> ReleaseIstream() { return std::move(istream_); } - - Index GetFuncSignatureCount() const { return sigs_.size(); } - Index GetFuncCount() const { return funcs_.size(); } - Index GetGlobalCount() const { return globals_.size(); } - Index GetMemoryCount() const { return memories_.size(); } - Index GetTableCount() const { return tables_.size(); } - Index GetDataSegmentCount() const { return data_segments_.size(); } - Index GetElemSegmentCount() const { return elem_segments_.size(); } - Index GetModuleCount() const { return modules_.size(); } - Index GetHostCount() const { return host_objects_.size(); } - - Index GetLastModuleIndex() const { - return modules_.empty() ? kInvalidIndex : modules_.size() - 1; - } - Index FindModuleIndex(string_view name) const; - - FuncSignature* GetFuncSignature(Index index) { return &sigs_[index]; } - Func* GetFunc(Index index) { - assert(index < funcs_.size()); - return funcs_[index].get(); - } - Global* GetGlobal(Index index) { - assert(index < globals_.size()); - return &globals_[index]; - } - Memory* GetMemory(Index index) { - assert(index < memories_.size()); - return &memories_[index]; - } - Table* GetTable(Index index) { - assert(index < tables_.size()); - return &tables_[index]; - } - DataSegment* GetDataSegment(Index index) { - assert(index < data_segments_.size()); - return &data_segments_[index]; - } - ElemSegment* GetElemSegment(Index index) { - assert(index < elem_segments_.size()); - return &elem_segments_[index]; - } - Module* GetModule(Index index) { - assert(index < modules_.size()); - return modules_[index].get(); - } - - Module* GetLastModule() { - return modules_.empty() ? nullptr : modules_.back().get(); - } - Module* FindModule(string_view name); - Module* FindRegisteredModule(string_view name); + static Memory::Ptr New(Store&, MemoryType); - template <typename... Args> - FuncSignature* EmplaceBackFuncSignature(Args&&... args) { - sigs_.emplace_back(std::forward<Args>(args)...); - return &sigs_.back(); - } + Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) override; - template <typename... Args> - Func* EmplaceBackFunc(Args&&... args) { - funcs_.emplace_back(std::forward<Args>(args)...); - return funcs_.back().get(); - } + bool IsValidAccess(u32 offset, u32 addend, size_t size) const; + bool IsValidAtomicAccess(u32 offset, u32 addend, size_t size) const; - template <typename... Args> - Global* EmplaceBackGlobal(Args&&... args) { - globals_.emplace_back(std::forward<Args>(args)...); - return &globals_.back(); - } + template <typename T> + Result Load(u32 offset, u32 addend, T* out) const; + template <typename T> + Result WABT_VECTORCALL Store(u32 offset, u32 addend, T); + Result Grow(u32 pages); + Result Fill(u32 offset, u8 value, u32 size); + Result Init(u32 dst_offset, const DataSegment&, u32 src_offset, u32 size); + static Result Copy(Memory& dst, + u32 dst_offset, + const Memory& src, + u32 src_offset, + u32 size); + + // Fake atomics; just checks alignment. + template <typename T> + Result AtomicLoad(u32 offset, u32 addend, T* out) const; + template <typename T> + Result AtomicStore(u32 offset, u32 addend, T); + template <typename T, typename F> + Result AtomicRmw(u32 offset, u32 addend, T, F&& func, T* out); + template <typename T> + Result AtomicRmwCmpxchg(u32 offset, u32 addend, T expect, T replace, T* out); - template <typename... Args> - Table* EmplaceBackTable(Args&&... args) { - tables_.emplace_back(std::forward<Args>(args)...); - return &tables_.back(); - } + u32 ByteSize() const; + u32 PageSize() const; - template <typename... Args> - Memory* EmplaceBackMemory(Args&&... args) { - memories_.emplace_back(std::forward<Args>(args)...); - return &memories_.back(); - } + // Unsafe API. + template <typename T> + T WABT_VECTORCALL UnsafeLoad(u32 offset, u32 addend) const; + u8* UnsafeData(); - template <typename... Args> - DataSegment* EmplaceBackDataSegment(Args&&... args) { - data_segments_.emplace_back(std::forward<Args>(args)...); - return &data_segments_.back(); - } + const ExternType& extern_type() override; + const MemoryType& type() const; - template <typename... Args> - ElemSegment* EmplaceBackElemSegment(Args&&... args) { - elem_segments_.emplace_back(std::forward<Args>(args)...); - return &elem_segments_.back(); - } + private: + friend class Store; + explicit Memory(class Store&, MemoryType); + void Mark(class Store&) override; - template <typename... Args> - Module* EmplaceBackModule(Args&&... args) { - modules_.emplace_back(std::forward<Args>(args)...); - return modules_.back().get(); - } + MemoryType type_; + Buffer data_; + u32 pages_; +}; - template <typename... Args> - HostObject* EmplaceBackHostObject(Args&&... args) { - host_objects_.emplace_back(std::forward<Args>(args)...); - return &host_objects_.back(); - } +class Global : public Extern { + public: + static bool classof(const Object* obj); + static const ObjectKind skind = ObjectKind::Global; + static const char* GetTypeName() { return "Global"; } + using Ptr = RefPtr<Global>; - template <typename... Args> - void EmplaceModuleBinding(Args&&... args) { - module_bindings_.emplace(std::forward<Args>(args)...); - } + static Global::Ptr New(Store&, GlobalType, Value); - template <typename... Args> - void EmplaceRegisteredModuleBinding(Args&&... args) { - registered_module_bindings_.emplace(std::forward<Args>(args)...); - } + Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) override; - HostModule* AppendHostModule(string_view name); + Value Get() const; + template <typename T> + Result Get(T* out) const; + template <typename T> + Result WABT_VECTORCALL Set(T); + Result Set(Store&, Ref); - bool FuncSignaturesAreEqual(Index sig_index_0, Index sig_index_1) const; + template <typename T> + T WABT_VECTORCALL UnsafeGet() const; + void UnsafeSet(Value); - MarkPoint Mark(); - void ResetToMarkPoint(const MarkPoint&); + const ExternType& extern_type() override; + const GlobalType& type() const; - void Disassemble(Stream* stream, IstreamOffset from, IstreamOffset to); - void DisassembleModule(Stream* stream, Module*); + private: + friend Store; + explicit Global(Store&, GlobalType, Value); + void Mark(Store&) override; - // Called when a module name isn't found in registered_module_bindings_. If - // you want to provide a module with this name, call AppendHostModule() with - // this name and return true. - std::function<bool(Environment*, string_view name)> on_unknown_module; + GlobalType type_; + Value value_; +}; - Features features_; +class Event : public Extern { + public: + static bool classof(const Object* obj); + static const ObjectKind skind = ObjectKind::Event; + static const char* GetTypeName() { return "Event"; } + using Ptr = RefPtr<Event>; + + static Event::Ptr New(Store&, EventType); + + Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) override; + + const ExternType& extern_type() override; + const EventType& type() const; private: - friend class Thread; - - std::vector<std::unique_ptr<Module>> modules_; - std::vector<FuncSignature> sigs_; - std::vector<std::unique_ptr<Func>> funcs_; - std::vector<Memory> memories_; - std::vector<Table> tables_; - std::vector<Global> globals_; - std::vector<DataSegment> data_segments_; - std::vector<ElemSegment> elem_segments_; - std::vector<HostObject> host_objects_; - std::unique_ptr<OutputBuffer> istream_; - BindingHash module_bindings_; - BindingHash registered_module_bindings_; -}; - -class Thread { + friend Store; + explicit Event(Store&, EventType); + void Mark(Store&) override; + + EventType type_; +}; + +class ElemSegment { public: - struct Options { - static const uint32_t kDefaultValueStackSize = 512 * 1024 / sizeof(Value); - static const uint32_t kDefaultCallStackSize = 64 * 1024; + explicit ElemSegment(const ElemDesc*, RefPtr<Instance>&); - explicit Options(uint32_t value_stack_size = kDefaultValueStackSize, - uint32_t call_stack_size = kDefaultCallStackSize); + bool IsValidRange(u32 offset, u32 size) const; + void Drop(); - uint32_t value_stack_size; - uint32_t call_stack_size; - }; + const ElemDesc& desc() const; + const RefVec& elements() const; + u32 size() const; - explicit Thread(Environment*, const Options& = Options()); + private: + friend Instance; + void Mark(Store&); - Environment* env() { return env_; } + const ElemDesc* desc_; // Borrowed from the Module. + RefVec elements_; +}; - void set_pc(IstreamOffset offset) { pc_ = offset; } - IstreamOffset pc() const { return pc_; } +class DataSegment { + public: + explicit DataSegment(const DataDesc*); - void Reset(); - Index NumValues() const { return value_stack_top_; } - Result Push(Value) WABT_WARN_UNUSED; - Value Pop(); - Value ValueAt(Index at) const; + bool IsValidRange(u32 offset, u32 size) const; + void Drop(); + + const DataDesc& desc() const; + u32 size() const; + + private: + const DataDesc* desc_; // Borrowed from the Module. + u32 size_; +}; + +class Module : public Object { + public: + static bool classof(const Object* obj); + static const ObjectKind skind = ObjectKind::Module; + static const char* GetTypeName() { return "Module"; } + using Ptr = RefPtr<Module>; + + static Module::Ptr New(Store&, ModuleDesc); + + const ModuleDesc& desc() const; + const std::vector<ImportType>& import_types() const; + const std::vector<ExportType>& export_types() const; - void Trace(Stream*); - Result Run(int num_instructions = 1); + private: + friend Store; + friend Instance; + explicit Module(Store&, ModuleDesc); + void Mark(Store&) override; + + ModuleDesc desc_; + std::vector<ImportType> import_types_; + std::vector<ExportType> export_types_; +}; - Result CallHost(HostFunc*); +class Instance : public Object { + public: + static bool classof(const Object* obj); + static const ObjectKind skind = ObjectKind::Instance; + static const char* GetTypeName() { return "Instance"; } + using Ptr = RefPtr<Instance>; + + static Instance::Ptr Instantiate(Store&, + Ref module, + const RefVec& imports, + Trap::Ptr* out_trap); + + Ref module() const; + const RefVec& imports() const; + const RefVec& funcs() const; + const RefVec& tables() const; + const RefVec& memories() const; + const RefVec& globals() const; + const RefVec& events() const; + const RefVec& exports() const; + const std::vector<ElemSegment>& elems() const; + std::vector<ElemSegment>& elems(); + const std::vector<DataSegment>& datas() const; + std::vector<DataSegment>& datas(); private: - const uint8_t* GetIstream() const { return env_->istream_->data.data(); } + friend Store; + friend ElemSegment; + friend DataSegment; + explicit Instance(Store&, Ref module); + void Mark(Store&) override; + + Value ResolveInitExpr(Store&, InitExpr); + + Ref module_; + RefVec imports_; + RefVec funcs_; + RefVec tables_; + RefVec memories_; + RefVec globals_; + RefVec events_; + RefVec exports_; + std::vector<ElemSegment> elems_; + std::vector<DataSegment> datas_; +}; + +enum class RunResult { + Ok, + Return, + Trap, +}; + +// TODO: Kinda weird to have a thread as an object, but it makes reference +// marking simpler. +class Thread : public Object { + public: + static bool classof(const Object* obj); + static const ObjectKind skind = ObjectKind::Thread; + static const char* GetTypeName() { return "Thread"; } + using Ptr = RefPtr<Thread>; - Memory* ReadMemory(const uint8_t** pc); - template <typename MemType> - Result GetAccessAddress(const uint8_t** pc, void** out_address); - template <typename MemType> - Result GetAtomicAccessAddress(const uint8_t** pc, void** out_address); + struct Options { + static const u32 kDefaultValueStackSize = 64 * 1024 / sizeof(Value); + static const u32 kDefaultCallStackSize = 64 * 1024 / sizeof(Frame); - Table* ReadTable(const uint8_t** pc); + u32 value_stack_size = kDefaultValueStackSize; + u32 call_stack_size = kDefaultCallStackSize; + Stream* trace_stream = nullptr; + }; - DataSegment* ReadDataSegment(const uint8_t** pc); - ElemSegment* ReadElemSegment(const uint8_t** pc); + static Thread::Ptr New(Store&, const Options&); - Value& Top(); - Value& Pick(Index depth); + RunResult Run(Trap::Ptr* out_trap); + RunResult Run(int num_instructions, Trap::Ptr* out_trap); + RunResult Step(Trap::Ptr* out_trap); + + Store& store(); + + private: + friend Store; + friend DefinedFunc; + + struct TraceSource; + + explicit Thread(Store&, const Options&); + void Mark(Store&) override; + + RunResult PushCall(Ref func, u32 offset, Trap::Ptr* out_trap); + RunResult PushCall(const DefinedFunc&, Trap::Ptr* out_trap); + RunResult PushCall(const HostFunc&, Trap::Ptr* out_trap); + RunResult PopCall(); + RunResult DoCall(const Func::Ptr&, Trap::Ptr* out_trap); + RunResult DoReturnCall(const Func::Ptr&, Trap::Ptr* out_trap); + + void PushValues(const ValueTypes&, const Values&); + void PopValues(const ValueTypes&, Values*); + + Value& Pick(Index); - // Push/Pop values with conversions, e.g. Push<float> will convert to the - // ValueTypeRep (uint32_t) and push that. Similarly, Pop<float> will pop the - // value and convert to float. - template <typename T> - Result Push(T) WABT_WARN_UNUSED; template <typename T> - T Pop(); + T WABT_VECTORCALL Pop(); + Value Pop(); - // Push/Pop values without conversions, e.g. PushRep<float> will take a - // uint32_t argument which is the integer representation of that float value. - // Similarly, PopRep<float> will not convert the value to a float. template <typename T> - Result PushRep(ValueTypeRep<T>) WABT_WARN_UNUSED; + void WABT_VECTORCALL Push(T); + void Push(Value); + void Push(Ref); + + template <typename R, typename T> + using UnopFunc = R WABT_VECTORCALL(T); + template <typename R, typename T> + using UnopTrapFunc = RunResult WABT_VECTORCALL(T, R*, std::string*); + template <typename R, typename T> + using BinopFunc = R WABT_VECTORCALL(T, T); + template <typename R, typename T> + using BinopTrapFunc = RunResult WABT_VECTORCALL(T, T, R*, std::string*); + + template <typename R, typename T> + RunResult DoUnop(UnopFunc<R, T>); + template <typename R, typename T> + RunResult DoUnop(UnopTrapFunc<R, T>, Trap::Ptr* out_trap); + template <typename R, typename T> + RunResult DoBinop(BinopFunc<R, T>); + template <typename R, typename T> + RunResult DoBinop(BinopTrapFunc<R, T>, Trap::Ptr* out_trap); + + template <typename R, typename T> + RunResult DoConvert(Trap::Ptr* out_trap); + template <typename R, typename T> + RunResult DoReinterpret(); + template <typename T> - ValueTypeRep<T> PopRep(); - - void DropKeep(uint32_t drop_count, uint32_t keep_count); - - Result PushCall(const uint8_t* pc) WABT_WARN_UNUSED; - IstreamOffset PopCall(); - - template <typename R, typename T> using UnopFunc = R(T); - template <typename R, typename T> using UnopTrapFunc = Result(T, R*); - template <typename R, typename T> using BinopFunc = R(T, T); - template <typename R, typename T> using BinopTrapFunc = Result(T, T, R*); - - template <typename MemType, typename ResultType = MemType> - Result Load(const uint8_t** pc) WABT_WARN_UNUSED; - template <typename MemType, typename ResultType = MemType> - Result Store(const uint8_t** pc) WABT_WARN_UNUSED; - template <typename MemType, typename ResultType = MemType> - Result AtomicLoad(const uint8_t** pc) WABT_WARN_UNUSED; - template <typename MemType, typename ResultType = MemType> - Result AtomicStore(const uint8_t** pc) WABT_WARN_UNUSED; - template <typename MemType, typename ResultType = MemType> - Result AtomicRmw(BinopFunc<ResultType, ResultType>, - const uint8_t** pc) WABT_WARN_UNUSED; - template <typename MemType, typename ResultType = MemType> - Result AtomicRmwCmpxchg(const uint8_t** pc) WABT_WARN_UNUSED; - - Result MemoryInit(const uint8_t** pc) WABT_WARN_UNUSED; - Result DataDrop(const uint8_t** pc) WABT_WARN_UNUSED; - Result MemoryCopy(const uint8_t** pc) WABT_WARN_UNUSED; - Result MemoryFill(const uint8_t** pc) WABT_WARN_UNUSED; - Result TableInit(const uint8_t** pc) WABT_WARN_UNUSED; - Result TableGet(const uint8_t** pc) WABT_WARN_UNUSED; - Result TableSet(const uint8_t** pc) WABT_WARN_UNUSED; - Result TableFill(const uint8_t** pc) WABT_WARN_UNUSED; - Result ElemDrop(const uint8_t** pc) WABT_WARN_UNUSED; - Result TableCopy(const uint8_t** pc) WABT_WARN_UNUSED; - Result RefFunc(const uint8_t** pc) WABT_WARN_UNUSED; - - template <typename R, typename T = R> - Result Unop(UnopFunc<R, T> func) WABT_WARN_UNUSED; - template <typename R, typename T = R> - Result UnopTrap(UnopTrapFunc<R, T> func) WABT_WARN_UNUSED; - - template <typename T, typename L, typename R, typename P = R> - Result SimdUnop(UnopFunc<R, P> func) WABT_WARN_UNUSED; - - template <typename R, typename T = R> - Result Binop(BinopFunc<R, T> func) WABT_WARN_UNUSED; - template <typename R, typename T = R> - Result BinopTrap(BinopTrapFunc<R, T> func) WABT_WARN_UNUSED; - - template <typename T, typename L, typename R, typename P = R> - Result SimdBinop(BinopFunc<R, P> func) WABT_WARN_UNUSED; - - template <typename T, typename L, typename R, typename P = R> - Result SimdRelBinop(BinopFunc<R, P> func) WABT_WARN_UNUSED; - - Environment* env_ = nullptr; - std::vector<Value> value_stack_; - std::vector<IstreamOffset> call_stack_; - uint32_t value_stack_top_ = 0; - uint32_t call_stack_top_ = 0; - IstreamOffset pc_ = 0; -}; - -struct ExecResult { - ExecResult() = default; - explicit ExecResult(Result result) : result(result) {} - bool ok() const { return result.ok(); } - ExecResult(Result result, const TypedValues& values) - : result(result), values(values) {} - - Result result = ResultType::Ok; - TypedValues values; -}; - -class Executor { - public: - explicit Executor(Environment*, - Stream* trace_stream = nullptr, - const Thread::Options& options = Thread::Options()); + RunResult Load(Instr, T* out, Trap::Ptr* out_trap); + template <typename T, typename V = T> + RunResult DoLoad(Instr, Trap::Ptr* out_trap); + template <typename T, typename V = T> + RunResult DoStore(Instr, Trap::Ptr* out_trap); + + RunResult DoMemoryInit(Instr, Trap::Ptr* out_trap); + RunResult DoDataDrop(Instr); + RunResult DoMemoryCopy(Instr, Trap::Ptr* out_trap); + RunResult DoMemoryFill(Instr, Trap::Ptr* out_trap); + + RunResult DoTableInit(Instr, Trap::Ptr* out_trap); + RunResult DoElemDrop(Instr); + RunResult DoTableCopy(Instr, Trap::Ptr* out_trap); + RunResult DoTableGet(Instr, Trap::Ptr* out_trap); + RunResult DoTableSet(Instr, Trap::Ptr* out_trap); + RunResult DoTableGrow(Instr, Trap::Ptr* out_trap); + RunResult DoTableSize(Instr); + RunResult DoTableFill(Instr, Trap::Ptr* out_trap); + + template <typename R, typename T> + RunResult DoSimdSplat(); + template <typename R, typename T> + RunResult DoSimdExtract(Instr); + template <typename R, typename T> + RunResult DoSimdReplace(Instr); + + template <typename R, typename T> + RunResult DoSimdUnop(UnopFunc<R, T>); + template <typename R, typename T> + RunResult DoSimdBinop(BinopFunc<R, T>); + RunResult DoSimdBitSelect(); + template <typename S, u8 count> + RunResult DoSimdIsTrue(); + template <typename R, typename T> + RunResult DoSimdShift(BinopFunc<R, T>); + template <typename S, typename T> + RunResult DoSimdLoadSplat(Instr, Trap::Ptr* out_trap); + RunResult DoSimdSwizzle(); + RunResult DoSimdShuffle(Instr); + template <typename S, typename T> + RunResult DoSimdNarrow(); + template <typename S, typename T, bool low> + RunResult DoSimdWiden(); + template <typename S, typename T> + RunResult DoSimdLoadExtend(Instr, Trap::Ptr* out_trap); + + template <typename T, typename V = T> + RunResult DoAtomicLoad(Instr, Trap::Ptr* out_trap); + template <typename T, typename V = T> + RunResult DoAtomicStore(Instr, Trap::Ptr* out_trap); + template <typename R, typename T> + RunResult DoAtomicRmw(BinopFunc<T, T>, Instr, Trap::Ptr* out_trap); + template <typename T, typename V = T> + RunResult DoAtomicRmwCmpxchg(Instr, Trap::Ptr* out_trap); + + RunResult StepInternal(Trap::Ptr* out_trap); + + std::vector<Frame> frames_; + std::vector<Value> values_; + std::vector<u32> refs_; // Index into values_. + + // Cached for convenience. + Store& store_; + Instance* inst_ = nullptr; + Module* mod_ = nullptr; + + // Tracing. + Stream* trace_stream_; + std::unique_ptr<TraceSource> trace_source_; +}; - ExecResult RunFunction(Index func_index, const TypedValues& args); - ExecResult Initialize(DefinedModule* module); - ExecResult RunExport(const Export*, const TypedValues& args); - ExecResult RunExportByName(Module* module, - string_view name, - const TypedValues& args); +struct Thread::TraceSource : Istream::TraceSource { + public: + explicit TraceSource(Thread*); + std::string Header(Istream::Offset) override; + std::string Pick(Index, Instr) override; private: - ExecResult RunStartFunction(DefinedModule* module); - Result InitializeSegments(DefinedModule* module); - Result RunDefinedFunction(IstreamOffset function_offset); - Result PushArgs(const FuncSignature*, const TypedValues& args); - void CopyResults(const FuncSignature*, TypedValues* out_results); - - Environment* env_ = nullptr; - Stream* trace_stream_ = nullptr; - Thread thread_; -}; - -bool IsCanonicalNan(uint32_t f32_bits); -bool IsCanonicalNan(uint64_t f64_bits); -bool IsArithmeticNan(uint32_t f32_bits); -bool IsArithmeticNan(uint64_t f64_bits); - -std::string RefTypeToString(RefType t); -std::string TypedValueToString(const TypedValue&); -std::string ResultToString(Result); -const char* ResultTypeToString(ResultType); - -void WriteTypedValue(Stream* stream, const TypedValue&); -void WriteTypedValues(Stream* stream, const TypedValues&); -void WriteResult(Stream* stream, const char* desc, Result); -void WriteCall(Stream* stream, - string_view module_name, - string_view func_name, - const TypedValues& args, - const TypedValues& results, - Result); + ValueType GetLocalType(Index); + ValueType GetGlobalType(Index); + ValueType GetTableElementType(Index); + + Thread* thread_; +}; } // namespace interp } // namespace wabt -#endif /* WABT_INTERP_H_ */ +#include "src/interp/interp-inl.h" + +#endif // WABT_INTERP_H_ diff --git a/src/interp/interp-trace.cc b/src/interp/istream.cc index c8ff02bb..51ce23d0 100644 --- a/src/interp/interp-trace.cc +++ b/src/interp/istream.cc @@ -1,5 +1,5 @@ /* - * Copyright 2018 WebAssembly Community Group participants + * Copyright 2020 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,768 +14,850 @@ * limitations under the License. */ -#include "src/interp/interp.h" +#include "src/interp/istream.h" #include <cinttypes> -#include "src/interp/interp-internal.h" - namespace wabt { namespace interp { -void Thread::Trace(Stream* stream) { - const uint8_t* istream = GetIstream(); - const uint8_t* pc = &istream[pc_]; +template <typename T> +void WABT_VECTORCALL Istream::EmitAt(Offset offset, T val) { + u32 new_size = offset + sizeof(T); + if (new_size > data_.size()) { + data_.resize(new_size); + } + memcpy(data_.data() + offset, &val, sizeof(val)); +} - stream->Writef("#%u. %4" PRIzd ": V:%-3u| ", call_stack_top_, pc - istream, - value_stack_top_); +template <typename T> +void WABT_VECTORCALL Istream::EmitInternal(T val) { + EmitAt(end(), val); +} - Opcode opcode = ReadOpcode(&pc); - assert(!opcode.IsInvalid()); - switch (opcode) { - case Opcode::Select: - case Opcode::SelectT: - // TODO(binji): We don't know the type here so we can't display the value - // to the user. This used to display the full 64-bit value, but that - // will potentially display garbage if the value is 32-bit. - stream->Writef("%s %u, %%[-2], %%[-1]\n", opcode.GetName(), Pick(3).i32); - break; +void Istream::Emit(u32 val) { + EmitInternal(val); +} - case Opcode::Br: - stream->Writef("%s @%u\n", opcode.GetName(), ReadU32At(pc)); - break; +void Istream::Emit(Opcode::Enum op) { + EmitInternal(static_cast<SerializedOpcode>(op)); +} - case Opcode::BrIf: - stream->Writef("%s @%u, %u\n", opcode.GetName(), ReadU32At(pc), - Top().i32); - break; +void Istream::Emit(Opcode::Enum op, u8 val) { + Emit(op); + EmitInternal(val); +} - case Opcode::BrTable: { - const Index num_targets = ReadU32At(pc); - const IstreamOffset table_offset = ReadU32At(pc + 4); - const uint32_t key = Top().i32; - stream->Writef("%s %u, $#%" PRIindex ", table:$%u\n", opcode.GetName(), - key, num_targets, table_offset); - break; +void Istream::Emit(Opcode::Enum op, u32 val) { + Emit(op); + EmitInternal(val); +} + +void Istream::Emit(Opcode::Enum op, u64 val) { + Emit(op); + EmitInternal(val); +} + +void Istream::Emit(Opcode::Enum op, v128 val) { + Emit(op); + EmitInternal(val); +} + +void Istream::Emit(Opcode::Enum op, u32 val1, u32 val2) { + Emit(op); + EmitInternal(val1); + EmitInternal(val2); +} + +void Istream::EmitDropKeep(u32 drop, u32 keep) { + if (drop > 0) { + if (drop == 1 && keep == 0) { + Emit(Opcode::Drop); + } else { + Emit(Opcode::InterpDropKeep, drop, keep); } + } +} +Istream::Offset Istream::EmitFixupU32() { + auto result = end(); + EmitInternal(kInvalidOffset); + return result; +} + +void Istream::ResolveFixupU32(Offset fixup_offset) { + EmitAt(fixup_offset, end()); +} + +Istream::Offset Istream::end() const { + return static_cast<u32>(data_.size()); +} + +template <typename T> +T WABT_VECTORCALL Istream::ReadAt(Offset* offset) const { + assert(*offset + sizeof(T) <= data_.size()); + T result; + memcpy(&result, data_.data() + *offset, sizeof(T)); + *offset += sizeof(T); + return result; +} + +Instr Istream::Read(Offset* offset) const { + Instr instr; + instr.op = static_cast<Opcode::Enum>(ReadAt<SerializedOpcode>(offset)); + + switch (instr.op) { + case Opcode::Drop: case Opcode::Nop: case Opcode::Return: case Opcode::Unreachable: - case Opcode::Drop: - stream->Writef("%s\n", opcode.GetName()); + case Opcode::RefNull: + // 0 immediates, 0 operands. + instr.kind = InstrKind::Imm_0_Op_0; break; - case Opcode::MemorySize: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex "\n", opcode.GetName(), memory_index); + case Opcode::F32Abs: + case Opcode::F32Ceil: + case Opcode::F32ConvertI32S: + case Opcode::F32ConvertI32U: + case Opcode::F32ConvertI64S: + case Opcode::F32ConvertI64U: + case Opcode::F32DemoteF64: + case Opcode::F32Floor: + case Opcode::F32Nearest: + case Opcode::F32Neg: + case Opcode::F32ReinterpretI32: + case Opcode::F32Sqrt: + case Opcode::F32Trunc: + case Opcode::F32X4Abs: + case Opcode::F32X4ConvertI32X4S: + case Opcode::F32X4ConvertI32X4U: + case Opcode::F32X4Neg: + case Opcode::F32X4Splat: + case Opcode::F32X4Sqrt: + case Opcode::F64Abs: + case Opcode::F64Ceil: + case Opcode::F64ConvertI32S: + case Opcode::F64ConvertI32U: + case Opcode::F64ConvertI64S: + case Opcode::F64ConvertI64U: + case Opcode::F64Floor: + case Opcode::F64Nearest: + case Opcode::F64Neg: + case Opcode::F64PromoteF32: + case Opcode::F64ReinterpretI64: + case Opcode::F64Sqrt: + case Opcode::F64Trunc: + case Opcode::F64X2Abs: + case Opcode::F64X2Neg: + case Opcode::F64X2Splat: + case Opcode::F64X2Sqrt: + case Opcode::I16X8AllTrue: + case Opcode::I16X8AnyTrue: + case Opcode::I16X8Neg: + case Opcode::I16X8Splat: + case Opcode::I16X8WidenHighI8X16S: + case Opcode::I16X8WidenHighI8X16U: + case Opcode::I16X8WidenLowI8X16S: + case Opcode::I16X8WidenLowI8X16U: + case Opcode::I32Clz: + case Opcode::I32Ctz: + case Opcode::I32Eqz: + case Opcode::I32Extend16S: + case Opcode::I32Extend8S: + case Opcode::I32Popcnt: + case Opcode::I32ReinterpretF32: + case Opcode::I32TruncF32S: + case Opcode::I32TruncF32U: + case Opcode::I32TruncF64S: + case Opcode::I32TruncF64U: + case Opcode::I32TruncSatF32S: + case Opcode::I32TruncSatF32U: + case Opcode::I32TruncSatF64S: + case Opcode::I32TruncSatF64U: + case Opcode::I32WrapI64: + case Opcode::I32X4AllTrue: + case Opcode::I32X4AnyTrue: + case Opcode::I32X4Neg: + case Opcode::I32X4Splat: + case Opcode::I32X4TruncSatF32X4S: + case Opcode::I32X4TruncSatF32X4U: + case Opcode::I32X4WidenHighI16X8S: + case Opcode::I32X4WidenHighI16X8U: + case Opcode::I32X4WidenLowI16X8S: + case Opcode::I32X4WidenLowI16X8U: + case Opcode::I64Clz: + case Opcode::I64Ctz: + case Opcode::I64Eqz: + case Opcode::I64Extend16S: + case Opcode::I64Extend32S: + case Opcode::I64Extend8S: + case Opcode::I64ExtendI32S: + case Opcode::I64ExtendI32U: + case Opcode::I64Popcnt: + case Opcode::I64ReinterpretF64: + case Opcode::I64TruncF32S: + case Opcode::I64TruncF32U: + case Opcode::I64TruncF64S: + case Opcode::I64TruncF64U: + case Opcode::I64TruncSatF32S: + case Opcode::I64TruncSatF32U: + case Opcode::I64TruncSatF64S: + case Opcode::I64TruncSatF64U: + case Opcode::I64X2Neg: + case Opcode::I64X2Splat: + case Opcode::I8X16AllTrue: + case Opcode::I8X16AnyTrue: + case Opcode::I8X16Neg: + case Opcode::I8X16Splat: + case Opcode::RefIsNull: + case Opcode::V128Not: + // 0 immediates, 1 operand. + instr.kind = InstrKind::Imm_0_Op_1; break; - } - case Opcode::I32Const: - stream->Writef("%s %u\n", opcode.GetName(), ReadU32At(pc)); + case Opcode::F32Add: + case Opcode::F32Copysign: + case Opcode::F32Div: + case Opcode::F32Eq: + case Opcode::F32Ge: + case Opcode::F32Gt: + case Opcode::F32Le: + case Opcode::F32Lt: + case Opcode::F32Max: + case Opcode::F32Min: + case Opcode::F32Mul: + case Opcode::F32Ne: + case Opcode::F32Sub: + case Opcode::F32X4Add: + case Opcode::F32X4Div: + case Opcode::F32X4Eq: + case Opcode::F32X4Ge: + case Opcode::F32X4Gt: + case Opcode::F32X4Le: + case Opcode::F32X4Lt: + case Opcode::F32X4Max: + case Opcode::F32X4Min: + case Opcode::F32X4Mul: + case Opcode::F32X4Ne: + case Opcode::F32X4Sub: + case Opcode::F64Add: + case Opcode::F64Copysign: + case Opcode::F64Div: + case Opcode::F64Eq: + case Opcode::F64Ge: + case Opcode::F64Gt: + case Opcode::F64Le: + case Opcode::F64Lt: + case Opcode::F64Max: + case Opcode::F64Min: + case Opcode::F64Mul: + case Opcode::F64Ne: + case Opcode::F64Sub: + case Opcode::F64X2Add: + case Opcode::F64X2Div: + case Opcode::F64X2Eq: + case Opcode::F64X2Ge: + case Opcode::F64X2Gt: + case Opcode::F64X2Le: + case Opcode::F64X2Lt: + case Opcode::F64X2Max: + case Opcode::F64X2Min: + case Opcode::F64X2Mul: + case Opcode::F64X2Ne: + case Opcode::F64X2Sub: + case Opcode::I16X8Add: + case Opcode::I16X8AddSaturateS: + case Opcode::I16X8AddSaturateU: + case Opcode::I16X8AvgrU: + case Opcode::I16X8Eq: + case Opcode::I16X8GeS: + case Opcode::I16X8GeU: + case Opcode::I16X8GtS: + case Opcode::I16X8GtU: + case Opcode::I16X8LeS: + case Opcode::I16X8LeU: + case Opcode::I16X8LtS: + case Opcode::I16X8LtU: + case Opcode::I16X8MaxS: + case Opcode::I16X8MaxU: + case Opcode::I16X8MinS: + case Opcode::I16X8MinU: + case Opcode::I16X8Mul: + case Opcode::I16X8NarrowI32X4S: + case Opcode::I16X8NarrowI32X4U: + case Opcode::I16X8Ne: + case Opcode::I16X8Shl: + case Opcode::I16X8ShrS: + case Opcode::I16X8ShrU: + case Opcode::I16X8Sub: + case Opcode::I16X8SubSaturateS: + case Opcode::I16X8SubSaturateU: + case Opcode::I32Add: + case Opcode::I32And: + case Opcode::I32DivS: + case Opcode::I32DivU: + case Opcode::I32Eq: + case Opcode::I32GeS: + case Opcode::I32GeU: + case Opcode::I32GtS: + case Opcode::I32GtU: + case Opcode::I32LeS: + case Opcode::I32LeU: + case Opcode::I32LtS: + case Opcode::I32LtU: + case Opcode::I32Mul: + case Opcode::I32Ne: + case Opcode::I32Or: + case Opcode::I32RemS: + case Opcode::I32RemU: + case Opcode::I32Rotl: + case Opcode::I32Rotr: + case Opcode::I32Shl: + case Opcode::I32ShrS: + case Opcode::I32ShrU: + case Opcode::I32Sub: + case Opcode::I32X4Add: + case Opcode::I32X4Eq: + case Opcode::I32X4GeS: + case Opcode::I32X4GeU: + case Opcode::I32X4GtS: + case Opcode::I32X4GtU: + case Opcode::I32X4LeS: + case Opcode::I32X4LeU: + case Opcode::I32X4LtS: + case Opcode::I32X4LtU: + case Opcode::I32X4MaxS: + case Opcode::I32X4MaxU: + case Opcode::I32X4MinS: + case Opcode::I32X4MinU: + case Opcode::I32X4Mul: + case Opcode::I32X4Ne: + case Opcode::I32X4Shl: + case Opcode::I32X4ShrS: + case Opcode::I32X4ShrU: + case Opcode::I32X4Sub: + case Opcode::I32Xor: + case Opcode::I64Add: + case Opcode::I64And: + case Opcode::I64DivS: + case Opcode::I64DivU: + case Opcode::I64Eq: + case Opcode::I64GeS: + case Opcode::I64GeU: + case Opcode::I64GtS: + case Opcode::I64GtU: + case Opcode::I64LeS: + case Opcode::I64LeU: + case Opcode::I64LtS: + case Opcode::I64LtU: + case Opcode::I64Mul: + case Opcode::I64Ne: + case Opcode::I64Or: + case Opcode::I64RemS: + case Opcode::I64RemU: + case Opcode::I64Rotl: + case Opcode::I64Rotr: + case Opcode::I64Shl: + case Opcode::I64ShrS: + case Opcode::I64ShrU: + case Opcode::I64Sub: + case Opcode::I64X2Add: + case Opcode::I64X2Shl: + case Opcode::I64X2ShrS: + case Opcode::I64X2ShrU: + case Opcode::I64X2Sub: + case Opcode::I64X2Mul: + case Opcode::I64Xor: + case Opcode::I8X16Add: + case Opcode::I8X16AddSaturateS: + case Opcode::I8X16AddSaturateU: + case Opcode::I8X16AvgrU: + case Opcode::I8X16Eq: + case Opcode::I8X16GeS: + case Opcode::I8X16GeU: + case Opcode::I8X16GtS: + case Opcode::I8X16GtU: + case Opcode::I8X16LeS: + case Opcode::I8X16LeU: + case Opcode::I8X16LtS: + case Opcode::I8X16LtU: + case Opcode::I8X16MaxS: + case Opcode::I8X16MaxU: + case Opcode::I8X16MinS: + case Opcode::I8X16MinU: + case Opcode::I8X16NarrowI16X8S: + case Opcode::I8X16NarrowI16X8U: + case Opcode::I8X16Ne: + case Opcode::I8X16Shl: + case Opcode::I8X16ShrS: + case Opcode::I8X16ShrU: + case Opcode::I8X16Sub: + case Opcode::I8X16SubSaturateS: + case Opcode::I8X16SubSaturateU: + case Opcode::V128And: + case Opcode::V128Andnot: + case Opcode::V128BitSelect: + case Opcode::V128Or: + case Opcode::V128Xor: + case Opcode::V8X16Swizzle: + // 0 immediates, 2 operands + instr.kind = InstrKind::Imm_0_Op_2; break; - case Opcode::I64Const: - stream->Writef("%s %" PRIu64 "\n", opcode.GetName(), ReadU64At(pc)); + case Opcode::Select: + case Opcode::SelectT: + // 0 immediates, 3 operands + instr.kind = InstrKind::Imm_0_Op_3; break; - case Opcode::F32Const: - stream->Writef("%s %g\n", opcode.GetName(), - Bitcast<float>(ReadU32At(pc))); + case Opcode::Br: + // Jump target immediate, 0 operands. + instr.kind = InstrKind::Imm_Jump_Op_0; + instr.imm_u32 = ReadAt<u32>(offset); break; - case Opcode::F64Const: - stream->Writef("%s %g\n", opcode.GetName(), - Bitcast<double>(ReadU64At(pc))); + case Opcode::BrIf: + case Opcode::BrTable: + case Opcode::InterpBrUnless: + // Jump target immediate, 1 operand. + instr.kind = InstrKind::Imm_Jump_Op_1; + instr.imm_u32 = ReadAt<u32>(offset); break; - case Opcode::LocalGet: case Opcode::GlobalGet: - stream->Writef("%s $%u\n", opcode.GetName(), ReadU32At(pc)); + case Opcode::LocalGet: + case Opcode::MemorySize: + case Opcode::TableSize: + case Opcode::DataDrop: + case Opcode::ElemDrop: + case Opcode::RefFunc: + // Index immediate, 0 operands. + instr.kind = InstrKind::Imm_Index_Op_0; + instr.imm_u32 = ReadAt<u32>(offset); break; - case Opcode::LocalSet: case Opcode::GlobalSet: + case Opcode::LocalSet: case Opcode::LocalTee: - stream->Writef("%s $%u, %u\n", opcode.GetName(), ReadU32At(pc), - Top().i32); + case Opcode::MemoryGrow: + case Opcode::TableGet: + // Index immediate, 1 operand. + instr.kind = InstrKind::Imm_Index_Op_1; + instr.imm_u32 = ReadAt<u32>(offset); + break; + + case Opcode::TableSet: + case Opcode::TableGrow: + // Index immediate, 2 operands. + instr.kind = InstrKind::Imm_Index_Op_2; + instr.imm_u32 = ReadAt<u32>(offset); + break; + + case Opcode::MemoryFill: + case Opcode::TableFill: + // Index immediate, 3 operands. + instr.kind = InstrKind::Imm_Index_Op_3; + instr.imm_u32 = ReadAt<u32>(offset); break; case Opcode::Call: - case Opcode::ReturnCall: - stream->Writef("%s @%u\n", opcode.GetName(), ReadU32At(pc)); + case Opcode::InterpCallHost: + instr.kind = InstrKind::Imm_Index_Op_N; + instr.imm_u32 = ReadAt<u32>(offset); break; case Opcode::CallIndirect: case Opcode::ReturnCallIndirect: - stream->Writef("%s $%u, %u\n", opcode.GetName(), ReadU32At(pc), - Top().i32); + // Index immediate, N operands. + instr.kind = InstrKind::Imm_Index_Index_Op_N; + instr.imm_u32x2.fst = ReadAt<u32>(offset); + instr.imm_u32x2.snd = ReadAt<u32>(offset); break; - case Opcode::InterpCallHost: - stream->Writef("%s $%u\n", opcode.GetName(), ReadU32At(pc)); + case Opcode::MemoryInit: + case Opcode::TableInit: + case Opcode::MemoryCopy: + case Opcode::TableCopy: + // Index + index immediates, 3 operands. + instr.kind = InstrKind::Imm_Index_Index_Op_3; + instr.imm_u32x2.fst = ReadAt<u32>(offset); + instr.imm_u32x2.snd = ReadAt<u32>(offset); break; - case Opcode::I32AtomicLoad8U: - case Opcode::I32AtomicLoad16U: + case Opcode::F32Load: + case Opcode::F64Load: + case Opcode::I16X8Load8X8S: + case Opcode::I16X8Load8X8U: + case Opcode::V16X8LoadSplat: case Opcode::I32AtomicLoad: - case Opcode::I64AtomicLoad8U: - case Opcode::I64AtomicLoad16U: - case Opcode::I64AtomicLoad32U: - case Opcode::I64AtomicLoad: - case Opcode::I32Load8S: - case Opcode::I32Load8U: + case Opcode::I32AtomicLoad16U: + case Opcode::I32AtomicLoad8U: + case Opcode::I32Load: case Opcode::I32Load16S: case Opcode::I32Load16U: - case Opcode::I64Load8S: - case Opcode::I64Load8U: + case Opcode::I32Load8S: + case Opcode::I32Load8U: + case Opcode::I32X4Load16X4S: + case Opcode::I32X4Load16X4U: + case Opcode::V32X4LoadSplat: + case Opcode::I64AtomicLoad: + case Opcode::I64AtomicLoad16U: + case Opcode::I64AtomicLoad32U: + case Opcode::I64AtomicLoad8U: + case Opcode::I64Load: case Opcode::I64Load16S: case Opcode::I64Load16U: case Opcode::I64Load32S: case Opcode::I64Load32U: - case Opcode::I32Load: - case Opcode::I64Load: - case Opcode::F32Load: - case Opcode::F64Load: - case Opcode::V128Load: - case Opcode::V8X16LoadSplat: - case Opcode::V16X8LoadSplat: - case Opcode::V32X4LoadSplat: - case Opcode::V64X2LoadSplat: - case Opcode::I16X8Load8X8S: - case Opcode::I16X8Load8X8U: - case Opcode::I32X4Load16X4S: - case Opcode::I32X4Load16X4U: + case Opcode::I64Load8S: + case Opcode::I64Load8U: case Opcode::I64X2Load32X2S: - case Opcode::I64X2Load32X2U: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%u+$%u\n", opcode.GetName(), - memory_index, Top().i32, ReadU32At(pc)); + case Opcode::I64X2Load32X2U: + case Opcode::V64X2LoadSplat: + case Opcode::V8X16LoadSplat: + case Opcode::V128Load: + // Index + memory offset immediates, 1 operand. + instr.kind = InstrKind::Imm_Index_Offset_Op_1; + instr.imm_u32x2.fst = ReadAt<u32>(offset); + instr.imm_u32x2.snd = ReadAt<u32>(offset); break; - } case Opcode::AtomicNotify: - case Opcode::I32AtomicStore: - case Opcode::I32AtomicStore8: - case Opcode::I32AtomicStore16: - case Opcode::I32AtomicRmw8AddU: + case Opcode::F32Store: + case Opcode::F64Store: case Opcode::I32AtomicRmw16AddU: - case Opcode::I32AtomicRmwAdd: - case Opcode::I32AtomicRmw8SubU: - case Opcode::I32AtomicRmw16SubU: - case Opcode::I32AtomicRmwSub: - case Opcode::I32AtomicRmw8AndU: case Opcode::I32AtomicRmw16AndU: - case Opcode::I32AtomicRmwAnd: - case Opcode::I32AtomicRmw8OrU: case Opcode::I32AtomicRmw16OrU: - case Opcode::I32AtomicRmwOr: - case Opcode::I32AtomicRmw8XorU: + case Opcode::I32AtomicRmw16SubU: + case Opcode::I32AtomicRmw16XchgU: case Opcode::I32AtomicRmw16XorU: - case Opcode::I32AtomicRmwXor: + case Opcode::I32AtomicRmw8AddU: + case Opcode::I32AtomicRmw8AndU: + case Opcode::I32AtomicRmw8OrU: + case Opcode::I32AtomicRmw8SubU: case Opcode::I32AtomicRmw8XchgU: - case Opcode::I32AtomicRmw16XchgU: + case Opcode::I32AtomicRmw8XorU: + case Opcode::I32AtomicRmwAdd: + case Opcode::I32AtomicRmwAnd: + case Opcode::I32AtomicRmwOr: + case Opcode::I32AtomicRmwSub: case Opcode::I32AtomicRmwXchg: - case Opcode::I32Store8: + case Opcode::I32AtomicRmwXor: + case Opcode::I32AtomicStore: + case Opcode::I32AtomicStore16: + case Opcode::I32AtomicStore8: + case Opcode::I32Store: case Opcode::I32Store16: - case Opcode::I32Store: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%u+$%u, %u\n", opcode.GetName(), - memory_index, Pick(2).i32, ReadU32At(pc), Pick(1).i32); - break; - } - - case Opcode::I32AtomicRmwCmpxchg: - case Opcode::I32AtomicRmw8CmpxchgU: - case Opcode::I32AtomicRmw16CmpxchgU: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%u+$%u, %u, %u\n", opcode.GetName(), - memory_index, Pick(3).i32, ReadU32At(pc), Pick(2).i32, - Pick(1).i32); - break; - } - - case Opcode::I64AtomicStore8: - case Opcode::I64AtomicStore16: - case Opcode::I64AtomicStore32: - case Opcode::I64AtomicStore: - case Opcode::I64AtomicRmw8AddU: + case Opcode::I32Store8: case Opcode::I64AtomicRmw16AddU: - case Opcode::I64AtomicRmw32AddU: - case Opcode::I64AtomicRmwAdd: - case Opcode::I64AtomicRmw8SubU: - case Opcode::I64AtomicRmw16SubU: - case Opcode::I64AtomicRmw32SubU: - case Opcode::I64AtomicRmwSub: - case Opcode::I64AtomicRmw8AndU: case Opcode::I64AtomicRmw16AndU: - case Opcode::I64AtomicRmw32AndU: - case Opcode::I64AtomicRmwAnd: - case Opcode::I64AtomicRmw8OrU: case Opcode::I64AtomicRmw16OrU: - case Opcode::I64AtomicRmw32OrU: - case Opcode::I64AtomicRmwOr: - case Opcode::I64AtomicRmw8XorU: + case Opcode::I64AtomicRmw16SubU: + case Opcode::I64AtomicRmw16XchgU: case Opcode::I64AtomicRmw16XorU: + case Opcode::I64AtomicRmw32AddU: + case Opcode::I64AtomicRmw32AndU: + case Opcode::I64AtomicRmw32OrU: + case Opcode::I64AtomicRmw32SubU: + case Opcode::I64AtomicRmw32XchgU: case Opcode::I64AtomicRmw32XorU: - case Opcode::I64AtomicRmwXor: + case Opcode::I64AtomicRmw8AddU: + case Opcode::I64AtomicRmw8AndU: + case Opcode::I64AtomicRmw8OrU: + case Opcode::I64AtomicRmw8SubU: case Opcode::I64AtomicRmw8XchgU: - case Opcode::I64AtomicRmw16XchgU: - case Opcode::I64AtomicRmw32XchgU: + case Opcode::I64AtomicRmw8XorU: + case Opcode::I64AtomicRmwAdd: + case Opcode::I64AtomicRmwAnd: + case Opcode::I64AtomicRmwOr: + case Opcode::I64AtomicRmwSub: case Opcode::I64AtomicRmwXchg: - case Opcode::I64Store8: + case Opcode::I64AtomicRmwXor: + case Opcode::I64AtomicStore: + case Opcode::I64AtomicStore16: + case Opcode::I64AtomicStore32: + case Opcode::I64AtomicStore8: + case Opcode::I64Store: case Opcode::I64Store16: case Opcode::I64Store32: - case Opcode::I64Store: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%u+$%u, %" PRIu64 "\n", - opcode.GetName(), memory_index, Pick(2).i32, ReadU32At(pc), - Pick(1).i64); - break; - } - - case Opcode::I32AtomicWait: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%u+$%u, %u, %" PRIu64 "\n", - opcode.GetName(), memory_index, Pick(3).i32, ReadU32At(pc), - Pick(2).i32, Pick(1).i64); + case Opcode::I64Store8: + case Opcode::V128Store: + // Index and memory offset immediates, 2 operands. + instr.kind = InstrKind::Imm_Index_Offset_Op_2; + instr.imm_u32x2.fst = ReadAt<u32>(offset); + instr.imm_u32x2.snd = ReadAt<u32>(offset); break; - } - case Opcode::I64AtomicWait: - case Opcode::I64AtomicRmwCmpxchg: - case Opcode::I64AtomicRmw8CmpxchgU: + case Opcode::I32AtomicRmw16CmpxchgU: + case Opcode::I32AtomicRmw8CmpxchgU: + case Opcode::I32AtomicRmwCmpxchg: + case Opcode::I32AtomicWait: case Opcode::I64AtomicRmw16CmpxchgU: - case Opcode::I64AtomicRmw32CmpxchgU: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%u+$%u, %" PRIu64 ", %" PRIu64 "\n", - opcode.GetName(), memory_index, Pick(3).i32, ReadU32At(pc), - Pick(2).i64, Pick(1).i64); + case Opcode::I64AtomicRmw32CmpxchgU: + case Opcode::I64AtomicRmw8CmpxchgU: + case Opcode::I64AtomicRmwCmpxchg: + case Opcode::I64AtomicWait: + // Index and memory offset immediates, 3 operands. + instr.kind = InstrKind::Imm_Index_Offset_Op_3; + instr.imm_u32x2.fst = ReadAt<u32>(offset); + instr.imm_u32x2.snd = ReadAt<u32>(offset); break; - } - case Opcode::F32Store: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%u+$%u, %g\n", opcode.GetName(), - memory_index, Pick(2).i32, ReadU32At(pc), - Bitcast<float>(Pick(1).f32_bits)); + case Opcode::I32Const: + case Opcode::InterpAlloca: + // i32/f32 immediate, 0 operands. + instr.kind = InstrKind::Imm_I32_Op_0; + instr.imm_u32 = ReadAt<u32>(offset); break; - } - case Opcode::F64Store: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%u+$%u, %g\n", opcode.GetName(), - memory_index, Pick(2).i32, ReadU32At(pc), - Bitcast<double>(Pick(1).f64_bits)); + case Opcode::I64Const: + // i64 immediate, 0 operands. + instr.kind = InstrKind::Imm_I64_Op_0; + instr.imm_u64 = ReadAt<u64>(offset); break; - } - case Opcode::V128Store: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%u+$%u, $0x%08x 0x%08x 0x%08x 0x%08x\n", - opcode.GetName(), memory_index, Pick(2).i32, ReadU32At(pc), - Pick(1).vec128.v[0], Pick(1).vec128.v[1], - Pick(1).vec128.v[2], Pick(1).vec128.v[3]); + case Opcode::F32Const: + // f32 immediate, 0 operands. + instr.kind = InstrKind::Imm_F32_Op_0; + instr.imm_f32 = ReadAt<f32>(offset); break; - } - case Opcode::MemoryGrow: { - const Index memory_index = ReadU32(&pc); - stream->Writef("%s $%" PRIindex ":%u\n", opcode.GetName(), memory_index, - Top().i32); + case Opcode::F64Const: + // f64 immediate, 0 operands. + instr.kind = InstrKind::Imm_F64_Op_0; + instr.imm_f64 = ReadAt<f64>(offset); break; - } - case Opcode::I32Add: - case Opcode::I32Sub: - case Opcode::I32Mul: - case Opcode::I32DivS: - case Opcode::I32DivU: - case Opcode::I32RemS: - case Opcode::I32RemU: - case Opcode::I32And: - case Opcode::I32Or: - case Opcode::I32Xor: - case Opcode::I32Shl: - case Opcode::I32ShrU: - case Opcode::I32ShrS: - case Opcode::I32Eq: - case Opcode::I32Ne: - case Opcode::I32LtS: - case Opcode::I32LeS: - case Opcode::I32LtU: - case Opcode::I32LeU: - case Opcode::I32GtS: - case Opcode::I32GeS: - case Opcode::I32GtU: - case Opcode::I32GeU: - case Opcode::I32Rotr: - case Opcode::I32Rotl: - stream->Writef("%s %u, %u\n", opcode.GetName(), Pick(2).i32, Pick(1).i32); + case Opcode::InterpDropKeep: + // i32 and i32 immediates, 0 operands. + instr.kind = InstrKind::Imm_I32_I32_Op_0; + instr.imm_u32x2.fst = ReadAt<u32>(offset); + instr.imm_u32x2.snd = ReadAt<u32>(offset); break; - case Opcode::I32Clz: - case Opcode::I32Ctz: - case Opcode::I32Popcnt: - case Opcode::I32Eqz: - case Opcode::I32Extend16S: - case Opcode::I32Extend8S: - case Opcode::I8X16Splat: - case Opcode::I16X8Splat: - case Opcode::I32X4Splat: - stream->Writef("%s %u\n", opcode.GetName(), Top().i32); + case Opcode::I8X16ExtractLaneS: + case Opcode::I8X16ExtractLaneU: + case Opcode::I16X8ExtractLaneS: + case Opcode::I16X8ExtractLaneU: + case Opcode::I32X4ExtractLane: + case Opcode::I64X2ExtractLane: + case Opcode::F32X4ExtractLane: + case Opcode::F64X2ExtractLane: + // u8 immediate, 1 operand. + instr.kind = InstrKind::Imm_I8_Op_1; + instr.imm_u8 = ReadAt<u8>(offset); break; - case Opcode::I64Add: - case Opcode::I64Sub: - case Opcode::I64Mul: - case Opcode::I64DivS: - case Opcode::I64DivU: - case Opcode::I64RemS: - case Opcode::I64RemU: - case Opcode::I64And: - case Opcode::I64Or: - case Opcode::I64Xor: - case Opcode::I64Shl: - case Opcode::I64ShrU: - case Opcode::I64ShrS: - case Opcode::I64Eq: - case Opcode::I64Ne: - case Opcode::I64LtS: - case Opcode::I64LeS: - case Opcode::I64LtU: - case Opcode::I64LeU: - case Opcode::I64GtS: - case Opcode::I64GeS: - case Opcode::I64GtU: - case Opcode::I64GeU: - case Opcode::I64Rotr: - case Opcode::I64Rotl: - stream->Writef("%s %" PRIu64 ", %" PRIu64 "\n", opcode.GetName(), - Pick(2).i64, Pick(1).i64); + case Opcode::I8X16ReplaceLane: + case Opcode::I16X8ReplaceLane: + case Opcode::I32X4ReplaceLane: + case Opcode::I64X2ReplaceLane: + case Opcode::F32X4ReplaceLane: + case Opcode::F64X2ReplaceLane: + // u8 immediate, 2 operands. + instr.kind = InstrKind::Imm_I8_Op_2; + instr.imm_u8 = ReadAt<u8>(offset); break; - case Opcode::I64Clz: - case Opcode::I64Ctz: - case Opcode::I64Popcnt: - case Opcode::I64Eqz: - case Opcode::I64Extend16S: - case Opcode::I64Extend32S: - case Opcode::I64Extend8S: - case Opcode::I64X2Splat: - stream->Writef("%s %" PRIu64 "\n", opcode.GetName(), Top().i64); + case Opcode::V128Const: + // v128 immediate, 0 operands. + instr.kind = InstrKind::Imm_V128_Op_0; + instr.imm_v128 = ReadAt<v128>(offset); break; - case Opcode::F32Add: - case Opcode::F32Sub: - case Opcode::F32Mul: - case Opcode::F32Div: - case Opcode::F32Min: - case Opcode::F32Max: - case Opcode::F32Copysign: - case Opcode::F32Eq: - case Opcode::F32Ne: - case Opcode::F32Lt: - case Opcode::F32Le: - case Opcode::F32Gt: - case Opcode::F32Ge: - stream->Writef("%s %g, %g\n", opcode.GetName(), - Bitcast<float>(Pick(2).i32), Bitcast<float>(Pick(1).i32)); + case Opcode::V8X16Shuffle: + // v128 immediate, 2 operands. + instr.kind = InstrKind::Imm_V128_Op_2; + instr.imm_v128 = ReadAt<v128>(offset); break; - case Opcode::F32Abs: - case Opcode::F32Neg: - case Opcode::F32Ceil: - case Opcode::F32Floor: - case Opcode::F32Trunc: - case Opcode::F32Nearest: - case Opcode::F32Sqrt: - case Opcode::F32X4Splat: - stream->Writef("%s %g\n", opcode.GetName(), Bitcast<float>(Top().i32)); + case Opcode::Block: + case Opcode::BrOnExn: + case Opcode::Catch: + case Opcode::Else: + case Opcode::End: + case Opcode::If: + case Opcode::InterpData: + case Opcode::Invalid: + case Opcode::Loop: + case Opcode::Rethrow: + case Opcode::Throw: + case Opcode::Try: + case Opcode::ReturnCall: + // Not used. break; + } + return instr; +} - case Opcode::F64Add: - case Opcode::F64Sub: - case Opcode::F64Mul: - case Opcode::F64Div: - case Opcode::F64Min: - case Opcode::F64Max: - case Opcode::F64Copysign: - case Opcode::F64Eq: - case Opcode::F64Ne: - case Opcode::F64Lt: - case Opcode::F64Le: - case Opcode::F64Gt: - case Opcode::F64Ge: - stream->Writef("%s %g, %g\n", opcode.GetName(), - Bitcast<double>(Pick(2).i64), - Bitcast<double>(Pick(1).i64)); - break; +void Istream::Disassemble(Stream* stream) const { + Disassemble(stream, 0, data_.size()); +} - case Opcode::F64Abs: - case Opcode::F64Neg: - case Opcode::F64Ceil: - case Opcode::F64Floor: - case Opcode::F64Trunc: - case Opcode::F64Nearest: - case Opcode::F64Sqrt: - case Opcode::F64X2Splat: - stream->Writef("%s %g\n", opcode.GetName(), Bitcast<double>(Top().i64)); - break; +std::string Istream::DisassemblySource::Header(Offset offset) { + return StringPrintf("%4u", offset); +} - case Opcode::I32TruncF32S: - case Opcode::I32TruncF32U: - case Opcode::I64TruncF32S: - case Opcode::I64TruncF32U: - case Opcode::F64PromoteF32: - case Opcode::I32ReinterpretF32: - case Opcode::I32TruncSatF32S: - case Opcode::I32TruncSatF32U: - case Opcode::I64TruncSatF32S: - case Opcode::I64TruncSatF32U: - stream->Writef("%s %g\n", opcode.GetName(), Bitcast<float>(Top().i32)); +std::string Istream::DisassemblySource::Pick(Index index, Instr instr) { + return StringPrintf("%%[-%d]", index); +} + +Istream::Offset Istream::Disassemble(Stream* stream, Offset offset) const { + DisassemblySource source; + return Trace(stream, offset, &source); +} + +void Istream::Disassemble(Stream* stream, Offset from, Offset to) const { + DisassemblySource source; + assert(from <= data_.size() && to <= data_.size() && from <= to); + + Offset pc = from; + while (pc < to) { + pc = Trace(stream, pc, &source); + } +} + +Istream::Offset Istream::Trace(Stream* stream, + Offset offset, + TraceSource* source) const { + Offset start = offset; + Instr instr = Read(&offset); + stream->Writef("%s| %s", source->Header(start).c_str(), instr.op.GetName()); + + switch (instr.kind) { + case InstrKind::Imm_0_Op_0: + stream->Writef("\n"); break; - case Opcode::I32TruncF64S: - case Opcode::I32TruncF64U: - case Opcode::I64TruncF64S: - case Opcode::I64TruncF64U: - case Opcode::F32DemoteF64: - case Opcode::I64ReinterpretF64: - case Opcode::I32TruncSatF64S: - case Opcode::I32TruncSatF64U: - case Opcode::I64TruncSatF64S: - case Opcode::I64TruncSatF64U: - stream->Writef("%s %g\n", opcode.GetName(), Bitcast<double>(Top().i64)); + case InstrKind::Imm_0_Op_1: + stream->Writef(" %s\n", source->Pick(1, instr).c_str()); break; - case Opcode::I32WrapI64: - case Opcode::F32ConvertI64S: - case Opcode::F32ConvertI64U: - case Opcode::F64ConvertI64S: - case Opcode::F64ConvertI64U: - case Opcode::F64ReinterpretI64: - stream->Writef("%s %" PRIu64 "\n", opcode.GetName(), Top().i64); + case InstrKind::Imm_0_Op_2: + stream->Writef(" %s, %s\n", source->Pick(2, instr).c_str(), + source->Pick(1, instr).c_str()); break; - case Opcode::I64ExtendI32S: - case Opcode::I64ExtendI32U: - case Opcode::F32ConvertI32S: - case Opcode::F32ConvertI32U: - case Opcode::F32ReinterpretI32: - case Opcode::F64ConvertI32S: - case Opcode::F64ConvertI32U: - stream->Writef("%s %u\n", opcode.GetName(), Top().i32); + case InstrKind::Imm_0_Op_3: + stream->Writef(" %s, %s, %s\n", source->Pick(3, instr).c_str(), + source->Pick(2, instr).c_str(), + source->Pick(1, instr).c_str()); break; - case Opcode::InterpAlloca: - stream->Writef("%s $%u\n", opcode.GetName(), ReadU32At(pc)); + case InstrKind::Imm_Jump_Op_0: + stream->Writef(" @%u\n", instr.imm_u32); break; - case Opcode::InterpBrUnless: - stream->Writef("%s @%u, %u\n", opcode.GetName(), ReadU32At(pc), - Top().i32); + case InstrKind::Imm_Jump_Op_1: + stream->Writef(" @%u, %s\n", instr.imm_u32, + source->Pick(1, instr).c_str()); break; - case Opcode::InterpDropKeep: - stream->Writef("%s $%u $%u\n", opcode.GetName(), ReadU32At(pc), - ReadU32At(pc + 4)); + case InstrKind::Imm_Index_Op_0: + stream->Writef(" $%u\n", instr.imm_u32); break; - case Opcode::V128Const: { - stream->Writef("%s i32x4 0x%08x 0x%08x 0x%08x 0x%08x\n", opcode.GetName(), - ReadU32At(pc), ReadU32At(pc + 4), ReadU32At(pc + 8), - ReadU32At(pc + 12)); + case InstrKind::Imm_Index_Op_1: + stream->Writef(" $%u, %s\n", instr.imm_u32, + source->Pick(1, instr).c_str()); break; - } - case Opcode::I8X16Neg: - case Opcode::I16X8Neg: - case Opcode::I32X4Neg: - case Opcode::I64X2Neg: - case Opcode::V128Not: - case Opcode::I8X16AnyTrue: - case Opcode::I16X8AnyTrue: - case Opcode::I32X4AnyTrue: - case Opcode::I8X16AllTrue: - case Opcode::I16X8AllTrue: - case Opcode::I32X4AllTrue: - case Opcode::F32X4Neg: - case Opcode::F64X2Neg: - case Opcode::F32X4Abs: - case Opcode::F64X2Abs: - case Opcode::F32X4Sqrt: - case Opcode::F64X2Sqrt: - case Opcode::F32X4ConvertI32X4S: - case Opcode::F32X4ConvertI32X4U: - case Opcode::I32X4TruncSatF32X4S: - case Opcode::I32X4TruncSatF32X4U: - case Opcode::I16X8WidenLowI8X16S: - case Opcode::I16X8WidenHighI8X16S: - case Opcode::I16X8WidenLowI8X16U: - case Opcode::I16X8WidenHighI8X16U: - case Opcode::I32X4WidenLowI16X8S: - case Opcode::I32X4WidenHighI16X8S: - case Opcode::I32X4WidenLowI16X8U: - case Opcode::I32X4WidenHighI16X8U: { - stream->Writef("%s $0x%08x 0x%08x 0x%08x 0x%08x\n", opcode.GetName(), - Top().vec128.v[0], Top().vec128.v[1], - Top().vec128.v[2], Top().vec128.v[3]); + case InstrKind::Imm_Index_Op_2: + stream->Writef(" $%u, %s, %s\n", instr.imm_u32, + source->Pick(2, instr).c_str(), + source->Pick(1, instr).c_str()); break; - } - case Opcode::V128BitSelect: + case InstrKind::Imm_Index_Op_3: stream->Writef( - "%s $0x%08x %08x %08x %08x $0x%08x %08x %08x %08x $0x%08x %08x %08x " - "%08x\n", - opcode.GetName(), Pick(3).vec128.v[0], Pick(3).vec128.v[1], - Pick(3).vec128.v[2], Pick(3).vec128.v[3], - Pick(2).vec128.v[0], Pick(2).vec128.v[1], - Pick(2).vec128.v[2], Pick(2).vec128.v[3], - Pick(1).vec128.v[0], Pick(1).vec128.v[1], - Pick(1).vec128.v[2], Pick(1).vec128.v[3]); + " $%u, %s, %s, %s\n", instr.imm_u32, source->Pick(3, instr).c_str(), + source->Pick(2, instr).c_str(), source->Pick(1, instr).c_str()); break; - case Opcode::I8X16ExtractLaneS: - case Opcode::I8X16ExtractLaneU: - case Opcode::I16X8ExtractLaneS: - case Opcode::I16X8ExtractLaneU: - case Opcode::I32X4ExtractLane: - case Opcode::I64X2ExtractLane: - case Opcode::F32X4ExtractLane: - case Opcode::F64X2ExtractLane: { - stream->Writef("%s : LaneIdx %d From $0x%08x 0x%08x 0x%08x 0x%08x\n", - opcode.GetName(), ReadU8At(pc), Top().vec128.v[0], - Top().vec128.v[1], Top().vec128.v[2], - Top().vec128.v[3]); + case InstrKind::Imm_Index_Op_N: + stream->Writef(" $%u\n", instr.imm_u32); // TODO param/result count? break; - } - case Opcode::I8X16ReplaceLane: - case Opcode::I16X8ReplaceLane: - case Opcode::I32X4ReplaceLane: { - stream->Writef( - "%s : Set %u to LaneIdx %d In $0x%08x 0x%08x 0x%08x 0x%08x\n", - opcode.GetName(), Pick(1).i32, ReadU8At(pc), Pick(2).vec128.v[0], - Pick(2).vec128.v[1], Pick(2).vec128.v[2], - Pick(2).vec128.v[3]); + case InstrKind::Imm_Index_Index_Op_3: + stream->Writef(" $%u, $%u, %s, %s, %s\n", instr.imm_u32x2.fst, + instr.imm_u32x2.snd, source->Pick(3, instr).c_str(), + source->Pick(2, instr).c_str(), + source->Pick(1, instr).c_str()); break; - } - case Opcode::I64X2ReplaceLane: { - stream->Writef("%s : Set %" PRIu64 - " to LaneIdx %d In $0x%08x 0x%08x 0x%08x 0x%08x\n", - opcode.GetName(), Pick(1).i64, ReadU8At(pc), - Pick(2).vec128.v[0], Pick(2).vec128.v[1], - Pick(2).vec128.v[2], Pick(2).vec128.v[3]); + + case InstrKind::Imm_Index_Index_Op_N: + stream->Writef(" $%u, $%u\n", instr.imm_u32x2.fst, + instr.imm_u32x2.snd); // TODO param/result count? break; - } - case Opcode::F32X4ReplaceLane: { - stream->Writef( - "%s : Set %g to LaneIdx %d In $0x%08x 0x%08x 0x%08x 0x%08x\n", - opcode.GetName(), Bitcast<float>(Pick(1).f32_bits), ReadU8At(pc), - Pick(2).vec128.v[0], Pick(2).vec128.v[1], - Pick(2).vec128.v[2], Pick(2).vec128.v[3]); + case InstrKind::Imm_Index_Offset_Op_1: + stream->Writef(" $%u:%s+$%u\n", instr.imm_u32x2.fst, + source->Pick(1, instr).c_str(), instr.imm_u32x2.snd); break; - } - case Opcode::F64X2ReplaceLane: { - stream->Writef( - "%s : Set %g to LaneIdx %d In $0x%08x 0x%08x 0x%08x 0x%08x\n", - opcode.GetName(), Bitcast<double>(Pick(1).f64_bits), ReadU8At(pc), - Pick(2).vec128.v[0], Pick(2).vec128.v[1], - Pick(2).vec128.v[2], Pick(2).vec128.v[3]); + + case InstrKind::Imm_Index_Offset_Op_2: + stream->Writef(" $%u:%s+$%u, %s\n", instr.imm_u32x2.fst, + source->Pick(2, instr).c_str(), instr.imm_u32x2.snd, + source->Pick(1, instr).c_str()); break; - } - case Opcode::V8X16Shuffle: - stream->Writef( - "%s $0x%08x %08x %08x %08x $0x%08x %08x %08x %08x : with lane imm: " - "$0x%08x %08x %08x %08x\n", - opcode.GetName(), Pick(2).vec128.v[0], Pick(2).vec128.v[1], - Pick(2).vec128.v[2], Pick(2).vec128.v[3], - Pick(1).vec128.v[0], Pick(1).vec128.v[1], - Pick(1).vec128.v[2], Pick(1).vec128.v[3], ReadU32At(pc), - ReadU32At(pc + 4), ReadU32At(pc + 8), ReadU32At(pc + 12)); + case InstrKind::Imm_Index_Offset_Op_3: + stream->Writef(" $%u:%s+$%u, %s, %s\n", instr.imm_u32x2.fst, + source->Pick(3, instr).c_str(), instr.imm_u32x2.snd, + source->Pick(2, instr).c_str(), + source->Pick(1, instr).c_str()); break; - case Opcode::I8X16Add: - case Opcode::I16X8Add: - case Opcode::I32X4Add: - case Opcode::I64X2Add: - case Opcode::I8X16Sub: - case Opcode::I16X8Sub: - case Opcode::I32X4Sub: - case Opcode::I64X2Sub: - case Opcode::I16X8Mul: - case Opcode::I32X4Mul: - case Opcode::I64X2Mul: - case Opcode::I8X16AddSaturateS: - case Opcode::I8X16AddSaturateU: - case Opcode::I16X8AddSaturateS: - case Opcode::I16X8AddSaturateU: - case Opcode::I8X16SubSaturateS: - case Opcode::I8X16SubSaturateU: - case Opcode::I16X8SubSaturateS: - case Opcode::I16X8SubSaturateU: - case Opcode::I8X16MinS: - case Opcode::I16X8MinS: - case Opcode::I32X4MinS: - case Opcode::I8X16MinU: - case Opcode::I16X8MinU: - case Opcode::I32X4MinU: - case Opcode::I8X16MaxS: - case Opcode::I16X8MaxS: - case Opcode::I32X4MaxS: - case Opcode::I8X16MaxU: - case Opcode::I16X8MaxU: - case Opcode::I32X4MaxU: - case Opcode::V128And: - case Opcode::V128Or: - case Opcode::V128Xor: - case Opcode::I8X16Eq: - case Opcode::I16X8Eq: - case Opcode::I32X4Eq: - case Opcode::F32X4Eq: - case Opcode::F64X2Eq: - case Opcode::I8X16Ne: - case Opcode::I16X8Ne: - case Opcode::I32X4Ne: - case Opcode::F32X4Ne: - case Opcode::F64X2Ne: - case Opcode::I8X16LtS: - case Opcode::I8X16LtU: - case Opcode::I16X8LtS: - case Opcode::I16X8LtU: - case Opcode::I32X4LtS: - case Opcode::I32X4LtU: - case Opcode::F32X4Lt: - case Opcode::F64X2Lt: - case Opcode::I8X16LeS: - case Opcode::I8X16LeU: - case Opcode::I16X8LeS: - case Opcode::I16X8LeU: - case Opcode::I32X4LeS: - case Opcode::I32X4LeU: - case Opcode::F32X4Le: - case Opcode::F64X2Le: - case Opcode::I8X16GtS: - case Opcode::I8X16GtU: - case Opcode::I16X8GtS: - case Opcode::I16X8GtU: - case Opcode::I32X4GtS: - case Opcode::I32X4GtU: - case Opcode::F32X4Gt: - case Opcode::F64X2Gt: - case Opcode::I8X16GeS: - case Opcode::I8X16GeU: - case Opcode::I16X8GeS: - case Opcode::I16X8GeU: - case Opcode::I32X4GeS: - case Opcode::I32X4GeU: - case Opcode::F32X4Ge: - case Opcode::F64X2Ge: - case Opcode::F32X4Min: - case Opcode::F64X2Min: - case Opcode::F32X4Max: - case Opcode::F64X2Max: - case Opcode::F32X4Add: - case Opcode::F64X2Add: - case Opcode::F32X4Sub: - case Opcode::F64X2Sub: - case Opcode::F32X4Div: - case Opcode::F64X2Div: - case Opcode::F32X4Mul: - case Opcode::F64X2Mul: - case Opcode::V8X16Swizzle: - case Opcode::I8X16NarrowI16X8S: - case Opcode::I8X16NarrowI16X8U: - case Opcode::I16X8NarrowI32X4S: - case Opcode::I16X8NarrowI32X4U: - case Opcode::V128Andnot: - case Opcode::I8X16AvgrU: - case Opcode::I16X8AvgrU: { - stream->Writef("%s $0x%08x %08x %08x %08x $0x%08x %08x %08x %08x\n", - opcode.GetName(), Pick(2).vec128.v[0], - Pick(2).vec128.v[1], Pick(2).vec128.v[2], - Pick(2).vec128.v[3], Pick(1).vec128.v[0], - Pick(1).vec128.v[1], Pick(1).vec128.v[2], - Pick(1).vec128.v[3]); + case InstrKind::Imm_I32_Op_0: + stream->Writef(" %u\n", instr.imm_u32); break; - } - case Opcode::I8X16Shl: - case Opcode::I16X8Shl: - case Opcode::I32X4Shl: - case Opcode::I64X2Shl: - case Opcode::I8X16ShrS: - case Opcode::I8X16ShrU: - case Opcode::I16X8ShrS: - case Opcode::I16X8ShrU: - case Opcode::I32X4ShrS: - case Opcode::I32X4ShrU: - case Opcode::I64X2ShrS: - case Opcode::I64X2ShrU: { - stream->Writef("%s $0x%08x %08x %08x %08x $0x%08x\n", opcode.GetName(), - Pick(2).vec128.v[0], Pick(2).vec128.v[1], - Pick(2).vec128.v[2], Pick(2).vec128.v[3], - Pick(1).i32); + case InstrKind::Imm_I64_Op_0: + stream->Writef(" %" PRIu64 "\n", instr.imm_u64); break; - } - case Opcode::RefNull: - stream->Writef("%s\n", opcode.GetName()); + case InstrKind::Imm_F32_Op_0: + stream->Writef(" %g\n", instr.imm_f32); break; - case Opcode::RefIsNull: - stream->Writef("%s %s:%08x\n", opcode.GetName(), - RefTypeToString(Pick(1).ref.kind).c_str(), - Pick(1).ref.index); + case InstrKind::Imm_F64_Op_0: + stream->Writef(" %g\n", instr.imm_f64); break; - case Opcode::RefFunc: - stream->Writef("%s $%u\n", opcode.GetName(), ReadU32At(pc)); + case InstrKind::Imm_I32_I32_Op_0: + stream->Writef(" $%u $%u\n", instr.imm_u32x2.fst, instr.imm_u32x2.snd); break; - case Opcode::MemoryInit: - case Opcode::TableInit: { - Index index = ReadU32At(pc); - Index segment_index = ReadU32At(pc + 4); - stream->Writef("%s $%" PRIindex ", $%" PRIindex "\n", opcode.GetName(), - index, segment_index); + case InstrKind::Imm_I8_Op_1: + // TODO: cleanup + stream->Writef(" %s : (Lane imm: %u)\n", source->Pick(1, instr).c_str(), + instr.imm_u8); break; - } - case Opcode::TableGet: - case Opcode::TableSet: - case Opcode::TableSize: - case Opcode::TableFill: - case Opcode::TableGrow: - case Opcode::DataDrop: - case Opcode::ElemDrop: - case Opcode::MemoryCopy: - case Opcode::TableCopy: - case Opcode::MemoryFill: - stream->Writef("%s $%u\n", opcode.GetName(), ReadU32At(pc)); + case InstrKind::Imm_I8_Op_2: + // TODO: cleanup + stream->Writef(" %s, %s : (Lane imm: $%u)\n", + source->Pick(2, instr).c_str(), + source->Pick(1, instr).c_str(), instr.imm_u8); break; - // The following opcodes are either never generated or should never be - // executed. - case Opcode::Block: - case Opcode::BrOnExn: - case Opcode::Catch: - case Opcode::Else: - case Opcode::End: - case Opcode::If: - case Opcode::InterpData: - case Opcode::Invalid: - case Opcode::Loop: - case Opcode::Rethrow: - case Opcode::Throw: - case Opcode::Try: - WABT_UNREACHABLE; + case InstrKind::Imm_V128_Op_0: + stream->Writef(" i32x4 0x%08x 0x%08x 0x%08x 0x%08x\n", + instr.imm_v128.v[0], instr.imm_v128.v[1], + instr.imm_v128.v[2], instr.imm_v128.v[3]); + break; + + case InstrKind::Imm_V128_Op_2: + // TODO: cleanup + stream->Writef( + " %s, %s : (Lane imm: i32x4 0x%08x 0x%08x 0x%08x 0x%08x )\n", + source->Pick(2, instr).c_str(), source->Pick(1, instr).c_str(), + instr.imm_v128.v[0], instr.imm_v128.v[1], instr.imm_v128.v[2], + instr.imm_v128.v[3]); break; } + return offset; } } // namespace interp diff --git a/src/interp/istream.h b/src/interp/istream.h new file mode 100644 index 00000000..927b6490 --- /dev/null +++ b/src/interp/istream.h @@ -0,0 +1,155 @@ +/* + * Copyright 2020 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WABT_INTERP_ISTREAM_H_ +#define WABT_INTERP_ISTREAM_H_ + +#include <cstdint> +#include <vector> +#include <string> + +#include "src/common.h" +#include "src/opcode.h" +#include "src/stream.h" + +namespace wabt { +namespace interp { + +using u8 = uint8_t; +using u16 = uint16_t; +using u32 = uint32_t; +using u64 = uint64_t; +using f32 = float; +using f64 = double; + +using Buffer = std::vector<u8>; + +using ValueType = wabt::Type; + +// Group instructions based on their immediates their operands. This way we can +// simplify instruction decoding, disassembling, and tracing. There is an +// example of an instruction that uses this encoding on the right. +enum class InstrKind { + Imm_0_Op_0, // Nop + Imm_0_Op_1, // i32.eqz + Imm_0_Op_2, // i32.add + Imm_0_Op_3, // select + Imm_Jump_Op_0, // br + Imm_Jump_Op_1, // br_if + Imm_Index_Op_0, // global.get + Imm_Index_Op_1, // global.set + Imm_Index_Op_2, // table.set + Imm_Index_Op_3, // memory.fill + Imm_Index_Op_N, // call + Imm_Index_Index_Op_3, // memory.init + Imm_Index_Index_Op_N, // call_indirect + Imm_Index_Offset_Op_1, // i32.load + Imm_Index_Offset_Op_2, // i32.store + Imm_Index_Offset_Op_3, // i32.atomic.rmw.cmpxchg + Imm_I32_Op_0, // i32.const + Imm_I64_Op_0, // i64.const + Imm_F32_Op_0, // f32.const + Imm_F64_Op_0, // f64.const + Imm_I32_I32_Op_0, // drop_keep + Imm_I8_Op_1, // i32x4.extract_lane + Imm_I8_Op_2, // i32x4.replace_lane + Imm_V128_Op_0, // v128.const + Imm_V128_Op_2, // v8x16.shuffle +}; + +struct Instr { + Opcode op; + InstrKind kind; + union { + u8 imm_u8; + u32 imm_u32; + f32 imm_f32; + u64 imm_u64; + f64 imm_f64; + v128 imm_v128; + struct { u32 fst, snd; } imm_u32x2; + }; +}; + +class Istream { + public: + using SerializedOpcode = u32; // TODO: change to u16 + using Offset = u32; + static const Offset kInvalidOffset = ~0; + // Each br_table entry is made up of two instructions: + // + // interp_drop_keep $drop $keep + // br $label + // + // Each opcode is a SerializedOpcode, and each immediate is a u32. + static const Offset kBrTableEntrySize = + sizeof(SerializedOpcode) * 2 + 3 * sizeof(u32); + + // Emit API. + void Emit(u32); + void Emit(Opcode::Enum); + void Emit(Opcode::Enum, u8); + void Emit(Opcode::Enum, u32); + void Emit(Opcode::Enum, u64); + void Emit(Opcode::Enum, v128); + void Emit(Opcode::Enum, u32, u32); + void EmitDropKeep(u32 drop, u32 keep); + + Offset EmitFixupU32(); + void ResolveFixupU32(Offset); + + Offset end() const; + + // Read API. + Instr Read(Offset*) const; + + // Disassemble/Trace API. + // TODO separate out disassembly/tracing? + struct TraceSource { + virtual ~TraceSource() {} + // Whatever content should go before the instruction on each line, e.g. the + // call stack size, value stack size, and istream offset. + virtual std::string Header(Offset) = 0; + virtual std::string Pick(Index, Instr) = 0; + }; + + struct DisassemblySource : TraceSource { + std::string Header(Offset) override; + std::string Pick(Index, Instr) override; + }; + + void Disassemble(Stream*) const; + Offset Disassemble(Stream*, Offset) const; + void Disassemble(Stream*, Offset from, Offset to) const; + + Offset Trace(Stream*, Offset, TraceSource*) const; + +private: + template <typename T> + void WABT_VECTORCALL EmitAt(Offset, T val); + template <typename T> + void WABT_VECTORCALL EmitInternal(T val); + + template <typename T> + T WABT_VECTORCALL ReadAt(Offset*) const; + + Buffer data_; +}; + +} // namespace interp +} // namespace wabt + +#endif // WABT_INTERP_ISTREAM_H_ diff --git a/src/opcode.cc b/src/opcode.cc index 5327f4b4..ff164752 100644 --- a/src/opcode.cc +++ b/src/opcode.cc @@ -24,13 +24,12 @@ namespace wabt { Opcode::Info Opcode::infos_[] = { #define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \ text, decomp) \ - {text, decomp, Type::rtype, Type::type1, \ - Type::type2, Type::type3, mem_size, \ - prefix, code, PrefixCode(prefix, code)}, + {text, decomp, Type::rtype, {Type::type1, Type::type2, Type::type3}, \ + mem_size, prefix, code, PrefixCode(prefix, code)}, #include "src/opcode.def" #undef WABT_OPCODE - {"<invalid>", "", Type::Void, Type::Void, Type::Void, Type::Void, 0, 0, 0, 0}, + {"<invalid>", "", Type::Void, {Type::Void, Type::Void, Type::Void}, 0, 0, 0, 0}, }; #define WABT_OPCODE(rtype, type1, type2, type3, mem_size, prefix, code, Name, \ diff --git a/src/opcode.def b/src/opcode.def index bb17dfee..408e4d04 100644 --- a/src/opcode.def +++ b/src/opcode.def @@ -48,16 +48,16 @@ WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x09, Rethrow, "rethrow", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0a, BrOnExn, "br_on_exn", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0b, End, "end", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0c, Br, "br", "") -WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0d, BrIf, "br_if", "") -WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0e, BrTable, "br_table", "") +WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x0d, BrIf, "br_if", "") +WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x0e, BrTable, "br_table", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x0f, Return, "return", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x10, Call, "call", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x11, CallIndirect, "call_indirect", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x12, ReturnCall, "return_call", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x13, ReturnCallIndirect, "return_call_indirect", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x1a, Drop, "drop", "") -WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x1b, Select, "select", "") -WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x1c, SelectT, "select", "") +WABT_OPCODE(___, ___, ___, I32, 0, 0, 0x1b, Select, "select", "") +WABT_OPCODE(___, ___, ___, I32, 0, 0, 0x1c, SelectT, "select", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x20, LocalGet, "local.get", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x21, LocalSet, "local.set", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x22, LocalTee, "local.tee", "") @@ -225,7 +225,7 @@ WABT_OPCODE(I64, I64, ___, ___, 0, 0, 0xC4, I64Extend32S, "i64.extend32_ /* Interpreter-only opcodes */ WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe0, InterpAlloca, "alloca", "") -WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe1, InterpBrUnless, "br_unless", "") +WABT_OPCODE(___, I32, ___, ___, 0, 0, 0xe1, InterpBrUnless, "br_unless", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe2, InterpCallHost, "call_host", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe3, InterpData, "data", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xe4, InterpDropKeep, "drop_keep", "") @@ -250,11 +250,11 @@ WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x0d, ElemDrop, "elem.drop", "") WABT_OPCODE(___, I32, I32, I32, 0, 0xfc, 0x0e, TableCopy, "table.copy", "") /* Reference types (--enable-reference-types) */ -WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x25, TableGet, "table.get", "") -WABT_OPCODE(___, ___, ___, ___, 0, 0, 0x26, TableSet, "table.set", "") -WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x0f, TableGrow, "table.grow", "") +WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x25, TableGet, "table.get", "") +WABT_OPCODE(___, I32, ___, ___, 0, 0, 0x26, TableSet, "table.set", "") +WABT_OPCODE(___, ___, I32, ___, 0, 0xfc, 0x0f, TableGrow, "table.grow", "") WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x10, TableSize, "table.size", "") -WABT_OPCODE(___, ___, ___, ___, 0, 0xfc, 0x11, TableFill, "table.fill", "") +WABT_OPCODE(___, I32, ___, I32, 0, 0xfc, 0x11, TableFill, "table.fill", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd0, RefNull, "ref.null", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd1, RefIsNull, "ref.is_null", "") WABT_OPCODE(___, ___, ___, ___, 0, 0, 0xd2, RefFunc, "ref.func", "") diff --git a/src/opcode.h b/src/opcode.h index 8cd85f15..d96c08e7 100644 --- a/src/opcode.h +++ b/src/opcode.h @@ -63,9 +63,10 @@ struct Opcode { return *GetInfo().decomp ? GetInfo().decomp : GetInfo().name; } Type GetResultType() const { return GetInfo().result_type; } - Type GetParamType1() const { return GetInfo().param1_type; } - Type GetParamType2() const { return GetInfo().param2_type; } - Type GetParamType3() const { return GetInfo().param3_type; } + Type GetParamType1() const { return GetInfo().param_types[0]; } + Type GetParamType2() const { return GetInfo().param_types[1]; } + Type GetParamType3() const { return GetInfo().param_types[2]; } + Type GetParamType(int n) const { return GetInfo().param_types[n - 1]; } Address GetMemorySize() const { return GetInfo().memory_size; } // Get the byte sequence for this opcode, including prefix. @@ -98,9 +99,7 @@ struct Opcode { const char* name; const char* decomp; Type result_type; - Type param1_type; - Type param2_type; - Type param3_type; + Type param_types[3]; Address memory_size; uint8_t prefix; uint32_t code; diff --git a/src/test-interp.cc b/src/test-interp.cc index 0a3d31f7..02636a8e 100644 --- a/src/test-interp.cc +++ b/src/test-interp.cc @@ -1,5 +1,5 @@ /* - * Copyright 2018 WebAssembly Community Group participants + * Copyright 2020 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,183 +16,388 @@ #include "gtest/gtest.h" -#include <algorithm> -#include <functional> -#include <memory> -#include <string> -#include <vector> - #include "src/binary-reader.h" -#include "src/cast.h" +#include "src/error-formatter.h" + #include "src/interp/binary-reader-interp.h" #include "src/interp/interp.h" -#include "src/make-unique.h" using namespace wabt; +using namespace wabt::interp; -namespace { - -interp::Result TrapCallback(const interp::HostFunc* func, - const interp::FuncSignature* sig, - const interp::TypedValues& args, - interp::TypedValues& results) { - return interp::ResultType::TrapHostTrapped; -} - -Features s_features; +class InterpTest : public ::testing::Test { + public: + void ReadModule(const std::vector<u8>& data) { + Errors errors; + ReadBinaryOptions options; + Result result = ReadBinaryInterp(data.data(), data.size(), options, &errors, + &module_desc_); + ASSERT_EQ(Result::Ok, result) + << FormatErrorsToString(errors, Location::Type::Binary); + } -class HostTrapTest : public ::testing::Test { - protected: - virtual void SetUp() { - env_ = MakeUnique<interp::Environment>(s_features); - interp::HostModule* host_module = env_->AppendHostModule("host"); - host_module->AppendFuncExport("a", {{}, {}}, TrapCallback); + void Instantiate(const RefVec& imports = RefVec{}) { + mod_ = Module::New(store_, module_desc_); + RefPtr<Trap> trap; + inst_ = Instance::Instantiate(store_, mod_.ref(), imports, &trap); + ASSERT_TRUE(inst_) << trap->message(); } - virtual void TearDown() {} + DefinedFunc::Ptr GetFuncExport(interp::Index index) { + EXPECT_LT(index, inst_->exports().size()); + return store_.UnsafeGet<DefinedFunc>(inst_->exports()[index]); + } - interp::ExecResult LoadModuleAndRunStartFunction( - const std::vector<uint8_t>& data) { - Errors errors; - interp::DefinedModule* module = nullptr; - ReadBinaryOptions options; - Result result = ReadBinaryInterp(env_.get(), data.data(), data.size(), - options, &errors, &module); - EXPECT_EQ(Result::Ok, result); - - if (result == Result::Ok) { - interp::Executor executor(env_.get()); - return executor.Initialize(module); - } else { - return {}; - } + void ExpectBufferStrEq(OutputBuffer& buf, const char* str) { + std::string buf_str(buf.data.begin(), buf.data.end()); + EXPECT_STREQ(buf_str.c_str(), str); } - std::unique_ptr<interp::Environment> env_; + Store store_; + ModuleDesc module_desc_; + Module::Ptr mod_; + Instance::Ptr inst_; }; -} // end of anonymous namespace -TEST_F(HostTrapTest, Call) { - // (import "host" "a" (func $0)) - // (func $1 call $0) - // (start $1) - std::vector<uint8_t> data = { - 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, - 0x60, 0x00, 0x00, 0x02, 0x0a, 0x01, 0x04, 0x68, 0x6f, 0x73, 0x74, - 0x01, 0x61, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x08, 0x01, 0x01, - 0x0a, 0x06, 0x01, 0x04, 0x00, 0x10, 0x00, 0x0b, - }; - ASSERT_EQ(LoadModuleAndRunStartFunction(data).result.type, - interp::ResultType::TrapHostTrapped); +TEST_F(InterpTest, Empty) { + ReadModule({0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00}); } -TEST_F(HostTrapTest, CallIndirect) { - // (import "host" "a" (func $0)) - // (table anyfunc (elem $0)) - // (func $1 i32.const 0 call_indirect) - // (start $1) - std::vector<uint8_t> data = { - 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, - 0x00, 0x00, 0x02, 0x0a, 0x01, 0x04, 0x68, 0x6f, 0x73, 0x74, 0x01, 0x61, - 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01, - 0x01, 0x08, 0x01, 0x01, 0x09, 0x07, 0x01, 0x00, 0x41, 0x00, 0x0b, 0x01, - 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x41, 0x00, 0x11, 0x00, 0x00, 0x0b, - }; - ASSERT_EQ(LoadModuleAndRunStartFunction(data).result.type, - interp::ResultType::TrapHostTrapped); +TEST_F(InterpTest, MVP) { + // (module + // (type (;0;) (func (param i32) (result i32))) + // (type (;1;) (func (param f32) (result f32))) + // (type (;2;) (func)) + // (import "foo" "bar" (func (;0;) (type 0))) + // (func (;1;) (type 1) (param f32) (result f32) + // (f32.const 0x1.5p+5 (;=42;))) + // (func (;2;) (type 2)) + // (table (;0;) 1 2 funcref) + // (memory (;0;) 1) + // (global (;0;) i32 (i32.const 1)) + // (export "quux" (func 1)) + // (start 2) + // (elem (;0;) (i32.const 0) 0 1) + // (data (;0;) (i32.const 2) "hello")) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0e, 0x03, 0x60, + 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7d, 0x01, 0x7d, 0x60, 0x00, 0x00, + 0x02, 0x0b, 0x01, 0x03, 0x66, 0x6f, 0x6f, 0x03, 0x62, 0x61, 0x72, 0x00, + 0x00, 0x03, 0x03, 0x02, 0x01, 0x02, 0x04, 0x05, 0x01, 0x70, 0x01, 0x01, + 0x02, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x06, 0x01, 0x7f, 0x00, 0x41, + 0x01, 0x0b, 0x07, 0x08, 0x01, 0x04, 0x71, 0x75, 0x75, 0x78, 0x00, 0x01, + 0x08, 0x01, 0x02, 0x09, 0x08, 0x01, 0x00, 0x41, 0x00, 0x0b, 0x02, 0x00, + 0x01, 0x0a, 0x0c, 0x02, 0x07, 0x00, 0x43, 0x00, 0x00, 0x28, 0x42, 0x0b, + 0x02, 0x00, 0x0b, 0x0b, 0x0b, 0x01, 0x00, 0x41, 0x02, 0x0b, 0x05, 0x68, + 0x65, 0x6c, 0x6c, 0x6f, + }); + + EXPECT_EQ(3u, module_desc_.func_types.size()); + EXPECT_EQ(1u, module_desc_.imports.size()); + EXPECT_EQ(2u, module_desc_.funcs.size()); + EXPECT_EQ(1u, module_desc_.tables.size()); + EXPECT_EQ(1u, module_desc_.memories.size()); + EXPECT_EQ(1u, module_desc_.globals.size()); + EXPECT_EQ(0u, module_desc_.events.size()); + EXPECT_EQ(1u, module_desc_.exports.size()); + EXPECT_EQ(1u, module_desc_.starts.size()); + EXPECT_EQ(1u, module_desc_.elems.size()); + EXPECT_EQ(1u, module_desc_.datas.size()); } namespace { -class HostMemoryTest : public ::testing::Test { - protected: - virtual void SetUp() { - env_ = MakeUnique<interp::Environment>(s_features); - interp::HostModule* host_module = env_->AppendHostModule("host"); - executor_ = MakeUnique<interp::Executor>(env_.get()); - std::pair<interp::Memory*, Index> pair = - host_module->AppendMemoryExport("mem", Limits(1)); - - using namespace std::placeholders; - - host_module->AppendFuncExport( - "fill_buf", {{Type::I32, Type::I32}, {Type::I32}}, - std::bind(&HostMemoryTest::FillBufCallback, this, _1, _2, _3, _4)); - host_module->AppendFuncExport( - "buf_done", {{Type::I32, Type::I32}, {}}, - std::bind(&HostMemoryTest::BufDoneCallback, this, _1, _2, _3, _4)); - memory_ = pair.first; - } - - virtual void TearDown() { - executor_.reset(); - } - - Result LoadModule(const std::vector<uint8_t>& data) { - Errors errors; - ReadBinaryOptions options; - return ReadBinaryInterp(env_.get(), data.data(), data.size(), options, - &errors, &module_); - } +// (func (export "fac") (param $n i32) (result i32) +// (local $result i32) +// (local.set $result (i32.const 1)) +// (loop (result i32) +// (local.set $result +// (i32.mul +// (br_if 1 (local.get $result) (i32.eqz (local.get $n))) +// (local.get $n))) +// (local.set $n (i32.sub (local.get $n) (i32.const 1))) +// (br 0))) +const std::vector<u8> s_fac_module = { + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, + 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, + 0x01, 0x03, 0x66, 0x61, 0x63, 0x00, 0x00, 0x0a, 0x22, 0x01, 0x20, + 0x01, 0x01, 0x7f, 0x41, 0x01, 0x21, 0x01, 0x03, 0x7f, 0x20, 0x01, + 0x20, 0x00, 0x45, 0x0d, 0x01, 0x20, 0x00, 0x6c, 0x21, 0x01, 0x20, + 0x00, 0x41, 0x01, 0x6b, 0x21, 0x00, 0x0c, 0x00, 0x0b, 0x0b, +}; - std::string string_data; +} // namespace + +TEST_F(InterpTest, Disassemble) { + ReadModule(s_fac_module); + + MemoryStream stream; + module_desc_.istream.Disassemble(&stream); + auto buf = stream.ReleaseOutputBuffer(); + + ExpectBufferStrEq(*buf, +R"( 0| alloca 1 + 8| i32.const 1 + 16| local.set $2, %[-1] + 24| local.get $1 + 32| local.get $3 + 40| i32.eqz %[-1] + 44| br_unless @60, %[-1] + 52| br @116 + 60| local.get $3 + 68| i32.mul %[-2], %[-1] + 72| local.set $2, %[-1] + 80| local.get $2 + 88| i32.const 1 + 96| i32.sub %[-2], %[-1] + 100| local.set $3, %[-1] + 108| br @24 + 116| drop_keep $2 $1 + 128| return +)"); +} - interp::Result FillBufCallback(const interp::HostFunc* func, - const interp::FuncSignature* sig, - const interp::TypedValues& args, - interp::TypedValues& results) { - // (param $ptr i32) (param $max_size i32) (result $size i32) - EXPECT_EQ(2u, args.size()); - EXPECT_EQ(Type::I32, args[0].type); - EXPECT_EQ(Type::I32, args[1].type); - EXPECT_EQ(1u, results.size()); - EXPECT_EQ(Type::I32, results[0].type); +TEST_F(InterpTest, Fac) { + ReadModule(s_fac_module); + Instantiate(); + auto func = GetFuncExport(0); - uint32_t ptr = args[0].get_i32(); - uint32_t max_size = args[1].get_i32(); - uint32_t size = std::min(max_size, uint32_t(string_data.size())); + Values results; + Trap::Ptr trap; + Result result = func->Call(store_, {Value::Make(5)}, results, &trap); - EXPECT_LT(ptr + size, memory_->data.size()); + ASSERT_EQ(Result::Ok, result); + EXPECT_EQ(1u, results.size()); + EXPECT_EQ(120u, results[0].Get<u32>()); +} - std::copy(string_data.begin(), string_data.begin() + size, - memory_->data.begin() + ptr); +TEST_F(InterpTest, Fac_Trace) { + ReadModule(s_fac_module); + Instantiate(); + auto func = GetFuncExport(0); + + Values results; + Trap::Ptr trap; + MemoryStream stream; + Result result = func->Call(store_, {Value::Make(2)}, results, &trap, &stream); + ASSERT_EQ(Result::Ok, result); + + auto buf = stream.ReleaseOutputBuffer(); + ExpectBufferStrEq(*buf, +R"(#0. 0: V:1 | alloca 1 +#0. 8: V:2 | i32.const 1 +#0. 16: V:3 | local.set $2, 1 +#0. 24: V:2 | local.get $1 +#0. 32: V:3 | local.get $3 +#0. 40: V:4 | i32.eqz 2 +#0. 44: V:4 | br_unless @60, 0 +#0. 60: V:3 | local.get $3 +#0. 68: V:4 | i32.mul 1, 2 +#0. 72: V:3 | local.set $2, 2 +#0. 80: V:2 | local.get $2 +#0. 88: V:3 | i32.const 1 +#0. 96: V:4 | i32.sub 2, 1 +#0. 100: V:3 | local.set $3, 1 +#0. 108: V:2 | br @24 +#0. 24: V:2 | local.get $1 +#0. 32: V:3 | local.get $3 +#0. 40: V:4 | i32.eqz 1 +#0. 44: V:4 | br_unless @60, 0 +#0. 60: V:3 | local.get $3 +#0. 68: V:4 | i32.mul 2, 1 +#0. 72: V:3 | local.set $2, 2 +#0. 80: V:2 | local.get $2 +#0. 88: V:3 | i32.const 1 +#0. 96: V:4 | i32.sub 1, 1 +#0. 100: V:3 | local.set $3, 0 +#0. 108: V:2 | br @24 +#0. 24: V:2 | local.get $1 +#0. 32: V:3 | local.get $3 +#0. 40: V:4 | i32.eqz 0 +#0. 44: V:4 | br_unless @60, 1 +#0. 52: V:3 | br @116 +#0. 116: V:3 | drop_keep $2 $1 +#0. 128: V:1 | return +)"); +} - results[0].set_i32(size); - return interp::ResultType::Ok; - } +TEST_F(InterpTest, Local_Trace) { + // (func (export "a") + // (local i32 i64 f32 f64) + // (local.set 0 (i32.const 0)) + // (local.set 1 (i64.const 1)) + // (local.set 2 (f32.const 2)) + // (local.set 3 (f64.const 3))) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, + 0x60, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, + 0x61, 0x00, 0x00, 0x0a, 0x26, 0x01, 0x24, 0x04, 0x01, 0x7f, 0x01, + 0x7e, 0x01, 0x7d, 0x01, 0x7c, 0x41, 0x00, 0x21, 0x00, 0x42, 0x01, + 0x21, 0x01, 0x43, 0x00, 0x00, 0x00, 0x40, 0x21, 0x02, 0x44, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x40, 0x21, 0x03, 0x0b, + }); + + Instantiate(); + auto func = GetFuncExport(0); + + Values results; + Trap::Ptr trap; + MemoryStream stream; + Result result = func->Call(store_, {}, results, &trap, &stream); + ASSERT_EQ(Result::Ok, result); + + auto buf = stream.ReleaseOutputBuffer(); + ExpectBufferStrEq(*buf, +R"(#0. 0: V:0 | alloca 4 +#0. 8: V:4 | i32.const 0 +#0. 16: V:5 | local.set $5, 0 +#0. 24: V:4 | i64.const 1 +#0. 36: V:5 | local.set $4, 1 +#0. 44: V:4 | f32.const 2 +#0. 52: V:5 | local.set $3, 2 +#0. 60: V:4 | f64.const 3 +#0. 72: V:5 | local.set $2, 3 +#0. 80: V:4 | drop_keep $4 $0 +#0. 92: V:0 | return +)"); +} - interp::Result BufDoneCallback(const interp::HostFunc* func, - const interp::FuncSignature* sig, - const interp::TypedValues& args, - interp::TypedValues& results) { - // (param $ptr i32) (param $size i32) - EXPECT_EQ(2u, args.size()); - EXPECT_EQ(Type::I32, args[0].type); - EXPECT_EQ(Type::I32, args[1].type); - EXPECT_EQ(0u, results.size()); +TEST_F(InterpTest, HostFunc) { + // (import "" "f" (func $f (param i32) (result i32))) + // (func (export "g") (result i32) + // (call $f (i32.const 1))) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0a, + 0x02, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x01, 0x7f, + 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00, 0x03, 0x02, + 0x01, 0x01, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a, + 0x08, 0x01, 0x06, 0x00, 0x41, 0x01, 0x10, 0x00, 0x0b, + }); + + auto host_func = HostFunc::New( + store_, FuncType{{ValueType::I32}, {ValueType::I32}}, + [](const Values& params, Values& results, Trap::Ptr* out_trap) -> Result { + results[0] = Value::Make(params[0].Get<u32>() + 1); + return Result::Ok; + }); + + Instantiate({host_func->self()}); + + Values results; + Trap::Ptr trap; + Result result = GetFuncExport(0)->Call(store_, {}, results, &trap); + + ASSERT_EQ(Result::Ok, result); + EXPECT_EQ(1u, results.size()); + EXPECT_EQ(2u, results[0].Get<u32>()); +} - uint32_t ptr = args[0].get_i32(); - uint32_t size = args[1].get_i32(); +TEST_F(InterpTest, HostFunc_PingPong) { + // (import "" "f" (func $f (param i32) (result i32))) + // (func (export "g") (param i32) (result i32) + // (call $f (i32.add (local.get 0) (i32.const 1)))) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, + 0x01, 0x7f, 0x01, 0x7f, 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00, + 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a, + 0x0b, 0x01, 0x09, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x10, 0x00, 0x0b, + }); + + auto host_func = HostFunc::New( + store_, FuncType{{ValueType::I32}, {ValueType::I32}}, + [&](const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + auto val = params[0].Get<u32>(); + if (val < 10) { + return GetFuncExport(0)->Call(store_, {Value::Make(val * 2)}, results, + out_trap); + } + results[0] = Value::Make(val); + return Result::Ok; + }); + + Instantiate({host_func->self()}); + + // Should produce the following calls: + // g(1) -> f(2) -> g(4) -> f(5) -> g(10) -> f(11) -> return 11 + + Values results; + Trap::Ptr trap; + Result result = GetFuncExport(0)->Call(store_, {Value::Make(1)}, results, &trap); + + ASSERT_EQ(Result::Ok, result); + EXPECT_EQ(1u, results.size()); + EXPECT_EQ(11u, results[0].Get<u32>()); +} - EXPECT_LT(ptr + size, memory_->data.size()); +TEST_F(InterpTest, HostFunc_PingPong_SameThread) { + // (import "" "f" (func $f (param i32) (result i32))) + // (func (export "g") (param i32) (result i32) + // (call $f (i32.add (local.get 0) (i32.const 1)))) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x06, 0x01, 0x60, + 0x01, 0x7f, 0x01, 0x7f, 0x02, 0x06, 0x01, 0x00, 0x01, 0x66, 0x00, 0x00, + 0x03, 0x02, 0x01, 0x00, 0x07, 0x05, 0x01, 0x01, 0x67, 0x00, 0x01, 0x0a, + 0x0b, 0x01, 0x09, 0x00, 0x20, 0x00, 0x41, 0x01, 0x6a, 0x10, 0x00, 0x0b, + }); + + auto thread = Thread::New(store_, {}); + + auto host_func = HostFunc::New( + store_, FuncType{{ValueType::I32}, {ValueType::I32}}, + [&](const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + auto val = params[0].Get<u32>(); + if (val < 10) { + return GetFuncExport(0)->Call(*thread, {Value::Make(val * 2)}, results, + out_trap); + } + results[0] = Value::Make(val); + return Result::Ok; + }); + + Instantiate({host_func->self()}); + + // Should produce the following calls: + // g(1) -> f(2) -> g(4) -> f(5) -> g(10) -> f(11) -> return 11 + + Values results; + Trap::Ptr trap; + Result result = GetFuncExport(0)->Call(*thread, {Value::Make(1)}, results, &trap); + + ASSERT_EQ(Result::Ok, result); + EXPECT_EQ(1u, results.size()); + EXPECT_EQ(11u, results[0].Get<u32>()); +} - string_data.resize(size); - std::copy(memory_->data.begin() + ptr, memory_->data.begin() + ptr + size, - string_data.begin()); +TEST_F(InterpTest, HostTrap) { + // (import "host" "a" (func $0)) + // (func $1 call $0) + // (start $1) + ReadModule({ + 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, + 0x60, 0x00, 0x00, 0x02, 0x0a, 0x01, 0x04, 0x68, 0x6f, 0x73, 0x74, + 0x01, 0x61, 0x00, 0x00, 0x03, 0x02, 0x01, 0x00, 0x08, 0x01, 0x01, + 0x0a, 0x06, 0x01, 0x04, 0x00, 0x10, 0x00, 0x0b, + }); - return interp::ResultType::Ok; - } + auto host_func = HostFunc::New(store_, FuncType{{}, {}}, + [&](const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + *out_trap = Trap::New(store_, "boom"); + return Result::Error; + }); - std::unique_ptr<interp::Environment> env_; - interp::Memory* memory_; - interp::DefinedModule* module_; - std::unique_ptr<interp::Executor> executor_; -}; + mod_ = Module::New(store_, module_desc_); + RefPtr<Trap> trap; + Instance::Instantiate(store_, mod_.ref(), {host_func->self()}, &trap); -} // end of anonymous namespace + ASSERT_TRUE(trap); + ASSERT_EQ("boom", trap->message()); +} -TEST_F(HostMemoryTest, Rot13) { +TEST_F(InterpTest, Rot13) { // (import "host" "mem" (memory $mem 1)) // (import "host" "fill_buf" (func $fill_buf (param i32 i32) (result i32))) // (import "host" "buf_done" (func $buf_done (param i32 i32))) @@ -249,7 +454,7 @@ TEST_F(HostMemoryTest, Rot13) { // // (call $buf_done (i32.const 0) (get_local $size)) // ) - std::vector<uint8_t> data = { + ReadModule({ 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x14, 0x04, 0x60, 0x02, 0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x02, 0x7f, 0x7f, 0x00, 0x60, 0x01, 0x7f, 0x01, 0x7f, 0x60, 0x00, 0x00, 0x02, 0x2d, 0x03, 0x04, 0x68, 0x6f, @@ -268,17 +473,72 @@ TEST_F(HostMemoryTest, Rot13) { 0x0b, 0x20, 0x01, 0x20, 0x01, 0x2d, 0x00, 0x00, 0x10, 0x02, 0x3a, 0x00, 0x00, 0x20, 0x01, 0x41, 0x01, 0x6a, 0x21, 0x01, 0x0c, 0x00, 0x0b, 0x0b, 0x41, 0x00, 0x20, 0x00, 0x10, 0x01, 0x0b, + }); + + auto host_func = HostFunc::New( + store_, FuncType{{ValueType::I32}, {ValueType::I32}}, + [](const Values& params, Values& results, Trap::Ptr* out_trap) -> Result { + results[0] = Value::Make(params[0].Get<u32>() + 1); + return Result::Ok; + }); + + std::string string_data = "Hello, WebAssembly!"; + + auto memory = Memory::New(store_, MemoryType{Limits{1}}); + + auto fill_buf = [&](const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + // (param $ptr i32) (param $max_size i32) (result $size i32) + EXPECT_EQ(2u, params.size()); + EXPECT_EQ(1u, results.size()); + + u32 ptr = params[0].Get<u32>(); + u32 max_size = params[1].Get<u32>(); + u32 size = std::min(max_size, u32(string_data.size())); + + EXPECT_LT(ptr + size, memory->ByteSize()); + + std::copy(string_data.begin(), string_data.begin() + size, + memory->UnsafeData() + ptr); + + results[0].Set(size); + return Result::Ok; + }; + auto fill_buf_func = HostFunc::New( + store_, FuncType{{ValueType::I32, ValueType::I32}, {ValueType::I32}}, + fill_buf); + + auto buf_done = [&](const Values& params, Values& results, + Trap::Ptr* out_trap) -> Result { + // (param $ptr i32) (param $size i32) + EXPECT_EQ(2u, params.size()); + EXPECT_EQ(0u, results.size()); + + u32 ptr = params[0].Get<u32>(); + u32 size = params[1].Get<u32>(); + + EXPECT_LT(ptr + size, memory->ByteSize()); + + string_data.resize(size); + std::copy(memory->UnsafeData() + ptr, memory->UnsafeData() + ptr + size, + string_data.begin()); + + return Result::Ok; }; + auto buf_done_func = HostFunc::New( + store_, FuncType{{ValueType::I32, ValueType::I32}, {}}, buf_done); - ASSERT_EQ(Result::Ok, LoadModule(data)); + Instantiate({memory->self(), fill_buf_func->self(), buf_done_func->self()}); - string_data = "Hello, WebAssembly!"; + auto rot13 = GetFuncExport(0); - ASSERT_TRUE(executor_->RunExportByName(module_, "rot13", {}).ok()); + Values results; + Trap::Ptr trap; + ASSERT_EQ(Result::Ok, rot13->Call(store_, {}, results, &trap)); ASSERT_EQ("Uryyb, JroNffrzoyl!", string_data); - ASSERT_TRUE(executor_->RunExportByName(module_, "rot13", {}).ok()); + ASSERT_EQ(Result::Ok, rot13->Call(store_, {}, results, &trap)); ASSERT_EQ("Hello, WebAssembly!", string_data); } diff --git a/src/tools/spectest-interp.cc b/src/tools/spectest-interp.cc index 047a3dc1..b0837ca7 100644 --- a/src/tools/spectest-interp.cc +++ b/src/tools/spectest-interp.cc @@ -19,6 +19,7 @@ #include <cinttypes> #include <cstdio> #include <cstdlib> +#include <map> #include <memory> #include <string> #include <vector> @@ -29,6 +30,7 @@ #include "src/error-formatter.h" #include "src/feature.h" #include "src/interp/binary-reader-interp.h" +#include "src/interp/interp-util.h" #include "src/interp/interp.h" #include "src/literal.h" #include "src/option-parser.h" @@ -139,7 +141,8 @@ class Action { ActionType type = ActionType::Invoke; std::string module_name; std::string field_name; - TypedValues args; + ValueTypes types; + Values args; }; template <CommandType TypeEnum> @@ -222,9 +225,9 @@ class JSONParser { wabt::Result ParseTypeVector(TypeVector* out_types); wabt::Result ParseConst(TypedValue* out_value); wabt::Result ParseConstValue(TypedValue* out_value, - string_view type_str, - string_view value_str); - wabt::Result ParseConstVector(TypedValues* out_values); + string_view type_str, + string_view value_str); + wabt::Result ParseConstVector(ValueTypes* out_types, Values* out_values); wabt::Result ParseExpectedValue(ExpectedValue* out_value); wabt::Result ParseExpectedValues(std::vector<ExpectedValue>* out_values); wabt::Result ParseAction(Action* out_action); @@ -500,8 +503,8 @@ wabt::Result JSONParser::ParseConst(TypedValue* out_value) { } wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, - string_view type_str, - string_view value_str) { + string_view type_str, + string_view value_str) { const char* value_start = value_str.data(); const char* value_end = value_str.data() + value_str.size(); if (type_str == "i32") { @@ -511,8 +514,8 @@ wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, PrintError("invalid i32 literal"); return wabt::Result::Error; } - out_value->type = Type::I32; - out_value->value.i32 = value; + out_value->type = ValueType::I32; + out_value->value.Set(value); } else if (type_str == "f32") { uint32_t value_bits; if (Failed(ParseInt32(value_start, value_end, &value_bits, @@ -520,8 +523,8 @@ wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, PrintError("invalid f32 literal"); return wabt::Result::Error; } - out_value->type = Type::F32; - out_value->value.f32_bits = value_bits; + out_value->type = ValueType::F32; + out_value->value.Set(Bitcast<f32>(value_bits)); } else if (type_str == "i64") { uint64_t value; if (Failed(ParseInt64(value_start, value_end, &value, @@ -529,8 +532,8 @@ wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, PrintError("invalid i64 literal"); return wabt::Result::Error; } - out_value->type = Type::I64; - out_value->value.i64 = value; + out_value->type = ValueType::I64; + out_value->value.Set(value); } else if (type_str == "f64") { uint64_t value_bits; if (Failed((ParseInt64(value_start, value_end, &value_bits, @@ -538,19 +541,19 @@ wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, PrintError("invalid f64 literal"); return wabt::Result::Error; } - out_value->type = Type::F64; - out_value->value.f64_bits = value_bits; + out_value->type = ValueType::F64; + out_value->value.Set(Bitcast<f64>(value_bits)); } else if (type_str == "v128") { v128 value_bits; if (Failed(ParseUint128(value_start, value_end, &value_bits))) { PrintError("invalid v128 literal"); return wabt::Result::Error; } - out_value->type = Type::V128; - out_value->value.vec128 = value_bits; + out_value->type = ValueType::V128; + out_value->value.Set(value_bits); } else if (type_str == "nullref") { - out_value->type = Type::Nullref; - out_value->value.ref = {RefType::Null, 0}; + out_value->type = ValueType::Nullref; + out_value->value.Set(Ref::Null); } else if (type_str == "hostref") { uint32_t value; if (Failed(ParseInt32(value_start, value_end, &value, @@ -558,16 +561,19 @@ wabt::Result JSONParser::ParseConstValue(TypedValue* out_value, PrintError("invalid hostref literal"); return wabt::Result::Error; } - out_value->type = Type::Hostref; - out_value->value.ref = {RefType::Host, value}; + out_value->type = ValueType::Hostref; + // TODO: hack, just whatever ref is at this index; but skip null (which is + // always 0). + out_value->value.Set(Ref{value + 1}); } else if (type_str == "funcref") { uint32_t value; - if (Failed(ParseInt32(value_start, value_end, &value, ParseIntType::UnsignedOnly))) { + if (Failed(ParseInt32(value_start, value_end, &value, + ParseIntType::UnsignedOnly))) { PrintError("invalid funcref literal"); return wabt::Result::Error; } - out_value->type = Type::Funcref; - out_value->value.ref = {RefType::Func, value}; + out_value->type = ValueType::Funcref; + out_value->value.Set(Ref{value}); } else { PrintError("unknown concrete type: \"%s\"", type_str.to_string().c_str()); return wabt::Result::Error; @@ -586,13 +592,12 @@ wabt::Result JSONParser::ParseExpectedValue(ExpectedValue* out_value) { EXPECT("}"); if (type_str == "f32" || type_str == "f64") { + out_value->value.type = type_str == "f32" ? ValueType::F32 : ValueType::F64; if (value_str == "nan:canonical") { - out_value->value.type = type_str == "f32" ? Type::F32 : Type::F64; out_value->is_expected_nan = true; out_value->expectedNan = ExpectedNan::Canonical; return wabt::Result::Ok; } else if (value_str == "nan:arithmetic") { - out_value->value.type = type_str == "f32" ? Type::F32 : Type::F64; out_value->is_expected_nan = true; out_value->expectedNan = ExpectedNan::Arithmetic; return wabt::Result::Ok; @@ -603,7 +608,8 @@ wabt::Result JSONParser::ParseExpectedValue(ExpectedValue* out_value) { return ParseConstValue(&out_value->value, type_str, value_str); } -wabt::Result JSONParser::ParseExpectedValues(std::vector<ExpectedValue>* out_values) { +wabt::Result JSONParser::ParseExpectedValues( + std::vector<ExpectedValue>* out_values) { out_values->clear(); EXPECT("["); bool first = true; @@ -619,7 +625,7 @@ wabt::Result JSONParser::ParseExpectedValues(std::vector<ExpectedValue>* out_val return wabt::Result::Ok; } -wabt::Result JSONParser::ParseConstVector(TypedValues* out_values) { +wabt::Result JSONParser::ParseConstVector(ValueTypes* out_types, Values* out_values) { out_values->clear(); EXPECT("["); bool first = true; @@ -627,9 +633,10 @@ wabt::Result JSONParser::ParseConstVector(TypedValues* out_values) { if (!first) { EXPECT(","); } - TypedValue value; - CHECK_RESULT(ParseConst(&value)); - out_values->push_back(value); + TypedValue tv; + CHECK_RESULT(ParseConst(&tv)); + out_types->push_back(tv.type); + out_values->push_back(tv.value); first = false; } return wabt::Result::Ok; @@ -655,7 +662,7 @@ wabt::Result JSONParser::ParseAction(Action* out_action) { if (out_action->type == ActionType::Invoke) { EXPECT(","); EXPECT_KEY("args"); - CHECK_RESULT(ParseConstVector(&out_action->args)); + CHECK_RESULT(ParseConstVector(&out_action->types, &out_action->args)); } EXPECT("}"); return wabt::Result::Ok; @@ -859,21 +866,34 @@ wabt::Result JSONParser::ParseScript(Script* out_script) { return wabt::Result::Ok; } +struct ActionResult { + ValueTypes types; + Values values; + Trap::Ptr trap; +}; + class CommandRunner { public: CommandRunner(); - wabt::Result Run(const Script& script); int passed() const { return passed_; } int total() const { return total_; } private: + using ExportMap = std::map<std::string, Extern::Ptr>; + using Registry = std::map<std::string, ExportMap>; + void WABT_PRINTF_FORMAT(3, 4) PrintError(uint32_t line_number, const char* format, ...); - ExecResult RunAction(int line_number, - const Action* action, - RunVerbosity verbose); + ActionResult RunAction(int line_number, + const Action* action, + RunVerbosity verbose); + + interp::Module::Ptr ReadModule(string_view module_filename, Errors* errors); + Extern::Ptr GetImport(const std::string&, const std::string&); + void PopulateImports(const interp::Module::Ptr&, RefVec*); + void PopulateExports(const Instance::Ptr&, ExportMap*); wabt::Result OnModuleCommand(const ModuleCommand*); wabt::Result OnActionCommand(const ActionCommand*); @@ -890,61 +910,67 @@ class CommandRunner { void TallyCommand(wabt::Result); wabt::Result ReadInvalidTextModule(string_view module_filename, - Environment* env, const std::string& header); wabt::Result ReadInvalidModule(int line_number, - string_view module_filename, - Environment* env, - ModuleType module_type, - const char* desc); + string_view module_filename, + ModuleType module_type, + const char* desc); wabt::Result ReadUnlinkableModule(int line_number, - string_view module_filename, - Environment* env, - ModuleType module_type, - const char* desc); - - Environment env_; - Executor executor_; - DefinedModule* last_module_ = nullptr; + string_view module_filename, + ModuleType module_type, + const char* desc); + + Store store_; + Registry registry_; // Used when importing. + Registry instances_; // Used when referencing module by name in invoke. + ExportMap last_instance_; int passed_ = 0; int total_ = 0; std::string source_filename_; }; -static interp::Result PrintCallback(const HostFunc* func, - const interp::FuncSignature* sig, - const TypedValues& args, - TypedValues& results) { - printf("called host "); - WriteCall(s_stdout_stream.get(), func->module_name, func->field_name, args, - results, interp::ResultType::Ok); - return interp::ResultType::Ok; -} +CommandRunner::CommandRunner() : store_(s_features) { + auto&& spectest = registry_["spectest"]; + + // Initialize print functions for the spec test. + struct { + const char* name; + interp::FuncType type; + } const print_funcs[] = { + {"print", interp::FuncType{{}, {}}}, + {"print_i32", interp::FuncType{{ValueType::I32}, {}}}, + {"print_f32", interp::FuncType{{ValueType::F32}, {}}}, + {"print_f64", interp::FuncType{{ValueType::F64}, {}}}, + {"print_i32_f32", interp::FuncType{{ValueType::I32, ValueType::F32}, {}}}, + {"print_f64_f64", interp::FuncType{{ValueType::F64, ValueType::F64}, {}}}, + }; + + for (auto&& print : print_funcs) { + auto import_name = StringPrintf("spectest.%s", print.name); + spectest[print.name] = HostFunc::New( + store_, print.type, + [=](const Values& params, Values& results, Trap::Ptr* trap) -> wabt::Result { + printf("called host "); + WriteCall(s_stdout_stream.get(), import_name, print.type, params, + results, *trap); + return wabt::Result::Ok; + }); + } -static void InitEnvironment(Environment* env) { - HostModule* host_module = env->AppendHostModule("spectest"); - host_module->AppendFuncExport("print", {{}, {}}, PrintCallback); - host_module->AppendFuncExport("print_i32", {{Type::I32}, {}}, PrintCallback); - host_module->AppendFuncExport("print_f32", {{Type::F32}, {}}, PrintCallback); - host_module->AppendFuncExport("print_f64", {{Type::F64}, {}}, PrintCallback); - host_module->AppendFuncExport("print_i32_f32", {{Type::I32, Type::F32}, {}}, - PrintCallback); - host_module->AppendFuncExport("print_f64_f64", {{Type::F64, Type::F64}, {}}, - PrintCallback); - - host_module->AppendTableExport("table", Type::Funcref, Limits(10, 20)); - host_module->AppendMemoryExport("memory", Limits(1, 2)); - - host_module->AppendGlobalExport("global_i32", false, uint32_t(666)); - host_module->AppendGlobalExport("global_i64", false, uint64_t(666)); - host_module->AppendGlobalExport("global_f32", false, float(666.6f)); - host_module->AppendGlobalExport("global_f64", false, double(666.6)); -} + spectest["table"] = + interp::Table::New(store_, TableType{ValueType::Funcref, Limits{10, 20}}); -CommandRunner::CommandRunner() - : env_(s_features), executor_(&env_, s_trace_stream, s_thread_options) { - InitEnvironment(&env_); + spectest["memory"] = interp::Memory::New(store_, MemoryType{Limits{1, 2}}); + + spectest["global_i32"] = interp::Global::New( + store_, GlobalType{ValueType::I32, Mutability::Const}, Value::Make(u32{666})); + spectest["global_i64"] = interp::Global::New( + store_, GlobalType{ValueType::I64, Mutability::Const}, Value::Make(u64{666})); + spectest["global_f32"] = interp::Global::New( + store_, GlobalType{ValueType::F32, Mutability::Const}, Value::Make(f32{666})); + spectest["global_f64"] = interp::Global::New( + store_, GlobalType{ValueType::F64, Mutability::Const}, Value::Make(f64{666})); } wabt::Result CommandRunner::Run(const Script& script) { @@ -1009,58 +1035,50 @@ void CommandRunner::PrintError(uint32_t line_number, const char* format, ...) { printf("%s:%u: %s\n", source_filename_.c_str(), line_number, buffer); } -static ExecResult GetGlobalExportByName(Environment* env, - interp::Module* module, - string_view name) { - interp::Export* export_ = module->GetExport(name); - if (!export_) { - return ExecResult(interp::ResultType::UnknownExport); - } - if (export_->kind != ExternalKind::Global) { - return ExecResult(interp::ResultType::ExportKindMismatch); +ActionResult CommandRunner::RunAction(int line_number, + const Action* action, + RunVerbosity verbose) { + ExportMap& module = !action->module_name.empty() + ? instances_[action->module_name] + : last_instance_; + Extern::Ptr extern_ = module[action->field_name]; + if (!extern_) { + PrintError(line_number, "unknown invoke \"%s.%s\"", + action->module_name.c_str(), action->field_name.c_str()); + return {}; } - interp::Global* global = env->GetGlobal(export_->index); - return ExecResult(interp::ResultType::Ok, {global->typed_value}); -} - -ExecResult CommandRunner::RunAction(int line_number, - const Action* action, - RunVerbosity verbose) { - interp::Module* module; - if (!action->module_name.empty()) { - module = env_.FindModule(action->module_name); - } else { - module = env_.GetLastModule(); - } - assert(module); - - ExecResult exec_result; + ActionResult result; switch (action->type) { - case ActionType::Invoke: - exec_result = - executor_.RunExportByName(module, action->field_name, action->args); + case ActionType::Invoke: { + auto* func = cast<interp::Func>(extern_.get()); + func->Call(store_, action->args, result.values, &result.trap, + s_trace_stream); + result.types = func->type().results; if (verbose == RunVerbosity::Verbose) { - WriteCall(s_stdout_stream.get(), string_view(), action->field_name, - action->args, exec_result.values, exec_result.result); + WriteCall(s_stdout_stream.get(), action->field_name, func->type(), + action->args, result.values, result.trap); } break; + } - case ActionType::Get: - exec_result = GetGlobalExportByName(&env_, module, action->field_name); + case ActionType::Get: { + auto* global = cast<interp::Global>(extern_.get()); + result.values.push_back(global->Get()); + result.types.push_back(global->type().type); break; + } default: WABT_UNREACHABLE; } - return exec_result; + return result; } wabt::Result CommandRunner::ReadInvalidTextModule(string_view module_filename, - Environment* env, - const std::string& header) { + const std::string& header) { std::vector<uint8_t> file_data; wabt::Result result = ReadFile(module_filename, &file_data); std::unique_ptr<WastLexer> lexer = WastLexer::CreateBufferLexer( @@ -1078,101 +1096,128 @@ wabt::Result CommandRunner::ReadInvalidTextModule(string_view module_filename, return result; } -static wabt::Result ReadModule(string_view module_filename, - Environment* env, - Errors* errors, - DefinedModule** out_module) { - wabt::Result result; +interp::Module::Ptr CommandRunner::ReadModule(string_view module_filename, + Errors* errors) { std::vector<uint8_t> file_data; - *out_module = nullptr; + if (Failed(ReadFile(module_filename, &file_data))) { + return {}; + } - result = ReadFile(module_filename, &file_data); - if (Succeeded(result)) { - const bool kReadDebugNames = true; - const bool kStopOnFirstError = true; - const bool kFailOnCustomSectionError = true; - ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, - kStopOnFirstError, kFailOnCustomSectionError); - result = ReadBinaryInterp(env, file_data.data(), file_data.size(), options, - errors, out_module); - - if (Succeeded(result)) { - if (s_verbose) { - env->DisassembleModule(s_stdout_stream.get(), *out_module); - } - } + const bool kReadDebugNames = true; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = true; + ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, + kStopOnFirstError, kFailOnCustomSectionError); + ModuleDesc module_desc; + if (Failed(ReadBinaryInterp(file_data.data(), file_data.size(), options, + errors, &module_desc))) { + return {}; } - return result; + + if (s_verbose) { + module_desc.istream.Disassemble(s_stdout_stream.get()); + } + + return interp::Module::New(store_, module_desc); } wabt::Result CommandRunner::ReadInvalidModule(int line_number, - string_view module_filename, - Environment* env, - ModuleType module_type, - const char* desc) { + string_view module_filename, + ModuleType module_type, + const char* desc) { std::string header = StringPrintf( "%s:%d: %s passed", source_filename_.c_str(), line_number, desc); switch (module_type) { case ModuleType::Text: { - return ReadInvalidTextModule(module_filename, env, header); + return ReadInvalidTextModule(module_filename, header); } case ModuleType::Binary: { - DefinedModule* module; Errors errors; - wabt::Result result = ReadModule(module_filename, env, &errors, &module); - if (Failed(result)) { + auto module = ReadModule(module_filename, &errors); + if (!module) { FormatErrorsToFile(errors, Location::Type::Binary, {}, stdout, header, PrintHeader::Once); - return result; + return wabt::Result::Error; + } else { + return wabt::Result::Ok; } - return result; } } WABT_UNREACHABLE; } +Extern::Ptr CommandRunner::GetImport(const std::string& module, + const std::string& name) { + auto mod_iter = registry_.find(module); + if (mod_iter != registry_.end()) { + auto extern_iter = mod_iter->second.find(name); + if (extern_iter != mod_iter->second.end()) { + return extern_iter->second; + } + } + return {}; +} + +void CommandRunner::PopulateImports(const interp::Module::Ptr& module, + RefVec* imports) { + for (auto&& import : module->desc().imports) { + auto extern_ = GetImport(import.type.module, import.type.name); + imports->push_back(extern_ ? extern_.ref() : Ref::Null); + } +} + +void CommandRunner::PopulateExports(const Instance::Ptr& instance, + ExportMap* map) { + map->clear(); + interp::Module::Ptr module{store_, instance->module()}; + for (size_t i = 0; i < module->export_types().size(); ++i) { + const ExportType& export_type = module->export_types()[i]; + (*map)[export_type.name] = store_.UnsafeGet<Extern>(instance->exports()[i]); + } +} + wabt::Result CommandRunner::OnModuleCommand(const ModuleCommand* command) { - Environment::MarkPoint mark = env_.Mark(); Errors errors; - wabt::Result result = ReadModule(command->filename, &env_, - &errors, &last_module_); + auto module = ReadModule(command->filename, &errors); FormatErrorsToFile(errors, Location::Type::Binary); - if (Failed(result)) { - env_.ResetToMarkPoint(mark); + if (!module) { PrintError(command->line, "error reading module: \"%s\"", command->filename.c_str()); return wabt::Result::Error; } - ExecResult exec_result = executor_.Initialize(last_module_); - if (!exec_result.ok()) { - env_.ResetToMarkPoint(mark); - WriteResult(s_stdout_stream.get(), "error initializing module", - exec_result.result); + RefVec imports; + PopulateImports(module, &imports); + + Trap::Ptr trap; + auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap); + if (trap) { + assert(!instance); + PrintError(command->line, "error instantiating module: \"%s\"", + trap->message().c_str()); return wabt::Result::Error; } + PopulateExports(instance, &last_instance_); if (!command->name.empty()) { - last_module_->name = command->name; - env_.EmplaceModuleBinding(command->name, - Binding(env_.GetModuleCount() - 1)); + instances_[command->name] = last_instance_; } return wabt::Result::Ok; } wabt::Result CommandRunner::OnActionCommand(const ActionCommand* command) { - ExecResult exec_result = + ActionResult result = RunAction(command->line, &command->action, RunVerbosity::Verbose); - if (!exec_result.ok()) { + if (result.trap) { PrintError(command->line, "unexpected trap: %s", - ResultToString(exec_result.result).c_str()); + result.trap->message().c_str()); return wabt::Result::Error; } @@ -1181,12 +1226,8 @@ wabt::Result CommandRunner::OnActionCommand(const ActionCommand* command) { wabt::Result CommandRunner::OnAssertMalformedCommand( const AssertMalformedCommand* command) { - Environment env(s_features); - InitEnvironment(&env); - - wabt::Result result = - ReadInvalidModule(command->line, command->filename, &env, command->type, - "assert_malformed"); + wabt::Result result = ReadInvalidModule(command->line, command->filename, + command->type, "assert_malformed"); if (Succeeded(result)) { PrintError(command->line, "expected module to be malformed: \"%s\"", command->filename.c_str()); @@ -1197,55 +1238,52 @@ wabt::Result CommandRunner::OnAssertMalformedCommand( } wabt::Result CommandRunner::OnRegisterCommand(const RegisterCommand* command) { - Index module_index; if (!command->name.empty()) { - module_index = env_.FindModuleIndex(command->name); + auto instance_iter = instances_.find(command->name); + if (instance_iter == instances_.end()) { + PrintError(command->line, "unknown module in register"); + return wabt::Result::Error; + } + registry_[command->as] = instance_iter->second; } else { - module_index = env_.GetLastModuleIndex(); - } - - if (module_index == kInvalidIndex) { - PrintError(command->line, "unknown module in register"); - return wabt::Result::Error; + registry_[command->as] = last_instance_; } - env_.EmplaceRegisteredModuleBinding(command->as, Binding(module_index)); return wabt::Result::Ok; } wabt::Result CommandRunner::OnAssertUnlinkableCommand( const AssertUnlinkableCommand* command) { Errors errors; - wabt::Result result = - ReadModule(command->filename, &env_, &errors, &last_module_); - - if (Failed(result)) { - std::string header = StringPrintf("%s:%d: assert_unlinkable passed", - source_filename_.c_str(), command->line); - FormatErrorsToFile(errors, Location::Type::Binary, {}, stdout, header, - PrintHeader::Once); - return wabt::Result::Ok; + auto module = ReadModule(command->filename, &errors); + + if (!module) { + PrintError(command->line, "unable to compile unlinkable module: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; } - ExecResult exec_result = executor_.Initialize(last_module_); - if (exec_result.ok()) { + RefVec imports; + PopulateImports(module, &imports); + + Trap::Ptr trap; + auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap); + if (!trap) { PrintError(command->line, "expected module to be unlinkable: \"%s\"", command->filename.c_str()); return wabt::Result::Error; } - WriteResult(s_stdout_stream.get(), "assert_unlinkable passed", - exec_result.result); + // TODO: Change to one-line error. + PrintError(command->line, "assert_unlinkable passed:\n error: %s", + trap->message().c_str()); return wabt::Result::Ok; } wabt::Result CommandRunner::OnAssertInvalidCommand( const AssertInvalidCommand* command) { - Environment env(s_features); - InitEnvironment(&env); - - wabt::Result result = ReadInvalidModule( - command->line, command->filename, &env, command->type, "assert_invalid"); + wabt::Result result = ReadInvalidModule(command->line, command->filename, + command->type, "assert_invalid"); if (Succeeded(result)) { PrintError(command->line, "expected module to be invalid: \"%s\"", command->filename.c_str()); @@ -1258,94 +1296,127 @@ wabt::Result CommandRunner::OnAssertInvalidCommand( wabt::Result CommandRunner::OnAssertUninstantiableCommand( const AssertUninstantiableCommand* command) { Errors errors; - DefinedModule* module; - wabt::Result result = ReadModule(command->filename, &env_, &errors, &module); - FormatErrorsToFile(errors, Location::Type::Binary); + auto module = ReadModule(command->filename, &errors); - if (Succeeded(result)) { - ExecResult exec_result = executor_.Initialize(module); - if (exec_result.ok()) { - PrintError(command->line, "expected instantiation error: \"%s\"", - command->filename.c_str()); - result = wabt::Result::Error; - } else { - result = wabt::Result::Ok; - } - } else { - PrintError(command->line, "error reading module: \"%s\"", + if (!module) { + PrintError(command->line, "unable to compile uninstantiable module: \"%s\"", command->filename.c_str()); - result = wabt::Result::Error; + return wabt::Result::Error; } - // Don't reset env_ here; if the start function fails, the environment is - // still modified. For example, a table may have been populated with a - // function from this module. - return result; -} + RefVec imports; + PopulateImports(module, &imports); -static bool TypedValuesAreEqual(const TypedValue& tv1, const TypedValue& tv2) { - if (tv1.type != tv2.type) { - return false; + Trap::Ptr trap; + auto instance = Instance::Instantiate(store_, module.ref(), imports, &trap); + if (!trap) { + PrintError(command->line, "expected module to be uninstantiable: \"%s\"", + command->filename.c_str()); + return wabt::Result::Error; } - switch (tv1.type) { + // TODO: print error when assertion passes. +#if 0 + PrintError(command->line, "assert_uninstantiable passed: %s", + trap->message().c_str()); +#endif + return wabt::Result::Ok; +} + +static bool TypedValuesAreEqual(const TypedValue& expected, + const TypedValue& actual) { + assert(expected.type == actual.type || IsReference(expected.type)); + switch (expected.type) { case Type::I32: - return tv1.value.i32 == tv2.value.i32; + return expected.value.Get<u32>() == actual.value.Get<u32>(); + case Type::F32: - return tv1.value.f32_bits == tv2.value.f32_bits; + return Bitcast<u32>(expected.value.Get<f32>()) == + Bitcast<u32>(actual.value.Get<f32>()); + case Type::I64: - return tv1.value.i64 == tv2.value.i64; + return expected.value.Get<u64>() == actual.value.Get<u64>(); + case Type::F64: - return tv1.value.f64_bits == tv2.value.f64_bits; + return Bitcast<u64>(expected.value.Get<f64>()) == + Bitcast<u64>(actual.value.Get<f64>()); + case Type::V128: - return tv1.value.vec128 == tv2.value.vec128; + return expected.value.Get<v128>() == actual.value.Get<v128>(); + case Type::Nullref: - return true; + return actual.value.Get<Ref>() == Ref::Null; + case Type::Funcref: - return tv1.value.ref.index == tv2.value.ref.index; case Type::Hostref: - return tv1.value.ref.index == tv2.value.ref.index; + return expected.value.Get<Ref>() == actual.value.Get<Ref>(); + default: WABT_UNREACHABLE; } } +static bool WABT_VECTORCALL IsCanonicalNan(f32 val) { + const u32 kQuietNan = 0x7fc00000U; + const u32 kQuietNegNan = 0xffc00000U; + u32 bits = Bitcast<u32>(val); + return bits == kQuietNan || bits == kQuietNegNan; +} + +static bool WABT_VECTORCALL IsCanonicalNan(f64 val) { + const u64 kQuietNan = 0x7ff8000000000000ULL; + const u64 kQuietNegNan = 0xfff8000000000000ULL; + u64 bits = Bitcast<u64>(val); + return bits == kQuietNan || bits == kQuietNegNan; +} + +static bool WABT_VECTORCALL IsArithmeticNan(f32 val) { + const u32 kQuietNan = 0x7fc00000U; + return (Bitcast<u32>(val) & kQuietNan) == kQuietNan; +} + +static bool WABT_VECTORCALL IsArithmeticNan(f64 val) { + const u64 kQuietNan = 0x7ff8000000000000ULL; + return (Bitcast<u64>(val) & kQuietNan) == kQuietNan; +} + wabt::Result CommandRunner::OnAssertReturnCommand( const AssertReturnCommand* command) { - ExecResult exec_result = + ActionResult action_result = RunAction(command->line, &command->action, RunVerbosity::Quiet); - if (!exec_result.ok()) { + if (action_result.trap) { PrintError(command->line, "unexpected trap: %s", - ResultToString(exec_result.result).c_str()); + action_result.trap->message().c_str()); return wabt::Result::Error; } - if (exec_result.values.size() != command->expected.size()) { + if (action_result.values.size() != command->expected.size()) { PrintError(command->line, "result length mismatch in assert_return: expected %" PRIzd ", got %" PRIzd, - command->expected.size(), exec_result.values.size()); + command->expected.size(), action_result.values.size()); return wabt::Result::Error; } wabt::Result result = wabt::Result::Ok; - for (size_t i = 0; i < exec_result.values.size(); ++i) { + for (size_t i = 0; i < action_result.values.size(); ++i) { const ExpectedValue& expected = command->expected[i]; - const TypedValue& actual = exec_result.values[i]; + TypedValue actual{action_result.types[i], action_result.values[i]}; + if (expected.is_expected_nan) { bool is_nan; if (expected.expectedNan == ExpectedNan::Arithmetic) { if (expected.value.type == Type::F64) { - is_nan = IsArithmeticNan(actual.value.f64_bits); + is_nan = IsArithmeticNan(actual.value.Get<f64>()); } else { - is_nan = IsArithmeticNan(actual.value.f32_bits); + is_nan = IsArithmeticNan(actual.value.Get<f32>()); } } else if (expected.expectedNan == ExpectedNan::Canonical) { if (expected.value.type == Type::F64) { - is_nan = IsCanonicalNan(actual.value.f64_bits); + is_nan = IsCanonicalNan(actual.value.Get<f64>()); } else { - is_nan = IsCanonicalNan(actual.value.f32_bits); + is_nan = IsCanonicalNan(actual.value.Get<f32>()); } } else { WABT_UNREACHABLE; @@ -1356,7 +1427,7 @@ wabt::Result CommandRunner::OnAssertReturnCommand( result = wabt::Result::Error; } } else if (expected.value.type == Type::Funcref) { - if (actual.type != Type::Funcref) { + if (!store_.HasValueType(actual.value.Get<Ref>(), Type::Funcref)) { PrintError(command->line, "mismatch in result %" PRIzd " of assert_return: expected funcref, got %s", @@ -1379,28 +1450,32 @@ wabt::Result CommandRunner::OnAssertReturnCommand( wabt::Result CommandRunner::OnAssertTrapCommand( const AssertTrapCommand* command) { - ExecResult exec_result = + ActionResult result = RunAction(command->line, &command->action, RunVerbosity::Quiet); - if (exec_result.ok()) { + if (!result.trap) { PrintError(command->line, "expected trap: \"%s\"", command->text.c_str()); return wabt::Result::Error; } PrintError(command->line, "assert_trap passed: %s", - ResultToString(exec_result.result).c_str()); + result.trap->message().c_str()); return wabt::Result::Ok; } wabt::Result CommandRunner::OnAssertExhaustionCommand( const AssertExhaustionCommand* command) { - ExecResult exec_result = + ActionResult result = RunAction(command->line, &command->action, RunVerbosity::Quiet); - if (exec_result.result.type != interp::ResultType::TrapCallStackExhausted && - exec_result.result.type != interp::ResultType::TrapValueStackExhausted) { - PrintError(command->line, "expected call stack exhaustion"); + if (!result.trap || result.trap->message() != "call stack exhausted") { + PrintError(command->line, "expected trap: \"%s\"", command->text.c_str()); return wabt::Result::Error; } + // TODO: print message when assertion passes. +#if 0 + PrintError(command->line, "assert_exhaustion passed: %s", + result.trap->message().c_str()); +#endif return wabt::Result::Ok; } diff --git a/src/tools/wasm-interp.cc b/src/tools/wasm-interp.cc index 6126331b..733c54cd 100644 --- a/src/tools/wasm-interp.cc +++ b/src/tools/wasm-interp.cc @@ -16,7 +16,6 @@ #include <algorithm> #include <cassert> -#include <cinttypes> #include <cstdio> #include <cstdlib> #include <memory> @@ -24,18 +23,13 @@ #include <vector> #include "src/binary-reader.h" -#include "src/cast.h" #include "src/error-formatter.h" #include "src/feature.h" #include "src/interp/binary-reader-interp.h" +#include "src/interp/interp-util.h" #include "src/interp/interp.h" -#include "src/literal.h" #include "src/option-parser.h" -#include "src/resolve-names.h" #include "src/stream.h" -#include "src/validator.h" -#include "src/wast-lexer.h" -#include "src/wast-parser.h" using namespace wabt; using namespace wabt::interp; @@ -52,10 +46,7 @@ static Features s_features; static std::unique_ptr<FileStream> s_log_stream; static std::unique_ptr<FileStream> s_stdout_stream; -enum class RunVerbosity { - Quiet = 0, - Verbose = 1, -}; +static Store s_store; static const char s_description[] = R"( read a file in the wasm binary format, and run in it a stack-based @@ -117,112 +108,104 @@ static void ParseOptions(int argc, char** argv) { parser.Parse(argc, argv); } -static void RunAllExports(interp::Module* module, - Executor* executor, - RunVerbosity verbose) { - TypedValues args; - TypedValues results; - for (const interp::Export& export_ : module->exports) { - if (export_.kind != ExternalKind::Func) { - continue; - } - ExecResult exec_result = executor->RunExport(&export_, args); - if (verbose == RunVerbosity::Verbose) { - WriteCall(s_stdout_stream.get(), string_view(), export_.name, args, - exec_result.values, exec_result.result); - } - } -} +Result RunAllExports(const Instance::Ptr& instance, Errors* errors) { + Result result = Result::Ok; -static wabt::Result ReadModule(const char* module_filename, - Environment* env, - Errors* errors, - DefinedModule** out_module) { - wabt::Result result; - std::vector<uint8_t> file_data; + auto module = s_store.UnsafeGet<Module>(instance->module()); + auto&& module_desc = module->desc(); - *out_module = nullptr; - - result = ReadFile(module_filename, &file_data); - if (Succeeded(result)) { - const bool kReadDebugNames = true; - const bool kStopOnFirstError = true; - const bool kFailOnCustomSectionError = true; - ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, - kStopOnFirstError, kFailOnCustomSectionError); - result = ReadBinaryInterp(env, file_data.data(), file_data.size(), options, - errors, out_module); - - if (Succeeded(result)) { - if (s_verbose) { - env->DisassembleModule(s_stdout_stream.get(), *out_module); + for (auto&& export_ : module_desc.exports) { + if (export_.type.type->kind != ExternalKind::Func) { + continue; + } + auto* func_type = cast<FuncType>(export_.type.type.get()); + if (func_type->params.empty()) { + if (s_trace_stream) { + s_trace_stream->Writef(">>> running export \"%s\":\n", + export_.type.name.c_str()); } + auto func = s_store.UnsafeGet<Func>(instance->funcs()[export_.index]); + Values params; + Values results; + Trap::Ptr trap; + result |= func->Call(s_store, params, results, &trap, s_trace_stream); + WriteCall(s_stdout_stream.get(), export_.type.name, *func_type, params, + results, trap); } } + return result; } -static interp::Result PrintCallback(const HostFunc* func, - const interp::FuncSignature* sig, - const TypedValues& args, - TypedValues& results) { - printf("called host "); - WriteCall(s_stdout_stream.get(), func->module_name, func->field_name, args, - results, interp::ResultType::Ok); - return interp::ResultType::Ok; -} +Result ReadAndInstantiateModule(const char* module_filename, + Errors* errors, + Instance::Ptr* out_instance) { + auto* stream = s_stdout_stream.get(); + std::vector<uint8_t> file_data; + CHECK_RESULT(ReadFile(module_filename, &file_data)); + + ModuleDesc module_desc; + const bool kReadDebugNames = true; + const bool kStopOnFirstError = true; + const bool kFailOnCustomSectionError = true; + ReadBinaryOptions options(s_features, s_log_stream.get(), kReadDebugNames, + kStopOnFirstError, kFailOnCustomSectionError); + CHECK_RESULT(ReadBinaryInterp(file_data.data(), file_data.size(), options, + errors, &module_desc)); + + if (s_verbose) { + module_desc.istream.Disassemble(stream); + } -static void InitEnvironment(Environment* env) { - if (s_host_print) { - auto* host_module = env->AppendHostModule("host"); - host_module->on_unknown_func_export = - [](Environment* env, HostModule* host_module, string_view name, - Index sig_index) -> Index { - if (name != "print") { - return kInvalidIndex; - } + auto module = Module::New(s_store, module_desc); + + RefVec imports; + for (auto&& import : module_desc.imports) { + if (import.type.type->kind == ExternKind::Func && + ((s_host_print && import.type.module == "host" && + import.type.name == "print") || + s_dummy_import_func)) { + auto func_type = *cast<FuncType>(import.type.type.get()); + auto import_name = StringPrintf("%s.%s", import.type.module.c_str(), + import.type.name.c_str()); + + auto host_func = + HostFunc::New(s_store, func_type, + [=](const Values& params, Values& results, + Trap::Ptr* trap) -> Result { + printf("called host "); + WriteCall(stream, import_name, func_type, params, + results, *trap); + return Result::Ok; + }); + imports.push_back(host_func.ref()); + continue; + } - return host_module->AppendFuncExport(name, sig_index, PrintCallback) - .second; - }; + // By default, just push an null reference. This won't resolve, and + // instantiation will fail. + imports.push_back(Ref::Null); } - if (s_dummy_import_func) { - env->on_unknown_module = [](Environment* env, string_view name) { - auto* host_module = env->AppendHostModule(name); - host_module->on_unknown_func_export = - [](Environment* env, HostModule* host_module, string_view name, - Index sig_index) -> Index { - return host_module->AppendFuncExport(name, sig_index, PrintCallback) - .second; - }; - return true; - }; + RefPtr<Trap> trap; + *out_instance = Instance::Instantiate(s_store, module.ref(), imports, &trap); + if (!*out_instance) { + // TODO: change to "initializing" + WriteTrap(stream, "error initialiazing module", trap); + return Result::Error; } -} -static wabt::Result ReadAndRunModule(const char* module_filename) { - wabt::Result result; - Environment env(s_features); - InitEnvironment(&env); + return Result::Ok; +} +static Result ReadAndRunModule(const char* module_filename) { Errors errors; - DefinedModule* module = nullptr; - result = ReadModule(module_filename, &env, &errors, &module); - FormatErrorsToFile(errors, Location::Type::Binary); - if (Succeeded(result)) { - Executor executor(&env, s_trace_stream, s_thread_options); - ExecResult exec_result = executor.Initialize(module); - if (exec_result.ok()) { - if (s_run_all_exports) { - RunAllExports(module, &executor, RunVerbosity::Verbose); - } - } else { - WriteResult(s_stdout_stream.get(), "error initialiazing module", - exec_result.result); - return wabt::Result::Error; - } + Instance::Ptr instance; + Result result = ReadAndInstantiateModule(module_filename, &errors, &instance); + if (Succeeded(result) && s_run_all_exports) { + RunAllExports(instance, &errors); } + FormatErrorsToFile(errors, Location::Type::Binary); return result; } |