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 303028 8be707e04f9acfaa018a7ff838c36b74b214927f
parent 303027 0f7510fa983c1804792cb4a43d341b6b4e533172
child 303029 3842b1992f25049b7230930c30d8e646ceb778ae
push id8978
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 14:05:32 +0000
treeherdermozilla-aurora@b9a803752a2c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1234985
milestone46.0a1
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);