Bug 1276029 - Baldr: split bytecode from rest of compiled code (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Fri, 21 Oct 2016 22:29:52 -0500
changeset 318989 cc32d7a9d319a27c3a3f5a2f05288ab1454f9950
parent 318988 9b0f339a85d6bd732cac2ac37f528adf9bae1e38
child 318990 f325d73d76d14f2b4ee2e05d6c23a8f8f521907f
push id83052
push userlwagner@mozilla.com
push dateSat, 22 Oct 2016 03:30:45 +0000
treeherdermozilla-inbound@f325d73d76d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1276029
milestone52.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 1276029 - Baldr: split bytecode from rest of compiled code (r=bbouvier) MozReview-Commit-ID: HFf7bRiGgE4
js/src/asmjs/AsmJS.cpp
js/src/asmjs/WasmModule.cpp
js/src/asmjs/WasmModule.h
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -8451,17 +8451,21 @@ struct ScopedCacheEntryOpenedForRead
 
 static JS::AsmJSCacheResult
 StoreAsmJSModuleInCache(AsmJSParser& parser, Module& module, ExclusiveContext* cx)
 {
     ModuleCharsForStore moduleChars;
     if (!moduleChars.init(parser))
         return JS::AsmJSCache_InternalError;
 
-    size_t serializedSize = module.serializedSize() +
+    size_t bytecodeSize, compiledSize;
+    module.serializedSize(&bytecodeSize, &compiledSize);
+
+    size_t serializedSize = 2 * sizeof(size_t) +
+                            bytecodeSize + compiledSize +
                             moduleChars.serializedSize();
 
     JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite;
     if (!open)
         return JS::AsmJSCache_Disabled_Internal;
 
     const char16_t* begin = parser.tokenStream.rawCharPtrAt(ModuleChars::beginOffset(parser));
     const char16_t* end = parser.tokenStream.rawCharPtrAt(ModuleChars::endOffset(parser));
@@ -8469,20 +8473,30 @@ StoreAsmJSModuleInCache(AsmJSParser& par
 
     ScopedCacheEntryOpenedForWrite entry(cx, serializedSize);
     JS::AsmJSCacheResult openResult =
         open(cx->global(), installed, begin, end, serializedSize, &entry.memory, &entry.handle);
     if (openResult != JS::AsmJSCache_Success)
         return openResult;
 
     uint8_t* cursor = entry.memory;
-    cursor = module.serialize(cursor);
+
+    cursor = WriteScalar<size_t>(cursor, bytecodeSize);
+    cursor = WriteScalar<size_t>(cursor, compiledSize);
+
+    uint8_t* compiledBegin = cursor;
+    uint8_t* bytecodeBegin = compiledBegin + compiledSize;;
+
+    module.serialize(bytecodeBegin, bytecodeSize, compiledBegin, compiledSize);
+    cursor = bytecodeBegin + bytecodeSize;
+
     cursor = moduleChars.serialize(cursor);
 
-    MOZ_ASSERT(cursor == entry.memory + serializedSize);
+    MOZ_RELEASE_ASSERT(cursor == entry.memory + serializedSize);
+
     return JS::AsmJSCache_Success;
 }
 
 static bool
 LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, bool* loadedFromCache,
                          SharedModule* module, UniqueChars* compilationTimeReport)
 {
     int64_t before = PRMJ_Now();
@@ -8497,51 +8511,57 @@ LookupAsmJSModuleInCache(ExclusiveContex
     const char16_t* limit = parser.tokenStream.rawLimit();
 
     ScopedCacheEntryOpenedForRead entry(cx);
     if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory, &entry.handle))
         return true;
 
     const uint8_t* cursor = entry.memory;
 
+    size_t bytecodeSize, compiledSize;
+    cursor = ReadScalar<size_t>(cursor, &bytecodeSize);
+    cursor = ReadScalar<size_t>(cursor, &compiledSize);
+
+    const uint8_t* compiledBegin = cursor;
+    const uint8_t* bytecodeBegin = compiledBegin + compiledSize;
+
     Assumptions assumptions;
     if (!assumptions.initBuildIdFromContext(cx))
         return false;
 
-    if (!Module::assumptionsMatch(assumptions, cursor))
+    if (!Module::assumptionsMatch(assumptions, compiledBegin))
         return true;
 
     MutableAsmJSMetadata asmJSMetadata = cx->new_<AsmJSMetadata>();
     if (!asmJSMetadata)
         return false;
 
-    cursor = Module::deserialize(cursor, module, asmJSMetadata.get());
-    if (!cursor) {
+    *module = Module::deserialize(bytecodeBegin, bytecodeSize, compiledBegin, compiledSize,
+                                  asmJSMetadata.get());
+    if (!*module) {
         ReportOutOfMemory(cx);
         return false;
     }
+    cursor = bytecodeBegin + bytecodeSize;
 
     // Due to the hash comparison made by openEntryForRead, this should succeed
     // with high probability.
     ModuleCharsForLookup moduleChars;
     cursor = moduleChars.deserialize(cursor);
     if (!moduleChars.match(parser))
         return true;
 
+    MOZ_RELEASE_ASSERT(cursor == entry.memory + entry.serializedSize);
+
     // See AsmJSMetadata comment as well as ModuleValidator::init().
     asmJSMetadata->srcStart = parser.pc->functionBox()->functionNode->pn_body->pn_pos.begin;
     asmJSMetadata->srcBodyStart = parser.tokenStream.currentToken().pos.end;
     asmJSMetadata->strict = parser.pc->sc()->strict() && !parser.pc->sc()->hasExplicitUseStrict();
     asmJSMetadata->scriptSource.reset(parser.ss);
 
-    bool atEnd = cursor == entry.memory + entry.serializedSize;
-    MOZ_ASSERT(atEnd, "Corrupt cache file");
-    if (!atEnd)
-        return true;
-
     if (!parser.tokenStream.advance(asmJSMetadata->srcEndBeforeCurly()))
         return false;
 
     int64_t after = PRMJ_Now();
     int ms = (after - before) / PRMJ_USEC_PER_MSEC;
     *compilationTimeReport = UniqueChars(JS_smprintf("loaded from cache in %dms", ms));
     if (!*compilationTimeReport)
         return false;
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -252,62 +252,85 @@ ElemSegment::deserialize(const uint8_t* 
 
 size_t
 ElemSegment::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return elemFuncIndices.sizeOfExcludingThis(mallocSizeOf) +
            elemCodeRangeIndices.sizeOfExcludingThis(mallocSizeOf);
 }
 
-size_t
-Module::serializedSize() const
+void
+Module::serializedSize(size_t* bytecodeSize, size_t* compiledSize) const
 {
-    return assumptions_.serializedSize() +
-           SerializedPodVectorSize(code_) +
-           linkData_.serializedSize() +
-           SerializedVectorSize(imports_) +
-           SerializedVectorSize(exports_) +
-           SerializedPodVectorSize(dataSegments_) +
-           SerializedVectorSize(elemSegments_) +
-           metadata_->serializedSize() +
-           SerializedPodVectorSize(bytecode_->bytes);
+    *bytecodeSize = SerializedPodVectorSize(bytecode_->bytes);
+
+    *compiledSize = assumptions_.serializedSize() +
+                    SerializedPodVectorSize(code_) +
+                    linkData_.serializedSize() +
+                    SerializedVectorSize(imports_) +
+                    SerializedVectorSize(exports_) +
+                    SerializedPodVectorSize(dataSegments_) +
+                    SerializedVectorSize(elemSegments_) +
+                    metadata_->serializedSize();
 }
 
-uint8_t*
-Module::serialize(uint8_t* cursor) const
+void
+Module::serialize(uint8_t* bytecodeBegin, size_t bytecodeSize,
+                  uint8_t* compiledBegin, size_t compiledSize) const
 {
-    // Assumption must be serialized at the beginning so that assumptionsMatch
-    // can detect a build-id mismatch before any other decoding occurs.
+    uint8_t* bytecodeEnd = SerializePodVector(bytecodeBegin, bytecode_->bytes);
+    MOZ_RELEASE_ASSERT(bytecodeEnd == bytecodeBegin + bytecodeSize);
 
+    uint8_t* cursor = compiledBegin;
     cursor = assumptions_.serialize(cursor);
     cursor = SerializePodVector(cursor, code_);
     cursor = linkData_.serialize(cursor);
     cursor = SerializeVector(cursor, imports_);
     cursor = SerializeVector(cursor, exports_);
     cursor = SerializePodVector(cursor, dataSegments_);
     cursor = SerializeVector(cursor, elemSegments_);
     cursor = metadata_->serialize(cursor);
-    cursor = SerializePodVector(cursor, bytecode_->bytes);
-    return cursor;
+    MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
 }
 
 /* static */ bool
 Module::assumptionsMatch(const Assumptions& current, const uint8_t* cursor)
 {
     Assumptions cached;
     cursor = cached.deserialize(cursor);
     if (!cursor)
         return false;
 
     return current == cached;
 }
 
-/* static */ const uint8_t*
-Module::deserialize(const uint8_t* cursor, SharedModule* module, Metadata* maybeMetadata)
+/* static */ SharedModule
+Module::deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize,
+                    const uint8_t* compiledBegin, size_t compiledSize,
+                    Metadata* maybeMetadata)
 {
+    // Bytecode deserialization is not guarded by Assumptions and thus must not
+    // change incompatibly between builds.
+
+    MutableBytes bytecode = js_new<ShareableBytes>();
+    if (!bytecode)
+        return nullptr;
+
+    const uint8_t* bytecodeEnd = DeserializePodVector(bytecodeBegin, &bytecode->bytes);
+    if (!bytecodeEnd)
+        return nullptr;
+
+    MOZ_RELEASE_ASSERT(bytecodeEnd == bytecodeBegin + bytecodeSize);
+
+    // 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.
+
+    const uint8_t* cursor = compiledBegin;
+
     Assumptions assumptions;
     cursor = assumptions.deserialize(cursor);
     if (!cursor)
         return nullptr;
 
     Bytes code;
     cursor = DeserializePodVector(cursor, &code);
     if (!cursor)
@@ -344,38 +367,29 @@ Module::deserialize(const uint8_t* curso
     } else {
         metadata = js_new<Metadata>();
         if (!metadata)
             return nullptr;
     }
     cursor = metadata->deserialize(cursor);
     if (!cursor)
         return nullptr;
+
+    MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
     MOZ_RELEASE_ASSERT(!!maybeMetadata == metadata->isAsmJS());
 
-    MutableBytes bytecode = js_new<ShareableBytes>();
-    if (!bytecode)
-        return nullptr;
-    cursor = DeserializePodVector(cursor, &bytecode->bytes);
-    if (!cursor)
-        return nullptr;
-
-    *module = js_new<Module>(Move(assumptions),
-                             Move(code),
-                             Move(linkData),
-                             Move(imports),
-                             Move(exports),
-                             Move(dataSegments),
-                             Move(elemSegments),
-                             *metadata,
-                             *bytecode);
-    if (!*module)
-        return nullptr;
-
-    return cursor;
+    return js_new<Module>(Move(assumptions),
+                          Move(code),
+                          Move(linkData),
+                          Move(imports),
+                          Move(exports),
+                          Move(dataSegments),
+                          Move(elemSegments),
+                          *metadata,
+                          *bytecode);
 }
 
 /* virtual */ void
 Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                       Metadata::SeenSet* seenMetadata,
                       ShareableBytes::SeenSet* seenBytes,
                       size_t* code,
                       size_t* data) const
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -229,20 +229,22 @@ class Module : public RefCounted<Module>
                      HandleWasmTableObject tableImport,
                      HandleWasmMemoryObject memoryImport,
                      const ValVector& globalImports,
                      HandleObject instanceProto,
                      MutableHandleWasmInstanceObject instanceObj) const;
 
     // Structured clone support:
 
-    size_t serializedSize() const;
-    uint8_t* serialize(uint8_t* cursor) const;
-    static bool assumptionsMatch(const Assumptions& current, const uint8_t* cursor);
-    static const uint8_t* deserialize(const uint8_t* cursor, RefPtr<Module>* module,
+    void serializedSize(size_t* bytecodeSize, size_t* compiledSize) const;
+    void serialize(uint8_t* bytecodeBegin, size_t bytecodeSize,
+                   uint8_t* compiledBegin, size_t compiledSize) const;
+    static bool assumptionsMatch(const Assumptions& current, const uint8_t* compiledBegin);
+    static RefPtr<Module> deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize,
+                                      const uint8_t* compiledBegin, size_t compiledSize,
                                       Metadata* maybeMetadata = nullptr);
 
     // about:memory reporting:
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf,
                        Metadata::SeenSet* seenMetadata,
                        ShareableBytes::SeenSet* seenBytes,
                        size_t* code, size_t* data) const;