Bug 1276029 - Baldr: move assumptions from Metadata to Module (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Fri, 21 Oct 2016 22:29:52 -0500
changeset 319029 9b0f339a85d6bd732cac2ac37f528adf9bae1e38
parent 319028 099453a244223d8f85c4f19a25da5d7de0058dff
child 319030 cc32d7a9d319a27c3a3f5a2f05288ab1454f9950
push id30858
push userryanvm@gmail.com
push dateSun, 23 Oct 2016 17:17:41 +0000
treeherdermozilla-central@a9a41b69f3f9 [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: move assumptions from Metadata to Module (r=bbouvier) MozReview-Commit-ID: 7NzJql9oNyi
js/src/asmjs/AsmJS.cpp
js/src/asmjs/WasmCode.cpp
js/src/asmjs/WasmCode.h
js/src/asmjs/WasmGenerator.cpp
js/src/asmjs/WasmGenerator.h
js/src/asmjs/WasmModule.cpp
js/src/asmjs/WasmModule.h
js/src/asmjs/WasmSerialize.h
js/src/asmjs/WasmTypes.cpp
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -8451,36 +8451,36 @@ 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 = moduleChars.serializedSize() +
-                            module.serializedSize();
+    size_t serializedSize = module.serializedSize() +
+                            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));
     bool installed = parser.options().installedFile;
 
     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 = moduleChars.serialize(cursor);
-    cursor = module.serialize(cursor);
 
     MOZ_ASSERT(cursor == entry.memory + serializedSize);
     return JS::AsmJSCache_Success;
 }
 
 static bool
 LookupAsmJSModuleInCache(ExclusiveContext* cx, AsmJSParser& parser, bool* loadedFromCache,
                          SharedModule* module, UniqueChars* compilationTimeReport)
@@ -8497,49 +8497,51 @@ 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;
 
-    ModuleCharsForLookup moduleChars;
-    cursor = moduleChars.deserialize(cursor);
-    if (!moduleChars.match(parser))
+    Assumptions assumptions;
+    if (!assumptions.initBuildIdFromContext(cx))
+        return false;
+
+    if (!Module::assumptionsMatch(assumptions, cursor))
         return true;
 
     MutableAsmJSMetadata asmJSMetadata = cx->new_<AsmJSMetadata>();
     if (!asmJSMetadata)
         return false;
 
     cursor = Module::deserialize(cursor, module, asmJSMetadata.get());
     if (!cursor) {
         ReportOutOfMemory(cx);
         return false;
     }
 
+    // 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;
+
     // 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;
 
-    Assumptions assumptions;
-    if (!assumptions.initBuildIdFromContext(cx))
-        return false;
-
-    if (assumptions != (*module)->metadata().assumptions)
-        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/WasmCode.cpp
+++ b/js/src/asmjs/WasmCode.cpp
@@ -451,18 +451,17 @@ Metadata::serializedSize() const
            SerializedPodVectorSize(tables) +
            SerializedPodVectorSize(memoryAccesses) +
            SerializedPodVectorSize(memoryPatches) +
            SerializedPodVectorSize(boundsChecks) +
            SerializedPodVectorSize(codeRanges) +
            SerializedPodVectorSize(callSites) +
            SerializedPodVectorSize(callThunks) +
            SerializedPodVectorSize(funcNames) +
-           filename.serializedSize() +
-           assumptions.serializedSize();
+           filename.serializedSize();
 }
 
 uint8_t*
 Metadata::serialize(uint8_t* cursor) const
 {
     cursor = WriteBytes(cursor, &pod(), sizeof(pod()));
     cursor = SerializeVector(cursor, funcImports);
     cursor = SerializeVector(cursor, funcDefExports);
@@ -472,17 +471,16 @@ Metadata::serialize(uint8_t* cursor) con
     cursor = SerializePodVector(cursor, memoryAccesses);
     cursor = SerializePodVector(cursor, memoryPatches);
     cursor = SerializePodVector(cursor, boundsChecks);
     cursor = SerializePodVector(cursor, codeRanges);
     cursor = SerializePodVector(cursor, callSites);
     cursor = SerializePodVector(cursor, callThunks);
     cursor = SerializePodVector(cursor, funcNames);
     cursor = filename.serialize(cursor);
-    cursor = assumptions.serialize(cursor);
     return cursor;
 }
 
 /* static */ const uint8_t*
 Metadata::deserialize(const uint8_t* cursor)
 {
     (cursor = ReadBytes(cursor, &pod(), sizeof(pod()))) &&
     (cursor = DeserializeVector(cursor, &funcImports)) &&
@@ -492,18 +490,17 @@ Metadata::deserialize(const uint8_t* cur
     (cursor = DeserializePodVector(cursor, &tables)) &&
     (cursor = DeserializePodVector(cursor, &memoryAccesses)) &&
     (cursor = DeserializePodVector(cursor, &memoryPatches)) &&
     (cursor = DeserializePodVector(cursor, &boundsChecks)) &&
     (cursor = DeserializePodVector(cursor, &codeRanges)) &&
     (cursor = DeserializePodVector(cursor, &callSites)) &&
     (cursor = DeserializePodVector(cursor, &callThunks)) &&
     (cursor = DeserializePodVector(cursor, &funcNames)) &&
-    (cursor = filename.deserialize(cursor)) &&
-    (cursor = assumptions.deserialize(cursor));
+    (cursor = filename.deserialize(cursor));
     return cursor;
 }
 
 size_t
 Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
 {
     return SizeOfVectorExcludingThis(funcImports, mallocSizeOf) +
            SizeOfVectorExcludingThis(funcDefExports, mallocSizeOf) +
@@ -512,18 +509,17 @@ Metadata::sizeOfExcludingThis(MallocSize
            tables.sizeOfExcludingThis(mallocSizeOf) +
            memoryAccesses.sizeOfExcludingThis(mallocSizeOf) +
            memoryPatches.sizeOfExcludingThis(mallocSizeOf) +
            boundsChecks.sizeOfExcludingThis(mallocSizeOf) +
            codeRanges.sizeOfExcludingThis(mallocSizeOf) +
            callSites.sizeOfExcludingThis(mallocSizeOf) +
            callThunks.sizeOfExcludingThis(mallocSizeOf) +
            funcNames.sizeOfExcludingThis(mallocSizeOf) +
-           filename.sizeOfExcludingThis(mallocSizeOf) +
-           assumptions.sizeOfExcludingThis(mallocSizeOf);
+           filename.sizeOfExcludingThis(mallocSizeOf);
 }
 
 struct ProjectIndex
 {
     const FuncDefExportVector& funcDefExports;
 
     explicit ProjectIndex(const FuncDefExportVector& funcDefExports)
       : funcDefExports(funcDefExports)
--- a/js/src/asmjs/WasmCode.h
+++ b/js/src/asmjs/WasmCode.h
@@ -472,17 +472,16 @@ struct Metadata : ShareableBase<Metadata
     MemoryAccessVector    memoryAccesses;
     MemoryPatchVector     memoryPatches;
     BoundsCheckVector     boundsChecks;
     CodeRangeVector       codeRanges;
     CallSiteVector        callSites;
     CallThunkVector       callThunks;
     NameInBytecodeVector  funcNames;
     CacheableChars        filename;
-    Assumptions           assumptions;
 
     bool usesMemory() const { return UsesMemory(memoryUsage); }
     bool hasSharedMemory() const { return memoryUsage == MemoryUsage::Shared; }
 
     const FuncDefExport& lookupFuncDefExport(uint32_t funcDefIndex) const;
 
     // AsmJSMetadata derives Metadata iff isAsmJS(). Mostly this distinction is
     // encapsulated within AsmJS.cpp, but the additional virtual functions allow
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -120,17 +120,17 @@ ModuleGenerator::init(UniqueModuleGenera
     }
 
     if (args.scriptedCaller.filename) {
         metadata_->filename = DuplicateString(args.scriptedCaller.filename.get());
         if (!metadata_->filename)
             return false;
     }
 
-    if (!metadata_->assumptions.clone(args.assumptions))
+    if (!assumptions_.clone(args.assumptions))
         return false;
 
     // For asm.js, the Vectors in ModuleGeneratorData are max-sized reservations
     // and will be initialized in a linear order via init* functions as the
     // module is generated. For wasm, the Vectors are correctly-sized and
     // already initialized.
 
     if (!isAsmJS()) {
@@ -1149,17 +1149,18 @@ ModuleGenerator::finish(const ShareableB
         MOZ_ASSERT(codeRange.begin() >= lastEnd);
         lastEnd = codeRange.end();
     }
 #endif
 
     if (!finishLinkData(code))
         return nullptr;
 
-    return SharedModule(js_new<Module>(Move(code),
+    return SharedModule(js_new<Module>(Move(assumptions_),
+                                       Move(code),
                                        Move(linkData_),
                                        Move(imports_),
                                        Move(exports_),
                                        Move(dataSegments_),
                                        Move(elemSegments_),
                                        *metadata_,
                                        bytecode));
 }
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -88,16 +88,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     typedef Vector<IonCompileTask, 0, SystemAllocPolicy> IonCompileTaskVector;
     typedef Vector<IonCompileTask*, 0, SystemAllocPolicy> IonCompileTaskPtrVector;
     typedef EnumeratedArray<Trap, Trap::Limit, ProfilingOffsets> TrapExitOffsetArray;
 
     // Constant parameters
     bool                            alwaysBaseline_;
 
     // Data that is moved into the result of finish()
+    Assumptions                     assumptions_;
     LinkData                        linkData_;
     MutableMetadata                 metadata_;
     ExportVector                    exports_;
     ImportVector                    imports_;
     DataSegmentVector               dataSegments_;
     ElemSegmentVector               elemSegments_;
 
     // Data scoped to the ModuleGenerator's lifetime
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -255,43 +255,64 @@ ElemSegment::sizeOfExcludingThis(MallocS
 {
     return elemFuncIndices.sizeOfExcludingThis(mallocSizeOf) +
            elemCodeRangeIndices.sizeOfExcludingThis(mallocSizeOf);
 }
 
 size_t
 Module::serializedSize() const
 {
-    return SerializedPodVectorSize(code_) +
+    return assumptions_.serializedSize() +
+           SerializedPodVectorSize(code_) +
            linkData_.serializedSize() +
            SerializedVectorSize(imports_) +
            SerializedVectorSize(exports_) +
            SerializedPodVectorSize(dataSegments_) +
            SerializedVectorSize(elemSegments_) +
            metadata_->serializedSize() +
            SerializedPodVectorSize(bytecode_->bytes);
 }
 
 uint8_t*
 Module::serialize(uint8_t* cursor) const
 {
+    // Assumption must be serialized at the beginning so that assumptionsMatch
+    // can detect a build-id mismatch before any other decoding occurs.
+
+    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;
 }
 
+/* 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)
 {
+    Assumptions assumptions;
+    cursor = assumptions.deserialize(cursor);
+    if (!cursor)
+        return nullptr;
+
     Bytes code;
     cursor = DeserializePodVector(cursor, &code);
     if (!cursor)
         return nullptr;
 
     LinkData linkData;
     cursor = linkData.deserialize(cursor);
     if (!cursor)
@@ -332,17 +353,18 @@ Module::deserialize(const uint8_t* curso
 
     MutableBytes bytecode = js_new<ShareableBytes>();
     if (!bytecode)
         return nullptr;
     cursor = DeserializePodVector(cursor, &bytecode->bytes);
     if (!cursor)
         return nullptr;
 
-    *module = js_new<Module>(Move(code),
+    *module = js_new<Module>(Move(assumptions),
+                             Move(code),
                              Move(linkData),
                              Move(imports),
                              Move(exports),
                              Move(dataSegments),
                              Move(elemSegments),
                              *metadata,
                              *bytecode);
     if (!*module)
@@ -354,16 +376,17 @@ Module::deserialize(const uint8_t* curso
 /* virtual */ void
 Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
                       Metadata::SeenSet* seenMetadata,
                       ShareableBytes::SeenSet* seenBytes,
                       size_t* code,
                       size_t* data) const
 {
     *data += mallocSizeOf(this) +
+             assumptions_.sizeOfExcludingThis(mallocSizeOf) +
              code_.sizeOfExcludingThis(mallocSizeOf) +
              linkData_.sizeOfExcludingThis(mallocSizeOf) +
              SizeOfVectorExcludingThis(imports_, mallocSizeOf) +
              SizeOfVectorExcludingThis(exports_, mallocSizeOf) +
              dataSegments_.sizeOfExcludingThis(mallocSizeOf) +
              SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) +
              metadata_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenMetadata) +
              bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -172,16 +172,17 @@ typedef Vector<ElemSegment, 0, SystemAll
 // Since fully linked-and-instantiated code (represented by CodeSegment) cannot
 // be shared between instances, Module stores an unlinked, uninstantiated copy
 // of the code (represented by the Bytes) and creates a new CodeSegment each
 // time it is instantiated. In the future, Module will store a shareable,
 // immutable CodeSegment that can be shared by all its instances.
 
 class Module : public RefCounted<Module>
 {
+    const Assumptions       assumptions_;
     const Bytes             code_;
     const LinkData          linkData_;
     const ImportVector      imports_;
     const ExportVector      exports_;
     const DataSegmentVector dataSegments_;
     const ElemSegmentVector elemSegments_;
     const SharedMetadata    metadata_;
     const SharedBytes       bytecode_;
@@ -193,25 +194,27 @@ class Module : public RefCounted<Module>
                           SharedTableVector* tables) const;
     bool initSegments(JSContext* cx,
                       HandleWasmInstanceObject instance,
                       Handle<FunctionVector> funcImports,
                       HandleWasmMemoryObject memory,
                       const ValVector& globalImports) const;
 
   public:
-    Module(Bytes&& code,
+    Module(Assumptions&& assumptions,
+           Bytes&& code,
            LinkData&& linkData,
            ImportVector&& imports,
            ExportVector&& exports,
            DataSegmentVector&& dataSegments,
            ElemSegmentVector&& elemSegments,
            const Metadata& metadata,
            const ShareableBytes& bytecode)
-      : code_(Move(code)),
+      : assumptions_(Move(assumptions)),
+        code_(Move(code)),
         linkData_(Move(linkData)),
         imports_(Move(imports)),
         exports_(Move(exports)),
         dataSegments_(Move(dataSegments)),
         elemSegments_(Move(elemSegments)),
         metadata_(&metadata),
         bytecode_(&bytecode)
     {}
@@ -228,16 +231,17 @@ class Module : public RefCounted<Module>
                      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,
                                       Metadata* maybeMetadata = nullptr);
 
     // about:memory reporting:
 
     void addSizeOfMisc(MallocSizeOf mallocSizeOf,
                        Metadata::SeenSet* seenMetadata,
                        ShareableBytes::SeenSet* seenBytes,
--- a/js/src/asmjs/WasmSerialize.h
+++ b/js/src/asmjs/WasmSerialize.h
@@ -110,16 +110,19 @@ SerializedPodVectorSize(const mozilla::V
     return sizeof(uint32_t) +
            vec.length() * sizeof(T);
 }
 
 template <class T, size_t N>
 static inline uint8_t*
 SerializePodVector(uint8_t* cursor, const mozilla::Vector<T, N, SystemAllocPolicy>& vec)
 {
+    // This binary format must not change without taking into consideration the
+    // constraints in Assumptions::serialize.
+
     cursor = WriteScalar<uint32_t>(cursor, vec.length());
     cursor = WriteBytes(cursor, vec.begin(), vec.length() * sizeof(T));
     return cursor;
 }
 
 template <class T, size_t N>
 static inline const uint8_t*
 DeserializePodVector(const uint8_t* cursor, mozilla::Vector<T, N, SystemAllocPolicy>* vec)
--- a/js/src/asmjs/WasmTypes.cpp
+++ b/js/src/asmjs/WasmTypes.cpp
@@ -609,16 +609,20 @@ Assumptions::serializedSize() const
 {
     return sizeof(uint32_t) +
            SerializedPodVectorSize(buildId);
 }
 
 uint8_t*
 Assumptions::serialize(uint8_t* cursor) const
 {
+    // The format of serialized Assumptions must never change in a way that
+    // would cause old cache files written with by an old build-id to match the
+    // assumptions of a different build-id.
+
     cursor = WriteScalar<uint32_t>(cursor, cpuId);
     cursor = SerializePodVector(cursor, buildId);
     return cursor;
 }
 
 const uint8_t*
 Assumptions::deserialize(const uint8_t* cursor)
 {