Bug 1224389 - Odin: simplify AsmJSModule global data allocation (r=bbouvier)
authorLuke Wagner <luke@mozilla.com>
Tue, 24 Nov 2015 23:35:20 -0600
changeset 308908 0d1bdaedc48523484c97b284a87aa79f80e03ecb
parent 308907 9239605f27a8cfed6c559c0d1053144a2a323601
child 308909 88256698e1a57062e15432206904e4d3d75a32be
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs1224389
milestone45.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 1224389 - Odin: simplify AsmJSModule global data allocation (r=bbouvier)
js/src/asmjs/AsmJSLink.cpp
js/src/asmjs/AsmJSModule.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSValidate.cpp
js/src/jit/BaselineJIT.cpp
js/src/jit/PerfSpewer.cpp
js/src/jit/PerfSpewer.h
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -125,19 +125,17 @@ HasPureCoercion(JSContext* cx, HandleVal
 
     return false;
 }
 
 static bool
 ValidateGlobalVariable(JSContext* cx, const AsmJSModule& module, AsmJSModule::Global& global,
                        HandleValue importVal)
 {
-    MOZ_ASSERT(global.which() == AsmJSModule::Global::Variable);
-
-    void* datum = module.globalVarToGlobalDatum(global);
+    void* datum = module.globalData() + global.varGlobalDataOffset();
 
     switch (global.varInitKind()) {
       case AsmJSModule::Global::InitConstant: {
         Val v = global.varInitVal();
         switch (v.type()) {
           case ValType::I32:
             *(int32_t*)datum = v.i32();
             break;
@@ -583,20 +581,20 @@ DynamicallyLinkModule(JSContext* cx, con
             break;
           case AsmJSModule::Global::SimdOperation:
             if (!ValidateSimdOperation(cx, global, globalVal))
                 return false;
             break;
         }
     }
 
-    for (unsigned i = 0; i < module.numExits(); i++)
-        module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>();
-
-    module.initGlobalNaN();
+    for (unsigned i = 0; i < module.numExits(); i++) {
+        const AsmJSModule::Exit& exit = module.exit(i);
+        exit.datum(module).fun = &ffis[exit.ffiIndex()]->as<JSFunction>();
+    }
 
     // See the comment in AllocateExecutableMemory.
     ExecutableAllocator::makeExecutable(module.codeBase(), module.codeBytes());
 
     return true;
 }
 
 static bool
@@ -952,22 +950,17 @@ SendFunctionsToPerf(JSContext* cx, AsmJS
 
 static bool
 SendModuleToAttachedProfiler(JSContext* cx, AsmJSModule& module)
 {
 #if defined(MOZ_VTUNE)
     if (IsVTuneProfilingActive() && !SendFunctionsToVTune(cx, module))
         return false;
 #endif
-
 #if defined(JS_ION_PERF)
-    if (module.numExportedFunctions() > 0) {
-        size_t firstEntryCode = size_t(module.codeBase() + module.functionBytes());
-        writePerfSpewerAsmJSEntriesAndExits(firstEntryCode, module.codeBytes() - module.functionBytes());
-    }
     if (!SendFunctionsToPerf(cx, module))
         return false;
 #endif
 
     return true;
 }
 
 
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -83,18 +83,17 @@ AsmJSModule::AsmJSModule(ScriptSource* s
     prevLinked_(nullptr),
     nextLinked_(nullptr),
     dynamicallyLinked_(false),
     loadedFromCache_(false),
     profilingEnabled_(false),
     interrupted_(false)
 {
     mozilla::PodZero(&pod);
-    pod.funcPtrTableAndExitBytes_ = SIZE_MAX;
-    pod.functionBytes_ = UINT32_MAX;
+    pod.globalBytes_ = sInitialGlobalDataBytes;
     pod.minHeapLength_ = RoundUpToNextValidAsmJSHeapLength(0);
     pod.maxHeapLength_ = 0x80000000;
     pod.strict_ = strict;
     pod.usesSignalHandlers_ = canUseSignalHandlers;
 
     // AsmJSCheckedImmediateRange should be defined to be at most the minimum
     // heap length so that offsets can be folded into bounds checks.
     MOZ_ASSERT(pod.minHeapLength_ - AsmJSCheckedImmediateRange <= pod.minHeapLength_);
@@ -105,17 +104,17 @@ AsmJSModule::AsmJSModule(ScriptSource* s
 AsmJSModule::~AsmJSModule()
 {
     MOZ_ASSERT(!interrupted_);
 
     scriptSource_->decref();
 
     if (code_) {
         for (unsigned i = 0; i < numExits(); i++) {
-            AsmJSModule::ExitDatum& exitDatum = exitIndexToGlobalDatum(i);
+            AsmJSModule::ExitDatum& exitDatum = exit(i).datum(*this);
             if (!exitDatum.baselineScript)
                 continue;
 
             jit::DependentAsmJSModuleExit exit(this, i);
             exitDatum.baselineScript->removeDependentAsmJSModule(exit);
         }
 
         DeallocateExecutableMemory(code_, pod.totalBytes_, AsmJSPageSize);
@@ -125,29 +124,29 @@ AsmJSModule::~AsmJSModule()
         *prevLinked_ = nextLinked_;
     if (nextLinked_)
         nextLinked_->prevLinked_ = prevLinked_;
 }
 
 void
 AsmJSModule::trace(JSTracer* trc)
 {
-    for (unsigned i = 0; i < globals_.length(); i++)
-        globals_[i].trace(trc);
-    for (unsigned i = 0; i < exits_.length(); i++) {
-        if (exitIndexToGlobalDatum(i).fun)
-            TraceEdge(trc, &exitIndexToGlobalDatum(i).fun, "asm.js imported function");
+    for (Global& global : globals_)
+        global.trace(trc);
+    for (Exit& exit : exits_) {
+        if (exit.datum(*this).fun)
+            TraceEdge(trc, &exit.datum(*this).fun, "asm.js imported function");
     }
-    for (unsigned i = 0; i < exports_.length(); i++)
-        exports_[i].trace(trc);
-    for (unsigned i = 0; i < names_.length(); i++)
-        TraceManuallyBarrieredEdge(trc, &names_[i].name(), "asm.js module function name");
+    for (ExportedFunction& exp : exports_)
+        exp.trace(trc);
+    for (Name& name : names_)
+        TraceManuallyBarrieredEdge(trc, &name.name(), "asm.js module function name");
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
-    for (unsigned i = 0; i < profiledFunctions_.length(); i++)
-        profiledFunctions_[i].trace(trc);
+    for (ProfiledFunction& profiledFunction : profiledFunctions_)
+        profiledFunction.trace(trc);
 #endif
     if (globalArgumentName_)
         TraceManuallyBarrieredEdge(trc, &globalArgumentName_, "asm.js global argument name");
     if (importArgumentName_)
         TraceManuallyBarrieredEdge(trc, &importArgumentName_, "asm.js import argument name");
     if (bufferArgumentName_)
         TraceManuallyBarrieredEdge(trc, &bufferArgumentName_, "asm.js buffer argument name");
     if (maybeHeap_)
@@ -264,35 +263,36 @@ AsmJSModule::lookupHeapAccess(void* pc) 
 
     return &heapAccesses_[match];
 }
 
 bool
 AsmJSModule::finish(ExclusiveContext* cx, TokenStream& tokenStream, MacroAssembler& masm,
                     const Label& interruptLabel, const Label& outOfBoundsLabel)
 {
-    MOZ_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
+    MOZ_ASSERT(!isFinished());
 
     uint32_t endBeforeCurly = tokenStream.currentToken().pos.end;
     TokenPos pos;
     if (!tokenStream.peekTokenPos(&pos, TokenStream::Operand))
         return false;
     uint32_t endAfterCurly = pos.end;
     MOZ_ASSERT(endBeforeCurly >= srcBodyStart_);
     MOZ_ASSERT(endAfterCurly >= srcBodyStart_);
     pod.srcLength_ = endBeforeCurly - srcStart_;
     pod.srcLengthWithRightBrace_ = endAfterCurly - srcStart_;
 
     // Start global data on a new page so JIT code may be given independent
     // protection flags.
     pod.codeBytes_ = AlignBytes(masm.bytesNeeded(), AsmJSPageSize);
+    MOZ_ASSERT(pod.functionBytes_ <= pod.codeBytes_);
 
     // The entire region is allocated via mmap/VirtualAlloc which requires
     // units of pages.
-    pod.totalBytes_ = AlignBytes(pod.codeBytes_ + globalDataBytes(), AsmJSPageSize);
+    pod.totalBytes_ = AlignBytes(pod.codeBytes_ + pod.globalBytes_, AsmJSPageSize);
 
     MOZ_ASSERT(!code_);
     code_ = AllocateExecutableMemory(cx, pod.totalBytes_);
     if (!code_)
         return false;
 
     // Copy the code from the MacroAssembler into its final resting place in the
     // AsmJSModule.
@@ -501,35 +501,36 @@ TryEnablingJit(JSContext* cx, AsmJSModul
         TypeSet::Type type = TypeSet::DoubleType();
         if (!argv[i].isDouble())
             type = TypeSet::PrimitiveType(argv[i].extractNonDoubleType());
         if (!typeset->hasType(type))
             return true;
     }
 
     // The exit may have become optimized while executing the FFI.
-    if (module.exitIsOptimized(exitIndex))
+    AsmJSModule::Exit& exit = module.exit(exitIndex);
+    if (exit.isOptimized(module))
         return true;
 
     BaselineScript* baselineScript = script->baselineScript();
     if (!baselineScript->addDependentAsmJSModule(cx, DependentAsmJSModuleExit(&module, exitIndex)))
         return false;
 
-    module.optimizeExit(exitIndex, baselineScript);
+    exit.optimize(module, baselineScript);
     return true;
 }
 
 static bool
 InvokeFromAsmJS(AsmJSActivation* activation, int32_t exitIndex, int32_t argc, Value* argv,
                 MutableHandleValue rval)
 {
     JSContext* cx = activation->cx();
     AsmJSModule& module = activation->module();
 
-    RootedFunction fun(cx, module.exitIndexToGlobalDatum(exitIndex).fun);
+    RootedFunction fun(cx, module.exit(exitIndex).datum(module).fun);
     RootedValue fval(cx, ObjectValue(*fun));
     if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval))
         return false;
 
     return TryEnablingJit(cx, module, fun, exitIndex, argc, argv);
 }
 
 // Use an int32_t return type instead of bool since bool does not have a
@@ -705,17 +706,16 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
 
     MOZ_CRASH("Bad AsmJSImmKind");
 }
 
 void
 AsmJSModule::staticallyLink(ExclusiveContext* cx)
 {
     MOZ_ASSERT(isFinished());
-    MOZ_ASSERT(!isStaticallyLinked());
 
     // Process staticLinkData_
 
     interruptExit_ = code_ + staticLinkData_.interruptExitOffset;
     outOfBoundsExit_ = code_ + staticLinkData_.outOfBoundsExitOffset;
 
     for (size_t i = 0; i < staticLinkData_.relativeLinks.length(); i++) {
         RelativeLink link = staticLinkData_.relativeLinks[i];
@@ -757,24 +757,21 @@ AsmJSModule::staticallyLink(ExclusiveCon
             Assembler::PatchDataWithValueCheck(CodeLocationLabel(patchAt),
                                                PatchedImmPtr(target),
                                                PatchedImmPtr((void*)-1));
         }
     }
 
     // Initialize global data segment
 
-    for (size_t i = 0; i < exits_.length(); i++) {
-        ExitDatum& exitDatum = exitIndexToGlobalDatum(i);
-        exitDatum.exit = interpExitTrampoline(exits_[i]);
-        exitDatum.fun = nullptr;
-        exitDatum.baselineScript = nullptr;
-    }
+    *(double*)(globalData() + AsmJSNaN64GlobalDataOffset) = GenericNaN();
+    *(float*)(globalData() + AsmJSNaN32GlobalDataOffset) = GenericNaN();
 
-    MOZ_ASSERT(isStaticallyLinked());
+    for (AsmJSModule::Exit& exit : exits_)
+        exit.initDatum(*this);
 }
 
 void
 AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared*> heap, JSContext* cx)
 {
     MOZ_ASSERT_IF(heap->is<ArrayBufferObject>(), heap->as<ArrayBufferObject>().isAsmJS());
     MOZ_ASSERT(IsValidAsmJSHeapLength(heap->byteLength()));
     MOZ_ASSERT(dynamicallyLinked_);
@@ -1796,17 +1793,17 @@ AsmJSModule::setProfilingEnabled(bool en
 # error "Missing architecture"
 #endif
     }
 
     // Update all the addresses in the function-pointer tables to point to the
     // profiling prologues:
     for (size_t i = 0; i < funcPtrTables_.length(); i++) {
         FuncPtrTable& funcPtrTable = funcPtrTables_[i];
-        uint8_t** array = globalDataOffsetToFuncPtrTable(funcPtrTable.globalDataOffset());
+        auto array = reinterpret_cast<uint8_t**>(globalData() + funcPtrTable.globalDataOffset());
         for (size_t j = 0; j < funcPtrTable.numElems(); j++) {
             void* callee = array[j];
             const CodeRange* codeRange = lookupCodeRange(callee);
             uint8_t* profilingEntry = code_ + codeRange->profilingEntry();
             uint8_t* entry = code_ + codeRange->entry();
             MOZ_ASSERT_IF(profilingEnabled_, callee == profilingEntry);
             MOZ_ASSERT_IF(!profilingEnabled_, callee == entry);
             if (enabled)
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -109,17 +109,17 @@ class AsmJSModule
         enum VarInitKind { InitConstant, InitImport };
         enum ConstantKind { GlobalConstant, MathConstant };
 
       private:
         struct Pod {
             Which which_;
             union {
                 struct {
-                    uint32_t index_;
+                    uint32_t globalDataOffset_;
                     VarInitKind initKind_;
                     union {
                         wasm::ValType importType_;
                         wasm::Val val_;
                     } u;
                 } var;
                 uint32_t ffiIndex_;
                 Scalar::Type viewType_;
@@ -152,19 +152,19 @@ class AsmJSModule
                 TraceManuallyBarrieredEdge(trc, &name_, "asm.js global name");
         }
 
       public:
         Global() {}
         Which which() const {
             return pod.which_;
         }
-        uint32_t varIndex() const {
+        uint32_t varGlobalDataOffset() const {
             MOZ_ASSERT(pod.which_ == Variable);
-            return pod.u.var.index_;
+            return pod.u.var.globalDataOffset_;
         }
         VarInitKind varInitKind() const {
             MOZ_ASSERT(pod.which_ == Variable);
             return pod.u.var.initKind_;
         }
         wasm::Val varInitVal() const {
             MOZ_ASSERT(pod.which_ == Variable);
             MOZ_ASSERT(pod.u.var.initKind_ == InitConstant);
@@ -254,25 +254,34 @@ class AsmJSModule
         }
 
         size_t serializedSize() const;
         uint8_t* serialize(uint8_t* cursor) const;
         const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
         bool clone(ExclusiveContext* cx, Global* out) const;
     };
 
+    // An Exit holds bookkeeping information about an exit; the ExitDatum
+    // struct overlays the actual runtime data stored in the global data
+    // section.
+
+    struct ExitDatum
+    {
+        uint8_t* exit;
+        jit::BaselineScript* baselineScript;
+        HeapPtrFunction fun;
+    };
+
     class Exit
     {
         unsigned ffiIndex_;
         unsigned globalDataOffset_;
         unsigned interpCodeOffset_;
         unsigned jitCodeOffset_;
 
-        friend class AsmJSModule;
-
       public:
         Exit() {}
         Exit(unsigned ffiIndex, unsigned globalDataOffset)
           : ffiIndex_(ffiIndex), globalDataOffset_(globalDataOffset),
             interpCodeOffset_(0), jitCodeOffset_(0)
         {}
         unsigned ffiIndex() const {
             return ffiIndex_;
@@ -283,40 +292,53 @@ class AsmJSModule
         void initInterpOffset(unsigned off) {
             MOZ_ASSERT(!interpCodeOffset_);
             interpCodeOffset_ = off;
         }
         void initJitOffset(unsigned off) {
             MOZ_ASSERT(!jitCodeOffset_);
             jitCodeOffset_ = off;
         }
+        ExitDatum& datum(const AsmJSModule& module) const {
+            return *reinterpret_cast<ExitDatum*>(module.globalData() + globalDataOffset_);
+        }
+        void initDatum(const AsmJSModule& module) const {
+            MOZ_ASSERT(interpCodeOffset_);
+            ExitDatum& d = datum(module);
+            d.exit = module.codeBase() + interpCodeOffset_;
+            d.baselineScript = nullptr;
+            d.fun = nullptr;
+        }
+        bool isOptimized(const AsmJSModule& module) const {
+            return datum(module).exit == module.codeBase() + jitCodeOffset_;
+        }
+        void optimize(const AsmJSModule& module, jit::BaselineScript* baselineScript) const {
+            ExitDatum& d = datum(module);
+            d.exit = module.codeBase() + jitCodeOffset_;
+            d.baselineScript = baselineScript;
+        }
+        void deoptimize(const AsmJSModule& module) const {
+            ExitDatum& d = datum(module);
+            d.exit = module.codeBase() + interpCodeOffset_;
+            d.baselineScript = nullptr;
+        }
 
         size_t serializedSize() const;
         uint8_t* serialize(uint8_t* cursor) const;
         const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
         bool clone(ExclusiveContext* cx, Exit* out) const;
     };
 
     struct EntryArg {
         uint64_t lo;
         uint64_t hi;
     };
 
     typedef int32_t (*CodePtr)(EntryArg* args, uint8_t* global);
 
-    // An Exit holds bookkeeping information about an exit; the ExitDatum
-    // struct overlays the actual runtime data stored in the global data
-    // section.
-    struct ExitDatum
-    {
-        uint8_t* exit;
-        jit::BaselineScript* baselineScript;
-        HeapPtrFunction fun;
-    };
-
     class ExportedFunction
     {
         PropertyName* name_;
         PropertyName* maybeFieldName_;
         wasm::MallocSig sig_;
         struct Pod {
             bool isChangeHeap_;
             uint32_t codeOffset_;
@@ -674,25 +696,23 @@ class AsmJSModule
         const uint8_t* deserialize(ExclusiveContext* cx, const uint8_t* cursor);
         bool clone(ExclusiveContext* cx, StaticLinkData* out) const;
 
         size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
     };
 
   private:
     struct Pod {
-        size_t                            funcPtrTableAndExitBytes_;
-        size_t                            functionBytes_; // just the function bodies, no stubs
-        size_t                            codeBytes_;     // function bodies and stubs
-        size_t                            totalBytes_;    // function bodies, stubs, and global data
+        uint32_t                          functionBytes_;
+        uint32_t                          codeBytes_;
+        uint32_t                          globalBytes_;
+        uint32_t                          totalBytes_;
         uint32_t                          minHeapLength_;
         uint32_t                          maxHeapLength_;
         uint32_t                          heapLengthMask_;
-        uint32_t                          numGlobalScalarVars_;
-        uint32_t                          numGlobalSimdVars_;
         uint32_t                          numFFIs_;
         uint32_t                          srcLength_;
         uint32_t                          srcLengthWithRightBrace_;
         bool                              strict_;
         bool                              hasArrayView_;
         bool                              isSharedView_;
         bool                              hasFixedMinHeapLength_;
         bool                              usesSignalHandlers_;
@@ -739,21 +759,18 @@ class AsmJSModule
                                ExclusiveContext* cx);
 
   public:
     explicit AsmJSModule(ScriptSource* scriptSource, uint32_t srcStart, uint32_t srcBodyStart,
                          bool strict, bool canUseSignalHandlers);
     void trace(JSTracer* trc);
     ~AsmJSModule();
 
-    // An AsmJSModule transitions monotonically through these states:
-    bool isFinishedWithModulePrologue() const { return pod.funcPtrTableAndExitBytes_ != SIZE_MAX; }
-    bool isFinishedWithFunctionBodies() const { return pod.functionBytes_ != UINT32_MAX; }
+    // An AsmJSModule transitions from !finished to finished to dynamically linked.
     bool isFinished() const { return !!code_; }
-    bool isStaticallyLinked() const { return !!interruptExit_; }
     bool isDynamicallyLinked() const { return dynamicallyLinked_; }
 
     /*************************************************************************/
     // These functions may be used as soon as the module is constructed:
 
     ScriptSource* scriptSource() const {
         MOZ_ASSERT(scriptSource_);
         return scriptSource_;
@@ -807,130 +824,150 @@ class AsmJSModule
     void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t* asmJSModuleCode,
                        size_t* asmJSModuleData);
 
     /*************************************************************************/
     // These functions build the global scope of the module while parsing the
     // module prologue (before the function bodies):
 
     void initGlobalArgumentName(PropertyName* n) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         MOZ_ASSERT_IF(n, n->isTenured());
         globalArgumentName_ = n;
     }
     void initImportArgumentName(PropertyName* n) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         MOZ_ASSERT_IF(n, n->isTenured());
         importArgumentName_ = n;
     }
     void initBufferArgumentName(PropertyName* n) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         MOZ_ASSERT_IF(n, n->isTenured());
         bufferArgumentName_ = n;
     }
     PropertyName* globalArgumentName() const {
         return globalArgumentName_;
     }
     PropertyName* importArgumentName() const {
         return importArgumentName_;
     }
     PropertyName* bufferArgumentName() const {
         return bufferArgumentName_;
     }
-    bool addGlobalVarInit(const wasm::Val& v, uint32_t* globalIndex) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+
+    /*************************************************************************/
+    // These functions may only be called before finish():
+
+  private:
+    bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset) {
+        MOZ_ASSERT(!isFinished());
+        uint32_t pad = ComputeByteAlignment(pod.globalBytes_, align);
+        if (UINT32_MAX - pod.globalBytes_ < pad + bytes)
+            return false;
+        pod.globalBytes_ += pad;
+        *globalDataOffset = pod.globalBytes_;
+        pod.globalBytes_ += bytes;
+        return true;
+    }
+    bool addGlobalVar(wasm::ValType type, uint32_t* globalDataOffset) {
+        MOZ_ASSERT(!isFinished());
+        unsigned width = 0;
+        switch (type) {
+          case wasm::ValType::I32:   case wasm::ValType::F32:   width = 4;  break;
+          case wasm::ValType::I64:   case wasm::ValType::F64:   width = 8;  break;
+          case wasm::ValType::I32x4: case wasm::ValType::F32x4: width = 16; break;
+        }
+        return allocateGlobalBytes(width, width, globalDataOffset);
+    }
+  public:
+    bool addGlobalVarInit(const wasm::Val& v, uint32_t* globalDataOffset) {
+        MOZ_ASSERT(!isFinished());
+        if (!addGlobalVar(v.type(), globalDataOffset))
+            return false;
         Global g(Global::Variable, nullptr);
         g.pod.u.var.initKind_ = Global::InitConstant;
         g.pod.u.var.u.val_ = v;
-        if (v.isSimd()) {
-            if (pod.numGlobalSimdVars_ == UINT32_MAX)
-                return false;
-            *globalIndex = pod.numGlobalSimdVars_++;
-        } else {
-            if (pod.numGlobalScalarVars_ == UINT32_MAX)
-                return false;
-            *globalIndex = pod.numGlobalScalarVars_++;
-        }
-        g.pod.u.var.index_ = *globalIndex;
+        g.pod.u.var.globalDataOffset_ = *globalDataOffset;
         return globals_.append(g);
     }
-    bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t* globalIndex) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+    bool addGlobalVarImport(PropertyName* name, wasm::ValType importType, uint32_t* globalDataOffset) {
+        MOZ_ASSERT(!isFinished());
+        if (!addGlobalVar(importType, globalDataOffset))
+            return false;
         Global g(Global::Variable, name);
         g.pod.u.var.initKind_ = Global::InitImport;
         g.pod.u.var.u.importType_ = importType;
-        *globalIndex = IsSimdType(importType)
-                       ? pod.numGlobalSimdVars_++
-                       : pod.numGlobalScalarVars_++;
-        g.pod.u.var.index_ = *globalIndex;
+        g.pod.u.var.globalDataOffset_ = *globalDataOffset;
         return globals_.append(g);
     }
     bool addFFI(PropertyName* field, uint32_t* ffiIndex) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         if (pod.numFFIs_ == UINT32_MAX)
             return false;
         Global g(Global::FFI, field);
         g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++;
         return globals_.append(g);
     }
     bool addArrayView(Scalar::Type vt, PropertyName* maybeField, bool isSharedView) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         MOZ_ASSERT(!pod.hasArrayView_ || (pod.isSharedView_ == isSharedView));
         pod.hasArrayView_ = true;
         pod.isSharedView_ = isSharedView;
         Global g(Global::ArrayView, maybeField);
         g.pod.u.viewType_ = vt;
         return globals_.append(g);
     }
     bool addArrayViewCtor(Scalar::Type vt, PropertyName* field, bool isSharedView) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         MOZ_ASSERT(field);
         MOZ_ASSERT(!pod.isSharedView_ || isSharedView);
         pod.isSharedView_ = isSharedView;
         Global g(Global::ArrayViewCtor, field);
         g.pod.u.viewType_ = vt;
         return globals_.append(g);
     }
     bool addByteLength() {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         Global g(Global::ByteLength, nullptr);
         return globals_.append(g);
     }
     bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName* field) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         Global g(Global::MathBuiltinFunction, field);
         g.pod.u.mathBuiltinFunc_ = func;
         return globals_.append(g);
     }
     bool addMathBuiltinConstant(double value, PropertyName* field) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         Global g(Global::Constant, field);
         g.pod.u.constant.value_ = value;
         g.pod.u.constant.kind_ = Global::MathConstant;
         return globals_.append(g);
     }
     bool addAtomicsBuiltinFunction(AsmJSAtomicsBuiltinFunction func, PropertyName* field) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         Global g(Global::AtomicsBuiltinFunction, field);
         g.pod.u.atomicsBuiltinFunc_ = func;
         return globals_.append(g);
     }
     bool addSimdCtor(AsmJSSimdType type, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
         Global g(Global::SimdCtor, field);
         g.pod.u.simdCtorType_ = type;
         return globals_.append(g);
     }
     bool addSimdOperation(AsmJSSimdType type, AsmJSSimdOperation op, PropertyName* field) {
+        MOZ_ASSERT(!isFinished());
         Global g(Global::SimdOperation, field);
         g.pod.u.simdOp.type_ = type;
         g.pod.u.simdOp.which_ = op;
         return globals_.append(g);
     }
     bool addGlobalConstant(double value, PropertyName* name) {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         Global g(Global::Constant, name);
         g.pod.u.constant.value_ = value;
         g.pod.u.constant.kind_ = Global::GlobalConstant;
         return globals_.append(g);
     }
     unsigned numGlobals() const {
         return globals_.length();
     }
@@ -948,48 +985,39 @@ class AsmJSModule
         for (size_t i=0 ; i < globals_.length() ; i++) {
             Global& g = globals_[i];
             if (g.which() == Global::ArrayView)
                 g.makeViewShared();
         }
     }
 
     /*************************************************************************/
-
-    void startFunctionBodies() {
-        MOZ_ASSERT(!isFinishedWithModulePrologue());
-        pod.funcPtrTableAndExitBytes_ = 0;
-        MOZ_ASSERT(isFinishedWithModulePrologue());
-    }
-
-    /*************************************************************************/
     // These functions are called while parsing/compiling function bodies:
 
     bool hasArrayView() const {
-        MOZ_ASSERT(isFinishedWithModulePrologue());
         return pod.hasArrayView_;
     }
     bool isSharedView() const {
         MOZ_ASSERT(pod.hasArrayView_);
         return pod.isSharedView_;
     }
     void addChangeHeap(uint32_t mask, uint32_t min, uint32_t max) {
-        MOZ_ASSERT(isFinishedWithModulePrologue());
+        MOZ_ASSERT(!isFinished());
         MOZ_ASSERT(!pod.hasFixedMinHeapLength_);
         MOZ_ASSERT(IsValidAsmJSHeapLength(mask + 1));
         MOZ_ASSERT(min >= RoundUpToNextValidAsmJSHeapLength(0));
         MOZ_ASSERT(max <= pod.maxHeapLength_);
         MOZ_ASSERT(min <= max);
         pod.heapLengthMask_ = mask;
         pod.minHeapLength_ = min;
         pod.maxHeapLength_ = max;
         pod.hasFixedMinHeapLength_ = true;
     }
     bool tryRequireHeapLengthToBeAtLeast(uint32_t len) {
-        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
+        MOZ_ASSERT(!isFinished());
         if (pod.hasFixedMinHeapLength_ && len > pod.minHeapLength_)
             return false;
         if (len > pod.maxHeapLength_)
             return false;
         len = RoundUpToNextValidAsmJSHeapLength(len);
         if (len > pod.minHeapLength_)
             pod.minHeapLength_ = len;
         return true;
@@ -1008,118 +1036,89 @@ class AsmJSModule
         codeRange.initNameIndex(names_.length());
         return names_.append(name) && codeRanges_.append(codeRange);
     }
     bool addBuiltinThunkCodeRange(AsmJSExit::BuiltinKind builtin, AsmJSProfilingOffsets offsets) {
         return builtinThunkOffsets_.append(offsets.begin) &&
                codeRanges_.append(CodeRange(builtin, offsets));
     }
     bool addExit(unsigned ffiIndex, unsigned* exitIndex) {
-        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
-        if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < sizeof(ExitDatum))
+        MOZ_ASSERT(!isFinished());
+        static_assert(sizeof(ExitDatum) % sizeof(void*) == 0, "word aligned");
+        uint32_t globalDataOffset;
+        if (!allocateGlobalBytes(sizeof(ExitDatum), sizeof(void*), &globalDataOffset))
             return false;
-        uint32_t globalDataOffset = globalDataBytes();
-        JS_STATIC_ASSERT(sizeof(ExitDatum) % sizeof(void*) == 0);
-        pod.funcPtrTableAndExitBytes_ += sizeof(ExitDatum);
         *exitIndex = unsigned(exits_.length());
         return exits_.append(Exit(ffiIndex, globalDataOffset));
     }
     unsigned numExits() const {
-        MOZ_ASSERT(isFinishedWithModulePrologue());
         return exits_.length();
     }
     Exit& exit(unsigned i) {
-        MOZ_ASSERT(isFinishedWithModulePrologue());
         return exits_[i];
     }
     const Exit& exit(unsigned i) const {
-        MOZ_ASSERT(isFinishedWithModulePrologue());
         return exits_[i];
     }
     bool addFuncPtrTable(unsigned numElems, uint32_t* globalDataOffset) {
-        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinished());
+        MOZ_ASSERT(!isFinished());
         MOZ_ASSERT(IsPowerOfTwo(numElems));
-        if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < numElems * sizeof(void*))
+        if (!allocateGlobalBytes(numElems * sizeof(void*), sizeof(void*), globalDataOffset))
             return false;
-        *globalDataOffset = globalDataBytes();
-        if (!funcPtrTables_.append(FuncPtrTable(*globalDataOffset, numElems)))
-            return false;
-        pod.funcPtrTableAndExitBytes_ += numElems * sizeof(void*);
-        return true;
+        return funcPtrTables_.append(FuncPtrTable(*globalDataOffset, numElems));
     }
 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
-    bool addProfiledFunction(ProfiledFunction&& func)
-    {
-        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
-        return profiledFunctions_.append(mozilla::Move(func));
+    bool addProfiledFunction(ProfiledFunction func) {
+        MOZ_ASSERT(!isFinished());
+        return profiledFunctions_.append(func);
     }
     unsigned numProfiledFunctions() const {
-        MOZ_ASSERT(isFinishedWithModulePrologue());
         return profiledFunctions_.length();
     }
     ProfiledFunction& profiledFunction(unsigned i) {
-        MOZ_ASSERT(isFinishedWithModulePrologue());
         return profiledFunctions_[i];
     }
 #endif
 
-    /*************************************************************************/
-
-    // This function is called after compiling the function bodies (before
-    // compiling entries/exits) to record the extent of compiled function code.
-    void finishFunctionBodies(size_t functionBytes) {
-        MOZ_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
-        pod.functionBytes_ = functionBytes;
-        MOZ_ASSERT(isFinishedWithFunctionBodies());
-    }
-
-    /*************************************************************************/
-    // Exported functions are added after finishFunctionBodies() and before
-    // finish(). The list of exported functions can be accessed any time after
-    // the exported functions have been added.
-
     bool addExportedFunction(PropertyName* name,
                              uint32_t funcSrcBegin,
                              uint32_t funcSrcEnd,
                              PropertyName* maybeFieldName,
                              wasm::MallocSig&& sig)
     {
         // NB: funcSrcBegin/funcSrcEnd are given relative to the ScriptSource
         // (the entire file) and ExportedFunctions store offsets relative to
         // the beginning of the module (so that they are caching-invariant).
-        MOZ_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
+        MOZ_ASSERT(!isFinished());
         MOZ_ASSERT(srcStart_ < funcSrcBegin);
         MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
         ExportedFunction func(name, funcSrcBegin - srcStart_, funcSrcEnd - srcStart_,
                               maybeFieldName, mozilla::Move(sig));
         return exports_.length() < UINT32_MAX && exports_.append(mozilla::Move(func));
     }
     bool addExportedChangeHeap(PropertyName* name,
                                uint32_t funcSrcBegin,
                                uint32_t funcSrcEnd,
                                PropertyName* maybeFieldName)
     {
         // See addExportedFunction.
-        MOZ_ASSERT(isFinishedWithFunctionBodies() && !isFinished());
+        MOZ_ASSERT(!isFinished());
         MOZ_ASSERT(srcStart_ < funcSrcBegin);
         MOZ_ASSERT(funcSrcBegin < funcSrcEnd);
         ExportedFunction func(name, funcSrcBegin - srcStart_, funcSrcEnd - srcStart_,
                               maybeFieldName);
         return exports_.length() < UINT32_MAX && exports_.append(mozilla::Move(func));
     }
     unsigned numExportedFunctions() const {
-        MOZ_ASSERT(isFinishedWithFunctionBodies());
         return exports_.length();
     }
     const ExportedFunction& exportedFunction(unsigned i) const {
-        MOZ_ASSERT(isFinishedWithFunctionBodies());
         return exports_[i];
     }
     ExportedFunction& exportedFunction(unsigned i) {
-        MOZ_ASSERT(isFinishedWithFunctionBodies());
         return exports_[i];
     }
 
     /*************************************************************************/
 
     // finish() is called once the entire module has been parsed (via
     // tokenStream) and all function and entry/exit trampolines have been
     // generated (via masm). After this function, the module must still be
@@ -1128,215 +1127,126 @@ class AsmJSModule
                 frontend::TokenStream& tokenStream,
                 jit::MacroAssembler& masm,
                 const jit::Label& interruptLabel,
                 const jit::Label& outOfBoundsLabel);
 
     /*************************************************************************/
     // These accessor functions can be used after finish():
 
+    uint8_t* codeBase() const {
+        MOZ_ASSERT(isFinished());
+        MOZ_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
+        return code_;
+    }
+    uint32_t codeBytes() const {
+        MOZ_ASSERT(isFinished());
+        return pod.codeBytes_;
+    }
+    bool containsCodePC(void* pc) const {
+        MOZ_ASSERT(isFinished());
+        return pc >= code_ && pc < (code_ + codeBytes());
+    }
+
+    // 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.
+    void setFunctionBytes(uint32_t functionBytes) {
+        MOZ_ASSERT(!isFinished());
+        MOZ_ASSERT(!pod.functionBytes_);
+        pod.functionBytes_ = functionBytes;
+    }
+    uint32_t functionBytes() const {
+        MOZ_ASSERT(isFinished());
+        return pod.functionBytes_;
+    }
+    bool containsFunctionPC(void* pc) const {
+        MOZ_ASSERT(isFinished());
+        return pc >= code_ && pc < (code_ + functionBytes());
+    }
+
+    uint32_t globalBytes() const {
+        MOZ_ASSERT(isFinished());
+        return pod.globalBytes_;
+    }
+
     unsigned numFFIs() const {
         MOZ_ASSERT(isFinished());
         return pod.numFFIs_;
     }
     uint32_t srcEndBeforeCurly() const {
         MOZ_ASSERT(isFinished());
         return srcStart_ + pod.srcLength_;
     }
     uint32_t srcEndAfterCurly() const {
         MOZ_ASSERT(isFinished());
         return srcStart_ + pod.srcLengthWithRightBrace_;
     }
-    uint8_t* codeBase() const {
-        MOZ_ASSERT(isFinished());
-        MOZ_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
-        return code_;
-    }
-    size_t functionBytes() const {
-        MOZ_ASSERT(isFinished());
-        return pod.functionBytes_;
-    }
-    size_t codeBytes() const {
-        MOZ_ASSERT(isFinished());
-        return pod.codeBytes_;
-    }
-    bool containsFunctionPC(void* pc) const {
-        MOZ_ASSERT(isFinished());
-        return pc >= code_ && pc < (code_ + functionBytes());
-    }
-    bool containsCodePC(void* pc) const {
-        MOZ_ASSERT(isFinished());
-        return pc >= code_ && pc < (code_ + codeBytes());
-    }
-  private:
-    uint8_t* interpExitTrampoline(const Exit& exit) const {
-        MOZ_ASSERT(isFinished());
-        MOZ_ASSERT(exit.interpCodeOffset_);
-        return code_ + exit.interpCodeOffset_;
-    }
-    uint8_t* jitExitTrampoline(const Exit& exit) const {
-        MOZ_ASSERT(isFinished());
-        MOZ_ASSERT(exit.jitCodeOffset_);
-        return code_ + exit.jitCodeOffset_;
-    }
-  public:
 
     // Lookup a callsite by the return pc (from the callee to the caller).
     // Return null if no callsite was found.
     const jit::CallSite* lookupCallSite(void* returnAddress) const;
 
     // Lookup the name the code range containing the given pc. Return null if no
     // code range was found.
     const CodeRange* lookupCodeRange(void* pc) const;
 
     // Lookup a heap access site by the pc which performs the access. Return
     // null if no heap access was found.
     const jit::AsmJSHeapAccess* lookupHeapAccess(void* pc) const;
 
     // The global data section is placed after the executable code (i.e., at
     // offset codeBytes_) in the module's linear allocation. The global data
-    // are laid out in this order:
-    //   0. a pointer to the current AsmJSActivation
-    //   1. a pointer to the heap that was linked to the module
-    //   2. the double float constant NaN
-    //   3. the float32 constant NaN, padded to Simd128DataSize
-    //   4. global SIMD variable state (elements are Simd128DataSize)
-    //   5. global variable state (elements are sizeof(uint64_t))
-    //   6. interleaved function-pointer tables and exits. These are allocated
-    //      while type checking function bodies (as exits and uses of
-    //      function-pointer tables are encountered).
-    size_t offsetOfGlobalData() const {
+    // starts with some fixed allocations followed by interleaved global,
+    // function-pointer table and exit allocations.
+    uint32_t offsetOfGlobalData() const {
         MOZ_ASSERT(isFinished());
         return pod.codeBytes_;
     }
     uint8_t* globalData() const {
         MOZ_ASSERT(isFinished());
-        return code_ + offsetOfGlobalData();
-    }
-    size_t globalSimdVarsOffset() const {
-        return AlignBytes(/* 0 */ sizeof(void*) +
-                          /* 1 */ sizeof(void*) +
-                          /* 2 */ sizeof(double) +
-                          /* 3 */ sizeof(float),
-                          jit::Simd128DataSize);
+        return codeBase() + offsetOfGlobalData();
     }
-    size_t globalDataBytes() const {
-        return globalSimdVarsOffset() +
-               /* 4 */ pod.numGlobalSimdVars_ * jit::Simd128DataSize +
-               /* 5 */ pod.numGlobalScalarVars_ * sizeof(uint64_t) +
-               /* 6 */ pod.funcPtrTableAndExitBytes_;
+    static void assertGlobalDataOffsets() {
+        static_assert(jit::AsmJSActivationGlobalDataOffset == 0,
+                     "global data goes first");
+        static_assert(jit::AsmJSHeapGlobalDataOffset == jit::AsmJSActivationGlobalDataOffset + sizeof(void*),
+                      "then an AsmJSActivation*");
+        static_assert(jit::AsmJSNaN64GlobalDataOffset == jit::AsmJSHeapGlobalDataOffset + sizeof(uint8_t*),
+                      "then a pointer to the heap");
+        static_assert(jit::AsmJSNaN32GlobalDataOffset == jit::AsmJSNaN64GlobalDataOffset + sizeof(double),
+                      "then a 32-bit NaN");
+        static_assert(sInitialGlobalDataBytes == jit::AsmJSNaN32GlobalDataOffset + sizeof(float),
+                      "then a 64-bit NaN");
     }
-    static unsigned activationGlobalDataOffset() {
-        JS_STATIC_ASSERT(jit::AsmJSActivationGlobalDataOffset == 0);
-        return 0;
-    }
+    static const uint32_t sInitialGlobalDataBytes = jit::AsmJSNaN32GlobalDataOffset + sizeof(float);
+
     AsmJSActivation*& activation() const {
-        return *(AsmJSActivation**)(globalData() + activationGlobalDataOffset());
+        MOZ_ASSERT(isFinished());
+        return *(AsmJSActivation**)(globalData() + jit::AsmJSActivationGlobalDataOffset);
     }
     bool active() const {
         return activation() != nullptr;
     }
-    static unsigned heapGlobalDataOffset() {
-        JS_STATIC_ASSERT(jit::AsmJSHeapGlobalDataOffset == sizeof(void*));
-        return sizeof(void*);
-    }
   private:
     // The pointer may reference shared memory, use with care.
     // Generally you want to use maybeHeap(), not heapDatum().
     uint8_t*& heapDatum() const {
         MOZ_ASSERT(isFinished());
-        return *(uint8_t**)(globalData() + heapGlobalDataOffset());
+        return *(uint8_t**)(globalData() + jit::AsmJSHeapGlobalDataOffset);
     }
   public:
-    static unsigned nan64GlobalDataOffset() {
-        static_assert(jit::AsmJSNaN64GlobalDataOffset % sizeof(double) == 0,
-                      "Global data NaN should be aligned");
-        return heapGlobalDataOffset() + sizeof(void*);
-    }
-    static unsigned nan32GlobalDataOffset() {
-        static_assert(jit::AsmJSNaN32GlobalDataOffset % sizeof(double) == 0,
-                      "Global data NaN should be aligned");
-        return nan64GlobalDataOffset() + sizeof(double);
-    }
-    void initGlobalNaN() {
-        MOZ_ASSERT(jit::AsmJSNaN64GlobalDataOffset == nan64GlobalDataOffset());
-        MOZ_ASSERT(jit::AsmJSNaN32GlobalDataOffset == nan32GlobalDataOffset());
-        *(double*)(globalData() + nan64GlobalDataOffset()) = GenericNaN();
-        *(float*)(globalData() + nan32GlobalDataOffset()) = GenericNaN();
-    }
-    unsigned globalSimdVarIndexToGlobalDataOffset(unsigned i) const {
-        MOZ_ASSERT(isFinishedWithModulePrologue());
-        MOZ_ASSERT(i < pod.numGlobalSimdVars_);
-        return globalSimdVarsOffset() +
-               i * jit::Simd128DataSize;
-    }
-    unsigned globalScalarVarIndexToGlobalDataOffset(unsigned i) const {
-        MOZ_ASSERT(isFinishedWithModulePrologue());
-        MOZ_ASSERT(i < pod.numGlobalScalarVars_);
-        return globalSimdVarsOffset() +
-               pod.numGlobalSimdVars_ * jit::Simd128DataSize +
-               i * sizeof(uint64_t);
-    }
-    void* globalScalarVarIndexToGlobalDatum(unsigned i) const {
-        MOZ_ASSERT(isFinished());
-        return (void*)(globalData() + globalScalarVarIndexToGlobalDataOffset(i));
-    }
-    void* globalSimdVarIndexToGlobalDatum(unsigned i) const {
-        MOZ_ASSERT(isFinished());
-        return (void*)(globalData() + globalSimdVarIndexToGlobalDataOffset(i));
-    }
-    void* globalVarToGlobalDatum(const Global& g) const {
-        unsigned index = g.varIndex();
-        if (g.varInitKind() == Global::VarInitKind::InitConstant) {
-            return g.varInitVal().isSimd()
-                   ? globalSimdVarIndexToGlobalDatum(index)
-                   : globalScalarVarIndexToGlobalDatum(index);
-        }
-
-        MOZ_ASSERT(g.varInitKind() == Global::VarInitKind::InitImport);
-        return IsSimdType(g.varInitImportType())
-               ? globalSimdVarIndexToGlobalDatum(index)
-               : globalScalarVarIndexToGlobalDatum(index);
-    }
-    uint8_t** globalDataOffsetToFuncPtrTable(unsigned globalDataOffset) const {
-        MOZ_ASSERT(isFinished());
-        MOZ_ASSERT(globalDataOffset < globalDataBytes());
-        return (uint8_t**)(globalData() + globalDataOffset);
-    }
-    unsigned exitIndexToGlobalDataOffset(unsigned exitIndex) const {
-        MOZ_ASSERT(isFinishedWithModulePrologue());
-        return exits_[exitIndex].globalDataOffset();
-    }
-    ExitDatum& exitIndexToGlobalDatum(unsigned exitIndex) const {
-        MOZ_ASSERT(isFinished());
-        return *(ExitDatum*)(globalData() + exitIndexToGlobalDataOffset(exitIndex));
-    }
-    bool exitIsOptimized(unsigned exitIndex) const {
-        MOZ_ASSERT(isFinished());
-        ExitDatum& exitDatum = exitIndexToGlobalDatum(exitIndex);
-        return exitDatum.exit != interpExitTrampoline(exit(exitIndex));
-    }
-    void optimizeExit(unsigned exitIndex, jit::BaselineScript* baselineScript) const {
-        MOZ_ASSERT(!exitIsOptimized(exitIndex));
-        ExitDatum& exitDatum = exitIndexToGlobalDatum(exitIndex);
-        exitDatum.exit = jitExitTrampoline(exit(exitIndex));
-        exitDatum.baselineScript = baselineScript;
-    }
-    void detachJitCompilation(size_t exitIndex) const {
-        MOZ_ASSERT(isFinished());
-        ExitDatum& exitDatum = exitIndexToGlobalDatum(exitIndex);
-        exitDatum.exit = interpExitTrampoline(exit(exitIndex));
-        exitDatum.baselineScript = nullptr;
-    }
 
     /*************************************************************************/
     // These functions are called after finish() but before staticallyLink():
 
     bool addRelativeLink(RelativeLink link) {
-        MOZ_ASSERT(isFinished() && !isStaticallyLinked());
+        MOZ_ASSERT(isFinished());
         return staticLinkData_.relativeLinks.append(link);
     }
 
     // A module is serialized after it is finished but before it is statically
     // linked. (Technically, it could be serialized after static linking, but it
     // would still need to be statically linked on deserialization.)
     size_t serializedSize() const;
     uint8_t* serialize(uint8_t* cursor) const;
@@ -1344,29 +1254,30 @@ class AsmJSModule
 
     // Additionally, this function is called to flush the i-cache after
     // deserialization and cloning (but still before static linking, to prevent
     // a bunch of expensive micro-flushes).
     void setAutoFlushICacheRange();
 
     /*************************************************************************/
 
-    // After a module isFinished compiling or deserializing, it is "statically
+    // After a module is finished compiling or deserializing, it is "statically
     // linked" which specializes the code to its current address (this allows
     // code to be relocated between serialization and deserialization).
     void staticallyLink(ExclusiveContext* cx);
 
     // After a module is statically linked, it is "dynamically linked" which
     // specializes it to a particular set of arguments. In particular, this
     // binds the code to a particular heap (via initHeap) and set of global
     // variables. A given asm.js module cannot be dynamically linked more than
     // once so, if JS tries, the module is cloned. When linked, an asm.js module
     // is kept in a list so that it can be updated if the linked buffer is
     // detached.
     void setIsDynamicallyLinked(JSRuntime* rt) {
+        MOZ_ASSERT(isFinished());
         MOZ_ASSERT(!isDynamicallyLinked());
         dynamicallyLinked_ = true;
         nextLinked_ = rt->linkedAsmJSModules;
         prevLinked_ = &rt->linkedAsmJSModules;
         if (nextLinked_)
             nextLinked_->prevLinked_ = &nextLinked_;
         rt->linkedAsmJSModules = this;
         MOZ_ASSERT(isDynamicallyLinked());
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -1017,17 +1017,17 @@ class MOZ_STACK_CLASS ModuleValidator
             ChangeHeap
         };
 
       private:
         Which which_;
         union {
             struct {
                 Type::Which type_;
-                uint32_t index_;
+                uint32_t globalDataOffset_;
                 NumLit literalValue_;
             } varOrConst;
             uint32_t funcIndex_;
             uint32_t funcPtrTableIndex_;
             uint32_t ffiIndex_;
             struct {
                 Scalar::Type viewType_;
                 bool isSharedView_;
@@ -1053,19 +1053,19 @@ class MOZ_STACK_CLASS ModuleValidator
       public:
         Which which() const {
             return which_;
         }
         Type varOrConstType() const {
             MOZ_ASSERT(which_ == Variable || which_ == ConstantLiteral || which_ == ConstantImport);
             return u.varOrConst.type_;
         }
-        uint32_t varOrConstIndex() const {
+        uint32_t varOrConstGlobalDataOffset() const {
             MOZ_ASSERT(which_ == Variable || which_ == ConstantImport);
-            return u.varOrConst.index_;
+            return u.varOrConst.globalDataOffset_;
         }
         bool isConst() const {
             return which_ == ConstantLiteral || which_ == ConstantImport;
         }
         NumLit constLiteralValue() const {
             MOZ_ASSERT(which_ == ConstantLiteral);
             return u.varOrConst.literalValue_;
         }
@@ -1253,18 +1253,16 @@ class MOZ_STACK_CLASS ModuleValidator
     int64_t                                 usecBefore_;
     Vector<uint32_t>                        functionEntryOffsets_;
     SlowFunctionVector                      slowFunctions_;
 
     // Labels not accessible in functions compilations
     NonAssertingLabel                       asyncInterruptLabel_;
     NonAssertingLabel                       onDetachedLabel_;
 
-    DebugOnly<bool>                         finishedFunctionBodies_;
-
   public:
     ModuleValidator(ExclusiveContext* cx, AsmJSParser& parser)
       : cx_(cx),
         parser_(parser),
         moduleLifo_(LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
         functions_(cx),
         funcPtrTables_(cx),
         globals_(cx),
@@ -1280,18 +1278,17 @@ class MOZ_STACK_CLASS ModuleValidator
         errorOverRecursed_(false),
         canValidateChangeHeap_(false),
         hasChangeHeap_(false),
         supportsSimd_(cx->jitSupportsSimd()),
         atomicsPresent_(false),
         masm_(MacroAssembler::AsmJSToken()),
         usecBefore_(PRMJ_Now()),
         functionEntryOffsets_(cx),
-        slowFunctions_(cx),
-        finishedFunctionBodies_(false)
+        slowFunctions_(cx)
     {
         MOZ_ASSERT(moduleFunctionNode_->pn_funbox == parser.pc->sc->asFunctionBox());
     }
 
     ~ModuleValidator() {
         if (errorString_) {
             MOZ_ASSERT(errorOffset_ != UINT32_MAX);
             tokenStream().reportAsmJSError(errorOffset_,
@@ -1411,40 +1408,40 @@ class MOZ_STACK_CLASS ModuleValidator
     void initGlobalArgumentName(PropertyName* n)    { module_->initGlobalArgumentName(n); }
     void initImportArgumentName(PropertyName* n)    { module_->initImportArgumentName(n); }
     void initBufferArgumentName(PropertyName* n)    { module_->initBufferArgumentName(n); }
 
     bool addGlobalVarInit(PropertyName* varName, const NumLit& lit, bool isConst) {
         // The type of a const is the exact type of the literal (since its value
         // cannot change) which is more precise than the corresponding vartype.
         Type type = isConst ? Type::lit(lit) : Type::var(lit.type());
-        uint32_t globalIndex;
-        if (!module_->addGlobalVarInit(lit.value(), &globalIndex))
+        uint32_t globalDataOffset;
+        if (!module_->addGlobalVarInit(lit.value(), &globalDataOffset))
             return false;
         Global::Which which = isConst ? Global::ConstantLiteral : Global::Variable;
         Global* global = moduleLifo_.new_<Global>(which);
         if (!global)
             return false;
-        global->u.varOrConst.index_ = globalIndex;
+        global->u.varOrConst.globalDataOffset_ = globalDataOffset;
         global->u.varOrConst.type_ = type.which();
         if (isConst)
             global->u.varOrConst.literalValue_ = lit;
         return globals_.putNew(varName, global);
     }
     bool addGlobalVarImport(PropertyName* varName, PropertyName* fieldName, ValType importType,
                             bool isConst)
     {
-        uint32_t globalIndex;
-        if (!module_->addGlobalVarImport(fieldName, importType, &globalIndex))
+        uint32_t globalDataOffset;
+        if (!module_->addGlobalVarImport(fieldName, importType, &globalDataOffset))
             return false;
         Global::Which which = isConst ? Global::ConstantImport : Global::Variable;
         Global* global = moduleLifo_.new_<Global>(which);
         if (!global)
             return false;
-        global->u.varOrConst.index_ = globalIndex;
+        global->u.varOrConst.globalDataOffset_ = globalDataOffset;
         global->u.varOrConst.type_ = Type::var(importType).which();
         return globals_.putNew(varName, global);
     }
     bool addArrayView(PropertyName* varName, Scalar::Type vt, PropertyName* maybeField,
                       bool isSharedView)
     {
         if (!arrayViews_.append(ArrayView(varName, vt)))
             return false;
@@ -1825,44 +1822,48 @@ class MOZ_STACK_CLASS ModuleValidator
         if (!module().addProfiledFunction(Move(pf)))
             return false;
 #endif
 
         return true;
     }
 
     bool finishGeneratingEntry(unsigned exportIndex, AsmJSOffsets offsets) {
-        MOZ_ASSERT(finishedFunctionBodies_);
         module_->exportedFunction(exportIndex).initCodeOffset(offsets.begin);
         return module_->addCodeRange(AsmJSModule::CodeRange::Entry, offsets);
     }
     bool finishGeneratingInlineStub(AsmJSOffsets offsets) {
-        MOZ_ASSERT(finishedFunctionBodies_);
         return module_->addCodeRange(AsmJSModule::CodeRange::Inline, offsets);
     }
     bool finishGeneratingInterpExit(unsigned exitIndex, AsmJSProfilingOffsets offsets) {
-        MOZ_ASSERT(finishedFunctionBodies_);
         module_->exit(exitIndex).initInterpOffset(offsets.begin);
         return module_->addCodeRange(AsmJSModule::CodeRange::SlowFFI, offsets);
     }
     bool finishGeneratingJitExit(unsigned exitIndex, AsmJSProfilingOffsets offsets) {
-        MOZ_ASSERT(finishedFunctionBodies_);
         module_->exit(exitIndex).initJitOffset(offsets.begin);
         return module_->addCodeRange(AsmJSModule::CodeRange::JitFFI, offsets);
     }
     bool finishGeneratingInterrupt(AsmJSProfilingOffsets offsets) {
-        MOZ_ASSERT(finishedFunctionBodies_);
         return module_->addCodeRange(AsmJSModule::CodeRange::Interrupt, offsets);
     }
     bool finishGeneratingBuiltinThunk(AsmJSExit::BuiltinKind builtin, AsmJSProfilingOffsets offsets) {
-        MOZ_ASSERT(finishedFunctionBodies_);
         return module_->addBuiltinThunkCodeRange(builtin, offsets);
     }
 
     bool finish(ScopedJSDeletePtr<AsmJSModule>* module) {
+        // Patch internal calls to their final positions
+        for (auto& cs : masm().callSites()) {
+            if (!cs.isInternal())
+                continue;
+            MOZ_ASSERT(cs.kind() == CallSiteDesc::Relative);
+            uint32_t callerOffset = cs.returnAddressOffset();
+            uint32_t calleeOffset = functionEntryOffset(cs.targetIndex());
+            masm().patchCall(callerOffset, calleeOffset);
+        }
+
         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
@@ -1887,32 +1888,19 @@ class MOZ_STACK_CLASS ModuleValidator
         if (atomicsPresent_) {
             for (GlobalMap::Range r = globals_.all() ; !r.empty() ; r.popFront()) {
                 Global* g = r.front().value();
                 if (g->isAnyArrayView())
                     g->setViewIsSharedView();
             }
             module_->setViewsAreShared();
         }
-        module_->startFunctionBodies();
     }
     bool finishFunctionBodies() {
-        // Patch internal calls to their final positions
-        for (auto& cs : masm().callSites()) {
-            if (!cs.isInternal())
-                continue;
-            MOZ_ASSERT(cs.kind() == CallSiteDesc::Relative);
-            uint32_t callerOffset = cs.returnAddressOffset();
-            uint32_t calleeOffset = functionEntryOffset(cs.targetIndex());
-            masm().patchCall(callerOffset, calleeOffset);
-        }
-
-        MOZ_ASSERT(!finishedFunctionBodies_);
-        module_->finishFunctionBodies(masm().currentOffset());
-        finishedFunctionBodies_ = true;
+        module_->setFunctionBytes(masm().currentOffset());
         return true;
     }
 
     void buildCompilationTimeReport(JS::AsmJSCacheResult cacheResult, ScopedJSFreePtr<char>* out) {
 #ifndef JS_MORE_DETERMINISTIC
         ScopedJSFreePtr<char> slowFuns;
         int64_t usecAfter = PRMJ_Now();
         int msTotal = (usecAfter - usecBefore_) / PRMJ_USEC_PER_MSEC;
@@ -3240,22 +3228,17 @@ CheckVarRef(FunctionValidator& f, ParseN
               case Type::Int:       f.writeOp(I32::GetGlobal);   break;
               case Type::Double:    f.writeOp(F64::GetGlobal);   break;
               case Type::Float:     f.writeOp(F32::GetGlobal);   break;
               case Type::Int32x4:   f.writeOp(I32X4::GetGlobal); break;
               case Type::Float32x4: f.writeOp(F32X4::GetGlobal); break;
               default: MOZ_CRASH("unexpected global type");
             }
 
-            uint32_t globalIndex = global->varOrConstIndex();
-            // Write the global data offset
-            if (global->varOrConstType().isSimd())
-                f.writeU32(f.module().globalSimdVarIndexToGlobalDataOffset(globalIndex));
-            else
-                f.writeU32(f.module().globalScalarVarIndexToGlobalDataOffset(globalIndex));
+            f.writeU32(global->varOrConstGlobalDataOffset());
             f.writeU8(uint8_t(global->isConst()));
             *type = global->varOrConstType();
             break;
           }
           case ModuleValidator::Global::Function:
           case ModuleValidator::Global::FFI:
           case ModuleValidator::Global::MathBuiltinFunction:
           case ModuleValidator::Global::AtomicsBuiltinFunction:
@@ -3631,22 +3614,17 @@ CheckAssignName(FunctionValidator& f, Pa
           case Type::Int:       f.patchOp(opcodeAt, I32::SetGlobal);   break;
           case Type::Float:     f.patchOp(opcodeAt, F32::SetGlobal);   break;
           case Type::Double:    f.patchOp(opcodeAt, F64::SetGlobal);   break;
           case Type::Int32x4:   f.patchOp(opcodeAt, I32X4::SetGlobal); break;
           case Type::Float32x4: f.patchOp(opcodeAt, F32X4::SetGlobal); break;
           default: MOZ_CRASH("unexpected global type");
         }
 
-        unsigned globalIndex = global->varOrConstIndex();
-        // Global data offset
-        if (global->varOrConstType().isSimd())
-            f.patch32(indexAt, f.module().globalSimdVarIndexToGlobalDataOffset(globalIndex));
-        else
-            f.patch32(indexAt, f.module().globalScalarVarIndexToGlobalDataOffset(globalIndex));
+        f.patch32(indexAt, global->varOrConstGlobalDataOffset());
         *type = rhsType;
         return true;
     }
 
     return f.failName(lhs, "'%s' not found in local or asm.js module scope", name);
 }
 
 static bool
@@ -4360,17 +4338,17 @@ CheckFFICall(FunctionValidator& f, Parse
     MallocSig sig(Move(args), ret);
 
     unsigned exitIndex = 0;
     const LifoSig* lifoSig = nullptr;
     if (!f.m().addExit(ffiIndex, calleeName, sig, &exitIndex, &lifoSig))
         return false;
 
     JS_STATIC_ASSERT(offsetof(AsmJSModule::ExitDatum, exit) == 0);
-    f.patch32(offsetAt, f.module().exitIndexToGlobalDataOffset(exitIndex));
+    f.patch32(offsetAt, f.module().exit(exitIndex).globalDataOffset());
     f.patchSig(sigAt, lifoSig);
     *type = Type::ret(ret);
     return true;
 }
 
 static bool
 CheckFloatCoercionArg(FunctionValidator& f, ParseNode* inputNode, Type inputType,
                       size_t opcodeAt)
@@ -7720,17 +7698,17 @@ GenerateFFIIonExit(ModuleValidator& m, c
     masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(masm.getStackPointer(), argOffset));
     argOffset += sizeof(size_t);
 
     // 2. Callee
     Register callee = ABIArgGenerator::NonArgReturnReg0;   // live until call
     Register scratch = ABIArgGenerator::NonArgReturnReg1;  // repeatedly clobbered
 
     // 2.1. Get ExitDatum
-    unsigned globalDataOffset = m.module().exitIndexToGlobalDataOffset(exitIndex);
+    unsigned globalDataOffset = m.module().exit(exitIndex).globalDataOffset();
 #if defined(JS_CODEGEN_X64)
     m.masm().append(AsmJSGlobalAccess(masm.leaRipRelative(callee), globalDataOffset));
 #elif defined(JS_CODEGEN_X86)
     m.masm().append(AsmJSGlobalAccess(masm.movlWithPatch(Imm32(0), callee), globalDataOffset));
 #elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     masm.computeEffectiveAddress(Address(GlobalReg, globalDataOffset - AsmJSGlobalRegBias), callee);
 #endif
 
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -492,34 +492,30 @@ BaselineScript::Destroy(FreeOp* fop, Bas
 void
 BaselineScript::clearDependentAsmJSModules()
 {
     // Remove any links from AsmJSModules that contain optimized FFI calls into
     // this BaselineScript.
     if (dependentAsmJSModules_) {
         for (size_t i = 0; i < dependentAsmJSModules_->length(); i++) {
             DependentAsmJSModuleExit exit = (*dependentAsmJSModules_)[i];
-            exit.module->detachJitCompilation(exit.exitIndex);
+            exit.module->exit(exit.exitIndex).deoptimize(*exit.module);
         }
 
         dependentAsmJSModules_->clear();
     }
 }
 
 void
 BaselineScript::unlinkDependentAsmJSModules(FreeOp* fop)
 {
     // Remove any links from AsmJSModules that contain optimized FFI calls into
     // this BaselineScript.
+    clearDependentAsmJSModules();
     if (dependentAsmJSModules_) {
-        for (size_t i = 0; i < dependentAsmJSModules_->length(); i++) {
-            DependentAsmJSModuleExit exit = (*dependentAsmJSModules_)[i];
-            exit.module->detachJitCompilation(exit.exitIndex);
-        }
-
         fop->delete_(dependentAsmJSModules_);
         dependentAsmJSModules_ = nullptr;
     }
 }
 
 bool
 BaselineScript::addDependentAsmJSModule(JSContext* cx, DependentAsmJSModuleExit exit)
 {
--- a/js/src/jit/PerfSpewer.cpp
+++ b/js/src/jit/PerfSpewer.cpp
@@ -331,24 +331,9 @@ js::jit::writePerfSpewerAsmJSFunctionMap
         return;
 
     fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxPTR " %s:%u:%u: Function %s\n",
             base, size, filename, lineno, colIndex, funcName);
 
     unlockPerfMap();
 }
 
-void
-js::jit::writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size)
-{
-    if (size == 0)
-        return;
-
-    if (!lockPerfMap())
-        return;
-
-    fprintf(PerfFilePtr, "%" PRIxPTR " %" PRIxSIZE " AsmJS Entries and Exits (0x%" PRIxPTR " 0x%" PRIxSIZE ")\n",
-            base, size, base, size);
-
-    unlockPerfMap();
-}
-
 #endif // defined (JS_ION_PERF)
--- a/js/src/jit/PerfSpewer.h
+++ b/js/src/jit/PerfSpewer.h
@@ -82,16 +82,14 @@ class AsmJSPerfSpewer : public PerfSpewe
   public:
     bool startBasicBlock(MBasicBlock* blk, MacroAssembler& masm) { return true; }
     bool endBasicBlock(MacroAssembler& masm) { return true; }
 };
 
 void writePerfSpewerAsmJSFunctionMap(uintptr_t base, uintptr_t size, const char* filename,
                                      unsigned lineno, unsigned colIndex, const char* funcName);
 
-void writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size);
-
 #endif // JS_ION_PERF
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_PerfSpewer_h */