ab6c4669ba
This is a reland of9781aa076f
Original change's description: > Reland "[wasm] Cache streaming compilation result" > > This is a reland of015f379aa1
> > Original change's description: > > [wasm] Cache streaming compilation result > > > > Before compiling the code section, check whether the > > bytes received so far match a cached module. If they do, delay > > compilation until we receive the full bytes, since we are likely to find > > a cache entry for them. > > > > R=clemensb@chromium.org > > > > Bug: v8:6847 > > Change-Id: Ie5170d1274da3da6d52ff1b408abc7cb441bbe3c > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2002823 > > Commit-Queue: Thibaud Michaud <thibaudm@chromium.org> > > Reviewed-by: Clemens Backes <clemensb@chromium.org> > > Cr-Commit-Position: refs/heads/master@{#66000} > > Bug: v8:6847 > Change-Id: I0b5acffa01aeb7dade3dc966392814383d900015 > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2022951 > Commit-Queue: Thibaud Michaud <thibaudm@chromium.org> > Reviewed-by: Clemens Backes <clemensb@chromium.org> > Cr-Commit-Position: refs/heads/master@{#66047} Bug: v8:6847 Change-Id: I272f56eee28010f34cc99df475164581c8b63036 Cq-Include-Trybots: luci.v8.try:v8_linux64_tsan_rel Cq-Include-Trybots: luci.v8.try:v8_linux64_msan_rel Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2030741 Commit-Queue: Thibaud Michaud <thibaudm@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Cr-Commit-Position: refs/heads/master@{#66081}
239 lines
8.0 KiB
C++
239 lines
8.0 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_ = i_isolate->wasm_engine()->StartStreamingCompilation(
|
|
i_isolate, WasmFeatures::All(), context,
|
|
"WebAssembly.compileStreaming()", test_resolver_);
|
|
}
|
|
|
|
void OnBytesReceived(const uint8_t* start, size_t length) {
|
|
stream_->OnBytesReceived(Vector<const uint8_t>(start, length));
|
|
}
|
|
|
|
void FinishStream() { stream_->Finish(); }
|
|
|
|
void SetCompiledModuleBytes(const uint8_t* start, size_t length) {
|
|
stream_->SetCompiledModuleBytes(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, int 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(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 =
|
|
CcTest::i_isolate()
|
|
->wasm_engine()
|
|
->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);
|
|
|
|
CcTest::i_isolate()->wasm_engine()->AsyncCompile(
|
|
CcTest::i_isolate(), WasmFeatures::All(), resolverA1,
|
|
ModuleWireBytes(bufferA.begin(), bufferA.end()), true,
|
|
"WebAssembly.compile");
|
|
CcTest::i_isolate()->wasm_engine()->AsyncCompile(
|
|
CcTest::i_isolate(), WasmFeatures::All(), resolverA2,
|
|
ModuleWireBytes(bufferA.begin(), bufferA.end()), true,
|
|
"WebAssembly.compile");
|
|
CcTest::i_isolate()->wasm_engine()->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 = 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
|