Bug 1253137 - Baldr: switch local array to local entry array (r=sunfish)
authorLuke Wagner <luke@mozilla.com>
Sun, 06 Mar 2016 17:46:22 -0600
changeset 325237 57e9d3626218d7adf9463db51ad9aae8c44cd709
parent 325236 f1ff5388e591a53187620f03de8278c496442a05
child 325238 5eabc3a7368a09eb317dabd86b598e4f32ff6088
push id1128
push userjlund@mozilla.com
push dateWed, 01 Jun 2016 01:31:59 +0000
treeherdermozilla-release@fe0d30de989d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssunfish
bugs1253137
milestone47.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 1253137 - Baldr: switch local array to local entry array (r=sunfish) MozReview-Commit-ID: HUdKHzTuLqo
js/src/asmjs/AsmJS.cpp
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmBinary.cpp
js/src/asmjs/WasmBinary.h
js/src/asmjs/WasmGenerator.cpp
js/src/asmjs/WasmIonCompile.cpp
js/src/asmjs/WasmModule.cpp
js/src/asmjs/WasmModule.h
js/src/asmjs/WasmText.cpp
js/src/asmjs/WasmTypes.h
js/src/gc/StoreBuffer.h
js/src/jit-test/tests/wasm/binary.js
js/src/moz.build
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -3527,23 +3527,18 @@ CheckVariables(FunctionValidator& f, Par
         for (ParseNode* var = VarListHead(stmt); var; var = NextNode(var)) {
             if (!CheckVariable(f, var, &types, &inits))
                 return false;
         }
     }
 
     MOZ_ASSERT(f.encoder().empty());
 
-    if (!f.encoder().writeVarU32(types.length()))
-        return false;
-
-    for (ValType v : types) {
-        if (!f.encoder().writeValType(v))
-            return false;
-    }
+    if (!EncodeLocalEntries(f.encoder(), types))
+        return false;
 
     for (uint32_t i = 0; i < inits.length(); i++) {
         NumLit lit = inits[i];
         if (lit.isZeroBits())
             continue;
         if (!f.encoder().writeExpr(Expr::SetLocal))
             return false;
         if (!f.encoder().writeVarU32(firstVar + i))
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -139,68 +139,46 @@ static bool
 CheckType(FunctionDecoder& f, ExprType actual, ExprType expected)
 {
     MOZ_ASSERT(expected != AnyType);
     return expected == ExprType::Void ||
            CheckType(f, actual, NonVoidToValType(expected));
 }
 
 static bool
-DecodeExpr(FunctionDecoder& f, ExprType* type);
-
-static bool
-DecodeValType(JSContext* cx, Decoder& d, ValType *type)
+CheckValType(JSContext* cx, Decoder& d, ValType type)
 {
-    if (!d.readValType(type))
-        return Fail(cx, d, "bad value type");
-
-    switch (*type) {
+    switch (type) {
       case ValType::I32:
       case ValType::F32:
       case ValType::F64:
         return true;
       case ValType::I64:
 #ifndef JS_CPU_X64
         return Fail(cx, d, "i64 NYI on this platform");
 #endif
         return true;
       default:
         // Note: it's important not to remove this default since readValType()
         // can return ValType values for which there is no enumerator.
         break;
     }
 
-    return Fail(cx, "bad value type");
+    return Fail(cx, d, "bad value type");
 }
 
 static bool
-DecodeExprType(JSContext* cx, Decoder& d, ExprType *type)
+CheckExprType(JSContext* cx, Decoder& d, ExprType type)
 {
-    if (!d.readExprType(type))
-        return Fail(cx, d, "bad expression type");
+    return type == ExprType::Void ||
+           CheckValType(cx, d, NonVoidToValType(type));
+}
 
-    switch (*type) {
-      case ExprType::I32:
-      case ExprType::F32:
-      case ExprType::F64:
-      case ExprType::Void:
-        return true;
-      case ExprType::I64:
-#ifndef JS_CPU_X64
-        return Fail(cx, d, "i64 NYI on this platform");
-#endif
-        return true;
-      default:
-        // Note: it's important not to remove this default since readExprType()
-        // can return ExprType values for which there is no enumerator.
-        break;
-    }
-
-    return Fail(cx, "bad expression type");
-}
+static bool
+DecodeExpr(FunctionDecoder& f, ExprType* type);
 
 static bool
 DecodeNop(FunctionDecoder& f, ExprType* type)
 {
     *type = ExprType::Void;
     return true;
 }
 
@@ -907,25 +885,31 @@ DecodeSignatures(JSContext* cx, Decoder&
         uint32_t numArgs;
         if (!d.readVarU32(&numArgs))
             return Fail(cx, d, "bad number of signature args");
 
         if (numArgs > MaxArgsPerFunc)
             return Fail(cx, d, "too many arguments in signature");
 
         ExprType result;
-        if (!DecodeExprType(cx, d, &result))
+        if (!d.readExprType(&result))
+            return Fail(cx, d, "bad expression type");
+
+        if (!CheckExprType(cx, d, result))
             return false;
 
         ValTypeVector args;
         if (!args.resize(numArgs))
             return false;
 
         for (uint32_t i = 0; i < numArgs; i++) {
-            if (!DecodeValType(cx, d, &args[i]))
+            if (!d.readValType(&args[i]))
+                return Fail(cx, d, "bad value type");
+
+            if (!CheckValType(cx, d, args[i]))
                 return false;
         }
 
         init->sigs[sigIndex] = Sig(Move(args), result);
 
         SigSet::AddPtr p = dupSet.lookupForAdd(init->sigs[sigIndex]);
         if (p)
             return Fail(cx, d, "duplicate signature");
@@ -1253,25 +1237,21 @@ DecodeFunctionBody(JSContext* cx, Decode
     FunctionGenerator fg;
     if (!mg.startFuncDef(d.currentOffset(), &fg))
         return false;
 
     ValTypeVector locals;
     if (!locals.appendAll(mg.funcSig(funcIndex).args()))
         return false;
 
-    uint32_t numVars;
-    if (!d.readVarU32(&numVars))
-        return Fail(cx, d, "expected number of local vars");
+    if (!DecodeLocalEntries(d, &locals))
+        return Fail(cx, d, "failed decoding local entries");
 
-    for (uint32_t i = 0; i < numVars; i++) {
-        ValType type;
-        if (!DecodeValType(cx, d, &type))
-            return false;
-        if (!locals.append(type))
+    for (ValType type : locals) {
+        if (!CheckValType(cx, d, type))
             return false;
     }
 
     FunctionDecoder f(cx, d, mg, fg, funcIndex, locals);
 
     ExprType type = ExprType::Void;
 
     while (d.currentPosition() < bodyEnd) {
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmBinary.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "asmjs/WasmBinary.h"
+
+#include "asmjs/WasmTypes.h"
+
+using namespace js;
+using namespace js::wasm;
+
+bool
+wasm::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals)
+{
+    uint32_t numLocalEntries = 0;
+    ValType prev = ValType::Limit;
+    for (ValType t : locals) {
+        if (t != prev) {
+            numLocalEntries++;
+            prev = t;
+        }
+    }
+
+    if (!e.writeVarU32(numLocalEntries))
+        return false;
+
+    if (numLocalEntries) {
+        prev = locals[0];
+        uint32_t count = 1;
+        for (uint32_t i = 1; i < locals.length(); i++, count++) {
+            if (prev != locals[i]) {
+                if (!e.writeVarU32(count))
+                    return false;
+                if (!e.writeValType(prev))
+                    return false;
+                prev = locals[i];
+                count = 0;
+            }
+        }
+        if (!e.writeVarU32(count))
+            return false;
+        if (!e.writeValType(prev))
+            return false;
+    }
+
+    return true;
+}
+
+bool
+wasm::DecodeLocalEntries(Decoder& d, ValTypeVector* locals)
+{
+    uint32_t numLocalEntries;
+    if (!d.readVarU32(&numLocalEntries))
+        return false;
+
+    for (uint32_t i = 0; i < numLocalEntries; i++) {
+        uint32_t count;
+        if (!d.readVarU32(&count))
+            return false;
+
+        if (MaxLocals - locals->length() < count)
+            return false;
+
+        ValType type;
+        if (!d.readValType(&type))
+            return false;
+
+        if (!locals->appendN(type, count))
+            return false;
+    }
+
+    return true;
+}
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -793,12 +793,23 @@ class Decoder
     MOZ_WARN_UNUSED_RESULT bool readFixedI32(int32_t* i = nullptr) {
         return read<int32_t>(i);
     }
     uint8_t uncheckedReadFixedU8() {
         return uncheckedRead<uint8_t>();
     }
 };
 
+// Reusable macro encoding/decoding functions reused by both the two
+// encoders (AsmJS/WasmText) and decoders (Wasm/WasmIonCompile).
+
+typedef Vector<ValType, 8, SystemAllocPolicy> ValTypeVector;
+
+bool
+EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
+
+bool
+DecodeLocalEntries(Decoder& d, ValTypeVector* locals);
+
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_binary_h
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -720,17 +720,17 @@ ModuleGenerator::numExports() const
 {
     return module_->exports.length();
 }
 
 bool
 ModuleGenerator::addMemoryExport(UniqueChars fieldName)
 {
     return exportMap_->fieldNames.append(Move(fieldName)) &&
-           exportMap_->fieldsToExports.append(ExportMap::MemoryExport);
+           exportMap_->fieldsToExports.append(MemoryExport);
 }
 
 bool
 ModuleGenerator::startFuncDefs()
 {
     MOZ_ASSERT(!startedFuncDefs());
     threadView_ = MakeUnique<ModuleGeneratorThreadView>(*shared_);
     if (!threadView_)
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -3046,29 +3046,25 @@ EmitExpr(FunctionCompiler& f, MDefinitio
 bool
 wasm::IonCompileFunction(IonCompileTask* task)
 {
     int64_t before = PRMJ_Now();
 
     const FuncBytecode& func = task->func();
     FuncCompileResults& results = task->results();
 
-    // Read in the variable types to build the local types vector.
-
     Decoder d(func.bytecode());
 
+    // Build the local types vector.
+
     ValTypeVector locals;
     if (!locals.appendAll(func.sig().args()))
         return false;
-
-    uint32_t numVars = d.uncheckedReadVarU32();
-    for (uint32_t i = 0; i < numVars; i++) {
-        if (!locals.append(d.uncheckedReadValType()))
-            return false;
-    }
+    if (!DecodeLocalEntries(d, &locals))
+        return false;
 
     // Set up for Ion compilation.
 
     JitContext jitContext(CompileRuntime::get(task->runtime()), &results.alloc());
     const JitCompileOptions options;
     MIRGraph graph(&results.alloc());
     CompileInfo compileInfo(locals.length());
     MIRGenerator mir(nullptr, options, &results.alloc(), &graph, &compileInfo,
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -49,18 +49,16 @@ using namespace js::jit;
 using namespace js::wasm;
 using mozilla::BinarySearch;
 using mozilla::MakeEnumeratedRange;
 using mozilla::PodCopy;
 using mozilla::PodZero;
 using mozilla::Swap;
 using JS::GenericNaN;
 
-const uint32_t ExportMap::MemoryExport;
-
 UniqueCodePtr
 wasm::AllocateCode(ExclusiveContext* cx, size_t bytes)
 {
     // On most platforms, this will allocate RWX memory. On iOS, or when
     // --non-writable-jitcode is used, this will allocate RW memory. In this
     // case, DynamicallyLinkModule will reprotect the code as RX.
     unsigned permissions =
         ExecutableAllocator::initialProtectionFlags(ExecutableAllocator::Writable);
@@ -1125,17 +1123,17 @@ CreateExportObject(JSContext* cx,
     MOZ_ASSERT(exportMap.exportFuncIndices.length() == exports.length());
     MOZ_ASSERT(exportMap.fieldNames.length() == exportMap.fieldsToExports.length());
 
     for (size_t fieldIndex = 0; fieldIndex < exportMap.fieldNames.length(); fieldIndex++) {
         const char* fieldName = exportMap.fieldNames[fieldIndex].get();
         if (!*fieldName) {
             MOZ_ASSERT(!exportObj);
             uint32_t exportIndex = exportMap.fieldsToExports[fieldIndex];
-            if (exportIndex == ExportMap::MemoryExport) {
+            if (exportIndex == MemoryExport) {
                 MOZ_ASSERT(heap);
                 exportObj.set(heap);
             } else {
                 exportObj.set(NewExportedFunction(cx, moduleObj, exportMap, exportIndex));
                 if (!exportObj)
                     return false;
             }
             break;
@@ -1162,17 +1160,17 @@ CreateExportObject(JSContext* cx,
 
         JSAtom* atom = AtomizeUTF8Chars(cx, fieldName, strlen(fieldName));
         if (!atom)
             return false;
 
         RootedId id(cx, AtomToId(atom));
         RootedValue val(cx);
         uint32_t exportIndex = exportMap.fieldsToExports[fieldIndex];
-        if (exportIndex == ExportMap::MemoryExport)
+        if (exportIndex == MemoryExport)
             val = ObjectValue(*heap);
         else
             val = vals[exportIndex];
 
         if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
             return false;
     }
 
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -331,20 +331,20 @@ typedef Vector<CacheableChars, 0, System
 // The 'fieldNames' vector provides the list of names of the module's exports.
 // For each field in fieldNames, 'fieldsToExports' provides either:
 //  - the sentinel value MemoryExport indicating an export of linear memory; or
 //  - the index of an export (both into the module's ExportVector and the
 //    ExportMap's exportFuncIndices vector).
 // Lastly, the 'exportFuncIndices' vector provides, for each exported function,
 // the internal index of the function.
 
+static const uint32_t MemoryExport = UINT32_MAX;
+
 struct ExportMap
 {
-    static const uint32_t MemoryExport = UINT32_MAX;
-
     CacheableCharsVector fieldNames;
     Uint32Vector fieldsToExports;
     Uint32Vector exportFuncIndices;
 
     WASM_DECLARE_SERIALIZABLE(ExportMap)
 };
 
 typedef UniquePtr<ExportMap> UniqueExportMap;
--- a/js/src/asmjs/WasmText.cpp
+++ b/js/src/asmjs/WasmText.cpp
@@ -3932,23 +3932,21 @@ static bool
 EncodeFunctionBody(Encoder& e, WasmAstFunc& func)
 {
     size_t bodySizeAt;
     if (!e.writePatchableVarU32(&bodySizeAt))
         return false;
 
     size_t beforeBody = e.currentOffset();
 
-    if (!e.writeVarU32(func.vars().length()))
+    ValTypeVector varTypes;
+    if (!varTypes.appendAll(func.vars()))
         return false;
-
-    for (ValType type : func.vars()) {
-        if (!e.writeValType(type))
-            return false;
-    }
+    if (!EncodeLocalEntries(e, varTypes))
+        return false;
 
     for (WasmAstExpr* expr : func.body()) {
         if (!EncodeExpr(e, *expr))
             return false;
     }
 
     e.patchVarU32(bodySizeAt, e.currentOffset() - beforeBody);
     return true;
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -38,17 +38,16 @@ class PropertyName;
 
 namespace wasm {
 
 using mozilla::EnumeratedArray;
 using mozilla::Move;
 using mozilla::MallocSizeOf;
 
 typedef Vector<uint32_t, 0, SystemAllocPolicy> Uint32Vector;
-typedef Vector<ValType, 8, SystemAllocPolicy> ValTypeVector;
 
 // ValType/ExprType utilities
 
 static inline bool
 IsVoid(ExprType et)
 {
     return et == ExprType::Void;
 }
@@ -591,16 +590,17 @@ enum ModuleKind
 static const unsigned ActivationGlobalDataOffset = 0;
 static const unsigned HeapGlobalDataOffset       = ActivationGlobalDataOffset + sizeof(void*);
 static const unsigned NaN64GlobalDataOffset      = HeapGlobalDataOffset + sizeof(void*);
 static const unsigned NaN32GlobalDataOffset      = NaN64GlobalDataOffset + sizeof(double);
 static const unsigned InitialGlobalDataBytes     = NaN32GlobalDataOffset + sizeof(float);
 
 static const unsigned MaxSigs                    =        4 * 1024;
 static const unsigned MaxFuncs                   =      512 * 1024;
+static const unsigned MaxLocals                  =       64 * 1024;
 static const unsigned MaxImports                 =       64 * 1024;
 static const unsigned MaxExports                 =       64 * 1024;
 static const unsigned MaxTableElems              =      128 * 1024;
 static const unsigned MaxArgsPerFunc             =        4 * 1024;
 static const unsigned MaxBrTableElems            = 4 * 1024 * 1024;
 
 } // namespace wasm
 } // namespace js
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -309,18 +309,18 @@ class StoreBuffer
                    (start <= otherEnd && otherEnd <= end);
         }
 
         // Destructively make this SlotsEdge range the union of the other
         // SlotsEdge range and this one. A precondition is that the ranges must
         // overlap.
         void merge(const SlotsEdge& other) {
             MOZ_ASSERT(overlaps(other));
-            auto end = std::max(start_ + count_, other.start_ + other.count_);
-            start_ = std::min(start_, other.start_);
+            auto end = Max(start_ + count_, other.start_ + other.count_);
+            start_ = Min(start_, other.start_);
             count_ = end - start_;
         }
 
         bool maybeInRememberedSet(const Nursery& n) const {
             return !IsInsideNursery(reinterpret_cast<Cell*>(object()));
         }
 
         void trace(TenuringTracer& mover) const;
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -158,18 +158,18 @@ assertErrorMessage(() => wasmEval(module
 assertThrowsInstanceOf(() => wasmEval(moduleWithSections([{name: sigId, body: [1]}])), TypeError);
 assertThrowsInstanceOf(() => wasmEval(moduleWithSections([{name: sigId, body: [1, 1, 0]}])), TypeError);
 
 wasmEval(moduleWithSections([sigSection([])]));
 wasmEval(moduleWithSections([sigSection([v2vSig])]));
 wasmEval(moduleWithSections([sigSection([i2vSig])]));
 wasmEval(moduleWithSections([sigSection([v2vSig, i2vSig])]));
 
-assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([{args:[], ret:100}])])), TypeError, /bad expression type/);
-assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([{args:[100], ret:VoidCode}])])), TypeError, /bad value type/);
+assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([{args:[], ret:100}])])), TypeError, /expression type/);
+assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([{args:[100], ret:VoidCode}])])), TypeError, /value type/);
 
 assertThrowsInstanceOf(() => wasmEval(moduleWithSections([sigSection([]), declSection([0])])), TypeError, /signature index out of range/);
 assertThrowsInstanceOf(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([1])])), TypeError, /signature index out of range/);
 assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0])])), TypeError, /expected function bodies/);
 wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([v2vBody])]));
 
 assertThrowsInstanceOf(() => wasmEval(moduleWithSections([sigSection([v2vSig]), {name: importId, body:[]}])), TypeError);
 assertErrorMessage(() => wasmEval(moduleWithSections([importSection([{sigIndex:0, module:"a", func:"b"}])])), TypeError, /signature index out of range/);
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -142,16 +142,17 @@ EXPORTS.js += [
     '../public/Value.h',
     '../public/Vector.h',
     '../public/WeakMapPtr.h',
 ]
 
 UNIFIED_SOURCES += [
     'asmjs/AsmJS.cpp',
     'asmjs/Wasm.cpp',
+    'asmjs/WasmBinary.cpp',
     'asmjs/WasmFrameIterator.cpp',
     'asmjs/WasmGenerator.cpp',
     'asmjs/WasmIonCompile.cpp',
     'asmjs/WasmModule.cpp',
     'asmjs/WasmSignalHandlers.cpp',
     'asmjs/WasmStubs.cpp',
     'asmjs/WasmText.cpp',
     'asmjs/WasmTypes.cpp',