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; ...@@ -155,7 +155,7 @@ class FunctionCallbackArguments;
class GlobalHandles; class GlobalHandles;
namespace wasm { namespace wasm {
class CompilationResultResolver; class NativeModule;
class StreamingDecoder; class StreamingDecoder;
} // namespace wasm } // namespace wasm
...@@ -4422,13 +4422,17 @@ class V8_EXPORT WasmCompiledModule : public Object { ...@@ -4422,13 +4422,17 @@ class V8_EXPORT WasmCompiledModule : public Object {
TransferrableModule& operator=(const TransferrableModule& src) = delete; TransferrableModule& operator=(const TransferrableModule& src) = delete;
private: private:
typedef std::shared_ptr<internal::wasm::NativeModule> SharedModule;
typedef std::pair<std::unique_ptr<const uint8_t[]>, size_t> OwnedBuffer; typedef std::pair<std::unique_ptr<const uint8_t[]>, size_t> OwnedBuffer;
friend class WasmCompiledModule; friend class WasmCompiledModule;
TransferrableModule(OwnedBuffer code, OwnedBuffer bytes) explicit TransferrableModule(SharedModule shared_module)
: compiled_code(std::move(code)), wire_bytes(std::move(bytes)) {} : shared_module_(std::move(shared_module)) {}
TransferrableModule(OwnedBuffer serialized, OwnedBuffer bytes)
OwnedBuffer compiled_code = {nullptr, 0}; : serialized_(std::move(serialized)), wire_bytes_(std::move(bytes)) {}
OwnedBuffer wire_bytes = {nullptr, 0};
SharedModule shared_module_;
OwnedBuffer serialized_ = {nullptr, 0};
OwnedBuffer wire_bytes_ = {nullptr, 0};
}; };
/** /**
......
...@@ -7659,31 +7659,37 @@ Local<String> WasmCompiledModule::GetWasmWireBytes() { ...@@ -7659,31 +7659,37 @@ Local<String> WasmCompiledModule::GetWasmWireBytes() {
.ToLocalChecked(); .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::TransferrableModule
WasmCompiledModule::GetTransferrableModule() { WasmCompiledModule::GetTransferrableModule() {
i::DisallowHeapAllocation no_gc; if (i::FLAG_wasm_shared_code) {
WasmCompiledModule::SerializedModule compiled_part = Serialize(); i::Handle<i::WasmModuleObject> obj =
i::Handle<i::WasmModuleObject>::cast(Utils::OpenHandle(this));
BufferReference wire_bytes_ref = GetWasmWireBytesRef(); return TransferrableModule(obj->managed_native_module()->get());
size_t wire_size = wire_bytes_ref.size; } else {
std::unique_ptr<uint8_t[]> wire_bytes_copy(new uint8_t[wire_size]); WasmCompiledModule::SerializedModule serialized_module = Serialize();
memcpy(wire_bytes_copy.get(), wire_bytes_ref.start, wire_size); BufferReference wire_bytes_ref = GetWasmWireBytesRef();
size_t wire_size = wire_bytes_ref.size;
return TransferrableModule(std::move(compiled_part), std::unique_ptr<uint8_t[]> wire_bytes_copy(new uint8_t[wire_size]);
{std::move(wire_bytes_copy), 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( MaybeLocal<WasmCompiledModule> WasmCompiledModule::FromTransferrableModule(
Isolate* isolate, Isolate* isolate,
const WasmCompiledModule::TransferrableModule& transferrable_module) { const WasmCompiledModule::TransferrableModule& transferrable_module) {
MaybeLocal<WasmCompiledModule> ret = if (i::FLAG_wasm_shared_code) {
Deserialize(isolate, AsReference(transferrable_module.compiled_code), i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
AsReference(transferrable_module.wire_bytes)); i::Handle<i::WasmModuleObject> module_object =
return ret; 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() { WasmCompiledModule::SerializedModule WasmCompiledModule::Serialize() {
......
...@@ -615,6 +615,8 @@ DEFINE_BOOL(wasm_no_stack_checks, false, ...@@ -615,6 +615,8 @@ DEFINE_BOOL(wasm_no_stack_checks, false,
DEFINE_BOOL(wasm_shared_engine, false, DEFINE_BOOL(wasm_shared_engine, false,
"shares one wasm engine between all isolates within a process") "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, DEFINE_BOOL(wasm_trap_handler, true,
"use signal handlers to catch out of bounds memory access in wasm" "use signal handlers to catch out of bounds memory access in wasm"
" (currently Linux x86_64 only)") " (currently Linux x86_64 only)")
......
...@@ -333,6 +333,7 @@ class V8_EXPORT_PRIVATE NativeModule final { ...@@ -333,6 +333,7 @@ class V8_EXPORT_PRIVATE NativeModule final {
wire_bytes_ = std::move(wire_bytes); wire_bytes_ = std::move(wire_bytes);
} }
const WasmModule* module() const { return module_.get(); } const WasmModule* module() const { return module_.get(); }
WasmCodeManager* code_manager() const { return wasm_code_manager_; }
WasmCode* Lookup(Address) const; WasmCode* Lookup(Address) const;
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
#include "src/wasm/module-compiler.h" #include "src/wasm/module-compiler.h"
#include "src/wasm/module-decoder.h" #include "src/wasm/module-decoder.h"
#include "src/wasm/streaming-decoder.h" #include "src/wasm/streaming-decoder.h"
#include "src/wasm/wasm-objects.h" #include "src/wasm/wasm-objects-inl.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -161,6 +161,27 @@ std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation( ...@@ -161,6 +161,27 @@ std::shared_ptr<StreamingDecoder> WasmEngine::StartStreamingCompilation(
return job->CreateStreamingDecoder(); 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() { CompilationStatistics* WasmEngine::GetOrCreateTurboStatistics() {
base::LockGuard<base::Mutex> guard(&mutex_); base::LockGuard<base::Mutex> guard(&mutex_);
if (compilation_stats_ == nullptr) { if (compilation_stats_ == nullptr) {
......
...@@ -88,6 +88,16 @@ class V8_EXPORT_PRIVATE WasmEngine { ...@@ -88,6 +88,16 @@ class V8_EXPORT_PRIVATE WasmEngine {
Isolate* isolate, Handle<Context> context, Isolate* isolate, Handle<Context> context,
std::unique_ptr<CompilationResultResolver> resolver); 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(); } WasmCodeManager* code_manager() const { return code_manager_.get(); }
WasmMemoryTracker* memory_tracker() { return &memory_tracker_; } WasmMemoryTracker* memory_tracker() { return &memory_tracker_; }
......
...@@ -269,46 +269,74 @@ TEST(BlockWasmCodeGenAtDeserialization) { ...@@ -269,46 +269,74 @@ TEST(BlockWasmCodeGenAtDeserialization) {
Cleanup(); Cleanup();
} }
TEST(TransferrableWasmModules) { namespace {
void TestTransferrableWasmModules(bool should_share) {
i::wasm::WasmEngine::InitializeOncePerProcess();
v8::internal::AccountingAllocator allocator; v8::internal::AccountingAllocator allocator;
Zone zone(&allocator, ZONE_NAME); Zone zone(&allocator, ZONE_NAME);
ZoneBuffer buffer(&zone); ZoneBuffer buffer(&zone);
WasmSerializationTest::BuildWireBytes(&zone, &buffer); WasmSerializationTest::BuildWireBytes(&zone, &buffer);
Isolate* from_isolate = CcTest::InitIsolateOnce(); v8::Isolate::CreateParams create_params;
ErrorThrower thrower(from_isolate, ""); 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::vector<v8::WasmCompiledModule::TransferrableModule> store;
std::shared_ptr<NativeModule> original_native_module;
{ {
HandleScope scope(from_isolate); v8::HandleScope scope(from_isolate);
testing::SetupIsolateForWasmModule(from_isolate); LocalContext env(from_isolate);
MaybeHandle<WasmModuleObject> module_object = Isolate* from_i_isolate = reinterpret_cast<Isolate*>(from_isolate);
from_isolate->wasm_engine()->SyncCompile( testing::SetupIsolateForWasmModule(from_i_isolate);
from_isolate, &thrower, 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())); ModuleWireBytes(buffer.begin(), buffer.end()));
Handle<WasmModuleObject> module_object =
maybe_module_object.ToHandleChecked();
v8::Local<v8::WasmCompiledModule> v8_module = v8::Local<v8::WasmCompiledModule> v8_module =
v8::Local<v8::WasmCompiledModule>::Cast(v8::Utils::ToLocal( v8::Local<v8::WasmCompiledModule>::Cast(
Handle<JSObject>::cast(module_object.ToHandleChecked()))); v8::Utils::ToLocal(Handle<JSObject>::cast(module_object)));
store.push_back(v8_module->GetTransferrableModule()); 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::Isolate* to_isolate = v8::Isolate::New(create_params);
{ {
v8::HandleScope new_scope(to_isolate); v8::HandleScope scope(to_isolate);
v8::Local<v8::Context> deserialization_context = LocalContext env(to_isolate);
v8::Context::New(to_isolate);
deserialization_context->Enter(); v8::MaybeLocal<v8::WasmCompiledModule> transferred_module =
v8::MaybeLocal<v8::WasmCompiledModule> mod =
v8::WasmCompiledModule::FromTransferrableModule(to_isolate, store[0]); 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(); 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 #undef EMIT_CODE_WITH_END
......
...@@ -83,20 +83,9 @@ class SharedEngineIsolate { ...@@ -83,20 +83,9 @@ class SharedEngineIsolate {
return instance.ToHandleChecked(); 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) { 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 = Handle<WasmModuleObject> module_object =
WasmModuleObject::New(isolate(), shared_module, script); isolate()->wasm_engine()->ImportNativeModule(isolate(), shared_module);
// 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);
ErrorThrower thrower(isolate(), "ImportInstance"); ErrorThrower thrower(isolate(), "ImportInstance");
MaybeHandle<WasmInstanceObject> instance = MaybeHandle<WasmInstanceObject> instance =
isolate()->wasm_engine()->SyncInstantiate(isolate(), &thrower, 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