[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:
Clemens Backes 2020-06-05 13:05:04 +02:00 committed by Commit Bot
parent b644fac5a9
commit 47e501e169
6 changed files with 159 additions and 254 deletions

View File

@ -191,13 +191,12 @@ class InterpreterHandle {
DCHECK_EQ(sig->return_count(), return_values.size()); DCHECK_EQ(sig->return_count(), return_values.size());
WasmCodeRefScope code_ref_scope; WasmCodeRefScope code_ref_scope;
WasmInterpreter::Thread* thread = interpreter_.GetThread(0); interpreter_.InitFrame(&module()->functions[func_index],
thread->InitFrame(&module()->functions[func_index], argument_values.begin());
argument_values.begin());
bool finished = false; bool finished = false;
while (!finished) { while (!finished) {
// TODO(clemensb): Add occasional StackChecks. // TODO(clemensb): Add occasional StackChecks.
WasmInterpreter::State state = thread->Run(); WasmInterpreter::State state = interpreter_.Run();
switch (state) { switch (state) {
case WasmInterpreter::State::PAUSED: case WasmInterpreter::State::PAUSED:
UNREACHABLE(); UNREACHABLE();
@ -207,16 +206,16 @@ class InterpreterHandle {
break; break;
case WasmInterpreter::State::TRAPPED: { case WasmInterpreter::State::TRAPPED: {
MessageTemplate message_id = MessageTemplate message_id =
WasmOpcodes::TrapReasonToMessageId(thread->GetTrapReason()); WasmOpcodes::TrapReasonToMessageId(interpreter_.GetTrapReason());
Handle<JSObject> exception = Handle<JSObject> exception =
isolate_->factory()->NewWasmRuntimeError(message_id); isolate_->factory()->NewWasmRuntimeError(message_id);
JSObject::AddProperty(isolate_, exception, JSObject::AddProperty(isolate_, exception,
isolate_->factory()->wasm_uncatchable_symbol(), isolate_->factory()->wasm_uncatchable_symbol(),
isolate_->factory()->true_value(), NONE); isolate_->factory()->true_value(), NONE);
auto result = thread->RaiseException(isolate_, exception); auto result = interpreter_.RaiseException(isolate_, exception);
if (result == WasmInterpreter::Thread::HANDLED) break; if (result == WasmInterpreter::HANDLED) break;
// If no local handler was found, we fall-thru to {STOPPED}. // 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; V8_FALLTHROUGH;
} }
case WasmInterpreter::State::STOPPED: case WasmInterpreter::State::STOPPED:
@ -239,7 +238,7 @@ class InterpreterHandle {
#endif #endif
DCHECK_GE(max_count, sig->return_count()); DCHECK_GE(max_count, sig->return_count());
for (unsigned i = 0; i < sig->return_count(); ++i) { for (unsigned i = 0; i < sig->return_count(); ++i) {
return_values[i] = thread->GetReturnValue(i); return_values[i] = interpreter_.GetReturnValue(i);
} }
return true; return true;

View File

@ -1102,9 +1102,20 @@ V8_INLINE bool has_nondeterminism<double>(double val) {
} // namespace } // namespace
// Responsible for executing code directly. //============================================================================
class ThreadImpl { // The implementation details of the interpreter.
//============================================================================
class WasmInterpreterInternals {
public: 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 {ReferenceStackScope} sets up the reference stack in the interpreter.
// The handle to the reference stack has to be re-initialized everytime we // The handle to the reference stack has to be re-initialized everytime we
// call into the interpreter because there is no HandleScope that could // 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. // reference stack and thereby transitively keeps the interpreter alive.
class ReferenceStackScope { class ReferenceStackScope {
public: 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 // The reference stack is already initialized, we don't have to do
// anything. // anything.
if (!impl_->reference_stack_cell_.is_null()) return; if (!impl_->reference_stack_cell_.is_null()) return;
@ -1131,26 +1142,16 @@ class ThreadImpl {
} }
private: private:
ThreadImpl* impl_; WasmInterpreterInternals* impl_;
bool do_reset_stack_ = false; 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_; } WasmInterpreter::State state() { return state_; }
void InitFrame(const WasmFunction* function, WasmValue* args) { void InitFrame(const WasmFunction* function, WasmValue* args) {
ReferenceStackScope stack_scope(this);
DCHECK(frames_.empty()); DCHECK(frames_.empty());
InterpreterCode* code = codemap()->GetCode(function); InterpreterCode* code = codemap_.GetCode(function);
size_t num_params = function->sig->parameter_count(); size_t num_params = function->sig->parameter_count();
EnsureStackSpace(num_params); EnsureStackSpace(num_params);
Push(args, num_params); Push(args, num_params);
@ -1158,6 +1159,7 @@ class ThreadImpl {
} }
WasmInterpreter::State Run(int num_steps = -1) { WasmInterpreter::State Run(int num_steps = -1) {
ReferenceStackScope stack_scope(this);
DCHECK(state_ == WasmInterpreter::STOPPED || DCHECK(state_ == WasmInterpreter::STOPPED ||
state_ == WasmInterpreter::PAUSED); state_ == WasmInterpreter::PAUSED);
DCHECK(num_steps == -1 || num_steps > 0); DCHECK(num_steps == -1 || num_steps > 0);
@ -1178,6 +1180,7 @@ class ThreadImpl {
void Pause() { UNIMPLEMENTED(); } void Pause() { UNIMPLEMENTED(); }
void Reset() { void Reset() {
ReferenceStackScope stack_scope(this);
TRACE("----- RESET -----\n"); TRACE("----- RESET -----\n");
ResetStack(0); ResetStack(0);
frames_.clear(); frames_.clear();
@ -1186,12 +1189,8 @@ class ThreadImpl {
possible_nondeterminism_ = false; possible_nondeterminism_ = false;
} }
int GetFrameCount() {
DCHECK_GE(kMaxInt, frames_.size());
return static_cast<int>(frames_.size());
}
WasmValue GetReturnValue(uint32_t index) { WasmValue GetReturnValue(uint32_t index) {
ReferenceStackScope stack_scope(this);
if (state_ == WasmInterpreter::TRAPPED) return WasmValue(0xDEADBEEF); if (state_ == WasmInterpreter::TRAPPED) return WasmValue(0xDEADBEEF);
DCHECK_EQ(WasmInterpreter::FINISHED, state_); DCHECK_EQ(WasmInterpreter::FINISHED, state_);
return GetStackValue(index); return GetStackValue(index);
@ -1215,23 +1214,27 @@ class ThreadImpl {
Handle<Cell> reference_stack_cell() const { return reference_stack_cell_; } Handle<Cell> reference_stack_cell() const { return reference_stack_cell_; }
WasmInterpreter::Thread::ExceptionHandlingResult RaiseException( WasmInterpreter::ExceptionHandlingResult RaiseException(
Isolate* isolate, Handle<Object> exception) { Isolate* isolate, Handle<Object> exception) {
ReferenceStackScope stack_scope(this);
DCHECK_EQ(WasmInterpreter::TRAPPED, state_); DCHECK_EQ(WasmInterpreter::TRAPPED, state_);
isolate->Throw(*exception); // Will check that none is pending. 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_); DCHECK_EQ(WasmInterpreter::STOPPED, state_);
return WasmInterpreter::Thread::UNWOUND; return WasmInterpreter::UNWOUND;
} }
state_ = WasmInterpreter::PAUSED; state_ = WasmInterpreter::PAUSED;
return WasmInterpreter::Thread::HANDLED; return WasmInterpreter::HANDLED;
} }
CodeMap* codemap() { return &codemap_; }
private: private:
friend class ReferenceStackScope;
// Handle a thrown exception. Returns whether the exception was handled inside // Handle a thrown exception. Returns whether the exception was handled inside
// of wasm. Unwinds the interpreted stack accordingly. // of wasm. Unwinds the interpreted stack accordingly.
WasmInterpreter::Thread::ExceptionHandlingResult HandleException( WasmInterpreter::ExceptionHandlingResult HandleException(Isolate* isolate) {
Isolate* isolate) {
DCHECK(isolate->has_pending_exception()); DCHECK(isolate->has_pending_exception());
bool catchable = bool catchable =
isolate->is_catchable_by_wasm(isolate->pending_exception()); isolate->is_catchable_by_wasm(isolate->pending_exception());
@ -1245,7 +1248,7 @@ class ThreadImpl {
frame.pc += JumpToHandlerDelta(code, frame.pc); frame.pc += JumpToHandlerDelta(code, frame.pc);
TRACE(" => handler #%zu (#%u @%zu)\n", frames_.size() - 1, TRACE(" => handler #%zu (#%u @%zu)\n", frames_.size() - 1,
code->function->func_index, frame.pc); code->function->func_index, frame.pc);
return WasmInterpreter::Thread::HANDLED; return WasmInterpreter::HANDLED;
} }
TRACE(" => drop frame #%zu (#%u @%zu)\n", frames_.size() - 1, TRACE(" => drop frame #%zu (#%u @%zu)\n", frames_.size() - 1,
code->function->func_index, frame.pc); code->function->func_index, frame.pc);
@ -1256,7 +1259,7 @@ class ThreadImpl {
DCHECK(frames_.empty()); DCHECK(frames_.empty());
DCHECK_EQ(sp_, stack_.get()); DCHECK_EQ(sp_, stack_.get());
state_ = WasmInterpreter::STOPPED; state_ = WasmInterpreter::STOPPED;
return WasmInterpreter::Thread::UNWOUND; return WasmInterpreter::UNWOUND;
} }
// Entries on the stack of functions being evaluated. // Entries on the stack of functions being evaluated.
@ -1279,68 +1282,51 @@ class ThreadImpl {
class StackValue { class StackValue {
public: public:
StackValue() = default; // Only needed for resizing the stack. 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()) { if (IsReferenceValue()) {
value_ = WasmValue(Handle<Object>::null()); value_ = WasmValue(Handle<Object>::null());
int ref_index = static_cast<int>(index); 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_; if (!IsReferenceValue()) return value_;
DCHECK(value_.to_anyref().is_null()); DCHECK(value_.to_anyref().is_null());
int ref_index = static_cast<int>(index); int ref_index = static_cast<int>(index);
Isolate* isolate = thread->isolate_; Isolate* isolate = impl->isolate_;
Handle<Object> ref(thread->reference_stack().get(ref_index), isolate); Handle<Object> ref(impl->reference_stack().get(ref_index), isolate);
DCHECK(!ref->IsTheHole(isolate)); DCHECK(!ref->IsTheHole(isolate));
return WasmValue(ref); return WasmValue(ref);
} }
bool IsReferenceValue() const { return value_.type() == kWasmAnyRef; } bool IsReferenceValue() const { return value_.type() == kWasmAnyRef; }
void ClearValue(ThreadImpl* thread, sp_t index) { void ClearValue(WasmInterpreterInternals* impl, sp_t index) {
if (!IsReferenceValue()) return; if (!IsReferenceValue()) return;
int ref_index = static_cast<int>(index); int ref_index = static_cast<int>(index);
Isolate* isolate = thread->isolate_; Isolate* isolate = impl->isolate_;
thread->reference_stack().set_the_hole(isolate, ref_index); 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); 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); int ref_index = static_cast<int>(index);
Isolate* isolate = thread->isolate_; Isolate* isolate = impl->isolate_;
return thread->reference_stack().is_the_hole(isolate, ref_index); return impl->reference_stack().is_the_hole(isolate, ref_index);
} }
private: private:
WasmValue value_; WasmValue value_;
}; };
friend class ReferenceStackScope; const WasmModule* module() const { return codemap_.module(); }
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(); }
FixedArray reference_stack() const { FixedArray reference_stack() const {
return FixedArray::cast(reference_stack_cell_->value()); 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. // it to 0 here such that we report the same position as in compiled code.
frames_.back().pc = 0; frames_.back().pc = 0;
isolate_->StackOverflow(); isolate_->StackOverflow();
return HandleException(isolate_) == WasmInterpreter::Thread::HANDLED; return HandleException(isolate_) == WasmInterpreter::HANDLED;
} }
void EncodeI32ExceptionValue(Handle<FixedArray> encoded_values, void EncodeI32ExceptionValue(Handle<FixedArray> encoded_values,
@ -2863,14 +2849,14 @@ class ThreadImpl {
Drop(static_cast<int>(sig->parameter_count())); Drop(static_cast<int>(sig->parameter_count()));
// Now that the exception is ready, set it as pending. // Now that the exception is ready, set it as pending.
isolate_->Throw(*exception_object); 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 // Throw a given existing exception. Returns true if the exception is being
// handled locally by the interpreter, false otherwise (interpreter exits). // handled locally by the interpreter, false otherwise (interpreter exits).
bool DoRethrowException(WasmValue exception) { bool DoRethrowException(WasmValue exception) {
isolate_->ReThrow(*exception.to_anyref()); 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 // 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 // Compute the stack effect of this opcode, and verify later that the
// stack was modified accordingly. // stack was modified accordingly.
std::pair<uint32_t, uint32_t> stack_effect = 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); code->start + pc, code->end);
sp_t expected_new_stack_height = sp_t expected_new_stack_height =
StackHeight() - stack_effect.first + stack_effect.second; StackHeight() - stack_effect.first + stack_effect.second;
@ -3232,7 +3218,7 @@ class ThreadImpl {
case kExprCallFunction: { case kExprCallFunction: {
CallFunctionImmediate<Decoder::kNoValidate> imm(&decoder, CallFunctionImmediate<Decoder::kNoValidate> imm(&decoder,
code->at(pc)); code->at(pc));
InterpreterCode* target = codemap()->GetCode(imm.index); InterpreterCode* target = codemap_.GetCode(imm.index);
CHECK(!target->function->imported); CHECK(!target->function->imported);
// Execute an internal call. // Execute an internal call.
if (!DoCall(&decoder, target, &pc, &limit)) return; if (!DoCall(&decoder, target, &pc, &limit)) return;
@ -3264,7 +3250,7 @@ class ThreadImpl {
case kExprReturnCall: { case kExprReturnCall: {
CallFunctionImmediate<Decoder::kNoValidate> imm(&decoder, CallFunctionImmediate<Decoder::kNoValidate> imm(&decoder,
code->at(pc)); code->at(pc));
InterpreterCode* target = codemap()->GetCode(imm.index); InterpreterCode* target = codemap_.GetCode(imm.index);
CHECK(!target->function->imported); CHECK(!target->function->imported);
// Enter internal found function. // Enter internal found function.
@ -3815,96 +3801,27 @@ class ThreadImpl {
instance_object_.is_identical_to(object_ref)); instance_object_.is_identical_to(object_ref));
DCHECK_EQ(WasmCode::kFunction, code->kind()); 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 // Create a copy of the module bytes for the interpreter, since the passed
// pointer might be invalidated after constructing the interpreter. // pointer might be invalidated after constructing the interpreter.
const ZoneVector<uint8_t> module_bytes_; const ZoneVector<uint8_t> module_bytes_;
CodeMap codemap_; CodeMap codemap_;
std::vector<ThreadImpl> threads_; Isolate* isolate_;
Handle<WasmInstanceObject> instance_object_;
WasmInterpreterInternals(Zone* zone, const WasmModule* module, std::unique_ptr<StackValue[]> stack_;
const ModuleWireBytes& wire_bytes, StackValue* stack_limit_ = nullptr; // End of allocated stack space.
Handle<WasmInstanceObject> instance_object) StackValue* sp_ = nullptr; // Current stack pointer.
: module_bytes_(wire_bytes.start(), wire_bytes.end(), zone), // The reference stack is pointed to by a {Cell} to be able to replace the
codemap_(module, module_bytes_.data(), zone) { // underlying {FixedArray} when growing the stack. This avoids having to
threads_.emplace_back(zone, &codemap_, instance_object); // 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 { namespace {
@ -3939,27 +3856,44 @@ WasmInterpreter::WasmInterpreter(Isolate* isolate, const WasmModule* module,
// used in the {unique_ptr} in the header. // used in the {unique_ptr} in the header.
WasmInterpreter::~WasmInterpreter() = default; WasmInterpreter::~WasmInterpreter() = default;
void WasmInterpreter::Run() { internals_->threads_[0].Run(); } WasmInterpreter::State WasmInterpreter::state() { return internals_->state(); }
void WasmInterpreter::Pause() { internals_->threads_[0].Pause(); } void WasmInterpreter::InitFrame(const WasmFunction* function, WasmValue* args) {
internals_->InitFrame(function, args);
int WasmInterpreter::GetThreadCount() {
return 1; // only one thread for now.
} }
WasmInterpreter::Thread* WasmInterpreter::GetThread(int id) { WasmInterpreter::State WasmInterpreter::Run(int num_steps) {
CHECK_EQ(0, id); // only one thread for now. return internals_->Run(num_steps);
return ToThread(&internals_->threads_[id]); }
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) { void WasmInterpreter::AddFunctionForTesting(const WasmFunction* function) {
internals_->codemap_.AddFunction(function, nullptr, nullptr); internals_->codemap()->AddFunction(function, nullptr, nullptr);
} }
void WasmInterpreter::SetFunctionCodeForTesting(const WasmFunction* function, void WasmInterpreter::SetFunctionCodeForTesting(const WasmFunction* function,
const byte* start, const byte* start,
const byte* end) { const byte* end) {
internals_->codemap_.SetFunctionCode(function, start, end); internals_->codemap()->SetFunctionCode(function, start, end);
} }
ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting( ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting(

View File

@ -44,7 +44,7 @@ using ControlTransferMap = ZoneMap<pc_t, ControlTransferEntry>;
// An interpreter capable of executing WebAssembly. // An interpreter capable of executing WebAssembly.
class V8_EXPORT_PRIVATE WasmInterpreter { class V8_EXPORT_PRIVATE WasmInterpreter {
public: public:
// State machine for a Thread: // State machine for the interpreter:
// +----------------------------------------------------------+ // +----------------------------------------------------------+
// | +--------Run()/Step()---------+ | // | +--------Run()/Step()---------+ |
// V V | | // V V | |
@ -56,49 +56,7 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
// +----------- Finish -------------> FINISHED // +----------- Finish -------------> FINISHED
enum State { STOPPED, RUNNING, PAUSED, FINISHED, TRAPPED }; enum State { STOPPED, RUNNING, PAUSED, FINISHED, TRAPPED };
// Tells a thread to pause after certain instructions. enum ExceptionHandlingResult { HANDLED, UNWOUND };
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();
};
WasmInterpreter(Isolate* isolate, const WasmModule* module, WasmInterpreter(Isolate* isolate, const WasmModule* module,
const ModuleWireBytes& wire_bytes, const ModuleWireBytes& wire_bytes,
@ -109,14 +67,31 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
//========================================================================== //==========================================================================
// Execution controls. // 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 Pause();
void Reset();
//========================================================================== // Raise an exception in the current activation and unwind the stack
// Thread iteration and inspection. // accordingly. Return whether the exception was handled inside wasm:
//========================================================================== // - HANDLED: Activation at handler position and in {PAUSED} state.
int GetThreadCount(); // - UNWOUND: Frames unwound, exception pending, and in {STOPPED} state.
Thread* GetThread(int id); 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. // Testing functionality.

View File

@ -282,28 +282,27 @@ TEST(Step_I32Mul) {
r.Build(code, code + arraysize(code)); r.Build(code, code + arraysize(code));
WasmInterpreter* interpreter = r.interpreter(); WasmInterpreter* interpreter = r.interpreter();
WasmInterpreter::Thread* thread = interpreter->GetThread(0);
FOR_UINT32_INPUTS(a) { FOR_UINT32_INPUTS(a) {
for (uint32_t b = 33; b < 3000000000u; b += 1000000000u) { for (uint32_t b = 33; b < 3000000000u; b += 1000000000u) {
thread->Reset(); interpreter->Reset();
WasmValue args[] = {WasmValue(a), WasmValue(b)}; WasmValue args[] = {WasmValue(a), WasmValue(b)};
thread->InitFrame(r.function(), args); interpreter->InitFrame(r.function(), args);
// Run instructions one by one. // Run instructions one by one.
for (int i = 0; i < kTraceLength - 1; i++) { for (int i = 0; i < kTraceLength - 1; i++) {
thread->Step(); interpreter->Step();
// Check the thread stopped. // Check the interpreter stopped.
CHECK_EQ(WasmInterpreter::PAUSED, thread->state()); CHECK_EQ(WasmInterpreter::PAUSED, interpreter->state());
} }
// Run last instruction. // Run last instruction.
thread->Step(); interpreter->Step();
// Check the thread finished with the right value. // Check the interpreter finished with the right value.
CHECK_EQ(WasmInterpreter::FINISHED, thread->state()); CHECK_EQ(WasmInterpreter::FINISHED, interpreter->state());
uint32_t expected = (a) * (b); uint32_t expected = (a) * (b);
CHECK_EQ(expected, thread->GetReturnValue().to<uint32_t>()); CHECK_EQ(expected, interpreter->GetReturnValue().to<uint32_t>());
} }
} }
} }

View File

@ -510,17 +510,16 @@ class WasmRunner : public WasmRunnerBase {
} }
ReturnType CallInterpreter(ParamTypes... p) { ReturnType CallInterpreter(ParamTypes... p) {
WasmInterpreter::Thread* thread = interpreter()->GetThread(0); interpreter()->Reset();
thread->Reset();
std::array<WasmValue, sizeof...(p)> args{{WasmValue(p)...}}; std::array<WasmValue, sizeof...(p)> args{{WasmValue(p)...}};
thread->InitFrame(function(), args.data()); interpreter()->InitFrame(function(), args.data());
thread->Run(); interpreter()->Run();
CHECK_GT(thread->NumInterpretedCalls(), 0); CHECK_GT(interpreter()->NumInterpretedCalls(), 0);
if (thread->state() == WasmInterpreter::FINISHED) { if (interpreter()->state() == WasmInterpreter::FINISHED) {
WasmValue val = thread->GetReturnValue(); WasmValue val = interpreter()->GetReturnValue();
possible_nondeterminism_ |= thread->PossibleNondeterminism(); possible_nondeterminism_ |= interpreter()->PossibleNondeterminism();
return val.to<ReturnType>(); return val.to<ReturnType>();
} else if (thread->state() == WasmInterpreter::TRAPPED) { } else if (interpreter()->state() == WasmInterpreter::TRAPPED) {
// TODO(titzer): return the correct trap code // TODO(titzer): return the correct trap code
int64_t result = 0xDEADBEEFDEADBEEF; int64_t result = 0xDEADBEEFDEADBEEF;
return static_cast<ReturnType>(result); return static_cast<ReturnType>(result);
@ -559,7 +558,7 @@ class WasmRunner : public WasmRunnerBase {
} }
if (builder_.interpret()) { if (builder_.interpret()) {
CHECK_GT(builder_.interpreter()->GetThread(0)->NumInterpretedCalls(), 0); CHECK_GT(builder_.interpreter()->NumInterpretedCalls(), 0);
} }
} }

View File

@ -127,12 +127,11 @@ bool InterpretWasmModuleForTesting(Isolate* isolate,
Zone zone(isolate->allocator(), ZONE_NAME); Zone zone(isolate->allocator(), ZONE_NAME);
WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance); WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
WasmInterpreter::Thread* thread = interpreter->GetThread(0); interpreter->Reset();
thread->Reset();
thread->InitFrame(&instance->module()->functions[function_index], interpreter->InitFrame(&instance->module()->functions[function_index],
arguments.get()); arguments.get());
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps); WasmInterpreter::State interpreter_result = interpreter->Run(kMaxNumSteps);
if (isolate->has_pending_exception()) { if (isolate->has_pending_exception()) {
// Stack overflow during interpretation. // Stack overflow during interpretation.
@ -199,25 +198,25 @@ WasmInterpretationResult InterpretWasmModule(
v8::internal::HandleScope scope(isolate); v8::internal::HandleScope scope(isolate);
WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance); WasmInterpreter* interpreter = WasmDebugInfo::SetupForTesting(instance);
WasmInterpreter::Thread* thread = interpreter->GetThread(0); interpreter->Reset();
thread->Reset();
thread->InitFrame(&(instance->module()->functions[function_index]), args); interpreter->InitFrame(&instance->module()->functions[function_index], args);
WasmInterpreter::State interpreter_result = thread->Run(kMaxNumSteps); WasmInterpreter::State interpreter_result = interpreter->Run(kMaxNumSteps);
bool stack_overflow = isolate->has_pending_exception(); bool stack_overflow = isolate->has_pending_exception();
isolate->clear_pending_exception(); isolate->clear_pending_exception();
if (stack_overflow) return WasmInterpretationResult::Stopped(); if (stack_overflow) return WasmInterpretationResult::Stopped();
if (thread->state() == WasmInterpreter::TRAPPED) { if (interpreter->state() == WasmInterpreter::TRAPPED) {
return WasmInterpretationResult::Trapped(thread->PossibleNondeterminism()); return WasmInterpretationResult::Trapped(
interpreter->PossibleNondeterminism());
} }
if (interpreter_result == WasmInterpreter::FINISHED) { if (interpreter_result == WasmInterpreter::FINISHED) {
return WasmInterpretationResult::Finished( return WasmInterpretationResult::Finished(
thread->GetReturnValue().to<int32_t>(), interpreter->GetReturnValue().to<int32_t>(),
thread->PossibleNondeterminism()); interpreter->PossibleNondeterminism());
} }
return WasmInterpretationResult::Stopped(); return WasmInterpretationResult::Stopped();