[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());
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;

View File

@ -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(

View File

@ -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.

View File

@ -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>());
}
}
}

View File

@ -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);
}
}

View File

@ -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();