// 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 { class WasmInstanceObject; 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 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 inline T to() { UNREACHABLE(); } template 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()/Step()--------+ // V | // STOPPED ---Run()--> RUNNING ------Pause()-----+-> PAUSED // ^ | | | | / // +- HandleException -+ | | +--- Breakpoint ---+ // | | // | +---------- 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: enum ExceptionHandlingResult { HANDLED, UNWOUND }; // Execution control. State state(); void InitFrame(const WasmFunction* function, WasmVal* args); // Pass -1 as num_steps to run till completion, pause or breakpoint. State Run(int num_steps = -1); State Step() { return Run(1); } void Pause(); void Reset(); // Handle the pending exception in the passed isolate. Unwind the stack // accordingly. Return whether the exception was handled inside wasm. ExceptionHandlingResult HandleException(Isolate* isolate); // Stack inspection and modification. pc_t GetBreakpointPc(); // TODO(clemensh): Make this uint32_t. int GetFrameCount(); const InterpretedFrame GetFrame(int index); InterpretedFrame GetMutableFrame(int index); WasmVal GetReturnValue(int index = 0); TrapReason GetTrapReason(); // 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(); // Each thread can have multiple activations, each represented by a portion // of the stack frames of this thread. StartActivation returns the id // (counting from 0 up) of the started activation. // Activations must be properly stacked, i.e. if FinishActivation is called, // the given id must the the latest activation on the stack. uint32_t NumActivations(); uint32_t StartActivation(); void FinishActivation(uint32_t activation_id); // Return the frame base of the given activation, i.e. the number of frames // when this activation was started. uint32_t ActivationFrameBase(uint32_t activation_id); }; WasmInterpreter(Isolate* isolate, const ModuleBytesEnv& env); ~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); // Set the associated wasm instance object. // If the instance object has been set, some tables stored inside it are used // instead of the tables stored in the WasmModule struct. This allows to call // back and forth between the interpreter and outside code (JS or wasm // compiled) without repeatedly copying information. void SetInstanceObject(WasmInstanceObject*); //========================================================================== // 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. The func_index of the // function must match the current number of functions. void AddFunctionForTesting(const WasmFunction* function); // Manually adds code to the interpreter for the given function. void 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_