Commit 8d3ababb authored by Clemens Backes's avatar Clemens Backes Committed by Commit Bot

[wasm] Wait until top tier finished before serializing

We only want to serialize TurboFan code, because Liftoff code could
contain breakpoints, and we start thinking about embedding other
non-relocatable constants.
Thus, wait until top-tier compilation finished before triggering
serialization.

A follow-up CL will make serialization fail if any Liftoff code is
encountered.

R=ahaas@chromium.org

Bug: v8:10777
Change-Id: I73d6c2d868545fcd4069a8cf9850ca7fca375ecb
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2349293Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69332}
parent 5ec483c3
...@@ -1326,30 +1326,29 @@ RUNTIME_FUNCTION(Runtime_SerializeDeserializeNow) { ...@@ -1326,30 +1326,29 @@ RUNTIME_FUNCTION(Runtime_SerializeDeserializeNow) {
return ReadOnlyRoots(isolate).undefined_value(); return ReadOnlyRoots(isolate).undefined_value();
} }
// Take a compiled wasm module and serialize it into an array buffer, which is // Wait until the given module is fully tiered up, then serialize it into an
// then returned. // array buffer.
RUNTIME_FUNCTION(Runtime_SerializeWasmModule) { RUNTIME_FUNCTION(Runtime_SerializeWasmModule) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(WasmModuleObject, module_obj, 0); CONVERT_ARG_HANDLE_CHECKED(WasmModuleObject, module_obj, 0);
wasm::NativeModule* native_module = module_obj->native_module(); wasm::NativeModule* native_module = module_obj->native_module();
native_module->compilation_state()->WaitForTopTierFinished();
DCHECK(!native_module->compilation_state()->failed());
wasm::WasmSerializer wasm_serializer(native_module); wasm::WasmSerializer wasm_serializer(native_module);
size_t byte_length = wasm_serializer.GetSerializedNativeModuleSize(); size_t byte_length = wasm_serializer.GetSerializedNativeModuleSize();
MaybeHandle<JSArrayBuffer> result = Handle<JSArrayBuffer> array_buffer =
isolate->factory()->NewJSArrayBufferAndBackingStore( isolate->factory()
byte_length, InitializedFlag::kUninitialized); ->NewJSArrayBufferAndBackingStore(byte_length,
InitializedFlag::kUninitialized)
Handle<JSArrayBuffer> array_buffer; .ToHandleChecked();
if (result.ToHandle(&array_buffer) &&
wasm_serializer.SerializeNativeModule(
{reinterpret_cast<uint8_t*>(array_buffer->backing_store()),
byte_length})) {
return *array_buffer;
}
UNREACHABLE(); CHECK(wasm_serializer.SerializeNativeModule(
{static_cast<uint8_t*>(array_buffer->backing_store()), byte_length}));
return *array_buffer;
} }
// Take an array buffer and attempt to reconstruct a compiled wasm module. // Take an array buffer and attempt to reconstruct a compiled wasm module.
......
...@@ -1035,6 +1035,8 @@ auto Module::serialize() const -> vec<byte_t> { ...@@ -1035,6 +1035,8 @@ auto Module::serialize() const -> vec<byte_t> {
impl(this)->v8_object()->native_module(); impl(this)->v8_object()->native_module();
i::Vector<const uint8_t> wire_bytes = native_module->wire_bytes(); i::Vector<const uint8_t> wire_bytes = native_module->wire_bytes();
size_t binary_size = wire_bytes.size(); size_t binary_size = wire_bytes.size();
// We can only serialize after top-tier compilation (TurboFan) finished.
native_module->compilation_state()->WaitForTopTierFinished();
i::wasm::WasmSerializer serializer(native_module); i::wasm::WasmSerializer serializer(native_module);
size_t serial_size = serializer.GetSerializedNativeModuleSize(); size_t serial_size = serializer.GetSerializedNativeModuleSize();
size_t size_size = i::wasm::LEBHelper::sizeof_u64v(binary_size); size_t size_size = i::wasm::LEBHelper::sizeof_u64v(binary_size);
......
...@@ -101,7 +101,7 @@ enum class CompilationEvent : uint8_t { ...@@ -101,7 +101,7 @@ enum class CompilationEvent : uint8_t {
// The implementation of {CompilationState} lives in module-compiler.cc. // The implementation of {CompilationState} lives in module-compiler.cc.
// This is the PIMPL interface to that private class. // This is the PIMPL interface to that private class.
class CompilationState { class V8_EXPORT_PRIVATE CompilationState {
public: public:
using callback_t = std::function<void(CompilationEvent)>; using callback_t = std::function<void(CompilationEvent)>;
...@@ -113,15 +113,17 @@ class CompilationState { ...@@ -113,15 +113,17 @@ class CompilationState {
void SetWireBytesStorage(std::shared_ptr<WireBytesStorage>); void SetWireBytesStorage(std::shared_ptr<WireBytesStorage>);
V8_EXPORT_PRIVATE std::shared_ptr<WireBytesStorage> GetWireBytesStorage() std::shared_ptr<WireBytesStorage> GetWireBytesStorage() const;
const;
void AddCallback(callback_t); void AddCallback(callback_t);
// Wait until top tier finished, or compilation failed (to avoid deadlocks).
void WaitForTopTierFinished();
bool failed() const; bool failed() const;
V8_EXPORT_PRIVATE bool baseline_compilation_finished() const; bool baseline_compilation_finished() const;
V8_EXPORT_PRIVATE bool top_tier_compilation_finished() const; bool top_tier_compilation_finished() const;
V8_EXPORT_PRIVATE bool recompilation_finished() const; bool recompilation_finished() const;
// Override {operator delete} to avoid implicit instantiation of {operator // Override {operator delete} to avoid implicit instantiation of {operator
// delete} with {size_t} argument. The {size_t} argument would be incorrect. // delete} with {size_t} argument. The {size_t} argument would be incorrect.
......
...@@ -688,6 +688,17 @@ void CompilationState::AddCallback(CompilationState::callback_t callback) { ...@@ -688,6 +688,17 @@ void CompilationState::AddCallback(CompilationState::callback_t callback) {
return Impl(this)->AddCallback(std::move(callback)); return Impl(this)->AddCallback(std::move(callback));
} }
void CompilationState::WaitForTopTierFinished() {
auto top_tier_finished_semaphore = std::make_shared<base::Semaphore>(0);
AddCallback([top_tier_finished_semaphore](CompilationEvent event) {
if (event == CompilationEvent::kFailedCompilation ||
event == CompilationEvent::kFinishedTopTierCompilation) {
top_tier_finished_semaphore->Signal();
}
});
top_tier_finished_semaphore->Wait();
}
bool CompilationState::failed() const { return Impl(this)->failed(); } bool CompilationState::failed() const { return Impl(this)->failed(); }
bool CompilationState::baseline_compilation_finished() const { bool CompilationState::baseline_compilation_finished() const {
......
...@@ -268,10 +268,11 @@ ZoneBuffer GetValidCompiledModuleBytes(Zone* zone, ZoneBuffer wire_bytes) { ...@@ -268,10 +268,11 @@ ZoneBuffer GetValidCompiledModuleBytes(Zone* zone, ZoneBuffer wire_bytes) {
// Serialize the NativeModule. // Serialize the NativeModule.
std::shared_ptr<NativeModule> native_module = tester.native_module(); std::shared_ptr<NativeModule> native_module = tester.native_module();
CHECK(native_module); CHECK(native_module);
native_module->compilation_state()->WaitForTopTierFinished();
i::wasm::WasmSerializer serializer(native_module.get()); i::wasm::WasmSerializer serializer(native_module.get());
size_t size = serializer.GetSerializedNativeModuleSize(); size_t size = serializer.GetSerializedNativeModuleSize();
std::vector<byte> buffer(size); std::vector<byte> buffer(size);
CHECK(serializer.SerializeNativeModule({buffer.data(), size})); CHECK(serializer.SerializeNativeModule(VectorOf(buffer)));
ZoneBuffer result(zone, size); ZoneBuffer result(zone, size);
result.write(buffer.data(), size); result.write(buffer.data(), size);
return result; return result;
......
...@@ -148,6 +148,10 @@ class WasmSerializationTest { ...@@ -148,6 +148,10 @@ class WasmSerializationTest {
// Check that the native module exists at this point. // Check that the native module exists at this point.
CHECK(weak_native_module.lock()); CHECK(weak_native_module.lock());
auto* native_module = module_object->native_module();
native_module->compilation_state()->WaitForTopTierFinished();
DCHECK(!native_module->compilation_state()->failed());
v8::Local<v8::Object> v8_module_obj = v8::Local<v8::Object> v8_module_obj =
v8::Utils::ToLocal(Handle<JSObject>::cast(module_object)); v8::Utils::ToLocal(Handle<JSObject>::cast(module_object));
CHECK(v8_module_obj->IsWasmModuleObject()); CHECK(v8_module_obj->IsWasmModuleObject());
...@@ -163,6 +167,7 @@ class WasmSerializationTest { ...@@ -163,6 +167,7 @@ class WasmSerializationTest {
wire_bytes_ = {bytes_copy, uncompiled_bytes.size()}; wire_bytes_ = {bytes_copy, uncompiled_bytes.size()};
// keep alive data_ until the end // keep alive data_ until the end
data_ = compiled_module.Serialize(); data_ = compiled_module.Serialize();
CHECK_LT(0, data_.size);
} }
// Dispose of serialization isolate to destroy the reference to the // Dispose of serialization isolate to destroy the reference to the
// NativeModule, which removes it from the module cache in the wasm engine // NativeModule, which removes it from the module cache in the wasm engine
......
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