46635cc85b
R=sigurds@chromium.org Change-Id: I1af61feff81f21792d66af7d9b12fc59ea94aacd No-Try: true No-Tree-Checks: true Change-Id: I1af61feff81f21792d66af7d9b12fc59ea94aacd Reviewed-on: https://chromium-review.googlesource.com/1126933 Commit-Queue: Sigurd Schneider <sigurds@chromium.org> Reviewed-by: Sigurd Schneider <sigurds@chromium.org> Cr-Commit-Position: refs/heads/master@{#54249}
229 lines
8.5 KiB
C++
229 lines
8.5 KiB
C++
// 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(
|
|
ReadOnlyRoots(isolate()).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
|