Commit 30ce1ba6 authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[wasm] Avoid serializing {TransferrableModule} if possible.

This avoids serializing and deserializing the sharable part of a module
when it is transferred via {TransferrableModule}, which is possible when
all Isolates run off the same engine via the --wasm-shared-engine flag.

This adds a new --wasm-shared-code flag to enable this feature.

R=ahaas@chromium.org
BUG=v8:7424

Cq-Include-Trybots: luci.chromium.try:linux_chromium_rel_ng
Change-Id: I099d581d7ccc4d058a4646f545a011745fd84eb4
Reviewed-on: https://chromium-review.googlesource.com/1142144
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54806}
parent ec067322
......@@ -155,7 +155,7 @@ class FunctionCallbackArguments;
class GlobalHandles;
namespace wasm {
class CompilationResultResolver;
class NativeModule;
class StreamingDecoder;
} // namespace wasm
......@@ -4422,13 +4422,17 @@ class V8_EXPORT WasmCompiledModule : public Object {
TransferrableModule& operator=(const TransferrableModule& src) = delete;
private:
typedef std::shared_ptr<internal::wasm::NativeModule> SharedModule;
typedef std::pair<std::unique_ptr<const uint8_t[]>, size_t> OwnedBuffer;
friend class WasmCompiledModule;
TransferrableModule(OwnedBuffer code, OwnedBuffer bytes)
: compiled_code(std::move(code)), wire_bytes(std::move(bytes)) {}
OwnedBuffer compiled_code = {nullptr, 0};
OwnedBuffer wire_bytes = {nullptr, 0};
explicit TransferrableModule(SharedModule shared_module)
: shared_module_(std::move(shared_module)) {}
TransferrableModule(OwnedBuffer serialized, OwnedBuffer bytes)
: serialized_(std::move(serialized)), wire_bytes_(std::move(bytes)) {}
SharedModule shared_module_;
OwnedBuffer serialized_ = {nullptr, 0};
OwnedBuffer wire_bytes_ = {nullptr, 0};
};
/**
......
......@@ -7659,31 +7659,37 @@ Local<String> WasmCompiledModule::GetWasmWireBytes() {
.ToLocalChecked();
}
// Currently, wasm modules are bound, both to Isolate and to
// the Context they were created in. The currently-supported means to
// decontextualize and then re-contextualize a module is via
// serialization/deserialization.
WasmCompiledModule::TransferrableModule
WasmCompiledModule::GetTransferrableModule() {
i::DisallowHeapAllocation no_gc;
WasmCompiledModule::SerializedModule compiled_part = Serialize();
BufferReference wire_bytes_ref = GetWasmWireBytesRef();
size_t wire_size = wire_bytes_ref.size;
std::unique_ptr<uint8_t[]> wire_bytes_copy(new uint8_t[wire_size]);
memcpy(wire_bytes_copy.get(), wire_bytes_ref.start, wire_size);
return TransferrableModule(std::move(compiled_part),
{std::move(wire_bytes_copy), wire_size});
if (i::FLAG_wasm_shared_code) {
i::Handle<i::WasmModuleObject> obj =
i::Handle<i::WasmModuleObject>::cast(Utils::OpenHandle(this));
return TransferrableModule(obj->managed_native_module()->get());
} else {
WasmCompiledModule::SerializedModule serialized_module = Serialize();
BufferReference wire_bytes_ref = GetWasmWireBytesRef();
size_t wire_size = wire_bytes_ref.size;
std::unique_ptr<uint8_t[]> wire_bytes_copy(new uint8_t[wire_size]);
memcpy(wire_bytes_copy.get(), wire_bytes_ref.start, wire_size);
return TransferrableModule(std::move(serialized_module),
{std::move(wire_bytes_copy), wire_size});
}
}
MaybeLocal<WasmCompiledModule> WasmCompiledModule::FromTransferrableModule(
Isolate* isolate,
const WasmCompiledModule::TransferrableModule& transferrable_module) {
MaybeLocal<WasmCompiledModule> ret =
Deserialize(isolate, AsReference(transferrable_module.compiled_code),
AsReference(transferrable_module.wire_bytes));
return ret;
if (i::FLAG_wasm_shared_code) {
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::Handle<i::WasmModuleObject> module_object =
i_isolate->wasm_engine()->ImportNativeModule(
i_isolate, transferrable_module.shared_module_);
return Local<WasmCompiledModule>::Cast(
Utils::ToLocal(i::Handle<i::JSObject>::cast(module_object)));
} else {
return Deserialize(isolate, AsReference(transferrable_module.serialized_),
AsReference(transferrable_module.wire_bytes_));
}
}
WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() {
......
......@@ -615,6 +615,8 @@ DEFINE_BOOL(wasm_no_stack_checks, false,
DEFINE_BOOL(wasm_shared_engine, false,
"shares one wasm engine between all isolates within a process")
DEFINE_BOOL(wasm_shared_code, false,
"shares code underlying a wasm module when it is transferred")
DEFINE_BOOL(wasm_trap_handler, true,
"use signal handlers to catch out of bounds memory access in wasm"
" (currently Linux x86_64 only)")
......
......@@ -333,6 +333,7 @@ class V8_EXPORT_PRIVATE NativeModule final {
wire_bytes_ = std::move(wire_bytes);
}
const WasmModule* module() const { return module_.get(); }
WasmCodeManager* code_manager() const { return wasm_code_manager_; }
WasmCode* Lookup(Address) const;
......
......@@ -11,7 +11,7 @@
#include "src/wasm/module-compiler.h"
#include "src/wasm/module-decoder.h"
#include "src/wasm/streaming-decoder.h"
#include "src/wasm/wasm-objects.h"
#include "src/wasm/wasm-objects-inl.h"
namespace v8 {
namespace internal {
......@@ -161,6 +161,27 @@ std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
return job->CreateStreamingDecoder();
}
std::shared_ptr<NativeModule> WasmEngine::ExportNativeModule(
Handle<WasmModuleObject> module_object) {
return module_object->managed_native_module()->get();
}
Handle<WasmModuleObject> WasmEngine::ImportNativeModule(
Isolate* isolate, std::shared_ptr<NativeModule> shared_module) {
CHECK_EQ(code_manager(), shared_module->code_manager());
Vector<const byte> wire_bytes = shared_module->wire_bytes();
Handle<Script> script = CreateWasmScript(isolate, wire_bytes);
Handle<WasmModuleObject> module_object =
WasmModuleObject::New(isolate, shared_module, script);
// TODO(6792): Wrappers below might be cloned using {Factory::CopyCode}.
// This requires unlocking the code space here. This should eventually be
// moved into the allocator.
CodeSpaceMemoryModificationScope modification_scope(isolate->heap());
CompileJsToWasmWrappers(isolate, module_object);
return module_object;
}
CompilationStatistics* WasmEngine::GetOrCreateTurboStatistics() {
base::LockGuard<base::Mutex> guard(&mutex_);
if (compilation_stats_ == nullptr) {
......
......@@ -88,6 +88,16 @@ class V8_EXPORT_PRIVATE WasmEngine {
Isolate* isolate, Handle<Context> context,
std::unique_ptr<CompilationResultResolver> resolver);
// Exports the sharable parts of the given module object so that they can be
// transferred to a different Context/Isolate using the same engine.
std::shared_ptr<NativeModule> ExportNativeModule(
Handle<WasmModuleObject> module_object);
// Imports the shared part of a module from a different Context/Isolate using
// the the same engine, recreating a full module object in the given Isolate.
Handle<WasmModuleObject> ImportNativeModule(
Isolate* isolate, std::shared_ptr<NativeModule> shared_module);
WasmCodeManager* code_manager() const { return code_manager_.get(); }
WasmMemoryTracker* memory_tracker() { return &memory_tracker_; }
......
......@@ -269,46 +269,74 @@ TEST(BlockWasmCodeGenAtDeserialization) {
Cleanup();
}
TEST(TransferrableWasmModules) {
namespace {
void TestTransferrableWasmModules(bool should_share) {
i::wasm::WasmEngine::InitializeOncePerProcess();
v8::internal::AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME);
ZoneBuffer buffer(&zone);
WasmSerializationTest::BuildWireBytes(&zone, &buffer);
Isolate* from_isolate = CcTest::InitIsolateOnce();
ErrorThrower thrower(from_isolate, "");
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* from_isolate = v8::Isolate::New(create_params);
std::vector<v8::WasmCompiledModule::TransferrableModule> store;
std::shared_ptr<NativeModule> original_native_module;
{
HandleScope scope(from_isolate);
testing::SetupIsolateForWasmModule(from_isolate);
MaybeHandle<WasmModuleObject> module_object =
from_isolate->wasm_engine()->SyncCompile(
from_isolate, &thrower,
v8::HandleScope scope(from_isolate);
LocalContext env(from_isolate);
Isolate* from_i_isolate = reinterpret_cast<Isolate*>(from_isolate);
testing::SetupIsolateForWasmModule(from_i_isolate);
ErrorThrower thrower(from_i_isolate, "TestTransferrableWasmModules");
MaybeHandle<WasmModuleObject> maybe_module_object =
from_i_isolate->wasm_engine()->SyncCompile(
from_i_isolate, &thrower,
ModuleWireBytes(buffer.begin(), buffer.end()));
Handle<WasmModuleObject> module_object =
maybe_module_object.ToHandleChecked();
v8::Local<v8::WasmCompiledModule> v8_module =
v8::Local<v8::WasmCompiledModule>::Cast(v8::Utils::ToLocal(
Handle<JSObject>::cast(module_object.ToHandleChecked())));
v8::Local<v8::WasmCompiledModule>::Cast(
v8::Utils::ToLocal(Handle<JSObject>::cast(module_object)));
store.push_back(v8_module->GetTransferrableModule());
original_native_module = module_object->managed_native_module()->get();
}
{
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator =
from_isolate->array_buffer_allocator();
v8::Isolate* to_isolate = v8::Isolate::New(create_params);
{
v8::HandleScope new_scope(to_isolate);
v8::Local<v8::Context> deserialization_context =
v8::Context::New(to_isolate);
deserialization_context->Enter();
v8::MaybeLocal<v8::WasmCompiledModule> mod =
v8::HandleScope scope(to_isolate);
LocalContext env(to_isolate);
v8::MaybeLocal<v8::WasmCompiledModule> transferred_module =
v8::WasmCompiledModule::FromTransferrableModule(to_isolate, store[0]);
CHECK(!mod.IsEmpty());
CHECK(!transferred_module.IsEmpty());
Handle<WasmModuleObject> module_object = Handle<WasmModuleObject>::cast(
v8::Utils::OpenHandle(*transferred_module.ToLocalChecked()));
std::shared_ptr<NativeModule> transferred_native_module =
module_object->managed_native_module()->get();
bool is_sharing = (original_native_module == transferred_native_module);
CHECK_EQ(should_share, is_sharing);
}
to_isolate->Dispose();
}
original_native_module.reset();
from_isolate->Dispose();
}
} // namespace
UNINITIALIZED_TEST(TransferrableWasmModulesCloned) {
FlagScope<bool> flag_scope_code(&FLAG_wasm_shared_code, false);
TestTransferrableWasmModules(false);
}
UNINITIALIZED_TEST(TransferrableWasmModulesShared) {
FlagScope<bool> flag_scope_engine(&FLAG_wasm_shared_engine, true);
FlagScope<bool> flag_scope_code(&FLAG_wasm_shared_code, true);
TestTransferrableWasmModules(true);
}
#undef EMIT_CODE_WITH_END
......
......@@ -83,20 +83,9 @@ class SharedEngineIsolate {
return instance.ToHandleChecked();
}
// TODO(mstarzinger): Switch over to a public API for sharing modules via the
// {v8::WasmCompiledModule::TransferrableModule} class once it is ready.
Handle<WasmInstanceObject> ImportInstance(SharedModule shared_module) {
Vector<const byte> wire_bytes = shared_module->wire_bytes();
Handle<Script> script = CreateWasmScript(isolate(), wire_bytes);
Handle<WasmModuleObject> module_object =
WasmModuleObject::New(isolate(), shared_module, script);
// TODO(6792): Wrappers below might be cloned using {Factory::CopyCode}.
// This requires unlocking the code space here. This should eventually be
// moved into the allocator.
CodeSpaceMemoryModificationScope modification_scope(isolate()->heap());
CompileJsToWasmWrappers(isolate(), module_object);
isolate()->wasm_engine()->ImportNativeModule(isolate(), shared_module);
ErrorThrower thrower(isolate(), "ImportInstance");
MaybeHandle<WasmInstanceObject> instance =
isolate()->wasm_engine()->SyncInstantiate(isolate(), &thrower,
......
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