Bug 1253137 - Baldr: update section header structure to match BinaryEncoding.md, part 1 (r=sunfish)
authorLuke Wagner <luke@mozilla.com>
Wed, 02 Mar 2016 21:48:05 -0600
changeset 323023 56a5e1a497815ea0248d032fb388acdf8444ab43
parent 323022 53cb9d108ec54eb82a1ba98dee11f27b125973ed
child 323024 6257b3a68cfdc254619da53f56cbb8249b2b9bcf
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [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: update section header structure to match BinaryEncoding.md, part 1 (r=sunfish) MozReview-Commit-ID: 41Yhj7esXsj
js/src/asmjs/Wasm.cpp
js/src/asmjs/WasmBinary.h
js/src/asmjs/WasmText.cpp
js/src/jit-test/tests/wasm/binary.js
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -619,50 +619,16 @@ DecodeExpr(FunctionDecoder& f, ExprType 
         return DecodeReturn(f);
       default:
         break;
     }
 
     return f.fail("bad expression code");
 }
 
-static bool
-DecodeFuncBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, FunctionGenerator& fg,
-               uint32_t funcIndex)
-{
-    const uint8_t* bodyBegin = d.currentPosition();
-
-    FunctionDecoder f(cx, d, mg, fg, funcIndex);
-
-    uint32_t numExprs;
-    if (!d.readVarU32(&numExprs))
-        return Fail(cx, d, "expected number of function body expressions");
-
-    if (numExprs) {
-        for (size_t i = 0; i < numExprs - 1; i++) {
-            if (!DecodeExpr(f, ExprType::Void))
-                return false;
-        }
-
-        if (!DecodeExpr(f, f.ret()))
-            return false;
-    } else {
-        if (!CheckType(f, ExprType::Void, f.ret()))
-            return false;
-    }
-
-    const uint8_t* bodyEnd = d.currentPosition();
-    uintptr_t bodyLength = bodyEnd - bodyBegin;
-    if (!fg.bytecode().resize(bodyLength))
-        return false;
-
-    memcpy(fg.bytecode().begin(), bodyBegin, bodyLength);
-    return true;
-}
-
 /*****************************************************************************/
 // dynamic link data
 
 struct ImportName
 {
     UniqueChars module;
     UniqueChars func;
 
@@ -679,23 +645,22 @@ typedef Vector<ImportName, 0, SystemAllo
 /*****************************************************************************/
 // wasm decoding and generation
 
 typedef HashSet<const DeclaredSig*, SigHashPolicy> SigSet;
 
 static bool
 DecodeSignatureSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
 {
-    if (!d.readCStringIf(SigLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(SigLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected signature section byte size");
-
     uint32_t numSigs;
     if (!d.readVarU32(&numSigs))
         return Fail(cx, d, "expected number of signatures");
 
     if (numSigs > MaxSigs)
         return Fail(cx, d, "too many signatures");
 
     if (!init->sigs.resize(numSigs))
@@ -757,23 +722,22 @@ DecodeSignatureIndex(JSContext* cx, Deco
 
     *sig = &init.sigs[sigIndex];
     return true;
 }
 
 static bool
 DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
 {
-    if (!d.readCStringIf(DeclLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(DeclLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected decl section byte size");
-
     uint32_t numDecls;
     if (!d.readVarU32(&numDecls))
         return Fail(cx, d, "expected number of declarations");
 
     if (numDecls > MaxFuncs)
         return Fail(cx, d, "too many functions");
 
     if (!init->funcSigs.resize(numDecls))
@@ -788,23 +752,22 @@ DecodeDeclarationSection(JSContext* cx, 
         return Fail(cx, d, "decls section byte size mismatch");
 
     return true;
 }
 
 static bool
 DecodeTableSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
 {
-    if (!d.readCStringIf(TableLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(TableLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected table section byte size");
-
     if (!d.readVarU32(&init->numTableElems))
         return Fail(cx, d, "expected number of table elems");
 
     if (init->numTableElems > MaxTableElems)
         return Fail(cx, d, "too many table elements");
 
     Uint32Vector elems;
     if (!elems.resize(init->numTableElems))
@@ -889,23 +852,22 @@ DecodeImport(JSContext* cx, Decoder& d, 
         return Fail(cx, d, "expected import func name");
 
     return importNames->emplaceBack(Move(moduleName), Move(funcName));
 }
 
 static bool
 DecodeImportSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames)
 {
-    if (!d.readCStringIf(ImportLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(ImportLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected import section byte size");
-
     for (uint32_t i = 0; !d.readCStringIf(EndLabel); i++) {
         if (i >= MaxImports)
             return Fail(cx, d, "too many imports");
 
         if (!d.readCStringIf(FuncLabel))
             return Fail(cx, d, "expected 'func' import subsection");
 
         if (!DecodeImport(cx, d, init, importNames))
@@ -917,22 +879,22 @@ DecodeImportSection(JSContext* cx, Decod
 
     return true;
 }
 
 static bool
 DecodeMemorySection(JSContext* cx, Decoder& d, ModuleGenerator& mg,
                     MutableHandle<ArrayBufferObject*> heap)
 {
-    if (!d.readCStringIf(MemoryLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(MemoryLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected memory section byte size");
 
     if (!d.readCStringIf(InitialLabel))
         return Fail(cx, d, "expected memory section initial field");
 
     uint32_t initialHeapSize;
     if (!d.readVarU32(&initialHeapSize))
         return Fail(cx, d, "expected initial memory size");
 
@@ -1011,23 +973,22 @@ DecodeMemoryExport(JSContext* cx, Decode
         return false;
 
     return mg.addMemoryExport(Move(fieldName));
 }
 
 static bool
 DecodeExportsSection(JSContext* cx, Decoder& d, ModuleGenerator& mg)
 {
-    if (!d.readCStringIf(ExportLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(ExportLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected export section byte size");
-
     CStringSet dupSet(cx);
     if (!dupSet.init())
         return false;
 
     for (uint32_t i = 0; !d.readCStringIf(EndLabel); i++) {
         if (i >= MaxExports)
             return Fail(cx, d, "too many exports");
 
@@ -1044,28 +1005,24 @@ DecodeExportsSection(JSContext* cx, Deco
 
     if (!d.finishSection(sectionStart))
         return Fail(cx, d, "export section byte size mismatch");
 
     return true;
 }
 
 static bool
-DecodeFunctionSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
+DecodeFunctionBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
 {
     int64_t before = PRMJ_Now();
 
     FunctionGenerator fg;
     if (!mg.startFuncDef(d.currentOffset(), &fg))
         return false;
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected func section byte size");
-
     const DeclaredSig& sig = mg.funcSig(funcIndex);
     for (ValType type : sig.args()) {
         if (!fg.addLocal(type))
             return false;
     }
 
     uint32_t numVars;
     if (!d.readVarU32(&numVars))
@@ -1074,66 +1031,90 @@ DecodeFunctionSection(JSContext* cx, Dec
     for (uint32_t i = 0; i < numVars; i++) {
         ValType type;
         if (!DecodeValType(cx, d, &type))
             return false;
         if (!fg.addLocal(type))
             return false;
     }
 
-    if (!DecodeFuncBody(cx, d, mg, fg, funcIndex))
+    const uint8_t* bodyBegin = d.currentPosition();
+
+    FunctionDecoder f(cx, d, mg, fg, funcIndex);
+
+    uint32_t numExprs;
+    if (!d.readVarU32(&numExprs))
+        return Fail(cx, d, "expected number of function body expressions");
+
+    if (numExprs) {
+        for (size_t i = 0; i < numExprs - 1; i++) {
+            if (!DecodeExpr(f, ExprType::Void))
+                return false;
+        }
+
+        if (!DecodeExpr(f, f.ret()))
+            return false;
+    } else {
+        if (!CheckType(f, ExprType::Void, f.ret()))
+            return false;
+    }
+
+    const uint8_t* bodyEnd = d.currentPosition();
+    uintptr_t bodyLength = bodyEnd - bodyBegin;
+    if (!fg.bytecode().resize(bodyLength))
         return false;
 
-    if (!d.finishSection(sectionStart))
-        return Fail(cx, d, "func section byte size mismatch");
+    memcpy(fg.bytecode().begin(), bodyBegin, bodyLength);
 
     int64_t after = PRMJ_Now();
     unsigned generateTime = (after - before) / PRMJ_USEC_PER_MSEC;
 
     return mg.finishFuncDef(funcIndex, generateTime, &fg);
 }
 
 static bool
-DecodeFunctionSections(JSContext* cx, Decoder& d, ModuleGenerator& mg)
+DecodeFunctionBodiesSection(JSContext* cx, Decoder& d, ModuleGenerator& mg)
 {
     if (!mg.startFuncDefs())
         return false;
 
-    uint32_t funcIndex = 0;
+    uint32_t sectionStart;
+    if (!d.startSection(FuncLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
 
-    for (; d.readCStringIf(FuncLabel); funcIndex++) {
-        if (funcIndex >= mg.numFuncSigs())
-            return Fail(cx, d, "more function definitions than declarations");
+    if (sectionStart == Decoder::NotStarted) {
+        if (mg.numFuncSigs() != 0)
+            return Fail(cx, d, "expected function bodies");
 
-        if (!DecodeFunctionSection(cx, d, mg, funcIndex))
+        return mg.finishFuncDefs();
+    }
+
+    for (uint32_t funcIndex = 0; funcIndex < mg.numFuncSigs(); funcIndex++) {
+        if (!DecodeFunctionBody(cx, d, mg, funcIndex))
             return false;
     }
 
-    if (funcIndex < mg.numFuncSigs())
-        return Fail(cx, d, "fewer function definitions than declarations");
+    if (!d.finishSection(sectionStart))
+        return Fail(cx, d, "function section byte size mismatch");
 
-    if (!mg.finishFuncDefs())
-        return false;
-
-    return true;
+    return mg.finishFuncDefs();
 }
 
 static bool
 DecodeDataSection(JSContext* cx, Decoder& d, Handle<ArrayBufferObject*> heap)
 {
-    if (!d.readCStringIf(DataLabel))
+    uint32_t sectionStart;
+    if (!d.startSection(DataLabel, &sectionStart))
+        return Fail(cx, d, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
         return true;
 
     if (!heap)
         return Fail(cx, d, "data section requires a memory section");
 
-    uint32_t sectionStart;
-    if (!d.startSection(&sectionStart))
-        return Fail(cx, d, "expected data section byte size");
-
     uint8_t* const heapBase = heap->dataPointer();
     uint32_t const heapLength = heap->byteLength();
     uint32_t prevEnd = 0;
 
     for (uint32_t i = 0; !d.readCStringIf(EndLabel); i++) {
         if (!d.readCStringIf(SegmentLabel))
             return Fail(cx, d, "expected segment tag");
 
@@ -1161,41 +1142,16 @@ DecodeDataSection(JSContext* cx, Decoder
 
     if (!d.finishSection(sectionStart))
         return Fail(cx, d, "data section byte size mismatch");
 
     return true;
 }
 
 static bool
-DecodeUnknownSection(JSContext* cx, Decoder& d)
-{
-    UniqueChars sectionName = d.readCString();
-    if (!sectionName)
-        return Fail(cx, d, "failed to read section name");
-
-    if (!strcmp(sectionName.get(), SigLabel) ||
-        !strcmp(sectionName.get(), ImportLabel) ||
-        !strcmp(sectionName.get(), DeclLabel) ||
-        !strcmp(sectionName.get(), TableLabel) ||
-        !strcmp(sectionName.get(), MemoryLabel) ||
-        !strcmp(sectionName.get(), ExportLabel) ||
-        !strcmp(sectionName.get(), FuncLabel) ||
-        !strcmp(sectionName.get(), DataLabel))
-    {
-        return Fail(cx, d, "known section out of order");
-    }
-
-    if (!d.skipSection())
-        return Fail(cx, d, "unable to skip unknown section");
-
-    return true;
-}
-
-static bool
 DecodeModule(JSContext* cx, UniqueChars file, const uint8_t* bytes, uint32_t length,
              ImportNameVector* importNames, UniqueExportMap* exportMap,
              MutableHandle<ArrayBufferObject*> heap, MutableHandle<WasmModuleObject*> moduleObj)
 {
     Decoder d(bytes, bytes + length);
 
     uint32_t u32;
     if (!d.readFixedU32(&u32) || u32 != MagicNumber)
@@ -1225,27 +1181,27 @@ DecodeModule(JSContext* cx, UniqueChars 
         return false;
 
     if (!DecodeMemorySection(cx, d, mg, heap))
         return false;
 
     if (!DecodeExportsSection(cx, d, mg))
         return false;
 
-    if (!DecodeFunctionSections(cx, d, mg))
+    if (!DecodeFunctionBodiesSection(cx, d, mg))
         return false;
 
     if (!DecodeDataSection(cx, d, heap))
         return false;
 
     CacheableCharsVector funcNames;
 
     while (!d.readCStringIf(EndLabel)) {
-        if (!DecodeUnknownSection(cx, d))
-            return false;
+        if (!d.skipSection())
+            return Fail(cx, d, "unable to skip unknown section");
     }
 
     if (!d.done())
         return Fail(cx, d, "failed to consume all bytes of module");
 
     UniqueModuleData module;
     UniqueStaticLinkData staticLink;
     SlowFunctionVector slowFuncs(cx);
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -454,18 +454,19 @@ class Encoder
         return bytecode_.append(bytes, numBytes);
     }
 
     // A "section" is a contiguous region of bytes that stores its own size so
     // that it may be trivially skipped without examining the contents. Sections
     // require backpatching since the size of the section is only known at the
     // end while the size's uint32 must be stored at the beginning.
 
-    MOZ_WARN_UNUSED_RESULT bool startSection(size_t* offset) {
-        return writePatchableVarU32(offset);
+    MOZ_WARN_UNUSED_RESULT bool startSection(const char* name, size_t* offset) {
+        return writePatchableVarU32(offset) &&
+               writeCString(name);
     }
     void finishSection(size_t offset) {
         return patchVarU32(offset, bytecode_.length() - offset - varU32ByteLength(offset));
     }
 
     // Patching is necessary due to the combination of a preorder encoding and a
     // single-pass algorithm that only knows the precise opcode after visiting
     // children. Switching to a postorder encoding will remove these methods:
@@ -679,20 +680,30 @@ class Decoder
         if (bytesRemain() < numBytes)
             return false;
         cur_ += numBytes;
         return true;
     }
 
     // See "section" description in Encoder.
 
-    MOZ_WARN_UNUSED_RESULT bool startSection(uint32_t* startOffset) {
-        *startOffset = currentOffset();
-        uint32_t unused;
-        return readVarU32(&unused);
+    static const uint32_t NotStarted = UINT32_MAX;
+
+    MOZ_WARN_UNUSED_RESULT bool startSection(const char* name, uint32_t* startOffset) {
+        const uint8_t* before = cur_;
+        uint32_t numBytes;
+        if (!readVarU32(&numBytes) || bytesRemain() < numBytes)
+            return false;
+        if (!readCStringIf(name)) {
+            cur_ = before;
+            *startOffset = NotStarted;
+            return true;
+        }
+        *startOffset = before - beg_;
+        return  true;
     }
     MOZ_WARN_UNUSED_RESULT bool finishSection(uint32_t startOffset) {
         uint32_t currentOffset = cur_ - beg_;
         cur_ = beg_ + startOffset;
         uint32_t numBytes = uncheckedReadVarU32();
         uint32_t afterNumBytes = cur_ - beg_;
         cur_ = beg_ + currentOffset;
         return numBytes == (currentOffset - afterNumBytes);
--- a/js/src/asmjs/WasmText.cpp
+++ b/js/src/asmjs/WasmText.cpp
@@ -3704,21 +3704,18 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr
 // wasm AST binary serialization
 
 static bool
 EncodeSignatureSection(Encoder& e, WasmAstModule& module)
 {
     if (module.sigs().empty())
         return true;
 
-    if (!e.writeCString(SigLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(SigLabel, &offset))
         return false;
 
     if (!e.writeVarU32(module.sigs().length()))
         return false;
 
     for (WasmAstSig* sig : module.sigs()) {
         if (!e.writeVarU32(sig->args().length()))
             return false;
@@ -3737,21 +3734,18 @@ EncodeSignatureSection(Encoder& e, WasmA
 }
 
 static bool
 EncodeDeclarationSection(Encoder& e, WasmAstModule& module)
 {
     if (module.funcs().empty())
         return true;
 
-    if (!e.writeCString(DeclLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(DeclLabel, &offset))
         return false;
 
     if (!e.writeVarU32(module.funcs().length()))
         return false;
 
     for (WasmAstFunc* func : module.funcs()) {
         if (!e.writeVarU32(func->sig().index()))
             return false;
@@ -3785,21 +3779,18 @@ EncodeImport(Encoder& e, WasmAstImport& 
 }
 
 static bool
 EncodeImportSection(Encoder& e, WasmAstModule& module)
 {
     if (module.imports().empty())
         return true;
 
-    if (!e.writeCString(ImportLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(ImportLabel, &offset))
         return false;
 
     for (WasmAstImport* imp : module.imports()) {
         if (!e.writeCString(FuncLabel))
             return false;
         if (!EncodeImport(e, *imp))
             return false;
     }
@@ -3812,21 +3803,18 @@ EncodeImportSection(Encoder& e, WasmAstM
 }
 
 static bool
 EncodeMemorySection(Encoder& e, WasmAstModule& module)
 {
     if (!module.maybeMemory())
         return true;
 
-    if (!e.writeCString(MemoryLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(MemoryLabel, &offset))
         return false;
 
     WasmAstMemory& memory = *module.maybeMemory();
 
     if (!e.writeCString(InitialLabel))
         return false;
 
     if (!e.writeVarU32(memory.initialSize()))
@@ -3861,21 +3849,18 @@ EncodeMemoryExport(Encoder& e, WasmAstEx
 }
 
 static bool
 EncodeExportSection(Encoder& e, WasmAstModule& module)
 {
     if (module.exports().empty())
         return true;
 
-    if (!e.writeCString(ExportLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(ExportLabel, &offset))
         return false;
 
     for (WasmAstExport* exp : module.exports()) {
         switch (exp->kind()) {
           case WasmAstExportKind::Func:
             if (!e.writeCString(FuncLabel))
                 return false;
             if (!EncodeFunctionExport(e, *exp))
@@ -3898,63 +3883,70 @@ EncodeExportSection(Encoder& e, WasmAstM
 }
 
 static bool
 EncodeTableSection(Encoder& e, WasmAstModule& module)
 {
     if (!module.maybeTable())
         return true;
 
-    if (!e.writeCString(TableLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(TableLabel, &offset))
         return false;
 
     if (!e.writeVarU32(module.maybeTable()->elems().length()))
         return false;
 
     for (WasmRef& ref : module.maybeTable()->elems()) {
         if (!e.writeVarU32(ref.index()))
             return false;
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
-EncodeFunctionSection(Encoder& e, WasmAstFunc& func)
+EncodeFunctionBody(Encoder& e, WasmAstFunc& func)
 {
-    if (!e.writeCString(FuncLabel))
-        return false;
-
-    size_t offset;
-    if (!e.startSection(&offset))
-        return false;
-
     if (!e.writeVarU32(func.vars().length()))
         return false;
 
     for (ValType type : func.vars()) {
         if (!e.writeValType(type))
             return false;
     }
 
     if (!e.writeVarU32(func.body().length()))
         return false;
 
     for (WasmAstExpr* expr : func.body()) {
         if (!EncodeExpr(e, *expr))
             return false;
     }
 
+    return true;
+}
+
+static bool
+EncodeFunctionBodiesSection(Encoder& e, WasmAstModule& module)
+{
+    if (module.funcs().empty())
+        return true;
+
+    size_t offset;
+    if (!e.startSection(FuncLabel, &offset))
+        return false;
+
+    for (WasmAstFunc* func : module.funcs()) {
+        if (!EncodeFunctionBody(e, *func))
+            return false;
+    }
+
     e.finishSection(offset);
-
     return true;
 }
 
 static bool
 EncodeDataSegment(Encoder& e, WasmAstSegment& segment)
 {
     if (!e.writeVarU32(segment.offset()))
         return false;
@@ -3985,21 +3977,18 @@ EncodeDataSegment(Encoder& e, WasmAstSeg
 static bool
 EncodeDataSection(Encoder& e, WasmAstModule& module)
 {
     if (!module.maybeMemory() || module.maybeMemory()->segments().empty())
         return true;
 
     const WasmAstSegmentVector& segments = module.maybeMemory()->segments();
 
-    if (!e.writeCString(DataLabel))
-        return false;
-
     size_t offset;
-    if (!e.startSection(&offset))
+    if (!e.startSection(DataLabel, &offset))
         return false;
 
     for (WasmAstSegment* segment : segments) {
         if (!e.writeCString(SegmentLabel))
             return false;
         if (!EncodeDataSegment(e, *segment))
             return false;
     }
@@ -4039,20 +4028,18 @@ EncodeModule(WasmAstModule& module)
         return nullptr;
 
     if (!EncodeMemorySection(e, module))
         return nullptr;
 
     if (!EncodeExportSection(e, module))
         return nullptr;
 
-    for (WasmAstFunc* func : module.funcs()) {
-        if (!EncodeFunctionSection(e, *func))
-            return nullptr;
-    }
+    if (!EncodeFunctionBodiesSection(e, module))
+        return nullptr;
 
     if (!EncodeDataSection(e, module))
         return nullptr;
 
     if (!e.writeCString(EndLabel))
         return nullptr;
 
     return Move(bytecode);
--- a/js/src/jit-test/tests/wasm/binary.js
+++ b/js/src/jit-test/tests/wasm/binary.js
@@ -19,17 +19,17 @@ const tableLabel = "table";
 const importLabel = "import";
 const exportLabel = "export";
 const funcLabel = "func";
 const dataLabel = "data";
 
 const magicError = /failed to match magic number/;
 const versionError = /failed to match binary version/;
 const extraError = /failed to consume all bytes of module/;
-const sectionError = /failed to read section name/;
+const sectionError = /failed to start section/;
 
 const I32Code = 0;
 const I64Code = 1;
 const F32Code = 2;
 const F64Code = 3;
 const I32x4Code = 4;
 const F32x4Code = 5;
 const B32x4Code = 6;
@@ -70,18 +70,18 @@ assertErrorMessage(() => wasmEval(toBuf(
 
 function cstring(name) {
     return (name + '\0').split('').map(c => c.charCodeAt(0));
 }
 
 function moduleWithSections(sectionArray) {
     var bytes = moduleHeaderThen();
     for (let section of sectionArray) {
+        bytes.push(...varU32(section.name.length + 1 + section.body.length));
         bytes.push(...cstring(section.name));
-        bytes.push(...varU32(section.body.length));
         bytes.push(...section.body);
     }
     bytes.push(0);
     return bytes;
 }
 
 function sigSection(sigs) {
     var body = [];
@@ -98,22 +98,26 @@ function sigSection(sigs) {
 function declSection(decls) {
     var body = [];
     body.push(...varU32(decls.length));
     for (let decl of decls)
         body.push(...varU32(decl));
     return { name: declLabel, body };
 }
 
-function funcSection(func) {
-    var body = [];
-    var locals = varU32(func.locals.length);
+function funcBody(func) {
+    var body = varU32(func.locals.length);
     for (let local of func.locals)
-        locals.push(...varU32(local));
-    body = body.concat(locals, func.body);
+        body.push(...varU32(local));
+    body.push(...varU32(func.body.length));
+    return body.concat(...func.body);
+}
+
+function bodySection(bodies) {
+    var body = [].concat(...bodies);
     return { name: funcLabel, body };
 }
 
 function importSection(imports) {
     var body = [];
     for (let imp of imports) {
         body.push(...cstring(funcLabel));
         body.push(...varU32(imp.sigIndex));
@@ -129,17 +133,17 @@ function tableSection(elems) {
     body.push(...varU32(elems.length));
     for (let i of elems)
         body.push(...varU32(i));
     return { name: tableLabel, body };
 }
 
 const v2vSig = {args:[], ret:VoidCode};
 const i2vSig = {args:[I32Code], ret:VoidCode};
-const v2vFunc = funcSection({locals:[], body:[0]});
+const v2vBody = funcBody({locals:[], body:[]});
 
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigLabel, body: U32MAX_LEB, } ]))), TypeError, /too many signatures/);
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: sigLabel, body: [1, ...U32MAX_LEB], } ]))), TypeError, /too many arguments in signature/);
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:VoidCode}, {args:[], ret:VoidCode}])]))), TypeError, /duplicate signature/);
 
 assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigLabel, body: [1]}]))), TypeError);
 assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigLabel, body: [1, 1, 0]}]))), TypeError);
 
@@ -148,32 +152,32 @@ wasmEval(toBuf(moduleWithSections([sigSe
 wasmEval(toBuf(moduleWithSections([sigSection([i2vSig])])));
 wasmEval(toBuf(moduleWithSections([sigSection([v2vSig, i2vSig])])));
 
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:100}])]))), TypeError, /bad expression type/);
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[100], ret:VoidCode}])]))), TypeError, /bad value type/);
 
 assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([]), declSection([0])]))), TypeError, /signature index out of range/);
 assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([1])]))), TypeError, /signature index out of range/);
-assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0])]))), TypeError, /fewer function definitions than declarations/);
-wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), v2vFunc])));
+assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0])]))), TypeError, /expected function bodies/);
+wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([v2vBody])])));
 
 assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), {name: importLabel, body:[]}]))), TypeError);
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([importSection([{sigIndex:0, module:"a", func:"b"}])]))), TypeError, /signature index out of range/);
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), importSection([{sigIndex:1, module:"a", func:"b"}])]))), TypeError, /signature index out of range/);
 wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), importSection([])])));
 wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), importSection([{sigIndex:0, module:"a", func:""}])])), {a:()=>{}});
 
 wasmEval(toBuf(moduleWithSections([
     sigSection([v2vSig]),
     importSection([{sigIndex:0, module:"a", func:""}]),
     declSection([0]),
-    v2vFunc])), {a:()=>{}});
+    bodySection([v2vBody])])), {a:()=>{}});
 
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([ {name: dataLabel, body: [], } ]))), TypeError, /data section requires a memory section/);
 
 wasmEval(toBuf(moduleWithSections([tableSection([])])));
 assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([tableSection([0])]))), TypeError, /table element out of range/);
-wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0]), v2vFunc])));
-wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,0]), v2vFunc])));
-assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,1]), v2vFunc]))), TypeError, /table element out of range/);
-wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0,0,0]), tableSection([0,1,0,2]), v2vFunc, v2vFunc, v2vFunc])));
-wasmEval(toBuf(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), tableSection([0,1,2]), v2vFunc, v2vFunc, v2vFunc])));
+wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0]), bodySection([v2vBody])])));
+wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,0]), bodySection([v2vBody])])));
+assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0]), tableSection([0,1]), bodySection([v2vBody])]))), TypeError, /table element out of range/);
+wasmEval(toBuf(moduleWithSections([sigSection([v2vSig]), declSection([0,0,0]), tableSection([0,1,0,2]), bodySection([v2vBody, v2vBody, v2vBody])])));
+wasmEval(toBuf(moduleWithSections([sigSection([v2vSig,i2vSig]), declSection([0,0,1]), tableSection([0,1,2]), bodySection([v2vBody, v2vBody, v2vBody])])));