[wasm] Optimize decode fastpath

This CL further optimizes the decoding fastpath by moving feature
checks off the critical path. For prototype opcodes that are enabled
by feature flags, they are handled in a switch case off the main
path.

R=mstarzinger@chromium.org

Change-Id: If40fedbaadb9c611c78bc2b7df035ced056cb39a
Reviewed-on: https://chromium-review.googlesource.com/1076187
Reviewed-by: Michael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Ben Titzer <titzer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53406}
This commit is contained in:
Ben L. Titzer 2018-05-29 13:02:30 +02:00 committed by Commit Bot
parent b2abe2cf97
commit 42f179ad02
3 changed files with 47 additions and 28 deletions

View File

@ -1908,6 +1908,14 @@ class WasmFullDecoder : public WasmDecoder<validate> {
len += DecodeAtomicOpcode(opcode);
break;
}
// Note that prototype opcodes are not handled in the fastpath
// above this switch, to avoid checking a feature flag.
#define SIMPLE_PROTOTYPE_CASE(name, opc, sig) \
case kExpr##name: /* fallthrough */
FOREACH_SIMPLE_PROTOTYPE_OPCODE(SIMPLE_PROTOTYPE_CASE)
#undef SIMPLE_PROTOTYPE_CASE
BuildSimplePrototypeOperator(opcode);
break;
default: {
// Deal with special asmjs opcodes.
if (this->module_ != nullptr &&
@ -2423,14 +2431,18 @@ class WasmFullDecoder : public WasmDecoder<validate> {
CALL_INTERFACE(OnFirstError);
}
inline void BuildSimpleOperator(WasmOpcode opcode, FunctionSig* sig) {
void BuildSimplePrototypeOperator(WasmOpcode opcode) {
if (WasmOpcodes::IsSignExtensionOpcode(opcode)) {
RET_ON_PROTOTYPE_OPCODE(se);
}
if (WasmOpcodes::IsAnyRefOpcode(opcode)) {
RET_ON_PROTOTYPE_OPCODE(anyref);
}
FunctionSig* sig = WasmOpcodes::Signature(opcode);
BuildSimpleOperator(opcode, sig);
}
inline void BuildSimpleOperator(WasmOpcode opcode, FunctionSig* sig) {
switch (sig->parameter_count()) {
case 1: {
auto val = Pop(0, sig->GetParam(0));

View File

@ -360,6 +360,7 @@ bool WasmOpcodes::IsAnyRefOpcode(WasmOpcode opcode) {
return false;
}
}
std::ostream& operator<<(std::ostream& os, const FunctionSig& sig) {
if (sig.return_count() == 0) os << "v";
for (auto ret : sig.returns()) {
@ -398,7 +399,7 @@ FOREACH_SIGNATURE(DECLARE_SIG)
#undef DECLARE_SIG
#define DECLARE_SIG_ENTRY(name, ...) &kSig_##name,
constexpr const FunctionSig* kSimpleExprSigs[] = {
constexpr const FunctionSig* kCachedSigs[] = {
nullptr, FOREACH_SIGNATURE(DECLARE_SIG_ENTRY)};
#undef DECLARE_SIG_ENTRY
@ -407,18 +408,11 @@ constexpr const FunctionSig* kSimpleExprSigs[] = {
// encapsulate these constexpr functions in functors.
// TODO(clemensh): Remove this once we require gcc >= 5.0.
struct GetOpcodeSigIndex {
struct GetShortOpcodeSigIndex {
constexpr WasmOpcodeSig operator()(byte opcode) const {
#define CASE(name, opc, sig) opcode == opc ? kSigEnum_##sig:
return FOREACH_SIMPLE_OPCODE(CASE) kSigEnum_None;
#undef CASE
}
};
struct GetSimpleOpcodeSig {
constexpr const FunctionSig* operator()(byte opcode) const {
#define CASE(name, opc, sig) opcode == opc ? &kSig_##sig:
return FOREACH_SIMPLE_OPCODE(CASE) nullptr;
return FOREACH_SIMPLE_OPCODE(CASE) FOREACH_SIMPLE_PROTOTYPE_OPCODE(CASE)
kSigEnum_None;
#undef CASE
}
};
@ -455,8 +449,8 @@ struct GetNumericOpcodeSigIndex {
}
};
constexpr std::array<WasmOpcodeSig, 256> kSimpleExprSigTable =
base::make_array<256>(GetOpcodeSigIndex{});
constexpr std::array<WasmOpcodeSig, 256> kShortSigTable =
base::make_array<256>(GetShortOpcodeSigIndex{});
constexpr std::array<WasmOpcodeSig, 256> kSimpleAsmjsExprSigTable =
base::make_array<256>(GetAsmJsOpcodeSigIndex{});
constexpr std::array<WasmOpcodeSig, 256> kSimdExprSigTable =
@ -466,6 +460,15 @@ constexpr std::array<WasmOpcodeSig, 256> kAtomicExprSigTable =
constexpr std::array<WasmOpcodeSig, 256> kNumericExprSigTable =
base::make_array<256>(GetNumericOpcodeSigIndex{});
// Computes a direct pointer to a cached signature for a simple opcode.
struct GetSimpleOpcodeSig {
constexpr const FunctionSig* operator()(byte opcode) const {
#define CASE(name, opc, sig) opcode == opc ? &kSig_##sig:
return FOREACH_SIMPLE_OPCODE(CASE) nullptr;
#undef CASE
}
};
} // namespace
const std::array<const FunctionSig*, 256> kSimpleOpcodeSigs =
@ -473,26 +476,27 @@ const std::array<const FunctionSig*, 256> kSimpleOpcodeSigs =
FunctionSig* WasmOpcodes::Signature(WasmOpcode opcode) {
switch (opcode >> 8) {
case 0:
return const_cast<FunctionSig*>(kCachedSigs[kShortSigTable[opcode]]);
case kSimdPrefix:
return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimdExprSigTable[opcode & 0xFF]]);
kCachedSigs[kSimdExprSigTable[opcode & 0xFF]]);
case kAtomicPrefix:
return const_cast<FunctionSig*>(
kSimpleExprSigs[kAtomicExprSigTable[opcode & 0xFF]]);
kCachedSigs[kAtomicExprSigTable[opcode & 0xFF]]);
case kNumericPrefix:
return const_cast<FunctionSig*>(
kSimpleExprSigs[kNumericExprSigTable[opcode & 0xFF]]);
kCachedSigs[kNumericExprSigTable[opcode & 0xFF]]);
default:
DCHECK_GT(kSimpleExprSigTable.size(), opcode);
return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimpleExprSigTable[opcode]]);
UNREACHABLE(); // invalid prefix.
return nullptr;
}
}
FunctionSig* WasmOpcodes::AsmjsSignature(WasmOpcode opcode) {
DCHECK_GT(kSimpleAsmjsExprSigTable.size(), opcode);
return const_cast<FunctionSig*>(
kSimpleExprSigs[kSimpleAsmjsExprSigTable[opcode]]);
kCachedSigs[kSimpleAsmjsExprSigTable[opcode]]);
}
// Define constexpr arrays.

View File

@ -218,13 +218,15 @@ using WasmName = Vector<const char>;
V(I32ReinterpretF32, 0xbc, i_f) \
V(I64ReinterpretF64, 0xbd, l_d) \
V(F32ReinterpretI32, 0xbe, f_i) \
V(F64ReinterpretI64, 0xbf, d_l) \
V(I32SExtendI8, 0xc0, i_i) \
V(I32SExtendI16, 0xc1, i_i) \
V(I64SExtendI8, 0xc2, l_l) \
V(I64SExtendI16, 0xc3, l_l) \
V(I64SExtendI32, 0xc4, l_l) \
V(RefIsNull, 0xd1, i_r) \
V(F64ReinterpretI64, 0xbf, d_l)
#define FOREACH_SIMPLE_PROTOTYPE_OPCODE(V) \
V(I32SExtendI8, 0xc0, i_i) \
V(I32SExtendI16, 0xc1, i_i) \
V(I64SExtendI8, 0xc2, l_l) \
V(I64SExtendI16, 0xc3, l_l) \
V(I64SExtendI32, 0xc4, l_l) \
V(RefIsNull, 0xd1, i_r) \
V(RefEq, 0xd2, i_rr)
// For compatibility with Asm.js.
@ -479,6 +481,7 @@ using WasmName = Vector<const char>;
FOREACH_CONTROL_OPCODE(V) \
FOREACH_MISC_OPCODE(V) \
FOREACH_SIMPLE_OPCODE(V) \
FOREACH_SIMPLE_PROTOTYPE_OPCODE(V) \
FOREACH_STORE_MEM_OPCODE(V) \
FOREACH_LOAD_MEM_OPCODE(V) \
FOREACH_MISC_MEM_OPCODE(V) \