summaryrefslogtreecommitdiff
path: root/deps/v8/src/wasm/wasm-interpreter.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/wasm/wasm-interpreter.cc')
-rw-r--r--deps/v8/src/wasm/wasm-interpreter.cc765
1 files changed, 453 insertions, 312 deletions
diff --git a/deps/v8/src/wasm/wasm-interpreter.cc b/deps/v8/src/wasm/wasm-interpreter.cc
index 66e4172850..d344d1fae4 100644
--- a/deps/v8/src/wasm/wasm-interpreter.cc
+++ b/deps/v8/src/wasm/wasm-interpreter.cc
@@ -601,56 +601,15 @@ inline int32_t ExecuteGrowMemory(uint32_t delta_pages,
DCHECK_EQ(0, instance->mem_size % WasmModule::kPageSize);
uint32_t old_pages = instance->mem_size / WasmModule::kPageSize;
- // If an instance is set, execute GrowMemory on the instance. This will also
- // update the WasmInstance struct used here.
- if (!instance_obj.is_null()) {
- Isolate* isolate = instance_obj.ToHandleChecked()->GetIsolate();
- int32_t ret = WasmInstanceObject::GrowMemory(
- isolate, instance_obj.ToHandleChecked(), delta_pages);
- // Some sanity checks.
- DCHECK_EQ(ret == -1 ? old_pages : old_pages + delta_pages,
- instance->mem_size / WasmModule::kPageSize);
- DCHECK(ret == -1 || static_cast<uint32_t>(ret) == old_pages);
- return ret;
- }
-
- // TODO(ahaas): Move memory allocation to wasm-module.cc for better
- // encapsulation.
- if (delta_pages > FLAG_wasm_max_mem_pages ||
- delta_pages > instance->module->max_mem_pages) {
- return -1;
- }
-
- uint32_t new_pages = old_pages + delta_pages;
- if (new_pages > FLAG_wasm_max_mem_pages ||
- new_pages > instance->module->max_mem_pages) {
- return -1;
- }
-
- byte* new_mem_start;
- if (instance->mem_size == 0) {
- // TODO(gdeepti): Fix bounds check to take into account size of memtype.
- new_mem_start = static_cast<byte*>(
- calloc(new_pages * WasmModule::kPageSize, sizeof(byte)));
- if (!new_mem_start) return -1;
- } else {
- DCHECK_NOT_NULL(instance->mem_start);
- if (EnableGuardRegions()) {
- v8::base::OS::Unprotect(instance->mem_start,
- new_pages * WasmModule::kPageSize);
- new_mem_start = instance->mem_start;
- } else {
- new_mem_start = static_cast<byte*>(
- realloc(instance->mem_start, new_pages * WasmModule::kPageSize));
- if (!new_mem_start) return -1;
- }
- // Zero initializing uninitialized memory from realloc
- memset(new_mem_start + old_pages * WasmModule::kPageSize, 0,
- delta_pages * WasmModule::kPageSize);
- }
- instance->mem_start = new_mem_start;
- instance->mem_size = new_pages * WasmModule::kPageSize;
- return static_cast<int32_t>(old_pages);
+ Isolate* isolate = instance_obj.ToHandleChecked()->GetIsolate();
+ int32_t ret = WasmInstanceObject::GrowMemory(
+ isolate, instance_obj.ToHandleChecked(), delta_pages);
+ // Some sanity checks.
+ DCHECK_EQ(ret == -1 ? old_pages : old_pages + delta_pages,
+ instance->mem_size / WasmModule::kPageSize);
+ DCHECK(ret == -1 || static_cast<uint32_t>(ret) == old_pages);
+ USE(old_pages);
+ return ret;
}
enum InternalOpcode {
@@ -694,45 +653,84 @@ Handle<HeapObject> UnwrapWasmToJSWrapper(Isolate* isolate,
return Handle<HeapObject>::null();
}
+class SideTable;
+
+// Code and metadata needed to execute a function.
+struct InterpreterCode {
+ const WasmFunction* function; // wasm function
+ BodyLocalDecls locals; // local declarations
+ const byte* orig_start; // start of original code
+ const byte* orig_end; // end of original code
+ byte* start; // start of (maybe altered) code
+ byte* end; // end of (maybe altered) code
+ SideTable* side_table; // precomputed side table for control flow.
+
+ const byte* at(pc_t pc) { return start + pc; }
+};
+
// A helper class to compute the control transfers for each bytecode offset.
// Control transfers allow Br, BrIf, BrTable, If, Else, and End bytecodes to
// be directly executed without the need to dynamically track blocks.
-class ControlTransfers : public ZoneObject {
+class SideTable : public ZoneObject {
public:
ControlTransferMap map_;
+ uint32_t max_stack_height_;
+
+ SideTable(Zone* zone, const WasmModule* module, InterpreterCode* code)
+ : map_(zone), max_stack_height_(0) {
+ // Create a zone for all temporary objects.
+ Zone control_transfer_zone(zone->allocator(), ZONE_NAME);
- ControlTransfers(Zone* zone, BodyLocalDecls* locals, const byte* start,
- const byte* end)
- : map_(zone) {
// Represents a control flow label.
- struct CLabel : public ZoneObject {
+ class CLabel : public ZoneObject {
+ explicit CLabel(Zone* zone, uint32_t target_stack_height, uint32_t arity)
+ : target(nullptr),
+ target_stack_height(target_stack_height),
+ arity(arity),
+ refs(zone) {}
+
+ public:
+ struct Ref {
+ const byte* from_pc;
+ const uint32_t stack_height;
+ };
const byte* target;
- ZoneVector<const byte*> refs;
+ uint32_t target_stack_height;
+ // Arity when branching to this label.
+ const uint32_t arity;
+ ZoneVector<Ref> refs;
- explicit CLabel(Zone* zone) : target(nullptr), refs(zone) {}
+ static CLabel* New(Zone* zone, uint32_t stack_height, uint32_t arity) {
+ return new (zone) CLabel(zone, stack_height, arity);
+ }
// Bind this label to the given PC.
- void Bind(ControlTransferMap* map, const byte* start, const byte* pc) {
+ void Bind(const byte* pc) {
DCHECK_NULL(target);
target = pc;
- for (auto from_pc : refs) {
- auto pcdiff = static_cast<pcdiff_t>(target - from_pc);
- size_t offset = static_cast<size_t>(from_pc - start);
- (*map)[offset] = pcdiff;
- }
}
// Reference this label from the given location.
- void Ref(ControlTransferMap* map, const byte* start,
- const byte* from_pc) {
- if (target) {
- // Target being bound before a reference means this is a loop.
- DCHECK_EQ(kExprLoop, *target);
- auto pcdiff = static_cast<pcdiff_t>(target - from_pc);
- size_t offset = static_cast<size_t>(from_pc - start);
- (*map)[offset] = pcdiff;
- } else {
- refs.push_back(from_pc);
+ void Ref(const byte* from_pc, uint32_t stack_height) {
+ // Target being bound before a reference means this is a loop.
+ DCHECK_IMPLIES(target, *target == kExprLoop);
+ refs.push_back({from_pc, stack_height});
+ }
+
+ void Finish(ControlTransferMap* map, const byte* start) {
+ DCHECK_NOT_NULL(target);
+ for (auto ref : refs) {
+ size_t offset = static_cast<size_t>(ref.from_pc - start);
+ auto pcdiff = static_cast<pcdiff_t>(target - ref.from_pc);
+ DCHECK_GE(ref.stack_height, target_stack_height);
+ spdiff_t spdiff =
+ static_cast<spdiff_t>(ref.stack_height - target_stack_height);
+ TRACE("control transfer @%zu: Δpc %d, stack %u->%u = -%u\n", offset,
+ pcdiff, ref.stack_height, target_stack_height, spdiff);
+ ControlTransferEntry& entry = (*map)[offset];
+ entry.pc_diff = pcdiff;
+ entry.sp_diff = spdiff;
+ entry.target_arity = arity;
}
}
};
@@ -742,10 +740,25 @@ class ControlTransfers : public ZoneObject {
const byte* pc;
CLabel* end_label;
CLabel* else_label;
-
- void Ref(ControlTransferMap* map, const byte* start,
- const byte* from_pc) {
- end_label->Ref(map, start, from_pc);
+ // Arity (number of values on the stack) when exiting this control
+ // structure via |end|.
+ uint32_t exit_arity;
+ // Track whether this block was already left, i.e. all further
+ // instructions are unreachable.
+ bool unreachable = false;
+
+ Control(const byte* pc, CLabel* end_label, CLabel* else_label,
+ uint32_t exit_arity)
+ : pc(pc),
+ end_label(end_label),
+ else_label(else_label),
+ exit_arity(exit_arity) {}
+ Control(const byte* pc, CLabel* end_label, uint32_t exit_arity)
+ : Control(pc, end_label, nullptr, exit_arity) {}
+
+ void Finish(ControlTransferMap* map, const byte* start) {
+ end_label->Finish(map, start);
+ if (else_label) else_label->Finish(map, start);
}
};
@@ -754,54 +767,93 @@ class ControlTransfers : public ZoneObject {
// AST decoder. The {control_stack} allows matching {br,br_if,br_table}
// bytecodes with their target, as well as determining whether the current
// bytecodes are within the true or false block of an else.
- std::vector<Control> control_stack;
- CLabel* func_label = new (zone) CLabel(zone);
- control_stack.push_back({start, func_label, nullptr});
- for (BytecodeIterator i(start, end, locals); i.has_next(); i.next()) {
+ ZoneVector<Control> control_stack(&control_transfer_zone);
+ uint32_t stack_height = 0;
+ uint32_t func_arity =
+ static_cast<uint32_t>(code->function->sig->return_count());
+ CLabel* func_label =
+ CLabel::New(&control_transfer_zone, stack_height, func_arity);
+ control_stack.emplace_back(code->orig_start, func_label, func_arity);
+ auto control_parent = [&]() -> Control& {
+ DCHECK_LE(2, control_stack.size());
+ return control_stack[control_stack.size() - 2];
+ };
+ auto copy_unreachable = [&] {
+ control_stack.back().unreachable = control_parent().unreachable;
+ };
+ for (BytecodeIterator i(code->orig_start, code->orig_end, &code->locals);
+ i.has_next(); i.next()) {
WasmOpcode opcode = i.current();
- TRACE("@%u: control %s\n", i.pc_offset(),
- WasmOpcodes::OpcodeName(opcode));
+ bool unreachable = control_stack.back().unreachable;
+ if (unreachable) {
+ TRACE("@%u: %s (is unreachable)\n", i.pc_offset(),
+ WasmOpcodes::OpcodeName(opcode));
+ } else {
+ auto stack_effect =
+ StackEffect(module, code->function->sig, i.pc(), i.end());
+ TRACE("@%u: %s (sp %d - %d + %d)\n", i.pc_offset(),
+ WasmOpcodes::OpcodeName(opcode), stack_height, stack_effect.first,
+ stack_effect.second);
+ DCHECK_GE(stack_height, stack_effect.first);
+ DCHECK_GE(kMaxUInt32, static_cast<uint64_t>(stack_height) -
+ stack_effect.first + stack_effect.second);
+ stack_height = stack_height - stack_effect.first + stack_effect.second;
+ if (stack_height > max_stack_height_) max_stack_height_ = stack_height;
+ }
switch (opcode) {
- case kExprBlock: {
- TRACE("control @%u: Block\n", i.pc_offset());
- CLabel* label = new (zone) CLabel(zone);
- control_stack.push_back({i.pc(), label, nullptr});
- break;
- }
+ case kExprBlock:
case kExprLoop: {
- TRACE("control @%u: Loop\n", i.pc_offset());
- CLabel* label = new (zone) CLabel(zone);
- control_stack.push_back({i.pc(), label, nullptr});
- label->Bind(&map_, start, i.pc());
+ bool is_loop = opcode == kExprLoop;
+ BlockTypeOperand<false> operand(&i, i.pc());
+ TRACE("control @%u: %s, arity %d\n", i.pc_offset(),
+ is_loop ? "Loop" : "Block", operand.arity);
+ CLabel* label = CLabel::New(&control_transfer_zone, stack_height,
+ is_loop ? 0 : operand.arity);
+ control_stack.emplace_back(i.pc(), label, operand.arity);
+ copy_unreachable();
+ if (is_loop) label->Bind(i.pc());
break;
}
case kExprIf: {
TRACE("control @%u: If\n", i.pc_offset());
- CLabel* end_label = new (zone) CLabel(zone);
- CLabel* else_label = new (zone) CLabel(zone);
- control_stack.push_back({i.pc(), end_label, else_label});
- else_label->Ref(&map_, start, i.pc());
+ BlockTypeOperand<false> operand(&i, i.pc());
+ CLabel* end_label =
+ CLabel::New(&control_transfer_zone, stack_height, operand.arity);
+ CLabel* else_label =
+ CLabel::New(&control_transfer_zone, stack_height, 0);
+ control_stack.emplace_back(i.pc(), end_label, else_label,
+ operand.arity);
+ copy_unreachable();
+ if (!unreachable) else_label->Ref(i.pc(), stack_height);
break;
}
case kExprElse: {
Control* c = &control_stack.back();
+ copy_unreachable();
TRACE("control @%u: Else\n", i.pc_offset());
- c->end_label->Ref(&map_, start, i.pc());
+ if (!control_parent().unreachable) {
+ c->end_label->Ref(i.pc(), stack_height);
+ }
DCHECK_NOT_NULL(c->else_label);
- c->else_label->Bind(&map_, start, i.pc() + 1);
+ c->else_label->Bind(i.pc() + 1);
+ c->else_label->Finish(&map_, code->orig_start);
c->else_label = nullptr;
+ DCHECK_GE(stack_height, c->end_label->target_stack_height);
+ stack_height = c->end_label->target_stack_height;
break;
}
case kExprEnd: {
Control* c = &control_stack.back();
TRACE("control @%u: End\n", i.pc_offset());
- if (c->end_label->target) {
- // only loops have bound labels.
- DCHECK_EQ(kExprLoop, *c->pc);
- } else {
- if (c->else_label) c->else_label->Bind(&map_, start, i.pc());
- c->end_label->Bind(&map_, start, i.pc() + 1);
+ // Only loops have bound labels.
+ DCHECK_IMPLIES(c->end_label->target, *c->pc == kExprLoop);
+ if (!c->end_label->target) {
+ if (c->else_label) c->else_label->Bind(i.pc());
+ c->end_label->Bind(i.pc() + 1);
}
+ c->Finish(&map_, code->orig_start);
+ DCHECK_GE(stack_height, c->end_label->target_stack_height);
+ stack_height = c->end_label->target_stack_height + c->exit_arity;
control_stack.pop_back();
break;
}
@@ -809,14 +861,14 @@ class ControlTransfers : public ZoneObject {
BreakDepthOperand<false> operand(&i, i.pc());
TRACE("control @%u: Br[depth=%u]\n", i.pc_offset(), operand.depth);
Control* c = &control_stack[control_stack.size() - operand.depth - 1];
- c->Ref(&map_, start, i.pc());
+ if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
break;
}
case kExprBrIf: {
BreakDepthOperand<false> operand(&i, i.pc());
TRACE("control @%u: BrIf[depth=%u]\n", i.pc_offset(), operand.depth);
Control* c = &control_stack[control_stack.size() - operand.depth - 1];
- c->Ref(&map_, start, i.pc());
+ if (!unreachable) c->end_label->Ref(i.pc(), stack_height);
break;
}
case kExprBrTable: {
@@ -824,44 +876,34 @@ class ControlTransfers : public ZoneObject {
BranchTableIterator<false> iterator(&i, operand);
TRACE("control @%u: BrTable[count=%u]\n", i.pc_offset(),
operand.table_count);
- while (iterator.has_next()) {
- uint32_t j = iterator.cur_index();
- uint32_t target = iterator.next();
- Control* c = &control_stack[control_stack.size() - target - 1];
- c->Ref(&map_, start, i.pc() + j);
+ if (!unreachable) {
+ while (iterator.has_next()) {
+ uint32_t j = iterator.cur_index();
+ uint32_t target = iterator.next();
+ Control* c = &control_stack[control_stack.size() - target - 1];
+ c->end_label->Ref(i.pc() + j, stack_height);
+ }
}
break;
}
- default: {
+ default:
break;
- }
+ }
+ if (WasmOpcodes::IsUnconditionalJump(opcode)) {
+ control_stack.back().unreachable = true;
}
}
- if (!func_label->target) func_label->Bind(&map_, start, end);
+ DCHECK_EQ(0, control_stack.size());
+ DCHECK_EQ(func_arity, stack_height);
}
- pcdiff_t Lookup(pc_t from) {
+ ControlTransferEntry& Lookup(pc_t from) {
auto result = map_.find(from);
- if (result == map_.end()) {
- V8_Fatal(__FILE__, __LINE__, "no control target for pc %zu", from);
- }
+ DCHECK(result != map_.end());
return result->second;
}
};
-// Code and metadata needed to execute a function.
-struct InterpreterCode {
- const WasmFunction* function; // wasm function
- BodyLocalDecls locals; // local declarations
- const byte* orig_start; // start of original code
- const byte* orig_end; // end of original code
- byte* start; // start of (maybe altered) code
- byte* end; // end of (maybe altered) code
- ControlTransfers* targets; // helper for control flow.
-
- const byte* at(pc_t pc) { return start + pc; }
-};
-
struct ExternalCallResult {
enum Type {
// The function should be executed inside this interpreter.
@@ -980,11 +1022,9 @@ class CodeMap {
InterpreterCode* Preprocess(InterpreterCode* code) {
DCHECK_EQ(code->function->imported, code->start == nullptr);
- if (code->targets == nullptr && code->start != nullptr) {
+ if (!code->side_table && code->start) {
// Compute the control targets map and the local declarations.
- CHECK(DecodeLocalDecls(&code->locals, code->start, code->end));
- code->targets = new (zone_) ControlTransfers(
- zone_, &code->locals, code->orig_start, code->orig_end);
+ code->side_table = new (zone_) SideTable(zone_, module_, code);
}
return code;
}
@@ -1005,11 +1045,11 @@ class CodeMap {
DCHECK_LT(function->func_index, interpreter_code_.size());
InterpreterCode* code = &interpreter_code_[function->func_index];
DCHECK_EQ(function, code->function);
- code->targets = nullptr;
code->orig_start = start;
code->orig_end = end;
code->start = const_cast<byte*>(start);
code->end = const_cast<byte*>(end);
+ code->side_table = nullptr;
Preprocess(code);
}
@@ -1113,17 +1153,16 @@ WasmVal ToWebAssemblyValue(Isolate* isolate, Handle<Object> value,
class ThreadImpl {
struct Activation {
uint32_t fp;
- uint32_t sp;
- Activation(uint32_t fp, uint32_t sp) : fp(fp), sp(sp) {}
+ sp_t sp;
+ Activation(uint32_t fp, sp_t sp) : fp(fp), sp(sp) {}
};
public:
ThreadImpl(Zone* zone, CodeMap* codemap, WasmInstance* instance)
: codemap_(codemap),
instance_(instance),
- stack_(zone),
+ zone_(zone),
frames_(zone),
- blocks_(zone),
activations_(zone) {}
//==========================================================================
@@ -1135,9 +1174,9 @@ class ThreadImpl {
void InitFrame(const WasmFunction* function, WasmVal* args) {
DCHECK_EQ(current_activation().fp, frames_.size());
InterpreterCode* code = codemap()->GetCode(function);
- for (size_t i = 0; i < function->sig->parameter_count(); ++i) {
- stack_.push_back(args[i]);
- }
+ size_t num_params = function->sig->parameter_count();
+ EnsureStackSpace(num_params);
+ Push(args, num_params);
PushFrame(code);
}
@@ -1164,7 +1203,7 @@ class ThreadImpl {
void Reset() {
TRACE("----- RESET -----\n");
- stack_.clear();
+ sp_ = stack_start_;
frames_.clear();
state_ = WasmInterpreter::STOPPED;
trap_reason_ = kTrapCount;
@@ -1176,27 +1215,23 @@ class ThreadImpl {
return static_cast<int>(frames_.size());
}
- template <typename FrameCons>
- InterpretedFrame GetMutableFrame(int index, FrameCons frame_cons) {
- DCHECK_LE(0, index);
- DCHECK_GT(frames_.size(), index);
- Frame* frame = &frames_[index];
- DCHECK_GE(kMaxInt, frame->pc);
- DCHECK_GE(kMaxInt, frame->sp);
- DCHECK_GE(kMaxInt, frame->llimit());
- return frame_cons(frame->code->function, static_cast<int>(frame->pc),
- static_cast<int>(frame->sp),
- static_cast<int>(frame->llimit()));
- }
-
WasmVal GetReturnValue(uint32_t index) {
if (state_ == WasmInterpreter::TRAPPED) return WasmVal(0xdeadbeef);
DCHECK_EQ(WasmInterpreter::FINISHED, state_);
Activation act = current_activation();
// Current activation must be finished.
DCHECK_EQ(act.fp, frames_.size());
- DCHECK_GT(stack_.size(), act.sp + index);
- return stack_[act.sp + index];
+ return GetStackValue(act.sp + index);
+ }
+
+ WasmVal GetStackValue(sp_t index) {
+ DCHECK_GT(StackHeight(), index);
+ return stack_start_[index];
+ }
+
+ void SetStackValue(sp_t index, WasmVal value) {
+ DCHECK_GT(StackHeight(), index);
+ stack_start_[index] = value;
}
TrapReason GetTrapReason() { return trap_reason_; }
@@ -1219,10 +1254,10 @@ class ThreadImpl {
TRACE("----- START ACTIVATION %zu -----\n", activations_.size());
// If you use activations, use them consistently:
DCHECK_IMPLIES(activations_.empty(), frames_.empty());
- DCHECK_IMPLIES(activations_.empty(), stack_.empty());
+ DCHECK_IMPLIES(activations_.empty(), StackHeight() == 0);
uint32_t activation_id = static_cast<uint32_t>(activations_.size());
activations_.emplace_back(static_cast<uint32_t>(frames_.size()),
- static_cast<uint32_t>(stack_.size()));
+ StackHeight());
state_ = WasmInterpreter::STOPPED;
return activation_id;
}
@@ -1234,8 +1269,8 @@ class ThreadImpl {
// Stack height must match the start of this activation (otherwise unwind
// first).
DCHECK_EQ(activations_.back().fp, frames_.size());
- DCHECK_LE(activations_.back().sp, stack_.size());
- stack_.resize(activations_.back().sp);
+ DCHECK_LE(activations_.back().sp, StackHeight());
+ sp_ = stack_start_ + activations_.back().sp;
activations_.pop_back();
}
@@ -1256,8 +1291,8 @@ class ThreadImpl {
Activation& act = activations_.back();
DCHECK_LE(act.fp, frames_.size());
frames_.resize(act.fp);
- DCHECK_LE(act.sp, stack_.size());
- stack_.resize(act.sp);
+ DCHECK_LE(act.sp, StackHeight());
+ sp_ = stack_start_ + act.sp;
state_ = WasmInterpreter::STOPPED;
return WasmInterpreter::Thread::UNWOUND;
}
@@ -1282,11 +1317,15 @@ class ThreadImpl {
unsigned arity;
};
+ friend class InterpretedFrameImpl;
+
CodeMap* codemap_;
WasmInstance* instance_;
- ZoneVector<WasmVal> stack_;
+ Zone* zone_;
+ WasmVal* stack_start_ = nullptr; // Start of allocated stack space.
+ WasmVal* stack_limit_ = nullptr; // End of allocated stack space.
+ WasmVal* sp_ = nullptr; // Current stack pointer.
ZoneVector<Frame> frames_;
- ZoneVector<Block> blocks_;
WasmInterpreter::State state_ = WasmInterpreter::STOPPED;
pc_t break_pc_ = kInvalidPc;
TrapReason trap_reason_ = kTrapCount;
@@ -1310,14 +1349,15 @@ class ThreadImpl {
// Push a frame with arguments already on the stack.
void PushFrame(InterpreterCode* code) {
DCHECK_NOT_NULL(code);
+ DCHECK_NOT_NULL(code->side_table);
+ EnsureStackSpace(code->side_table->max_stack_height_ +
+ code->locals.type_list.size());
+
++num_interpreted_calls_;
size_t arity = code->function->sig->parameter_count();
// The parameters will overlap the arguments already on the stack.
- DCHECK_GE(stack_.size(), arity);
- frames_.push_back({code, 0, stack_.size() - arity});
- blocks_.push_back(
- {0, stack_.size(), frames_.size(),
- static_cast<uint32_t>(code->function->sig->return_count())});
+ DCHECK_GE(StackHeight(), arity);
+ frames_.push_back({code, 0, StackHeight() - arity});
frames_.back().pc = InitLocals(code);
TRACE(" => PushFrame #%zu (#%u @%zu)\n", frames_.size() - 1,
code->function->func_index, frames_.back().pc);
@@ -1337,7 +1377,7 @@ class ThreadImpl {
UNREACHABLE();
break;
}
- stack_.push_back(val);
+ Push(val);
}
return code->locals.encoded_size;
}
@@ -1356,16 +1396,15 @@ class ThreadImpl {
return false;
}
- int LookupTarget(InterpreterCode* code, pc_t pc) {
- return static_cast<int>(code->targets->Lookup(pc));
+ int LookupTargetDelta(InterpreterCode* code, pc_t pc) {
+ return static_cast<int>(code->side_table->Lookup(pc).pc_diff);
}
int DoBreak(InterpreterCode* code, pc_t pc, size_t depth) {
- size_t bp = blocks_.size() - depth - 1;
- Block* target = &blocks_[bp];
- DoStackTransfer(target->sp, target->arity);
- blocks_.resize(bp);
- return LookupTarget(code, pc);
+ ControlTransferEntry& control_transfer_entry = code->side_table->Lookup(pc);
+ DoStackTransfer(sp_ - control_transfer_entry.sp_diff,
+ control_transfer_entry.target_arity);
+ return control_transfer_entry.pc_diff;
}
pc_t ReturnPc(Decoder* decoder, InterpreterCode* code, pc_t pc) {
@@ -1387,17 +1426,12 @@ class ThreadImpl {
bool DoReturn(Decoder* decoder, InterpreterCode** code, pc_t* pc, pc_t* limit,
size_t arity) {
DCHECK_GT(frames_.size(), 0);
- // Pop all blocks for this frame.
- while (!blocks_.empty() && blocks_.back().fp == frames_.size()) {
- blocks_.pop_back();
- }
-
- sp_t dest = frames_.back().sp;
+ WasmVal* sp_dest = stack_start_ + frames_.back().sp;
frames_.pop_back();
if (frames_.size() == current_activation().fp) {
// A return from the last frame terminates the execution.
state_ = WasmInterpreter::FINISHED;
- DoStackTransfer(dest, arity);
+ DoStackTransfer(sp_dest, arity);
TRACE(" => finish\n");
return false;
} else {
@@ -1409,7 +1443,7 @@ class ThreadImpl {
*limit = top->code->end - top->code->start;
TRACE(" => Return to #%zu (#%u @%zu)\n", frames_.size() - 1,
(*code)->function->func_index, *pc);
- DoStackTransfer(dest, arity);
+ DoStackTransfer(sp_dest, arity);
return true;
}
}
@@ -1429,19 +1463,16 @@ class ThreadImpl {
// Copies {arity} values on the top of the stack down the stack to {dest},
// dropping the values in-between.
- void DoStackTransfer(sp_t dest, size_t arity) {
+ void DoStackTransfer(WasmVal* dest, size_t arity) {
// before: |---------------| pop_count | arity |
- // ^ 0 ^ dest ^ stack_.size()
+ // ^ 0 ^ dest ^ sp_
//
// after: |---------------| arity |
- // ^ 0 ^ stack_.size()
- DCHECK_LE(dest, stack_.size());
- DCHECK_LE(dest + arity, stack_.size());
- size_t pop_count = stack_.size() - dest - arity;
- for (size_t i = 0; i < arity; i++) {
- stack_[dest + i] = stack_[dest + pop_count + i];
- }
- stack_.resize(stack_.size() - pop_count);
+ // ^ 0 ^ sp_
+ DCHECK_LE(dest, sp_);
+ DCHECK_LE(dest + arity, sp_);
+ if (arity) memcpy(dest, sp_ - arity, arity * sizeof(*sp_));
+ sp_ = dest + arity;
}
template <typename mtype>
@@ -1461,7 +1492,7 @@ class ThreadImpl {
byte* addr = instance()->mem_start + operand.offset + index;
WasmVal result(static_cast<ctype>(ReadLittleEndianValue<mtype>(addr)));
- Push(pc, result);
+ Push(result);
len = 1 + operand.length;
return true;
}
@@ -1514,6 +1545,15 @@ class ThreadImpl {
}
void Execute(InterpreterCode* code, pc_t pc, int max) {
+ DCHECK_NOT_NULL(code->side_table);
+ DCHECK(!frames_.empty());
+ // There must be enough space on the stack to hold the arguments, locals,
+ // and the value stack.
+ DCHECK_LE(code->function->sig->parameter_count() +
+ code->locals.type_list.size() +
+ code->side_table->max_stack_height_,
+ stack_limit_ - stack_start_ - frames_.back().sp);
+
Decoder decoder(code->start, code->end);
pc_t limit = code->end - code->start;
bool hit_break = false;
@@ -1558,18 +1598,26 @@ class ThreadImpl {
TraceValueStack();
TRACE("\n");
+#ifdef DEBUG
+ // Compute the stack effect of this opcode, and verify later that the
+ // stack was modified accordingly.
+ std::pair<uint32_t, uint32_t> stack_effect = wasm::StackEffect(
+ codemap_->module(), frames_.back().code->function->sig,
+ code->orig_start + pc, code->orig_end);
+ sp_t expected_new_stack_height =
+ StackHeight() - stack_effect.first + stack_effect.second;
+#endif
+
switch (orig) {
case kExprNop:
break;
case kExprBlock: {
BlockTypeOperand<false> operand(&decoder, code->at(pc));
- blocks_.push_back({pc, stack_.size(), frames_.size(), operand.arity});
len = 1 + operand.length;
break;
}
case kExprLoop: {
BlockTypeOperand<false> operand(&decoder, code->at(pc));
- blocks_.push_back({pc, stack_.size(), frames_.size(), 0});
len = 1 + operand.length;
break;
}
@@ -1577,20 +1625,18 @@ class ThreadImpl {
BlockTypeOperand<false> operand(&decoder, code->at(pc));
WasmVal cond = Pop();
bool is_true = cond.to<uint32_t>() != 0;
- blocks_.push_back({pc, stack_.size(), frames_.size(), operand.arity});
if (is_true) {
// fall through to the true block.
len = 1 + operand.length;
TRACE(" true => fallthrough\n");
} else {
- len = LookupTarget(code, pc);
+ len = LookupTargetDelta(code, pc);
TRACE(" false => @%zu\n", pc + len);
}
break;
}
case kExprElse: {
- blocks_.pop_back();
- len = LookupTarget(code, pc);
+ len = LookupTargetDelta(code, pc);
TRACE(" end => @%zu\n", pc + len);
break;
}
@@ -1598,7 +1644,7 @@ class ThreadImpl {
WasmVal cond = Pop();
WasmVal fval = Pop();
WasmVal tval = Pop();
- Push(pc, cond.to<int32_t>() != 0 ? tval : fval);
+ Push(cond.to<int32_t>() != 0 ? tval : fval);
break;
}
case kExprBr: {
@@ -1644,51 +1690,50 @@ class ThreadImpl {
return DoTrap(kTrapUnreachable, pc);
}
case kExprEnd: {
- blocks_.pop_back();
break;
}
case kExprI32Const: {
ImmI32Operand<false> operand(&decoder, code->at(pc));
- Push(pc, WasmVal(operand.value));
+ Push(WasmVal(operand.value));
len = 1 + operand.length;
break;
}
case kExprI64Const: {
ImmI64Operand<false> operand(&decoder, code->at(pc));
- Push(pc, WasmVal(operand.value));
+ Push(WasmVal(operand.value));
len = 1 + operand.length;
break;
}
case kExprF32Const: {
ImmF32Operand<false> operand(&decoder, code->at(pc));
- Push(pc, WasmVal(operand.value));
+ Push(WasmVal(operand.value));
len = 1 + operand.length;
break;
}
case kExprF64Const: {
ImmF64Operand<false> operand(&decoder, code->at(pc));
- Push(pc, WasmVal(operand.value));
+ Push(WasmVal(operand.value));
len = 1 + operand.length;
break;
}
case kExprGetLocal: {
LocalIndexOperand<false> operand(&decoder, code->at(pc));
- Push(pc, stack_[frames_.back().sp + operand.index]);
+ Push(GetStackValue(frames_.back().sp + operand.index));
len = 1 + operand.length;
break;
}
case kExprSetLocal: {
LocalIndexOperand<false> operand(&decoder, code->at(pc));
WasmVal val = Pop();
- stack_[frames_.back().sp + operand.index] = val;
+ SetStackValue(frames_.back().sp + operand.index, val);
len = 1 + operand.length;
break;
}
case kExprTeeLocal: {
LocalIndexOperand<false> operand(&decoder, code->at(pc));
WasmVal val = Pop();
- stack_[frames_.back().sp + operand.index] = val;
- Push(pc, val);
+ SetStackValue(frames_.back().sp + operand.index, val);
+ Push(val);
len = 1 + operand.length;
break;
}
@@ -1770,7 +1815,7 @@ class ThreadImpl {
default:
UNREACHABLE();
}
- Push(pc, val);
+ Push(val);
len = 1 + operand.length;
break;
}
@@ -1843,7 +1888,7 @@ class ThreadImpl {
/* TODO(titzer): alignment for asmjs load mem? */ \
result = static_cast<ctype>(*reinterpret_cast<mtype*>(addr)); \
} \
- Push(pc, WasmVal(result)); \
+ Push(WasmVal(result)); \
break; \
}
ASMJS_LOAD_CASE(I32AsmjsLoadMem8S, int32_t, int8_t, 0);
@@ -1866,7 +1911,7 @@ class ThreadImpl {
/* TODO(titzer): alignment for asmjs store mem? */ \
*(reinterpret_cast<mtype*>(addr)) = static_cast<mtype>(val.to<ctype>()); \
} \
- Push(pc, val); \
+ Push(val); \
break; \
}
@@ -1879,15 +1924,15 @@ class ThreadImpl {
case kExprGrowMemory: {
MemoryIndexOperand<false> operand(&decoder, code->at(pc));
uint32_t delta_pages = Pop().to<uint32_t>();
- Push(pc, WasmVal(ExecuteGrowMemory(
- delta_pages, codemap_->maybe_instance(), instance())));
+ Push(WasmVal(ExecuteGrowMemory(
+ delta_pages, codemap_->maybe_instance(), instance())));
len = 1 + operand.length;
break;
}
case kExprMemorySize: {
MemoryIndexOperand<false> operand(&decoder, code->at(pc));
- Push(pc, WasmVal(static_cast<uint32_t>(instance()->mem_size /
- WasmModule::kPageSize)));
+ Push(WasmVal(static_cast<uint32_t>(instance()->mem_size /
+ WasmModule::kPageSize)));
len = 1 + operand.length;
break;
}
@@ -1896,15 +1941,13 @@ class ThreadImpl {
// ia32 by the reinterpret casts.
case kExprI32ReinterpretF32: {
WasmVal val = Pop();
- WasmVal result(ExecuteI32ReinterpretF32(val));
- Push(pc, result);
+ Push(WasmVal(ExecuteI32ReinterpretF32(val)));
possible_nondeterminism_ |= std::isnan(val.to<float>());
break;
}
case kExprI64ReinterpretF64: {
WasmVal val = Pop();
- WasmVal result(ExecuteI64ReinterpretF64(val));
- Push(pc, result);
+ Push(WasmVal(ExecuteI64ReinterpretF64(val)));
possible_nondeterminism_ |= std::isnan(val.to<double>());
break;
}
@@ -1913,7 +1956,7 @@ class ThreadImpl {
WasmVal rval = Pop(); \
WasmVal lval = Pop(); \
WasmVal result(lval.to<ctype>() op rval.to<ctype>()); \
- Push(pc, result); \
+ Push(result); \
break; \
}
FOREACH_SIMPLE_BINOP(EXECUTE_SIMPLE_BINOP)
@@ -1926,7 +1969,7 @@ class ThreadImpl {
volatile ctype lval = Pop().to<ctype>(); \
WasmVal result(Execute##name(lval, rval, &trap)); \
if (trap != kTrapCount) return DoTrap(trap, pc); \
- Push(pc, result); \
+ Push(result); \
break; \
}
FOREACH_OTHER_BINOP(EXECUTE_OTHER_BINOP)
@@ -1939,7 +1982,7 @@ class ThreadImpl {
volatile float rval = Pop().to<float>();
volatile float lval = Pop().to<float>();
WasmVal result(ExecuteF32CopySign(lval, rval, &trap));
- Push(pc, result);
+ Push(result);
possible_nondeterminism_ |= std::isnan(rval);
break;
}
@@ -1950,7 +1993,7 @@ class ThreadImpl {
volatile double rval = Pop().to<double>();
volatile double lval = Pop().to<double>();
WasmVal result(ExecuteF64CopySign(lval, rval, &trap));
- Push(pc, result);
+ Push(result);
possible_nondeterminism_ |= std::isnan(rval);
break;
}
@@ -1960,7 +2003,7 @@ class ThreadImpl {
volatile ctype val = Pop().to<ctype>(); \
WasmVal result(Execute##name(val, &trap)); \
if (trap != kTrapCount) return DoTrap(trap, pc); \
- Push(pc, result); \
+ Push(result); \
break; \
}
FOREACH_OTHER_UNOP(EXECUTE_OTHER_UNOP)
@@ -1972,6 +2015,12 @@ class ThreadImpl {
UNREACHABLE();
}
+#ifdef DEBUG
+ if (!WasmOpcodes::IsControlOpcode(static_cast<WasmOpcode>(opcode))) {
+ DCHECK_EQ(expected_new_stack_height, StackHeight());
+ }
+#endif
+
pc += len;
if (pc == limit) {
// Fell off end of code; do an implicit return.
@@ -1989,20 +2038,17 @@ class ThreadImpl {
}
WasmVal Pop() {
- DCHECK_GT(stack_.size(), 0);
DCHECK_GT(frames_.size(), 0);
- DCHECK_GT(stack_.size(), frames_.back().llimit()); // can't pop into locals
- WasmVal val = stack_.back();
- stack_.pop_back();
- return val;
+ DCHECK_GT(StackHeight(), frames_.back().llimit()); // can't pop into locals
+ return *--sp_;
}
void PopN(int n) {
- DCHECK_GE(stack_.size(), n);
+ DCHECK_GE(StackHeight(), n);
DCHECK_GT(frames_.size(), 0);
- size_t nsize = stack_.size() - n;
- DCHECK_GE(nsize, frames_.back().llimit()); // can't pop into locals
- stack_.resize(nsize);
+ // Check that we don't pop into locals.
+ DCHECK_GE(StackHeight() - n, frames_.back().llimit());
+ sp_ -= n;
}
WasmVal PopArity(size_t arity) {
@@ -2011,12 +2057,36 @@ class ThreadImpl {
return Pop();
}
- void Push(pc_t pc, WasmVal val) {
- // TODO(titzer): store PC as well?
+ void Push(WasmVal val) {
DCHECK_NE(kWasmStmt, val.type);
- stack_.push_back(val);
+ DCHECK_LE(1, stack_limit_ - sp_);
+ *sp_++ = val;
}
+ void Push(WasmVal* vals, size_t arity) {
+ DCHECK_LE(arity, stack_limit_ - sp_);
+ for (WasmVal *val = vals, *end = vals + arity; val != end; ++val) {
+ DCHECK_NE(kWasmStmt, val->type);
+ }
+ memcpy(sp_, vals, arity * sizeof(*sp_));
+ sp_ += arity;
+ }
+
+ void EnsureStackSpace(size_t size) {
+ if (V8_LIKELY(static_cast<size_t>(stack_limit_ - sp_) >= size)) return;
+ size_t old_size = stack_limit_ - stack_start_;
+ size_t requested_size =
+ base::bits::RoundUpToPowerOfTwo64((sp_ - stack_start_) + size);
+ size_t new_size = Max(size_t{8}, Max(2 * old_size, requested_size));
+ WasmVal* new_stack = zone_->NewArray<WasmVal>(new_size);
+ memcpy(new_stack, stack_start_, old_size * sizeof(*sp_));
+ sp_ = new_stack + (sp_ - stack_start_);
+ stack_start_ = new_stack;
+ stack_limit_ = new_stack + new_size;
+ }
+
+ sp_t StackHeight() { return sp_ - stack_start_; }
+
void TraceStack(const char* phase, pc_t pc) {
if (FLAG_trace_wasm_interpreter) {
PrintF("%s @%zu", phase, pc);
@@ -2027,39 +2097,38 @@ class ThreadImpl {
void TraceValueStack() {
#ifdef DEBUG
+ if (!FLAG_trace_wasm_interpreter) return;
Frame* top = frames_.size() > 0 ? &frames_.back() : nullptr;
sp_t sp = top ? top->sp : 0;
sp_t plimit = top ? top->plimit() : 0;
sp_t llimit = top ? top->llimit() : 0;
- if (FLAG_trace_wasm_interpreter) {
- for (size_t i = sp; i < stack_.size(); ++i) {
- if (i < plimit)
- PrintF(" p%zu:", i);
- else if (i < llimit)
- PrintF(" l%zu:", i);
- else
- PrintF(" s%zu:", i);
- WasmVal val = stack_[i];
- switch (val.type) {
- case kWasmI32:
- PrintF("i32:%d", val.to<int32_t>());
- break;
- case kWasmI64:
- PrintF("i64:%" PRId64 "", val.to<int64_t>());
- break;
- case kWasmF32:
- PrintF("f32:%f", val.to<float>());
- break;
- case kWasmF64:
- PrintF("f64:%lf", val.to<double>());
- break;
- case kWasmStmt:
- PrintF("void");
- break;
- default:
- UNREACHABLE();
- break;
- }
+ for (size_t i = sp; i < StackHeight(); ++i) {
+ if (i < plimit)
+ PrintF(" p%zu:", i);
+ else if (i < llimit)
+ PrintF(" l%zu:", i);
+ else
+ PrintF(" s%zu:", i);
+ WasmVal val = GetStackValue(i);
+ switch (val.type) {
+ case kWasmI32:
+ PrintF("i32:%d", val.to<int32_t>());
+ break;
+ case kWasmI64:
+ PrintF("i64:%" PRId64 "", val.to<int64_t>());
+ break;
+ case kWasmF32:
+ PrintF("f32:%f", val.to<float>());
+ break;
+ case kWasmF64:
+ PrintF("f64:%lf", val.to<double>());
+ break;
+ case kWasmStmt:
+ PrintF("void");
+ break;
+ default:
+ UNREACHABLE();
+ break;
}
}
#endif // DEBUG
@@ -2112,7 +2181,7 @@ class ThreadImpl {
// Get all arguments as JS values.
std::vector<Handle<Object>> args;
args.reserve(num_args);
- WasmVal* wasm_args = stack_.data() + (stack_.size() - num_args);
+ WasmVal* wasm_args = sp_ - num_args;
for (int i = 0; i < num_args; ++i) {
args.push_back(WasmValToNumber(isolate->factory(), wasm_args[i],
signature->GetParam(i)));
@@ -2132,12 +2201,11 @@ class ThreadImpl {
Handle<Object> retval = maybe_retval.ToHandleChecked();
// Pop arguments off the stack.
- stack_.resize(stack_.size() - num_args);
+ sp_ -= num_args;
if (signature->return_count() > 0) {
// TODO(wasm): Handle multiple returns.
DCHECK_EQ(1, signature->return_count());
- stack_.push_back(
- ToWebAssemblyValue(isolate, retval, signature->GetReturn()));
+ Push(ToWebAssemblyValue(isolate, retval, signature->GetReturn()));
}
return {ExternalCallResult::EXTERNAL_RETURNED};
}
@@ -2157,7 +2225,8 @@ class ThreadImpl {
ExternalCallResult CallIndirectFunction(uint32_t table_index,
uint32_t entry_index,
uint32_t sig_index) {
- if (!codemap()->has_instance()) {
+ if (!codemap()->has_instance() ||
+ !codemap()->instance()->compiled_module()->has_function_tables()) {
// No instance. Rely on the information stored in the WasmModule.
// TODO(wasm): This is only needed for testing. Refactor testing to use
// the same paths as production.
@@ -2234,6 +2303,68 @@ class ThreadImpl {
}
};
+class InterpretedFrameImpl {
+ public:
+ InterpretedFrameImpl(ThreadImpl* thread, int index)
+ : thread_(thread), index_(index) {
+ DCHECK_LE(0, index);
+ }
+
+ const WasmFunction* function() const { return frame()->code->function; }
+
+ int pc() const {
+ DCHECK_LE(0, frame()->pc);
+ DCHECK_GE(kMaxInt, frame()->pc);
+ return static_cast<int>(frame()->pc);
+ }
+
+ int GetParameterCount() const {
+ DCHECK_GE(kMaxInt, function()->sig->parameter_count());
+ return static_cast<int>(function()->sig->parameter_count());
+ }
+
+ int GetLocalCount() const {
+ size_t num_locals = function()->sig->parameter_count() +
+ frame()->code->locals.type_list.size();
+ DCHECK_GE(kMaxInt, num_locals);
+ return static_cast<int>(num_locals);
+ }
+
+ int GetStackHeight() const {
+ bool is_top_frame =
+ static_cast<size_t>(index_) + 1 == thread_->frames_.size();
+ size_t stack_limit =
+ is_top_frame ? thread_->StackHeight() : thread_->frames_[index_ + 1].sp;
+ DCHECK_LE(frame()->sp, stack_limit);
+ size_t frame_size = stack_limit - frame()->sp;
+ DCHECK_LE(GetLocalCount(), frame_size);
+ return static_cast<int>(frame_size) - GetLocalCount();
+ }
+
+ WasmVal GetLocalValue(int index) const {
+ DCHECK_LE(0, index);
+ DCHECK_GT(GetLocalCount(), index);
+ return thread_->GetStackValue(static_cast<int>(frame()->sp) + index);
+ }
+
+ WasmVal GetStackValue(int index) const {
+ DCHECK_LE(0, index);
+ // Index must be within the number of stack values of this frame.
+ DCHECK_GT(GetStackHeight(), index);
+ return thread_->GetStackValue(static_cast<int>(frame()->sp) +
+ GetLocalCount() + index);
+ }
+
+ private:
+ ThreadImpl* thread_;
+ int index_;
+
+ ThreadImpl::Frame* frame() const {
+ DCHECK_GT(thread_->frames_.size(), index_);
+ return &thread_->frames_[index_];
+ }
+};
+
// 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
@@ -2245,6 +2376,14 @@ ThreadImpl* ToImpl(WasmInterpreter::Thread* thread) {
return reinterpret_cast<ThreadImpl*>(thread);
}
+// Same conversion for InterpretedFrame and InterpretedFrameImpl.
+InterpretedFrame* ToFrame(InterpretedFrameImpl* impl) {
+ return reinterpret_cast<InterpretedFrame*>(impl);
+}
+const InterpretedFrameImpl* ToImpl(const InterpretedFrame* frame) {
+ return reinterpret_cast<const InterpretedFrameImpl*>(frame);
+}
+
} // namespace
//============================================================================
@@ -2275,16 +2414,11 @@ pc_t WasmInterpreter::Thread::GetBreakpointPc() {
int WasmInterpreter::Thread::GetFrameCount() {
return ToImpl(this)->GetFrameCount();
}
-const InterpretedFrame WasmInterpreter::Thread::GetFrame(int index) {
- return GetMutableFrame(index);
-}
-InterpretedFrame WasmInterpreter::Thread::GetMutableFrame(int index) {
- // We have access to the constructor of InterpretedFrame, but ThreadImpl has
- // not. So pass it as a lambda (should all get inlined).
- auto frame_cons = [](const WasmFunction* function, int pc, int fp, int sp) {
- return InterpretedFrame(function, pc, fp, sp);
- };
- return ToImpl(this)->GetMutableFrame(index, frame_cons);
+std::unique_ptr<InterpretedFrame> WasmInterpreter::Thread::GetFrame(int index) {
+ DCHECK_LE(0, index);
+ DCHECK_GT(GetFrameCount(), index);
+ return std::unique_ptr<InterpretedFrame>(
+ ToFrame(new InterpretedFrameImpl(ToImpl(this), index)));
}
WasmVal WasmInterpreter::Thread::GetReturnValue(int index) {
return ToImpl(this)->GetReturnValue(index);
@@ -2416,6 +2550,11 @@ void WasmInterpreter::WriteMemory(size_t offset, WasmVal val) {
UNIMPLEMENTED();
}
+void WasmInterpreter::UpdateMemory(byte* mem_start, uint32_t mem_size) {
+ internals_->instance_->mem_start = mem_start;
+ internals_->instance_->mem_size = mem_size;
+}
+
void WasmInterpreter::AddFunctionForTesting(const WasmFunction* function) {
internals_->codemap_.AddFunction(function, nullptr, nullptr);
}
@@ -2427,39 +2566,41 @@ void WasmInterpreter::SetFunctionCodeForTesting(const WasmFunction* function,
}
ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting(
- Zone* zone, const byte* start, const byte* end) {
- ControlTransfers targets(zone, nullptr, start, end);
- return targets.map_;
+ Zone* zone, const WasmModule* module, const byte* start, const byte* end) {
+ // Create some dummy structures, to avoid special-casing the implementation
+ // just for testing.
+ FunctionSig sig(0, 0, nullptr);
+ WasmFunction function{&sig, 0, 0, 0, 0, 0, 0, false, false};
+ InterpreterCode code{
+ &function, BodyLocalDecls(zone), start, end, nullptr, nullptr, nullptr};
+
+ // Now compute and return the control transfers.
+ SideTable side_table(zone, module, &code);
+ return side_table.map_;
}
//============================================================================
// Implementation of the frame inspection interface.
//============================================================================
+const WasmFunction* InterpretedFrame::function() const {
+ return ToImpl(this)->function();
+}
+int InterpretedFrame::pc() const { return ToImpl(this)->pc(); }
int InterpretedFrame::GetParameterCount() const {
- USE(fp_);
- USE(sp_);
- // TODO(clemensh): Return the correct number of parameters.
- return 0;
+ return ToImpl(this)->GetParameterCount();
}
-
-WasmVal InterpretedFrame::GetLocalVal(int index) const {
- CHECK_GE(index, 0);
- UNIMPLEMENTED();
- WasmVal none;
- none.type = kWasmStmt;
- return none;
+int InterpretedFrame::GetLocalCount() const {
+ return ToImpl(this)->GetLocalCount();
}
-
-WasmVal InterpretedFrame::GetExprVal(int pc) const {
- UNIMPLEMENTED();
- WasmVal none;
- none.type = kWasmStmt;
- return none;
+int InterpretedFrame::GetStackHeight() const {
+ return ToImpl(this)->GetStackHeight();
+}
+WasmVal InterpretedFrame::GetLocalValue(int index) const {
+ return ToImpl(this)->GetLocalValue(index);
+}
+WasmVal InterpretedFrame::GetStackValue(int index) const {
+ return ToImpl(this)->GetStackValue(index);
}
-
-void InterpretedFrame::SetLocalVal(int index, WasmVal val) { UNIMPLEMENTED(); }
-
-void InterpretedFrame::SetExprVal(int pc, WasmVal val) { UNIMPLEMENTED(); }
} // namespace wasm
} // namespace internal