Chromium Code Reviews| Index: src/wasm/wasm-interpreter.cc | 
| diff --git a/src/wasm/wasm-interpreter.cc b/src/wasm/wasm-interpreter.cc | 
| index 8c23bb2fd8872150f414fcec3e4bfd520b80ea0c..a2134734990cece9d719644c06a16cb260aaaaaa 100644 | 
| --- a/src/wasm/wasm-interpreter.cc | 
| +++ b/src/wasm/wasm-interpreter.cc | 
| @@ -722,54 +722,38 @@ class ControlTransfers : public ZoneObject { | 
| public: | 
| ControlTransferMap map_; | 
| - ControlTransfers(Zone* zone, size_t locals_encoded_size, const byte* start, | 
| - const byte* end) | 
| + ControlTransfers(Zone* zone, ModuleEnv* env, AstLocalDecls* locals, | 
| + const byte* start, const byte* end) | 
| : map_(zone) { | 
| - // A control reference including from PC, from value depth, and whether | 
| - // a value is explicitly passed (e.g. br/br_if/br_table with value). | 
| - struct CRef { | 
| - const byte* pc; | 
| - sp_t value_depth; | 
| - bool explicit_value; | 
| - }; | 
| - | 
| // Represents a control flow label. | 
| struct CLabel : public ZoneObject { | 
| const byte* target; | 
| - size_t value_depth; | 
| - ZoneVector<CRef> refs; | 
| + ZoneVector<const byte*> refs; | 
| - CLabel(Zone* zone, size_t v) | 
| - : target(nullptr), value_depth(v), refs(zone) {} | 
| + explicit CLabel(Zone* zone) : target(nullptr), refs(zone) {} | 
| // Bind this label to the given PC. | 
| - void Bind(ControlTransferMap* map, const byte* start, const byte* pc, | 
| - bool expect_value) { | 
| + void Bind(ControlTransferMap* map, const byte* start, const byte* pc) { | 
| DCHECK_NULL(target); | 
| target = pc; | 
| - for (auto from : refs) { | 
| - auto pcdiff = static_cast<pcdiff_t>(target - from.pc); | 
| - auto spdiff = static_cast<spdiff_t>(from.value_depth - value_depth); | 
| - ControlTransfer::StackAction action = ControlTransfer::kNoAction; | 
| - if (expect_value && !from.explicit_value) { | 
| - action = spdiff == 0 ? ControlTransfer::kPushVoid | 
| - : ControlTransfer::kPopAndRepush; | 
| - } | 
| - pc_t offset = static_cast<size_t>(from.pc - start); | 
| - (*map)[offset] = {pcdiff, spdiff, action}; | 
| + for (auto from_pc : refs) { | 
| + auto pcdiff = static_cast<pcdiff_t>(target - from_pc); | 
| 
 
ahaas
2016/09/16 13:48:19
pcdiff_t is hardly any longer than auto.
 
 | 
| + 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, CRef from) { | 
| - DCHECK_GE(from.value_depth, value_depth); | 
| + void Ref(ControlTransferMap* map, const byte* start, | 
| + const byte* from_pc) { | 
| if (target) { | 
| - auto pcdiff = static_cast<pcdiff_t>(target - from.pc); | 
| - auto spdiff = static_cast<spdiff_t>(from.value_depth - value_depth); | 
| - pc_t offset = static_cast<size_t>(from.pc - start); | 
| - (*map)[offset] = {pcdiff, spdiff, ControlTransfer::kNoAction}; | 
| + // 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); | 
| + refs.push_back(from_pc); | 
| } | 
| } | 
| }; | 
| @@ -780,122 +764,102 @@ class ControlTransfers : public ZoneObject { | 
| CLabel* end_label; | 
| CLabel* else_label; | 
| - void Ref(ControlTransferMap* map, const byte* start, const byte* from_pc, | 
| - size_t from_value_depth, bool explicit_value) { | 
| - end_label->Ref(map, start, {from_pc, from_value_depth, explicit_value}); | 
| + void Ref(ControlTransferMap* map, const byte* start, | 
| + const byte* from_pc) { | 
| + end_label->Ref(map, start, from_pc); | 
| } | 
| }; | 
| // Compute the ControlTransfer map. | 
| - // This works by maintaining a stack of control constructs similar to the | 
| + // This algorithm maintains a stack of control constructs similar to the | 
| // 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. | 
| - // The value stack depth is tracked as {value_depth} and is needed to | 
| - // determine how many values to pop off the stack for explicit and | 
| - // implicit control flow. | 
| - | 
| std::vector<Control> control_stack; | 
| - size_t value_depth = 0; | 
| - for (BytecodeIterator i(start + locals_encoded_size, end); i.has_next(); | 
| - i.next()) { | 
| + 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()) { | 
| WasmOpcode opcode = i.current(); | 
| - TRACE("@%u: control %s (depth = %zu)\n", i.pc_offset(), | 
| - WasmOpcodes::OpcodeName(opcode), value_depth); | 
| + TRACE("@%u: control %s\n", i.pc_offset(), | 
| + WasmOpcodes::OpcodeName(opcode)); | 
| switch (opcode) { | 
| case kExprBlock: { | 
| - TRACE("control @%u $%zu: Block\n", i.pc_offset(), value_depth); | 
| - CLabel* label = new (zone) CLabel(zone, value_depth); | 
| + TRACE("control @%u: Block\n", i.pc_offset()); | 
| + CLabel* label = new (zone) CLabel(zone); | 
| control_stack.push_back({i.pc(), label, nullptr}); | 
| break; | 
| } | 
| case kExprLoop: { | 
| - TRACE("control @%u $%zu: Loop\n", i.pc_offset(), value_depth); | 
| - CLabel* label1 = new (zone) CLabel(zone, value_depth); | 
| - CLabel* label2 = new (zone) CLabel(zone, value_depth); | 
| - control_stack.push_back({i.pc(), label1, nullptr}); | 
| - control_stack.push_back({i.pc(), label2, nullptr}); | 
| - label2->Bind(&map_, start, i.pc(), false); | 
| + 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()); | 
| break; | 
| } | 
| case kExprIf: { | 
| - TRACE("control @%u $%zu: If\n", i.pc_offset(), value_depth); | 
| - value_depth--; | 
| - CLabel* end_label = new (zone) CLabel(zone, value_depth); | 
| - CLabel* else_label = new (zone) CLabel(zone, value_depth); | 
| + 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(), value_depth, false}); | 
| + else_label->Ref(&map_, start, i.pc()); | 
| break; | 
| } | 
| case kExprElse: { | 
| Control* c = &control_stack.back(); | 
| - TRACE("control @%u $%zu: Else\n", i.pc_offset(), value_depth); | 
| - c->end_label->Ref(&map_, start, {i.pc(), value_depth, false}); | 
| - value_depth = c->end_label->value_depth; | 
| + TRACE("control @%u: Else\n", i.pc_offset()); | 
| + c->end_label->Ref(&map_, start, i.pc()); | 
| DCHECK_NOT_NULL(c->else_label); | 
| - c->else_label->Bind(&map_, start, i.pc() + 1, false); | 
| + c->else_label->Bind(&map_, start, i.pc() + 1); | 
| c->else_label = nullptr; | 
| break; | 
| } | 
| case kExprEnd: { | 
| Control* c = &control_stack.back(); | 
| - TRACE("control @%u $%zu: End\n", i.pc_offset(), value_depth); | 
| + TRACE("control @%u: End\n", i.pc_offset()); | 
| if (c->end_label->target) { | 
| // only loops have bound labels. | 
| DCHECK_EQ(kExprLoop, *c->pc); | 
| - control_stack.pop_back(); | 
| - c = &control_stack.back(); | 
| + } else { | 
| + if (c->else_label) c->else_label->Bind(&map_, start, i.pc()); | 
| + c->end_label->Bind(&map_, start, i.pc() + 1); | 
| } | 
| - if (c->else_label) | 
| - c->else_label->Bind(&map_, start, i.pc() + 1, true); | 
| - c->end_label->Ref(&map_, start, {i.pc(), value_depth, false}); | 
| - c->end_label->Bind(&map_, start, i.pc() + 1, true); | 
| - value_depth = c->end_label->value_depth + 1; | 
| control_stack.pop_back(); | 
| break; | 
| } | 
| case kExprBr: { | 
| BreakDepthOperand operand(&i, i.pc()); | 
| - TRACE("control @%u $%zu: Br[arity=%u, depth=%u]\n", i.pc_offset(), | 
| - value_depth, operand.arity, operand.depth); | 
| - value_depth -= operand.arity; | 
| - control_stack[control_stack.size() - operand.depth - 1].Ref( | 
| - &map_, start, i.pc(), value_depth, operand.arity > 0); | 
| - value_depth++; | 
| + 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()); | 
| break; | 
| } | 
| case kExprBrIf: { | 
| BreakDepthOperand operand(&i, i.pc()); | 
| - TRACE("control @%u $%zu: BrIf[arity=%u, depth=%u]\n", i.pc_offset(), | 
| - value_depth, operand.arity, operand.depth); | 
| - value_depth -= (operand.arity + 1); | 
| - control_stack[control_stack.size() - operand.depth - 1].Ref( | 
| - &map_, start, i.pc(), value_depth, operand.arity > 0); | 
| - value_depth++; | 
| + 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()); | 
| break; | 
| } | 
| case kExprBrTable: { | 
| BranchTableOperand operand(&i, i.pc()); | 
| - TRACE("control @%u $%zu: BrTable[arity=%u count=%u]\n", i.pc_offset(), | 
| - value_depth, operand.arity, operand.table_count); | 
| - value_depth -= (operand.arity + 1); | 
| + TRACE("control @%u: BrTable[count=%u]\n", i.pc_offset(), | 
| + operand.table_count); | 
| for (uint32_t j = 0; j < operand.table_count + 1; ++j) { | 
| uint32_t target = operand.read_entry(&i, j); | 
| - control_stack[control_stack.size() - target - 1].Ref( | 
| - &map_, start, i.pc() + j, value_depth, operand.arity > 0); | 
| + Control* c = &control_stack[control_stack.size() - target - 1]; | 
| + c->Ref(&map_, start, i.pc() + j); | 
| } | 
| - value_depth++; | 
| break; | 
| } | 
| default: { | 
| - value_depth = value_depth - OpcodeArity(i.pc(), end) + 1; | 
| break; | 
| } | 
| } | 
| } | 
| + if (!func_label->target) func_label->Bind(&map_, start, end); | 
| } | 
| - ControlTransfer Lookup(pc_t from) { | 
| + pcdiff_t Lookup(pc_t from) { | 
| auto result = map_.find(from); | 
| if (result == map_.end()) { | 
| V8_Fatal(__FILE__, __LINE__, "no control target for pc %zu", from); | 
| @@ -965,9 +929,9 @@ class CodeMap { | 
| if (code->targets == nullptr && 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.decls_encoded_size, | 
| - code->orig_start, code->orig_end); | 
| + ModuleEnv env = {module_, nullptr, kWasmOrigin}; | 
| + code->targets = new (zone_) ControlTransfers( | 
| + zone_, &env, &code->locals, code->orig_start, code->orig_end); | 
| } | 
| return code; | 
| } | 
| @@ -1006,6 +970,7 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| instance_(instance), | 
| stack_(zone), | 
| frames_(zone), | 
| + blocks_(zone), | 
| state_(WasmInterpreter::STOPPED), | 
| break_pc_(kInvalidPc), | 
| trap_reason_(kTrapCount) {} | 
| @@ -1026,6 +991,9 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| stack_.push_back(args[i]); | 
| } | 
| frames_.back().ret_pc = InitLocals(code); | 
| + blocks_.push_back( | 
| + {0, stack_.size(), frames_.size(), | 
| + static_cast<uint32_t>(code->function->sig->return_count())}); | 
| TRACE(" => PushFrame(#%u @%zu)\n", code->function->func_index, | 
| frames_.back().ret_pc); | 
| } | 
| @@ -1074,11 +1042,11 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| return nullptr; | 
| } | 
| - virtual WasmVal GetReturnValue() { | 
| + virtual WasmVal GetReturnValue(int index) { | 
| if (state_ == WasmInterpreter::TRAPPED) return WasmVal(0xdeadbeef); | 
| CHECK_EQ(WasmInterpreter::FINISHED, state_); | 
| - CHECK_EQ(1, stack_.size()); | 
| - return stack_[0]; | 
| + CHECK_LT(static_cast<size_t>(index), stack_.size()); | 
| + return stack_[index]; | 
| } | 
| virtual pc_t GetBreakpointPc() { return break_pc_; } | 
| @@ -1102,10 +1070,18 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| sp_t llimit() { return plimit() + code->locals.total_local_count; } | 
| }; | 
| + struct Block { | 
| + pc_t pc; | 
| + sp_t sp; | 
| + size_t fp; | 
| + unsigned arity; | 
| + }; | 
| + | 
| CodeMap* codemap_; | 
| WasmModuleInstance* instance_; | 
| ZoneVector<WasmVal> stack_; | 
| ZoneVector<Frame> frames_; | 
| + ZoneVector<Block> blocks_; | 
| WasmInterpreter::State state_; | 
| pc_t break_pc_; | 
| TrapReason trap_reason_; | 
| @@ -1130,6 +1106,9 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| DCHECK_GE(stack_.size(), arity); | 
| // The parameters will overlap the arguments already on the stack. | 
| frames_.push_back({code, 0, 0, stack_.size() - arity}); | 
| + blocks_.push_back( | 
| + {0, stack_.size(), frames_.size(), | 
| + static_cast<uint32_t>(code->function->sig->return_count())}); | 
| frames_.back().ret_pc = InitLocals(code); | 
| TRACE(" => push func#%u @%zu\n", code->function->func_index, | 
| frames_.back().ret_pc); | 
| @@ -1168,21 +1147,38 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| bool SkipBreakpoint(InterpreterCode* code, pc_t pc) { | 
| if (pc == break_pc_) { | 
| + // Skip the previously hit breakpoint when resuming. | 
| break_pc_ = kInvalidPc; | 
| return true; | 
| } | 
| return false; | 
| } | 
| - bool DoReturn(InterpreterCode** code, pc_t* pc, pc_t* limit, WasmVal val) { | 
| + int LookupTarget(InterpreterCode* code, pc_t pc) { | 
| + return static_cast<int>(code->targets->Lookup(pc)); | 
| + } | 
| + | 
| + 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); | 
| + } | 
| + | 
| + bool DoReturn(InterpreterCode** code, pc_t* pc, pc_t* limit, size_t arity) { | 
| DCHECK_GT(frames_.size(), 0u); | 
| - stack_.resize(frames_.back().sp); | 
| + // Pop all blocks for this frame. | 
| + while (blocks_.size() > 0 && blocks_.back().fp == frames_.size()) { | 
| + blocks_.pop_back(); | 
| + } | 
| + | 
| + sp_t dest = frames_.back().sp; | 
| frames_.pop_back(); | 
| if (frames_.size() == 0) { | 
| - // A return from the top frame terminates the execution. | 
| + // A return from the last frame terminates the execution. | 
| state_ = WasmInterpreter::FINISHED; | 
| - stack_.clear(); | 
| - stack_.push_back(val); | 
| + DoStackTransfer(0, arity); | 
| TRACE(" => finish\n"); | 
| return false; | 
| } else { | 
| @@ -1191,16 +1187,8 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| *code = top->code; | 
| *pc = top->ret_pc; | 
| *limit = top->code->end - top->code->start; | 
| - if (top->code->start[top->call_pc] == kExprCallIndirect || | 
| - (top->code->orig_start && | 
| - top->code->orig_start[top->call_pc] == kExprCallIndirect)) { | 
| - // UGLY: An indirect call has the additional function index on the | 
| - // stack. | 
| - stack_.pop_back(); | 
| - } | 
| TRACE(" => pop func#%u @%zu\n", (*code)->function->func_index, *pc); | 
| - | 
| - stack_.push_back(val); | 
| + DoStackTransfer(dest, arity); | 
| return true; | 
| } | 
| } | 
| @@ -1211,31 +1199,21 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| *limit = target->end - target->start; | 
| } | 
| - // Adjust the program counter {pc} and the stack contents according to the | 
| - // code's precomputed control transfer map. Returns the different between | 
| - // the new pc and the old pc. | 
| - int DoControlTransfer(InterpreterCode* code, pc_t pc) { | 
| - auto target = code->targets->Lookup(pc); | 
| - switch (target.action) { | 
| - case ControlTransfer::kNoAction: | 
| - TRACE(" action [sp-%u]\n", target.spdiff); | 
| - PopN(target.spdiff); | 
| - break; | 
| - case ControlTransfer::kPopAndRepush: { | 
| - WasmVal val = Pop(); | 
| - TRACE(" action [pop x, sp-%u, push x]\n", target.spdiff - 1); | 
| - DCHECK_GE(target.spdiff, 1u); | 
| - PopN(target.spdiff - 1); | 
| - Push(pc, val); | 
| - break; | 
| - } | 
| - case ControlTransfer::kPushVoid: | 
| - TRACE(" action [sp-%u, push void]\n", target.spdiff); | 
| - PopN(target.spdiff); | 
| - Push(pc, WasmVal()); | 
| - break; | 
| + // 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) { | 
| + // before: |---------------| pop_count | arity | | 
| + // ^ 0 ^ dest ^ stack_.size() | 
| + // | 
| + // 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]; | 
| } | 
| - return target.pcdiff; | 
| + stack_.resize(stack_.size() - pop_count); | 
| } | 
| void Execute(InterpreterCode* code, pc_t pc, int max) { | 
| @@ -1251,8 +1229,8 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| if (pc >= limit) { | 
| // Fell off end of code; do an implicit return. | 
| TRACE("@%-3zu: ImplicitReturn\n", pc); | 
| - WasmVal val = PopArity(code->function->sig->return_count()); | 
| - if (!DoReturn(&code, &pc, &limit, val)) return; | 
| + if (!DoReturn(&code, &pc, &limit, code->function->sig->return_count())) | 
| + return; | 
| decoder.Reset(code->start, code->end); | 
| continue; | 
| } | 
| @@ -1285,27 +1263,37 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| switch (orig) { | 
| case kExprNop: | 
| - Push(pc, WasmVal()); | 
| break; | 
| - case kExprBlock: | 
| + case kExprBlock: { | 
| + BlockTypeOperand operand(&decoder, code->at(pc)); | 
| + blocks_.push_back({pc, stack_.size(), frames_.size(), operand.arity}); | 
| + len = 1 + operand.length; | 
| + break; | 
| + } | 
| case kExprLoop: { | 
| - // Do nothing. | 
| + BlockTypeOperand operand(&decoder, code->at(pc)); | 
| + blocks_.push_back({pc, stack_.size(), frames_.size(), 0}); | 
| + len = 1 + operand.length; | 
| break; | 
| } | 
| case kExprIf: { | 
| + BlockTypeOperand 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 = DoControlTransfer(code, pc); | 
| + len = LookupTarget(code, pc); | 
| TRACE(" false => @%zu\n", pc + len); | 
| } | 
| break; | 
| } | 
| case kExprElse: { | 
| - len = DoControlTransfer(code, pc); | 
| + blocks_.pop_back(); | 
| + len = LookupTarget(code, pc); | 
| TRACE(" end => @%zu\n", pc + len); | 
| break; | 
| } | 
| @@ -1318,42 +1306,34 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| } | 
| case kExprBr: { | 
| BreakDepthOperand operand(&decoder, code->at(pc)); | 
| - WasmVal val = PopArity(operand.arity); | 
| - len = DoControlTransfer(code, pc); | 
| + len = DoBreak(code, pc, operand.depth); | 
| TRACE(" br => @%zu\n", pc + len); | 
| - if (operand.arity > 0) Push(pc, val); | 
| break; | 
| } | 
| case kExprBrIf: { | 
| BreakDepthOperand operand(&decoder, code->at(pc)); | 
| WasmVal cond = Pop(); | 
| - WasmVal val = PopArity(operand.arity); | 
| bool is_true = cond.to<uint32_t>() != 0; | 
| if (is_true) { | 
| - len = DoControlTransfer(code, pc); | 
| + len = DoBreak(code, pc, operand.depth); | 
| TRACE(" br_if => @%zu\n", pc + len); | 
| - if (operand.arity > 0) Push(pc, val); | 
| } else { | 
| TRACE(" false => fallthrough\n"); | 
| len = 1 + operand.length; | 
| - Push(pc, WasmVal()); | 
| } | 
| break; | 
| } | 
| case kExprBrTable: { | 
| BranchTableOperand operand(&decoder, code->at(pc)); | 
| uint32_t key = Pop().to<uint32_t>(); | 
| - WasmVal val = PopArity(operand.arity); | 
| if (key >= operand.table_count) key = operand.table_count; | 
| - len = DoControlTransfer(code, pc + key) + key; | 
| - TRACE(" br[%u] => @%zu\n", key, pc + len); | 
| - if (operand.arity > 0) Push(pc, val); | 
| + len = key + DoBreak(code, pc + key, operand.table[key]); | 
| + TRACE(" br[%u] => @%zu\n", key, pc + key + len); | 
| break; | 
| } | 
| case kExprReturn: { | 
| - ReturnArityOperand operand(&decoder, code->at(pc)); | 
| - WasmVal val = PopArity(operand.arity); | 
| - if (!DoReturn(&code, &pc, &limit, val)) return; | 
| + size_t arity = code->function->sig->return_count(); | 
| + if (!DoReturn(&code, &pc, &limit, arity)) return; | 
| decoder.Reset(code->start, code->end); | 
| continue; | 
| } | 
| @@ -1362,8 +1342,7 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| return CommitPc(pc); | 
| } | 
| case kExprEnd: { | 
| - len = DoControlTransfer(code, pc); | 
| - DCHECK_EQ(1, len); | 
| + blocks_.pop_back(); | 
| break; | 
| } | 
| case kExprI8Const: { | 
| @@ -1406,10 +1385,21 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| LocalIndexOperand operand(&decoder, code->at(pc)); | 
| WasmVal val = Pop(); | 
| stack_[frames_.back().sp + operand.index] = val; | 
| + len = 1 + operand.length; | 
| + break; | 
| + } | 
| + case kExprTeeLocal: { | 
| + LocalIndexOperand operand(&decoder, code->at(pc)); | 
| + WasmVal val = Pop(); | 
| + stack_[frames_.back().sp + operand.index] = val; | 
| Push(pc, val); | 
| len = 1 + operand.length; | 
| break; | 
| } | 
| + case kExprDrop: { | 
| + Pop(); | 
| + break; | 
| + } | 
| case kExprCallFunction: { | 
| CallFunctionOperand operand(&decoder, code->at(pc)); | 
| InterpreterCode* target = codemap()->GetCode(operand.index); | 
| @@ -1420,9 +1410,7 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| } | 
| case kExprCallIndirect: { | 
| CallIndirectOperand operand(&decoder, code->at(pc)); | 
| - size_t index = stack_.size() - operand.arity - 1; | 
| - DCHECK_LT(index, stack_.size()); | 
| - uint32_t entry_index = stack_[index].to<uint32_t>(); | 
| + uint32_t entry_index = Pop().to<uint32_t>(); | 
| // Assume only one table for now. | 
| DCHECK_LE(module()->function_tables.size(), 1u); | 
| InterpreterCode* target = codemap()->GetIndirectCode(0, entry_index); | 
| @@ -1437,10 +1425,6 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| decoder.Reset(code->start, code->end); | 
| continue; | 
| } | 
| - case kExprCallImport: { | 
| - UNIMPLEMENTED(); | 
| - break; | 
| - } | 
| case kExprGetGlobal: { | 
| GlobalIndexOperand operand(&decoder, code->at(pc)); | 
| const WasmGlobal* global = &module()->globals[operand.index]; | 
| @@ -1479,7 +1463,6 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| } else { | 
| UNREACHABLE(); | 
| } | 
| - Push(pc, val); | 
| len = 1 + operand.length; | 
| break; | 
| } | 
| @@ -1528,7 +1511,6 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| } \ | 
| byte* addr = instance()->mem_start + operand.offset + index; \ | 
| WriteLittleEndianValue<mtype>(addr, static_cast<mtype>(val.to<ctype>())); \ | 
| - Push(pc, val); \ | 
| len = 1 + operand.length; \ | 
| break; \ | 
| } | 
| @@ -1669,7 +1651,7 @@ class ThreadImpl : public WasmInterpreter::Thread { | 
| void Push(pc_t pc, WasmVal val) { | 
| // TODO(titzer): store PC as well? | 
| - stack_.push_back(val); | 
| + if (val.type != kAstStmt) stack_.push_back(val); | 
| } | 
| void TraceStack(const char* phase, pc_t pc) { | 
| @@ -1850,7 +1832,7 @@ bool WasmInterpreter::SetFunctionCodeForTesting(const WasmFunction* function, | 
| ControlTransferMap WasmInterpreter::ComputeControlTransfersForTesting( | 
| Zone* zone, const byte* start, const byte* end) { | 
| - ControlTransfers targets(zone, 0, start, end); | 
| + ControlTransfers targets(zone, nullptr, nullptr, start, end); | 
| return targets.map_; | 
| } |