// Copyright 2016 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef V8_WASM_INTERPRETER_H_ #define V8_WASM_INTERPRETER_H_ #include "src/wasm/wasm-opcodes.h" #include "src/zone/zone-containers.h" namespace v8 { namespace base { class AccountingAllocator; } namespace internal { namespace wasm { // forward declarations. struct ModuleBytesEnv; struct WasmFunction; class WasmInterpreterInternals; typedef size_t pc_t; typedef size_t sp_t; typedef int32_t pcdiff_t; typedef uint32_t spdiff_t; const pc_t kInvalidPc = 0x80000000; typedef ZoneMap<pc_t, pcdiff_t> ControlTransferMap; // Macro for defining union members. #define FOREACH_UNION_MEMBER(V) \ V(i32, kWasmI32, int32_t) \ V(u32, kWasmI32, uint32_t) \ V(i64, kWasmI64, int64_t) \ V(u64, kWasmI64, uint64_t) \ V(f32, kWasmF32, float) \ V(f64, kWasmF64, double) // Representation of values within the interpreter. struct WasmVal { ValueType type; union { #define DECLARE_FIELD(field, localtype, ctype) ctype field; FOREACH_UNION_MEMBER(DECLARE_FIELD) #undef DECLARE_FIELD } val; WasmVal() : type(kWasmStmt) {} #define DECLARE_CONSTRUCTOR(field, localtype, ctype) \ explicit WasmVal(ctype v) : type(localtype) { val.field = v; } FOREACH_UNION_MEMBER(DECLARE_CONSTRUCTOR) #undef DECLARE_CONSTRUCTOR template <typename T> inline T to() { UNREACHABLE(); } template <typename T> inline T to_unchecked() { UNREACHABLE(); } }; #define DECLARE_CAST(field, localtype, ctype) \ template <> \ inline ctype WasmVal::to_unchecked() { \ return val.field; \ } \ template <> \ inline ctype WasmVal::to() { \ CHECK_EQ(localtype, type); \ return val.field; \ } FOREACH_UNION_MEMBER(DECLARE_CAST) #undef DECLARE_CAST // Representation of frames within the interpreter. class InterpretedFrame { public: const WasmFunction* function() const { return function_; } int pc() const { return pc_; } //========================================================================== // Stack frame inspection. //========================================================================== int GetParameterCount() const; WasmVal GetLocalVal(int index) const; WasmVal GetExprVal(int pc) const; void SetLocalVal(int index, WasmVal val); void SetExprVal(int pc, WasmVal val); private: friend class WasmInterpreter; InterpretedFrame(const WasmFunction* function, int pc, int fp, int sp) : function_(function), pc_(pc), fp_(fp), sp_(sp) {} const WasmFunction* function_; int pc_; int fp_; int sp_; }; // An interpreter capable of executing WASM. class V8_EXPORT_PRIVATE WasmInterpreter { public: // State machine for a Thread: // +---------------Run()-----------+ // V | // STOPPED ---Run()--> RUNNING ------Pause()-----+-> PAUSED <------+ // | | | / | | // | | +---- Breakpoint ---+ +-- Step() --+ // | | // | +------------ Trap --------------> TRAPPED // +------------- Finish -------------> FINISHED enum State { STOPPED, RUNNING, PAUSED, FINISHED, TRAPPED }; // Tells a thread to pause after certain instructions. enum BreakFlag : uint8_t { None = 0, AfterReturn = 1 << 0, AfterCall = 1 << 1 }; // Representation of a thread in the interpreter. class V8_EXPORT_PRIVATE Thread { // Don't instante Threads; they will be allocated as ThreadImpl in the // interpreter implementation. Thread() = delete; public: // Execution control. State state(); void PushFrame(const WasmFunction* function, WasmVal* args); State Run(); State Step(); void Pause(); void Reset(); // Stack inspection and modification. pc_t GetBreakpointPc(); int GetFrameCount(); const InterpretedFrame GetFrame(int index); InterpretedFrame GetMutableFrame(int index); WasmVal GetReturnValue(int index = 0); // Returns true if the thread executed an instruction which may produce // nondeterministic results, e.g. float div, float sqrt, and float mul, // where the sign bit of a NaN is nondeterministic. bool PossibleNondeterminism(); // Returns the number of calls / function frames executed on this thread. uint64_t NumInterpretedCalls(); // Thread-specific breakpoints. // TODO(wasm): Implement this once we support multiple threads. // bool SetBreakpoint(const WasmFunction* function, int pc, bool enabled); // bool GetBreakpoint(const WasmFunction* function, int pc); void AddBreakFlags(uint8_t flags); void ClearBreakFlags(); }; WasmInterpreter(const ModuleBytesEnv& env, AccountingAllocator* allocator); ~WasmInterpreter(); //========================================================================== // Execution controls. //========================================================================== void Run(); void Pause(); // Set a breakpoint at {pc} in {function} to be {enabled}. Returns the // previous state of the breakpoint at {pc}. bool SetBreakpoint(const WasmFunction* function, pc_t pc, bool enabled); // Gets the current state of the breakpoint at {function}. bool GetBreakpoint(const WasmFunction* function, pc_t pc); // Enable or disable tracing for {function}. Return the previous state. bool SetTracing(const WasmFunction* function, bool enabled); //========================================================================== // Thread iteration and inspection. //========================================================================== int GetThreadCount(); Thread* GetThread(int id); //========================================================================== // Memory access. //========================================================================== size_t GetMemorySize(); WasmVal ReadMemory(size_t offset); void WriteMemory(size_t offset, WasmVal val); //========================================================================== // Testing functionality. //========================================================================== // Manually adds a function to this interpreter, returning the index of the // function. int AddFunctionForTesting(const WasmFunction* function); // Manually adds code to the interpreter for the given function. bool SetFunctionCodeForTesting(const WasmFunction* function, const byte* start, const byte* end); // Computes the control transfers for the given bytecode. Used internally in // the interpreter, but exposed for testing. static ControlTransferMap ComputeControlTransfersForTesting(Zone* zone, const byte* start, const byte* end); private: Zone zone_; WasmInterpreterInternals* internals_; }; } // namespace wasm } // namespace internal } // namespace v8 #endif // V8_WASM_INTERPRETER_H_