blob: b0c100b5a9408b48b5472be2101aeb24f8eef4f1 [file] [log] [blame]
// 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/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);
// 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_INTERPRETER_H_