Bug 1103056 - Make optimized asm.js FFI exit also work with Baseline scripts. r=luke,h4writer
authorJan de Mooij <jdemooij@mozilla.com>
Tue, 25 Nov 2014 13:43:30 +0100
changeset 217478 9d885b1029113b2d6486dff74949792e012efa8c
parent 217477 9dffe0f511c9366173592c09ebf5d1f432320d2f
child 217479 2b255e983a41c223918f434ba9888c3990ed672b
push id12397
push userryanvm@gmail.com
push dateTue, 25 Nov 2014 22:01:31 +0000
treeherderb2g-inbound@05c138a502c2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke, h4writer
bugs1103056
milestone36.0a1
Bug 1103056 - Make optimized asm.js FFI exit also work with Baseline scripts. r=luke,h4writer
js/src/asmjs/AsmJSFrameIterator.cpp
js/src/asmjs/AsmJSFrameIterator.h
js/src/asmjs/AsmJSModule.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSValidate.cpp
js/src/jit/BaselineJIT.cpp
js/src/jit/BaselineJIT.h
js/src/jit/Ion.cpp
js/src/jit/IonCode.h
js/src/jit/arm/MacroAssembler-arm.cpp
js/src/jit/arm/MacroAssembler-arm.h
js/src/jit/mips/MacroAssembler-mips.cpp
js/src/jit/mips/MacroAssembler-mips.h
js/src/jit/none/MacroAssembler-none.h
js/src/jit/shared/MacroAssembler-x86-shared.h
--- a/js/src/asmjs/AsmJSFrameIterator.cpp
+++ b/js/src/asmjs/AsmJSFrameIterator.cpp
@@ -74,17 +74,17 @@ AsmJSFrameIterator::settle()
       case AsmJSModule::CodeRange::Function:
         callsite_ = module_->lookupCallSite(returnAddress);
         MOZ_ASSERT(callsite_);
         break;
       case AsmJSModule::CodeRange::Entry:
         fp_ = nullptr;
         MOZ_ASSERT(done());
         break;
-      case AsmJSModule::CodeRange::IonFFI:
+      case AsmJSModule::CodeRange::JitFFI:
       case AsmJSModule::CodeRange::SlowFFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Inline:
       case AsmJSModule::CodeRange::Thunk:
         MOZ_CRASH("Should not encounter an exit during iteration");
     }
 }
 
@@ -453,17 +453,17 @@ AsmJSProfilingFrameIterator::initFromFP(
         callerFP_ = nullptr;
         break;
       case AsmJSModule::CodeRange::Function:
         fp = CallerFPFromFP(fp);
         callerPC_ = ReturnAddressFromFP(fp);
         callerFP_ = CallerFPFromFP(fp);
         AssertMatchesCallSite(*module_, codeRange, callerPC_, callerFP_, fp);
         break;
-      case AsmJSModule::CodeRange::IonFFI:
+      case AsmJSModule::CodeRange::JitFFI:
       case AsmJSModule::CodeRange::SlowFFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Inline:
       case AsmJSModule::CodeRange::Thunk:
         MOZ_CRASH("Unexpected CodeRange kind");
     }
 
     // Since, despite the above reasoning for skipping a frame, we do want FFI
@@ -508,17 +508,17 @@ AsmJSProfilingFrameIterator::AsmJSProfil
     }
 
     // Note: fp may be null while entering and leaving the activation.
     uint8_t *fp = activation.fp();
 
     const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(state.pc);
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Function:
-      case AsmJSModule::CodeRange::IonFFI:
+      case AsmJSModule::CodeRange::JitFFI:
       case AsmJSModule::CodeRange::SlowFFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Thunk: {
         // While codeRange describes the *current* frame, the fp/pc state stored in
         // the iterator is the *caller's* frame. The reason for this is that the
         // activation.fp isn't always the AsmJSFrame for state.pc; during the
         // prologue/epilogue, activation.fp will point to the caller's frame.
         // Naively unwinding starting at activation.fp could thus lead to the
@@ -610,17 +610,17 @@ AsmJSProfilingFrameIterator::operator++(
 
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Entry:
         MOZ_ASSERT(callerFP_ == nullptr);
         MOZ_ASSERT(callerPC_ != nullptr);
         callerPC_ = nullptr;
         break;
       case AsmJSModule::CodeRange::Function:
-      case AsmJSModule::CodeRange::IonFFI:
+      case AsmJSModule::CodeRange::JitFFI:
       case AsmJSModule::CodeRange::SlowFFI:
       case AsmJSModule::CodeRange::Interrupt:
       case AsmJSModule::CodeRange::Inline:
       case AsmJSModule::CodeRange::Thunk:
         stackAddress_ = callerFP_;
         callerPC_ = ReturnAddressFromFP(callerFP_);
         AssertMatchesCallSite(*module_, codeRange, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
         callerFP_ = CallerFPFromFP(callerFP_);
@@ -667,38 +667,38 @@ AsmJSProfilingFrameIterator::label() con
 {
     MOZ_ASSERT(!done());
 
     // Use the same string for both time inside and under so that the two
     // entries will be coalesced by the profiler.
     //
     // NB: these labels are regexp-matched by
     //     browser/devtools/profiler/cleopatra/js/parserWorker.js.
-    const char *ionFFIDescription = "fast FFI trampoline (in asm.js)";
+    const char *jitFFIDescription = "fast FFI trampoline (in asm.js)";
     const char *slowFFIDescription = "slow FFI trampoline (in asm.js)";
     const char *interruptDescription = "interrupt due to out-of-bounds or long execution (in asm.js)";
 
     switch (AsmJSExit::ExtractReasonKind(exitReason_)) {
       case AsmJSExit::Reason_None:
         break;
-      case AsmJSExit::Reason_IonFFI:
-        return ionFFIDescription;
+      case AsmJSExit::Reason_JitFFI:
+        return jitFFIDescription;
       case AsmJSExit::Reason_SlowFFI:
         return slowFFIDescription;
       case AsmJSExit::Reason_Interrupt:
         return interruptDescription;
       case AsmJSExit::Reason_Builtin:
         return BuiltinToName(AsmJSExit::ExtractBuiltinKind(exitReason_));
     }
 
     auto codeRange = reinterpret_cast<const AsmJSModule::CodeRange*>(codeRange_);
     switch (codeRange->kind()) {
       case AsmJSModule::CodeRange::Function:  return codeRange->functionProfilingLabel(*module_);
       case AsmJSModule::CodeRange::Entry:     return "entry trampoline (in asm.js)";
-      case AsmJSModule::CodeRange::IonFFI:    return ionFFIDescription;
+      case AsmJSModule::CodeRange::JitFFI:    return jitFFIDescription;
       case AsmJSModule::CodeRange::SlowFFI:   return slowFFIDescription;
       case AsmJSModule::CodeRange::Interrupt: return interruptDescription;
       case AsmJSModule::CodeRange::Inline:    return "inline stub (in asm.js)";
       case AsmJSModule::CodeRange::Thunk:     return BuiltinToName(codeRange->thunkTarget());
     }
 
     MOZ_CRASH("Bad exit kind");
 }
--- a/js/src/asmjs/AsmJSFrameIterator.h
+++ b/js/src/asmjs/AsmJSFrameIterator.h
@@ -64,17 +64,17 @@ namespace AsmJSExit
 {
     // List of reasons for execution leaving asm.js-generated code, stored in
     // AsmJSActivation. The initial and default state is AsmJSNoExit. If
     // AsmJSNoExit is observed when the pc isn't in asm.js code, execution must
     // have been interrupted asynchronously (viz., by a exception/signal
     // handler).
     enum ReasonKind {
         Reason_None,
-        Reason_IonFFI,
+        Reason_JitFFI,
         Reason_SlowFFI,
         Reason_Interrupt,
         Reason_Builtin
     };
 
     // For Reason_Builtin, the list of builtins, so they can be displayed in the
     // profile call stack.
     enum BuiltinKind {
@@ -101,17 +101,17 @@ namespace AsmJSExit
         Builtin_Limit
     };
 
     // A Reason contains both a ReasonKind and (if Reason_Builtin) a
     // BuiltinKind.
     typedef uint32_t Reason;
 
     static const uint32_t None = Reason_None;
-    static const uint32_t IonFFI = Reason_IonFFI;
+    static const uint32_t JitFFI = Reason_JitFFI;
     static const uint32_t SlowFFI = Reason_SlowFFI;
     static const uint32_t Interrupt = Reason_Interrupt;
     static inline Reason Builtin(BuiltinKind builtin) {
         return uint16_t(Reason_Builtin) | (uint16_t(builtin) << 16);
     }
     static inline ReasonKind ExtractReasonKind(Reason reason) {
         return ReasonKind(uint16_t(reason));
     }
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -100,21 +100,21 @@ AsmJSModule::~AsmJSModule()
 {
     MOZ_ASSERT(!interrupted_);
 
     scriptSource_->decref();
 
     if (code_) {
         for (unsigned i = 0; i < numExits(); i++) {
             AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i);
-            if (!exitDatum.ionScript)
+            if (!exitDatum.baselineScript)
                 continue;
 
             jit::DependentAsmJSModuleExit exit(this, i);
-            exitDatum.ionScript->removeDependentAsmJSModule(exit);
+            exitDatum.baselineScript->removeDependentAsmJSModule(exit);
         }
 
         DeallocateExecutableMemory(code_, pod.totalBytes_, AsmJSPageSize);
     }
 
     for (size_t i = 0; i < numFunctionCounts(); i++)
         js_delete(functionCounts(i));
 
@@ -496,69 +496,77 @@ CoerceInPlace_ToNumber(MutableHandleValu
     if (!ToNumber(cx, val, &dbl))
         return false;
     val.set(DoubleValue(dbl));
 
     return true;
 }
 
 static bool
-TryEnablingIon(JSContext *cx, AsmJSModule &module, HandleFunction fun, uint32_t exitIndex,
+TryEnablingJit(JSContext *cx, AsmJSModule &module, HandleFunction fun, uint32_t exitIndex,
                int32_t argc, Value *argv)
 {
     if (!fun->hasScript())
         return true;
 
-    // Test if the function is Ion compiled
+    // Test if the function is JIT compiled.
     JSScript *script = fun->nonLazyScript();
-    if (!script->hasIonScript())
+    if (!script->hasBaselineScript()) {
+        MOZ_ASSERT(!script->hasIonScript());
         return true;
+    }
 
     // Currently we can't rectify arguments. Therefore disabling if argc is too low.
     if (fun->nargs() > size_t(argc))
         return true;
 
-    // Normally the types should correspond, since we just ran with those types,
-    // but there are reports this is asserting. Therefore doing it as a check, instead of DEBUG only.
+    // Ensure the argument types are included in the argument TypeSets stored in
+    // the TypeScript. This is necessary for Ion, because the FFI exit will
+    // use the skip-arg-checks entry point.
+    //
+    // Note that the TypeScript is never discarded while the script has a
+    // BaselineScript, so if those checks hold now they must hold at least until
+    // the BaselineScript is discarded and when that happens the FFI exit is
+    // patched back.
     if (!types::TypeScript::ThisTypes(script)->hasType(types::Type::UndefinedType()))
         return true;
     for (uint32_t i = 0; i < fun->nargs(); i++) {
         types::StackTypeSet *typeset = types::TypeScript::ArgTypes(script, i);
         types::Type type = types::Type::DoubleType();
         if (!argv[i].isDouble())
             type = types::Type::PrimitiveType(argv[i].extractNonDoubleType());
         if (!typeset->hasType(type))
             return true;
     }
 
     // The exit may have become optimized while executing the FFI.
     if (module.exitIsOptimized(exitIndex))
         return true;
 
-    IonScript *ionScript = script->ionScript();
-    if (!ionScript->addDependentAsmJSModule(cx, DependentAsmJSModuleExit(&module, exitIndex)))
+    BaselineScript *baselineScript = script->baselineScript();
+    if (!baselineScript->addDependentAsmJSModule(cx, DependentAsmJSModuleExit(&module, exitIndex)))
         return false;
 
-    module.optimizeExit(exitIndex, ionScript);
+    module.optimizeExit(exitIndex, 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);
     RootedValue fval(cx, ObjectValue(*fun));
     if (!Invoke(cx, UndefinedValue(), fval, argc, argv, rval))
         return false;
 
-    return TryEnablingIon(cx, module, fun, exitIndex, argc, argv);
+    return TryEnablingJit(cx, module, fun, exitIndex, argc, argv);
 }
 
 // Use an int32_t return type instead of bool since bool does not have a
 // specified width and the caller is assuming a word-sized return.
 static int32_t
 InvokeFromAsmJS_Ignore(int32_t exitIndex, int32_t argc, Value *argv)
 {
     AsmJSActivation *activation = PerThreadData::innermostAsmJSActivation();
@@ -743,17 +751,17 @@ AsmJSModule::staticallyLink(ExclusiveCon
     }
 
     // Initialize global data segment
 
     for (size_t i = 0; i < exits_.length(); i++) {
         AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i);
         exitDatum.exit = interpExitTrampoline(exits_[i]);
         exitDatum.fun = nullptr;
-        exitDatum.ionScript = nullptr;
+        exitDatum.baselineScript = nullptr;
     }
 
     MOZ_ASSERT(isStaticallyLinked());
 }
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
 static inline size_t
 ViewTypeByteSize(AsmJSHeapAccess::ViewType vt)
@@ -899,17 +907,17 @@ AsmJSModule::detachHeap(JSContext *cx)
     if (interrupted_) {
         JS_ReportError(cx, "attempt to detach from inside interrupt handler");
         return false;
     }
 
     // Even if this->active(), to reach here, the activation must have called
     // out via an FFI stub. FFI stubs check if heapDatum() is null on reentry
     // and throw an exception if so.
-    MOZ_ASSERT_IF(active(), activation()->exitReason() == AsmJSExit::Reason_IonFFI ||
+    MOZ_ASSERT_IF(active(), activation()->exitReason() == AsmJSExit::Reason_JitFFI ||
                             activation()->exitReason() == AsmJSExit::Reason_SlowFFI);
 
     restoreHeapToInitialState(maybeHeap_);
 
     MOZ_ASSERT(hasDetachedHeap());
     return true;
 }
 
@@ -1333,17 +1341,17 @@ AsmJSModule::CodeRange::CodeRange(Kind k
   : begin_(begin),
     profilingReturn_(profilingReturn),
     end_(end)
 {
     u.kind_ = kind;
 
     MOZ_ASSERT(begin_ < profilingReturn_);
     MOZ_ASSERT(profilingReturn_ < end_);
-    MOZ_ASSERT(u.kind_ == IonFFI || u.kind_ == SlowFFI || u.kind_ == Interrupt);
+    MOZ_ASSERT(u.kind_ == JitFFI || u.kind_ == SlowFFI || u.kind_ == Interrupt);
 }
 
 AsmJSModule::CodeRange::CodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin,
                                   uint32_t profilingReturn, uint32_t end)
   : begin_(begin),
     profilingReturn_(profilingReturn),
     end_(end)
 {
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -366,43 +366,43 @@ class AsmJSModule
         bool clone(ExclusiveContext *cx, Global *out) const;
     };
 
     class Exit
     {
         unsigned ffiIndex_;
         unsigned globalDataOffset_;
         unsigned interpCodeOffset_;
-        unsigned ionCodeOffset_;
+        unsigned jitCodeOffset_;
 
         friend class AsmJSModule;
 
       public:
         Exit() {}
         Exit(unsigned ffiIndex, unsigned globalDataOffset)
           : ffiIndex_(ffiIndex), globalDataOffset_(globalDataOffset),
-            interpCodeOffset_(0), ionCodeOffset_(0)
+            interpCodeOffset_(0), jitCodeOffset_(0)
         {}
         unsigned ffiIndex() const {
             return ffiIndex_;
         }
         unsigned globalDataOffset() const {
             return globalDataOffset_;
         }
         void initInterpOffset(unsigned off) {
             MOZ_ASSERT(!interpCodeOffset_);
             interpCodeOffset_ = off;
         }
-        void initIonOffset(unsigned off) {
-            MOZ_ASSERT(!ionCodeOffset_);
-            ionCodeOffset_ = off;
+        void initJitOffset(unsigned off) {
+            MOZ_ASSERT(!jitCodeOffset_);
+            jitCodeOffset_ = off;
         }
         void updateOffsets(jit::MacroAssembler &masm) {
             interpCodeOffset_ = masm.actualOffset(interpCodeOffset_);
-            ionCodeOffset_ = masm.actualOffset(ionCodeOffset_);
+            jitCodeOffset_ = masm.actualOffset(jitCodeOffset_);
         }
 
         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;
     };
 
@@ -414,17 +414,17 @@ class AsmJSModule
     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::IonScript *ionScript;
+        jit::BaselineScript *baselineScript;
         HeapPtrFunction fun;
     };
 
     typedef Vector<AsmJSCoercion, 0, SystemAllocPolicy> ArgCoercionVector;
 
     enum ReturnType { Return_Int32, Return_Double, Return_Int32x4, Return_Float32x4, Return_Void };
 
     class ExportedFunction
@@ -553,29 +553,29 @@ class AsmJSModule
                 uint16_t target_;
             } thunk;
             uint8_t kind_;
         } u;
 
         void setDeltas(uint32_t entry, uint32_t profilingJump, uint32_t profilingEpilogue);
 
       public:
-        enum Kind { Function, Entry, IonFFI, SlowFFI, Interrupt, Thunk, Inline };
+        enum Kind { Function, Entry, JitFFI, SlowFFI, Interrupt, Thunk, Inline };
 
         CodeRange() {}
         CodeRange(uint32_t nameIndex, uint32_t lineNumber, const AsmJSFunctionLabels &l);
         CodeRange(Kind kind, uint32_t begin, uint32_t end);
         CodeRange(Kind kind, uint32_t begin, uint32_t profilingReturn, uint32_t end);
         CodeRange(AsmJSExit::BuiltinKind builtin, uint32_t begin, uint32_t pret, uint32_t end);
         void updateOffsets(jit::MacroAssembler &masm);
 
         Kind kind() const { return Kind(u.kind_); }
         bool isFunction() const { return kind() == Function; }
         bool isEntry() const { return kind() == Entry; }
-        bool isFFI() const { return kind() == IonFFI || kind() == SlowFFI; }
+        bool isFFI() const { return kind() == JitFFI || kind() == SlowFFI; }
         bool isInterrupt() const { return kind() == Interrupt; }
         bool isThunk() const { return kind() == Thunk; }
 
         uint32_t begin() const {
             return begin_;
         }
         uint32_t entry() const {
             MOZ_ASSERT(isFunction());
@@ -1320,20 +1320,20 @@ class AsmJSModule
         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 *ionExitTrampoline(const Exit &exit) const {
+    uint8_t *jitExitTrampoline(const Exit &exit) const {
         MOZ_ASSERT(isFinished());
-        MOZ_ASSERT(exit.ionCodeOffset_);
-        return code_ + exit.ionCodeOffset_;
+        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
@@ -1458,27 +1458,27 @@ class AsmJSModule
         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::IonScript *ionScript) const {
+    void optimizeExit(unsigned exitIndex, jit::BaselineScript *baselineScript) const {
         MOZ_ASSERT(!exitIsOptimized(exitIndex));
         ExitDatum &exitDatum = exitIndexToGlobalDatum(exitIndex);
-        exitDatum.exit = ionExitTrampoline(exit(exitIndex));
-        exitDatum.ionScript = ionScript;
+        exitDatum.exit = jitExitTrampoline(exit(exitIndex));
+        exitDatum.baselineScript = baselineScript;
     }
-    void detachIonCompilation(size_t exitIndex) const {
+    void detachJitCompilation(size_t exitIndex) const {
         MOZ_ASSERT(isFinished());
         ExitDatum &exitDatum = exitIndexToGlobalDatum(exitIndex);
         exitDatum.exit = interpExitTrampoline(exit(exitIndex));
-        exitDatum.ionScript = nullptr;
+        exitDatum.baselineScript = nullptr;
     }
 
     /*************************************************************************/
     // These functions are called after finish() but before staticallyLink():
 
     bool addRelativeLink(RelativeLink link) {
         MOZ_ASSERT(isFinished() && !isStaticallyLinked());
         return staticLinkData_.relativeLinks.append(link);
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -1865,23 +1865,23 @@ class MOZ_STACK_CLASS ModuleCompiler
     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 finishGeneratingIonExit(unsigned exitIndex, Label *begin, Label *profilingReturn) {
+    bool finishGeneratingJitExit(unsigned exitIndex, Label *begin, Label *profilingReturn) {
         MOZ_ASSERT(finishedFunctionBodies_);
         uint32_t beg = begin->offset();
-        module_->exit(exitIndex).initIonOffset(beg);
+        module_->exit(exitIndex).initJitOffset(beg);
         uint32_t pret = profilingReturn->offset();
         uint32_t end = masm_.currentOffset();
-        return module_->addCodeRange(AsmJSModule::CodeRange::IonFFI, beg, pret, end);
+        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);
     }
@@ -8448,17 +8448,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
     coerceArgTypes.infallibleAppend(MIRType_Pointer); // argv
     unsigned offsetToCoerceArgv = AlignBytes(StackArgBytes(coerceArgTypes), sizeof(double));
     unsigned totalCoerceBytes = offsetToCoerceArgv + sizeof(Value) + MaybeSavedGlobalReg;
     unsigned coerceFrameSize = StackDecrementForCall(masm, AsmJSStackAlignment, totalCoerceBytes);
 
     unsigned framePushed = Max(ionFrameSize, coerceFrameSize);
 
     Label begin;
-    GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::IonFFI, &begin);
+    GenerateAsmJSExitPrologue(masm, framePushed, AsmJSExit::JitFFI, &begin);
 
     // 1. Descriptor
     size_t argOffset = offsetToIonArgs;
     uint32_t descriptor = MakeFrameDescriptor(framePushed, JitFrame_Entry);
     masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(StackPointer, argOffset));
     argOffset += sizeof(size_t);
 
     // 2. Callee
@@ -8496,17 +8496,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
     argOffset += sizeof(Value);
 
     // 5. Fill the arguments
     unsigned offsetToCallerStackArgs = framePushed + sizeof(AsmJSFrame);
     FillArgumentArray(m, exit.sig().args(), argOffset, offsetToCallerStackArgs, scratch);
     argOffset += exit.sig().args().length() * sizeof(Value);
     MOZ_ASSERT(argOffset == offsetToIonArgs + ionArgBytes);
 
-    // 6. Ion will clobber all registers, even non-volatiles. GlobalReg and
+    // 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.
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
     JS_STATIC_ASSERT(MaybeSavedGlobalReg > 0);
     unsigned savedGlobalOffset = framePushed - MaybeSavedGlobalReg;
@@ -8548,17 +8548,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
         masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitTop()));
         masm.loadPtr(Address(reg0, offsetOfJitJSContext), reg2);
         masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitJSContext()));
         masm.storePtr(reg3, Address(reg0, offsetOfJitJSContext));
     }
 
     // 2. Call
     AssertStackAlignment(masm, AsmJSStackAlignment);
-    masm.callIonFromAsmJS(callee);
+    masm.callJitFromAsmJS(callee);
     AssertStackAlignment(masm, AsmJSStackAlignment);
 
     {
         // Disable Activation.
         //
         // This sequence needs three registers, and must preserve the JSReturnReg_Data and
         // JSReturnReg_Type, so there are five live registers.
         MOZ_ASSERT(JSReturnReg_Data == AsmJSIonExitRegReturnData);
@@ -8620,17 +8620,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
 #endif
 
     // The heap pointer has to be reloaded anyway since Ion could have clobbered
     // it. Additionally, the FFI may have detached the heap buffer.
     masm.loadAsmJSHeapRegisterFromGlobalData();
     GenerateCheckForHeapDetachment(m, ABIArgGenerator::NonReturn_VolatileReg0);
 
     Label profilingReturn;
-    GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::IonFFI, &profilingReturn);
+    GenerateAsmJSExitEpilogue(masm, framePushed, AsmJSExit::JitFFI, &profilingReturn);
 
     if (oolConvert.used()) {
         masm.bind(&oolConvert);
         masm.setFramePushed(framePushed);
 
         // Store return value into argv[0]
         masm.storeValue(JSReturnOperand, Address(StackPointer, offsetToCoerceArgv));
 
@@ -8664,17 +8664,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
         }
 
         masm.jump(&done);
         masm.setFramePushed(0);
     }
 
     MOZ_ASSERT(masm.framePushed() == 0);
 
-    return m.finishGeneratingIonExit(exitIndex, &begin, &profilingReturn) && !masm.oom();
+    return m.finishGeneratingJitExit(exitIndex, &begin, &profilingReturn) && !masm.oom();
 }
 
 // See "asm.js FFI calls" comment above.
 static bool
 GenerateFFIExits(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit, unsigned exitIndex,
                  Label *throwLabel)
 {
     // Generate the slow path through the interpreter
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -3,16 +3,17 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jit/BaselineJIT.h"
 
 #include "mozilla/MemoryReporting.h"
 
+#include "asmjs/AsmJSModule.h"
 #include "jit/BaselineCompiler.h"
 #include "jit/BaselineIC.h"
 #include "jit/CompileInfo.h"
 #include "jit/JitCommon.h"
 #include "jit/JitSpewer.h"
 #include "vm/Interpreter.h"
 #include "vm/TraceLogging.h"
 
@@ -40,16 +41,17 @@ PCMappingSlotInfo::ToSlotLocation(const 
     return SlotIgnore;
 }
 
 BaselineScript::BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset,
                                uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset)
   : method_(nullptr),
     templateScope_(nullptr),
     fallbackStubSpace_(),
+    dependentAsmJSModules_(nullptr),
     prologueOffset_(prologueOffset),
     epilogueOffset_(epilogueOffset),
 #ifdef DEBUG
     spsOn_(false),
 #endif
     spsPushToggleOffset_(spsPushToggleOffset),
     postDebugPrologueOffset_(postDebugPrologueOffset),
     flags_(0)
@@ -435,19 +437,64 @@ BaselineScript::Destroy(FreeOp *fop, Bas
      * will contain entries refering to the referenced things. Since we can
      * destroy scripts outside the context of a GC, this situation can result
      * in invalid store buffer entries. Assert that if we do destroy scripts
      * outside of a GC that we at least emptied the nursery first.
      */
     MOZ_ASSERT(fop->runtime()->gc.nursery.isEmpty());
 #endif
 
+    script->unlinkDependentAsmJSModules(fop);
+
     fop->delete_(script);
 }
 
+void
+BaselineScript::unlinkDependentAsmJSModules(FreeOp *fop)
+{
+    // 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);
+        }
+
+        fop->delete_(dependentAsmJSModules_);
+        dependentAsmJSModules_ = nullptr;
+    }
+}
+
+bool
+BaselineScript::addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit)
+{
+    if (!dependentAsmJSModules_) {
+        dependentAsmJSModules_ = cx->new_<Vector<DependentAsmJSModuleExit> >(cx);
+        if (!dependentAsmJSModules_)
+            return false;
+    }
+    return dependentAsmJSModules_->append(exit);
+}
+
+void
+BaselineScript::removeDependentAsmJSModule(DependentAsmJSModuleExit exit)
+{
+    if (!dependentAsmJSModules_)
+        return;
+
+    for (size_t i = 0; i < dependentAsmJSModules_->length(); i++) {
+        if ((*dependentAsmJSModules_)[i].module == exit.module &&
+            (*dependentAsmJSModules_)[i].exitIndex == exit.exitIndex)
+        {
+            dependentAsmJSModules_->erase(dependentAsmJSModules_->begin() + i);
+            break;
+        }
+    }
+}
+
 ICEntry &
 BaselineScript::icEntry(size_t index)
 {
     MOZ_ASSERT(index < numICEntries());
     return icEntryList()[index];
 }
 
 PCMappingIndexEntry &
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -88,16 +88,29 @@ struct PCMappingIndexEntry
 
     // Native code offset.
     uint32_t nativeOffset;
 
     // Offset in the CompactBuffer where data for pcOffset starts.
     uint32_t bufferOffset;
 };
 
+// Describes a single AsmJSModule which jumps (via an FFI exit with the given
+// index) directly to a BaselineScript or IonScript.
+struct DependentAsmJSModuleExit
+{
+    const AsmJSModule *module;
+    size_t exitIndex;
+
+    DependentAsmJSModuleExit(const AsmJSModule *module, size_t exitIndex)
+      : module(module),
+        exitIndex(exitIndex)
+    { }
+};
+
 struct BaselineScript
 {
   public:
     static const uint32_t MAX_JSSCRIPT_LENGTH = 0x0fffffffu;
 
     // Limit the locals on a given script so that stack check on baseline frames
     // doesn't overflow a uint32_t value.
     // (MAX_JSSCRIPT_SLOTS * sizeof(Value)) must fit within a uint32_t.
@@ -109,16 +122,20 @@ struct BaselineScript
 
     // For heavyweight scripts, template objects to use for the call object and
     // decl env object (linked via the call object's enclosing scope).
     HeapPtrObject templateScope_;
 
     // Allocated space for fallback stubs.
     FallbackICStubSpace fallbackStubSpace_;
 
+    // If non-null, the list of AsmJSModules that contain an optimized call
+    // directly to this script.
+    Vector<DependentAsmJSModuleExit> *dependentAsmJSModules_;
+
     // Native code offset right before the scope chain is initialized.
     uint32_t prologueOffset_;
 
     // Native code offset right before the frame is popped and the method
     // returned from.
     uint32_t epilogueOffset_;
 
     // The offsets for the toggledJump instructions for SPS update ICs.
@@ -342,16 +359,20 @@ struct BaselineScript
     uint8_t *nativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo = nullptr);
 
     jsbytecode *pcForReturnOffset(JSScript *script, uint32_t nativeOffset);
     jsbytecode *pcForReturnAddress(JSScript *script, uint8_t *nativeAddress);
 
     jsbytecode *pcForNativeAddress(JSScript *script, uint8_t *nativeAddress);
     jsbytecode *pcForNativeOffset(JSScript *script, uint32_t nativeOffset);
 
+    bool addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit);
+    void unlinkDependentAsmJSModules(FreeOp *fop);
+    void removeDependentAsmJSModule(DependentAsmJSModuleExit exit);
+
   private:
     jsbytecode *pcForNativeOffset(JSScript *script, uint32_t nativeOffset, bool isReturn);
 
   public:
     // Toggle debug traps (used for breakpoints and step mode) in the script.
     // If |pc| is nullptr, toggle traps for all ops in the script. Else, only
     // toggle traps at |pc|.
     void toggleDebugTraps(JSScript *script, jsbytecode *pc);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -7,17 +7,16 @@
 #include "jit/Ion.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ThreadLocal.h"
 
 #include "jscompartment.h"
 #include "jsprf.h"
 
-#include "asmjs/AsmJSModule.h"
 #include "gc/Marking.h"
 #include "jit/AliasAnalysis.h"
 #include "jit/BacktrackingAllocator.h"
 #include "jit/BaselineFrame.h"
 #include "jit/BaselineInspector.h"
 #include "jit/BaselineJIT.h"
 #include "jit/CodeGenerator.h"
 #include "jit/EdgeCaseAnalysis.h"
@@ -839,17 +838,16 @@ IonScript::IonScript()
     callTargetList_(0),
     callTargetEntries_(0),
     backedgeList_(0),
     backedgeEntries_(0),
     invalidationCount_(0),
     parallelAge_(0),
     recompileInfo_(),
     osrPcMismatchCounter_(0),
-    dependentAsmJSModules(nullptr),
     pendingBuilder_(nullptr)
 {
 }
 
 IonScript *
 IonScript::New(JSContext *cx, types::RecompileInfo recompileInfo,
                uint32_t frameSlots, uint32_t frameSize,
                size_t snapshotsListSize, size_t snapshotsRVATableSize,
@@ -1217,42 +1215,19 @@ IonScript::purgeCaches()
 
 void
 IonScript::destroyCaches()
 {
     for (size_t i = 0; i < numCaches(); i++)
         getCacheFromIndex(i).destroy();
 }
 
-bool
-IonScript::addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit)
-{
-    if (!dependentAsmJSModules) {
-        dependentAsmJSModules = cx->new_<Vector<DependentAsmJSModuleExit> >(cx);
-        if (!dependentAsmJSModules)
-            return false;
-    }
-    return dependentAsmJSModules->append(exit);
-}
-
 void
 IonScript::unlinkFromRuntime(FreeOp *fop)
 {
-    // Remove any links from AsmJSModules that contain optimized FFI calls into
-    // this IonScript.
-    if (dependentAsmJSModules) {
-        for (size_t i = 0; i < dependentAsmJSModules->length(); i++) {
-            DependentAsmJSModuleExit exit = dependentAsmJSModules->begin()[i];
-            exit.module->detachIonCompilation(exit.exitIndex);
-        }
-
-        fop->delete_(dependentAsmJSModules);
-        dependentAsmJSModules = nullptr;
-    }
-
     // The writes to the executable buffer below may clobber backedge jumps, so
     // make sure that those backedges are unlinked from the runtime and not
     // reclobbered with garbage if an interrupt is requested.
     JitRuntime *jrt = fop->runtime()->jitRuntime();
     JitRuntime::AutoMutateBackedges amb(jrt);
     for (size_t i = 0; i < backedgeEntries_; i++)
         jrt->removePatchableBackedge(&backedgeList()[i]);
 
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -155,29 +155,16 @@ class SnapshotWriter;
 class RecoverWriter;
 class SafepointWriter;
 class SafepointIndex;
 class OsiIndex;
 class IonCache;
 struct PatchableBackedgeInfo;
 struct CacheLocation;
 
-// Describes a single AsmJSModule which jumps (via an FFI exit with the given
-// index) directly into an IonScript.
-struct DependentAsmJSModuleExit
-{
-    const AsmJSModule *module;
-    size_t exitIndex;
-
-    DependentAsmJSModuleExit(const AsmJSModule *module, size_t exitIndex)
-      : module(module),
-        exitIndex(exitIndex)
-    { }
-};
-
 // An IonScript attaches Ion-generated information to a JSScript.
 struct IonScript
 {
   private:
     // Code pointer containing the actual method.
     PreBarrieredJitCode method_;
 
     // Deoptimization table used by this method.
@@ -293,20 +280,16 @@ struct IonScript
 
     // The optimization level this script was compiled in.
     OptimizationLevel optimizationLevel_;
 
     // Number of times we tried to enter this script via OSR but failed due to
     // a LOOPENTRY pc other than osrPc_.
     uint32_t osrPcMismatchCounter_;
 
-    // If non-null, the list of AsmJSModules
-    // that contain an optimized call directly into this IonScript.
-    Vector<DependentAsmJSModuleExit> *dependentAsmJSModules;
-
     IonBuilder *pendingBuilder_;
 
   private:
     inline uint8_t *bottomBuffer() {
         return reinterpret_cast<uint8_t *>(this);
     }
     inline const uint8_t *bottomBuffer() const {
         return reinterpret_cast<const uint8_t *>(this);
@@ -347,29 +330,16 @@ struct IonScript
         return  &bottomBuffer()[runtimeData_];
     }
     JSScript **callTargetList() {
         return (JSScript **) &bottomBuffer()[callTargetList_];
     }
     PatchableBackedge *backedgeList() {
         return (PatchableBackedge *) &bottomBuffer()[backedgeList_];
     }
-    bool addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit);
-    void removeDependentAsmJSModule(DependentAsmJSModuleExit exit) {
-        if (!dependentAsmJSModules)
-            return;
-        for (size_t i = 0; i < dependentAsmJSModules->length(); i++) {
-            if (dependentAsmJSModules->begin()[i].module == exit.module &&
-                dependentAsmJSModules->begin()[i].exitIndex == exit.exitIndex)
-            {
-                dependentAsmJSModules->erase(dependentAsmJSModules->begin() + i);
-                break;
-            }
-        }
-    }
 
   private:
     void trace(JSTracer *trc);
 
   public:
     // Do not call directly, use IonScript::New. This is public for cx->new_.
     IonScript();
 
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1856,21 +1856,21 @@ MacroAssemblerARMCompat::callIon(Registe
         ma_callIonHalfPush(callee);
     } else {
         adjustFrame(sizeof(void*));
         ma_callIon(callee);
     }
 }
 
 void
-MacroAssemblerARMCompat::callIonFromAsmJS(Register callee)
+MacroAssemblerARMCompat::callJitFromAsmJS(Register callee)
 {
     ma_callIonNoPush(callee);
 
-    // The Ion ABI has the callee pop the return address off the stack.
+    // The JIT ABI has the callee pop the return address off the stack.
     // The asm.js caller assumes that the call leaves sp unchanged, so bump
     // the stack.
     subPtr(Imm32(sizeof(void*)), sp);
 }
 
 void
 MacroAssembler::alignFrameForICArguments(AfterICSaveLive &aic)
 {
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -1288,17 +1288,17 @@ class MacroAssemblerARMCompat : public M
 
     void callWithExitFrame(Label *target);
     void callWithExitFrame(JitCode *target);
     void callWithExitFrame(JitCode *target, Register dynStack);
 
     // Makes an Ion call using the only two methods that it is sane for
     // independent code to make a call.
     void callIon(Register callee);
-    void callIonFromAsmJS(Register callee);
+    void callJitFromAsmJS(Register callee);
 
     void reserveStack(uint32_t amount);
     void freeStack(uint32_t amount);
     void freeStack(Register amount);
 
     void add32(Register src, Register dest);
     void add32(Imm32 imm, Register dest);
     void add32(Imm32 imm, const Address &dest);
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -1544,21 +1544,21 @@ MacroAssemblerMIPSCompat::callIon(Regist
     if ((framePushed() & 7) == 4) {
         ma_callIonHalfPush(callee);
     } else {
         adjustFrame(sizeof(uint32_t));
         ma_callIon(callee);
     }
 }
 void
-MacroAssemblerMIPSCompat::callIonFromAsmJS(Register callee)
+MacroAssemblerMIPSCompat::callJitFromAsmJS(Register callee)
 {
     ma_callIonNoPush(callee);
 
-    // The Ion ABI has the callee pop the return address off the stack.
+    // The JIT ABI has the callee pop the return address off the stack.
     // The asm.js caller assumes that the call leaves sp unchanged, so bump
     // the stack.
     subPtr(Imm32(sizeof(void*)), StackPointer);
 }
 
 void
 MacroAssemblerMIPSCompat::reserveStack(uint32_t amount)
 {
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -1135,17 +1135,17 @@ public:
 
     void callWithExitFrame(Label *target);
     void callWithExitFrame(JitCode *target);
     void callWithExitFrame(JitCode *target, Register dynStack);
 
     // Makes an Ion call using the only two methods that it is sane for
     // indep code to make a call
     void callIon(Register callee);
-    void callIonFromAsmJS(Register callee);
+    void callJitFromAsmJS(Register callee);
 
     void reserveStack(uint32_t amount);
     void freeStack(uint32_t amount);
     void freeStack(Register amount);
 
     void add32(Register src, Register dest);
     void add32(Imm32 imm, Register dest);
     void add32(Imm32 imm, const Address &dest);
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -186,17 +186,17 @@ class MacroAssemblerNone : public Assemb
     void setupUnalignedABICall(uint32_t, Register) { MOZ_CRASH(); }
     template <typename T> void passABIArg(T, MoveOp::Type v = MoveOp::GENERAL) { MOZ_CRASH(); }
 
     void callWithExitFrame(Label *) { MOZ_CRASH(); }
     void callWithExitFrame(JitCode *) { MOZ_CRASH(); }
     void callWithExitFrame(JitCode *, Register) { MOZ_CRASH(); }
 
     void callIon(Register callee) { MOZ_CRASH(); }
-    void callIonFromAsmJS(Register callee) { MOZ_CRASH(); }
+    void callJitFromAsmJS(Register callee) { MOZ_CRASH(); }
 
     void nop() { MOZ_CRASH(); }
     void breakpoint() { MOZ_CRASH(); }
     void abiret() { MOZ_CRASH(); }
     void ret() { MOZ_CRASH(); }
 
     CodeOffsetLabel toggledJump(Label *) { MOZ_CRASH(); }
     CodeOffsetLabel toggledCall(JitCode *, bool) { MOZ_CRASH(); }
--- a/js/src/jit/shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/shared/MacroAssembler-x86-shared.h
@@ -1200,17 +1200,17 @@ class MacroAssemblerX86Shared : public A
     }
     void call(const CallSiteDesc &desc, Register reg) {
         call(reg);
         append(desc, currentOffset(), framePushed_);
     }
     void callIon(Register callee) {
         call(callee);
     }
-    void callIonFromAsmJS(Register callee) {
+    void callJitFromAsmJS(Register callee) {
         call(callee);
     }
     void call(AsmJSImmPtr target) {
         mov(target, eax);
         call(eax);
     }
     void callAndPushReturnAddress(Label *label) {
         call(label);