[wasm] Restore eager parallel export-wrapper compilation
As part of moving export wrappers to the isolate, it was tried to compile them lazily on the main thread. This resulted in large slowdowns in some cases, therefore we restore the eager parallel compilation. Bug: chromium:1365726 Change-Id: I9cc8d5728f3a5c71099f0e0fdcc605b37d4d6618 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3905193 Reviewed-by: Jakob Kummerow <jkummerow@chromium.org> Commit-Queue: Manos Koukoutos <manoskouk@chromium.org> Cr-Commit-Position: refs/heads/main@{#83339}
This commit is contained in:
parent
67106ff494
commit
2e8d8f8f86
@ -616,6 +616,7 @@ class CompilationStateImpl {
|
||||
|
||||
std::shared_ptr<JSToWasmWrapperCompilationUnit>
|
||||
GetNextJSToWasmWrapperCompilationUnit();
|
||||
void FinalizeJSToWasmWrappers(Isolate* isolate, const WasmModule* module);
|
||||
|
||||
void OnFinishedUnits(base::Vector<WasmCode*>);
|
||||
void OnFinishedJSToWasmWrapperUnits(int num);
|
||||
@ -1509,6 +1510,13 @@ void TierUpNowForTesting(Isolate* isolate, WasmInstanceObject instance,
|
||||
|
||||
namespace {
|
||||
|
||||
void RecordStats(CodeT codet, Counters* counters) {
|
||||
if (codet.is_off_heap_trampoline()) return;
|
||||
Code code = FromCodeT(codet);
|
||||
counters->wasm_generated_code_size()->Increment(code.raw_body_size());
|
||||
counters->wasm_reloc_size()->Increment(code.relocation_info().length());
|
||||
}
|
||||
|
||||
enum CompilationExecutionResult : int8_t { kNoMoreUnits, kYield };
|
||||
|
||||
CompilationExecutionResult ExecuteJSToWasmWrapperCompilationUnits(
|
||||
@ -1932,6 +1940,8 @@ void CompileNativeModule(Isolate* isolate,
|
||||
return;
|
||||
}
|
||||
|
||||
compilation_state->FinalizeJSToWasmWrappers(isolate, native_module->module());
|
||||
|
||||
compilation_state->WaitForCompilationEvent(
|
||||
CompilationEvent::kFinishedBaselineCompilation);
|
||||
|
||||
@ -1994,6 +2004,7 @@ std::shared_ptr<NativeModule> CompileToNativeModule(
|
||||
std::shared_ptr<NativeModule> native_module = engine->MaybeGetNativeModule(
|
||||
wasm_module->origin, wire_bytes_copy.as_vector(), isolate);
|
||||
if (native_module) {
|
||||
CompileJsToWasmWrappers(isolate, wasm_module);
|
||||
return native_module;
|
||||
}
|
||||
|
||||
@ -2028,6 +2039,7 @@ std::shared_ptr<NativeModule> CompileToNativeModule(
|
||||
if (thrower->error()) return {};
|
||||
|
||||
if (cache_hit) {
|
||||
CompileJsToWasmWrappers(isolate, wasm_module);
|
||||
return native_module;
|
||||
}
|
||||
|
||||
@ -2303,6 +2315,17 @@ void AsyncCompileJob::FinishCompile(bool is_after_cache_hit) {
|
||||
isolate_->debug()->OnAfterCompile(script);
|
||||
}
|
||||
|
||||
// TODO(bbudge) Allow deserialization without wrapper compilation, so we can
|
||||
// just compile wrappers here.
|
||||
if (!is_after_deserialization) {
|
||||
if (is_after_cache_hit) {
|
||||
// TODO(thibaudm): Look into sharing wrappers.
|
||||
CompileJsToWasmWrappers(isolate_, module);
|
||||
} else {
|
||||
compilation_state->FinalizeJSToWasmWrappers(isolate_, module);
|
||||
}
|
||||
}
|
||||
|
||||
// We can only update the feature counts once the entire compile is done.
|
||||
compilation_state->PublishDetectedFeatures(isolate_);
|
||||
|
||||
@ -3524,6 +3547,30 @@ CompilationStateImpl::GetNextJSToWasmWrapperCompilationUnit() {
|
||||
return js_to_wasm_wrapper_units_[outstanding_units - 1];
|
||||
}
|
||||
|
||||
void CompilationStateImpl::FinalizeJSToWasmWrappers(Isolate* isolate,
|
||||
const WasmModule* module) {
|
||||
// TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
|
||||
// optimization we create a code memory modification scope that avoids
|
||||
// changing the page permissions back-and-forth between RWX and RX, because
|
||||
// many such wrapper are allocated in sequence below.
|
||||
TRACE_EVENT1(TRACE_DISABLED_BY_DEFAULT("v8.wasm.detailed"),
|
||||
"wasm.FinalizeJSToWasmWrappers", "wrappers",
|
||||
js_to_wasm_wrapper_units_.size());
|
||||
|
||||
isolate->heap()->EnsureWasmCanonicalRttsSize(module->MaxCanonicalTypeIndex() +
|
||||
1);
|
||||
CodePageCollectionMemoryModificationScope modification_scope(isolate->heap());
|
||||
for (auto& unit : js_to_wasm_wrapper_units_) {
|
||||
DCHECK_EQ(isolate, unit->isolate());
|
||||
Handle<CodeT> code = unit->Finalize();
|
||||
uint32_t index =
|
||||
GetExportWrapperIndex(unit->canonical_sig_index(), unit->is_import());
|
||||
isolate->heap()->js_to_wasm_wrappers().Set(index,
|
||||
MaybeObject::FromObject(*code));
|
||||
RecordStats(*code, isolate->counters());
|
||||
}
|
||||
}
|
||||
|
||||
CompilationUnitQueues::Queue* CompilationStateImpl::GetQueueForCompileTask(
|
||||
int task_id) {
|
||||
return compilation_unit_queues_.GetQueueForTask(task_id);
|
||||
@ -3898,6 +3945,78 @@ class CompileJSToWasmWrapperJob final : public JobTask {
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module) {
|
||||
TRACE_EVENT0("v8.wasm", "wasm.CompileJsToWasmWrappers");
|
||||
|
||||
isolate->heap()->EnsureWasmCanonicalRttsSize(module->MaxCanonicalTypeIndex() +
|
||||
1);
|
||||
|
||||
JSToWasmWrapperQueue queue;
|
||||
JSToWasmWrapperUnitMap compilation_units;
|
||||
WasmFeatures enabled_features = WasmFeatures::FromIsolate(isolate);
|
||||
|
||||
// Prepare compilation units in the main thread.
|
||||
for (auto exp : module->export_table) {
|
||||
if (exp.kind != kExternalFunction) continue;
|
||||
|
||||
auto& function = module->functions[exp.index];
|
||||
uint32_t canonical_type_index =
|
||||
module->isorecursive_canonical_type_ids[function.sig_index];
|
||||
int wrapper_index =
|
||||
GetExportWrapperIndex(canonical_type_index, function.imported);
|
||||
auto existing_wrapper =
|
||||
isolate->heap()->js_to_wasm_wrappers().Get(wrapper_index);
|
||||
if (existing_wrapper.IsStrongOrWeak() &&
|
||||
!existing_wrapper.GetHeapObject().IsUndefined()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
JSToWasmWrapperKey key(function.imported, canonical_type_index);
|
||||
if (queue.insert(key, nullptr)) {
|
||||
auto unit = std::make_unique<JSToWasmWrapperCompilationUnit>(
|
||||
isolate, function.sig, canonical_type_index, module,
|
||||
function.imported, enabled_features,
|
||||
JSToWasmWrapperCompilationUnit::kAllowGeneric);
|
||||
compilation_units.emplace(key, std::move(unit));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// This is nested inside the event above, so the name can be less
|
||||
// descriptive. It's mainly to log the number of wrappers.
|
||||
TRACE_EVENT1("v8.wasm", "wasm.JsToWasmWrapperCompilation", "num_wrappers",
|
||||
compilation_units.size());
|
||||
auto job =
|
||||
std::make_unique<CompileJSToWasmWrapperJob>(&queue, &compilation_units);
|
||||
if (v8_flags.wasm_num_compilation_tasks > 0) {
|
||||
auto job_handle = V8::GetCurrentPlatform()->CreateJob(
|
||||
TaskPriority::kUserVisible, std::move(job));
|
||||
|
||||
// Wait for completion, while contributing to the work.
|
||||
job_handle->Join();
|
||||
} else {
|
||||
job->Run(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
// Finalize compilation jobs in the main thread.
|
||||
// TODO(6792): Wrappers below are allocated with {Factory::NewCode}. As an
|
||||
// optimization we create a code memory modification scope that avoids
|
||||
// changing the page permissions back-and-forth between RWX and RX, because
|
||||
// many such wrapper are allocated in sequence below.
|
||||
CodePageCollectionMemoryModificationScope modification_scope(isolate->heap());
|
||||
for (auto& pair : compilation_units) {
|
||||
JSToWasmWrapperKey key = pair.first;
|
||||
JSToWasmWrapperCompilationUnit* unit = pair.second.get();
|
||||
DCHECK_EQ(isolate, unit->isolate());
|
||||
Handle<CodeT> code = unit->Finalize();
|
||||
int wrapper_index = GetExportWrapperIndex(key.second, key.first);
|
||||
isolate->heap()->js_to_wasm_wrappers().Set(
|
||||
wrapper_index, HeapObjectReference::Strong(*code));
|
||||
RecordStats(*code, isolate->counters());
|
||||
}
|
||||
}
|
||||
|
||||
WasmCode* CompileImportWrapper(
|
||||
NativeModule* native_module, Counters* counters,
|
||||
compiler::WasmImportCallKind kind, const FunctionSig* sig,
|
||||
|
@ -62,6 +62,9 @@ std::shared_ptr<NativeModule> CompileToNativeModule(
|
||||
void RecompileNativeModule(NativeModule* native_module,
|
||||
TieringState new_tiering_state);
|
||||
|
||||
V8_EXPORT_PRIVATE
|
||||
void CompileJsToWasmWrappers(Isolate* isolate, const WasmModule* module);
|
||||
|
||||
// Compiles the wrapper for this (kind, sig) pair and sets the corresponding
|
||||
// cache entry. Assumes the key already exists in the cache but has not been
|
||||
// compiled yet.
|
||||
|
@ -1370,9 +1370,11 @@ WasmInstanceObject::GetOrCreateWasmInternalFunction(
|
||||
wrapper = wasm::JSToWasmWrapperCompilationUnit::CompileJSToWasmWrapper(
|
||||
isolate, function.sig, canonical_sig_index, instance->module(),
|
||||
function.imported);
|
||||
isolate->heap()->js_to_wasm_wrappers().Set(
|
||||
wrapper_index, HeapObjectReference::Weak(*wrapper));
|
||||
}
|
||||
// Store the wrapper in the isolate, or make its reference weak now that we
|
||||
// have a function referencing it.
|
||||
isolate->heap()->js_to_wasm_wrappers().Set(
|
||||
wrapper_index, HeapObjectReference::Weak(*wrapper));
|
||||
auto external = Handle<WasmExternalFunction>::cast(WasmExportedFunction::New(
|
||||
isolate, instance, function_index,
|
||||
static_cast<int>(function.sig->parameter_count()), wrapper));
|
||||
|
Loading…
Reference in New Issue
Block a user