Commit 92bf8327 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[wasm] [interpreter] Precompute side table for breaks

Instead of dynamically tracking the block nesting, precompute the
information statically.
The interpreter was already using a side table to store the pc diff for
each break, conditional break and others. The information needed to
adjust the stack was tracked dynamically, however. This CL also
precomputes this information, as it is statically known.
Instead of just storing the pc diff in the side table, we now store the
pc diff, the stack height diff and the arity of the target block.

Local measurements show speedups of 5-6% on average, sometimes >10%.

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

Change-Id: I986cfa989aabe1488f2ff79ddbfbb28aeffe1452
Reviewed-on: https://chromium-review.googlesource.com/485482Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#44837}
parent c2abd807
......@@ -515,6 +515,68 @@ class WasmDecoder : public Decoder {
return 1;
}
}
std::pair<uint32_t, uint32_t> StackEffect(const byte* pc) {
WasmOpcode opcode = static_cast<WasmOpcode>(*pc);
// Handle "simple" opcodes with a fixed signature first.
FunctionSig* sig = WasmOpcodes::Signature(opcode);
if (!sig) sig = WasmOpcodes::AsmjsSignature(opcode);
if (sig) return {sig->parameter_count(), sig->return_count()};
#define DECLARE_OPCODE_CASE(name, opcode, sig) case kExpr##name:
// clang-format off
switch (opcode) {
case kExprSelect:
return {3, 1};
FOREACH_STORE_MEM_OPCODE(DECLARE_OPCODE_CASE)
return {2, 0};
FOREACH_LOAD_MEM_OPCODE(DECLARE_OPCODE_CASE)
case kExprTeeLocal:
case kExprGrowMemory:
return {1, 1};
case kExprSetLocal:
case kExprSetGlobal:
case kExprDrop:
case kExprBrIf:
case kExprBrTable:
case kExprIf:
return {1, 0};
case kExprGetLocal:
case kExprGetGlobal:
case kExprI32Const:
case kExprI64Const:
case kExprF32Const:
case kExprF64Const:
case kExprMemorySize:
return {0, 1};
case kExprCallFunction: {
CallFunctionOperand<true> operand(this, pc);
CHECK(Complete(pc, operand));
return {operand.sig->parameter_count(), operand.sig->return_count()};
}
case kExprCallIndirect: {
CallIndirectOperand<true> operand(this, pc);
CHECK(Complete(pc, operand));
// Indirect calls pop an additional argument for the table index.
return {operand.sig->parameter_count() + 1,
operand.sig->return_count()};
}
case kExprBr:
case kExprBlock:
case kExprLoop:
case kExprEnd:
case kExprElse:
case kExprNop:
case kExprReturn:
case kExprUnreachable:
return {0, 0};
default:
V8_Fatal(__FILE__, __LINE__, "unimplemented opcode: %x", opcode);
return {0, 0};
}
#undef DECLARE_OPCODE_CASE
// clang-format on
}
};
static const int32_t kNullCatch = -1;
......@@ -2055,6 +2117,13 @@ unsigned OpcodeLength(const byte* pc, const byte* end) {
return WasmDecoder::OpcodeLength(&decoder, pc);
}
std::pair<uint32_t, uint32_t> StackEffect(const WasmModule* module,
FunctionSig* sig, const byte* pc,
const byte* end) {
WasmDecoder decoder(module, sig, pc, end);
return decoder.StackEffect(pc);
}
void PrintRawWasmCode(const byte* start, const byte* end) {
AccountingAllocator allocator;
PrintRawWasmCode(&allocator, FunctionBodyForTesting(start, end), nullptr);
......
......@@ -81,12 +81,12 @@ struct BodyLocalDecls {
ZoneVector<ValueType> type_list;
// Constructor initializes the vector.
explicit BodyLocalDecls(Zone* zone) : encoded_size(0), type_list(zone) {}
};
V8_EXPORT_PRIVATE bool DecodeLocalDecls(BodyLocalDecls* decls,
const byte* start, const byte* end);
V8_EXPORT_PRIVATE BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone,
size_t num_locals,
const byte* start,
......@@ -95,6 +95,15 @@ V8_EXPORT_PRIVATE BitVector* AnalyzeLoopAssignmentForTesting(Zone* zone,
// Computes the length of the opcode at the given address.
V8_EXPORT_PRIVATE unsigned OpcodeLength(const byte* pc, const byte* end);
// Computes the stack effect of the opcode at the given address.
// Returns <pop count, push count>.
// Be cautious with control opcodes: This function only covers their immediate,
// local stack effect (e.g. BrIf pops 1, Br pops 0). Those opcodes can have
// non-local stack effect though, which are not covered here.
std::pair<uint32_t, uint32_t> StackEffect(const WasmModule* module,
FunctionSig* sig, const byte* pc,
const byte* end);
// A simple forward iterator for bytecodes.
class V8_EXPORT_PRIVATE BytecodeIterator : public NON_EXPORTED_BASE(Decoder) {
// Base class for both iterators defined below.
......
This diff is collapsed.
......@@ -21,16 +21,27 @@ namespace wasm {
// forward declarations.
struct ModuleBytesEnv;
struct WasmFunction;
struct WasmModule;
class WasmInterpreterInternals;
typedef size_t pc_t;
typedef size_t sp_t;
typedef int32_t pcdiff_t;
typedef uint32_t spdiff_t;
const pc_t kInvalidPc = 0x80000000;
using pc_t = size_t;
using sp_t = size_t;
using pcdiff_t = int32_t;
using spdiff_t = uint32_t;
constexpr pc_t kInvalidPc = 0x80000000;
struct ControlTransferEntry {
// Distance from the instruction to the label to jump to (forward, but can be
// negative).
pcdiff_t pc_diff;
// Delta by which to decrease the stack height.
spdiff_t sp_diff;
// Arity of the block we jump to.
uint32_t target_arity;
};
typedef ZoneMap<pc_t, pcdiff_t> ControlTransferMap;
using ControlTransferMap = ZoneMap<pc_t, ControlTransferEntry>;
// Macro for defining union members.
#define FOREACH_UNION_MEMBER(V) \
......@@ -258,9 +269,8 @@ class V8_EXPORT_PRIVATE WasmInterpreter {
// Computes the control transfers for the given bytecode. Used internally in
// the interpreter, but exposed for testing.
static ControlTransferMap ComputeControlTransfersForTesting(Zone* zone,
const byte* start,
const byte* end);
static ControlTransferMap ComputeControlTransfersForTesting(
Zone* zone, const WasmModule* module, const byte* start, const byte* end);
private:
Zone zone_;
......
......@@ -286,6 +286,17 @@ bool WasmOpcodes::IsPrefixOpcode(WasmOpcode opcode) {
return false;
}
}
bool WasmOpcodes::IsControlOpcode(WasmOpcode opcode) {
switch (opcode) {
#define CHECK_OPCODE(name, opcode, _) \
case kExpr##name: \
return true;
FOREACH_CONTROL_OPCODE(CHECK_OPCODE)
#undef CHECK_OPCODE
default:
return false;
}
}
std::ostream& operator<<(std::ostream& os, const FunctionSig& sig) {
if (sig.return_count() == 0) os << "v";
......
......@@ -597,6 +597,7 @@ class V8_EXPORT_PRIVATE WasmOpcodes {
static FunctionSig* AsmjsSignature(WasmOpcode opcode);
static FunctionSig* AtomicSignature(WasmOpcode opcode);
static bool IsPrefixOpcode(WasmOpcode opcode);
static bool IsControlOpcode(WasmOpcode opcode);
static int TrapReasonToMessageId(TrapReason reason);
static const char* TrapReasonMessage(TrapReason reason);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment