v8/test/cctest/wasm/test-wasm-import-wrapper-cache.cc
Paolo Severini e6414f6e24 [wasm] Faster wasm-to-js calls with arguments mismatch
Currently WebAssembly always goes through the ArgumentsAdaptorTrampoline
builtin for wasm-to-js calls as soon as there's a mismatch between the
actual number of arguments and the expected number of arguments.

This can be made faster in cases where:
1. the callee has "don't adapt arguments" set, which is often the case
for builtins, or
2. the callee has "skip adapt arguments" set, which is often the case
for strict mode functions.

TurboFan already supports this for JS calls:
https://chromium-review.googlesource.com/c/1482735;
explainer document:
http://bit.ly/v8-faster-calls-with-arguments-mismatch.

Even though it is probably not as common to have arity mismatches in
Wasm->JS calls as it is in JS->JS calls, this still seems a worthwhile
optimization to do.

This CL ports the TurboFan fix to WebAssembly. In particular, the CL
introduces a new WasmImportCallKind (kJSFunctionArityMismatchSkipAdaptor)
for the case where the call to  Builtins_ArgumentsAdaptorTrampoline
can be skipped, and modifies WasmImportWrapperCache::CacheKey to also
consider the arity of the imported JS function.

A micro-benchmark for this change can be found here:
- https://gist.github.com/paolosevMSFT/72c67591170d6163f67c9b03a7e12525#file-adapter-cc
- https://gist.github.com/paolosevMSFT/72c67591170d6163f67c9b03a7e12525#file-adapter_test-js

With this benchmark, we can save a 40% overhead of
Builtins_ArgumentsAdaptorTrampoline for calls that pass too many
arguments, while the savings for calls that pass too few arguments are
less impressive:

                            Before     After
callProperApplication:      563 ms     566 ms
callOverApplication1:       972 ms     562 ms
callOverApplication2:       962 ms     562 ms
callUnderApplication:       949 ms     890 ms


Bug: v8:8909
Change-Id: Id51764e7c422d00ecc4a48704323e11bdca9377f
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2317061
Commit-Queue: Paolo Severini <paolosev@microsoft.com>
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69110}
2020-07-28 15:53:21 +00:00

152 lines
4.9 KiB
C++

// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/compiler/wasm-compiler.h"
#include "src/wasm/function-compiler.h"
#include "src/wasm/module-compiler.h"
#include "src/wasm/wasm-code-manager.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-import-wrapper-cache.h"
#include "src/wasm/wasm-module.h"
#include "test/cctest/cctest.h"
#include "test/common/wasm/test-signatures.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace test_wasm_import_wrapper_cache {
std::shared_ptr<NativeModule> NewModule(Isolate* isolate) {
std::shared_ptr<WasmModule> module(new WasmModule);
constexpr size_t kCodeSizeEstimate = 16384;
auto native_module = isolate->wasm_engine()->NewNativeModule(
isolate, WasmFeatures::All(), std::move(module), kCodeSizeEstimate);
native_module->SetWireBytes({});
return native_module;
}
TEST(CacheHit) {
Isolate* isolate = CcTest::InitIsolateOnce();
auto module = NewModule(isolate);
TestSignatures sigs;
WasmCodeRefScope wasm_code_ref_scope;
WasmImportWrapperCache::ModificationScope cache_scope(
module->import_wrapper_cache());
auto kind = compiler::WasmImportCallKind::kJSFunctionArityMatch;
auto sig = sigs.i_i();
int expected_arity = static_cast<int>(sig->parameter_count());
WasmCode* c1 = CompileImportWrapper(isolate->wasm_engine(), module.get(),
isolate->counters(), kind, sig,
expected_arity, &cache_scope);
CHECK_NOT_NULL(c1);
CHECK_EQ(WasmCode::Kind::kWasmToJsWrapper, c1->kind());
WasmCode* c2 = cache_scope[{kind, sig, expected_arity}];
CHECK_NOT_NULL(c2);
CHECK_EQ(c1, c2);
}
TEST(CacheMissSig) {
Isolate* isolate = CcTest::InitIsolateOnce();
auto module = NewModule(isolate);
TestSignatures sigs;
WasmCodeRefScope wasm_code_ref_scope;
WasmImportWrapperCache::ModificationScope cache_scope(
module->import_wrapper_cache());
auto kind = compiler::WasmImportCallKind::kJSFunctionArityMatch;
auto sig1 = sigs.i_i();
int expected_arity1 = static_cast<int>(sig1->parameter_count());
auto sig2 = sigs.i_ii();
int expected_arity2 = static_cast<int>(sig2->parameter_count());
WasmCode* c1 = CompileImportWrapper(isolate->wasm_engine(), module.get(),
isolate->counters(), kind, sig1,
expected_arity1, &cache_scope);
CHECK_NOT_NULL(c1);
CHECK_EQ(WasmCode::Kind::kWasmToJsWrapper, c1->kind());
WasmCode* c2 = cache_scope[{kind, sig2, expected_arity2}];
CHECK_NULL(c2);
}
TEST(CacheMissKind) {
Isolate* isolate = CcTest::InitIsolateOnce();
auto module = NewModule(isolate);
TestSignatures sigs;
WasmCodeRefScope wasm_code_ref_scope;
WasmImportWrapperCache::ModificationScope cache_scope(
module->import_wrapper_cache());
auto kind1 = compiler::WasmImportCallKind::kJSFunctionArityMatch;
auto kind2 = compiler::WasmImportCallKind::kJSFunctionArityMismatch;
auto sig = sigs.i_i();
int expected_arity = static_cast<int>(sig->parameter_count());
WasmCode* c1 = CompileImportWrapper(isolate->wasm_engine(), module.get(),
isolate->counters(), kind1, sig,
expected_arity, &cache_scope);
CHECK_NOT_NULL(c1);
CHECK_EQ(WasmCode::Kind::kWasmToJsWrapper, c1->kind());
WasmCode* c2 = cache_scope[{kind2, sig, expected_arity}];
CHECK_NULL(c2);
}
TEST(CacheHitMissSig) {
Isolate* isolate = CcTest::InitIsolateOnce();
auto module = NewModule(isolate);
TestSignatures sigs;
WasmCodeRefScope wasm_code_ref_scope;
WasmImportWrapperCache::ModificationScope cache_scope(
module->import_wrapper_cache());
auto kind = compiler::WasmImportCallKind::kJSFunctionArityMatch;
auto sig1 = sigs.i_i();
int expected_arity1 = static_cast<int>(sig1->parameter_count());
auto sig2 = sigs.i_ii();
int expected_arity2 = static_cast<int>(sig2->parameter_count());
WasmCode* c1 = CompileImportWrapper(isolate->wasm_engine(), module.get(),
isolate->counters(), kind, sig1,
expected_arity1, &cache_scope);
CHECK_NOT_NULL(c1);
CHECK_EQ(WasmCode::Kind::kWasmToJsWrapper, c1->kind());
WasmCode* c2 = cache_scope[{kind, sig2, expected_arity2}];
CHECK_NULL(c2);
c2 = CompileImportWrapper(isolate->wasm_engine(), module.get(),
isolate->counters(), kind, sig2, expected_arity2,
&cache_scope);
CHECK_NE(c1, c2);
WasmCode* c3 = cache_scope[{kind, sig1, expected_arity1}];
CHECK_NOT_NULL(c3);
CHECK_EQ(c1, c3);
WasmCode* c4 = cache_scope[{kind, sig2, expected_arity2}];
CHECK_NOT_NULL(c4);
CHECK_EQ(c2, c4);
}
} // namespace test_wasm_import_wrapper_cache
} // namespace wasm
} // namespace internal
} // namespace v8