summaryrefslogtreecommitdiff
path: root/include/wabt/interp/interp.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/wabt/interp/interp.h')
-rw-r--r--include/wabt/interp/interp.h1275
1 files changed, 1275 insertions, 0 deletions
diff --git a/include/wabt/interp/interp.h b/include/wabt/interp/interp.h
new file mode 100644
index 00000000..418d00e0
--- /dev/null
+++ b/include/wabt/interp/interp.h
@@ -0,0 +1,1275 @@
+/*
+ * 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_H_
+#define WABT_INTERP_H_
+
+#include <cstdint>
+#include <functional>
+#include <memory>
+#include <set>
+#include <string>
+#include <string_view>
+#include <type_traits>
+#include <vector>
+
+#include "wabt/cast.h"
+#include "wabt/common.h"
+#include "wabt/feature.h"
+#include "wabt/opcode.h"
+#include "wabt/result.h"
+
+#include "wabt/interp/istream.h"
+
+namespace wabt {
+namespace interp {
+
+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 TagAttr { Exception };
+using SegmentMode = SegmentKind;
+enum class ElemKind { RefNull, RefFunc };
+
+enum class ObjectKind {
+ Null,
+ Foreign,
+ Trap,
+ Exception,
+ DefinedFunc,
+ HostFunc,
+ Table,
+ Memory,
+ Global,
+ Tag,
+ Module,
+ Instance,
+
+ First = Null,
+ Last = Instance,
+};
+
+static const int kCommandTypeCount = WABT_ENUM_COUNT(ObjectKind);
+
+const char* GetName(Mutability);
+const std::string GetName(ValueType);
+const char* GetName(ExternKind);
+const char* GetName(ObjectKind);
+
+struct Ref {
+ 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];
+
+ inline T& operator[](u8 idx) {
+#if WABT_BIG_ENDIAN
+ idx = (~idx) & (L - 1);
+#endif
+ return v[idx];
+ }
+ inline T operator[](u8 idx) const {
+#if WABT_BIG_ENDIAN
+ idx = (~idx) & (L - 1);
+#endif
+ return v[idx];
+ }
+};
+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 TableType : ExternType {
+ static const ExternKind skind = ExternKind::Table;
+ static bool classof(const ExternType* type);
+
+ explicit TableType(ValueType, Limits);
+
+ std::unique_ptr<ExternType> Clone() const override;
+
+ friend Result Match(const TableType& expected,
+ const TableType& actual,
+ std::string* out_msg);
+
+ ValueType element;
+ Limits limits;
+};
+
+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 limits;
+};
+
+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;
+
+ friend Result Match(const GlobalType& expected,
+ const GlobalType& actual,
+ std::string* out_msg);
+
+ ValueType type;
+ Mutability mut;
+};
+
+struct TagType : ExternType {
+ static const ExternKind skind = ExternKind::Tag;
+ static bool classof(const ExternType* type);
+
+ explicit TagType(TagAttr, const ValueTypes&);
+
+ std::unique_ptr<ExternType> Clone() const override;
+
+ friend Result Match(const TagType& expected,
+ const TagType& actual,
+ std::string* out_msg);
+
+ TagAttr attr;
+ ValueTypes signature;
+};
+
+struct ImportType {
+ explicit ImportType(std::string module,
+ std::string name,
+ std::unique_ptr<ExternType>);
+ ImportType(const ImportType&);
+ ImportType& operator=(const ImportType&);
+
+ std::string module;
+ std::string name;
+ std::unique_ptr<ExternType> type;
+};
+
+struct ExportType {
+ explicit ExportType(std::string name, std::unique_ptr<ExternType>);
+ ExportType(const ExportType&);
+ ExportType& operator=(const ExportType&);
+
+ std::string name;
+ std::unique_ptr<ExternType> type;
+};
+
+//// Structure ////
+
+struct ImportDesc {
+ ImportType type;
+};
+
+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;
+};
+
+// Metadata for representing exception handlers associated with a function's
+// code. This is needed to look up exceptions from call frames from interpreter
+// instructions.
+struct CatchDesc {
+ Index tag_index;
+ u32 offset;
+};
+
+// Handlers for a catch-less `try` or `try-catch` block are included in the
+// Catch kind. `try-delegate` instructions create a Delegate handler.
+enum class HandlerKind { Catch, Delegate };
+
+struct HandlerDesc {
+ HandlerKind kind;
+ u32 try_start_offset;
+ u32 try_end_offset;
+ std::vector<CatchDesc> catches;
+ union {
+ u32 catch_all_offset;
+ u32 delegate_handler_index;
+ };
+ // Local stack heights at the handler site that need to be restored.
+ u32 values;
+ u32 exceptions;
+};
+
+struct FuncDesc {
+ // Includes params.
+ ValueType GetLocalType(Index) const;
+
+ FuncType type;
+ std::vector<LocalDesc> locals;
+ u32 code_offset; // Istream offset.
+ std::vector<HandlerDesc> handlers;
+};
+
+struct TableDesc {
+ TableType type;
+};
+
+struct MemoryDesc {
+ MemoryType type;
+};
+
+struct GlobalDesc {
+ GlobalType type;
+ FuncDesc init_func;
+};
+
+struct TagDesc {
+ TagType type;
+};
+
+struct ExportDesc {
+ ExportType type;
+ Index index;
+};
+
+struct StartDesc {
+ Index func_index;
+};
+
+struct DataDesc {
+ Buffer data;
+ SegmentMode mode;
+ Index memory_index;
+ FuncDesc init_func;
+};
+
+struct ElemExpr {
+ ElemKind kind;
+ Index index;
+};
+
+struct ElemDesc {
+ std::vector<ElemExpr> elements;
+ ValueType type;
+ SegmentMode mode;
+ Index table_index;
+ FuncDesc init_func;
+};
+
+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<TagDesc> tags;
+ 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 exceptions,
+ u32 offset,
+ Instance*,
+ Module*);
+
+ void Mark(Store&);
+
+ Ref func;
+ u32 values; // Height of the value stack at this activation.
+ u32 exceptions; // Height of the exception 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;
+};
+
+template <typename T>
+class FreeList {
+ public:
+ using Index = size_t;
+
+ ~FreeList();
+
+ template <typename... Args>
+ Index New(Args&&...);
+ void Delete(Index);
+
+ bool IsUsed(Index) const;
+
+ const T& Get(Index) const;
+ T& Get(Index);
+
+ Index size() const; // 1 greater than the maximum index.
+ Index count() const; // The number of used elements.
+
+ private:
+ // As for Refs, the free bit is 0x80..0. This bit is never
+ // set for valid Refs, since it would mean more objects
+ // are allocated than the total amount of memory.
+ static const Index refFreeBit = (SIZE_MAX >> 1) + 1;
+
+ // As for Objects, the free bit is 0x1. This bit is never
+ // set for valid Objects, since pointers are aligned to at
+ // least four bytes.
+ static const Index ptrFreeBit = 1;
+ static const int ptrFreeShift = 1;
+
+ std::vector<T> list_;
+ // If free_head_ is zero, there is no free slots in list_,
+ // otherwise free_head_ - 1 represents the first free slot.
+ Index free_head_ = 0;
+ Index free_items_ = 0;
+};
+
+class Store {
+ public:
+ using ObjectList = FreeList<Object*>;
+ using RootList = FreeList<Ref>;
+
+ explicit Store(const Features& = Features{});
+
+ Store(const Store&) = delete;
+ Store& operator=(const Store&) = delete;
+ Store& operator=(const Store&&) = delete;
+
+ bool IsValid(Ref) const;
+ bool HasValueType(Ref, ValueType) const;
+ template <typename T>
+ bool Is(Ref) const;
+
+ 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&);
+
+ ObjectList::Index object_count() const;
+
+ const Features& features() const;
+ void setFeatures(const Features& features) { features_ = features; }
+
+ std::set<Thread*>& threads();
+
+ private:
+ template <typename T>
+ friend class RefPtr;
+
+ struct GCContext {
+ int call_depth = 0;
+ std::vector<bool> marks;
+ std::vector<size_t> untraced_objects;
+ };
+
+ static const int max_call_depth = 10;
+
+ Features features_;
+ GCContext gc_context_;
+ // This set contains the currently active Thread objects.
+ std::set<Thread*> threads_;
+ ObjectList objects_;
+ RootList roots_;
+};
+
+template <typename T>
+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_;
+};
+
+struct Value {
+ 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);
+
+ private:
+ union {
+ u32 i32_;
+ u64 i64_;
+ f32 f32_;
+ f64 f64_;
+ v128 v128_;
+ Ref ref_;
+ };
+
+ public:
+#ifndef NDEBUG
+ Value() : v128_(0, 0, 0, 0), type(ValueType::Any) {}
+ void SetType(ValueType t) { type = t; }
+ void CheckType(ValueType t) const {
+ // Sadly we must allow Any here, since locals may be uninitialized.
+ // Alternatively we could modify InterpAlloca to set the type.
+ assert(t == type || type == ValueType::Any);
+ }
+ ValueType type;
+#else
+ Value() : v128_(0, 0, 0, 0) {}
+ void SetType(ValueType) {}
+ void CheckType(ValueType) const {}
+#endif
+};
+using Values = std::vector<Value>;
+
+struct TypedValue {
+ ValueType type;
+ Value value;
+};
+using TypedValues = std::vector<TypedValue>;
+
+using Finalizer = std::function<void(Object*)>;
+
+class Object {
+ public:
+ static bool classof(const Object* obj);
+ static const char* GetTypeName() { return "Object"; }
+ using Ptr = RefPtr<Object>;
+
+ Object(const Object&) = delete;
+ Object& operator=(const Object&) = delete;
+
+ virtual ~Object();
+
+ 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;
+};
+
+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*);
+
+ void* ptr();
+
+ private:
+ friend Store;
+ explicit Foreign(Store&, void*);
+ void Mark(Store&) override;
+
+ void* ptr_;
+};
+
+class Trap : public Object {
+ public:
+ static const ObjectKind skind = ObjectKind::Trap;
+ static bool classof(const Object* obj);
+ using Ptr = RefPtr<Trap>;
+
+ static Trap::Ptr New(Store&,
+ const std::string& msg,
+ const std::vector<Frame>& trace = std::vector<Frame>());
+
+ 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_;
+};
+
+class Exception : public Object {
+ public:
+ static bool classof(const Object* obj);
+ static const ObjectKind skind = ObjectKind::Exception;
+ static const char* GetTypeName() { return "Exception"; }
+ using Ptr = RefPtr<Exception>;
+
+ static Exception::Ptr New(Store&, Ref tag, Values& args);
+
+ Ref tag() const;
+ Values& args();
+
+ private:
+ friend Store;
+ explicit Exception(Store&, Ref, Values&);
+ void Mark(Store&) override;
+
+ Ref tag_;
+ Values args_;
+};
+
+class Extern : public Object {
+ public:
+ static bool classof(const Object* obj);
+ static const char* GetTypeName() { return "Foreign"; }
+ using Ptr = RefPtr<Extern>;
+
+ virtual Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) = 0;
+ virtual const ExternType& extern_type() = 0;
+
+ protected:
+ friend Store;
+ explicit Extern(ObjectKind);
+
+ template <typename T>
+ Result MatchImpl(Store&,
+ const ImportType&,
+ const T& actual,
+ Trap::Ptr* out_trap);
+};
+
+class Func : public Extern {
+ public:
+ static bool classof(const Object* obj);
+ using Ptr = RefPtr<Func>;
+
+ 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 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;
+
+ 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_;
+};
+
+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>;
+
+ using Callback = std::function<Result(Thread& thread,
+ const Values& params,
+ Values& results,
+ Trap::Ptr* out_trap)>;
+
+ static HostFunc::Ptr New(Store&, FuncType, Callback);
+
+ Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) override;
+
+ protected:
+ 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_;
+};
+
+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;
+
+ private:
+ friend Store;
+ explicit Table(Store&, TableType);
+ void Mark(Store&) override;
+
+ TableType type_;
+ RefVec elements_;
+};
+
+class Memory : public Extern {
+ public:
+ static bool classof(const Object* obj);
+ static const ObjectKind skind = ObjectKind::Memory;
+ static const char* GetTypeName() { return "Memory"; }
+ using Ptr = RefPtr<Memory>;
+
+ static Memory::Ptr New(Store&, MemoryType);
+
+ Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) override;
+
+ bool IsValidAccess(u64 offset, u64 addend, u64 size) const;
+ bool IsValidAtomicAccess(u64 offset, u64 addend, u64 size) const;
+
+ template <typename T>
+ Result Load(u64 offset, u64 addend, T* out) const;
+ template <typename T>
+ Result WABT_VECTORCALL Store(u64 offset, u64 addend, T);
+ Result Grow(u64 pages);
+ Result Fill(u64 offset, u8 value, u64 size);
+ Result Init(u64 dst_offset, const DataSegment&, u64 src_offset, u64 size);
+ static Result Copy(Memory& dst,
+ u64 dst_offset,
+ const Memory& src,
+ u64 src_offset,
+ u64 size);
+
+ // Fake atomics; just checks alignment.
+ template <typename T>
+ Result AtomicLoad(u64 offset, u64 addend, T* out) const;
+ template <typename T>
+ Result AtomicStore(u64 offset, u64 addend, T);
+ template <typename T, typename F>
+ Result AtomicRmw(u64 offset, u64 addend, T, F&& func, T* out);
+ template <typename T>
+ Result AtomicRmwCmpxchg(u64 offset, u64 addend, T expect, T replace, T* out);
+
+ u64 ByteSize() const;
+ u64 PageSize() const;
+
+ // Unsafe API.
+ template <typename T>
+ T WABT_VECTORCALL UnsafeLoad(u64 offset, u64 addend) const;
+ u8* UnsafeData();
+
+ const ExternType& extern_type() override;
+ const MemoryType& type() const;
+
+ private:
+ friend class Store;
+ explicit Memory(class Store&, MemoryType);
+ void Mark(class Store&) override;
+
+ MemoryType type_;
+ Buffer data_;
+ u64 pages_;
+};
+
+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>;
+
+ static Global::Ptr New(Store&, GlobalType, Value);
+
+ Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) override;
+
+ Value Get() const;
+ template <typename T>
+ Result Get(T* out) const;
+ template <typename T>
+ Result WABT_VECTORCALL Set(T);
+ Result Set(Store&, Ref);
+
+ template <typename T>
+ T WABT_VECTORCALL UnsafeGet() const;
+ void UnsafeSet(Value);
+
+ const ExternType& extern_type() override;
+ const GlobalType& type() const;
+
+ private:
+ friend Store;
+ explicit Global(Store&, GlobalType, Value);
+ void Mark(Store&) override;
+
+ GlobalType type_;
+ Value value_;
+};
+
+class Tag : public Extern {
+ public:
+ static bool classof(const Object* obj);
+ static const ObjectKind skind = ObjectKind::Tag;
+ static const char* GetTypeName() { return "Tag"; }
+ using Ptr = RefPtr<Tag>;
+
+ static Tag::Ptr New(Store&, TagType);
+
+ Result Match(Store&, const ImportType&, Trap::Ptr* out_trap) override;
+
+ const ExternType& extern_type() override;
+ const TagType& type() const;
+
+ private:
+ friend Store;
+ explicit Tag(Store&, TagType);
+ void Mark(Store&) override;
+
+ TagType type_;
+};
+
+class ElemSegment {
+ public:
+ explicit ElemSegment(const ElemDesc*, RefPtr<Instance>&);
+
+ bool IsValidRange(u32 offset, u32 size) const;
+ void Drop();
+
+ const ElemDesc& desc() const;
+ const RefVec& elements() const;
+ u32 size() const;
+
+ private:
+ friend Instance;
+ void Mark(Store&);
+
+ const ElemDesc* desc_; // Borrowed from the Module.
+ RefVec elements_;
+};
+
+class DataSegment {
+ public:
+ explicit DataSegment(const DataDesc*);
+
+ bool IsValidRange(u64 offset, u64 size) const;
+ void Drop();
+
+ const DataDesc& desc() const;
+ u64 size() const;
+
+ private:
+ const DataDesc* desc_; // Borrowed from the Module.
+ u64 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;
+
+ 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_;
+};
+
+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& tags() 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:
+ friend Store;
+ friend ElemSegment;
+ friend DataSegment;
+ explicit Instance(Store&, Ref module);
+ void Mark(Store&) override;
+
+ Result CallInitFunc(Store&,
+ const Ref func_ref,
+ Value* result,
+ Trap::Ptr* out_trap);
+
+ Ref module_;
+ RefVec imports_;
+ RefVec funcs_;
+ RefVec tables_;
+ RefVec memories_;
+ RefVec globals_;
+ RefVec tags_;
+ RefVec exports_;
+ std::vector<ElemSegment> elems_;
+ std::vector<DataSegment> datas_;
+};
+
+enum class RunResult {
+ Ok,
+ Return,
+ Trap,
+ Exception,
+};
+
+class Thread {
+ public:
+ struct Options {
+ static const u32 kDefaultValueStackSize = 64 * 1024 / sizeof(Value);
+ static const u32 kDefaultCallStackSize = 64 * 1024 / sizeof(Frame);
+
+ u32 value_stack_size = kDefaultValueStackSize;
+ u32 call_stack_size = kDefaultCallStackSize;
+ Stream* trace_stream = nullptr;
+ };
+
+ Thread(Store& store, Stream* trace_stream = nullptr);
+ ~Thread();
+
+ RunResult Run(Trap::Ptr* out_trap);
+ RunResult Run(int num_instructions, Trap::Ptr* out_trap);
+ RunResult Step(Trap::Ptr* out_trap);
+
+ Store& store();
+ void Mark();
+
+ Instance* GetCallerInstance();
+
+ private:
+ friend Store;
+ friend DefinedFunc;
+
+ struct TraceSource;
+
+ 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);
+
+ template <typename T>
+ T WABT_VECTORCALL Pop();
+ Value Pop();
+ u64 PopPtr(const Memory::Ptr& memory);
+
+ template <typename T>
+ 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>
+ 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>);
+ // Like DoSimdUnop but zeroes top half.
+ template <typename R, typename T>
+ RunResult DoSimdUnopZero(UnopFunc<R, T>);
+ template <typename R, typename T>
+ RunResult DoSimdBinop(BinopFunc<R, T>);
+ RunResult DoSimdBitSelect();
+ template <typename S, u8 count>
+ RunResult DoSimdIsTrue();
+ template <typename S>
+ RunResult DoSimdBitmask();
+ template <typename R, typename T>
+ RunResult DoSimdShift(BinopFunc<R, T>);
+ template <typename S>
+ RunResult DoSimdLoadSplat(Instr, Trap::Ptr* out_trap);
+ template <typename S>
+ RunResult DoSimdLoadLane(Instr, Trap::Ptr* out_trap);
+ template <typename S>
+ RunResult DoSimdStoreLane(Instr, Trap::Ptr* out_trap);
+ template <typename S, typename T>
+ RunResult DoSimdLoadZero(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 DoSimdConvert();
+ template <typename S, typename T>
+ RunResult DoSimdDot();
+ template <typename S, typename T>
+ RunResult DoSimdLoadExtend(Instr, Trap::Ptr* out_trap);
+ template <typename S, typename T>
+ RunResult DoSimdExtaddPairwise();
+ template <typename S, typename T, bool low>
+ RunResult DoSimdExtmul();
+
+ 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 DoThrow(Exception::Ptr exn_ref);
+
+ RunResult StepInternal(Trap::Ptr* out_trap);
+
+ std::vector<Frame> frames_;
+ std::vector<Value> values_;
+ std::vector<u32> refs_; // Index into values_.
+
+ // Exception handling requires tracking a separate stack of caught
+ // exceptions for catch blocks.
+ RefVec exceptions_;
+
+ // Cached for convenience.
+ Store& store_;
+ Instance* inst_ = nullptr;
+ Module* mod_ = nullptr;
+
+ // Tracing.
+ Stream* trace_stream_;
+ std::unique_ptr<TraceSource> trace_source_;
+};
+
+struct Thread::TraceSource : Istream::TraceSource {
+ public:
+ explicit TraceSource(Thread*);
+ std::string Header(Istream::Offset) override;
+ std::string Pick(Index, Instr) override;
+
+ private:
+ ValueType GetLocalType(Index);
+ ValueType GetGlobalType(Index);
+ ValueType GetTableElementType(Index);
+
+ Thread* thread_;
+};
+
+} // namespace interp
+} // namespace wabt
+
+#include "wabt/interp/interp-inl.h"
+
+#endif // WABT_INTERP_H_