[wasm][interpreter] Remove threads support
The wasm interpreter was always single-threaded, and there are no plans to change this. Still, there was a concept of threads, but with the hard-coded constraint that there is always exactly one of them. In order to clean up the code, and as a preparation to remove more unneeded functionality before moving the interpreter over to the test directory, this CL removes the concept of threads and merges the {ThreadImpl} class into {WasmInterpreterInternals}. Drive-by: Remove the dead {GetFrameCount} method. R=ahaas@chromium.org Bug: v8:10389 Change-Id: If65cdd21b34ce8debf8ba0f24dbeacec15e0a1d7 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2231354 Reviewed-by: Andreas Haas <ahaas@chromium.org> Commit-Queue: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/master@{#68204}
This commit is contained in:
parent
b644fac5a9
commit
47e501e169
@ -191,13 +191,12 @@ class InterpreterHandle {
|
||||
DCHECK_EQ(sig->return_count(), return_values.size());
|
||||
|
||||
WasmCodeRefScope code_ref_scope;
|
||||
WasmInterpreter::Thread* thread = interpreter_.GetThread(0);
|
||||
thread->InitFrame(&module()->functions[func_index],
|
||||
argument_values.begin());
|
||||
interpreter_.InitFrame(&module()->functions[func_index],
|
||||
argument_values.begin());
|
||||
bool finished = false;
|
||||
while (!finished) {
|
||||
// TODO(clemensb): Add occasional StackChecks.
|
||||
WasmInterpreter::State state = thread->Run();
|
||||
WasmInterpreter::State state = interpreter_.Run();
|
||||
switch (state) {
|
||||
case WasmInterpreter::State::PAUSED:
|
||||
UNREACHABLE();
|
||||
@ -207,16 +206,16 @@ class InterpreterHandle {
|
||||
break;
|
||||
case WasmInterpreter::State::TRAPPED: {
|
||||
MessageTemplate message_id =
|
||||
WasmOpcodes::TrapReasonToMessageId(thread->GetTrapReason());
|
||||
WasmOpcodes::TrapReasonToMessageId(interpreter_.GetTrapReason());
|
||||
Handle<JSObject> exception =
|
||||
isolate_->factory()->NewWasmRuntimeError(message_id);
|
||||
JSObject::AddProperty(isolate_, exception,
|
||||
isolate_->factory()->wasm_uncatchable_symbol(),
|
||||
isolate_->factory()->true_value(), NONE);
|
||||
auto result = thread->RaiseException(isolate_, exception);
|
||||
if (result == WasmInterpreter::Thread::HANDLED) break;
|
||||
auto result = interpreter_.RaiseException(isolate_, exception);
|
||||
if (result == WasmInterpreter::HANDLED) break;
|
||||
// If no local handler was found, we fall-thru to {STOPPED}.
|
||||
DCHECK_EQ(WasmInterpreter::State::STOPPED, thread->state());
|
||||
DCHECK_EQ(WasmInterpreter::State::STOPPED, interpreter_.state());
|
||||
V8_FALLTHROUGH;
|
||||
}
|
||||
case WasmInterpreter::State::STOPPED:
|
||||
@ -239,7 +238,7 @@ class InterpreterHandle {
|
||||
#endif
|
||||
DCHECK_GE(max_count, sig->return_count());
|
||||
for (unsigned i = 0; i < sig->return_count(); ++i) {
|
||||
return_values[i] = thread->GetReturnValue(i);
|
||||
return_values[i] = interpreter_.GetReturnValue(i);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -1102,9 +1102,20 @@ V8_INLINE bool has_nondeterminism<double>(double val) {
|
||||
|
||||
} // namespace
|
||||
|
||||
// Responsible for executing code directly.
|
||||
class ThreadImpl {
|
||||
//============================================================================
|
||||
// The implementation details of the interpreter.
|
||||
//============================================================================
|
||||
class WasmInterpreterInternals {
|
||||
public:
|
||||
WasmInterpreterInternals(Zone* zone, const WasmModule* module,
|
||||
const ModuleWireBytes& wire_bytes,
|
||||
Handle<WasmInstanceObject> instance_object)
|
||||
: module_bytes_(wire_bytes.start(), wire_bytes.end(), zone),
|
||||
codemap_(module, module_bytes_.data(), zone),
|
||||
isolate_(instance_object->GetIsolate()),
|
||||
instance_object_(instance_object),
|
||||
frames_(zone) {}
|
||||
|
||||
// The {ReferenceStackScope} sets up the reference stack in the interpreter.
|
||||
// The handle to the reference stack has to be re-initialized everytime we
|
||||
// call into the interpreter because there is no HandleScope that could
|
||||
@ -1113,7 +1124,7 @@ class ThreadImpl {
|
||||
// reference stack and thereby transitively keeps the interpreter alive.
|
||||
class ReferenceStackScope {
|
||||
public:
|
||||
explicit ReferenceStackScope(ThreadImpl* impl) : impl_(impl) {
|
||||
explicit ReferenceStackScope(WasmInterpreterInternals* impl) : impl_(impl) {
|
||||
// The reference stack is already initialized, we don't have to do
|
||||
// anything.
|
||||
if (!impl_->reference_stack_cell_.is_null()) return;
|
||||
@ -1131,26 +1142,16 @@ class ThreadImpl {
|
||||
}
|
||||
|
||||
private:
|
||||
ThreadImpl* impl_;
|
||||
WasmInterpreterInternals* impl_;
|
||||
bool do_reset_stack_ = false;
|
||||
};
|
||||
|
||||
ThreadImpl(Zone* zone, CodeMap* codemap,
|
||||
Handle<WasmInstanceObject> instance_object)
|
||||
: codemap_(codemap),
|
||||
isolate_(instance_object->GetIsolate()),
|
||||
instance_object_(instance_object),
|
||||
frames_(zone) {}
|
||||
|
||||
//==========================================================================
|
||||
// Implementation of public interface for WasmInterpreter::Thread.
|
||||
//==========================================================================
|
||||
|
||||
WasmInterpreter::State state() { return state_; }
|
||||
|
||||
void InitFrame(const WasmFunction* function, WasmValue* args) {
|
||||
ReferenceStackScope stack_scope(this);
|
||||
DCHECK(frames_.empty());
|
||||
InterpreterCode* code = codemap()->GetCode(function);
|
||||
InterpreterCode* code = codemap_.GetCode(function);
|
||||
size_t num_params = function->sig->parameter_count();
|
||||
EnsureStackSpace(num_params);
|
||||
Push(args, num_params);
|
||||
@ -1158,6 +1159,7 @@ class ThreadImpl {
|
||||
}
|
||||
|
||||
WasmInterpreter::State Run(int num_steps = -1) {
|
||||
ReferenceStackScope stack_scope(this);
|
||||
DCHECK(state_ == WasmInterpreter::STOPPED ||
|
||||
state_ == WasmInterpreter::PAUSED);
|
||||
DCHECK(num_steps == -1 || num_steps > 0);
|
||||
@ -1178,6 +1180,7 @@ class ThreadImpl {
|
||||
void Pause() { UNIMPLEMENTED(); }
|
||||
|
||||
void Reset() {
|
||||
ReferenceStackScope stack_scope(this);
|
||||
TRACE("----- RESET -----\n");
|
||||
ResetStack(0);
|
||||
frames_.clear();
|
||||
@ -1186,12 +1189,8 @@ class ThreadImpl {
|
||||
possible_nondeterminism_ = false;
|
||||
}
|
||||
|
||||
int GetFrameCount() {
|
||||
DCHECK_GE(kMaxInt, frames_.size());
|
||||
return static_cast<int>(frames_.size());
|
||||
}
|
||||
|
||||
WasmValue GetReturnValue(uint32_t index) {
|
||||
ReferenceStackScope stack_scope(this);
|
||||
if (state_ == WasmInterpreter::TRAPPED) return WasmValue(0xDEADBEEF);
|
||||
DCHECK_EQ(WasmInterpreter::FINISHED, state_);
|
||||
return GetStackValue(index);
|
||||
@ -1215,23 +1214,27 @@ class ThreadImpl {
|
||||
|
||||
Handle<Cell> reference_stack_cell() const { return reference_stack_cell_; }
|
||||
|
||||
WasmInterpreter::Thread::ExceptionHandlingResult RaiseException(
|
||||
WasmInterpreter::ExceptionHandlingResult RaiseException(
|
||||
Isolate* isolate, Handle<Object> exception) {
|
||||
ReferenceStackScope stack_scope(this);
|
||||
DCHECK_EQ(WasmInterpreter::TRAPPED, state_);
|
||||
isolate->Throw(*exception); // Will check that none is pending.
|
||||
if (HandleException(isolate) == WasmInterpreter::Thread::UNWOUND) {
|
||||
if (HandleException(isolate) == WasmInterpreter::UNWOUND) {
|
||||
DCHECK_EQ(WasmInterpreter::STOPPED, state_);
|
||||
return WasmInterpreter::Thread::UNWOUND;
|
||||
return WasmInterpreter::UNWOUND;
|
||||
}
|
||||
state_ = WasmInterpreter::PAUSED;
|
||||
return WasmInterpreter::Thread::HANDLED;
|
||||
return WasmInterpreter::HANDLED;
|
||||
}
|
||||
|
||||
CodeMap* codemap() { return &codemap_; }
|
||||
|
||||
private:
|
||||
friend class ReferenceStackScope;
|
||||
|
||||
// Handle a thrown exception. Returns whether the exception was handled inside
|
||||
// of wasm. Unwinds the interpreted stack accordingly.
|
||||
WasmInterpreter::Thread::ExceptionHandlingResult HandleException(
|
||||
Isolate* isolate) {
|
||||
WasmInterpreter::ExceptionHandlingResult HandleException(Isolate* isolate) {
|
||||
DCHECK(isolate->has_pending_exception());
|
||||
bool catchable =
|
||||
isolate->is_catchable_by_wasm(isolate->pending_exception());
|
||||
@ -1245,7 +1248,7 @@ class ThreadImpl {
|
||||
frame.pc += JumpToHandlerDelta(code, frame.pc);
|
||||
TRACE(" => handler #%zu (#%u @%zu)\n", frames_.size() - 1,
|
||||
code->function->func_index, frame.pc);
|
||||
return WasmInterpreter::Thread::HANDLED;
|
||||
return WasmInterpreter::HANDLED;
|
||||
}
|
||||
TRACE(" => drop frame #%zu (#%u @%zu)\n", frames_.size() - 1,
|
||||
code->function->func_index, frame.pc);
|
||||
@ -1256,7 +1259,7 @@ class ThreadImpl {
|
||||
DCHECK(frames_.empty());
|
||||
DCHECK_EQ(sp_, stack_.get());
|
||||
state_ = WasmInterpreter::STOPPED;
|
||||
return WasmInterpreter::Thread::UNWOUND;
|
||||
return WasmInterpreter::UNWOUND;
|
||||
}
|
||||
|
||||
// Entries on the stack of functions being evaluated.
|
||||
@ -1279,68 +1282,51 @@ class ThreadImpl {
|
||||
class StackValue {
|
||||
public:
|
||||
StackValue() = default; // Only needed for resizing the stack.
|
||||
StackValue(WasmValue v, ThreadImpl* thread, sp_t index) : value_(v) {
|
||||
StackValue(WasmValue v, WasmInterpreterInternals* impl, sp_t index)
|
||||
: value_(v) {
|
||||
if (IsReferenceValue()) {
|
||||
value_ = WasmValue(Handle<Object>::null());
|
||||
int ref_index = static_cast<int>(index);
|
||||
thread->reference_stack().set(ref_index, *v.to_anyref());
|
||||
impl->reference_stack().set(ref_index, *v.to_anyref());
|
||||
}
|
||||
}
|
||||
|
||||
WasmValue ExtractValue(ThreadImpl* thread, sp_t index) {
|
||||
WasmValue ExtractValue(WasmInterpreterInternals* impl, sp_t index) {
|
||||
if (!IsReferenceValue()) return value_;
|
||||
DCHECK(value_.to_anyref().is_null());
|
||||
int ref_index = static_cast<int>(index);
|
||||
Isolate* isolate = thread->isolate_;
|
||||
Handle<Object> ref(thread->reference_stack().get(ref_index), isolate);
|
||||
Isolate* isolate = impl->isolate_;
|
||||
Handle<Object> ref(impl->reference_stack().get(ref_index), isolate);
|
||||
DCHECK(!ref->IsTheHole(isolate));
|
||||
return WasmValue(ref);
|
||||
}
|
||||
|
||||
bool IsReferenceValue() const { return value_.type() == kWasmAnyRef; }
|
||||
|
||||
void ClearValue(ThreadImpl* thread, sp_t index) {
|
||||
void ClearValue(WasmInterpreterInternals* impl, sp_t index) {
|
||||
if (!IsReferenceValue()) return;
|
||||
int ref_index = static_cast<int>(index);
|
||||
Isolate* isolate = thread->isolate_;
|
||||
thread->reference_stack().set_the_hole(isolate, ref_index);
|
||||
Isolate* isolate = impl->isolate_;
|
||||
impl->reference_stack().set_the_hole(isolate, ref_index);
|
||||
}
|
||||
|
||||
static void ClearValues(ThreadImpl* thread, sp_t index, int count) {
|
||||
static void ClearValues(WasmInterpreterInternals* impl, sp_t index,
|
||||
int count) {
|
||||
int ref_index = static_cast<int>(index);
|
||||
thread->reference_stack().FillWithHoles(ref_index, ref_index + count);
|
||||
impl->reference_stack().FillWithHoles(ref_index, ref_index + count);
|
||||
}
|
||||
|
||||
static bool IsClearedValue(ThreadImpl* thread, sp_t index) {
|
||||
static bool IsClearedValue(WasmInterpreterInternals* impl, sp_t index) {
|
||||
int ref_index = static_cast<int>(index);
|
||||
Isolate* isolate = thread->isolate_;
|
||||
return thread->reference_stack().is_the_hole(isolate, ref_index);
|
||||
Isolate* isolate = impl->isolate_;
|
||||
return impl->reference_stack().is_the_hole(isolate, ref_index);
|
||||
}
|
||||
|
||||
private:
|
||||
WasmValue value_;
|
||||
};
|
||||
|
||||
friend class ReferenceStackScope;
|
||||
|
||||
CodeMap* codemap_;
|
||||
Isolate* isolate_;
|
||||
Handle<WasmInstanceObject> instance_object_;
|
||||
std::unique_ptr<StackValue[]> stack_;
|
||||
StackValue* stack_limit_ = nullptr; // End of allocated stack space.
|
||||
StackValue* sp_ = nullptr; // Current stack pointer.
|
||||
// The reference stack is pointed to by a {Cell} to be able to replace the
|
||||
// underlying {FixedArray} when growing the stack. This avoids having to
|
||||
// recreate or update the global handle keeping this object alive.
|
||||
Handle<Cell> reference_stack_cell_; // References are on an on-heap stack.
|
||||
ZoneVector<Frame> frames_;
|
||||
WasmInterpreter::State state_ = WasmInterpreter::STOPPED;
|
||||
TrapReason trap_reason_ = kTrapCount;
|
||||
bool possible_nondeterminism_ = false;
|
||||
uint64_t num_interpreted_calls_ = 0;
|
||||
|
||||
CodeMap* codemap() const { return codemap_; }
|
||||
const WasmModule* module() const { return codemap_->module(); }
|
||||
const WasmModule* module() const { return codemap_.module(); }
|
||||
FixedArray reference_stack() const {
|
||||
return FixedArray::cast(reference_stack_cell_->value());
|
||||
}
|
||||
@ -2770,7 +2756,7 @@ class ThreadImpl {
|
||||
// it to 0 here such that we report the same position as in compiled code.
|
||||
frames_.back().pc = 0;
|
||||
isolate_->StackOverflow();
|
||||
return HandleException(isolate_) == WasmInterpreter::Thread::HANDLED;
|
||||
return HandleException(isolate_) == WasmInterpreter::HANDLED;
|
||||
}
|
||||
|
||||
void EncodeI32ExceptionValue(Handle<FixedArray> encoded_values,
|
||||
@ -2863,14 +2849,14 @@ class ThreadImpl {
|
||||
Drop(static_cast<int>(sig->parameter_count()));
|
||||
// Now that the exception is ready, set it as pending.
|
||||
isolate_->Throw(*exception_object);
|
||||
return HandleException(isolate_) == WasmInterpreter::Thread::HANDLED;
|
||||
return HandleException(isolate_) == WasmInterpreter::HANDLED;
|
||||
}
|
||||
|
||||
// Throw a given existing exception. Returns true if the exception is being
|
||||
// handled locally by the interpreter, false otherwise (interpreter exits).
|
||||
bool DoRethrowException(WasmValue exception) {
|
||||
isolate_->ReThrow(*exception.to_anyref());
|
||||
return HandleException(isolate_) == WasmInterpreter::Thread::HANDLED;
|
||||
return HandleException(isolate_) == WasmInterpreter::HANDLED;
|
||||
}
|
||||
|
||||
// Determines whether the given exception has a tag matching the expected tag
|
||||
@ -3022,7 +3008,7 @@ class ThreadImpl {
|
||||
// Compute the stack effect of this opcode, and verify later that the
|
||||
// stack was modified accordingly.
|
||||
std::pair<uint32_t, uint32_t> stack_effect =
|
||||
StackEffect(codemap_->module(), frames_.back().code->function->sig,
|
||||
StackEffect(codemap_.module(), frames_.back().code->function->sig,
|
||||
code->start + pc, code->end);
|
||||
sp_t expected_new_stack_height =
|
||||
StackHeight() - stack_effect.first + stack_effect.second;
|
||||
@ -3232,7 +3218,7 @@ class ThreadImpl {
|
||||
case kExprCallFunction: {
|
||||
CallFunctionImmediate<Decoder::kNoValidate> imm(&decoder,
|
||||
code->at(pc));
|
||||
InterpreterCode* target = codemap()->GetCode(imm.index);
|
||||
InterpreterCode* target = codemap_.GetCode(imm.index);
|
||||
CHECK(!target->function->imported);
|
||||
// Execute an internal call.
|
||||
if (!DoCall(&decoder, target, &pc, &limit)) return;
|
||||
@ -3264,7 +3250,7 @@ class ThreadImpl {
|
||||
case kExprReturnCall: {
|
||||
CallFunctionImmediate<Decoder::kNoValidate> imm(&decoder,
|
||||
code->at(pc));
|
||||
InterpreterCode* target = codemap()->GetCode(imm.index);
|
||||
InterpreterCode* target = codemap_.GetCode(imm.index);
|
||||
|
||||
CHECK(!target->function->imported);
|
||||
// Enter internal found function.
|
||||
@ -3815,96 +3801,27 @@ class ThreadImpl {
|
||||
instance_object_.is_identical_to(object_ref));
|
||||
|
||||
DCHECK_EQ(WasmCode::kFunction, code->kind());
|
||||
return {CallResult::INTERNAL, codemap()->GetCode(code->index())};
|
||||
return {CallResult::INTERNAL, codemap_.GetCode(code->index())};
|
||||
}
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
// Converters between WasmInterpreter::Thread and WasmInterpreter::ThreadImpl.
|
||||
// Thread* is the public interface, without knowledge of the object layout.
|
||||
// This cast is potentially risky, but as long as we always cast it back before
|
||||
// accessing any data, it should be fine. UBSan is not complaining.
|
||||
WasmInterpreter::Thread* ToThread(ThreadImpl* impl) {
|
||||
return reinterpret_cast<WasmInterpreter::Thread*>(impl);
|
||||
}
|
||||
ThreadImpl* ToImpl(WasmInterpreter::Thread* thread) {
|
||||
return reinterpret_cast<ThreadImpl*>(thread);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
//============================================================================
|
||||
// Implementation of the pimpl idiom for WasmInterpreter::Thread.
|
||||
// Instead of placing a pointer to the ThreadImpl inside of the Thread object,
|
||||
// we just reinterpret_cast them. ThreadImpls are only allocated inside this
|
||||
// translation unit anyway.
|
||||
//============================================================================
|
||||
WasmInterpreter::State WasmInterpreter::Thread::state() {
|
||||
ThreadImpl* impl = ToImpl(this);
|
||||
ThreadImpl::ReferenceStackScope stack_scope(impl);
|
||||
return impl->state();
|
||||
}
|
||||
void WasmInterpreter::Thread::InitFrame(const WasmFunction* function,
|
||||
WasmValue* args) {
|
||||
ThreadImpl* impl = ToImpl(this);
|
||||
ThreadImpl::ReferenceStackScope stack_scope(impl);
|
||||
impl->InitFrame(function, args);
|
||||
}
|
||||
WasmInterpreter::State WasmInterpreter::Thread::Run(int num_steps) {
|
||||
ThreadImpl* impl = ToImpl(this);
|
||||
ThreadImpl::ReferenceStackScope stack_scope(impl);
|
||||
return impl->Run(num_steps);
|
||||
}
|
||||
void WasmInterpreter::Thread::Pause() { return ToImpl(this)->Pause(); }
|
||||
void WasmInterpreter::Thread::Reset() {
|
||||
ThreadImpl* impl = ToImpl(this);
|
||||
ThreadImpl::ReferenceStackScope stack_scope(impl);
|
||||
return impl->Reset();
|
||||
}
|
||||
WasmInterpreter::Thread::ExceptionHandlingResult
|
||||
WasmInterpreter::Thread::RaiseException(Isolate* isolate,
|
||||
Handle<Object> exception) {
|
||||
ThreadImpl* impl = ToImpl(this);
|
||||
ThreadImpl::ReferenceStackScope stack_scope(impl);
|
||||
return impl->RaiseException(isolate, exception);
|
||||
}
|
||||
int WasmInterpreter::Thread::GetFrameCount() {
|
||||
return ToImpl(this)->GetFrameCount();
|
||||
}
|
||||
WasmValue WasmInterpreter::Thread::GetReturnValue(int index) {
|
||||
ThreadImpl* impl = ToImpl(this);
|
||||
ThreadImpl::ReferenceStackScope stack_scope(impl);
|
||||
return impl->GetReturnValue(index);
|
||||
}
|
||||
TrapReason WasmInterpreter::Thread::GetTrapReason() {
|
||||
return ToImpl(this)->GetTrapReason();
|
||||
}
|
||||
bool WasmInterpreter::Thread::PossibleNondeterminism() {
|
||||
return ToImpl(this)->PossibleNondeterminism();
|
||||
}
|
||||
uint64_t WasmInterpreter::Thread::NumInterpretedCalls() {
|
||||
return ToImpl(this)->NumInterpretedCalls();
|
||||
}
|
||||
|
||||
//============================================================================
|
||||
// The implementation details of the interpreter.
|
||||
//============================================================================
|
||||
class WasmInterpreterInternals {
|
||||
public:
|
||||
// Create a copy of the module bytes for the interpreter, since the passed
|
||||
// pointer might be invalidated after constructing the interpreter.
|
||||
const ZoneVector<uint8_t> module_bytes_;
|
||||
CodeMap codemap_;
|
||||
std::vector<ThreadImpl> threads_;
|
||||
|
||||
WasmInterpreterInternals(Zone* zone, const WasmModule* module,
|
||||
const ModuleWireBytes& wire_bytes,
|
||||
Handle<WasmInstanceObject> instance_object)
|
||||
: module_bytes_(wire_bytes.start(), wire_bytes.end(), zone),
|
||||
codemap_(module, module_bytes_.data(), zone) {
|
||||
threads_.emplace_back(zone, &codemap_, instance_object);
|
||||
}
|
||||
Isolate* isolate_;
|
||||
Handle<WasmInstanceObject> instance_object_;
|
||||
std::unique_ptr<StackValue[]> stack_;
|
||||
StackValue* stack_limit_ = nullptr; // End of allocated stack space.
|
||||
StackValue* sp_ = nullptr; // Current stack pointer.
|
||||
// The reference stack is pointed to by a {Cell} to be able to replace the
|
||||
// underlying {FixedArray} when growing the stack. This avoids having to
|
||||
// recreate or update the global handle keeping this object alive.
|
||||
Handle<Cell> reference_stack_cell_; // References are on an on-heap stack.
|
||||
ZoneVector<Frame> frames_;
|
||||
WasmInterpreter::State state_ = WasmInterpreter::STOPPED;
|
||||
TrapReason trap_reason_ = kTrapCount;
|
||||
bool possible_nondeterminism_ = false;
|
||||
uint64_t num_interpreted_calls_ = 0;
|
||||
};
|
||||
|
||||
namespace {
|
||||
@ -3939,27 +3856,44 @@ WasmInterpreter::WasmInterpreter(Isolate* isolate, const WasmModule* module,
|
||||
// used in the {unique_ptr} in the header.
|
||||
WasmInterpreter::~WasmInterpreter() = default;
|
||||
|
||||
void WasmInterpreter::Run() { internals_->threads_[0].Run(); }
|
||||
WasmInterpreter::State WasmInterpreter::state() { return internals_->state(); }
|
||||
|
||||
void WasmInterpreter::Pause() { internals_->threads_[0].Pause(); }
|
||||
|
||||
int WasmInterpreter::GetThreadCount() {
|
||||
return 1; // only one thread for now.
|
||||
void WasmInterpreter::InitFrame(const WasmFunction* function, WasmValue* args) {
|
||||
internals_->InitFrame(function, args);
|
||||
}
|
||||
|
||||
WasmInterpreter::Thread* WasmInterpreter::GetThread(int id) {
|
||||
CHECK_EQ(0, id); // only one thread for now.
|
||||
return ToThread(&internals_->threads_[id]);
|
||||
WasmInterpreter::State WasmInterpreter::Run(int num_steps) {
|
||||
return internals_->Run(num_steps);
|
||||
}
|
||||
|
||||
void WasmInterpreter::Pause() { internals_->Pause(); }
|
||||
|
||||
void WasmInterpreter::Reset() { internals_->Reset(); }
|
||||
|
||||
WasmValue WasmInterpreter::GetReturnValue(int index) {
|
||||
return internals_->GetReturnValue(index);
|
||||
}
|
||||
|
||||
TrapReason WasmInterpreter::GetTrapReason() {
|
||||
return internals_->GetTrapReason();
|
||||
}
|
||||
|
||||
bool WasmInterpreter::PossibleNondeterminism() {
|
||||
return internals_->PossibleNondeterminism();
|
||||
}
|
||||
|
||||
uint64_t WasmInterpreter::NumInterpretedCalls() {
|
||||
return internals_->NumInterpretedCalls();
|
||||
}
|
||||
|
||||
void WasmInterpreter::AddFunctionForTesting(const WasmFunction* function) {
|
||||
internals_->codemap_.AddFunction(function, nullptr, nullptr);
|
||||
internals_->codemap()->AddFunction(function, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void WasmInterpreter::SetFunctionCodeForTesting(const WasmFunction* function,
|
||||
const byte* start,
|
||||
const byte* end) {
|
||||
internals_->codemap_.SetFunctionCode(function, start, end);
|
||||
internals_->codemap()->SetFunctionCode(function, start, end);
|
||||
}
|
||||
|
||||
ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting(
|
||||
|
@ -44,7 +44,7 @@ using ControlTransferMap = ZoneMap<pc_t, ControlTransferEntry>;
|
||||
// An interpreter capable of executing WebAssembly.
|
||||
class V8_EXPORT_PRIVATE WasmInterpreter {
|
||||
public:
|
||||
// State machine for a Thread:
|
||||
// State machine for the interpreter:
|
||||
// +----------------------------------------------------------+
|
||||
// | +--------Run()/Step()---------+ |
|
||||
// V V | |
|
||||
@ -56,49 +56,7 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
|
||||
// +----------- 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 {
|
||||
public:
|
||||
// Don't instante Threads; they will be allocated as ThreadImpl in the
|
||||
// interpreter implementation.
|
||||
Thread() = delete;
|
||||
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();
|
||||
|
||||
// Raise an exception in the current activation and unwind the stack
|
||||
// accordingly. Return whether the exception was handled inside wasm:
|
||||
// - HANDLED: Activation at handler position and in {PAUSED} state.
|
||||
// - UNWOUND: Frames unwound, exception pending, and in {STOPPED} state.
|
||||
ExceptionHandlingResult RaiseException(Isolate*, Handle<Object> exception);
|
||||
|
||||
// Stack inspection and modification.
|
||||
int GetFrameCount();
|
||||
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();
|
||||
};
|
||||
enum ExceptionHandlingResult { HANDLED, UNWOUND };
|
||||
|
||||
WasmInterpreter(Isolate* isolate, const WasmModule* module,
|
||||
const ModuleWireBytes& wire_bytes,
|
||||
@ -109,14 +67,31 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
|
||||
//==========================================================================
|
||||
// Execution controls.
|
||||
//==========================================================================
|
||||
void Run();
|
||||
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();
|
||||
|
||||
//==========================================================================
|
||||
// Thread iteration and inspection.
|
||||
//==========================================================================
|
||||
int GetThreadCount();
|
||||
Thread* GetThread(int id);
|
||||
// Raise an exception in the current activation and unwind the stack
|
||||
// accordingly. Return whether the exception was handled inside wasm:
|
||||
// - HANDLED: Activation at handler position and in {PAUSED} state.
|
||||
// - UNWOUND: Frames unwound, exception pending, and in {STOPPED} state.
|
||||
ExceptionHandlingResult RaiseException(Isolate*, Handle<Object> exception);
|
||||
|
||||
// Stack inspection and modification.
|
||||
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();
|
||||
|
||||
//==========================================================================
|
||||
// Testing functionality.
|
||||
|
@ -282,28 +282,27 @@ TEST(Step_I32Mul) {
|
||||
r.Build(code, code + arraysize(code));
|
||||
|
||||
WasmInterpreter* interpreter = r.interpreter();
|
||||
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
|
||||
|
||||
FOR_UINT32_INPUTS(a) {
|
||||
for (uint32_t b = 33; b < 3000000000u; b += 1000000000u) {
|
||||
thread->Reset();
|
||||
interpreter->Reset();
|
||||
WasmValue args[] = {WasmValue(a), WasmValue(b)};
|
||||
thread->InitFrame(r.function(), args);
|
||||
interpreter->InitFrame(r.function(), args);
|
||||
|
||||
// Run instructions one by one.
|
||||
for (int i = 0; i < kTraceLength - 1; i++) {
|
||||
thread->Step();
|
||||
// Check the thread stopped.
|
||||
CHECK_EQ(WasmInterpreter::PAUSED, thread->state());
|
||||
interpreter->Step();
|
||||
// Check the interpreter stopped.
|
||||
CHECK_EQ(WasmInterpreter::PAUSED, interpreter->state());
|
||||
}
|
||||
|
||||
// Run last instruction.
|
||||
thread->Step();
|
||||
interpreter->Step();
|
||||
|
||||
// Check the thread finished with the right value.
|
||||
CHECK_EQ(WasmInterpreter::FINISHED, thread->state());
|
||||
// Check the interpreter finished with the right value.
|
||||
CHECK_EQ(WasmInterpreter::FINISHED, interpreter->state());
|
||||
uint32_t expected = (a) * (b);
|
||||
CHECK_EQ(expected, thread->GetReturnValue().to<uint32_t>());
|
||||
CHECK_EQ(expected, interpreter->GetReturnValue().to<uint32_t>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -510,17 +510,16 @@ class WasmRunner : public WasmRunnerBase {
|
||||
}
|
||||
|
||||
ReturnType CallInterpreter(ParamTypes... p) {
|
||||
WasmInterpreter::Thread* thread = interpreter()->GetThread(0);
|
||||
thread->Reset();
|
||||
interpreter()->Reset();
|
||||
std::array<WasmValue, sizeof...(p)> args{{WasmValue(p)...}};
|
||||
thread->InitFrame(function(), args.data());
|
||||
thread->Run();
|
||||
CHECK_GT(thread->NumInterpretedCalls(), 0);
|
||||
if (thread->state() == WasmInterpreter::FINISHED) {
|
||||
WasmValue val = thread->GetReturnValue();
|
||||
possible_nondeterminism_ |= thread->PossibleNondeterminism();
|
||||
interpreter()->InitFrame(function(), args.data());
|
||||
interpreter()->Run();
|
||||
CHECK_GT(interpreter()->NumInterpretedCalls(), 0);
|
||||
if (interpreter()->state() == WasmInterpreter::FINISHED) {
|
||||
WasmValue val = interpreter()->GetReturnValue();
|
||||
possible_nondeterminism_ |= interpreter()->PossibleNondeterminism();
|
||||
return val.to<ReturnType>();
|
||||
} else if (thread->state() == WasmInterpreter::TRAPPED) {
|
||||
} else if (interpreter()->state() == WasmInterpreter::TRAPPED) {
|
||||
// TODO(titzer): return the correct trap code
|
||||
int64_t result = 0xDEADBEEFDEADBEEF;
|
||||
return static_cast<ReturnType>(result);
|
||||
@ -559,7 +558,7 @@ class WasmRunner : public WasmRunnerBase {
|
||||
}
|
||||
|
||||
if (builder_.interpret()) {
|
||||
CHECK_GT(builder_.interpreter()->GetThread(0)->NumInterpretedCalls(), 0);
|
||||
CHECK_GT(builder_.interpreter()->NumInterpretedCalls(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,12 +127,11 @@ bool InterpretWasmModuleForTesting(Isolate* isolate,
|
||||
Zone zone(isolate->allocator(), ZONE_NAME);
|
||||
|
||||
WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
|
||||
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
|
||||
thread->Reset();
|
||||
interpreter->Reset();
|
||||
|
||||
thread->InitFrame(&instance->module()->functions[function_index],
|
||||
arguments.get());
|
||||
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
|
||||
interpreter->InitFrame(&instance->module()->functions[function_index],
|
||||
arguments.get());
|
||||
WasmInterpreter::State interpreter_result = interpreter->Run(kMaxNumSteps);
|
||||
|
||||
if (isolate->has_pending_exception()) {
|
||||
// Stack overflow during interpretation.
|
||||
@ -199,25 +198,25 @@ WasmInterpretationResult InterpretWasmModule(
|
||||
v8::internal::HandleScope scope(isolate);
|
||||
|
||||
WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
|
||||
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
|
||||
thread->Reset();
|
||||
interpreter->Reset();
|
||||
|
||||
thread->InitFrame(&(instance->module()->functions[function_index]), args);
|
||||
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps);
|
||||
interpreter->InitFrame(&instance->module()->functions[function_index], args);
|
||||
WasmInterpreter::State interpreter_result = interpreter->Run(kMaxNumSteps);
|
||||
|
||||
bool stack_overflow = isolate->has_pending_exception();
|
||||
isolate->clear_pending_exception();
|
||||
|
||||
if (stack_overflow) return WasmInterpretationResult::Stopped();
|
||||
|
||||
if (thread->state() == WasmInterpreter::TRAPPED) {
|
||||
return WasmInterpretationResult::Trapped(thread->PossibleNondeterminism());
|
||||
if (interpreter->state() == WasmInterpreter::TRAPPED) {
|
||||
return WasmInterpretationResult::Trapped(
|
||||
interpreter->PossibleNondeterminism());
|
||||
}
|
||||
|
||||
if (interpreter_result == WasmInterpreter::FINISHED) {
|
||||
return WasmInterpretationResult::Finished(
|
||||
thread->GetReturnValue().to<int32_t>(),
|
||||
thread->PossibleNondeterminism());
|
||||
interpreter->GetReturnValue().to<int32_t>(),
|
||||
interpreter->PossibleNondeterminism());
|
||||
}
|
||||
|
||||
return WasmInterpretationResult::Stopped();
|
||||
|
Loading…
Reference in New Issue
Block a user