Bug 1351488 - Baldr: change JS::WasmModule interface to separate bytecode from compiled code (r=lth)
authorLuke Wagner <luke@mozilla.com>
Wed, 26 Jul 2017 11:00:39 -0500
changeset 420028 a7f939d3f4a462c0b372917ffe21cc2702c0a877
parent 420027 ea5a24395bb7bde69dd84d0105851616f694e8f3
child 420029 add7f83758561c8d534b47af5e98379b4f2f19dd
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslth
bugs1351488
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1351488 - Baldr: change JS::WasmModule interface to separate bytecode from compiled code (r=lth) MozReview-Commit-ID: 9PboJiP8BMM
dom/indexedDB/ActorsParent.cpp
dom/indexedDB/IDBObjectStore.cpp
js/src/jsapi.h
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmModule.cpp
js/src/wasm/WasmModule.h
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -9879,25 +9879,23 @@ CheckWasmModule(FileHelper* aFileHelper,
                                                             Move(buildId),
                                                             nullptr,
                                                             0,
                                                             0);
   if (NS_WARN_IF(!module)) {
     return NS_ERROR_FAILURE;
   }
 
-  size_t compiledSize;
-  module->serializedSize(nullptr, &compiledSize);
-
+  size_t compiledSize = module->compiledSerializedSize();
   UniquePtr<uint8_t[]> compiled(new (fallible) uint8_t[compiledSize]);
   if (NS_WARN_IF(!compiled)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  module->serialize(nullptr, 0, compiled.get(), compiledSize);
+  module->compiledSerialize(compiled.get(), compiledSize);
 
   nsCOMPtr<nsIInputStream> inputStream;
   rv = NS_NewByteInputStream(getter_AddRefs(inputStream),
                              reinterpret_cast<const char*>(compiled.get()),
                              compiledSize);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -344,35 +344,30 @@ StructuredCloneWriteCallback(JSContext* 
       return true;
     }
   }
 
   if (JS::IsWasmModuleObject(aObj)) {
     RefPtr<JS::WasmModule> module = JS::GetWasmModule(aObj);
     MOZ_ASSERT(module);
 
-    size_t bytecodeSize;
-    size_t compiledSize;
-    module->serializedSize(&bytecodeSize, &compiledSize);
-
+    size_t bytecodeSize = module->bytecodeSerializedSize();
     UniquePtr<uint8_t[]> bytecode(new uint8_t[bytecodeSize]);
     MOZ_ASSERT(bytecode);
-
-    UniquePtr<uint8_t[]> compiled(new uint8_t[compiledSize]);
-    MOZ_ASSERT(compiled);
-
-    module->serialize(bytecode.get(),
-                      bytecodeSize,
-                      compiled.get(),
-                      compiledSize);
+    module->bytecodeSerialize(bytecode.get(), bytecodeSize);
 
     RefPtr<BlobImpl> blobImpl =
       new MemoryBlobImpl(bytecode.release(), bytecodeSize, EmptyString());
     RefPtr<Blob> bytecodeBlob = Blob::Create(nullptr, blobImpl);
 
+    size_t compiledSize = module->compiledSerializedSize();
+    UniquePtr<uint8_t[]> compiled(new uint8_t[compiledSize]);
+    MOZ_ASSERT(compiled);
+    module->compiledSerialize(compiled.get(), compiledSize);
+
     blobImpl =
       new MemoryBlobImpl(compiled.release(), compiledSize, EmptyString());
     RefPtr<Blob> compiledBlob = Blob::Create(nullptr, blobImpl);
 
     if (cloneWriteInfo->mFiles.Length() + 1 > size_t(UINT32_MAX)) {
       MOZ_ASSERT(false, "Fix the structured clone data to use a bigger type!");
       return false;
     }
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -6384,46 +6384,61 @@ typedef bool
 (* BuildIdOp)(BuildIdCharVector* buildId);
 
 extern JS_PUBLIC_API(void)
 SetBuildIdOp(JSContext* cx, BuildIdOp buildIdOp);
 
 /**
  * The WasmModule interface allows the embedding to hold a reference to the
  * underying C++ implementation of a JS WebAssembly.Module object for purposes
- * of (de)serialization off the object's JSRuntime's thread.
+ * of efficient postMessage() of WebAssembly.Module and (de)serialization off
+ * the object's JSRuntime's thread for IndexedDB.
+ *
+ * For postMessage() sharing:
+ *
+ * - GetWasmModule() is called when making a structured clone of payload
+ * containing a WebAssembly.Module object. The structured clone buffer holds a
+ * refcount of the JS::WasmModule until createObject() is called in the target
+ * agent's JSContext. The new WebAssembly.Module object continues to hold the
+ * JS::WasmModule and thus the final reference of a JS::WasmModule may be
+ * dropped from any thread and so the virtual destructor (and all internal
+ * methods of the C++ module) must be thread-safe.
+ *
+ * For (de)serialization:
  *
  * - Serialization starts when WebAssembly.Module is passed to the
  * structured-clone algorithm. JS::GetWasmModule is called on the JSRuntime
  * thread that initiated the structured clone to get the JS::WasmModule.
- * This interface is then taken to a background thread where serializedSize()
- * and serialize() are called to write the object to two files: a bytecode file
- * that always allows successful deserialization and a compiled-code file keyed
- * on cpu- and build-id that may become invalid if either of these change between
- * serialization and deserialization. After serialization, the reference is
- * dropped from the background thread.
+ * This interface is then taken to a background thread where the bytecode and
+ * compiled code are written into separate files: a bytecode file that always
+ * allows successful deserialization and a compiled-code file keyed on cpu- and
+ * build-id that may become invalid if either of these change between
+ * serialization and deserialization. After serialization, a reference is
+ * dropped from a separate thread so the virtual destructor must be thread-safe.
  *
  * - Deserialization starts when the structured clone algorithm encounters a
  * serialized WebAssembly.Module. On a background thread, the compiled-code file
  * is opened and CompiledWasmModuleAssumptionsMatch is called to see if it is
  * still valid (as described above). DeserializeWasmModule is then called to
  * construct a JS::WasmModule (also on the background thread), passing the
  * bytecode file descriptor and, if valid, the compiled-code file descriptor.
  * The JS::WasmObject is then transported to the JSRuntime thread (which
  * originated the request) and the wrapping WebAssembly.Module object is created
  * by calling createObject().
  */
 
 struct WasmModule : js::AtomicRefCounted<WasmModule>
 {
     virtual ~WasmModule() {}
 
-    virtual void serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const = 0;
-    virtual void serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
-                           uint8_t* maybeCompiledBegin, size_t maybeCompiledSize) const = 0;
+    virtual size_t bytecodeSerializedSize() const = 0;
+    virtual void bytecodeSerialize(uint8_t* bytecodeBegin, size_t bytecodeSize) const = 0;
+
+    virtual size_t compiledSerializedSize() const = 0;
+    virtual void compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const = 0;
 
     virtual JSObject* createObject(JSContext* cx) = 0;
 };
 
 extern JS_PUBLIC_API(bool)
 IsWasmModuleObject(HandleObject obj);
 
 extern JS_PUBLIC_API(RefPtr<WasmModule>)
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -8489,19 +8489,20 @@ struct ScopedCacheEntryOpenedForRead
 
 static JS::AsmJSCacheResult
 StoreAsmJSModuleInCache(AsmJSParser& parser, Module& module, JSContext* cx)
 {
     ModuleCharsForStore moduleChars;
     if (!moduleChars.init(parser))
         return JS::AsmJSCache_InternalError;
 
-    size_t bytecodeSize, compiledSize;
-    module.serializedSize(&bytecodeSize, &compiledSize);
+    size_t bytecodeSize = module.bytecodeSerializedSize();
     MOZ_RELEASE_ASSERT(bytecodeSize == 0);
+
+    size_t compiledSize = module.compiledSerializedSize();
     MOZ_RELEASE_ASSERT(compiledSize <= UINT32_MAX);
 
     size_t serializedSize = sizeof(uint32_t) +
                             compiledSize +
                             moduleChars.serializedSize();
 
     JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite;
     if (!open)
@@ -8519,17 +8520,17 @@ StoreAsmJSModuleInCache(AsmJSParser& par
     uint8_t* cursor = entry.memory;
 
     // Everything serialized before the Module must not change incompatibly
     // between any two builds (regardless of platform, architecture, ...).
     // (The Module::assumptionsMatch() guard everything in the Module and
     // afterwards.)
     cursor = WriteScalar<uint32_t>(cursor, compiledSize);
 
-    module.serialize(/* bytecodeBegin = */ nullptr, /* bytecodeSize = */ 0, cursor, compiledSize);
+    module.compiledSerialize(cursor, compiledSize);
     cursor += compiledSize;
 
     cursor = moduleChars.serialize(cursor);
 
     MOZ_RELEASE_ASSERT(cursor == entry.memory + serializedSize);
 
     return JS::AsmJSCache_Success;
 }
--- a/js/src/wasm/WasmModule.cpp
+++ b/js/src/wasm/WasmModule.cpp
@@ -208,75 +208,75 @@ LinkData::deserialize(const uint8_t* cur
 }
 
 size_t
 LinkData::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return tier_->sizeOfExcludingThis(mallocSizeOf);
 }
 
-/* virtual */ void
-Module::serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const
+/* virtual */ size_t
+Module::bytecodeSerializedSize() const
 {
-    if (maybeBytecodeSize)
-        *maybeBytecodeSize = bytecode_->bytes.length();
-
-    // The compiled debug code must not be saved, set compiled size to 0,
-    // so Module::assumptionsMatch will return false during assumptions
-    // deserialization.
-    if (maybeCompiledSize && metadata().debugEnabled)
-        *maybeCompiledSize = 0;
-
-    if (maybeCompiledSize && !metadata().debugEnabled) {
-        *maybeCompiledSize = assumptions_.serializedSize() +
-                             linkData_.serializedSize() +
-                             SerializedVectorSize(imports_) +
-                             SerializedVectorSize(exports_) +
-                             SerializedPodVectorSize(dataSegments_) +
-                             SerializedVectorSize(elemSegments_) +
-                             code_->serializedSize();
-    }
+    return bytecode_->bytes.length();
 }
 
 /* virtual */ void
-Module::serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
-                  uint8_t* maybeCompiledBegin, size_t maybeCompiledSize) const
+Module::bytecodeSerialize(uint8_t* bytecodeBegin, size_t bytecodeSize) const
 {
-    MOZ_ASSERT(!!maybeBytecodeBegin == !!maybeBytecodeSize);
-    MOZ_ASSERT(!!maybeCompiledBegin == !!maybeCompiledSize);
+    MOZ_ASSERT(!!bytecodeBegin == !!bytecodeSize);
+
+    // Bytecode deserialization is not guarded by Assumptions and thus must not
+    // change incompatibly between builds. For simplicity, the format of the
+    // bytecode file is a .wasm file which ensures backwards compatibility.
+
+    const Bytes& bytes = bytecode_->bytes;
+    uint8_t* bytecodeEnd = WriteBytes(bytecodeBegin, bytes.begin(), bytes.length());
+    MOZ_RELEASE_ASSERT(bytecodeEnd == bytecodeBegin + bytecodeSize);
+}
 
-    if (maybeBytecodeBegin) {
-        // Bytecode deserialization is not guarded by Assumptions and thus must not
-        // change incompatibly between builds. Thus, for simplicity, the format
-        // of the bytecode file is simply a .wasm file (thus, backwards
-        // compatibility is ensured by backwards compatibility of the wasm
-        // binary format).
+/* virtual */ size_t
+Module::compiledSerializedSize() const
+{
+    // The compiled debug code must not be saved, set compiled size to 0,
+    // so Module::assumptionsMatch will return false during assumptions
+    // deserialization.
+    if (metadata().debugEnabled)
+        return 0;
 
-        const Bytes& bytes = bytecode_->bytes;
-        uint8_t* bytecodeEnd = WriteBytes(maybeBytecodeBegin, bytes.begin(), bytes.length());
-        MOZ_RELEASE_ASSERT(bytecodeEnd == maybeBytecodeBegin + maybeBytecodeSize);
+    return assumptions_.serializedSize() +
+           linkData_.serializedSize() +
+           SerializedVectorSize(imports_) +
+           SerializedVectorSize(exports_) +
+           SerializedPodVectorSize(dataSegments_) +
+           SerializedVectorSize(elemSegments_) +
+           code_->serializedSize();
+}
+
+/* virtual */ void
+Module::compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const
+{
+    if (metadata().debugEnabled) {
+        MOZ_RELEASE_ASSERT(compiledSize == 0);
+        return;
     }
 
-    MOZ_ASSERT_IF(maybeCompiledBegin && metadata().debugEnabled, maybeCompiledSize == 0);
-
-    if (maybeCompiledBegin && !metadata().debugEnabled) {
-        // Assumption must be serialized at the beginning of the compiled bytes so
-        // that compiledAssumptionsMatch can detect a build-id mismatch before any
-        // other decoding occurs.
+    // Assumption must be serialized at the beginning of the compiled bytes so
+    // that compiledAssumptionsMatch can detect a build-id mismatch before any
+    // other decoding occurs.
 
-        uint8_t* cursor = maybeCompiledBegin;
-        cursor = assumptions_.serialize(cursor);
-        cursor = linkData_.serialize(cursor);
-        cursor = SerializeVector(cursor, imports_);
-        cursor = SerializeVector(cursor, exports_);
-        cursor = SerializePodVector(cursor, dataSegments_);
-        cursor = SerializeVector(cursor, elemSegments_);
-        cursor = code_->serialize(cursor, linkData_);
-        MOZ_RELEASE_ASSERT(cursor == maybeCompiledBegin + maybeCompiledSize);
-    }
+    uint8_t* cursor = compiledBegin;
+    cursor = assumptions_.serialize(cursor);
+    cursor = linkData_.serialize(cursor);
+    cursor = SerializeVector(cursor, imports_);
+    cursor = SerializeVector(cursor, exports_);
+    cursor = SerializePodVector(cursor, dataSegments_);
+    cursor = SerializeVector(cursor, elemSegments_);
+    cursor = code_->serialize(cursor, linkData_);
+    MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
 }
 
 /* static */ bool
 Module::assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin, size_t remain)
 {
     Assumptions cached;
     if (!cached.deserialize(compiledBegin, remain))
         return false;
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -183,19 +183,21 @@ class Module : public JS::WasmModule
                      HandleWasmTableObject tableImport,
                      HandleWasmMemoryObject memoryImport,
                      const ValVector& globalImports,
                      HandleObject instanceProto,
                      MutableHandleWasmInstanceObject instanceObj) const;
 
     // Structured clone support:
 
-    void serializedSize(size_t* maybeBytecodeSize, size_t* maybeCompiledSize) const override;
-    void serialize(uint8_t* maybeBytecodeBegin, size_t maybeBytecodeSize,
-                   uint8_t* maybeCompiledBegin, size_t maybeCompiledSize) const override;
+    size_t bytecodeSerializedSize() const override;
+    void bytecodeSerialize(uint8_t* bytecodeBegin, size_t bytecodeSize) const override;
+    size_t compiledSerializedSize() const override;
+    void compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const override;
+
     static bool assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin,
                                  size_t remain);
     static RefPtr<Module> deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize,
                                       const uint8_t* compiledBegin, size_t compiledSize,
                                       Metadata* maybeMetadata = nullptr);
     JSObject* createObject(JSContext* cx) override;
 
     // about:memory reporting: