v8/test/cctest/wasm/test-wasm-shared-engine.cc
Clemens Backes fa3cd68a3f [isolate][cleanup] Remove pointer to WasmEngine
The WasmEngine is shared across the whole process, so there is no need
to store it in every Isolate.
Instead, we can just get it from everywhere on any thread using
{wasm::GetWasmEngine()}, which is a simple read of a global.

R=jkummerow@chromium.org

Bug: v8:11879
Change-Id: I13afb8ca3d116aa14bfaec5a4bbd6d71faa9aa17
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2969825
Reviewed-by: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75265}
2021-06-21 09:09:25 +00:00

314 lines
11 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/execution/microtask-queue.h"
#include "src/objects/objects-inl.h"
#include "src/wasm/function-compiler.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 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 that uses the process-wide (shared) wasm
// engine.
class SharedEngineIsolate {
public:
SharedEngineIsolate() : isolate_(v8::Isolate::Allocate()) {
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();
}
Handle<WasmInstanceObject> ImportInstance(SharedModule shared_module) {
Handle<WasmModuleObject> module_object =
GetWasmEngine()->ImportNativeModule(isolate(), shared_module, {});
ErrorThrower thrower(isolate(), "ImportInstance");
MaybeHandle<WasmInstanceObject> instance = GetWasmEngine()->SyncInstantiate(
isolate(), &thrower, module_object, {}, {});
return instance.ToHandleChecked();
}
SharedModule ExportInstance(Handle<WasmInstanceObject> instance) {
return instance->module_object().shared_native_module();
}
int32_t Run(Handle<WasmInstanceObject> instance) {
return testing::CallWasmFunctionForTesting(isolate(), instance, "main", 0,
nullptr);
}
private:
v8::Isolate* isolate_;
std::unique_ptr<Zone> zone_;
};
// Helper class representing a Thread running its own instance of an Isolate
// with a shared WebAssembly engine available at construction time.
class SharedEngineThread : public v8::base::Thread {
public:
explicit SharedEngineThread(
std::function<void(SharedEngineIsolate*)> callback)
: Thread(Options("SharedEngineThread")), callback_(callback) {}
void Run() override {
SharedEngineIsolate isolate;
callback_(&isolate);
}
private:
std::function<void(SharedEngineIsolate*)> callback_;
};
namespace {
ZoneBuffer* BuildReturnConstantModule(Zone* zone, int constant) {
TestSignatures sigs;
ZoneBuffer* buffer = zone->New<ZoneBuffer>(zone);
WasmModuleBuilder* builder = zone->New<WasmModuleBuilder>(zone);
WasmFunctionBuilder* f = builder->AddFunction(sigs.i_v());
f->builder()->AddExport(base::CStrVector("main"), f);
byte code[] = {WASM_I32V_2(constant)};
f->EmitCode(code, sizeof(code));
f->Emit(kExprEnd);
builder->WriteTo(buffer);
return buffer;
}
class MockInstantiationResolver : public InstantiationResultResolver {
public:
explicit MockInstantiationResolver(Handle<Object>* out_instance)
: out_instance_(out_instance) {}
void OnInstantiationSucceeded(Handle<WasmInstanceObject> result) override {
*out_instance_->location() = result->ptr();
}
void OnInstantiationFailed(Handle<Object> error_reason) override {
UNREACHABLE();
}
private:
Handle<Object>* out_instance_;
};
class MockCompilationResolver : public CompilationResultResolver {
public:
MockCompilationResolver(SharedEngineIsolate* isolate,
Handle<Object>* out_instance)
: isolate_(isolate), out_instance_(out_instance) {}
void OnCompilationSucceeded(Handle<WasmModuleObject> result) override {
GetWasmEngine()->AsyncInstantiate(
isolate_->isolate(),
std::make_unique<MockInstantiationResolver>(out_instance_), result, {});
}
void OnCompilationFailed(Handle<Object> error_reason) override {
UNREACHABLE();
}
private:
SharedEngineIsolate* isolate_;
Handle<Object>* out_instance_;
};
void PumpMessageLoop(SharedEngineIsolate* isolate) {
v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(),
isolate->v8_isolate(),
platform::MessageLoopBehavior::kWaitForWork);
isolate->isolate()->default_microtask_queue()->RunMicrotasks(
isolate->isolate());
}
Handle<WasmInstanceObject> CompileAndInstantiateAsync(
SharedEngineIsolate* isolate, ZoneBuffer* buffer) {
Handle<Object> maybe_instance = handle(Smi::zero(), isolate->isolate());
auto enabled_features = WasmFeatures::FromIsolate(isolate->isolate());
constexpr const char* kAPIMethodName = "Test.CompileAndInstantiateAsync";
GetWasmEngine()->AsyncCompile(
isolate->isolate(), enabled_features,
std::make_unique<MockCompilationResolver>(isolate, &maybe_instance),
ModuleWireBytes(buffer->begin(), buffer->end()), true, kAPIMethodName);
while (!maybe_instance->IsWasmInstanceObject()) PumpMessageLoop(isolate);
Handle<WasmInstanceObject> instance =
Handle<WasmInstanceObject>::cast(maybe_instance);
return instance;
}
} // namespace
TEST(SharedEngineRunSeparated) {
{
SharedEngineIsolate isolate;
HandleScope scope(isolate.isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
CHECK_EQ(23, isolate.Run(instance));
}
{
SharedEngineIsolate isolate;
HandleScope scope(isolate.isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 42);
Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
CHECK_EQ(42, isolate.Run(instance));
}
}
TEST(SharedEngineRunImported) {
SharedModule module;
{
SharedEngineIsolate isolate;
HandleScope scope(isolate.isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
module = isolate.ExportInstance(instance);
CHECK_EQ(23, isolate.Run(instance));
}
{
SharedEngineIsolate isolate;
HandleScope scope(isolate.isolate());
Handle<WasmInstanceObject> instance = isolate.ImportInstance(module);
CHECK_EQ(23, isolate.Run(instance));
}
}
TEST(SharedEngineRunThreadedBuildingSync) {
SharedEngineThread thread1([](SharedEngineIsolate* isolate) {
HandleScope scope(isolate->isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate->zone(), 23);
Handle<WasmInstanceObject> instance =
isolate->CompileAndInstantiate(buffer);
CHECK_EQ(23, isolate->Run(instance));
});
SharedEngineThread thread2([](SharedEngineIsolate* isolate) {
HandleScope scope(isolate->isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate->zone(), 42);
Handle<WasmInstanceObject> instance =
isolate->CompileAndInstantiate(buffer);
CHECK_EQ(42, isolate->Run(instance));
});
CHECK(thread1.Start());
CHECK(thread2.Start());
thread1.Join();
thread2.Join();
}
TEST(SharedEngineRunThreadedBuildingAsync) {
SharedEngineThread thread1([](SharedEngineIsolate* isolate) {
HandleScope scope(isolate->isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate->zone(), 23);
Handle<WasmInstanceObject> instance =
CompileAndInstantiateAsync(isolate, buffer);
CHECK_EQ(23, isolate->Run(instance));
});
SharedEngineThread thread2([](SharedEngineIsolate* isolate) {
HandleScope scope(isolate->isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate->zone(), 42);
Handle<WasmInstanceObject> instance =
CompileAndInstantiateAsync(isolate, buffer);
CHECK_EQ(42, isolate->Run(instance));
});
CHECK(thread1.Start());
CHECK(thread2.Start());
thread1.Join();
thread2.Join();
}
TEST(SharedEngineRunThreadedExecution) {
SharedModule module;
{
SharedEngineIsolate isolate;
HandleScope scope(isolate.isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
module = isolate.ExportInstance(instance);
}
SharedEngineThread thread1([module](SharedEngineIsolate* isolate) {
HandleScope scope(isolate->isolate());
Handle<WasmInstanceObject> instance = isolate->ImportInstance(module);
CHECK_EQ(23, isolate->Run(instance));
});
SharedEngineThread thread2([module](SharedEngineIsolate* isolate) {
HandleScope scope(isolate->isolate());
Handle<WasmInstanceObject> instance = isolate->ImportInstance(module);
CHECK_EQ(23, isolate->Run(instance));
});
CHECK(thread1.Start());
CHECK(thread2.Start());
thread1.Join();
thread2.Join();
}
TEST(SharedEngineRunThreadedTierUp) {
SharedModule module;
{
SharedEngineIsolate isolate;
HandleScope scope(isolate.isolate());
ZoneBuffer* buffer = BuildReturnConstantModule(isolate.zone(), 23);
Handle<WasmInstanceObject> instance = isolate.CompileAndInstantiate(buffer);
module = isolate.ExportInstance(instance);
}
constexpr int kNumberOfThreads = 5;
std::list<SharedEngineThread> threads;
for (int i = 0; i < kNumberOfThreads; ++i) {
threads.emplace_back([module](SharedEngineIsolate* isolate) {
constexpr int kNumberOfIterations = 100;
HandleScope scope(isolate->isolate());
Handle<WasmInstanceObject> instance = isolate->ImportInstance(module);
for (int j = 0; j < kNumberOfIterations; ++j) {
CHECK_EQ(23, isolate->Run(instance));
}
});
}
threads.emplace_back([module](SharedEngineIsolate* isolate) {
HandleScope scope(isolate->isolate());
Handle<WasmInstanceObject> instance = isolate->ImportInstance(module);
WasmFeatures detected = WasmFeatures::None();
WasmCompilationUnit::CompileWasmFunction(
isolate->isolate(), module.get(), &detected,
&module->module()->functions[0], ExecutionTier::kTurbofan);
CHECK_EQ(23, isolate->Run(instance));
});
for (auto& thread : threads) CHECK(thread.Start());
for (auto& thread : threads) thread.Join();
}
} // namespace test_wasm_shared_engine
} // namespace wasm
} // namespace internal
} // namespace v8