[wasm][capi] Optimize all functions before serialization
The existing implementation of `serialize` in the C-API is to produce a snapshot of the current state of the `NativeModule`. However, so far all users of `serialize` did not care about the runtime of `serialize`, but cared about `deserialize` starting up fast. With this CL all functions of a module get tiered up to TurboFan before serializing the module. R=clemensb@chromium.org Change-Id: Icaef846e33509d90b38559c0b689f798d35a98db Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4129495 Commit-Queue: Andreas Haas <ahaas@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/main@{#85052}
This commit is contained in:
parent
58ae6e4a81
commit
84e470845a
@ -1179,13 +1179,13 @@ auto Module::exports() const -> ownvec<ExportType> {
|
||||
return ExportsImpl(impl(this)->v8_object());
|
||||
}
|
||||
|
||||
// We serialize the state of the module when calling this method; an arbitrary
|
||||
// number of functions can be tiered up to TurboFan, and only those will be
|
||||
// serialized.
|
||||
// The caller is responsible for "warming up" the module before serializing.
|
||||
// We tier up all functions to TurboFan, and then serialize all TurboFan code.
|
||||
// If no TurboFan code existed before calling this function, then the call to
|
||||
// {serialize} may take a long time.
|
||||
auto Module::serialize() const -> vec<byte_t> {
|
||||
i::wasm::NativeModule* native_module =
|
||||
impl(this)->v8_object()->native_module();
|
||||
native_module->compilation_state()->TierUpAllFunctions();
|
||||
v8::base::Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
|
||||
size_t binary_size = wire_bytes.size();
|
||||
i::wasm::WasmSerializer serializer(native_module);
|
||||
@ -1200,8 +1200,10 @@ auto Module::serialize() const -> vec<byte_t> {
|
||||
ptr += binary_size;
|
||||
if (!serializer.SerializeNativeModule(
|
||||
{reinterpret_cast<uint8_t*>(ptr), serial_size})) {
|
||||
// Serialization failed, because no TurboFan code is present yet. In this
|
||||
// case, the serialized module just contains the wire bytes.
|
||||
// Serialization fails if no TurboFan code is present. This may happen
|
||||
// because the module does not have any functions, or because another thread
|
||||
// modifies the {NativeModule} concurrently. In this case, the serialized
|
||||
// module just contains the wire bytes.
|
||||
buffer = vec<byte_t>::make_uninitialized(size_size + binary_size);
|
||||
byte_t* ptr = buffer.get();
|
||||
i::wasm::LEBHelper::write_u64v(reinterpret_cast<uint8_t**>(&ptr),
|
||||
|
@ -172,6 +172,8 @@ class V8_EXPORT_PRIVATE CompilationState {
|
||||
// Set a higher priority for the compilation job.
|
||||
void SetHighPriority();
|
||||
|
||||
void TierUpAllFunctions();
|
||||
|
||||
bool failed() const;
|
||||
bool baseline_compilation_finished() const;
|
||||
|
||||
|
@ -158,7 +158,7 @@ WasmCompilationResult WasmCompilationUnit::ExecuteFunctionCompilation(
|
||||
}
|
||||
|
||||
// static
|
||||
void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
|
||||
void WasmCompilationUnit::CompileWasmFunction(Counters* counters,
|
||||
NativeModule* native_module,
|
||||
WasmFeatures* detected,
|
||||
const WasmFunction* function,
|
||||
@ -174,7 +174,7 @@ void WasmCompilationUnit::CompileWasmFunction(Isolate* isolate,
|
||||
CompilationEnv env = native_module->CreateCompilationEnv();
|
||||
WasmCompilationResult result = unit.ExecuteCompilation(
|
||||
&env, native_module->compilation_state()->GetWireBytesStorage().get(),
|
||||
isolate->counters(), nullptr, detected);
|
||||
counters, nullptr, detected);
|
||||
if (result.succeeded()) {
|
||||
WasmCodeRefScope code_ref_scope;
|
||||
native_module->PublishCode(
|
||||
|
@ -76,7 +76,7 @@ class V8_EXPORT_PRIVATE WasmCompilationUnit final {
|
||||
ForDebugging for_debugging() const { return for_debugging_; }
|
||||
int func_index() const { return func_index_; }
|
||||
|
||||
static void CompileWasmFunction(Isolate*, NativeModule*,
|
||||
static void CompileWasmFunction(Counters*, NativeModule*,
|
||||
WasmFeatures* detected, const WasmFunction*,
|
||||
ExecutionTier);
|
||||
|
||||
|
@ -631,6 +631,8 @@ class CompilationStateImpl {
|
||||
compile_job_->UpdatePriority(TaskPriority::kUserBlocking);
|
||||
}
|
||||
|
||||
void TierUpAllFunctions();
|
||||
|
||||
bool failed() const {
|
||||
return compile_failed_.load(std::memory_order_relaxed);
|
||||
}
|
||||
@ -836,6 +838,10 @@ void CompilationState::AddCallback(
|
||||
|
||||
void CompilationState::SetHighPriority() { Impl(this)->SetHighPriority(); }
|
||||
|
||||
void CompilationState::TierUpAllFunctions() {
|
||||
Impl(this)->TierUpAllFunctions();
|
||||
}
|
||||
|
||||
void CompilationState::InitializeAfterDeserialization(
|
||||
base::Vector<const int> lazy_functions,
|
||||
base::Vector<const int> eager_functions) {
|
||||
@ -1410,7 +1416,8 @@ void TierUpNowForTesting(Isolate* isolate, WasmInstanceObject instance,
|
||||
TransitiveTypeFeedbackProcessor::Process(instance, func_index);
|
||||
}
|
||||
auto* native_module = instance.module_object().native_module();
|
||||
wasm::GetWasmEngine()->CompileFunction(isolate, native_module, func_index,
|
||||
wasm::GetWasmEngine()->CompileFunction(isolate->counters(), native_module,
|
||||
func_index,
|
||||
wasm::ExecutionTier::kTurbofan);
|
||||
CHECK(!native_module->compilation_state()->failed());
|
||||
}
|
||||
@ -3698,6 +3705,45 @@ void CompilationStateImpl::WaitForCompilationEvent(
|
||||
semaphore->Wait();
|
||||
}
|
||||
|
||||
void CompilationStateImpl::TierUpAllFunctions() {
|
||||
const WasmModule* module = native_module_->module();
|
||||
uint32_t num_wasm_functions = module->num_declared_functions;
|
||||
WasmCodeRefScope code_ref_scope;
|
||||
CompilationUnitBuilder builder(native_module_);
|
||||
for (uint32_t i = 0; i < num_wasm_functions; ++i) {
|
||||
int func_index = module->num_imported_functions + i;
|
||||
WasmCode* code = native_module_->GetCode(func_index);
|
||||
if (!code || !code->is_turbofan()) {
|
||||
builder.AddTopTierUnit(func_index, ExecutionTier::kTurbofan);
|
||||
}
|
||||
}
|
||||
builder.Commit();
|
||||
|
||||
// Join the compilation, until no compilation units are left anymore.
|
||||
class DummyDelegate final : public JobDelegate {
|
||||
bool ShouldYield() override { return false; }
|
||||
bool IsJoiningThread() const override { return true; }
|
||||
void NotifyConcurrencyIncrease() override { UNIMPLEMENTED(); }
|
||||
uint8_t GetTaskId() override { return kMainTaskId; }
|
||||
};
|
||||
|
||||
DummyDelegate delegate;
|
||||
ExecuteCompilationUnits(native_module_weak_, async_counters_.get(), &delegate,
|
||||
kBaselineOrTopTier);
|
||||
|
||||
// We cannot wait for other compilation threads to finish, so we explicitly
|
||||
// compile all functions which are not yet available as TurboFan code.
|
||||
for (uint32_t i = 0; i < num_wasm_functions; ++i) {
|
||||
uint32_t func_index = module->num_imported_functions + i;
|
||||
WasmCode* code = native_module_->GetCode(func_index);
|
||||
if (!code || !code->is_turbofan()) {
|
||||
wasm::GetWasmEngine()->CompileFunction(async_counters_.get(),
|
||||
native_module_, func_index,
|
||||
wasm::ExecutionTier::kTurbofan);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
using JSToWasmWrapperQueue = WrapperQueue<JSToWasmWrapperKey, std::nullptr_t,
|
||||
base::hash<JSToWasmWrapperKey>>;
|
||||
|
@ -705,12 +705,13 @@ std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
|
||||
isolate, enabled, context, api_method_name, std::move(resolver));
|
||||
}
|
||||
|
||||
void WasmEngine::CompileFunction(Isolate* isolate, NativeModule* native_module,
|
||||
void WasmEngine::CompileFunction(Counters* counters,
|
||||
NativeModule* native_module,
|
||||
uint32_t function_index, ExecutionTier tier) {
|
||||
// Note we assume that "one-off" compilations can discard detected features.
|
||||
WasmFeatures detected = WasmFeatures::None();
|
||||
WasmCompilationUnit::CompileWasmFunction(
|
||||
isolate, native_module, &detected,
|
||||
counters, native_module, &detected,
|
||||
&native_module->module()->functions[function_index], tier);
|
||||
}
|
||||
|
||||
|
@ -202,7 +202,7 @@ class V8_EXPORT_PRIVATE WasmEngine {
|
||||
// Compiles the function with the given index at a specific compilation tier.
|
||||
// Errors are stored internally in the CompilationState.
|
||||
// This is mostly used for testing to force a function into a specific tier.
|
||||
void CompileFunction(Isolate* isolate, NativeModule* native_module,
|
||||
void CompileFunction(Counters* counters, NativeModule* native_module,
|
||||
uint32_t function_index, ExecutionTier tier);
|
||||
|
||||
void EnterDebuggingForIsolate(Isolate* isolate);
|
||||
|
@ -301,7 +301,7 @@ TEST(SharedEngineRunThreadedTierUp) {
|
||||
Handle<WasmInstanceObject> instance = isolate->ImportInstance(module);
|
||||
WasmFeatures detected = WasmFeatures::None();
|
||||
WasmCompilationUnit::CompileWasmFunction(
|
||||
isolate->isolate(), module.get(), &detected,
|
||||
isolate->isolate()->counters(), module.get(), &detected,
|
||||
&module->module()->functions[0], ExecutionTier::kTurbofan);
|
||||
CHECK_EQ(23, isolate->Run(instance));
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user