Commit f2b98fa8 authored by Manos Koukoutos's avatar Manos Koukoutos Committed by V8 LUCI CQ

[wasm] Index wrappers by isorecursive canonical type

Before, import and export wrappers were cached based on their
signature. This change
- makes wrapper canonicalization consistent with that of types and
  call_indirect signatures under --wasm-type-canonicalization,
- removes the last uses of signature maps, which will enable us to
  remove them in a future CL.

Change-Id: I512bc234f0ae10e50bd94237e8e675ca47ed13c5
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3891250
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/main@{#83183}
parent 74c2cec6
......@@ -293,6 +293,8 @@ RUNTIME_FUNCTION(Runtime_WasmCompileWrapper) {
const int function_index = function_data->function_index();
const wasm::WasmFunction& function = module->functions[function_index];
const wasm::FunctionSig* sig = function.sig;
const uint32_t canonical_sig_index =
module->isorecursive_canonical_type_ids[function.sig_index];
// The start function is not guaranteed to be registered as
// an exported function (although it is called as one).
......@@ -307,7 +309,7 @@ RUNTIME_FUNCTION(Runtime_WasmCompileWrapper) {
Handle<CodeT> wrapper_code =
wasm::JSToWasmWrapperCompilationUnit::CompileSpecificJSToWasmWrapper(
isolate, sig, module);
isolate, sig, canonical_sig_index, module);
// Replace the wrapper for the function that triggered the tier-up.
// This is to verify that the wrapper is replaced, even if the function
......
......@@ -10,7 +10,7 @@ namespace v8 {
namespace internal {
namespace wasm {
V8_EXPORT_PRIVATE TypeCanonicalizer* GetTypeCanonicalizer() {
TypeCanonicalizer* GetTypeCanonicalizer() {
return GetWasmEngine()->type_canonicalizer();
}
......@@ -55,6 +55,33 @@ void TypeCanonicalizer::AddRecursiveGroup(WasmModule* module, uint32_t size) {
}
}
uint32_t TypeCanonicalizer::AddRecursiveGroup(const FunctionSig* sig) {
base::MutexGuard mutex_guard(&mutex_);
// Types in the signature must be module-independent.
#if DEBUG
for (ValueType type : sig->all()) DCHECK(!type.has_index());
#endif
CanonicalGroup group;
group.types.resize(1);
group.types[0].type_def = TypeDefinition(sig, kNoSuperType);
group.types[0].is_relative_supertype = false;
int canonical_index = FindCanonicalGroup(group);
if (canonical_index < 0) {
canonical_index = static_cast<int>(canonical_supertypes_.size());
// We need to copy the signature in the local zone, or else we risk
// storing a dangling pointer in the future.
auto builder = FunctionSig::Builder(&zone_, sig->return_count(),
sig->parameter_count());
for (auto type : sig->returns()) builder.AddReturn(type);
for (auto type : sig->parameters()) builder.AddParam(type);
const FunctionSig* allocated_sig = builder.Build();
group.types[0].type_def = TypeDefinition(allocated_sig, kNoSuperType);
canonical_groups_.emplace(group, canonical_index);
canonical_supertypes_.emplace_back(kNoSuperType);
}
return canonical_index;
}
// An index in a type gets mapped to a relative index if it is inside the new
// canonical group, or the canonical representative if it is not.
ValueType TypeCanonicalizer::CanonicalizeValueType(
......@@ -88,8 +115,8 @@ bool TypeCanonicalizer::IsCanonicalSubtype(uint32_t sub_index,
return false;
}
// Map all type indices (including supertype) inside {type} to indices relative
// to {recursive_group_start}.
// Map all type indices (including supertype) inside {type} to indices
// relative to {recursive_group_start}.
TypeCanonicalizer::CanonicalType TypeCanonicalizer::CanonicalizeTypeDef(
const WasmModule* module, TypeDefinition type,
uint32_t recursive_group_start) {
......
......@@ -43,6 +43,11 @@ class TypeCanonicalizer {
// Modifies {module->isorecursive_canonical_type_ids}.
V8_EXPORT_PRIVATE void AddRecursiveGroup(WasmModule* module, uint32_t size);
// Adds a module-independent signature as a recursive group, and canonicalizes
// it if an identical is found. Returns the canonical index of the added
// signature.
V8_EXPORT_PRIVATE uint32_t AddRecursiveGroup(const FunctionSig* sig);
// Returns if the type at {sub_index} in {sub_module} is a subtype of the
// type at {super_index} in {super_module} after canonicalization.
V8_EXPORT_PRIVATE bool IsCanonicalSubtype(uint32_t sub_index,
......
......@@ -204,12 +204,13 @@ bool UseGenericWrapper(const FunctionSig* sig) {
} // namespace
JSToWasmWrapperCompilationUnit::JSToWasmWrapperCompilationUnit(
Isolate* isolate, const FunctionSig* sig, const WasmModule* module,
bool is_import, const WasmFeatures& enabled_features,
AllowGeneric allow_generic)
Isolate* isolate, const FunctionSig* sig, uint32_t canonical_sig_index,
const WasmModule* module, bool is_import,
const WasmFeatures& enabled_features, AllowGeneric allow_generic)
: isolate_(isolate),
is_import_(is_import),
sig_(sig),
canonical_sig_index_(canonical_sig_index),
use_generic_wrapper_(allow_generic && UseGenericWrapper(sig) &&
!is_import),
job_(use_generic_wrapper_
......@@ -248,24 +249,27 @@ Handle<CodeT> JSToWasmWrapperCompilationUnit::Finalize() {
// static
Handle<CodeT> JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
Isolate* isolate, const FunctionSig* sig, const WasmModule* module,
bool is_import) {
Isolate* isolate, const FunctionSig* sig, uint32_t canonical_sig_index,
const WasmModule* module, bool is_import) {
// Run the compilation unit synchronously.
WasmFeatures enabled_features = WasmFeatures::FromIsolate(isolate);
JSToWasmWrapperCompilationUnit unit(isolate, sig, module, is_import,
enabled_features, kAllowGeneric);
JSToWasmWrapperCompilationUnit unit(isolate, sig, canonical_sig_index, module,
is_import, enabled_features,
kAllowGeneric);
unit.Execute();
return unit.Finalize();
}
// static
Handle<CodeT> JSToWasmWrapperCompilationUnit::CompileSpecificJSToWasmWrapper(
Isolate* isolate, const FunctionSig* sig, const WasmModule* module) {
Isolate* isolate, const FunctionSig* sig, uint32_t canonical_sig_index,
const WasmModule* module) {
// Run the compilation unit synchronously.
const bool is_import = false;
WasmFeatures enabled_features = WasmFeatures::FromIsolate(isolate);
JSToWasmWrapperCompilationUnit unit(isolate, sig, module, is_import,
enabled_features, kDontAllowGeneric);
JSToWasmWrapperCompilationUnit unit(isolate, sig, canonical_sig_index, module,
is_import, enabled_features,
kDontAllowGeneric);
unit.Execute();
return unit.Finalize();
}
......
......@@ -104,6 +104,7 @@ class V8_EXPORT_PRIVATE JSToWasmWrapperCompilationUnit final {
enum AllowGeneric : bool { kAllowGeneric = true, kDontAllowGeneric = false };
JSToWasmWrapperCompilationUnit(Isolate* isolate, const FunctionSig* sig,
uint32_t canonical_sig_index,
const wasm::WasmModule* module, bool is_import,
const WasmFeatures& enabled_features,
AllowGeneric allow_generic);
......@@ -116,18 +117,20 @@ class V8_EXPORT_PRIVATE JSToWasmWrapperCompilationUnit final {
bool is_import() const { return is_import_; }
const FunctionSig* sig() const { return sig_; }
uint32_t canonical_sig_index() const { return canonical_sig_index_; }
// Run a compilation unit synchronously.
static Handle<CodeT> CompileJSToWasmWrapper(Isolate* isolate,
const FunctionSig* sig,
uint32_t canonical_sig_index,
const WasmModule* module,
bool is_import);
// Run a compilation unit synchronously, but ask for the specific
// wrapper.
static Handle<CodeT> CompileSpecificJSToWasmWrapper(Isolate* isolate,
const FunctionSig* sig,
const WasmModule* module);
static Handle<CodeT> CompileSpecificJSToWasmWrapper(
Isolate* isolate, const FunctionSig* sig, uint32_t canonical_sig_index,
const WasmModule* module);
private:
// Wrapper compilation is bound to an isolate. Concurrent accesses to the
......@@ -137,6 +140,7 @@ class V8_EXPORT_PRIVATE JSToWasmWrapperCompilationUnit final {
Isolate* isolate_;
bool is_import_;
const FunctionSig* sig_;
uint32_t canonical_sig_index_;
bool use_generic_wrapper_;
std::unique_ptr<TurbofanCompilationJob> job_;
};
......
......@@ -1713,7 +1713,8 @@ CompilationExecutionResult ExecuteCompilationUnits(
UNREACHABLE();
}
using JSToWasmWrapperKey = std::pair<bool, FunctionSig>;
// (function is imported, canonical type index)
using JSToWasmWrapperKey = std::pair<bool, uint32_t>;
// Returns the number of units added.
int AddExportWrapperUnits(Isolate* isolate, NativeModule* native_module,
......@@ -1722,11 +1723,14 @@ int AddExportWrapperUnits(Isolate* isolate, NativeModule* native_module,
for (auto exp : native_module->module()->export_table) {
if (exp.kind != kExternalFunction) continue;
auto& function = native_module->module()->functions[exp.index];
JSToWasmWrapperKey key(function.imported, *function.sig);
uint32_t canonical_type_index =
native_module->module()
->isorecursive_canonical_type_ids[function.sig_index];
JSToWasmWrapperKey key(function.imported, canonical_type_index);
if (keys.insert(key).second) {
auto unit = std::make_shared<JSToWasmWrapperCompilationUnit>(
isolate, function.sig, native_module->module(), function.imported,
native_module->enabled_features(),
isolate, function.sig, canonical_type_index, native_module->module(),
function.imported, native_module->enabled_features(),
JSToWasmWrapperCompilationUnit::kAllowGeneric);
builder->AddJSToWasmWrapperUnit(std::move(unit));
}
......@@ -1743,14 +1747,18 @@ int AddImportWrapperUnits(NativeModule* native_module,
keys;
int num_imported_functions = native_module->num_imported_functions();
for (int func_index = 0; func_index < num_imported_functions; func_index++) {
const FunctionSig* sig = native_module->module()->functions[func_index].sig;
if (!IsJSCompatibleSignature(sig, native_module->module(),
const WasmFunction& function =
native_module->module()->functions[func_index];
if (!IsJSCompatibleSignature(function.sig, native_module->module(),
native_module->enabled_features())) {
continue;
}
uint32_t canonical_type_index =
native_module->module()
->isorecursive_canonical_type_ids[function.sig_index];
WasmImportWrapperCache::CacheKey key(
compiler::kDefaultImportCallKind, sig,
static_cast<int>(sig->parameter_count()), kNoSuspend);
compiler::kDefaultImportCallKind, canonical_type_index,
static_cast<int>(function.sig->parameter_count()), kNoSuspend);
auto it = keys.insert(key);
if (it.second) {
// Ensure that all keys exist in the cache, so that we can populate the
......@@ -3553,8 +3561,8 @@ void CompilationStateImpl::FinalizeJSToWasmWrappers(
for (auto& unit : js_to_wasm_wrapper_units_) {
DCHECK_EQ(isolate, unit->isolate());
Handle<CodeT> code = unit->Finalize();
int wrapper_index =
GetExportWrapperIndex(module, unit->sig(), unit->is_import());
int wrapper_index = GetExportWrapperIndex(
module, unit->canonical_sig_index(), unit->is_import());
(*export_wrappers_out)->set(wrapper_index, *code);
RecordStats(*code, isolate->counters());
}
......@@ -3752,11 +3760,14 @@ void CompilationStateImpl::PublishCompilationResults(
DCHECK_LE(0, func_index);
DCHECK_LT(func_index, native_module_->num_functions());
if (func_index < num_imported_functions) {
const FunctionSig* sig =
native_module_->module()->functions[func_index].sig;
const WasmFunction& function =
native_module_->module()->functions[func_index];
uint32_t canonical_type_index =
native_module_->module()
->isorecursive_canonical_type_ids[function.sig_index];
WasmImportWrapperCache::CacheKey key(
compiler::kDefaultImportCallKind, sig,
static_cast<int>(sig->parameter_count()), kNoSuspend);
compiler::kDefaultImportCallKind, canonical_type_index,
static_cast<int>(function.sig->parameter_count()), kNoSuspend);
// If two imported functions have the same key, only one of them should
// have been added as a compilation unit. So it is always the first time
// we compile a wrapper for this key here.
......@@ -3889,8 +3900,8 @@ void CompilationStateImpl::WaitForCompilationEvent(
}
namespace {
using JSToWasmWrapperQueue =
WrapperQueue<JSToWasmWrapperKey, base::hash<JSToWasmWrapperKey>>;
using JSToWasmWrapperQueue = WrapperQueue<JSToWasmWrapperKey, std::nullptr_t,
base::hash<JSToWasmWrapperKey>>;
using JSToWasmWrapperUnitMap =
std::unordered_map<JSToWasmWrapperKey,
std::unique_ptr<JSToWasmWrapperCompilationUnit>,
......@@ -3905,8 +3916,10 @@ class CompileJSToWasmWrapperJob final : public JobTask {
outstanding_units_(queue->size()) {}
void Run(JobDelegate* delegate) override {
while (base::Optional<JSToWasmWrapperKey> key = queue_->pop()) {
JSToWasmWrapperCompilationUnit* unit = (*compilation_units_)[*key].get();
while (base::Optional<std::pair<JSToWasmWrapperKey, std::nullptr_t>> key =
queue_->pop()) {
JSToWasmWrapperCompilationUnit* unit =
(*compilation_units_)[key->first].get();
unit->Execute();
outstanding_units_.fetch_sub(1, std::memory_order_relaxed);
if (delegate && delegate->ShouldYield()) return;
......@@ -3943,10 +3956,13 @@ void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
for (auto exp : module->export_table) {
if (exp.kind != kExternalFunction) continue;
auto& function = module->functions[exp.index];
JSToWasmWrapperKey key(function.imported, *function.sig);
if (queue.insert(key)) {
uint32_t canonical_type_index =
module->isorecursive_canonical_type_ids[function.sig_index];
JSToWasmWrapperKey key(function.imported, canonical_type_index);
if (queue.insert(key, nullptr)) {
auto unit = std::make_unique<JSToWasmWrapperCompilationUnit>(
isolate, function.sig, module, function.imported, enabled_features,
isolate, function.sig, canonical_type_index, module,
function.imported, enabled_features,
JSToWasmWrapperCompilationUnit::kAllowGeneric);
compilation_units.emplace(key, std::move(unit));
}
......@@ -3981,7 +3997,7 @@ void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
JSToWasmWrapperCompilationUnit* unit = pair.second.get();
DCHECK_EQ(isolate, unit->isolate());
Handle<CodeT> code = unit->Finalize();
int wrapper_index = GetExportWrapperIndex(module, &key.second, key.first);
int wrapper_index = GetExportWrapperIndex(module, key.second, key.first);
(*export_wrappers_out)->set(wrapper_index, *code);
RecordStats(*code, isolate->counters());
}
......@@ -3990,12 +4006,13 @@ void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module,
WasmCode* CompileImportWrapper(
NativeModule* native_module, Counters* counters,
compiler::WasmImportCallKind kind, const FunctionSig* sig,
int expected_arity, Suspend suspend,
uint32_t canonical_type_index, int expected_arity, Suspend suspend,
WasmImportWrapperCache::ModificationScope* cache_scope) {
// Entry should exist, so that we don't insert a new one and invalidate
// other threads' iterators/references, but it should not have been compiled
// yet.
WasmImportWrapperCache::CacheKey key(kind, sig, expected_arity, suspend);
WasmImportWrapperCache::CacheKey key(kind, canonical_type_index,
expected_arity, suspend);
DCHECK_NULL((*cache_scope)[key]);
bool source_positions = is_asmjs_module(native_module->module());
// Keep the {WasmCode} alive until we explicitly call {IncRef}.
......
......@@ -74,7 +74,7 @@ V8_EXPORT_PRIVATE
WasmCode* CompileImportWrapper(
NativeModule* native_module, Counters* counters,
compiler::WasmImportCallKind kind, const FunctionSig* sig,
int expected_arity, Suspend suspend,
uint32_t canonical_type_index, int expected_arity, Suspend suspend,
WasmImportWrapperCache::ModificationScope* cache_scope);
// Triggered by the WasmCompileLazy builtin. The return value indicates whether
......@@ -96,14 +96,14 @@ V8_EXPORT_PRIVATE void TriggerTierUp(WasmInstanceObject instance,
void TierUpNowForTesting(Isolate* isolate, WasmInstanceObject instance,
int func_index);
template <typename Key, typename Hash>
template <typename Key, typename KeyInfo, typename Hash>
class WrapperQueue {
public:
// Removes an arbitrary key from the queue and returns it.
// If the queue is empty, returns nullopt.
// Thread-safe.
base::Optional<Key> pop() {
base::Optional<Key> key = base::nullopt;
base::Optional<std::pair<Key, KeyInfo>> pop() {
base::Optional<std::pair<Key, KeyInfo>> key = base::nullopt;
base::MutexGuard lock(&mutex_);
auto it = queue_.begin();
if (it != queue_.end()) {
......@@ -116,7 +116,9 @@ class WrapperQueue {
// Add the given key to the queue and returns true iff the insert was
// successful.
// Not thread-safe.
bool insert(const Key& key) { return queue_.insert(key).second; }
bool insert(const Key& key, KeyInfo key_info) {
return queue_.insert({key, key_info}).second;
}
size_t size() {
base::MutexGuard lock(&mutex_);
......@@ -125,7 +127,7 @@ class WrapperQueue {
private:
base::Mutex mutex_;
std::unordered_set<Key, Hash> queue_;
std::unordered_map<Key, KeyInfo, Hash> queue_;
};
// Encapsulates all the state and steps of an asynchronous compilation.
......
......@@ -684,9 +684,7 @@ class ModuleDecoderTemplate : public Decoder {
const FunctionSig* sig = consume_sig(module_->signature_zone.get());
if (!ok()) break;
module_->add_signature(sig, kNoSuperType);
if (v8_flags.wasm_type_canonicalization) {
type_canon->AddRecursiveGroup(module_.get(), 1);
}
type_canon->AddRecursiveGroup(module_.get(), 1);
break;
}
case kWasmArrayTypeCode:
......@@ -727,17 +725,13 @@ class ModuleDecoderTemplate : public Decoder {
TypeDefinition type = consume_subtype_definition();
if (ok()) module_->add_type(type);
}
if (ok() && v8_flags.wasm_type_canonicalization) {
type_canon->AddRecursiveGroup(module_.get(), group_size);
}
if (ok()) type_canon->AddRecursiveGroup(module_.get(), group_size);
} else {
tracer_.TypeOffset(pc_offset());
TypeDefinition type = consume_subtype_definition();
if (ok()) {
module_->add_type(type);
if (v8_flags.wasm_type_canonicalization) {
type_canon->AddRecursiveGroup(module_.get(), 1);
}
type_canon->AddRecursiveGroup(module_.get(), 1);
}
}
}
......
......@@ -42,8 +42,9 @@ byte* raw_buffer_ptr(MaybeHandle<JSArrayBuffer> buffer, int offset) {
return static_cast<byte*>(buffer.ToHandleChecked()->backing_store()) + offset;
}
using ImportWrapperQueue = WrapperQueue<WasmImportWrapperCache::CacheKey,
WasmImportWrapperCache::CacheKeyHash>;
using ImportWrapperQueue =
WrapperQueue<WasmImportWrapperCache::CacheKey, const FunctionSig*,
WasmImportWrapperCache::CacheKeyHash>;
class CompileImportWrapperJob final : public JobTask {
public:
......@@ -66,12 +67,15 @@ class CompileImportWrapperJob final : public JobTask {
void Run(JobDelegate* delegate) override {
TRACE_EVENT0("v8.wasm", "wasm.CompileImportWrapperJob.Run");
while (base::Optional<WasmImportWrapperCache::CacheKey> key =
queue_->pop()) {
while (base::Optional<std::pair<const WasmImportWrapperCache::CacheKey,
const FunctionSig*>>
key = queue_->pop()) {
// TODO(wasm): Batch code publishing, to avoid repeated locking and
// permission switching.
CompileImportWrapper(native_module_, counters_, key->kind, key->signature,
key->expected_arity, key->suspend, cache_scope_);
CompileImportWrapper(native_module_, counters_, key->first.kind,
key->second, key->first.canonical_type_index,
key->first.expected_arity, key->first.suspend,
cache_scope_);
if (delegate->ShouldYield()) return;
}
}
......@@ -828,9 +832,13 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
if (module_->start_function_index >= 0) {
int start_index = module_->start_function_index;
auto& function = module_->functions[start_index];
uint32_t canonical_sig_index =
module_->isorecursive_canonical_type_ids[module_->functions[start_index]
.sig_index];
Handle<CodeT> wrapper_code =
JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
isolate_, function.sig, module_, function.imported);
isolate_, function.sig, canonical_sig_index, module_,
function.imported);
// TODO(clemensb): Don't generate an exported function for the start
// function. Use CWasmEntry instead.
start_function_ = WasmExportedFunction::New(
......@@ -1158,15 +1166,18 @@ bool InstanceBuilder::ProcessImportedFunction(
WasmImportWrapperCache* cache = native_module->import_wrapper_cache();
// TODO(jkummerow): Consider precompiling CapiCallWrappers in parallel,
// just like other import wrappers.
WasmCode* wasm_code =
cache->MaybeGet(kind, expected_sig, expected_arity, kNoSuspend);
uint32_t canonical_type_index =
module_->isorecursive_canonical_type_ids
[module_->functions[func_index].sig_index];
WasmCode* wasm_code = cache->MaybeGet(kind, canonical_type_index,
expected_arity, kNoSuspend);
if (wasm_code == nullptr) {
WasmCodeRefScope code_ref_scope;
WasmImportWrapperCache::ModificationScope cache_scope(cache);
wasm_code =
compiler::CompileWasmCapiCallWrapper(native_module, expected_sig);
WasmImportWrapperCache::CacheKey key(kind, expected_sig, expected_arity,
kNoSuspend);
WasmImportWrapperCache::CacheKey key(kind, canonical_type_index,
expected_arity, kNoSuspend);
cache_scope[key] = wasm_code;
wasm_code->IncRef();
isolate_->counters()->wasm_generated_code_size()->Increment(
......@@ -1203,8 +1214,11 @@ bool InstanceBuilder::ProcessImportedFunction(
}
NativeModule* native_module = instance->module_object().native_module();
uint32_t canonical_type_index =
module_->isorecursive_canonical_type_ids
[module_->functions[func_index].sig_index];
WasmCode* wasm_code = native_module->import_wrapper_cache()->Get(
kind, expected_sig, expected_arity, resolved.suspend);
kind, canonical_type_index, expected_arity, resolved.suspend);
DCHECK_NOT_NULL(wasm_code);
ImportedFunctionEntry entry(instance, func_index);
if (wasm_code->kind() == WasmCode::kWasmToJsWrapper) {
......@@ -1614,14 +1628,16 @@ void InstanceBuilder::CompileImportWrappers(
expected_arity =
shared.internal_formal_parameter_count_without_receiver();
}
WasmImportWrapperCache::CacheKey key(kind, sig, expected_arity,
resolved.suspend);
uint32_t canonical_type_index =
module_->isorecursive_canonical_type_ids[module_->functions[func_index]
.sig_index];
WasmImportWrapperCache::CacheKey key(kind, canonical_type_index,
expected_arity, resolved.suspend);
if (cache_scope[key] != nullptr) {
// Cache entry already exists, no need to compile it again.
continue;
}
import_wrapper_queue.insert(key);
import_wrapper_queue.insert(key, sig);
}
auto compile_job_task = std::make_unique<CompileImportWrapperJob>(
......
......@@ -23,23 +23,25 @@ WasmCode*& WasmImportWrapperCache::operator[](
}
WasmCode* WasmImportWrapperCache::Get(compiler::WasmImportCallKind kind,
const FunctionSig* sig,
uint32_t canonical_type_index,
int expected_arity,
Suspend suspend) const {
base::MutexGuard lock(&mutex_);
auto it = entry_map_.find({kind, sig, expected_arity, suspend});
auto it =
entry_map_.find({kind, canonical_type_index, expected_arity, suspend});
DCHECK(it != entry_map_.end());
return it->second;
}
WasmCode* WasmImportWrapperCache::MaybeGet(compiler::WasmImportCallKind kind,
const FunctionSig* sig,
uint32_t canonical_type_index,
int expected_arity,
Suspend suspend) const {
base::MutexGuard lock(&mutex_);
auto it = entry_map_.find({kind, sig, expected_arity, suspend});
auto it =
entry_map_.find({kind, canonical_type_index, expected_arity, suspend});
if (it == entry_map_.end()) return nullptr;
return it->second;
}
......
......@@ -28,22 +28,23 @@ using FunctionSig = Signature<ValueType>;
class WasmImportWrapperCache {
public:
struct CacheKey {
CacheKey(const compiler::WasmImportCallKind& _kind, const FunctionSig* _sig,
int _expected_arity, Suspend _suspend)
: kind(_kind),
signature(_sig),
expected_arity(_expected_arity == kDontAdaptArgumentsSentinel
CacheKey(const compiler::WasmImportCallKind& kind,
uint32_t canonical_type_index, int expected_arity, Suspend suspend)
: kind(kind),
canonical_type_index(canonical_type_index),
expected_arity(expected_arity == kDontAdaptArgumentsSentinel
? 0
: _expected_arity),
suspend(_suspend) {}
: expected_arity),
suspend(suspend) {}
bool operator==(const CacheKey& rhs) const {
return kind == rhs.kind && signature == rhs.signature &&
return kind == rhs.kind &&
canonical_type_index == rhs.canonical_type_index &&
expected_arity == rhs.expected_arity && suspend == rhs.suspend;
}
compiler::WasmImportCallKind kind;
const FunctionSig* signature;
uint32_t canonical_type_index;
int expected_arity;
Suspend suspend;
};
......@@ -51,8 +52,8 @@ class WasmImportWrapperCache {
class CacheKeyHash {
public:
size_t operator()(const CacheKey& key) const {
return base::hash_combine(static_cast<uint8_t>(key.kind), key.signature,
key.expected_arity);
return base::hash_combine(static_cast<uint8_t>(key.kind),
key.canonical_type_index, key.expected_arity);
}
};
......@@ -75,11 +76,12 @@ class WasmImportWrapperCache {
// Thread-safe. Assumes the key exists in the map.
V8_EXPORT_PRIVATE WasmCode* Get(compiler::WasmImportCallKind kind,
const FunctionSig* sig, int expected_arity,
Suspend suspend) const;
uint32_t canonical_type_index,
int expected_arity, Suspend suspend) const;
// Thread-safe. Returns nullptr if the key doesn't exist in the map.
WasmCode* MaybeGet(compiler::WasmImportCallKind kind, const FunctionSig* sig,
int expected_arity, Suspend suspend) const;
WasmCode* MaybeGet(compiler::WasmImportCallKind kind,
uint32_t canonical_type_index, int expected_arity,
Suspend suspend) const;
~WasmImportWrapperCache();
......
......@@ -66,29 +66,19 @@ bool LazilyGeneratedNames::Has(uint32_t function_index) {
// static
int MaxNumExportWrappers(const WasmModule* module) {
// For each signature there may exist a wrapper, both for imported and
// internal functions.
return static_cast<int>(module->signature_map.size()) * 2;
}
int GetExportWrapperIndexInternal(const WasmModule* module,
int canonical_sig_index, bool is_import) {
if (is_import) canonical_sig_index += module->signature_map.size();
return canonical_sig_index;
}
int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
bool is_import) {
int canonical_sig_index = module->signature_map.Find(*sig);
CHECK_GE(canonical_sig_index, 0);
return GetExportWrapperIndexInternal(module, canonical_sig_index, is_import);
}
int GetExportWrapperIndex(const WasmModule* module, uint32_t sig_index,
bool is_import) {
uint32_t canonical_sig_index =
module->per_module_canonical_type_ids[sig_index];
return GetExportWrapperIndexInternal(module, canonical_sig_index, is_import);
if (module->isorecursive_canonical_type_ids.empty()) return 0;
// TODO(manoskouk): This will create oversized wrappers for modules with few
// types but large canonical type indices. Move wrappers to isolate to avoid
// this.
uint32_t max_canonical_index =
*std::max_element(module->isorecursive_canonical_type_ids.begin(),
module->isorecursive_canonical_type_ids.end());
return (max_canonical_index + 1) * 2;
}
int GetExportWrapperIndex(const WasmModule* module,
uint32_t canonical_sig_index, bool is_import) {
return 2 * canonical_sig_index + (is_import ? 1 : 0);
}
// static
......
......@@ -629,13 +629,11 @@ size_t EstimateStoredSize(const WasmModule* module);
// Returns the number of possible export wrappers for a given module.
V8_EXPORT_PRIVATE int MaxNumExportWrappers(const WasmModule* module);
// Returns the wrapper index for a function in {module} with signature {sig}
// or {sig_index} and origin defined by {is_import}.
// Prefer to use the {sig_index} consuming version, as it is much faster.
int GetExportWrapperIndex(const WasmModule* module, const FunctionSig* sig,
bool is_import);
int GetExportWrapperIndex(const WasmModule* module, uint32_t sig_index,
bool is_import);
// Returns the wrapper index for a function in {module} with isorecursive
// canonical signature index {canonical_sig_index}, and origin defined by
// {is_import}.
int GetExportWrapperIndex(const WasmModule* module,
uint32_t canonical_sig_index, bool is_import);
// Return the byte offset of the function identified by the given index.
// The offset will be relative to the start of the module bytes.
......
......@@ -548,14 +548,16 @@ void WasmTableObject::UpdateDispatchTables(
instance->module_object().native_module();
wasm::WasmImportWrapperCache* cache = native_module->import_wrapper_cache();
auto kind = compiler::WasmImportCallKind::kWasmToCapi;
wasm::WasmCode* wasm_code =
cache->MaybeGet(kind, &sig, param_count, wasm::kNoSuspend);
uint32_t canonical_type_index =
wasm::GetTypeCanonicalizer()->AddRecursiveGroup(&sig);
wasm::WasmCode* wasm_code = cache->MaybeGet(kind, canonical_type_index,
param_count, wasm::kNoSuspend);
if (wasm_code == nullptr) {
wasm::WasmCodeRefScope code_ref_scope;
wasm::WasmImportWrapperCache::ModificationScope cache_scope(cache);
wasm_code = compiler::CompileWasmCapiCallWrapper(native_module, &sig);
wasm::WasmImportWrapperCache::CacheKey key(kind, &sig, param_count,
wasm::kNoSuspend);
wasm::WasmImportWrapperCache::CacheKey key(kind, canonical_type_index,
param_count, wasm::kNoSuspend);
cache_scope[key] = wasm_code;
wasm_code->IncRef();
isolate->counters()->wasm_generated_code_size()->Increment(
......@@ -567,7 +569,9 @@ void WasmTableObject::UpdateDispatchTables(
// not found; it will simply never match any check.
// It is safe to use this even when v8_flags.wasm_type_canonicalization, as
// the C API cannot refer to user-defined types.
auto sig_id = instance->module()->signature_map.Find(sig);
auto sig_id = v8_flags.wasm_type_canonicalization
? canonical_type_index
: instance->module()->signature_map.Find(sig);
instance->GetIndirectFunctionTable(isolate, table_index)
->Set(entry_index, sig_id, wasm_code->instruction_start(),
WasmCapiFunctionData::cast(
......@@ -1379,10 +1383,10 @@ WasmInstanceObject::GetOrCreateWasmInternalFunction(
Handle<WasmModuleObject> module_object(instance->module_object(), isolate);
const WasmModule* module = module_object->module();
const WasmFunction& function = module->functions[function_index];
uint32_t canonical_sig_index =
module->isorecursive_canonical_type_ids[function.sig_index];
int wrapper_index =
GetExportWrapperIndex(module, function.sig_index, function.imported);
DCHECK_EQ(wrapper_index,
GetExportWrapperIndex(module, function.sig, function.imported));
GetExportWrapperIndex(module, canonical_sig_index, function.imported);
Handle<Object> entry =
FixedArray::get(module_object->export_wrappers(), wrapper_index, isolate);
......@@ -1395,7 +1399,8 @@ WasmInstanceObject::GetOrCreateWasmInternalFunction(
// this signature. We compile it and store the wrapper in the module for
// later use.
wrapper = wasm::JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
isolate, function.sig, instance->module(), function.imported);
isolate, function.sig, canonical_sig_index, instance->module(),
function.imported);
module_object->export_wrappers().set(wrapper_index, *wrapper);
}
auto external = Handle<WasmExternalFunction>::cast(WasmExportedFunction::New(
......
......@@ -77,11 +77,13 @@ extern class WasmExportedFunctionData extends WasmFunctionData {
extern class WasmJSFunctionData extends WasmFunctionData {
serialized_return_count: Smi;
serialized_parameter_count: Smi;
// TODO(7748): Maybe store the canonical type index of the signature instead.
serialized_signature: PodArrayOfWasmValueType;
}
extern class WasmCapiFunctionData extends WasmFunctionData {
embedder_data: Foreign; // Managed<wasm::FuncData>
// TODO(7748): Maybe store the canonical type index of the signature instead.
serialized_signature: PodArrayOfWasmValueType;
}
......
......@@ -37,16 +37,19 @@ TEST(CacheHit) {
auto kind = compiler::WasmImportCallKind::kJSFunctionArityMatch;
auto sig = sigs.i_i();
uint32_t canonical_type_index =
GetTypeCanonicalizer()->AddRecursiveGroup(sig);
int expected_arity = static_cast<int>(sig->parameter_count());
WasmCode* c1 =
CompileImportWrapper(module.get(), isolate->counters(), kind, sig,
expected_arity, kNoSuspend, &cache_scope);
WasmCode* c1 = CompileImportWrapper(module.get(), isolate->counters(), kind,
sig, canonical_type_index, expected_arity,
kNoSuspend, &cache_scope);
CHECK_NOT_NULL(c1);
CHECK_EQ(WasmCode::Kind::kWasmToJsWrapper, c1->kind());
WasmCode* c2 = cache_scope[{kind, sig, expected_arity, kNoSuspend}];
WasmCode* c2 =
cache_scope[{kind, canonical_type_index, expected_arity, kNoSuspend}];
CHECK_NOT_NULL(c2);
CHECK_EQ(c1, c2);
......@@ -63,17 +66,22 @@ TEST(CacheMissSig) {
auto kind = compiler::WasmImportCallKind::kJSFunctionArityMatch;
auto sig1 = sigs.i_i();
int expected_arity1 = static_cast<int>(sig1->parameter_count());
uint32_t canonical_type_index1 =
GetTypeCanonicalizer()->AddRecursiveGroup(sig1);
auto sig2 = sigs.i_ii();
int expected_arity2 = static_cast<int>(sig2->parameter_count());
uint32_t canonical_type_index2 =
GetTypeCanonicalizer()->AddRecursiveGroup(sig2);
WasmCode* c1 =
CompileImportWrapper(module.get(), isolate->counters(), kind, sig1,
expected_arity1, kNoSuspend, &cache_scope);
WasmCode* c1 = CompileImportWrapper(
module.get(), isolate->counters(), kind, sig1, canonical_type_index1,
expected_arity1, kNoSuspend, &cache_scope);
CHECK_NOT_NULL(c1);
CHECK_EQ(WasmCode::Kind::kWasmToJsWrapper, c1->kind());
WasmCode* c2 = cache_scope[{kind, sig2, expected_arity2, kNoSuspend}];
WasmCode* c2 =
cache_scope[{kind, canonical_type_index2, expected_arity2, kNoSuspend}];
CHECK_NULL(c2);
}
......@@ -90,15 +98,18 @@ TEST(CacheMissKind) {
auto kind2 = compiler::WasmImportCallKind::kJSFunctionArityMismatch;
auto sig = sigs.i_i();
int expected_arity = static_cast<int>(sig->parameter_count());
uint32_t canonical_type_index =
GetTypeCanonicalizer()->AddRecursiveGroup(sig);
WasmCode* c1 =
CompileImportWrapper(module.get(), isolate->counters(), kind1, sig,
expected_arity, kNoSuspend, &cache_scope);
WasmCode* c1 = CompileImportWrapper(module.get(), isolate->counters(), kind1,
sig, canonical_type_index, expected_arity,
kNoSuspend, &cache_scope);
CHECK_NOT_NULL(c1);
CHECK_EQ(WasmCode::Kind::kWasmToJsWrapper, c1->kind());
WasmCode* c2 = cache_scope[{kind2, sig, expected_arity, kNoSuspend}];
WasmCode* c2 =
cache_scope[{kind2, canonical_type_index, expected_arity, kNoSuspend}];
CHECK_NULL(c2);
}
......@@ -114,31 +125,39 @@ TEST(CacheHitMissSig) {
auto kind = compiler::WasmImportCallKind::kJSFunctionArityMatch;
auto sig1 = sigs.i_i();
int expected_arity1 = static_cast<int>(sig1->parameter_count());
uint32_t canonical_type_index1 =
GetTypeCanonicalizer()->AddRecursiveGroup(sig1);
auto sig2 = sigs.i_ii();
int expected_arity2 = static_cast<int>(sig2->parameter_count());
uint32_t canonical_type_index2 =
GetTypeCanonicalizer()->AddRecursiveGroup(sig2);
WasmCode* c1 =
CompileImportWrapper(module.get(), isolate->counters(), kind, sig1,
expected_arity1, kNoSuspend, &cache_scope);
WasmCode* c1 = CompileImportWrapper(
module.get(), isolate->counters(), kind, sig1, canonical_type_index1,
expected_arity1, kNoSuspend, &cache_scope);
CHECK_NOT_NULL(c1);
CHECK_EQ(WasmCode::Kind::kWasmToJsWrapper, c1->kind());
WasmCode* c2 = cache_scope[{kind, sig2, expected_arity2, kNoSuspend}];
WasmCode* c2 =
cache_scope[{kind, canonical_type_index2, expected_arity2, kNoSuspend}];
CHECK_NULL(c2);
c2 = CompileImportWrapper(module.get(), isolate->counters(), kind, sig2,
expected_arity2, kNoSuspend, &cache_scope);
canonical_type_index2, expected_arity2, kNoSuspend,
&cache_scope);
CHECK_NE(c1, c2);
WasmCode* c3 = cache_scope[{kind, sig1, expected_arity1, kNoSuspend}];
WasmCode* c3 =
cache_scope[{kind, canonical_type_index1, expected_arity1, kNoSuspend}];
CHECK_NOT_NULL(c3);
CHECK_EQ(c1, c3);
WasmCode* c4 = cache_scope[{kind, sig2, expected_arity2, kNoSuspend}];
WasmCode* c4 =
cache_scope[{kind, canonical_type_index2, expected_arity2, kNoSuspend}];
CHECK_NOT_NULL(c4);
CHECK_EQ(c2, c4);
......
......@@ -82,14 +82,17 @@ TestingModuleBuilder::TestingModuleBuilder(
Handle<JSReceiver> callable = resolved.callable;
WasmImportWrapperCache::ModificationScope cache_scope(
native_module_->import_wrapper_cache());
uint32_t canonical_type_index =
GetTypeCanonicalizer()->AddRecursiveGroup(maybe_import->sig);
WasmImportWrapperCache::CacheKey key(
kind, maybe_import->sig,
kind, canonical_type_index,
static_cast<int>(maybe_import->sig->parameter_count()), kNoSuspend);
auto import_wrapper = cache_scope[key];
if (import_wrapper == nullptr) {
CodeSpaceWriteScope write_scope(native_module_);
import_wrapper = CompileImportWrapper(
native_module_, isolate_->counters(), kind, maybe_import->sig,
canonical_type_index,
static_cast<int>(maybe_import->sig->parameter_count()), kNoSuspend,
&cache_scope);
}
......
......@@ -138,8 +138,8 @@ class TestingModuleBuilder {
DCHECK_EQ(test_module_->types.size(),
test_module_->per_module_canonical_type_ids.size());
test_module_->add_signature(sig, kNoSuperType);
GetTypeCanonicalizer()->AddRecursiveGroup(test_module_.get(), 1);
if (v8_flags.wasm_type_canonicalization) {
GetTypeCanonicalizer()->AddRecursiveGroup(test_module_.get(), 1);
instance_object_->set_isorecursive_canonical_types(
test_module_->isorecursive_canonical_type_ids.data());
}
......
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