// 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_WASM_INTERPRETER_H_ #define V8_WASM_WASM_INTERPRETER_H_ #include "src/wasm/wasm-opcodes.h" #include "src/wasm/wasm-value.h" #include "src/zone/zone-containers.h" namespace v8 { namespace base { class AccountingAllocator; } namespace internal { class WasmInstanceObject; struct WasmContext; namespace wasm { // forward declarations. struct ModuleWireBytes; struct WasmFunction; struct WasmModule; class WasmInterpreterInternals; using pc_t = size_t; using sp_t = size_t; using pcdiff_t = int32_t; using spdiff_t = uint32_t; constexpr pc_t kInvalidPc = 0x80000000; struct ControlTransferEntry { // Distance from the instruction to the label to jump to (forward, but can be // negative). pcdiff_t pc_diff; // Delta by which to decrease the stack height. spdiff_t sp_diff; // Arity of the block we jump to. uint32_t target_arity; }; using ControlTransferMap = ZoneMap<pc_t, ControlTransferEntry>; // Representation of frames within the interpreter. // // Layout of a frame: // ----------------- // stack slot #N ‾\. // ... | stack entries: GetStackHeight(); GetStackValue() // stack slot #0 _/· // local #L ‾\. // ... | locals: GetLocalCount(); GetLocalValue() // local #P+1 | // param #P | ‾\. // ... | | parameters: GetParameterCount(); GetLocalValue() // param #0 _/· _/· // ----------------- // class InterpretedFrame { public: const WasmFunction* function() const; int pc() const; int GetParameterCount() const; int GetLocalCount() const; int GetStackHeight() const; WasmValue GetLocalValue(int index) const; WasmValue GetStackValue(int index) const; // Deleter struct to delete the underlying InterpretedFrameImpl without // violating language specifications. struct Deleter { void operator()(InterpretedFrame* ptr); }; private: friend class WasmInterpreter; // Don't instante InterpretedFrames; they will be allocated as // InterpretedFrameImpl in the interpreter implementation. InterpretedFrame() = delete; DISALLOW_COPY_AND_ASSIGN(InterpretedFrame); }; // An interpreter capable of executing WebAssembly. class V8_EXPORT_PRIVATE WasmInterpreter { public: // Open a HeapObjectsScope before running any code in the interpreter which // needs access to the instance object or needs to call to JS functions. class V8_EXPORT_PRIVATE HeapObjectsScope { public: HeapObjectsScope(WasmInterpreter* interpreter, Handle<WasmInstanceObject> instance); ~HeapObjectsScope(); private: char data[3 * sizeof(void*)]; // must match sizeof(HeapObjectsScopeImpl). DISALLOW_COPY_AND_ASSIGN(HeapObjectsScope); }; // 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 }; using FramePtr = std::unique_ptr<InterpretedFrame, InterpretedFrame::Deleter>; // 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, WasmValue* 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(); // The InterpretedFrame is only valid as long as the Thread is paused. FramePtr GetFrame(int index); WasmValue 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 WasmModule* module, const ModuleWireBytes& wire_bytes, WasmContext* wasm_context); ~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); //========================================================================== // 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); void SetCallIndirectTestMode(); // Computes the control transfers for the given bytecode. Used internally in // the interpreter, but exposed for testing. static ControlTransferMap ComputeControlTransfersForTesting( Zone* zone, const WasmModule* module, const byte* start, const byte* end); private: Zone zone_; WasmInterpreterInternals* const internals_; }; } // namespace wasm } // namespace internal } // namespace v8 #endif // V8_WASM_WASM_INTERPRETER_H_