[wasm] [interpreter] Store just one pc per frame

Instead of storing call_pc and ret_pc, store just one pc. This
simplifies frame inspection (no distinction between top frame and other
frames). The previous ret_pc can easily be computed from the stored pc,
since we know that we must be at a indirect or direct call site when
returning to a previous frame.
It also slightly simplifies the upcoming CL to call imported functions,
which would also have to set the call_pc.

R=titzer@chromium.org, ahaas@chromium.org
BUG=v8:5822

Change-Id: I5876c09ec36450dc1474a760282fd5e41eab38be
Reviewed-on: https://chromium-review.googlesource.com/453159
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43777}
This commit is contained in:
Clemens Hammacher 2017-03-14 11:46:18 +01:00 committed by Commit Bot
parent a0077f222b
commit 4def292384

View File

@ -936,16 +936,16 @@ class ThreadImpl {
InterpreterCode* code = codemap()->FindCode(function); InterpreterCode* code = codemap()->FindCode(function);
CHECK_NOT_NULL(code); CHECK_NOT_NULL(code);
++num_interpreted_calls_; ++num_interpreted_calls_;
frames_.push_back({code, 0, 0, stack_.size()}); frames_.push_back({code, 0, stack_.size()});
for (size_t i = 0; i < function->sig->parameter_count(); ++i) { for (size_t i = 0; i < function->sig->parameter_count(); ++i) {
stack_.push_back(args[i]); stack_.push_back(args[i]);
} }
frames_.back().ret_pc = InitLocals(code); frames_.back().pc = InitLocals(code);
blocks_.push_back( blocks_.push_back(
{0, stack_.size(), frames_.size(), {0, stack_.size(), frames_.size(),
static_cast<uint32_t>(code->function->sig->return_count())}); static_cast<uint32_t>(code->function->sig->return_count())});
TRACE(" => PushFrame(#%u @%zu)\n", code->function->func_index, TRACE(" => PushFrame(#%u @%zu)\n", code->function->func_index,
frames_.back().ret_pc); frames_.back().pc);
} }
WasmInterpreter::State Run() { WasmInterpreter::State Run() {
@ -954,7 +954,7 @@ class ThreadImpl {
if (state_ == WasmInterpreter::STOPPED || if (state_ == WasmInterpreter::STOPPED ||
state_ == WasmInterpreter::PAUSED) { state_ == WasmInterpreter::PAUSED) {
state_ = WasmInterpreter::RUNNING; state_ = WasmInterpreter::RUNNING;
Execute(frames_.back().code, frames_.back().ret_pc, kRunSteps); Execute(frames_.back().code, frames_.back().pc, kRunSteps);
} }
} while (state_ == WasmInterpreter::STOPPED); } while (state_ == WasmInterpreter::STOPPED);
return state_; return state_;
@ -965,7 +965,7 @@ class ThreadImpl {
if (state_ == WasmInterpreter::STOPPED || if (state_ == WasmInterpreter::STOPPED ||
state_ == WasmInterpreter::PAUSED) { state_ == WasmInterpreter::PAUSED) {
state_ = WasmInterpreter::RUNNING; state_ = WasmInterpreter::RUNNING;
Execute(frames_.back().code, frames_.back().ret_pc, 1); Execute(frames_.back().code, frames_.back().pc, 1);
} }
return state_; return state_;
} }
@ -991,10 +991,10 @@ class ThreadImpl {
DCHECK_LE(0, index); DCHECK_LE(0, index);
DCHECK_GT(frames_.size(), index); DCHECK_GT(frames_.size(), index);
Frame* frame = &frames_[index]; Frame* frame = &frames_[index];
DCHECK_GE(kMaxInt, frame->ret_pc); DCHECK_GE(kMaxInt, frame->pc);
DCHECK_GE(kMaxInt, frame->sp); DCHECK_GE(kMaxInt, frame->sp);
DCHECK_GE(kMaxInt, frame->llimit()); DCHECK_GE(kMaxInt, frame->llimit());
return frame_cons(frame->code->function, static_cast<int>(frame->ret_pc), return frame_cons(frame->code->function, static_cast<int>(frame->pc),
static_cast<int>(frame->sp), static_cast<int>(frame->sp),
static_cast<int>(frame->llimit())); static_cast<int>(frame->llimit()));
} }
@ -1020,8 +1020,7 @@ class ThreadImpl {
// Entries on the stack of functions being evaluated. // Entries on the stack of functions being evaluated.
struct Frame { struct Frame {
InterpreterCode* code; InterpreterCode* code;
pc_t call_pc; pc_t pc;
pc_t ret_pc;
sp_t sp; sp_t sp;
// Limit of parameters. // Limit of parameters.
@ -1060,22 +1059,21 @@ class ThreadImpl {
} }
// Push a frame with arguments already on the stack. // Push a frame with arguments already on the stack.
void PushFrame(InterpreterCode* code, pc_t call_pc, pc_t ret_pc) { void PushFrame(InterpreterCode* code, pc_t pc) {
CHECK_NOT_NULL(code); CHECK_NOT_NULL(code);
DCHECK(!frames_.empty()); DCHECK(!frames_.empty());
++num_interpreted_calls_; ++num_interpreted_calls_;
frames_.back().call_pc = call_pc; frames_.back().pc = pc;
frames_.back().ret_pc = ret_pc;
size_t arity = code->function->sig->parameter_count(); size_t arity = code->function->sig->parameter_count();
DCHECK_GE(stack_.size(), arity); DCHECK_GE(stack_.size(), arity);
// The parameters will overlap the arguments already on the stack. // The parameters will overlap the arguments already on the stack.
frames_.push_back({code, 0, 0, stack_.size() - arity}); frames_.push_back({code, 0, stack_.size() - arity});
blocks_.push_back( blocks_.push_back(
{0, stack_.size(), frames_.size(), {0, stack_.size(), frames_.size(),
static_cast<uint32_t>(code->function->sig->return_count())}); static_cast<uint32_t>(code->function->sig->return_count())});
frames_.back().ret_pc = InitLocals(code); frames_.back().pc = InitLocals(code);
TRACE(" => push func#%u @%zu\n", code->function->func_index, TRACE(" => push func#%u @%zu\n", code->function->func_index,
frames_.back().ret_pc); frames_.back().pc);
} }
pc_t InitLocals(InterpreterCode* code) { pc_t InitLocals(InterpreterCode* code) {
@ -1105,7 +1103,7 @@ class ThreadImpl {
void CommitPc(pc_t pc) { void CommitPc(pc_t pc) {
if (!frames_.empty()) { if (!frames_.empty()) {
frames_.back().ret_pc = pc; frames_.back().pc = pc;
} }
} }
@ -1130,7 +1128,24 @@ class ThreadImpl {
return LookupTarget(code, pc); return LookupTarget(code, pc);
} }
bool DoReturn(InterpreterCode** code, pc_t* pc, pc_t* limit, size_t arity) { pc_t ReturnPc(Decoder* decoder, InterpreterCode* code, pc_t pc) {
switch (code->orig_start[pc]) {
case kExprCallFunction: {
CallFunctionOperand operand(decoder, code->at(pc));
return pc + 1 + operand.length;
}
case kExprCallIndirect: {
CallIndirectOperand operand(decoder, code->at(pc));
return pc + 1 + operand.length;
}
default:
UNREACHABLE();
return 0;
}
}
bool DoReturn(Decoder* decoder, InterpreterCode** code, pc_t* pc, pc_t* limit,
size_t arity) {
DCHECK_GT(frames_.size(), 0); DCHECK_GT(frames_.size(), 0);
// Pop all blocks for this frame. // Pop all blocks for this frame.
while (!blocks_.empty() && blocks_.back().fp == frames_.size()) { while (!blocks_.empty() && blocks_.back().fp == frames_.size()) {
@ -1149,7 +1164,8 @@ class ThreadImpl {
// Return to caller frame. // Return to caller frame.
Frame* top = &frames_.back(); Frame* top = &frames_.back();
*code = top->code; *code = top->code;
*pc = top->ret_pc; decoder->Reset((*code)->start, (*code)->end);
*pc = ReturnPc(decoder, *code, top->pc);
*limit = top->code->end - top->code->start; *limit = top->code->end - top->code->start;
TRACE(" => pop func#%u @%zu\n", (*code)->function->func_index, *pc); TRACE(" => pop func#%u @%zu\n", (*code)->function->func_index, *pc);
DoStackTransfer(dest, arity); DoStackTransfer(dest, arity);
@ -1157,10 +1173,12 @@ class ThreadImpl {
} }
} }
void DoCall(InterpreterCode* target, pc_t* pc, pc_t ret_pc, pc_t* limit) { void DoCall(Decoder* decoder, InterpreterCode* target, pc_t* pc,
PushFrame(target, *pc, ret_pc); pc_t* limit) {
*pc = frames_.back().ret_pc; PushFrame(target, *pc);
*pc = frames_.back().pc;
*limit = target->end - target->start; *limit = target->end - target->start;
decoder->Reset(target->start, target->end);
} }
// Copies {arity} values on the top of the stack down the stack to {dest}, // Copies {arity} values on the top of the stack down the stack to {dest},
@ -1334,14 +1352,12 @@ class ThreadImpl {
} }
case kExprReturn: { case kExprReturn: {
size_t arity = code->function->sig->return_count(); size_t arity = code->function->sig->return_count();
if (!DoReturn(&code, &pc, &limit, arity)) return; if (!DoReturn(&decoder, &code, &pc, &limit, arity)) return;
decoder.Reset(code->start, code->end);
PAUSE_IF_BREAK_FLAG(AfterReturn); PAUSE_IF_BREAK_FLAG(AfterReturn);
continue; continue;
} }
case kExprUnreachable: { case kExprUnreachable: {
DoTrap(kTrapUnreachable, pc); return DoTrap(kTrapUnreachable, pc);
return CommitPc(pc);
} }
case kExprEnd: { case kExprEnd: {
blocks_.pop_back(); blocks_.pop_back();
@ -1398,10 +1414,8 @@ class ThreadImpl {
} }
case kExprCallFunction: { case kExprCallFunction: {
CallFunctionOperand operand(&decoder, code->at(pc)); CallFunctionOperand operand(&decoder, code->at(pc));
InterpreterCode* target = codemap()->GetCode(operand.index); code = codemap()->GetCode(operand.index);
DoCall(target, &pc, pc + 1 + operand.length, &limit); DoCall(&decoder, code, &pc, &limit);
code = target;
decoder.Reset(code->start, code->end);
PAUSE_IF_BREAK_FLAG(AfterCall); PAUSE_IF_BREAK_FLAG(AfterCall);
continue; continue;
} }
@ -1426,9 +1440,8 @@ class ThreadImpl {
} }
} }
DoCall(target, &pc, pc + 1 + operand.length, &limit); DoCall(&decoder, target, &pc, &limit);
code = target; code = target;
decoder.Reset(code->start, code->end);
PAUSE_IF_BREAK_FLAG(AfterCall); PAUSE_IF_BREAK_FLAG(AfterCall);
continue; continue;
} }
@ -1656,9 +1669,9 @@ class ThreadImpl {
if (pc == limit) { if (pc == limit) {
// Fell off end of code; do an implicit return. // Fell off end of code; do an implicit return.
TRACE("@%-3zu: ImplicitReturn\n", pc); TRACE("@%-3zu: ImplicitReturn\n", pc);
if (!DoReturn(&code, &pc, &limit, code->function->sig->return_count())) if (!DoReturn(&decoder, &code, &pc, &limit,
code->function->sig->return_count()))
return; return;
decoder.Reset(code->start, code->end);
PAUSE_IF_BREAK_FLAG(AfterReturn); PAUSE_IF_BREAK_FLAG(AfterReturn);
} }
} }