Commit e2d7129f authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[wasm] First test for sharing the {WasmEngine}.

This is a first set of test cases for sharing an {WasmEngine} and the
contained {WasmCode} between multiple Isolates. Currently this can only
be done using internal API methods on the Isolate, an external API that
is usable by embedders does not exist yet.

R=clemensh@chromium.org
TEST=cctest/test-wasm-shared-engine
BUG=v8:7424

Change-Id: I35541a76b5aceec4519e3a46e6a9ef4d01cad22b
Reviewed-on: https://chromium-review.googlesource.com/1126382Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#54248}
parent cc9d4b14
......@@ -2990,16 +2990,18 @@ bool Isolate::Init(StartupDeserializer* des) {
DCHECK(!heap_.HasBeenSetUp());
heap_.SetUp();
// Setup the wasm engine. Currently, there's one per Isolate.
wasm_engine_.reset(
new wasm::WasmEngine(std::unique_ptr<wasm::WasmCodeManager>(
new wasm::WasmCodeManager(kMaxWasmCodeMemory))));
wasm_engine_->memory_tracker()->SetAllocationResultHistogram(
counters()->wasm_memory_allocation_result());
wasm_engine_->memory_tracker()->SetAddressSpaceUsageHistogram(
counters()->wasm_address_space_usage_mb());
wasm_engine_->code_manager()->SetModuleCodeSizeHistogram(
counters()->wasm_module_code_size_mb());
// Setup the wasm engine. Currently, there's one per Isolate by default.
if (wasm_engine_ == nullptr) {
wasm_engine_.reset(
new wasm::WasmEngine(std::unique_ptr<wasm::WasmCodeManager>(
new wasm::WasmCodeManager(kMaxWasmCodeMemory))));
wasm_engine_->memory_tracker()->SetAllocationResultHistogram(
counters()->wasm_memory_allocation_result());
wasm_engine_->memory_tracker()->SetAddressSpaceUsageHistogram(
counters()->wasm_address_space_usage_mb());
wasm_engine_->code_manager()->SetModuleCodeSizeHistogram(
counters()->wasm_module_code_size_mb());
}
deoptimizer_data_ = new DeoptimizerData(heap());
......
......@@ -902,7 +902,6 @@ class Isolate : private HiddenFactory {
}
StackGuard* stack_guard() { return &stack_guard_; }
Heap* heap() { return &heap_; }
wasm::WasmEngine* wasm_engine() const { return wasm_engine_.get(); }
StubCache* load_stub_cache() { return load_stub_cache_; }
StubCache* store_stub_cache() { return store_stub_cache_; }
DeoptimizerData* deoptimizer_data() { return deoptimizer_data_; }
......@@ -1378,6 +1377,12 @@ class Isolate : private HiddenFactory {
elements_deletion_counter_ = value;
}
wasm::WasmEngine* wasm_engine() const { return wasm_engine_.get(); }
void set_wasm_engine(std::shared_ptr<wasm::WasmEngine> engine) {
DCHECK_NULL(wasm_engine_); // Only call once before {Init}.
wasm_engine_ = std::move(engine);
}
const v8::Context::BackupIncumbentScope* top_backup_incumbent_scope() const {
return top_backup_incumbent_scope_;
}
......@@ -1690,7 +1695,7 @@ class Isolate : private HiddenFactory {
size_t elements_deletion_counter_ = 0;
std::unique_ptr<wasm::WasmEngine> wasm_engine_;
std::shared_ptr<wasm::WasmEngine> wasm_engine_;
std::unique_ptr<TracingCpuProfilerImpl> tracing_cpu_profiler_;
......
......@@ -130,8 +130,8 @@ class WasmMemoryTracker {
std::unordered_map<const void*, AllocationData> allocations_;
// Keep pointers to
Histogram* allocation_result_;
Histogram* address_space_usage_mb_; // in MiB
Histogram* allocation_result_ = nullptr;
Histogram* address_space_usage_mb_ = nullptr; // in MiB
DISALLOW_COPY_AND_ASSIGN(WasmMemoryTracker);
};
......
......@@ -251,6 +251,7 @@ v8_source_set("cctest_sources") {
"wasm/test-wasm-codegen.cc",
"wasm/test-wasm-interpreter-entry.cc",
"wasm/test-wasm-serialization.cc",
"wasm/test-wasm-shared-engine.cc",
"wasm/test-wasm-stack.cc",
"wasm/test-wasm-trap-position.cc",
"wasm/wasm-atomics-utils.h",
......
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "src/objects-inl.h"
#include "src/wasm/wasm-engine.h"
#include "src/wasm/wasm-module-builder.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-objects-inl.h"
#include "test/cctest/cctest.h"
#include "test/common/wasm/test-signatures.h"
#include "test/common/wasm/wasm-macro-gen.h"
#include "test/common/wasm/wasm-module-runner.h"
namespace v8 {
namespace internal {
namespace wasm {
namespace test_wasm_shared_engine {
// Helper class representing a WebAssembly engine that is capable of being
// shared between multiple Isolates, sharing the underlying generated code.
class SharedEngine {
public:
explicit SharedEngine(size_t max_committed = kMaxWasmCodeMemory)
: wasm_engine_(base::make_unique<WasmEngine>(
base::make_unique<WasmCodeManager>(max_committed))) {}
~SharedEngine() {
// Ensure no remaining uses exist.
CHECK(wasm_engine_.unique());
}
WasmEngine* engine() const { return wasm_engine_.get(); }
WasmCodeManager* code_manager() const { return engine()->code_manager(); }
int NumberOfExportedEngineUses() const {
// This class holds one implicit use itself, which we discount.
return static_cast<int>(wasm_engine_.use_count()) - 1;
}
std::shared_ptr<WasmEngine> ExportEngineForSharing() { return wasm_engine_; }
private:
std::shared_ptr<WasmEngine> wasm_engine_;
};
// Helper type definition representing a WebAssembly module shared between
// multiple Isolates with implicit reference counting.
using SharedModule = std::shared_ptr<NativeModule>;
// Helper class representing an Isolate based on a given shared WebAssembly
// engine available at construction time.
class SharedEngineIsolate {
public:
explicit SharedEngineIsolate(SharedEngine* engine)
: isolate_(v8::Isolate::Allocate()) {
isolate()->set_wasm_engine(engine->ExportEngineForSharing());
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate::Initialize(isolate_, create_params);
v8::HandleScope handle_scope(v8_isolate());
v8::Context::New(v8_isolate())->Enter();
testing::SetupIsolateForWasmModule(isolate());
zone_.reset(new Zone(isolate()->allocator(), ZONE_NAME));
}
~SharedEngineIsolate() {
zone_.reset();
isolate_->Dispose();
}
Zone* zone() const { return zone_.get(); }
v8::Isolate* v8_isolate() { return isolate_; }
Isolate* isolate() { return reinterpret_cast<Isolate*>(isolate_); }
Handle<WasmInstanceObject> CompileAndInstantiate(ZoneBuffer* buffer) {
ErrorThrower thrower(isolate(), "CompileAndInstantiate");
MaybeHandle<WasmInstanceObject> instance =
testing::CompileAndInstantiateForTesting(
isolate(), &thrower,
ModuleWireBytes(buffer->begin(), buffer->end()));
return instance.ToHandleChecked();
}
// TODO(mstarzinger): Turn this into a {WasmModuleObject::New} method.
Handle<WasmModuleObject> NewWasmModuleObject(
Handle<FixedArray> export_wrappers, Handle<Script> script,
SharedModule shared_module) {
Handle<JSFunction> module_cons(
isolate()->native_context()->wasm_module_constructor(), isolate());
auto module_object = Handle<WasmModuleObject>::cast(
isolate()->factory()->NewJSObject(module_cons));
module_object->set_export_wrappers(*export_wrappers);
script->set_wasm_module_object(*module_object);
module_object->set_script(*script);
module_object->set_weak_instance_list(
isolate()->heap()->empty_weak_array_list());
size_t native_memory_estimate =
isolate()->wasm_engine()->code_manager()->EstimateNativeModuleSize(
shared_module->module());
size_t memory_estimate = EstimateWasmModuleSize(shared_module->module()) +
native_memory_estimate;
Handle<Managed<wasm::NativeModule>> managed_native_module =
Managed<wasm::NativeModule>::FromSharedPtr(isolate(), memory_estimate,
shared_module);
module_object->set_managed_native_module(*managed_native_module);
return module_object;
}
// 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);
int export_wrappers_size =
static_cast<int>(shared_module->module()->num_exported_functions);
Handle<FixedArray> export_wrappers =
isolate()->factory()->NewFixedArray(export_wrappers_size, TENURED);
Handle<WasmModuleObject> module_object =
NewWasmModuleObject(export_wrappers, script, 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");
MaybeHandle<WasmInstanceObject> instance =
isolate()->wasm_engine()->SyncInstantiate(isolate(), &thrower,
module_object, {}, {});
return instance.ToHandleChecked();
}
SharedModule ExportInstance(Handle<WasmInstanceObject> instance) {
return instance->module_object()->managed_native_module()->get();
}
int32_t Run(Handle<WasmInstanceObject> instance) {
return testing::RunWasmModuleForTesting(isolate(), instance, 0, nullptr);
}
private:
v8::Isolate* isolate_;
std::unique_ptr<Zone> zone_;
};
namespace {
ZoneBuffer* BuildReturnConstantModule(Zone* zone, int constant) {
TestSignatures sigs;
ZoneBuffer* buffer = new (zone) ZoneBuffer(zone);
WasmModuleBuilder* builder = new (zone) WasmModuleBuilder(zone);
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
f->builder()->AddExport(CStrVector("main"), f);
byte code[] = {WASM_I32V_2(constant)};
f->EmitCode(code, sizeof(code));
f->Emit(kExprEnd);
builder->WriteTo(*buffer);
return buffer;
}
} // namespace
TEST(SharedEngineUseCount) {
SharedEngine engine;
CHECK_EQ(0, engine.NumberOfExportedEngineUses());
{
SharedEngineIsolate isolate(&engine);
CHECK_EQ(1, engine.NumberOfExportedEngineUses());
}
CHECK_EQ(0, engine.NumberOfExportedEngineUses());
{
SharedEngineIsolate isolate1(&engine);
CHECK_EQ(1, engine.NumberOfExportedEngineUses());
SharedEngineIsolate isolate2(&engine);
CHECK_EQ(2, engine.NumberOfExportedEngineUses());
}
CHECK_EQ(0, engine.NumberOfExportedEngineUses());
}
TEST(SharedEngineRunSeparated) {
SharedEngine engine;
{
SharedEngineIsolate isolate(&engine);
HandleScope scope(isolate.isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
CHECK_EQ(23, isolate.Run(instance));
}
{
SharedEngineIsolate isolate(&engine);
HandleScope scope(isolate.isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
CHECK_EQ(42, isolate.Run(instance));
}
}
TEST(SharedEngineRunImported) {
SharedEngine engine;
SharedModule module;
{
SharedEngineIsolate isolate(&engine);
HandleScope scope(isolate.isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
// TODO(mstarzinger): Even just deferring the destruction of the native
// module doesn't work currently because of {WasmModule::signature_zone}.
// module = isolate.ExportInstance(instance);
CHECK_EQ(23, isolate.Run(instance));
}
// TODO(mstarzinger): Tearing down the first Isolate still invalidates the
// compilation state and hence breaks tear down of the second Isolate.
// {
// SharedEngineIsolate isolate(&engine);
// HandleScope scope(isolate.isolate());
// Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
// CHECK_EQ(23, isolate.Run(instance));
// }
}
} // namespace test_wasm_shared_engine
} // namespace wasm
} // namespace internal
} // namespace v8
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