fa3cd68a3f
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}
239 lines
8.1 KiB
C++
239 lines
8.1 KiB
C++
// Copyright 2020 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 "src/api/api-inl.h"
|
|
#include "src/init/v8.h"
|
|
|
|
#include "src/wasm/streaming-decoder.h"
|
|
#include "src/wasm/wasm-code-manager.h"
|
|
#include "src/wasm/wasm-engine.h"
|
|
#include "src/wasm/wasm-module-builder.h"
|
|
|
|
#include "test/cctest/cctest.h"
|
|
|
|
#include "test/common/wasm/test-signatures.h"
|
|
#include "test/common/wasm/wasm-macro-gen.h"
|
|
|
|
namespace v8 {
|
|
namespace internal {
|
|
namespace wasm {
|
|
|
|
namespace {
|
|
|
|
class TestResolver : public CompilationResultResolver {
|
|
public:
|
|
explicit TestResolver(std::atomic<int>* pending)
|
|
: native_module_(nullptr), pending_(pending) {}
|
|
|
|
void OnCompilationSucceeded(i::Handle<i::WasmModuleObject> module) override {
|
|
if (!module.is_null()) {
|
|
native_module_ = module->shared_native_module();
|
|
pending_->fetch_sub(1);
|
|
}
|
|
}
|
|
|
|
void OnCompilationFailed(i::Handle<i::Object> error_reason) override {
|
|
CHECK(false);
|
|
}
|
|
|
|
std::shared_ptr<NativeModule> native_module() { return native_module_; }
|
|
|
|
private:
|
|
std::shared_ptr<NativeModule> native_module_;
|
|
std::atomic<int>* pending_;
|
|
};
|
|
|
|
class StreamTester {
|
|
public:
|
|
explicit StreamTester(std::shared_ptr<TestResolver> test_resolver)
|
|
: internal_scope_(CcTest::i_isolate()), test_resolver_(test_resolver) {
|
|
i::Isolate* i_isolate = CcTest::i_isolate();
|
|
|
|
Handle<Context> context = i_isolate->native_context();
|
|
|
|
stream_ = GetWasmEngine()->StartStreamingCompilation(
|
|
i_isolate, WasmFeatures::All(), context,
|
|
"WebAssembly.compileStreaming()", test_resolver_);
|
|
}
|
|
|
|
void OnBytesReceived(const uint8_t* start, size_t length) {
|
|
stream_->OnBytesReceived(base::Vector<const uint8_t>(start, length));
|
|
}
|
|
|
|
void FinishStream() { stream_->Finish(); }
|
|
|
|
void SetCompiledModuleBytes(const uint8_t* start, size_t length) {
|
|
stream_->SetCompiledModuleBytes(base::Vector<const uint8_t>(start, length));
|
|
}
|
|
|
|
private:
|
|
i::HandleScope internal_scope_;
|
|
std::shared_ptr<StreamingDecoder> stream_;
|
|
std::shared_ptr<TestResolver> test_resolver_;
|
|
};
|
|
|
|
// Create a valid module such that the bytes depend on {n}.
|
|
ZoneBuffer GetValidModuleBytes(Zone* zone, uint8_t n) {
|
|
ZoneBuffer buffer(zone);
|
|
TestSignatures sigs;
|
|
WasmModuleBuilder builder(zone);
|
|
{
|
|
WasmFunctionBuilder* f = builder.AddFunction(sigs.v_v());
|
|
uint8_t code[] = {kExprI32Const, n, kExprDrop, kExprEnd};
|
|
f->EmitCode(code, arraysize(code));
|
|
}
|
|
builder.WriteTo(&buffer);
|
|
return buffer;
|
|
}
|
|
|
|
std::shared_ptr<NativeModule> SyncCompile(base::Vector<const uint8_t> bytes) {
|
|
ErrorThrower thrower(CcTest::i_isolate(), "Test");
|
|
auto enabled_features = WasmFeatures::FromIsolate(CcTest::i_isolate());
|
|
auto wire_bytes = ModuleWireBytes(bytes.begin(), bytes.end());
|
|
Handle<WasmModuleObject> module =
|
|
GetWasmEngine()
|
|
->SyncCompile(CcTest::i_isolate(), enabled_features, &thrower,
|
|
wire_bytes)
|
|
.ToHandleChecked();
|
|
return module->shared_native_module();
|
|
}
|
|
|
|
// Shared prefix.
|
|
constexpr uint8_t kPrefix[] = {
|
|
WASM_MODULE_HEADER, // module header
|
|
kTypeSectionCode, // section code
|
|
U32V_1(1 + SIZEOF_SIG_ENTRY_v_v), // section size
|
|
U32V_1(1), // type count
|
|
SIG_ENTRY_v_v, // signature entry
|
|
kFunctionSectionCode, // section code
|
|
U32V_1(2), // section size
|
|
U32V_1(1), // functions count
|
|
0, // signature index
|
|
kCodeSectionCode, // section code
|
|
U32V_1(7), // section size
|
|
U32V_1(1), // functions count
|
|
5, // body size
|
|
};
|
|
|
|
constexpr uint8_t kFunctionA[] = {
|
|
U32V_1(0), kExprI32Const, U32V_1(0), kExprDrop, kExprEnd,
|
|
};
|
|
constexpr uint8_t kFunctionB[] = {
|
|
U32V_1(0), kExprI32Const, U32V_1(1), kExprDrop, kExprEnd,
|
|
};
|
|
|
|
constexpr size_t kPrefixSize = arraysize(kPrefix);
|
|
constexpr size_t kFunctionSize = arraysize(kFunctionA);
|
|
|
|
} // namespace
|
|
|
|
TEST(TestAsyncCache) {
|
|
CcTest::InitializeVM();
|
|
i::HandleScope internal_scope(CcTest::i_isolate());
|
|
AccountingAllocator allocator;
|
|
Zone zone(&allocator, "CompilationCacheTester");
|
|
|
|
auto bufferA = GetValidModuleBytes(&zone, 0);
|
|
auto bufferB = GetValidModuleBytes(&zone, 1);
|
|
|
|
std::atomic<int> pending(3);
|
|
auto resolverA1 = std::make_shared<TestResolver>(&pending);
|
|
auto resolverA2 = std::make_shared<TestResolver>(&pending);
|
|
auto resolverB = std::make_shared<TestResolver>(&pending);
|
|
|
|
GetWasmEngine()->AsyncCompile(CcTest::i_isolate(), WasmFeatures::All(),
|
|
resolverA1,
|
|
ModuleWireBytes(bufferA.begin(), bufferA.end()),
|
|
true, "WebAssembly.compile");
|
|
GetWasmEngine()->AsyncCompile(CcTest::i_isolate(), WasmFeatures::All(),
|
|
resolverA2,
|
|
ModuleWireBytes(bufferA.begin(), bufferA.end()),
|
|
true, "WebAssembly.compile");
|
|
GetWasmEngine()->AsyncCompile(CcTest::i_isolate(), WasmFeatures::All(),
|
|
resolverB,
|
|
ModuleWireBytes(bufferB.begin(), bufferB.end()),
|
|
true, "WebAssembly.compile");
|
|
|
|
while (pending > 0) {
|
|
v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(),
|
|
CcTest::isolate());
|
|
}
|
|
|
|
CHECK_EQ(resolverA1->native_module(), resolverA2->native_module());
|
|
CHECK_NE(resolverA1->native_module(), resolverB->native_module());
|
|
}
|
|
|
|
TEST(TestStreamingCache) {
|
|
CcTest::InitializeVM();
|
|
|
|
std::atomic<int> pending(3);
|
|
auto resolverA1 = std::make_shared<TestResolver>(&pending);
|
|
auto resolverA2 = std::make_shared<TestResolver>(&pending);
|
|
auto resolverB = std::make_shared<TestResolver>(&pending);
|
|
|
|
StreamTester testerA1(resolverA1);
|
|
StreamTester testerA2(resolverA2);
|
|
StreamTester testerB(resolverB);
|
|
|
|
// Start receiving kPrefix bytes.
|
|
testerA1.OnBytesReceived(kPrefix, kPrefixSize);
|
|
testerA2.OnBytesReceived(kPrefix, kPrefixSize);
|
|
testerB.OnBytesReceived(kPrefix, kPrefixSize);
|
|
|
|
// Receive function bytes and start streaming compilation.
|
|
testerA1.OnBytesReceived(kFunctionA, kFunctionSize);
|
|
testerA1.FinishStream();
|
|
testerA2.OnBytesReceived(kFunctionA, kFunctionSize);
|
|
testerA2.FinishStream();
|
|
testerB.OnBytesReceived(kFunctionB, kFunctionSize);
|
|
testerB.FinishStream();
|
|
|
|
while (pending > 0) {
|
|
v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(),
|
|
CcTest::isolate());
|
|
}
|
|
|
|
std::shared_ptr<NativeModule> native_module_A1 = resolverA1->native_module();
|
|
std::shared_ptr<NativeModule> native_module_A2 = resolverA2->native_module();
|
|
std::shared_ptr<NativeModule> native_module_B = resolverB->native_module();
|
|
CHECK_EQ(native_module_A1, native_module_A2);
|
|
CHECK_NE(native_module_A1, native_module_B);
|
|
}
|
|
|
|
TEST(TestStreamingAndSyncCache) {
|
|
CcTest::InitializeVM();
|
|
|
|
std::atomic<int> pending(1);
|
|
auto resolver = std::make_shared<TestResolver>(&pending);
|
|
StreamTester tester(resolver);
|
|
|
|
tester.OnBytesReceived(kPrefix, kPrefixSize);
|
|
|
|
// Compile the same module synchronously to make sure we don't deadlock
|
|
// waiting for streaming compilation to finish.
|
|
auto full_bytes =
|
|
base::OwnedVector<uint8_t>::New(kPrefixSize + kFunctionSize);
|
|
memcpy(full_bytes.begin(), kPrefix, kPrefixSize);
|
|
memcpy(full_bytes.begin() + kPrefixSize, kFunctionA, kFunctionSize);
|
|
auto native_module_sync = SyncCompile(full_bytes.as_vector());
|
|
|
|
// Streaming compilation should just discard its native module now and use the
|
|
// one inserted in the cache by sync compilation.
|
|
tester.OnBytesReceived(kFunctionA, kFunctionSize);
|
|
tester.FinishStream();
|
|
|
|
while (pending > 0) {
|
|
v8::platform::PumpMessageLoop(i::V8::GetCurrentPlatform(),
|
|
CcTest::isolate());
|
|
}
|
|
|
|
std::shared_ptr<NativeModule> native_module_streaming =
|
|
resolver->native_module();
|
|
CHECK_EQ(native_module_streaming, native_module_sync);
|
|
}
|
|
|
|
} // namespace wasm
|
|
} // namespace internal
|
|
} // namespace v8
|