Bug 1186424: Move compilation data into ModuleCompileResults and have MV finish module's compilation; r=luke
☠☠ backed out by f8dd6bbde6ce ☠ ☠
authorBenjamin Bouvier <benj@benj.me>
Sat, 15 Aug 2015 14:41:43 +0200
changeset 257953 4612c09141ae63f93b7d4dc042a18f521692f661
parent 257952 94c7d36f29ba70f22776a4cdeb71b770ce30e854
child 257954 51eebda696d4d9a2e03808b813daa3151513e9f4
push id29238
push userryanvm@gmail.com
push dateMon, 17 Aug 2015 13:06:57 +0000
treeherdermozilla-central@a6eeb28458fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1186424
milestone43.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 1186424: Move compilation data into ModuleCompileResults and have MV finish module's compilation; r=luke
js/src/asmjs/AsmJSValidate.cpp
js/src/devtools/rootAnalysis/annotations.js
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -24,17 +24,16 @@
 #ifdef MOZ_VTUNE
 # include "vtune/VTuneWrapper.h"
 #endif
 
 #include "jsmath.h"
 #include "jsprf.h"
 #include "jsutil.h"
 
-
 #include "asmjs/AsmJSLink.h"
 #include "asmjs/AsmJSModule.h"
 #include "asmjs/AsmJSSignalHandlers.h"
 #include "builtin/SIMD.h"
 #include "frontend/Parser.h"
 #include "jit/AtomicOperations.h"
 #include "jit/CodeGenerator.h"
 #include "jit/CompileWrappers.h"
@@ -1392,18 +1391,16 @@ enum class F32X4 : uint8_t {
     Load,
     Store,
 
     // asm.js specific
     Id,
     Bad
 };
 
-} // namespace
-
 class AsmFunction
 {
     typedef Vector<uint8_t, 4096> Bytecode;
     Bytecode bytecode_;
 
   public:
     typedef Vector<AsmJSNumLit> VarInitializerVector;
 
@@ -1527,131 +1524,145 @@ class AsmFunction
     // Read-only interface
     size_t size() const { return bytecode_.length(); }
 
     RetType returnedType() const { MOZ_ASSERT(returnedType_ != RetType::Which(-1)); return returnedType_; }
     const VarInitializerVector& varInitializers() const { return varInitializers_; }
     size_t numLocals() const { MOZ_ASSERT(numLocals_ != size_t(-1)); return numLocals_; }
 };
 
-namespace {
-
-class ModuleGlobals
+class Func
+{
+    Signature sig_;
+    AsmFunction* bytecode_;
+    PropertyName* name_;
+    Label* entry_;
+    uint32_t funcIndex_;
+    uint32_t srcBegin_;
+    uint32_t srcEnd_;
+    uint32_t compileTime_;
+    bool defined_;
+
+    public:
+    Func(PropertyName* name, Signature&& sig, Label* entry, uint32_t funcIndex)
+      : sig_(Move(sig)), bytecode_(nullptr), name_(name), entry_(entry),
+        funcIndex_(funcIndex), srcBegin_(0), srcEnd_(0), compileTime_(0), defined_(false)
+    {}
+
+    PropertyName* name() const { return name_; }
+    bool defined() const { return defined_; }
+    uint32_t funcIndex() const { return funcIndex_; }
+
+    void define(ParseNode* fn, AsmFunction* bytecode) {
+        MOZ_ASSERT(!defined_);
+        defined_ = true;
+        srcBegin_ = fn->pn_pos.begin;
+        srcEnd_ = fn->pn_pos.end;
+        bytecode_ = bytecode;
+    }
+
+    AsmFunction* bytecode() const { MOZ_ASSERT(defined_); return bytecode_; }
+    uint32_t srcBegin() const { MOZ_ASSERT(defined_); return srcBegin_; }
+    uint32_t srcEnd() const { MOZ_ASSERT(defined_); return srcEnd_; }
+    Signature& sig() { return sig_; }
+    const Signature& sig() const { return sig_; }
+    Label& entry() const { return *entry_; }
+    uint32_t compileTime() const { return compileTime_; }
+    void accumulateCompileTime(uint32_t ms) { compileTime_ += ms; }
+};
+
+typedef Vector<const Func*> FuncPtrVector;
+
+class FuncPtrTable
+{
+    Signature sig_;
+    uint32_t mask_;
+    uint32_t globalDataOffset_;
+    uint32_t tableIndex_;
+    FuncPtrVector elems_;
+
+    FuncPtrTable(FuncPtrTable&& rhs) = delete;
+
+    public:
+    FuncPtrTable(ExclusiveContext* cx, Signature&& sig, uint32_t mask, uint32_t gdo,
+                 uint32_t tableIndex)
+      : sig_(Move(sig)), mask_(mask), globalDataOffset_(gdo), tableIndex_(tableIndex),
+        elems_(cx)
+    {}
+
+    Signature& sig() { return sig_; }
+    const Signature& sig() const { return sig_; }
+    unsigned mask() const { return mask_; }
+    unsigned globalDataOffset() const { return globalDataOffset_; }
+    unsigned tableIndex() const { return tableIndex_; }
+
+    bool initialized() const { return !elems_.empty(); }
+    void initElems(FuncPtrVector&& elems) { elems_ = Move(elems); MOZ_ASSERT(initialized()); }
+    unsigned numElems() const { MOZ_ASSERT(initialized()); return elems_.length(); }
+    const Func& elem(unsigned i) const { return *elems_[i]; }
+};
+
+typedef Vector<FuncPtrTable*> FuncPtrTableVector;
+typedef Vector<Func*> FuncVector;
+
+} // namespace
+
+class ModuleCompileResults
 {
   public:
-
-    class Func
-    {
-        Signature sig_;
-        AsmFunction* bytecode_;
-        PropertyName* name_;
-        Label* entry_;
-        uint32_t funcIndex_;
-        uint32_t srcBegin_;
-        uint32_t srcEnd_;
-        uint32_t compileTime_;
-        bool defined_;
-
-      public:
-        Func(PropertyName* name, Signature&& sig, Label* entry, uint32_t funcIndex)
-          : sig_(Move(sig)), bytecode_(nullptr), name_(name), entry_(entry),
-            funcIndex_(funcIndex), srcBegin_(0), srcEnd_(0), compileTime_(0), defined_(false)
+    struct SlowFunction
+    {
+        SlowFunction(PropertyName* name, unsigned ms, unsigned line, unsigned column)
+         : name(name), ms(ms), line(line), column(column)
         {}
 
-        PropertyName* name() const { return name_; }
-        bool defined() const { return defined_; }
-        uint32_t funcIndex() const { return funcIndex_; }
-
-        void define(ParseNode* fn, AsmFunction* bytecode) {
-            MOZ_ASSERT(!defined_);
-            defined_ = true;
-            srcBegin_ = fn->pn_pos.begin;
-            srcEnd_ = fn->pn_pos.end;
-            bytecode_ = bytecode;
-        }
-
-        AsmFunction* bytecode() const { MOZ_ASSERT(defined_); return bytecode_; }
-        uint32_t srcBegin() const { MOZ_ASSERT(defined_); return srcBegin_; }
-        uint32_t srcEnd() const { MOZ_ASSERT(defined_); return srcEnd_; }
-        Signature& sig() { return sig_; }
-        const Signature& sig() const { return sig_; }
-        Label& entry() const { return *entry_; }
-        uint32_t compileTime() const { return compileTime_; }
-        void accumulateCompileTime(uint32_t ms) { compileTime_ += ms; }
+        PropertyName* name;
+        unsigned ms;
+        unsigned line;
+        unsigned column;
     };
 
-    typedef Vector<const Func*> FuncPtrVector;
-
-    class FuncPtrTable
-    {
-        Signature sig_;
-        uint32_t mask_;
-        uint32_t globalDataOffset_;
-        uint32_t tableIndex_;
-        FuncPtrVector elems_;
-
-        FuncPtrTable(FuncPtrTable&& rhs) = delete;
-
-      public:
-        FuncPtrTable(ExclusiveContext* cx, Signature&& sig, uint32_t mask, uint32_t gdo,
-                     uint32_t tableIndex)
-          : sig_(Move(sig)), mask_(mask), globalDataOffset_(gdo), tableIndex_(tableIndex),
-            elems_(cx)
-        {}
-
-        Signature& sig() { return sig_; }
-        const Signature& sig() const { return sig_; }
-        unsigned mask() const { return mask_; }
-        unsigned globalDataOffset() const { return globalDataOffset_; }
-        unsigned tableIndex() const { return tableIndex_; }
-
-        bool initialized() const { return !elems_.empty(); }
-        void initElems(FuncPtrVector&& elems) { elems_ = Move(elems); MOZ_ASSERT(initialized()); }
-        unsigned numElems() const { MOZ_ASSERT(initialized()); return elems_.length(); }
-        const Func& elem(unsigned i) const { return *elems_[i]; }
-    };
-
-    typedef Vector<FuncPtrTable*> FuncPtrTableVector;
-    typedef Vector<Func*> FuncVector;
+    typedef Vector<SlowFunction> SlowFunctionVector;
 
   private:
-    FuncVector functions_;
-    FuncPtrTableVector funcPtrTables_;
+    MacroAssembler      masm_;
+    SlowFunctionVector  slowFunctions_;
+    NonAssertingLabel   stackOverflowLabel_;
+    NonAssertingLabel   asyncInterruptLabel_;
+    NonAssertingLabel   syncInterruptLabel_;
+    NonAssertingLabel   onDetachedLabel_;
+    NonAssertingLabel   onConversionErrorLabel_;
+    NonAssertingLabel   onOutOfBoundsLabel_;
+    int64_t             usecBefore_;
 
   public:
-    ModuleGlobals(ExclusiveContext* cx)
-      : functions_(cx),
-        funcPtrTables_(cx)
+    explicit ModuleCompileResults(ExclusiveContext* cx)
+      : masm_(MacroAssembler::AsmJSToken()),
+        slowFunctions_(cx),
+        usecBefore_(PRMJ_Now())
     {}
 
-    unsigned numFunctions() const {
-        return functions_.length();
-    }
-    Func* function(unsigned i) {
-        return functions_[i];
-    }
-    bool addFunction(Func* func) {
-        return functions_.append(func);
-    }
-
-    unsigned numFuncPtrTables() const {
-        return funcPtrTables_.length();
-    }
-    FuncPtrTable& funcPtrTable(unsigned i) {
-        return *funcPtrTables_[i];
-    }
-    bool addFuncPtrTable(FuncPtrTable* table) {
-        return funcPtrTables_.append(table);
-    }
+    MacroAssembler& masm()              { return masm_; }
+    Label& stackOverflowLabel()         { return stackOverflowLabel_; }
+    Label& asyncInterruptLabel()        { return asyncInterruptLabel_; }
+    Label& syncInterruptLabel()         { return syncInterruptLabel_; }
+    Label& onOutOfBoundsLabel()         { return onOutOfBoundsLabel_; }
+    Label& onDetachedLabel()            { return onDetachedLabel_; }
+    Label& onConversionErrorLabel()     { return onConversionErrorLabel_; }
+    int64_t usecBefore()                { return usecBefore_; }
+    SlowFunctionVector& slowFunctions() { return slowFunctions_; }
 };
 
+namespace {
+
 // The ModuleValidator encapsulates the entire validation of an asm.js module.
 // Its lifetime goes from the validation of the top components of an asm.js
 // module (all the globals), the emission of bytecode for all the functions in
-// the module and the validation of function's pointer tables.
+// the module and the validation of function's pointer tables. It also finishes
+// the compilation of all the module's stubs.
 //
 // Rooting note: ModuleValidator is a stack class that contains unrooted
 // PropertyName (JSAtom) pointers.  This is safe because it cannot be
 // constructed without a TokenStream reference.  TokenStream is itself a stack
 // class that cannot be constructed without an AutoKeepAtoms being live on the
 // stack, which prevents collection of atoms.
 //
 // ModuleValidator is marked as rooted in the rooting analysis.  Don't add
@@ -1856,24 +1867,29 @@ class MOZ_STACK_CLASS ModuleValidator
     };
 
   private:
     typedef HashMap<PropertyName*, Global*> GlobalMap;
     typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
     typedef HashMap<PropertyName*, AsmJSAtomicsBuiltinFunction> AtomicsNameMap;
     typedef HashMap<PropertyName*, AsmJSSimdOperation> SimdOperationNameMap;
     typedef Vector<ArrayView> ArrayViewVector;
+
+  public:
     typedef HashMap<ExitDescriptor, unsigned, ExitDescriptor> ExitMap;
 
+  private:
     ExclusiveContext* cx_;
     AsmJSParser&      parser_;
 
     ScopedJSDeletePtr<AsmJSModule> module_;
     LifoAlloc                      moduleLifo_;
-    ModuleGlobals&                 moduleGlobals_;
+
+    FuncVector                     functions_;
+    FuncPtrTableVector             funcPtrTables_;
 
     GlobalMap                      globals_;
     ArrayViewVector                arrayViews_;
     ExitMap                        exits_;
 
     MathNameMap                    standardLibraryMathNames_;
     AtomicsNameMap                 standardLibraryAtomicsNames_;
     SimdOperationNameMap           standardLibrarySimdOpNames_;
@@ -1885,36 +1901,42 @@ class MOZ_STACK_CLASS ModuleValidator
     uint32_t                          errorOffset_;
     bool                              errorOverRecursed_;
 
     bool                           canValidateChangeHeap_;
     bool                           hasChangeHeap_;
 
     bool supportsSimd_;
 
+    ModuleCompileResults*  compileData_;
+    DebugOnly<bool>        finishedFunctionBodies_;
+
   public:
-    ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser, ModuleGlobals& moduleGlobals)
+    ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser)
       : cx_(cx),
         parser_(parser),
         moduleLifo_(LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-        moduleGlobals_(moduleGlobals),
+        functions_(cx),
+        funcPtrTables_(cx),
         globals_(cx),
         arrayViews_(cx),
         exits_(cx),
         standardLibraryMathNames_(cx),
         standardLibraryAtomicsNames_(cx),
         standardLibrarySimdOpNames_(cx),
         moduleFunctionNode_(parser.pc->maybeFunction),
         moduleFunctionName_(nullptr),
         errorString_(nullptr),
         errorOffset_(UINT32_MAX),
         errorOverRecursed_(false),
         canValidateChangeHeap_(false),
         hasChangeHeap_(false),
-        supportsSimd_(cx->jitSupportsSimd())
+        supportsSimd_(cx->jitSupportsSimd()),
+        compileData_(nullptr),
+        finishedFunctionBodies_(false)
     {
         MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
     }
 
     ~ModuleValidator() {
         if (errorString_) {
             MOZ_ASSERT(errorOffset_ != UINT32_MAX);
             tokenStream().reportAsmJSError(errorOffset_,
@@ -2176,61 +2198,61 @@ class MOZ_STACK_CLASS ModuleValidator
         if (!global)
             return false;
         uint32_t index;
         if (!module_->addFFI(field, &index))
             return false;
         global->u.ffiIndex_ = index;
         return globals_.putNew(varName, global);
     }
-    bool addExportedFunction(const ModuleGlobals::Func& func, PropertyName* maybeFieldName) {
+    bool addExportedFunction(const Func& func, PropertyName* maybeFieldName) {
         AsmJSModule::ArgCoercionVector argCoercions;
         const VarTypeVector& args = func.sig().args();
         if (!argCoercions.resize(args.length()))
             return false;
         for (unsigned i = 0; i < args.length(); i++)
             argCoercions[i] = args[i].toCoercion();
         AsmJSModule::ReturnType retType = func.sig().retType().toModuleReturnType();
         return module_->addExportedFunction(func.name(), func.srcBegin(), func.srcEnd(),
                                             maybeFieldName, Move(argCoercions), retType, func.funcIndex());
     }
     bool addExportedChangeHeap(PropertyName* name, const Global& g, PropertyName* maybeFieldName) {
         return module_->addExportedChangeHeap(name, g.changeHeapSrcBegin(), g.changeHeapSrcEnd(),
                                               maybeFieldName);
     }
-    bool addFunction(PropertyName* name, Signature&& sig, ModuleGlobals::Func** func) {
-        uint32_t funcIndex = moduleGlobals_.numFunctions();
+    bool addFunction(PropertyName* name, Signature&& sig, Func** func) {
+        uint32_t funcIndex = numFunctions();
         Global* global = moduleLifo_.new_<Global>(Global::Function);
         if (!global)
             return false;
         global->u.funcIndex_ = funcIndex;
         if (!globals_.putNew(name, global))
             return false;
         Label* entry = moduleLifo_.new_<Label>();
         if (!entry)
             return false;
-        *func = moduleLifo_.new_<ModuleGlobals::Func>(name, Move(sig), entry, funcIndex);
+        *func = moduleLifo_.new_<Func>(name, Move(sig), entry, funcIndex);
         if (!*func)
             return false;
-        return moduleGlobals_.addFunction(*func);
-    }
-    bool addFuncPtrTable(PropertyName* name, Signature&& sig, uint32_t mask, ModuleGlobals::FuncPtrTable** table)
-    {
-        uint32_t tableIndex = moduleGlobals_.numFuncPtrTables();
+        return functions_.append(*func);
+    }
+    bool addFuncPtrTable(PropertyName* name, Signature&& sig, uint32_t mask, FuncPtrTable** table)
+    {
+        uint32_t tableIndex = numFuncPtrTables();
         Global* global = moduleLifo_.new_<Global>(Global::FuncPtrTable);
         if (!global)
             return false;
         global->u.funcPtrTableIndex_ = tableIndex;
         if (!globals_.putNew(name, global))
             return false;
         uint32_t globalDataOffset;
         if (!module_->addFuncPtrTable(/* numElems = */ mask + 1, &globalDataOffset))
             return false;
-        *table = moduleLifo_.new_<ModuleGlobals::FuncPtrTable>(cx_, Move(sig), mask, globalDataOffset, tableIndex);
-        return *table && moduleGlobals_.addFuncPtrTable(*table);
+        *table = moduleLifo_.new_<FuncPtrTable>(cx_, Move(sig), mask, globalDataOffset, tableIndex);
+        return *table && funcPtrTables_.append(*table);
     }
     bool addExit(unsigned ffiIndex, PropertyName* name, Signature&& sig, unsigned* exitIndex,
                  Signature** lifoSig)
     {
         Signature* signature = moduleLifo_.new_<Signature>(Move(sig));
         if (!signature)
             return false;
         *lifoSig = signature;
@@ -2330,39 +2352,39 @@ class MOZ_STACK_CLASS ModuleValidator
 
     unsigned numArrayViews() const {
         return arrayViews_.length();
     }
     const ArrayView& arrayView(unsigned i) const {
         return arrayViews_[i];
     }
     unsigned numFunctions() const {
-        return moduleGlobals_.numFunctions();
-    }
-    ModuleGlobals::Func& function(unsigned i) const {
-        return *moduleGlobals_.function(i);
+        return functions_.length();
+    }
+    Func& function(unsigned i) const {
+        return *functions_[i];
     }
     unsigned numFuncPtrTables() const {
-        return moduleGlobals_.numFuncPtrTables();
-    }
-    ModuleGlobals::FuncPtrTable& funcPtrTable(unsigned i) const {
-        return moduleGlobals_.funcPtrTable(i);
+        return funcPtrTables_.length();
+    }
+    FuncPtrTable& funcPtrTable(unsigned i) const {
+        return *funcPtrTables_[i];
     }
 
     const Global* lookupGlobal(PropertyName* name) const {
         if (GlobalMap::Ptr p = globals_.lookup(name))
             return p->value();
         return nullptr;
     }
 
-    ModuleGlobals::Func* lookupFunction(PropertyName* name) {
+    Func* lookupFunction(PropertyName* name) {
         if (GlobalMap::Ptr p = globals_.lookup(name)) {
             Global* value = p->value();
             if (value->which() == Global::Function)
-                return moduleGlobals_.function(value->funcIndex());
+                return functions_[value->funcIndex()];
         }
         return nullptr;
     }
 
     bool lookupStandardLibraryMathName(PropertyName* name, MathBuiltin* mathBuiltin) const {
         if (MathNameMap::Ptr p = standardLibraryMathNames_.lookup(name)) {
             *mathBuiltin = p->value();
             return true;
@@ -2378,18 +2400,173 @@ class MOZ_STACK_CLASS ModuleValidator
     }
     bool lookupStandardSimdOpName(PropertyName* name, AsmJSSimdOperation* op) const {
         if (SimdOperationNameMap::Ptr p = standardLibrarySimdOpNames_.lookup(name)) {
             *op = p->value();
             return true;
         }
         return false;
     }
+
+    // End-of-compilation utils
+    void takeCompileData(ModuleCompileResults* compileData) { compileData_ = compileData; }
+
+    MacroAssembler& masm()          { return compileData_->masm(); }
+    Label& stackOverflowLabel()     { return compileData_->stackOverflowLabel(); }
+    Label& asyncInterruptLabel()    { return compileData_->asyncInterruptLabel(); }
+    Label& syncInterruptLabel()     { return compileData_->syncInterruptLabel(); }
+    Label& onDetachedLabel()        { return compileData_->onDetachedLabel(); }
+    Label& onOutOfBoundsLabel()     { return compileData_->onOutOfBoundsLabel(); }
+    Label& onConversionErrorLabel() { return compileData_->onConversionErrorLabel(); }
+    ExitMap::Range allExits() const { return exits_.all(); }
+
+    bool finishGeneratingEntry(unsigned exportIndex, Label* begin) {
+        MOZ_ASSERT(finishedFunctionBodies_);
+        module_->exportedFunction(exportIndex).initCodeOffset(begin->offset());
+        uint32_t end = masm().currentOffset();
+        return module_->addCodeRange(AsmJSModule::CodeRange::Entry, begin->offset(), end);
+    }
+    bool finishGeneratingInterpExit(unsigned exitIndex, Label* begin, Label* profilingReturn) {
+        MOZ_ASSERT(finishedFunctionBodies_);
+        uint32_t beg = begin->offset();
+        module_->exit(exitIndex).initInterpOffset(beg);
+        uint32_t pret = profilingReturn->offset();
+        uint32_t end = masm().currentOffset();
+        return module_->addCodeRange(AsmJSModule::CodeRange::SlowFFI, beg, pret, end);
+    }
+    bool finishGeneratingJitExit(unsigned exitIndex, Label* begin, Label* profilingReturn) {
+        MOZ_ASSERT(finishedFunctionBodies_);
+        uint32_t beg = begin->offset();
+        module_->exit(exitIndex).initJitOffset(beg);
+        uint32_t pret = profilingReturn->offset();
+        uint32_t end = masm().currentOffset();
+        return module_->addCodeRange(AsmJSModule::CodeRange::JitFFI, beg, pret, end);
+    }
+    bool finishGeneratingInlineStub(Label* begin) {
+        MOZ_ASSERT(finishedFunctionBodies_);
+        uint32_t end = masm().currentOffset();
+        return module_->addCodeRange(AsmJSModule::CodeRange::Inline, begin->offset(), end);
+    }
+    bool finishGeneratingInterrupt(Label* begin, Label* profilingReturn) {
+        MOZ_ASSERT(finishedFunctionBodies_);
+        uint32_t beg = begin->offset();
+        uint32_t pret = profilingReturn->offset();
+        uint32_t end = masm().currentOffset();
+        return module_->addCodeRange(AsmJSModule::CodeRange::Interrupt, beg, pret, end);
+    }
+    bool finishGeneratingBuiltinThunk(AsmJSExit::BuiltinKind builtin, Label* begin, Label* pret) {
+        MOZ_ASSERT(finishedFunctionBodies_);
+        uint32_t end = masm().currentOffset();
+        return module_->addBuiltinThunkCodeRange(builtin, begin->offset(), pret->offset(), end);
+    }
+
+    bool finish(ScopedJSDeletePtr<AsmJSModule>* module) {
+        masm().finish();
+        if (masm().oom())
+            return false;
+
+        if (!module_->finish(cx_, tokenStream(), masm(), asyncInterruptLabel(), onOutOfBoundsLabel()))
+            return false;
+
+        // Finally, convert all the function-pointer table elements into
+        // RelativeLinks that will be patched by AsmJSModule::staticallyLink.
+        for (unsigned tableIndex = 0; tableIndex < numFuncPtrTables(); tableIndex++) {
+            FuncPtrTable& table = funcPtrTable(tableIndex);
+            unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset();
+            for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) {
+                AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::RawPointer);
+                link.patchAtOffset = tableBaseOffset + elemIndex * sizeof(uint8_t*);
+                link.targetOffset = masm().actualOffset(table.elem(elemIndex).entry().offset());
+                if (!module_->addRelativeLink(link))
+                    return false;
+            }
+        }
+
+        *module = module_.forget();
+        return true;
+    }
+
+    void startFunctionBodies() {
+        module_->startFunctionBodies();
+    }
+    void finishFunctionBodies() {
+        // When an interrupt is triggered, all function code is mprotected and,
+        // for sanity, stub code (particularly the interrupt stub) is not.
+        // Protection works at page granularity, so we need to ensure that no
+        // stub code gets into the function code pages.
+        MOZ_ASSERT(!finishedFunctionBodies_);
+        masm().haltingAlign(AsmJSPageSize);
+        module_->finishFunctionBodies(masm().currentOffset());
+        finishedFunctionBodies_ = true;
+    }
+
+    void buildCompilationTimeReport(JS::AsmJSCacheResult cacheResult, ScopedJSFreePtr<char>* out) {
+        ScopedJSFreePtr<char> slowFuns;
+#ifndef JS_MORE_DETERMINISTIC
+        int64_t usecAfter = PRMJ_Now();
+        int msTotal = (usecAfter - compileData_->usecBefore()) / PRMJ_USEC_PER_MSEC;
+        ModuleCompileResults::SlowFunctionVector& slowFunctions = compileData_->slowFunctions();
+        if (!slowFunctions.empty()) {
+            slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions.length()));
+            if (!slowFuns)
+                return;
+            for (unsigned i = 0; i < slowFunctions.length(); i++) {
+                ModuleCompileResults::SlowFunction& func = slowFunctions[i];
+                JSAutoByteString name;
+                if (!AtomToPrintableString(cx_, func.name, &name))
+                    return;
+                slowFuns.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowFuns.get(),
+                                           name.ptr(), func.line, func.column, func.ms,
+                                           i+1 < slowFunctions.length() ? ", " : ""));
+                if (!slowFuns)
+                    return;
+            }
+        }
+        const char* cacheString = "";
+        switch (cacheResult) {
+          case JS::AsmJSCache_Success:
+            cacheString = "stored in cache";
+            break;
+          case JS::AsmJSCache_ModuleTooSmall:
+            cacheString = "not stored in cache (too small to benefit)";
+            break;
+          case JS::AsmJSCache_SynchronousScript:
+            cacheString = "unable to cache asm.js in synchronous scripts; try loading "
+                          "asm.js via <script async> or createElement('script')";
+            break;
+          case JS::AsmJSCache_QuotaExceeded:
+            cacheString = "not enough temporary storage quota to store in cache";
+            break;
+          case JS::AsmJSCache_StorageInitFailure:
+            cacheString = "storage initialization failed (consider filing a bug)";
+            break;
+          case JS::AsmJSCache_Disabled_Internal:
+            cacheString = "caching disabled by internal configuration (consider filing a bug)";
+            break;
+          case JS::AsmJSCache_Disabled_ShellFlags:
+            cacheString = "caching disabled by missing command-line arguments";
+            break;
+          case JS::AsmJSCache_Disabled_JitInspector:
+            cacheString = "caching disabled by active JIT inspector";
+            break;
+          case JS::AsmJSCache_InternalError:
+            cacheString = "unable to store in cache due to internal error (consider filing a bug)";
+            break;
+          case JS::AsmJSCache_LIMIT:
+            MOZ_CRASH("bad AsmJSCacheResult");
+            break;
+        }
+        out->reset(JS_smprintf("total compilation time %dms; %s%s",
+                               msTotal, cacheString, slowFuns ? slowFuns.get() : ""));
+#endif
+    }
 };
 
+} // namespace
+
 // ModuleCompiler encapsulates the compilation of an entire asm.js module. Over
 // the course of an ModuleCompiler object's lifetime, many FunctionCompiler
 // objects will be created and destroyed in sequence, one for each function in
 // the module.
 //
 // *** asm.js FFI calls ***
 //
 // asm.js allows calling out to non-asm.js via "FFI calls". The asm.js type
@@ -2440,123 +2617,70 @@ class MOZ_STACK_CLASS ModuleValidator
 // well.
 class MOZ_STACK_CLASS ModuleCompiler
 {
   public:
     // Map exitIndex to the corresponding exit's Signature.
     typedef HashMap<unsigned, const Signature*> ExitMap;
 
   private:
-    struct SlowFunction
-    {
-        SlowFunction(PropertyName* name, unsigned ms, unsigned line, unsigned column)
-         : name(name), ms(ms), line(line), column(column)
-        {}
-
-        PropertyName* name;
-        unsigned ms;
-        unsigned line;
-        unsigned column;
-    };
-
-    typedef Vector<SlowFunction> SlowFunctionVector;
-
     ExclusiveContext *              cx_;
     AsmJSParser &                   parser_;
 
-    MacroAssembler                  masm_;
+    ModuleCompileResults *          compileData_;
 
     ScopedJSDeletePtr<AsmJSModule>& module_;
-    ModuleGlobals &                 moduleGlobals_;
-
-    ExitMap                        exits_;
-
-    NonAssertingLabel              stackOverflowLabel_;
-    NonAssertingLabel              asyncInterruptLabel_;
-    NonAssertingLabel              syncInterruptLabel_;
-    NonAssertingLabel              onDetachedLabel_;
-    NonAssertingLabel              onOutOfBoundsLabel_;
-    NonAssertingLabel              onConversionErrorLabel_;
-
-    int64_t                        usecBefore_;
-    SlowFunctionVector             slowFunctions_;
-
-    DebugOnly<bool>                finishedFunctionBodies_;
 
   public:
-    ModuleCompiler(ExclusiveContext* cx, AsmJSParser& parser, ScopedJSDeletePtr<AsmJSModule>& module,
-                   ModuleGlobals& moduleGlobals)
+    ModuleCompiler(ExclusiveContext* cx, ModuleCompileResults* compileData, AsmJSParser& parser,
+                   ScopedJSDeletePtr<AsmJSModule>& module)
       : cx_(cx),
         parser_(parser),
-        masm_(MacroAssembler::AsmJSToken()),
-        module_(module),
-        moduleGlobals_(moduleGlobals),
-        exits_(cx),
-        usecBefore_(PRMJ_Now()),
-        slowFunctions_(cx),
-        finishedFunctionBodies_(false)
+        compileData_(compileData),
+        module_(module)
     {}
 
-    bool init() {
-        return exits_.init();
-    }
-
     /*************************************************** Read-only interface */
 
     ExclusiveContext* cx() const { return cx_; }
     AsmJSParser& parser() const { return parser_; }
     TokenStream& tokenStream() const { return parser_.tokenStream; }
-    MacroAssembler& masm() { return masm_; }
-    Label& stackOverflowLabel() { return stackOverflowLabel_; }
-    Label& asyncInterruptLabel() { return asyncInterruptLabel_; }
-    Label& syncInterruptLabel() { return syncInterruptLabel_; }
-    Label& onDetachedLabel() { return onDetachedLabel_; }
-    Label& onOutOfBoundsLabel() { return onOutOfBoundsLabel_; }
-    Label& onConversionErrorLabel() { return onConversionErrorLabel_; }
+    MacroAssembler& masm()          { return compileData_->masm(); }
+    Label& stackOverflowLabel()     { return compileData_->stackOverflowLabel(); }
+    Label& asyncInterruptLabel()    { return compileData_->asyncInterruptLabel(); }
+    Label& syncInterruptLabel()     { return compileData_->syncInterruptLabel(); }
+    Label& onOutOfBoundsLabel()     { return compileData_->onOutOfBoundsLabel(); }
+    Label& onConversionErrorLabel() { return compileData_->onConversionErrorLabel(); }
+    int64_t usecBefore()            { return compileData_->usecBefore(); }
     const AsmJSModule& module() const { return *module_.get(); }
     bool usesSignalHandlersForInterrupt() const { return module_->usesSignalHandlersForInterrupt(); }
     bool usesSignalHandlersForOOB() const { return module_->usesSignalHandlersForOOB(); }
 
     uint32_t minHeapLength() const {
         return module_->minHeapLength();
     }
-    ExitMap::Range allExits() const {
-        return exits_.all();
-    }
-    ModuleGlobals::Func& function(unsigned i) const {
-        return *moduleGlobals_.function(i);
-    }
-
     /***************************************************** Mutable interface */
-    bool addExit(unsigned exitIndex, const Signature* signature)
-    {
-        ExitMap::AddPtr p = exits_.lookupForAdd(exitIndex);
-        if (p)
-            return true;
-        return exits_.add(p, exitIndex, signature);
-    }
-
-    bool finishGeneratingFunction(ModuleGlobals::Func& func, CodeGenerator& codegen,
+    bool finishGeneratingFunction(Func& func, CodeGenerator& codegen,
                                   const AsmJSFunctionLabels& labels)
     {
         uint32_t line, column;
         tokenStream().srcCoords.lineNumAndColumnIndex(func.srcBegin(), &line, &column);
 
         if (!module_->addFunctionCodeRange(func.name(), line, labels))
             return false;
 
         jit::IonScriptCounts* counts = codegen.extractScriptCounts();
         if (counts && !module_->addFunctionCounts(counts)) {
             js_delete(counts);
             return false;
         }
 
         if (func.compileTime() >= 250) {
-            SlowFunction sf(func.name(), func.compileTime(), line, column);
-            if (!slowFunctions_.append(sf))
+            ModuleCompileResults::SlowFunction sf(func.name(), func.compileTime(), line, column);
+            if (!compileData_->slowFunctions().append(Move(sf)))
                 return false;
         }
 
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
         unsigned begin = labels.begin.offset();
         unsigned end = labels.end.offset();
         if (!module_->addProfiledFunction(func.name(), begin, end, line, column))
             return false;
@@ -2570,160 +2694,18 @@ class MOZ_STACK_CLASS ModuleCompiler
             if (!module_->addProfiledBlocks(func.name(), begin, inlineEnd, end, ps.basicBlocks()))
                 return false;
         }
 # endif
 #endif
         return true;
     }
 
-    void startFunctionBodies() {
-        module_->startFunctionBodies();
-    }
-    void finishFunctionBodies() {
-        // When an interrupt is triggered, all function code is mprotected and,
-        // for sanity, stub code (particularly the interrupt stub) is not.
-        // Protection works at page granularity, so we need to ensure that no
-        // stub code gets into the function code pages.
-        MOZ_ASSERT(!finishedFunctionBodies_);
-        masm_.haltingAlign(AsmJSPageSize);
-        module_->finishFunctionBodies(masm_.currentOffset());
-        finishedFunctionBodies_ = true;
-    }
-
-    bool finishGeneratingEntry(unsigned exportIndex, Label* begin) {
-        MOZ_ASSERT(finishedFunctionBodies_);
-        module_->exportedFunction(exportIndex).initCodeOffset(begin->offset());
-        uint32_t end = masm_.currentOffset();
-        return module_->addCodeRange(AsmJSModule::CodeRange::Entry, begin->offset(), end);
-    }
-    bool finishGeneratingInterpExit(unsigned exitIndex, Label* begin, Label* profilingReturn) {
-        MOZ_ASSERT(finishedFunctionBodies_);
-        uint32_t beg = begin->offset();
-        module_->exit(exitIndex).initInterpOffset(beg);
-        uint32_t pret = profilingReturn->offset();
-        uint32_t end = masm_.currentOffset();
-        return module_->addCodeRange(AsmJSModule::CodeRange::SlowFFI, beg, pret, end);
-    }
-    bool finishGeneratingJitExit(unsigned exitIndex, Label* begin, Label* profilingReturn) {
-        MOZ_ASSERT(finishedFunctionBodies_);
-        uint32_t beg = begin->offset();
-        module_->exit(exitIndex).initJitOffset(beg);
-        uint32_t pret = profilingReturn->offset();
-        uint32_t end = masm_.currentOffset();
-        return module_->addCodeRange(AsmJSModule::CodeRange::JitFFI, beg, pret, end);
-    }
-    bool finishGeneratingInterrupt(Label* begin, Label* profilingReturn) {
-        MOZ_ASSERT(finishedFunctionBodies_);
-        uint32_t beg = begin->offset();
-        uint32_t pret = profilingReturn->offset();
-        uint32_t end = masm_.currentOffset();
-        return module_->addCodeRange(AsmJSModule::CodeRange::Interrupt, beg, pret, end);
-    }
-    bool finishGeneratingInlineStub(Label* begin) {
-        MOZ_ASSERT(finishedFunctionBodies_);
-        uint32_t end = masm_.currentOffset();
-        return module_->addCodeRange(AsmJSModule::CodeRange::Inline, begin->offset(), end);
-    }
-    bool finishGeneratingBuiltinThunk(AsmJSExit::BuiltinKind builtin, Label* begin, Label* pret) {
-        MOZ_ASSERT(finishedFunctionBodies_);
-        uint32_t end = masm_.currentOffset();
-        return module_->addBuiltinThunkCodeRange(builtin, begin->offset(), pret->offset(), end);
-    }
-
-    void buildCompilationTimeReport(JS::AsmJSCacheResult cacheResult, ScopedJSFreePtr<char>* out) {
-        ScopedJSFreePtr<char> slowFuns;
-#ifndef JS_MORE_DETERMINISTIC
-        int64_t usecAfter = PRMJ_Now();
-        int msTotal = (usecAfter - usecBefore_) / PRMJ_USEC_PER_MSEC;
-        if (!slowFunctions_.empty()) {
-            slowFuns.reset(JS_smprintf("; %d functions compiled slowly: ", slowFunctions_.length()));
-            if (!slowFuns)
-                return;
-            for (unsigned i = 0; i < slowFunctions_.length(); i++) {
-                SlowFunction& func = slowFunctions_[i];
-                JSAutoByteString name;
-                if (!AtomToPrintableString(cx_, func.name, &name))
-                    return;
-                slowFuns.reset(JS_smprintf("%s%s:%u:%u (%ums)%s", slowFuns.get(),
-                                           name.ptr(), func.line, func.column, func.ms,
-                                           i+1 < slowFunctions_.length() ? ", " : ""));
-                if (!slowFuns)
-                    return;
-            }
-        }
-        const char* cacheString = "";
-        switch (cacheResult) {
-          case JS::AsmJSCache_Success:
-            cacheString = "stored in cache";
-            break;
-          case JS::AsmJSCache_ModuleTooSmall:
-            cacheString = "not stored in cache (too small to benefit)";
-            break;
-          case JS::AsmJSCache_SynchronousScript:
-            cacheString = "unable to cache asm.js in synchronous scripts; try loading "
-                          "asm.js via <script async> or createElement('script')";
-            break;
-          case JS::AsmJSCache_QuotaExceeded:
-            cacheString = "not enough temporary storage quota to store in cache";
-            break;
-          case JS::AsmJSCache_StorageInitFailure:
-            cacheString = "storage initialization failed (consider filing a bug)";
-            break;
-          case JS::AsmJSCache_Disabled_Internal:
-            cacheString = "caching disabled by internal configuration (consider filing a bug)";
-            break;
-          case JS::AsmJSCache_Disabled_ShellFlags:
-            cacheString = "caching disabled by missing command-line arguments";
-            break;
-          case JS::AsmJSCache_Disabled_JitInspector:
-            cacheString = "caching disabled by active JIT inspector";
-            break;
-          case JS::AsmJSCache_InternalError:
-            cacheString = "unable to store in cache due to internal error (consider filing a bug)";
-            break;
-          case JS::AsmJSCache_LIMIT:
-            MOZ_CRASH("bad AsmJSCacheResult");
-            break;
-        }
-        out->reset(JS_smprintf("total compilation time %dms; %s%s",
-                               msTotal, cacheString, slowFuns ? slowFuns.get() : ""));
-#endif
-    }
-
-    bool finish(ScopedJSDeletePtr<AsmJSModule>* module)
-    {
-        masm_.finish();
-        if (masm_.oom())
-            return false;
-
-        if (!module_->finish(cx_, tokenStream(), masm_, asyncInterruptLabel_, onOutOfBoundsLabel_))
-            return false;
-
-        // Finally, convert all the function-pointer table elements into
-        // RelativeLinks that will be patched by AsmJSModule::staticallyLink.
-        for (unsigned tableIndex = 0; tableIndex < moduleGlobals_.numFuncPtrTables(); tableIndex++) {
-            ModuleGlobals::FuncPtrTable& table = moduleGlobals_.funcPtrTable(tableIndex);
-            unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset();
-            for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) {
-                AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::RawPointer);
-                link.patchAtOffset = tableBaseOffset + elemIndex * sizeof(uint8_t*);
-                link.targetOffset = masm_.actualOffset(table.elem(elemIndex).entry().offset());
-                if (!module_->addRelativeLink(link))
-                    return false;
-            }
-        }
-
-        *module = module_.forget();
-        return true;
-    }
 };
 
-} /* anonymous namespace */
-
 /*****************************************************************************/
 // Numeric literal utilities
 
 static bool
 IsNumericNonFloatLiteral(ParseNode* pn)
 {
     // Note: '-' is never rolled into the number; numbers are always positive
     // and negations must be applied manually.
@@ -6396,19 +6378,19 @@ CheckSignatureAgainstExisting(ModuleVali
     }
 
     MOZ_ASSERT(sig == existing);
     return true;
 }
 
 static bool
 CheckFunctionSignature(ModuleValidator& m, ParseNode* usepn, Signature&& sig, PropertyName* name,
-                       ModuleGlobals::Func** func)
-{
-    ModuleGlobals::Func* existing = m.lookupFunction(name);
+                       Func** func)
+{
+    Func* existing = m.lookupFunction(name);
     if (!existing) {
         if (!CheckModuleLevelName(m, usepn, name))
             return false;
         return m.addFunction(name, Move(sig), func);
     }
 
     if (!CheckSignatureAgainstExisting(m, usepn, sig, existing->sig()))
         return false;
@@ -6449,17 +6431,17 @@ CheckInternalCall(FunctionBuilder& f, Pa
     size_t signatureAt = f.tempPtr();
     // Call node position (asm.js specific)
     f.writeU32(callNode->pn_pos.begin);
 
     Signature signature(f.m().lifo(), retType);
     if (!CheckCallArgs(f, callNode, CheckIsVarType, signature))
         return false;
 
-    ModuleGlobals::Func* callee;
+    Func* callee;
     if (!CheckFunctionSignature(f.m(), callNode, Move(signature), calleeName, &callee))
         return false;
 
     f.patchPtr(entryAt, (uint8_t*) &callee->entry());
     f.patchPtr(signatureAt, (uint8_t*) &callee->sig());
     *type = retType.toType();
     return true;
 }
@@ -6479,23 +6461,23 @@ EmitInternalCall(FunctionCompiler& f, Re
         return false;
 
     return f.internalCall(sig, entry, call, def);
 }
 
 static bool
 CheckFuncPtrTableAgainstExisting(ModuleValidator& m, ParseNode* usepn,
                                  PropertyName* name, Signature&& sig, unsigned mask,
-                                 ModuleGlobals::FuncPtrTable** tableOut)
+                                 FuncPtrTable** tableOut)
 {
     if (const ModuleValidator::Global* existing = m.lookupGlobal(name)) {
         if (existing->which() != ModuleValidator::Global::FuncPtrTable)
             return m.failName(usepn, "'%s' is not a function-pointer table", name);
 
-        ModuleGlobals::FuncPtrTable& table = m.funcPtrTable(existing->funcPtrTableIndex());
+        FuncPtrTable& table = m.funcPtrTable(existing->funcPtrTableIndex());
         if (mask != table.mask())
             return m.failf(usepn, "mask does not match previous value (%u)", table.mask());
 
         if (!CheckSignatureAgainstExisting(m, usepn, sig, table.sig()))
             return false;
 
         *tableOut = &table;
         return true;
@@ -6563,17 +6545,17 @@ CheckFuncPtrCall(FunctionBuilder& f, Par
 
     if (!indexType.isIntish())
         return f.failf(indexNode, "%s is not a subtype of intish", indexType.toChars());
 
     Signature sig(f.m().lifo(), retType);
     if (!CheckCallArgs(f, callNode, CheckIsVarType, sig))
         return false;
 
-    ModuleGlobals::FuncPtrTable* table;
+    FuncPtrTable* table;
     if (!CheckFuncPtrTableAgainstExisting(f.m(), tableNode, name, Move(sig), mask, &table))
         return false;
 
     f.patch32(globalDataOffsetAt, table->globalDataOffset());
     f.patchPtr(signatureAt, (uint8_t*) &table->sig());
 
     *type = retType.toType();
     return true;
@@ -6666,19 +6648,16 @@ EmitFFICall(FunctionCompiler& f, RetType
     const Signature& sig = *reinterpret_cast<Signature*>(signaturePtr);
     MOZ_ASSERT_IF(sig.retType() != RetType::Void, sig.retType() == retType);
 
     uint32_t callNodePosition = f.readU32();
     FunctionCompiler::Call call(f, callNodePosition);
     if (!EmitCallArgs(f, sig, &call))
         return false;
 
-    if (!f.m().addExit(exitIndex, &sig))
-        return false;
-
     return f.ffiCall(exitIndex, call, retType.toMIRType(), def);
 }
 
 static bool
 CheckFloatCoercionArg(FunctionBuilder& f, ParseNode* inputNode, Type inputType,
                       size_t opcodeAt)
 {
     if (inputType.isMaybeDouble()) {
@@ -10366,17 +10345,17 @@ EmitMIR(ModuleCompiler& m, const AsmFunc
 
     jit::SpewBeginFunction(mir, nullptr);
 
     f.checkPostconditions();
     return mir;
 }
 
 static bool
-CheckFunction(ModuleValidator& m, LifoAlloc& lifo, ModuleGlobals::Func** funcOut)
+CheckFunction(ModuleValidator& m, LifoAlloc& lifo, Func** funcOut)
 {
     int64_t before = PRMJ_Now();
 
     // asm.js modules can be quite large when represented as parse trees so pop
     // the backing LifoAlloc after parsing/compiling each function.
     AsmJSParser::Mark mark = m.parser().mark();
 
     ParseNode* fn = nullptr;  // initialize to silence GCC warning
@@ -10420,17 +10399,17 @@ CheckFunction(ModuleValidator& m, LifoAl
         if (!IsEmptyStatement(stmtIter))
             lastNonEmptyStmt = stmtIter;
     }
 
     if (!CheckFinalReturn(f, lastNonEmptyStmt))
         return false;
 
     Signature sig(Move(argTypes), f.returnedType());
-    ModuleGlobals::Func* func = nullptr;
+    Func* func = nullptr;
     if (!CheckFunctionSignature(m, fn, Move(sig), FunctionName(fn), &func))
         return false;
 
     if (func->defined())
         return m.failName(fn, "function '%s' already defined", FunctionName(fn));
 
     asmFunc->setNumLocals(f.numLocals());
     func->define(fn, asmFunc);
@@ -10439,31 +10418,31 @@ CheckFunction(ModuleValidator& m, LifoAl
 
     m.parser().release(mark);
 
     *funcOut = func;
     return true;
 }
 
 static bool
-GenerateMIR(ModuleCompiler& mc, LifoAlloc& lifo, ModuleGlobals::Func* func,
+GenerateMIR(ModuleCompiler& mc, LifoAlloc& lifo, Func* func,
             MIRGenerator** mir)
 {
     int64_t before = PRMJ_Now();
 
     *mir = EmitMIR(mc, *func->bytecode(), lifo, func->sig().args());
     if (!*mir)
         return false;
 
     func->accumulateCompileTime((PRMJ_Now() - before) / PRMJ_USEC_PER_MSEC);
     return true;
 }
 
 static bool
-GenerateCode(ModuleCompiler& m, ModuleGlobals::Func& func, MIRGenerator& mir, LIRGraph& lir)
+GenerateCode(ModuleCompiler& m, Func& func, MIRGenerator& mir, LIRGraph& lir)
 {
     int64_t before = PRMJ_Now();
 
     // A single MacroAssembler is reused for all function compilations so
     // that there is a single linear code segment for each module. To avoid
     // spiking memory, a LifoAllocScope in the caller frees all MIR/LIR
     // after each function is compiled. This method is responsible for cleaning
     // out any dangling pointers that the MacroAssembler may have kept.
@@ -10488,17 +10467,17 @@ GenerateCode(ModuleCompiler& m, ModuleGl
     // CodeGenerator so we can destroy it now (via ScopedJSDeletePtr).
     return true;
 }
 
 static bool
 CheckAllFunctionsDefined(ModuleValidator& m)
 {
     for (unsigned i = 0; i < m.numFunctions(); i++) {
-        if (!m.function(i).entry().bound())
+        if (!m.function(i).defined())
             return m.failName(nullptr, "missing definition of function %s", m.function(i).name());
     }
 
     return true;
 }
 
 static bool
 CheckFunctionsSequential(ModuleValidator& m, ModuleCompiler& mc)
@@ -10512,17 +10491,17 @@ CheckFunctionsSequential(ModuleValidator
         TokenKind tk;
         if (!PeekToken(m.parser(), &tk))
             return false;
         if (tk != TOK_FUNCTION)
             break;
 
         LifoAllocScope scope(&lifo);
 
-        ModuleGlobals::Func* func;
+        Func* func;
         if (!CheckFunction(m, lifo, &func))
             return false;
 
         // In the case of the change-heap function, no function is produced.
         if (!func)
             continue;
 
         MIRGenerator* mir;
@@ -10625,17 +10604,17 @@ GetFinishedCompilation(ModuleCompiler& m
 static bool
 GetUsedTask(ModuleCompiler& m, ParallelGroupState& group, AsmJSParallelTask** outTask)
 {
     // Block until a used LifoAlloc becomes available.
     AsmJSParallelTask* task = GetFinishedCompilation(m, group);
     if (!task)
         return false;
 
-    ModuleGlobals::Func& func = *reinterpret_cast<ModuleGlobals::Func*>(task->func);
+    Func& func = *reinterpret_cast<Func*>(task->func);
     func.accumulateCompileTime(task->compileTime);
 
     {
         // Perform code generation on the main thread.
         JitContext jitContext(m.cx(), &task->mir->alloc());
         if (!GenerateCode(m, func, *task->mir, *task->lir))
             return false;
     }
@@ -10681,17 +10660,17 @@ CheckFunctionsParallel(ModuleValidator& 
         if (!PeekToken(m.parser(), &tk))
             return false;
         if (tk != TOK_FUNCTION)
             break;
 
         if (!task && !GetUnusedTask(group, i, &task) && !GetUsedTask(mc, group, &task))
             return false;
 
-        ModuleGlobals::Func* func;
+        Func* func;
         if (!CheckFunction(m, task->lifo, &func))
             return false;
 
         // In the case of the change-heap function, no function is produced.
         if (!func)
             continue;
 
         // Generate MIR into the LifoAlloc on the main thread.
@@ -10802,17 +10781,17 @@ CheckFunctions(ModuleValidator& m, Modul
 
     // With compilation memory in-scope, dispatch helper threads.
     ParallelGroupState group(tasks);
     if (!CheckFunctionsParallel(m, mc, group)) {
         CancelOutstandingJobs(group);
 
         // If failure was triggered by a helper thread, report error.
         if (void* maybeFunc = HelperThreadState().maybeAsmJSFailedFunction()) {
-            ModuleGlobals::Func* func = reinterpret_cast<ModuleGlobals::Func*>(maybeFunc);
+            Func* func = reinterpret_cast<Func*>(maybeFunc);
             return m.failOffset(func->srcBegin(), "allocation failure during compilation");
         }
 
         // Otherwise, the error occurred on the main thread and was already reported.
         return false;
     }
     return true;
 }
@@ -10829,25 +10808,25 @@ CheckFuncPtrTable(ModuleValidator& m, Pa
 
     unsigned length = ListLength(arrayLiteral);
 
     if (!IsPowerOfTwo(length))
         return m.failf(arrayLiteral, "function-pointer table length must be a power of 2 (is %u)", length);
 
     unsigned mask = length - 1;
 
-    ModuleGlobals::FuncPtrVector elems(m.cx());
+    FuncPtrVector elems(m.cx());
     const Signature* firstSig = nullptr;
 
     for (ParseNode* elem = ListHead(arrayLiteral); elem; elem = NextNode(elem)) {
         if (!elem->isKind(PNK_NAME))
             return m.fail(elem, "function-pointer table's elements must be names of functions");
 
         PropertyName* funcName = elem->name();
-        const ModuleGlobals::Func* func = m.lookupFunction(funcName);
+        const Func* func = m.lookupFunction(funcName);
         if (!func)
             return m.fail(elem, "function-pointer table's elements must be names of functions");
 
         if (firstSig) {
             if (*firstSig != func->sig())
                 return m.fail(elem, "all functions in table must have same signature");
         } else {
             firstSig = &func->sig();
@@ -10856,17 +10835,17 @@ CheckFuncPtrTable(ModuleValidator& m, Pa
         if (!elems.append(func))
             return false;
     }
 
     Signature sig(m.lifo());
     if (!sig.copy(*firstSig))
         return false;
 
-    ModuleGlobals::FuncPtrTable* table;
+    FuncPtrTable* table;
     if (!CheckFuncPtrTableAgainstExisting(m, var, var->name(), Move(sig), mask, &table))
         return false;
 
     table->initElems(Move(elems));
     return true;
 }
 
 static bool
@@ -11026,17 +11005,17 @@ static const unsigned FramePushedAfterSa
 static const unsigned FramePushedAfterSave = 0;
 #else
 static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t)
                                            + NonVolatileRegs.fpus().getPushSizeInBytes();
 #endif
 static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void*);
 
 static bool
-GenerateEntry(ModuleCompiler& m, unsigned exportIndex)
+GenerateEntry(ModuleValidator& m, unsigned exportIndex)
 {
     MacroAssembler& masm = m.masm();
 
     Label begin;
     masm.haltingAlign(CodeAlignment);
     masm.bind(&begin);
 
     // Save the return address if it wasn't already saved by the call insn.
@@ -11090,17 +11069,17 @@ GenerateEntry(ModuleCompiler& m, unsigne
 
     // Dynamically align the stack since ABIStackAlignment is not necessarily
     // AsmJSStackAlignment. We'll use entrySP to recover the original stack
     // pointer on return.
     masm.andToStackPtr(Imm32(~(AsmJSStackAlignment - 1)));
 
     // Bump the stack for the call.
     uint32_t funcIndex = m.module().exportedFunction(exportIndex).funcIndex();
-    const ModuleGlobals::Func& func = m.function(funcIndex);
+    const Func& func = m.function(funcIndex);
     masm.reserveStack(AlignBytes(StackArgBytes(func.sig().args()), AsmJSStackAlignment));
 
     // Copy parameters out of argv and into the registers/stack-slots specified by
     // the system ABI.
     for (ABIArgTypeIter iter(func.sig().args()); !iter.done(); iter++) {
         unsigned argOffset = iter.index() * sizeof(AsmJSModule::EntryArg);
         Address src(argv, argOffset);
         MIRType type = iter.mirType();
@@ -11203,17 +11182,17 @@ GenerateEntry(ModuleCompiler& m, unsigne
 
     masm.move32(Imm32(true), ReturnReg);
     masm.ret();
 
     return !masm.oom() && m.finishGeneratingEntry(exportIndex, &begin);
 }
 
 static void
-FillArgumentArray(ModuleCompiler& m, const VarTypeVector& argTypes,
+FillArgumentArray(ModuleValidator& m, const VarTypeVector& argTypes,
                   unsigned offsetToArgs, unsigned offsetToCallerStackArgs,
                   Register scratch)
 {
     MacroAssembler& masm = m.masm();
 
     for (ABIArgTypeIter i(argTypes); !i.done(); i++) {
         Address dstAddr(masm.getStackPointer(), offsetToArgs + i.index() * sizeof(Value));
         switch (i->kind()) {
@@ -11245,17 +11224,17 @@ FillArgumentArray(ModuleCompiler& m, con
     }
 }
 
 // If an FFI detaches its heap (viz., via ArrayBuffer.transfer), it must
 // call change-heap to another heap (viz., the new heap returned by transfer)
 // before returning to asm.js code. If the application fails to do this (if the
 // heap pointer is null), jump to a stub.
 static void
-GenerateCheckForHeapDetachment(ModuleCompiler& m, Register scratch)
+GenerateCheckForHeapDetachment(ModuleValidator& m, Register scratch)
 {
     if (!m.module().hasArrayView())
         return;
 
     MacroAssembler& masm = m.masm();
     MOZ_ASSERT(int(masm.framePushed()) >= int(ShadowStackSpace));
     AssertStackAlignment(masm, ABIStackAlignment);
 #if defined(JS_CODEGEN_X86)
@@ -11263,17 +11242,17 @@ GenerateCheckForHeapDetachment(ModuleCom
     masm.append(AsmJSGlobalAccess(label, AsmJSHeapGlobalDataOffset));
     masm.branchTestPtr(Assembler::Zero, scratch, scratch, &m.onDetachedLabel());
 #else
     masm.branchTestPtr(Assembler::Zero, HeapReg, HeapReg, &m.onDetachedLabel());
 #endif
 }
 
 static bool
-GenerateFFIInterpExit(ModuleCompiler& m, const Signature* sig, unsigned exitIndex,
+GenerateFFIInterpExit(ModuleValidator& m, const Signature& sig, unsigned exitIndex,
                       Label* throwLabel)
 {
     MacroAssembler& masm = m.masm();
     MOZ_ASSERT(masm.framePushed() == 0);
 
     // Argument types for InvokeFromAsmJS_*:
     static const MIRType typeArray[] = { MIRType_Pointer,   // exitDatum
                                          MIRType_Int32,     // argc
@@ -11282,39 +11261,39 @@ GenerateFFIInterpExit(ModuleCompiler& m,
     if (!invokeArgTypes.append(typeArray, ArrayLength(typeArray)))
         return false;
 
     // At the point of the call, the stack layout shall be (sp grows to the left):
     //   | stack args | padding | Value argv[] | padding | retaddr | caller stack args |
     // The padding between stack args and argv ensures that argv is aligned. The
     // padding between argv and retaddr ensures that sp is aligned.
     unsigned offsetToArgv = AlignBytes(StackArgBytes(invokeArgTypes), sizeof(double));
-    unsigned argvBytes = Max<size_t>(1, sig->args().length()) * sizeof(Value);
+    unsigned argvBytes = Max<size_t>(1, sig.args().length()) * sizeof(Value);
     unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, offsetToArgv + argvBytes);
 
     Label begin;
     GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::SlowFFI, &begin);
 
     // Fill the argument array.
     unsigned offsetToCallerStackArgs = sizeof(AsmJSFrame) + masm.framePushed();
     Register scratch = ABIArgGenerator::NonArgReturnReg0;
-    FillArgumentArray(m, sig->args(), offsetToArgv, offsetToCallerStackArgs, scratch);
+    FillArgumentArray(m, sig.args(), offsetToArgv, offsetToCallerStackArgs, scratch);
 
     // Prepare the arguments for the call to InvokeFromAsmJS_*.
     ABIArgMIRTypeIter i(invokeArgTypes);
 
     // argument 0: exitIndex
     if (i->kind() == ABIArg::GPR)
         masm.mov(ImmWord(exitIndex), i->gpr());
     else
         masm.store32(Imm32(exitIndex), Address(masm.getStackPointer(), i->offsetFromArgBase()));
     i++;
 
     // argument 1: argc
-    unsigned argc = sig->args().length();
+    unsigned argc = sig.args().length();
     if (i->kind() == ABIArg::GPR)
         masm.mov(ImmWord(argc), i->gpr());
     else
         masm.store32(Imm32(argc), Address(masm.getStackPointer(), i->offsetFromArgBase()));
     i++;
 
     // argument 2: argv
     Address argv(masm.getStackPointer(), offsetToArgv);
@@ -11324,17 +11303,17 @@ GenerateFFIInterpExit(ModuleCompiler& m,
         masm.computeEffectiveAddress(argv, scratch);
         masm.storePtr(scratch, Address(masm.getStackPointer(), i->offsetFromArgBase()));
     }
     i++;
     MOZ_ASSERT(i.done());
 
     // Make the call, test whether it succeeded, and extract the return value.
     AssertStackAlignment(masm, ABIStackAlignment);
-    switch (sig->retType().which()) {
+    switch (sig.retType().which()) {
       case RetType::Void:
         masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_Ignore));
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         break;
       case RetType::Signed:
         masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToInt32));
         masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
         masm.unboxInt32(argv, ReturnReg);
@@ -11363,30 +11342,30 @@ GenerateFFIInterpExit(ModuleCompiler& m,
 
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32)
 static const unsigned MaybeSavedGlobalReg = sizeof(void*);
 #else
 static const unsigned MaybeSavedGlobalReg = 0;
 #endif
 
 static bool
-GenerateFFIIonExit(ModuleCompiler& m, const Signature* sig, unsigned exitIndex,
+GenerateFFIIonExit(ModuleValidator& m, const Signature& sig, unsigned exitIndex,
                    Label* throwLabel)
 {
     MacroAssembler& masm = m.masm();
     MOZ_ASSERT(masm.framePushed() == 0);
 
     // Ion calls use the following stack layout (sp grows to the left):
     //   | retaddr | descriptor | callee | argc | this | arg1..N |
     // After the Ion frame, the global register (if present) is saved since Ion
     // does not preserve non-volatile regs. Also, unlike most ABIs, Ion requires
     // that sp be JitStackAlignment-aligned *after* pushing the return address.
     static_assert(AsmJSStackAlignment >= JitStackAlignment, "subsumes");
     unsigned sizeOfRetAddr = sizeof(void*);
-    unsigned ionFrameBytes = 3 * sizeof(void*) + (1 + sig->args().length()) * sizeof(Value);
+    unsigned ionFrameBytes = 3 * sizeof(void*) + (1 + sig.args().length()) * sizeof(Value);
     unsigned totalIonBytes = sizeOfRetAddr + ionFrameBytes + MaybeSavedGlobalReg;
     unsigned ionFramePushed = StackDecrementForCall(masm, JitStackAlignment, totalIonBytes) -
                               sizeOfRetAddr;
 
     Label begin;
     GenerateAsmJSExitPrologue(masm, ionFramePushed, AsmJSExit::JitFFI, &begin);
 
     // 1. Descriptor
@@ -11416,28 +11395,28 @@ GenerateFFIIonExit(ModuleCompiler& m, co
     masm.storePtr(callee, Address(masm.getStackPointer(), argOffset));
     argOffset += sizeof(size_t);
 
     // 2.4. Load callee executable entry point
     masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
     masm.loadBaselineOrIonNoArgCheck(callee, callee, nullptr);
 
     // 3. Argc
-    unsigned argc = sig->args().length();
+    unsigned argc = sig.args().length();
     masm.storePtr(ImmWord(uintptr_t(argc)), Address(masm.getStackPointer(), argOffset));
     argOffset += sizeof(size_t);
 
     // 4. |this| value
     masm.storeValue(UndefinedValue(), Address(masm.getStackPointer(), argOffset));
     argOffset += sizeof(Value);
 
     // 5. Fill the arguments
     unsigned offsetToCallerStackArgs = ionFramePushed + sizeof(AsmJSFrame);
-    FillArgumentArray(m, sig->args(), argOffset, offsetToCallerStackArgs, scratch);
-    argOffset += sig->args().length() * sizeof(Value);
+    FillArgumentArray(m, sig.args(), argOffset, offsetToCallerStackArgs, scratch);
+    argOffset += sig.args().length() * sizeof(Value);
     MOZ_ASSERT(argOffset == ionFrameBytes);
 
     // 6. Jit code will clobber all registers, even non-volatiles. GlobalReg and
     //    HeapReg are removed from the general register set for asm.js code, so
     //    these will not have been saved by the caller like all other registers,
     //    so they must be explicitly preserved. Only save GlobalReg since
     //    HeapReg must be reloaded (from global data) after the call since the
     //    heap may change during the FFI call.
@@ -11569,17 +11548,17 @@ GenerateFFIIonExit(ModuleCompiler& m, co
     static_assert(ABIStackAlignment <= JitStackAlignment, "subsumes");
     masm.reserveStack(sizeOfRetAddr);
     unsigned nativeFramePushed = masm.framePushed();
     AssertStackAlignment(masm, ABIStackAlignment);
 
     masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel);
 
     Label oolConvert;
-    switch (sig->retType().which()) {
+    switch (sig.retType().which()) {
       case RetType::Void:
         break;
       case RetType::Signed:
         masm.convertValueToInt32(JSReturnOperand, ReturnDoubleReg, ReturnReg, &oolConvert,
                                  /* -0 check */ false);
         break;
       case RetType::Double:
         masm.convertValueToDouble(JSReturnOperand, ReturnDoubleReg, &oolConvert);
@@ -11626,17 +11605,17 @@ GenerateFFIIonExit(ModuleCompiler& m, co
             masm.computeEffectiveAddress(argv, scratch);
             masm.storePtr(scratch, Address(masm.getStackPointer(), i->offsetFromArgBase()));
         }
         i++;
         MOZ_ASSERT(i.done());
 
         // Call coercion function
         AssertStackAlignment(masm, ABIStackAlignment);
-        switch (sig->retType().which()) {
+        switch (sig.retType().which()) {
           case RetType::Signed:
             masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToInt32));
             masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
             masm.unboxInt32(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnReg);
             break;
           case RetType::Double:
             masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToNumber));
             masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
@@ -11652,17 +11631,17 @@ GenerateFFIIonExit(ModuleCompiler& m, co
 
     MOZ_ASSERT(masm.framePushed() == 0);
 
     return !masm.oom() && m.finishGeneratingJitExit(exitIndex, &begin, &profilingReturn);
 }
 
 // See "asm.js FFI calls" comment above.
 static bool
-GenerateFFIExits(ModuleCompiler& m, unsigned exitIndex, const Signature* signature,
+GenerateFFIExits(ModuleValidator& m, unsigned exitIndex, const Signature& signature,
                  Label* throwLabel)
 {
     // Generate the slow path through the interpreter
     if (!GenerateFFIInterpExit(m, signature, exitIndex, throwLabel))
         return false;
 
     // Generate the fast path
     if (!GenerateFFIIonExit(m, signature, exitIndex, throwLabel))
@@ -11677,17 +11656,17 @@ GenerateFFIExits(ModuleCompiler& m, unsi
 // pushes an AsmJSFrame on the stack, that means we must rebuild the stack
 // frame. Fortunately, these are low arity functions and everything is passed in
 // regs on everything but x86 anyhow.
 //
 // NB: Since this thunk is being injected at system ABI callsites, it must
 //     preserve the argument registers (going in) and the return register
 //     (coming out) and preserve non-volatile registers.
 static bool
-GenerateBuiltinThunk(ModuleCompiler& m, AsmJSExit::BuiltinKind builtin)
+GenerateBuiltinThunk(ModuleValidator& m, AsmJSExit::BuiltinKind builtin)
 {
     MacroAssembler& masm = m.masm();
     MOZ_ASSERT(masm.framePushed() == 0);
 
     MIRTypeVector argTypes(m.cx());
     if (!argTypes.reserve(5))
         return false;
 
@@ -11773,39 +11752,39 @@ GenerateBuiltinThunk(ModuleCompiler& m, 
     masm.call(BuiltinToImmKind(builtin));
 
     Label profilingReturn;
     GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::Builtin(builtin), &profilingReturn);
     return !masm.oom() && m.finishGeneratingBuiltinThunk(builtin, &begin, &profilingReturn);
 }
 
 static bool
-GenerateStackOverflowExit(ModuleCompiler& m, Label* throwLabel)
+GenerateStackOverflowExit(ModuleValidator& m, Label* throwLabel)
 {
     MacroAssembler& masm = m.masm();
     GenerateAsmJSStackOverflowExit(masm, &m.stackOverflowLabel(), throwLabel);
     return !masm.oom() && m.finishGeneratingInlineStub(&m.stackOverflowLabel());
 }
 
 static bool
-GenerateOnDetachedLabelExit(ModuleCompiler& m, Label* throwLabel)
+GenerateOnDetachedLabelExit(ModuleValidator& m, Label* throwLabel)
 {
     MacroAssembler& masm = m.masm();
     masm.bind(&m.onDetachedLabel());
     masm.assertStackAlignment(ABIStackAlignment);
 
     // For now, OnDetached always throws (see OnDetached comment).
     masm.call(AsmJSImmPtr(AsmJSImm_OnDetached));
     masm.jump(throwLabel);
 
     return !masm.oom() && m.finishGeneratingInlineStub(&m.onDetachedLabel());
 }
 
 static bool
-GenerateExceptionLabelExit(ModuleCompiler& m, Label* throwLabel, Label* exit, AsmJSImmKind func)
+GenerateExceptionLabelExit(ModuleValidator& m, Label* throwLabel, Label* exit, AsmJSImmKind func)
 {
     MacroAssembler& masm = m.masm();
     masm.bind(exit);
 
     // sp can be anything at this point, so ensure it is aligned when calling
     // into C++.  We unconditionally jump to throw so don't worry about restoring sp.
     masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
 
@@ -11826,17 +11805,17 @@ static const LiveRegisterSet AllRegsExce
 // code. That means we must first save *all* registers and restore *all*
 // registers (except the stack pointer) when we resume. The address to resume to
 // (assuming that js::HandleExecutionInterrupt doesn't indicate that the
 // execution should be aborted) is stored in AsmJSActivation::resumePC_.
 // Unfortunately, loading this requires a scratch register which we don't have
 // after restoring all registers. To hack around this, push the resumePC on the
 // stack so that it can be popped directly into PC.
 static bool
-GenerateAsyncInterruptExit(ModuleCompiler& m, Label* throwLabel)
+GenerateAsyncInterruptExit(ModuleValidator& m, Label* throwLabel)
 {
     MacroAssembler& masm = m.masm();
     masm.haltingAlign(CodeAlignment);
     masm.bind(&m.asyncInterruptLabel());
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     // Be very careful here not to perturb the machine state before saving it
     // to the stack. In particular, add/sub instructions may set conditions in
@@ -11979,17 +11958,17 @@ GenerateAsyncInterruptExit(ModuleCompile
 #else
 # error "Unknown architecture!"
 #endif
 
     return !masm.oom() && m.finishGeneratingInlineStub(&m.asyncInterruptLabel());
 }
 
 static bool
-GenerateSyncInterruptExit(ModuleCompiler& m, Label* throwLabel)
+GenerateSyncInterruptExit(ModuleValidator& m, Label* throwLabel)
 {
     MacroAssembler& masm = m.masm();
     masm.setFramePushed(0);
 
     unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, ShadowStackSpace);
 
     GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::Interrupt, &m.syncInterruptLabel());
 
@@ -12003,17 +11982,17 @@ GenerateSyncInterruptExit(ModuleCompiler
 }
 
 // If an exception is thrown, simply pop all frames (since asm.js does not
 // contain try/catch). To do this:
 //  1. Restore 'sp' to it's value right after the PushRegsInMask in GenerateEntry.
 //  2. PopRegsInMask to restore the caller's non-volatile registers.
 //  3. Return (to CallAsmJS).
 static bool
-GenerateThrowStub(ModuleCompiler& m, Label* throwLabel)
+GenerateThrowStub(ModuleValidator& m, Label* throwLabel)
 {
     MacroAssembler& masm = m.masm();
     masm.haltingAlign(CodeAlignment);
     masm.bind(throwLabel);
 
     // We are about to pop all frames in this AsmJSActivation. Set fp to null to
     // maintain the invariant that fp is either null or pointing to a valid
     // frame.
@@ -12029,29 +12008,29 @@ GenerateThrowStub(ModuleCompiler& m, Lab
 
     masm.mov(ImmWord(0), ReturnReg);
     masm.ret();
 
     return !masm.oom() && m.finishGeneratingInlineStub(throwLabel);
 }
 
 static bool
-GenerateStubs(ModuleCompiler& m)
+GenerateStubs(ModuleValidator& m)
 {
     for (unsigned i = 0; i < m.module().numExportedFunctions(); i++) {
         if (m.module().exportedFunction(i).isChangeHeap())
             continue;
         if (!GenerateEntry(m, i))
            return false;
     }
 
     Label throwLabel;
 
-    for (ModuleCompiler::ExitMap::Range r = m.allExits(); !r.empty(); r.popFront()) {
-        if (!GenerateFFIExits(m, r.front().key(), r.front().value(), &throwLabel))
+    for (ModuleValidator::ExitMap::Range r = m.allExits(); !r.empty(); r.popFront()) {
+        if (!GenerateFFIExits(m, r.front().value(), r.front().key().sig(), &throwLabel))
             return false;
     }
 
     if (m.stackOverflowLabel().used() && !GenerateStackOverflowExit(m, &throwLabel))
         return false;
 
     if (m.onDetachedLabel().used() && !GenerateOnDetachedLabelExit(m, &throwLabel))
         return false;
@@ -12073,17 +12052,17 @@ GenerateStubs(ModuleCompiler& m)
         if (!GenerateBuiltinThunk(m, AsmJSExit::BuiltinKind(i)))
             return false;
     }
 
     return true;
 }
 
 static bool
-FinishModule(ModuleCompiler& m,
+FinishModule(ModuleValidator& m,
              ScopedJSDeletePtr<AsmJSModule>* module)
 {
     LifoAlloc lifo(TempAllocator::PreferredLifoChunkSize);
     TempAllocator alloc(&lifo);
     JitContext jitContext(m.cx(), &alloc);
 
     m.masm().resetForNewCodeGenerator(alloc);
 
@@ -12098,18 +12077,17 @@ CheckModule(ExclusiveContext* cx, AsmJSP
             ScopedJSDeletePtr<AsmJSModule>* moduleOut,
             ScopedJSFreePtr<char>* compilationTimeReport)
 {
     if (!LookupAsmJSModuleInCache(cx, parser, moduleOut, compilationTimeReport))
         return false;
     if (*moduleOut)
         return true;
 
-    ModuleGlobals mg(cx);
-    ModuleValidator mv(cx, parser, mg);
+    ModuleValidator mv(cx, parser);
     if (!mv.init())
         return false;
 
     if (PropertyName* moduleFunctionName = FunctionName(mv.moduleFunctionNode())) {
         if (!CheckModuleLevelName(mv, mv.moduleFunctionNode(), moduleFunctionName))
             return false;
         mv.initModuleFunctionName(moduleFunctionName);
     }
@@ -12129,26 +12107,28 @@ CheckModule(ExclusiveContext* cx, AsmJSP
     if (!CheckModuleGlobals(mv))
         return false;
 
 #if !defined(ENABLE_SHARED_ARRAY_BUFFER)
     if (mv.module().hasArrayView() && mv.module().isSharedView())
         return mv.fail(nullptr, "shared views not supported by this build");
 #endif
 
-    ModuleCompiler mc(cx, parser, mv.modulePtr(), mg);
-    if (!mc.init())
-        return false;
-
-    mc.startFunctionBodies();
-
-    if (!CheckFunctions(mv, mc))
-        return false;
-
-    mc.finishFunctionBodies();
+    mv.startFunctionBodies();
+
+    ModuleCompileResults mcd(cx);
+    {
+        ModuleCompiler mc(cx, &mcd, parser, mv.modulePtr());
+        if (!CheckFunctions(mv, mc))
+            return false;
+    }
+
+    mv.takeCompileData(&mcd);
+
+    mv.finishFunctionBodies();
 
     if (!CheckFuncPtrTables(mv))
         return false;
 
     if (!CheckModuleReturn(mv))
         return false;
 
     TokenKind tk;
@@ -12157,23 +12137,23 @@ CheckModule(ExclusiveContext* cx, AsmJSP
     if (tk != TOK_EOF && tk != TOK_RC)
         return mv.fail(nullptr, "top-level export (return) must be the last statement");
 
     // Delay flushing until dynamic linking. The inhibited range is set by the
     // masm.executableCopy() called transitively by FinishModule.
     AutoFlushICache afc("CheckModule", /* inhibit = */ true);
 
     ScopedJSDeletePtr<AsmJSModule> module;
-    if (!FinishModule(mc, &module))
+    if (!FinishModule(mv, &module))
         return false;
 
     JS::AsmJSCacheResult cacheResult = StoreAsmJSModuleInCache(parser, *module, cx);
     module->staticallyLink(cx);
 
-    mc.buildCompilationTimeReport(cacheResult, compilationTimeReport);
+    mv.buildCompilationTimeReport(cacheResult, compilationTimeReport);
     *moduleOut = module.forget();
     return true;
 }
 
 static bool
 Warn(AsmJSParser& parser, int errorNumber, const char* str)
 {
     parser.reportNoOffset(ParseWarning, /* strict = */ false, errorNumber, str ? str : "");
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -283,17 +283,17 @@ function isRootedGCPointerTypeName(name)
     if (name.startsWith('MaybeRooted<'))
         return /\(js::AllowGC\)1u>::RootType/.test(name);
 
     if (name == "ErrorResult" ||
         name == "JSErrorResult" ||
         name == "WrappableJSErrorResult" ||
         name == "frontend::TokenStream" ||
         name == "frontend::TokenStream::Position" ||
-        name == "ModuleCompiler")
+        name == "ModuleCompiler" || name == "ModuleValidator")
     {
         return true;
     }
 
     return name.startsWith('Rooted') || name.startsWith('PersistentRooted');
 }
 
 function isRootedTypeName(name)