Bug 1234985 - Odin: tweak ModuleGenerator interface for Baldr use (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Wed, 20 Jan 2016 10:26:08 -0600
changeset 280813 8be707e04f9acfaa018a7ff838c36b74b214927f
parent 280812 0f7510fa983c1804792cb4a43d341b6b4e533172
child 280814 3842b1992f25049b7230930c30d8e646ceb778ae
push id29922
push usercbook@mozilla.com
push dateThu, 21 Jan 2016 10:51:00 +0000
treeherdermozilla-central@977d78a8dd78 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1234985
milestone46.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 1234985 - Odin: tweak ModuleGenerator interface for Baldr use (r=bbouvier)
js/src/asmjs/AsmJS.cpp
js/src/asmjs/WasmBinary.h
js/src/asmjs/WasmGenerator.cpp
js/src/asmjs/WasmGenerator.h
js/src/asmjs/WasmModule.cpp
js/src/asmjs/WasmModule.h
js/src/jsapi-tests/testWasmLEB128.cpp
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -51,16 +51,17 @@ using namespace js;
 using namespace js::frontend;
 using namespace js::jit;
 using namespace js::wasm;
 
 using mozilla::Compression::LZ4;
 using mozilla::HashGeneric;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
+using mozilla::Maybe;
 using mozilla::Move;
 using mozilla::PodCopy;
 using mozilla::PodEqual;
 using mozilla::PodZero;
 using mozilla::PositiveInfinity;
 using JS::AsmJSOption;
 using JS::GenericNaN;
 
@@ -392,17 +393,17 @@ class js::AsmJSModule final : public Mod
 
     const UniqueConstStaticLinkData link_;
     const UniqueConstAsmJSModuleData module_;
 
   public:
     AsmJSModule(UniqueModuleData base,
                 UniqueStaticLinkData link,
                 UniqueAsmJSModuleData module)
-      : Module(Move(base), AsmJSBool::IsAsmJS),
+      : Module(Move(base)),
         link_(Move(link)),
         module_(Move(module))
     {}
 
     virtual void trace(JSTracer* trc) override {
         Module::trace(trc);
         module_->trace(trc);
     }
@@ -1785,17 +1786,17 @@ class MOZ_STACK_CLASS ModuleValidator
         if (!genData ||
             !genData->sigs.resize(MaxSigs) ||
             !genData->funcSigs.resize(MaxFuncs) ||
             !genData->imports.resize(MaxImports))
         {
             return false;
         }
 
-        return mg_.init(Move(genData));
+        return mg_.init(Move(genData), ModuleKind::AsmJS);
     }
 
     ExclusiveContext* cx() const             { return cx_; }
     PropertyName* moduleFunctionName() const { return moduleFunctionName_; }
     PropertyName* globalArgumentName() const { return module_->globalArgumentName; }
     PropertyName* importArgumentName() const { return module_->importArgumentName; }
     PropertyName* bufferArgumentName() const { return module_->bufferArgumentName; }
     ModuleGenerator& mg()                    { return mg_; }
@@ -2222,17 +2223,17 @@ class MOZ_STACK_CLASS ModuleValidator
         }
         return false;
     }
 
     bool startFunctionBodies() {
         return true;
     }
     bool finishFunctionBodies() {
-        return mg_.finishFuncs();
+        return mg_.finishFuncDefs();
     }
     bool finish(MutableHandle<WasmModuleObject*> moduleObj, SlowFunctionVector* slowFuncs) {
         HeapUsage heap = arrayViews_.empty()
                               ? HeapUsage::None
                               : atomicsPresent_
                                 ? HeapUsage::Shared
                                 : HeapUsage::Unshared;
 
@@ -2585,17 +2586,17 @@ class MOZ_STACK_CLASS FunctionValidator
   private:
     typedef HashMap<PropertyName*, Local> LocalMap;
     typedef HashMap<PropertyName*, uint32_t> LabelMap;
 
     ModuleValidator&  m_;
     ParseNode*        fn_;
 
     FunctionGenerator fg_;
-    Encoder           encoder_;
+    Maybe<Encoder>    encoder_;
 
     LocalMap          locals_;
     LabelMap          labels_;
 
     bool              hasAlreadyReturned_;
     ExprType          ret_;
 
   public:
@@ -2607,25 +2608,28 @@ class MOZ_STACK_CLASS FunctionValidator
         hasAlreadyReturned_(false)
     {}
 
     ModuleValidator& m() const        { return m_; }
     ExclusiveContext* cx() const      { return m_.cx(); }
     ParseNode* fn() const             { return fn_; }
 
     bool init(PropertyName* name, unsigned line, unsigned column) {
-        UniqueBytecode recycled;
-        return m_.mg().startFunc(name, line, column, &recycled, &fg_) &&
-               encoder_.init(Move(recycled)) &&
-               locals_.init() &&
-               labels_.init();
+        if (!locals_.init() || !labels_.init())
+            return false;
+
+        if (!m_.mg().startFuncDef(name, line, column, &fg_))
+            return false;
+
+        encoder_.emplace(fg_.bytecode());
+        return true;
     }
 
     bool finish(uint32_t funcIndex, unsigned generateTime) {
-        return m_.mg().finishFunc(funcIndex, encoder().finish(), generateTime, &fg_);
+        return m_.mg().finishFuncDef(funcIndex, generateTime, &fg_);
     }
 
     bool fail(ParseNode* pn, const char* str) {
         return m_.fail(pn, str);
     }
 
     bool failf(ParseNode* pn, const char* fmt, ...) {
         va_list ap;
@@ -2699,17 +2703,18 @@ class MOZ_STACK_CLASS FunctionValidator
         if (locals_.has(name))
             return nullptr;
         return m_.lookupGlobal(name);
     }
 
     size_t numLocals() const { return locals_.count(); }
 
     /************************************************* Packing interface */
-    Encoder& encoder() { return encoder_; }
+
+    Encoder& encoder() { return *encoder_; }
 
     bool noteLineCol(ParseNode* pn) {
         uint32_t line, column;
         m().tokenStream().srcCoords.lineNumAndColumnIndex(pn->pn_pos.begin, &line, &column);
         return fg_.addSourceCoords(encoder().bytecodeOffset(), line, column);
     }
 
     MOZ_WARN_UNUSED_RESULT
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -299,29 +299,29 @@ enum NeedsBoundsCheck : uint8_t
 {
     NO_BOUNDS_CHECK,
     NEEDS_BOUNDS_CHECK
 };
 
 typedef Vector<uint8_t, 0, SystemAllocPolicy> Bytecode;
 typedef UniquePtr<Bytecode> UniqueBytecode;
 
-// The Encoder class recycles (through its constructor) or creates a new Bytecode (through its
-// init() method). Its Bytecode is released when it's done building the wasm IR in finish().
+// The Encoder class appends bytes to the Bytecode object it is given during
+// construction. The client is responsible for the Bytecode's lifetime and must
+// keep the Bytecode alive as long as the Encoder is used.
 class Encoder
 {
-    UniqueBytecode bytecode_;
-    DebugOnly<bool> done_;
+    Bytecode& bytecode_;
 
     template <class T>
     MOZ_WARN_UNUSED_RESULT
     bool write(T v, size_t* offset) {
         if (offset)
-            *offset = bytecode_->length();
-        return bytecode_->append(reinterpret_cast<uint8_t*>(&v), sizeof(T));
+            *offset = bytecode_.length();
+        return bytecode_.append(reinterpret_cast<uint8_t*>(&v), sizeof(T));
     }
 
     template <class T>
     MOZ_WARN_UNUSED_RESULT
     bool writeEnum(T v, size_t* offset) {
         // For now, just write a u8 instead of a variable-length integer.
         // Variable-length is somewhat annoying at the moment due to the
         // pre-order encoding and back-patching; let's see if we switch to
@@ -331,44 +331,29 @@ class Encoder
         return writeU8(uint8_t(v), offset);
     }
 
     template <class T>
     void patchEnum(size_t pc, T v) {
         // See writeEnum comment.
         static_assert(mozilla::IsEnum<T>::value, "is an enum");
         MOZ_ASSERT(uint64_t(v) < UINT8_MAX);
-        (*bytecode_)[pc] = uint8_t(v);
+        bytecode_[pc] = uint8_t(v);
     }
 
   public:
-    Encoder()
-      : bytecode_(nullptr),
-        done_(false)
-    {}
-
-    bool init(UniqueBytecode bytecode = UniqueBytecode()) {
-        if (bytecode) {
-            bytecode_ = Move(bytecode);
-            bytecode_->clear();
-            return true;
-        }
-        bytecode_ = MakeUnique<Bytecode>();
-        return !!bytecode_;
+    explicit Encoder(Bytecode& bytecode)
+      : bytecode_(bytecode)
+    {
+        MOZ_ASSERT(empty());
     }
 
-    size_t bytecodeOffset() const { return bytecode_->length(); }
+    size_t bytecodeOffset() const { return bytecode_.length(); }
     bool empty() const { return bytecodeOffset() == 0; }
 
-    UniqueBytecode finish() {
-        MOZ_ASSERT(!done_);
-        done_ = true;
-        return Move(bytecode_);
-    }
-
     MOZ_WARN_UNUSED_RESULT bool
     writeVarU32(uint32_t i) {
         do {
             uint8_t byte = i & 0x7F;
             i >>= 7;
             if (i != 0)
                 byte |= 0x80;
             if (!writeU8(byte))
@@ -413,34 +398,34 @@ class Encoder
         }
         return true;
     }
 
 #ifdef DEBUG
     bool pcIsPatchable(size_t pc, unsigned size) const {
         bool patchable = true;
         for (unsigned i = 0; patchable && i < size; i++)
-            patchable &= Expr((*bytecode_)[pc]) == Expr::Unreachable;
+            patchable &= Expr(bytecode_[pc]) == Expr::Unreachable;
         return patchable;
     }
 #endif
     void patchU8(size_t pc, uint8_t i) {
         MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint8_t)));
-        (*bytecode_)[pc] = i;
+        bytecode_[pc] = i;
     }
     void patchExpr(size_t pc, Expr expr) {
         MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint8_t)));
         patchEnum(pc, expr);
     }
     template<class T>
     void patch32(size_t pc, T i) {
         static_assert(sizeof(T) == sizeof(uint32_t),
                       "patch32 must be used with 32-bits wide types");
         MOZ_ASSERT(pcIsPatchable(pc, sizeof(uint32_t)));
-        memcpy(&(*bytecode_)[pc], &i, sizeof(uint32_t));
+        memcpy(&bytecode_[pc], &i, sizeof(uint32_t));
     }
 };
 
 class Decoder
 {
     const uint8_t* const beg_;
     const uint8_t* const end_;
     const uint8_t* cur_;
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -32,16 +32,18 @@ using namespace js::wasm;
 
 static const unsigned GENERATOR_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
 static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
 
 ModuleGenerator::ModuleGenerator(ExclusiveContext* cx)
   : cx_(cx),
     jcx_(CompileRuntime::get(cx->compartment()->runtimeFromAnyThread())),
     slowFuncs_(cx),
+    numSigs_(0),
+    numFuncSigs_(0),
     lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
     alloc_(&lifo_),
     masm_(MacroAssembler::AsmJSToken(), alloc_),
     funcIndexToExport_(cx),
     parallel_(false),
     outstanding_(0),
     tasks_(cx),
     freeTasks_(cx),
@@ -97,30 +99,43 @@ ParallelCompilationEnabled(ExclusiveCont
         return false;
 
     // If 'cx' isn't a JSContext, then we are already off the main thread so
     // off-thread compilation must be enabled.
     return !cx->isJSContext() || cx->asJSContext()->runtime()->canUseOffthreadIonCompilation();
 }
 
 bool
-ModuleGenerator::init(UniqueModuleGeneratorData shared)
+ModuleGenerator::init(UniqueModuleGeneratorData shared, ModuleKind kind)
 {
     module_ = MakeUnique<ModuleData>();
     if (!module_)
         return false;
 
     module_->globalBytes = InitialGlobalDataBytes;
     module_->compileArgs = CompileArgs(cx_);
+    module_->kind = kind;
 
     link_ = MakeUnique<StaticLinkData>();
     if (!link_)
         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.
     shared_ = Move(shared);
+    if (kind == ModuleKind::Wasm) {
+        numSigs_ = shared_->sigs.length();
+        numFuncSigs_ = shared_->funcSigs.length();
+        for (uint32_t i = 0; i < shared_->imports.length(); i++) {
+            if (!addImport(*shared_->imports[i].sig, shared_->imports[i].globalDataOffset))
+                return false;
+        }
+    }
 
     threadView_ = MakeUnique<ModuleGeneratorThreadView>(*shared_);
     if (!threadView_)
         return false;
 
     if (!funcIndexToExport_.init())
         return false;
 
@@ -196,16 +211,17 @@ ModuleGenerator::finishTask(IonCompileTa
     results.offsets().offsetBy(offsetInWhole);
 
     // Record the non-profiling entry for whole-module linking later.
     // Cannot simply append because funcIndex order is nonlinear.
     if (func.index() >= funcEntryOffsets_.length()) {
         if (!funcEntryOffsets_.resize(func.index() + 1))
             return false;
     }
+    MOZ_ASSERT(funcEntryOffsets_[func.index()] == 0);
     funcEntryOffsets_[func.index()] = results.offsets().nonProfilingEntry;
 
     // Merge the compiled results into the whole-module masm.
     DebugOnly<size_t> sizeBefore = masm_.size();
     if (!masm_.asmMergeWith(results.masm()))
         return false;
     MOZ_ASSERT(masm_.size() == offsetInWhole + results.masm().size());
 
@@ -226,16 +242,26 @@ ModuleGenerator::finishTask(IonCompileTa
             return false;
     }
 
     freeTasks_.infallibleAppend(task);
     return true;
 }
 
 bool
+ModuleGenerator::addImport(const Sig& sig, uint32_t globalDataOffset)
+{
+    Sig copy;
+    if (!copy.clone(sig))
+        return false;
+
+    return module_->imports.emplaceBack(Move(copy), globalDataOffset);
+}
+
+bool
 ModuleGenerator::allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset)
 {
     uint32_t globalBytes = module_->globalBytes;
 
     uint32_t pad = ComputeByteAlignment(globalBytes, align);
     if (UINT32_MAX - globalBytes < pad + bytes)
         return false;
 
@@ -267,35 +293,56 @@ ModuleGenerator::allocateGlobalVar(ValTy
         break;
     }
     return allocateGlobalBytes(width, width, globalDataOffset);
 }
 
 void
 ModuleGenerator::initSig(uint32_t sigIndex, Sig&& sig)
 {
+    MOZ_ASSERT(module_->kind == ModuleKind::AsmJS);
+    MOZ_ASSERT(sigIndex == numSigs_);
+    numSigs_++;
+
     MOZ_ASSERT(shared_->sigs[sigIndex] == Sig());
     shared_->sigs[sigIndex] = Move(sig);
 }
 
 const DeclaredSig&
 ModuleGenerator::sig(uint32_t index) const
 {
+    MOZ_ASSERT(index < numSigs_);
     return shared_->sigs[index];
 }
 
 bool
+ModuleGenerator::initFuncSig(uint32_t funcIndex, uint32_t sigIndex)
+{
+    MOZ_ASSERT(module_->kind == ModuleKind::AsmJS);
+    MOZ_ASSERT(funcIndex == numFuncSigs_);
+    MOZ_ASSERT(!shared_->funcSigs[funcIndex]);
+
+    numFuncSigs_++;
+    shared_->funcSigs[funcIndex] = &shared_->sigs[sigIndex];
+    return true;
+}
+
+const DeclaredSig&
+ModuleGenerator::funcSig(uint32_t funcIndex) const
+{
+    MOZ_ASSERT(shared_->funcSigs[funcIndex]);
+    return *shared_->funcSigs[funcIndex];
+}
+
+bool
 ModuleGenerator::initImport(uint32_t importIndex, uint32_t sigIndex, uint32_t globalDataOffset)
 {
+    MOZ_ASSERT(module_->kind == ModuleKind::AsmJS);
     MOZ_ASSERT(importIndex == module_->imports.length());
-
-    Sig copy;
-    if (!copy.clone(sig(sigIndex)))
-        return false;
-    if (!module_->imports.emplaceBack(Move(copy), globalDataOffset))
+    if (!addImport(sig(sigIndex), globalDataOffset))
         return false;
 
     ModuleImportGeneratorData& import = shared_->imports[importIndex];
     MOZ_ASSERT(!import.sig);
     import.sig = &shared_->sigs[sigIndex];
     import.globalDataOffset = globalDataOffset;
     return true;
 }
@@ -363,67 +410,58 @@ ModuleGenerator::numExports() const
 bool
 ModuleGenerator::defineExport(uint32_t index, Offsets offsets)
 {
     module_->exports[index].initStubOffset(offsets.begin);
     return module_->codeRanges.emplaceBack(CodeRange::Entry, offsets);
 }
 
 bool
-ModuleGenerator::initFuncSig(uint32_t funcIndex, uint32_t sigIndex)
-{
-    MOZ_ASSERT(!shared_->funcSigs[funcIndex]);
-    shared_->funcSigs[funcIndex] = &shared_->sigs[sigIndex];
-    return true;
-}
-
-const DeclaredSig&
-ModuleGenerator::funcSig(uint32_t funcIndex) const
-{
-    MOZ_ASSERT(shared_->funcSigs[funcIndex]);
-    return *shared_->funcSigs[funcIndex];
-}
-
-bool
-ModuleGenerator::startFunc(PropertyName* name, unsigned line, unsigned column,
-                           UniqueBytecode* recycled, FunctionGenerator* fg)
+ModuleGenerator::startFuncDef(PropertyName* name, unsigned line, unsigned column,
+                              FunctionGenerator* fg)
 {
     MOZ_ASSERT(!activeFunc_);
     MOZ_ASSERT(!finishedFuncs_);
 
     if (freeTasks_.empty() && !finishOutstandingTask())
         return false;
 
     IonCompileTask* task = freeTasks_.popCopy();
 
-    task->reset(recycled);
+    task->reset(&fg->bytecode_);
+    if (fg->bytecode_) {
+        fg->bytecode_->clear();
+    } else {
+        fg->bytecode_ = MakeUnique<Bytecode>();
+        if (!fg->bytecode_)
+            return false;
+    }
 
     fg->name_= name;
     fg->line_ = line;
     fg->column_ = column;
     fg->m_ = this;
     fg->task_ = task;
     activeFunc_ = fg;
     return true;
 }
 
 bool
-ModuleGenerator::finishFunc(uint32_t funcIndex, UniqueBytecode bytecode, unsigned generateTime,
-                            FunctionGenerator* fg)
+ModuleGenerator::finishFuncDef(uint32_t funcIndex, unsigned generateTime, FunctionGenerator* fg)
 {
     MOZ_ASSERT(activeFunc_ == fg);
 
     UniqueFuncBytecode func =
         js::MakeUnique<FuncBytecode>(fg->name_,
                                      fg->line_,
                                      fg->column_,
                                      Move(fg->callSourceCoords_),
                                      funcIndex,
                                      funcSig(funcIndex),
-                                     Move(bytecode),
+                                     Move(fg->bytecode_),
                                      Move(fg->localVars_),
                                      generateTime);
     if (!func)
         return false;
 
     fg->task_->init(Move(func));
 
     if (parallel_) {
@@ -439,17 +477,17 @@ ModuleGenerator::finishFunc(uint32_t fun
 
     fg->m_ = nullptr;
     fg->task_ = nullptr;
     activeFunc_ = nullptr;
     return true;
 }
 
 bool
-ModuleGenerator::finishFuncs()
+ModuleGenerator::finishFuncDefs()
 {
     MOZ_ASSERT(!activeFunc_);
     MOZ_ASSERT(!finishedFuncs_);
 
     while (outstanding_ > 0) {
         if (!finishOutstandingTask())
             return false;
     }
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -115,16 +115,18 @@ class MOZ_STACK_CLASS ModuleGenerator
 
     // Data handed back to the caller in finish()
     UniqueModuleData                module_;
     UniqueStaticLinkData            link_;
     SlowFunctionVector              slowFuncs_;
 
     // Data scoped to the ModuleGenerator's lifetime
     UniqueModuleGeneratorData       shared_;
+    uint32_t                        numSigs_;
+    uint32_t                        numFuncSigs_;
     LifoAlloc                       lifo_;
     jit::TempAllocator              alloc_;
     jit::MacroAssembler             masm_;
     Uint32Vector                    funcEntryOffsets_;
     Uint32Vector                    exportFuncIndices_;
     FuncIndexMap                    funcIndexToExport_;
 
     // Parallel compilation
@@ -135,56 +137,59 @@ class MOZ_STACK_CLASS ModuleGenerator
     Vector<IonCompileTask*>         freeTasks_;
 
     // Assertions
     DebugOnly<FunctionGenerator*>   activeFunc_;
     DebugOnly<bool>                 finishedFuncs_;
 
     bool finishOutstandingTask();
     bool finishTask(IonCompileTask* task);
+    bool addImport(const Sig& sig, uint32_t globalDataOffset);
 
   public:
     explicit ModuleGenerator(ExclusiveContext* cx);
     ~ModuleGenerator();
 
-    bool init(UniqueModuleGeneratorData shared);
+    bool init(UniqueModuleGeneratorData shared, ModuleKind = ModuleKind::Wasm);
 
     CompileArgs args() const { return module_->compileArgs; }
     jit::MacroAssembler& masm() { return masm_; }
     const Uint32Vector& funcEntryOffsets() const { return funcEntryOffsets_; }
 
     // Global data:
     bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
     bool allocateGlobalVar(ValType type, uint32_t* globalDataOffset);
 
     // Signatures:
     void initSig(uint32_t sigIndex, Sig&& sig);
+    uint32_t numSigs() const { return numSigs_; }
     const DeclaredSig& sig(uint32_t sigIndex) const;
 
+    // Function declarations:
+    bool initFuncSig(uint32_t funcIndex, uint32_t sigIndex);
+    uint32_t numFuncSigs() const { return numFuncSigs_; }
+    const DeclaredSig& funcSig(uint32_t funcIndex) const;
+
     // Imports:
     bool initImport(uint32_t importIndex, uint32_t sigIndex, uint32_t globalDataOffset);
     uint32_t numImports() const;
     const ModuleImportGeneratorData& import(uint32_t index) const;
     bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit);
 
     // Exports:
     bool declareExport(uint32_t funcIndex, uint32_t* exportIndex);
     uint32_t numExports() const;
     uint32_t exportFuncIndex(uint32_t index) const;
     const Sig& exportSig(uint32_t index) const;
     bool defineExport(uint32_t index, Offsets offsets);
 
-    // Functions:
-    bool initFuncSig(uint32_t funcIndex, uint32_t sigIndex);
-    const DeclaredSig& funcSig(uint32_t funcIndex) const;
-    bool startFunc(PropertyName* name, unsigned line, unsigned column, UniqueBytecode* recycled,
-                   FunctionGenerator* fg);
-    bool finishFunc(uint32_t funcIndex, UniqueBytecode bytecode, unsigned generateTime,
-                    FunctionGenerator* fg);
-    bool finishFuncs();
+    // Function definitions:
+    bool startFuncDef(PropertyName* name, unsigned line, unsigned column, FunctionGenerator* fg);
+    bool finishFuncDef(uint32_t funcIndex, unsigned generateTime, FunctionGenerator* fg);
+    bool finishFuncDefs();
 
     // Function-pointer tables:
     bool declareFuncPtrTable(uint32_t numElems, uint32_t* index);
     uint32_t funcPtrTableGlobalDataOffset(uint32_t index) const;
     void defineFuncPtrTable(uint32_t index, const Vector<uint32_t>& elemFuncIndices);
 
     // Stubs:
     bool defineInlineStub(Offsets offsets);
@@ -212,18 +217,19 @@ class MOZ_STACK_CLASS ModuleGenerator
 
 class MOZ_STACK_CLASS FunctionGenerator
 {
     friend class ModuleGenerator;
 
     ModuleGenerator*   m_;
     IonCompileTask*    task_;
 
-    // Function metadata created during function generation, then handed over
-    // to the FuncBytecode in ModuleGenerator::finishFunc().
+    // Data created during function generation, then handed over to the
+    // FuncBytecode in ModuleGenerator::finishFunc().
+    UniqueBytecode     bytecode_;
     SourceCoordsVector callSourceCoords_;
     ValTypeVector      localVars_;
 
     // Note: this unrooted field assumes AutoKeepAtoms via TokenStream via
     // asm.js compilation.
     PropertyName* name_;
     unsigned line_;
     unsigned column_;
@@ -232,16 +238,19 @@ class MOZ_STACK_CLASS FunctionGenerator
     FunctionGenerator()
       : m_(nullptr),
         task_(nullptr),
         name_(nullptr),
         line_(0),
         column_(0)
     {}
 
+    Bytecode& bytecode() const {
+        return *bytecode_;
+    }
     bool addSourceCoords(size_t byteOffset, uint32_t line, uint32_t column) {
         SourceCoords sc = { byteOffset, line, column };
         return callSourceCoords_.append(sc);
     }
     bool addVariable(ValType v) {
         return localVars_.append(v);
     }
 };
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -820,17 +820,16 @@ bool
 Module::clone(JSContext* cx, const StaticLinkData& link, Module* out) const
 {
     MOZ_ASSERT(dynamicallyLinked_);
 
     // The out->module_ field was already cloned and initialized when 'out' was
     // constructed. This function should clone the rest.
     MOZ_ASSERT(out->module_);
 
-    out->isAsmJS_ = isAsmJS_;
     out->profilingEnabled_ = profilingEnabled_;
 
     if (!CloneVector(cx, funcLabels_, &out->funcLabels_))
         return false;
 
 #ifdef DEBUG
     // Put the symbolic links back to -1 so PatchDataWithValueCheck assertions
     // in Module::staticallyLink are valid.
@@ -849,19 +848,18 @@ Module::clone(JSContext* cx, const Stati
     // unspecialized in the copy.
     if (usesHeap())
         out->despecializeFromHeap(heap_);
 
     return true;
 }
 
 
-Module::Module(UniqueModuleData module, AsmJSBool isAsmJS)
+Module::Module(UniqueModuleData module)
   : module_(Move(module)),
-    isAsmJS_(bool(isAsmJS)),
     staticallyLinked_(false),
     interrupt_(nullptr),
     outOfBounds_(nullptr),
     dynamicallyLinked_(false),
     profilingEnabled_(false)
 {
     *(double*)(globalData() + NaN64GlobalDataOffset) = GenericNaN();
     *(float*)(globalData() + NaN32GlobalDataOffset) = GenericNaN();
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -349,32 +349,41 @@ enum class HeapUsage
 };
 
 static inline bool
 UsesHeap(HeapUsage heapUsage)
 {
     return bool(heapUsage);
 }
 
+// A Module can either be asm.js or wasm.
+
+enum ModuleKind
+{
+    Wasm,
+    AsmJS
+};
+
 // See mutedErrors comment in jsapi.h.
 
 enum class MutedErrorsBool
 {
     DontMuteErrors = false,
     MuteErrors = true
 };
 
 // ModuleCacheablePod holds the trivially-memcpy()able serializable portion of
 // ModuleData.
 
 struct ModuleCacheablePod
 {
     uint32_t              functionBytes;
     uint32_t              codeBytes;
     uint32_t              globalBytes;
+    ModuleKind            kind;
     HeapUsage             heapUsage;
     MutedErrorsBool       mutedErrors;
     CompileArgs           compileArgs;
 
     uint32_t totalBytes() const { return codeBytes + globalBytes; }
 };
 
 // ModuleData holds the guts of a Module. ModuleData is mutably built up by
@@ -446,17 +455,16 @@ class Module
         {}
     };
     typedef Vector<FuncPtrTable, 0, SystemAllocPolicy> FuncPtrTableVector;
     typedef Vector<CacheableChars, 0, SystemAllocPolicy> FuncLabelVector;
     typedef RelocatablePtrArrayBufferObjectMaybeShared BufferPtr;
 
     // Initialized when constructed:
     const UniqueConstModuleData  module_;
-    bool                         isAsmJS_;
 
     // Initialized during staticallyLink:
     bool                         staticallyLinked_;
     uint8_t*                     interrupt_;
     uint8_t*                     outOfBounds_;
     FuncPtrTableVector           funcPtrTables_;
 
     // Initialized during dynamicallyLink:
@@ -474,26 +482,25 @@ class Module
     void despecializeFromHeap(ArrayBufferObjectMaybeShared* heap);
     void sendCodeRangesToProfiler(JSContext* cx);
     MOZ_WARN_UNUSED_RESULT bool setProfilingEnabled(JSContext* cx, bool enabled);
     ImportExit& importToExit(const Import& import);
 
     friend class js::WasmActivation;
 
   protected:
-    enum AsmJSBool { NotAsmJS = false, IsAsmJS = true };
     const ModuleData& base() const { return *module_; }
     bool clone(JSContext* cx, const StaticLinkData& link, Module* clone) const;
 
   public:
     static const unsigned SizeOfImportExit = sizeof(ImportExit);
     static const unsigned OffsetOfImportExitFun = offsetof(ImportExit, fun);
     static const unsigned SizeOfEntryArg = sizeof(EntryArg);
 
-    explicit Module(UniqueModuleData module, AsmJSBool = NotAsmJS);
+    explicit Module(UniqueModuleData module);
     virtual ~Module();
     virtual void trace(JSTracer* trc);
     virtual void addSizeOfMisc(MallocSizeOf mallocSizeOf, size_t* code, size_t* data);
 
     uint8_t* code() const { return module_->code.get(); }
     uint32_t codeBytes() const { return module_->codeBytes; }
     uint8_t* globalData() const { return code() + module_->codeBytes; }
     uint32_t globalBytes() const { return module_->globalBytes; }
@@ -511,19 +518,19 @@ class Module
     bool staticallyLinked() const { return staticallyLinked_; }
     bool dynamicallyLinked() const { return dynamicallyLinked_; }
 
     // Some wasm::Module's have the most-derived type AsmJSModule. The
     // AsmJSModule stores the extra metadata necessary to implement asm.js (JS)
     // semantics. The asAsmJS() member may be used as a checked downcast when
     // isAsmJS() is true.
 
-    bool isAsmJS() const { return isAsmJS_; }
-    AsmJSModule& asAsmJS() { MOZ_ASSERT(isAsmJS_); return *(AsmJSModule*)this; }
-    const AsmJSModule& asAsmJS() const { MOZ_ASSERT(isAsmJS_); return *(const AsmJSModule*)this; }
+    bool isAsmJS() const { return module_->kind == ModuleKind::AsmJS; }
+    AsmJSModule& asAsmJS() { MOZ_ASSERT(isAsmJS()); return *(AsmJSModule*)this; }
+    const AsmJSModule& asAsmJS() const { MOZ_ASSERT(isAsmJS()); return *(const AsmJSModule*)this; }
 
     // The range [0, functionBytes) is a subrange of [0, codeBytes) that
     // contains only function body code, not the stub code. This distinction is
     // used by the async interrupt handler to only interrupt when the pc is in
     // function code which, in turn, simplifies reasoning about how stubs
     // enter/exit.
 
     bool containsFunctionPC(void* pc) const;
--- a/js/src/jsapi-tests/testWasmLEB128.cpp
+++ b/js/src/jsapi-tests/testWasmLEB128.cpp
@@ -37,28 +37,24 @@ static bool WriteValidBytes(wasm::Encode
     *passed = true;
     return true;
 }
 
 BEGIN_TEST(testWasmLEB128_encoding)
 {
     using namespace wasm;
 
-    Encoder encoder;
-    if (!encoder.init())
-        return false;
+    Bytecode bc;
+    Encoder encoder(bc);
 
     bool passed;
     if (!WriteValidBytes(encoder, &passed))
         return false;
     CHECK(passed);
 
-    UniqueBytecode bytecode = mozilla::Move(encoder.finish());
-    Bytecode& bc = *bytecode;
-
     size_t i = 0;
     CHECK(bc[i++] == 0x0);
     CHECK(bc[i++] == 0x1);
     CHECK(bc[i++] == 0x42);
 
     CHECK(bc[i++] == 0x80);
     CHECK(bc[i++] == 0x01);