summaryrefslogtreecommitdiff
path: root/src/interp.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/interp.h')
-rw-r--r--src/interp.h604
1 files changed, 604 insertions, 0 deletions
diff --git a/src/interp.h b/src/interp.h
new file mode 100644
index 00000000..888609c1
--- /dev/null
+++ b/src/interp.h
@@ -0,0 +1,604 @@
+/*
+ * Copyright 2016 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 <stdint.h>
+
+#include <functional>
+#include <memory>
+#include <vector>
+
+#include "src/binding-hash.h"
+#include "src/common.h"
+#include "src/opcode.h"
+#include "src/stream.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") \
+ /* 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")
+
+enum class Result {
+#define V(Name, str) Name,
+ FOREACH_INTERP_RESULT(V)
+#undef V
+};
+
+typedef uint32_t IstreamOffset;
+static const IstreamOffset kInvalidIstreamOffset = ~0;
+
+// A table entry has the following packed layout:
+//
+// struct {
+// IstreamOffset offset;
+// uint32_t drop_count;
+// uint8_t keep_count;
+// };
+#define WABT_TABLE_ENTRY_SIZE \
+ (sizeof(IstreamOffset) + sizeof(uint32_t) + sizeof(uint8_t))
+#define WABT_TABLE_ENTRY_OFFSET_OFFSET 0
+#define WABT_TABLE_ENTRY_DROP_OFFSET sizeof(uint32_t)
+#define WABT_TABLE_ENTRY_KEEP_OFFSET (sizeof(IstreamOffset) + sizeof(uint32_t))
+
+struct FuncSignature {
+ FuncSignature() = default;
+ FuncSignature(Index param_count,
+ Type* param_types,
+ Index result_count,
+ Type* result_types);
+
+ std::vector<Type> param_types;
+ std::vector<Type> result_types;
+};
+
+struct Table {
+ explicit Table(const Limits& limits)
+ : limits(limits), func_indexes(limits.initial, kInvalidIndex) {}
+
+ Limits limits;
+ std::vector<Index> func_indexes;
+};
+
+struct Memory {
+ Memory() = default;
+ explicit Memory(const Limits& limits)
+ : page_limits(limits), data(limits.initial * WABT_PAGE_SIZE) {}
+
+ Limits page_limits;
+ std::vector<char> data;
+};
+
+// ValueTypeRep converts from one type to its representation on the
+// stack. For example, float -> uint32_t. See Value below.
+template <typename T>
+struct ValueTypeRepT;
+
+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 <typename T>
+using ValueTypeRep = typename ValueTypeRepT<T>::type;
+
+union Value {
+ uint32_t i32;
+ uint64_t i64;
+ ValueTypeRep<float> f32_bits;
+ ValueTypeRep<double> f64_bits;
+};
+
+struct TypedValue {
+ TypedValue() {}
+ explicit TypedValue(Type type) : type(type) {}
+ TypedValue(Type type, const Value& value) : type(type), value(value) {}
+
+ Type type;
+ Value value;
+};
+
+typedef std::vector<TypedValue> TypedValues;
+
+struct Global {
+ Global() : mutable_(false), import_index(kInvalidIndex) {}
+ Global(const TypedValue& typed_value, bool mutable_)
+ : typed_value(typed_value), mutable_(mutable_) {}
+
+ TypedValue typed_value;
+ bool mutable_;
+ Index import_index; /* or INVALID_INDEX if not imported */
+};
+
+struct Import {
+ explicit Import(ExternalKind kind) : kind(kind) {}
+ 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()) {}
+
+ ExternalKind kind;
+ std::string module_name;
+ std::string field_name;
+};
+
+struct FuncImport : Import {
+ FuncImport() : Import(ExternalKind::Func) {}
+ FuncImport(string_view module_name, string_view field_name)
+ : Import(ExternalKind::Func, module_name, field_name) {}
+
+ Index sig_index = kInvalidIndex;
+};
+
+struct TableImport : Import {
+ TableImport() : Import(ExternalKind::Table) {}
+ TableImport(string_view module_name, string_view field_name)
+ : Import(ExternalKind::Table, module_name, field_name) {}
+
+ Limits limits;
+};
+
+struct MemoryImport : Import {
+ MemoryImport() : Import(ExternalKind::Memory) {}
+ MemoryImport(string_view module_name, string_view field_name)
+ : Import(ExternalKind::Memory, module_name, field_name) {}
+
+ Limits limits;
+};
+
+struct GlobalImport : Import {
+ GlobalImport() : Import(ExternalKind::Global) {}
+ GlobalImport(string_view module_name, string_view field_name)
+ : Import(ExternalKind::Global, module_name, field_name) {}
+
+ Type type = Type::Void;
+ bool mutable_ = false;
+};
+
+struct ExceptImport : Import {
+ ExceptImport() : Import(ExternalKind::Except) {}
+ ExceptImport(string_view module_name, string_view field_name)
+ : Import(ExternalKind::Except, module_name, field_name) {}
+};
+
+struct Func;
+
+typedef Result (*HostFuncCallback)(const struct HostFunc* func,
+ const FuncSignature* sig,
+ Index num_args,
+ TypedValue* args,
+ Index num_results,
+ TypedValue* out_results,
+ void* user_data);
+
+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() {}
+
+ Index sig_index;
+ bool is_host;
+};
+
+struct DefinedFunc : Func {
+ DefinedFunc(Index sig_index)
+ : Func(sig_index, false),
+ offset(kInvalidIstreamOffset),
+ local_decl_count(0),
+ local_count(0) {}
+
+ static bool classof(const Func* func) { return !func->is_host; }
+
+ IstreamOffset offset;
+ Index local_decl_count;
+ Index local_count;
+ std::vector<Type> param_and_local_types;
+};
+
+struct HostFunc : Func {
+ HostFunc(string_view module_name, string_view field_name, Index sig_index)
+ : Func(sig_index, true),
+ module_name(module_name.to_string()),
+ field_name(field_name.to_string()) {}
+
+ static bool classof(const Func* func) { return func->is_host; }
+
+ std::string module_name;
+ std::string field_name;
+ HostFuncCallback callback;
+ void* user_data;
+};
+
+struct Export {
+ Export(string_view name, ExternalKind kind, Index index)
+ : name(name.to_string()), kind(kind), index(index) {}
+
+ std::string name;
+ ExternalKind kind;
+ Index index;
+};
+
+class HostImportDelegate {
+ public:
+ typedef std::function<void(const char* msg)> ErrorCallback;
+
+ virtual ~HostImportDelegate() {}
+ virtual wabt::Result ImportFunc(FuncImport*,
+ Func*,
+ FuncSignature*,
+ const ErrorCallback&) = 0;
+ virtual wabt::Result ImportTable(TableImport*,
+ Table*,
+ const ErrorCallback&) = 0;
+ virtual wabt::Result ImportMemory(MemoryImport*,
+ Memory*,
+ const ErrorCallback&) = 0;
+ virtual wabt::Result ImportGlobal(GlobalImport*,
+ Global*,
+ const ErrorCallback&) = 0;
+};
+
+struct Module {
+ WABT_DISALLOW_COPY_AND_ASSIGN(Module);
+ explicit Module(bool is_host);
+ Module(string_view name, bool is_host);
+ virtual ~Module() = default;
+
+ Export* GetExport(string_view name);
+
+ std::string name;
+ std::vector<Export> exports;
+ BindingHash export_bindings;
+ Index memory_index; /* kInvalidIndex if not defined */
+ Index table_index; /* kInvalidIndex if not defined */
+ bool is_host;
+};
+
+struct DefinedModule : Module {
+ DefinedModule();
+ static bool classof(const Module* module) { return !module->is_host; }
+
+ std::vector<FuncImport> func_imports;
+ std::vector<TableImport> table_imports;
+ std::vector<MemoryImport> memory_imports;
+ std::vector<GlobalImport> global_imports;
+ std::vector<ExceptImport> except_imports;
+ Index start_func_index; /* kInvalidIndex if not defined */
+ IstreamOffset istream_start;
+ IstreamOffset istream_end;
+};
+
+struct HostModule : Module {
+ explicit HostModule(string_view name);
+ static bool classof(const Module* module) { return module->is_host; }
+
+ std::unique_ptr<HostImportDelegate> import_delegate;
+};
+
+class Environment {
+ 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 istream_size = 0;
+ };
+
+ Environment();
+
+ 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 GetModuleCount() const { return modules_.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];
+ }
+ 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);
+
+ template <typename... Args>
+ FuncSignature* EmplaceBackFuncSignature(Args&&... args) {
+ sigs_.emplace_back(std::forward<Args>(args)...);
+ return &sigs_.back();
+ }
+
+ template <typename... Args>
+ Func* EmplaceBackFunc(Args&&... args) {
+ funcs_.emplace_back(std::forward<Args>(args)...);
+ return funcs_.back().get();
+ }
+
+ template <typename... Args>
+ Global* EmplaceBackGlobal(Args&&... args) {
+ globals_.emplace_back(std::forward<Args>(args)...);
+ return &globals_.back();
+ }
+
+ template <typename... Args>
+ Table* EmplaceBackTable(Args&&... args) {
+ tables_.emplace_back(std::forward<Args>(args)...);
+ return &tables_.back();
+ }
+
+ template <typename... Args>
+ Memory* EmplaceBackMemory(Args&&... args) {
+ memories_.emplace_back(std::forward<Args>(args)...);
+ return &memories_.back();
+ }
+
+ template <typename... Args>
+ Module* EmplaceBackModule(Args&&... args) {
+ modules_.emplace_back(std::forward<Args>(args)...);
+ return modules_.back().get();
+ }
+
+ template <typename... Args>
+ void EmplaceModuleBinding(Args&&... args) {
+ module_bindings_.emplace(std::forward<Args>(args)...);
+ }
+
+ template <typename... Args>
+ void EmplaceRegisteredModuleBinding(Args&&... args) {
+ registered_module_bindings_.emplace(std::forward<Args>(args)...);
+ }
+
+ HostModule* AppendHostModule(string_view name);
+
+ bool FuncSignaturesAreEqual(Index sig_index_0, Index sig_index_1) const;
+
+ MarkPoint Mark();
+ void ResetToMarkPoint(const MarkPoint&);
+
+ void Disassemble(Stream* stream, IstreamOffset from, IstreamOffset to);
+ void DisassembleModule(Stream* stream, Module*);
+
+ 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::unique_ptr<OutputBuffer> istream_;
+ BindingHash module_bindings_;
+ BindingHash registered_module_bindings_;
+};
+
+class Thread {
+ public:
+ struct Options {
+ static const uint32_t kDefaultValueStackSize = 512 * 1024 / sizeof(Value);
+ static const uint32_t kDefaultCallStackSize = 64 * 1024;
+
+ explicit Options(uint32_t value_stack_size = kDefaultValueStackSize,
+ uint32_t call_stack_size = kDefaultCallStackSize,
+ IstreamOffset pc = kInvalidIstreamOffset,
+ Stream* trace_stream = nullptr);
+
+ uint32_t value_stack_size;
+ uint32_t call_stack_size;
+ IstreamOffset pc;
+ Stream* trace_stream;
+ };
+
+ explicit Thread(Environment*, const Options& = Options());
+
+ Environment* env() { return env_; }
+
+ Result RunFunction(Index func_index,
+ const TypedValues& args,
+ TypedValues* out_results);
+ Result RunStartFunction(DefinedModule* module);
+ Result RunExport(const Export*,
+ const TypedValues& args,
+ TypedValues* out_results);
+ Result RunExportByName(Module* module,
+ string_view name,
+ const TypedValues& args,
+ TypedValues* out_results);
+
+ private:
+ const uint8_t* GetIstream() const { return env_->istream_->data.data(); }
+
+ Result PushArgs(const FuncSignature*, const TypedValues& args);
+ void CopyResults(const FuncSignature*, TypedValues* out_results);
+
+ Result Run(int num_instructions, IstreamOffset* call_stack_return_top);
+ void Trace(Stream*);
+
+ 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);
+
+ Value& Top();
+ Value& Pick(Index depth);
+
+ Result Push(Value) WABT_WARN_UNUSED;
+ Value Pop();
+
+ // 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();
+
+ // Push/Pop values without conversions, e.g. Push<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;
+ template <typename T>
+ ValueTypeRep<T> PopRep();
+
+ void DropKeep(uint32_t drop_count, uint8_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;
+
+ 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 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;
+
+ Result RunDefinedFunction(IstreamOffset);
+
+ Result CallHost(HostFunc*);
+
+ Environment* env_;
+ std::vector<Value> value_stack_;
+ std::vector<IstreamOffset> call_stack_;
+ Value* value_stack_top_;
+ Value* value_stack_end_;
+ IstreamOffset* call_stack_top_;
+ IstreamOffset* call_stack_end_;
+ IstreamOffset pc_;
+ Stream* trace_stream_;
+};
+
+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 TypedValueToString(const TypedValue&);
+const char* ResultToString(Result);
+
+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);
+
+} // namespace interp
+} // namespace wabt
+
+#endif /* WABT_INTERP_H_ */