Bug 898963 - Odinmonkey (ARM): Optimize asm.js FFI calls to Ion functions, trim regs saved, inline activation and deactivation. r=luke
authorDouglas Crosher <dtc-moz@scieneer.com>
Thu, 17 Apr 2014 00:11:28 +1000
changeset 179265 28d75105f7c853803bdd1afb16965fc47b07eb81
parent 179264 234646463e96462b42596a899f8228d6ba20d27e
child 179266 d1b66744e023100aee7296cb48dab568b1d84653
push id272
push userpvanderbeken@mozilla.com
push dateMon, 05 May 2014 16:31:18 +0000
reviewersluke
bugs898963
milestone31.0a1
Bug 898963 - Odinmonkey (ARM): Optimize asm.js FFI calls to Ion functions, trim regs saved, inline activation and deactivation. r=luke
js/src/jit/AsmJS.cpp
js/src/jit/AsmJSModule.cpp
js/src/jit/arm/Assembler-arm.h
js/src/jit/shared/Assembler-shared.h
js/src/jit/x64/Assembler-x64.h
js/src/jit/x86/Assembler-x86.h
js/src/jscntxt.h
js/src/vm/Runtime.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -6019,25 +6019,30 @@ static unsigned
 StackArgBytes(const VectorT &argTypes)
 {
     ABIArgIter<VectorT> iter(argTypes);
     while (!iter.done())
         iter++;
     return iter.stackBytesConsumedSoFar();
 }
 
+static unsigned
+StackDecrementForCall(MacroAssembler &masm, unsigned bytesToPush)
+{
+    // Include extra padding so that, after pushing the bytesToPush,
+    // the stack is aligned for a call instruction.
+    unsigned alreadyPushed = AlignmentAtPrologue + masm.framePushed();
+    return AlignBytes(alreadyPushed + bytesToPush, StackAlignment) - alreadyPushed;
+}
+
 template <class VectorT>
 static unsigned
 StackDecrementForCall(MacroAssembler &masm, const VectorT &argTypes, unsigned extraBytes = 0)
 {
-    // Include extra padding so that, after pushing the arguments and
-    // extraBytes, the stack is aligned for a call instruction.
-    unsigned argBytes = StackArgBytes(argTypes);
-    unsigned alreadyPushed = AlignmentAtPrologue + masm.framePushed();
-    return AlignBytes(alreadyPushed + extraBytes + argBytes, StackAlignment) - alreadyPushed;
+    return StackDecrementForCall(masm, StackArgBytes(argTypes) + extraBytes);
 }
 
 static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * sizeof(intptr_t) +
                                              NonVolatileRegs.fpus().size() * sizeof(double);
 
 static bool
 GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFunc)
 {
@@ -6447,21 +6452,18 @@ GenerateOOLConvert(ModuleCompiler &m, Re
 {
     MacroAssembler &masm = m.masm();
 
     MIRType typeArray[] = { MIRType_Pointer,   // cx
                             MIRType_Pointer }; // argv
     MIRTypeVector callArgTypes(m.cx());
     callArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray));
 
-    // Reserve space for a call to InvokeFromAsmJS_* and an array of values
-    // passed to this FFI call.
-    unsigned arraySize = sizeof(Value);
-    unsigned stackDec = StackDecrementForCall(masm, callArgTypes, arraySize);
-    masm.reserveStack(stackDec);
+    // The stack is assumed to be aligned.  The frame is allocated by GenerateFFIIonExit and
+    // the stack usage here needs to kept in sync with GenerateFFIIonExit.
 
     // Store value
     unsigned offsetToArgv = StackArgBytes(callArgTypes);
     masm.storeValue(JSReturnOperand, Address(StackPointer, offsetToArgv));
 
     // Store real arguments
     ABIArgMIRTypeIter i(callArgTypes);
     Register scratch = ABIArgGenerator::NonArgReturnVolatileReg0;
@@ -6499,52 +6501,78 @@ GenerateOOLConvert(ModuleCompiler &m, Re
       case RetType::Double:
           masm.call(AsmJSImm_CoerceInPlace_ToNumber);
           masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
           masm.loadDouble(Address(StackPointer, offsetToArgv), ReturnFloatReg);
           break;
       default:
           MOZ_ASSUME_UNREACHABLE("Unsupported convert type");
     }
-
-    masm.freeStack(stackDec);
 }
 
 static void
 GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit,
                          unsigned exitIndex, Label *throwLabel)
 {
     MacroAssembler &masm = m.masm();
     masm.align(CodeAlignment);
     m.setIonExitOffset(exitIndex);
     masm.setFramePushed(0);
 
-    RegisterSet restoreSet = RegisterSet::Intersect(RegisterSet::All(),
-                                                    RegisterSet::Not(RegisterSet::Volatile()));
-#if defined(JS_CODEGEN_ARM)
-    masm.Push(lr);
+#if defined(JS_CODEGEN_X64)
+    masm.Push(HeapReg);
+#elif defined(JS_CODEGEN_ARM)
+    // The lr register holds the return address and needs to be saved.  The GlobalReg
+    // (r10) and HeapReg (r11) also need to be restored before returning to asm.js code.
+    // The NANReg also needs to be restored, but is a constant and is reloaded before
+    // returning to asm.js code.
+    masm.PushRegsInMask(RegisterSet(GeneralRegisterSet((1<<GlobalReg.code()) |
+                                                       (1<<HeapReg.code()) |
+                                                       (1<<lr.code())),
+                                    FloatRegisterSet(uint32_t(0))));
 #endif
-    masm.PushRegsInMask(restoreSet);
-
-    // Arguments are in the following order on the stack:
+
+    // The stack frame is used for the call into Ion and also for calls into C for OOL
+    // conversion of the result.  A frame large enough for both is allocated.
+    //
+    // Arguments to the Ion function are in the following order on the stack:
     // descriptor | callee | argc | this | arg1 | arg2 | ...
-
-    // Reserve and align space for the arguments
-    MIRTypeVector emptyVector(m.cx());
     unsigned argBytes = 3 * sizeof(size_t) + (1 + exit.sig().args().length()) * sizeof(Value);
-    unsigned extraBytes = 0;
+
+    // On ARM, we call with ma_callIonNoPush which, following the Ion calling convention,
+    // stores the return address into *sp. This means we need to include an extra word of
+    // space before the arguments in the stack allocation. (On x86/x64, the call
+    // instruction does the push itself and the ABI just requires us to be aligned before
+    // the call instruction.)
+    unsigned offsetToArgs = 0;
 #if defined(JS_CODEGEN_ARM)
-    extraBytes += sizeof(size_t);
+    offsetToArgs += sizeof(size_t);
 #endif
-    unsigned stackDec = StackDecrementForCall(masm, emptyVector, argBytes + extraBytes);
-    masm.reserveStack(stackDec - extraBytes);
+
+    unsigned stackDecForIonCall = StackDecrementForCall(masm, argBytes + offsetToArgs);
+
+    // Reserve space for a call to AsmJSImm_CoerceInPlace_* and an array of values used by
+    // OOLConvert which reuses the same frame. This code needs to be kept in sync with the
+    // stack usage in GenerateOOLConvert.
+    MIRType typeArray[] = { MIRType_Pointer, MIRType_Pointer }; // cx, argv
+    MIRTypeVector callArgTypes(m.cx());
+    callArgTypes.infallibleAppend(typeArray, ArrayLength(typeArray));
+    unsigned stackDecForOOLCall = StackDecrementForCall(masm, callArgTypes, sizeof(Value));
+
+    // Allocate a frame large enough for both of the above calls.
+    unsigned stackDec = Max(stackDecForIonCall, stackDecForOOLCall);
+
+    masm.reserveStack(stackDec);
+    AssertStackAlignment(masm);
 
     // 1. Descriptor
-    uint32_t descriptor = MakeFrameDescriptor(masm.framePushed() + extraBytes, JitFrame_Entry);
-    masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(StackPointer, 0));
+    size_t argOffset = offsetToArgs;
+    uint32_t descriptor = MakeFrameDescriptor(masm.framePushed(), JitFrame_Entry);
+    masm.storePtr(ImmWord(uintptr_t(descriptor)), Address(StackPointer, argOffset));
+    argOffset += sizeof(size_t);
 
     // 2. Callee
     Register callee = ABIArgGenerator::NonArgReturnVolatileReg0;
     Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1;
 
     // 2.1. Get ExitDatum
     unsigned globalDataOffset = m.module().exitIndexToGlobalDataOffset(exitIndex);
 #if defined(JS_CODEGEN_X64)
@@ -6556,75 +6584,135 @@ GenerateFFIIonExit(ModuleCompiler &m, co
 #else
     masm.lea(Operand(GlobalReg, globalDataOffset), callee);
 #endif
 
     // 2.2. Get callee
     masm.loadPtr(Address(callee, offsetof(AsmJSModule::ExitDatum, fun)), callee);
 
     // 2.3. Save callee
-    masm.storePtr(callee, Address(StackPointer, sizeof(size_t)));
+    masm.storePtr(callee, Address(StackPointer, argOffset));
+    argOffset += sizeof(size_t);
 
     // 3. Argc
     unsigned argc = exit.sig().args().length();
-    masm.storePtr(ImmWord(uintptr_t(argc)), Address(StackPointer, 2 * sizeof(size_t)));
+    masm.storePtr(ImmWord(uintptr_t(argc)), Address(StackPointer, argOffset));
+    argOffset += sizeof(size_t);
 
     // 4. |this| value
-    masm.storeValue(UndefinedValue(), Address(StackPointer, 3 * sizeof(size_t)));
+    masm.storeValue(UndefinedValue(), Address(StackPointer, argOffset));
+    argOffset += sizeof(Value);
 
     // 5. Fill the arguments
-    unsigned offsetToArgs = 3 * sizeof(size_t) + sizeof(Value);
     unsigned offsetToCallerStackArgs = masm.framePushed();
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     offsetToCallerStackArgs += NativeFrameSize;
-#else
-    offsetToCallerStackArgs += ShadowStackSpace;
 #endif
-    FillArgumentArray(m, exit.sig().args(), offsetToArgs, offsetToCallerStackArgs, scratch);
+    FillArgumentArray(m, exit.sig().args(), argOffset, offsetToCallerStackArgs, scratch);
+    argOffset += exit.sig().args().length() * sizeof(Value);
+    JS_ASSERT(argOffset == offsetToArgs + argBytes);
 
     // Get the pointer to the ion code
     Label done, oolConvert;
     Label *maybeDebugBreakpoint = nullptr;
 
 #ifdef DEBUG
     Label ionFailed;
     maybeDebugBreakpoint = &ionFailed;
     masm.branchIfFunctionHasNoScript(callee, &ionFailed);
 #endif
 
-    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), scratch);
-    masm.loadBaselineOrIonNoArgCheck(scratch, scratch, SequentialExecution, maybeDebugBreakpoint);
-
-    LoadAsmJSActivationIntoRegister(masm, callee);
-    masm.push(scratch);
-    masm.setupUnalignedABICall(1, scratch);
-    masm.passABIArg(callee);
-    masm.callWithABI(AsmJSImm_EnableActivationFromAsmJS);
-    masm.pop(scratch);
+    masm.loadPtr(Address(callee, JSFunction::offsetOfNativeOrScript()), callee);
+    masm.loadBaselineOrIonNoArgCheck(callee, callee, SequentialExecution, maybeDebugBreakpoint);
+
+    AssertStackAlignment(masm);
+
+    {
+        // Enable Activation.
+        //
+        // This sequence requires four registers, and needs to preserve the 'callee'
+        // register, so there are five live registers.
+        JS_ASSERT(callee == AsmJSIonExitRegCallee);
+        Register reg0 = AsmJSIonExitRegE0;
+        Register reg1 = AsmJSIonExitRegE1;
+        Register reg2 = AsmJSIonExitRegE2;
+        Register reg3 = AsmJSIonExitRegE3;
+
+        LoadAsmJSActivationIntoRegister(masm, reg0);
+
+        // The following is inlined:
+        //   JSContext *cx = activation->cx();
+        //   Activation *act = cx->mainThread().activation();
+        //   act.active_ = true;
+        //   act.prevIonTop_ = cx->mainThread().ionTop;
+        //   act.prevJitJSContext_ = cx->mainThread().jitJSContext;
+        //   cx->mainThread().jitJSContext = cx;
+        // On the ARM store8() uses the secondScratchReg (lr) as a temp.
+        size_t offsetOfActivation = offsetof(JSRuntime, mainThread) +
+                                    PerThreadData::offsetOfActivation();
+        size_t offsetOfIonTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, ionTop);
+        size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) +
+                                      offsetof(PerThreadData, jitJSContext);
+        masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg3);
+        masm.loadPtr(Address(reg3, JSContext::offsetOfRuntime()), reg0);
+        masm.loadPtr(Address(reg0, offsetOfActivation), reg1);
+        masm.store8(Imm32(1), Address(reg1, JitActivation::offsetOfActiveUint8()));
+        masm.loadPtr(Address(reg0, offsetOfIonTop), reg2);
+        masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevIonTop()));
+        masm.loadPtr(Address(reg0, offsetOfJitJSContext), reg2);
+        masm.storePtr(reg2, Address(reg1, JitActivation::offsetOfPrevJitJSContext()));
+        masm.storePtr(reg3, Address(reg0, offsetOfJitJSContext));
+    }
 
     // 2. Call
-#if defined(JS_CODEGEN_ARM) && defined(DEBUG)
-    // ARM still needs to push, before stack is aligned
-    masm.Push(scratch);
+    AssertStackAlignment(masm);
+#if defined(JS_CODEGEN_ARM)
+    masm.ma_callIonNoPush(callee);
+    // The return address has been popped from the stack, so adjust the stack
+    // without changing the frame-pushed counter to keep the stack aligned.
+    masm.subPtr(Imm32(4), sp);
+#else
+    masm.callIon(callee);
 #endif
     AssertStackAlignment(masm);
-#if defined(JS_CODEGEN_ARM) && defined(DEBUG)
-    masm.freeStack(sizeof(size_t));
-#endif
-    masm.callIon(scratch);
-    masm.freeStack(stackDec - extraBytes);
-
-    masm.push(JSReturnReg_Type);
-    masm.push(JSReturnReg_Data);
-    LoadAsmJSActivationIntoRegister(masm, callee);
-    masm.setupUnalignedABICall(1, scratch);
-    masm.passABIArg(callee);
-    masm.callWithABI(AsmJSImm_DisableActivationFromAsmJS);
-    masm.pop(JSReturnReg_Data);
-    masm.pop(JSReturnReg_Type);
+
+    {
+        // Disable Activation.
+        //
+        // This sequence needs three registers, and must preserve the JSReturnReg_Data and
+        // JSReturnReg_Type, so there are five live registers.
+        JS_ASSERT(JSReturnReg_Data == AsmJSIonExitRegReturnData);
+        JS_ASSERT(JSReturnReg_Type == AsmJSIonExitRegReturnType);
+        Register reg0 = AsmJSIonExitRegD0;
+        Register reg1 = AsmJSIonExitRegD1;
+        Register reg2 = AsmJSIonExitRegD2;
+
+        LoadAsmJSActivationIntoRegister(masm, reg0);
+
+        // The following is inlined:
+        //   JSContext *cx = activation->cx();
+        //   Activation *act = cx->mainThread().activation();
+        //   act.active_ = false;
+        //   cx->mainThread().ionTop = prevIonTop_;
+        //   cx->mainThread().jitJSContext = prevJitJSContext_;
+        // On the ARM store8() uses the secondScratchReg (lr) as a temp.
+        size_t offsetOfActivation = offsetof(JSRuntime, mainThread) +
+                                    PerThreadData::offsetOfActivation();
+        size_t offsetOfIonTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, ionTop);
+        size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) +
+                                      offsetof(PerThreadData, jitJSContext);
+        masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg0);
+        masm.loadPtr(Address(reg0, JSContext::offsetOfRuntime()), reg0);
+        masm.loadPtr(Address(reg0, offsetOfActivation), reg1);
+        masm.store8(Imm32(0), Address(reg1, JitActivation::offsetOfActiveUint8()));
+        masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevIonTop()), reg2);
+        masm.storePtr(reg2, Address(reg0, offsetOfIonTop));
+        masm.loadPtr(Address(reg1, JitActivation::offsetOfPrevJitJSContext()), reg2);
+        masm.storePtr(reg2, Address(reg0, offsetOfJitJSContext));
+    }
 
 #ifdef DEBUG
     masm.branchTestMagicValue(Assembler::Equal, JSReturnOperand, JS_ION_ERROR, throwLabel);
     masm.branchTestMagic(Assembler::Equal, JSReturnOperand, &ionFailed);
 #else
     masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel);
 #endif
 
@@ -6640,18 +6728,30 @@ GenerateFFIIonExit(ModuleCompiler &m, co
         masm.convertValueToDouble(JSReturnOperand, ReturnFloatReg, &oolConvert);
         break;
       case RetType::Float:
         MOZ_ASSUME_UNREACHABLE("Float shouldn't be returned from a FFI");
         break;
     }
 
     masm.bind(&done);
-    masm.PopRegsInMask(restoreSet);
+    masm.freeStack(stackDec);
+#if defined(JS_CODEGEN_ARM)
+    masm.ma_vimm(GenericNaN(), NANReg);
+    masm.PopRegsInMask(RegisterSet(GeneralRegisterSet((1<<GlobalReg.code()) |
+                                                      (1<<HeapReg.code()) |
+                                                      (1<<pc.code())),
+                                   FloatRegisterSet(uint32_t(0))));
+#else
+# if defined(JS_CODEGEN_X64)
+    masm.Pop(HeapReg);
+# endif
     masm.ret();
+#endif
+    JS_ASSERT(masm.framePushed() == 0);
 
     // oolConvert
     if (oolConvert.used()) {
         masm.bind(&oolConvert);
         masm.setFramePushed(oolConvertFramePushed);
         GenerateOOLConvert(m, exit.sig().retType(), throwLabel);
         masm.setFramePushed(0);
         masm.jump(&done);
--- a/js/src/jit/AsmJSModule.cpp
+++ b/js/src/jit/AsmJSModule.cpp
@@ -144,34 +144,16 @@ CoerceInPlace_ToNumber(JSContext *cx, Mu
     double dbl;
     if (!ToNumber(cx, val, &dbl))
         return false;
     val.set(DoubleValue(dbl));
 
     return true;
 }
 
-static void
-EnableActivationFromAsmJS(AsmJSActivation *activation)
-{
-    JSContext *cx = activation->cx();
-    Activation *act = cx->mainThread().activation();
-    JS_ASSERT(act->isJit());
-    act->asJit()->setActive(cx);
-}
-
-static void
-DisableActivationFromAsmJS(AsmJSActivation *activation)
-{
-    JSContext *cx = activation->cx();
-    Activation *act = cx->mainThread().activation();
-    JS_ASSERT(act->isJit());
-    act->asJit()->setActive(cx, false);
-}
-
 namespace js {
 
 // Defined in AsmJS.cpp:
 
 int32_t
 InvokeFromAsmJS_Ignore(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv);
 
 int32_t
@@ -237,20 +219,16 @@ AddressOf(AsmJSImmKind kind, ExclusiveCo
       case AsmJSImm_InvokeFromAsmJS_ToNumber:
         return RedirectCall(FuncCast(InvokeFromAsmJS_ToNumber), Args_General4);
       case AsmJSImm_CoerceInPlace_ToInt32:
         return RedirectCall(FuncCast(CoerceInPlace_ToInt32), Args_General2);
       case AsmJSImm_CoerceInPlace_ToNumber:
         return RedirectCall(FuncCast(CoerceInPlace_ToNumber), Args_General2);
       case AsmJSImm_ToInt32:
         return RedirectCall(FuncCast<int32_t (double)>(js::ToInt32), Args_Int_Double);
-      case AsmJSImm_EnableActivationFromAsmJS:
-        return RedirectCall(FuncCast(EnableActivationFromAsmJS), Args_General1);
-      case AsmJSImm_DisableActivationFromAsmJS:
-        return RedirectCall(FuncCast(DisableActivationFromAsmJS), Args_General1);
 #if defined(JS_CODEGEN_ARM)
       case AsmJSImm_aeabi_idivmod:
         return RedirectCall(FuncCast(__aeabi_idivmod), Args_General2);
       case AsmJSImm_aeabi_uidivmod:
         return RedirectCall(FuncCast(__aeabi_uidivmod), Args_General2);
 #endif
       case AsmJSImm_ModD:
         return RedirectCall(FuncCast(NumberMod), Args_Double_DoubleDouble);
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -75,17 +75,16 @@ class ABIArgGenerator
 
   public:
     ABIArgGenerator();
     ABIArg next(MIRType argType);
     ABIArg &current() { return current_; }
     uint32_t stackBytesConsumedSoFar() const { return stackOffset_; }
     static const Register NonArgReturnVolatileReg0;
     static const Register NonArgReturnVolatileReg1;
-
 };
 
 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = r1;
 
 static MOZ_CONSTEXPR_VAR Register InvalidReg = { Registers::invalid_reg };
 static MOZ_CONSTEXPR_VAR FloatRegister InvalidFloatReg = { FloatRegisters::invalid_freg };
 
 static MOZ_CONSTEXPR_VAR Register JSReturnReg_Type = r3;
@@ -93,16 +92,32 @@ static MOZ_CONSTEXPR_VAR Register JSRetu
 static MOZ_CONSTEXPR_VAR Register StackPointer = sp;
 static MOZ_CONSTEXPR_VAR Register FramePointer = InvalidReg;
 static MOZ_CONSTEXPR_VAR Register ReturnReg = r0;
 static MOZ_CONSTEXPR_VAR FloatRegister ReturnFloatReg = { FloatRegisters::d0 };
 static MOZ_CONSTEXPR_VAR FloatRegister ScratchFloatReg = { FloatRegisters::d15 };
 
 static MOZ_CONSTEXPR_VAR FloatRegister NANReg = { FloatRegisters::d14 };
 
+// Registers used in the GenerateFFIIonExit Enable Activation block.
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = r4;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = r0;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = r1;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = r2;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = r3;
+
+// Registers used in the GenerateFFIIonExit Disable Activation block.
+// None of these may be the second scratch register (lr).
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = r2;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = r3;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = r0;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = r1;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = r4;
+
+
 static MOZ_CONSTEXPR_VAR FloatRegister d0  = {FloatRegisters::d0};
 static MOZ_CONSTEXPR_VAR FloatRegister d1  = {FloatRegisters::d1};
 static MOZ_CONSTEXPR_VAR FloatRegister d2  = {FloatRegisters::d2};
 static MOZ_CONSTEXPR_VAR FloatRegister d3  = {FloatRegisters::d3};
 static MOZ_CONSTEXPR_VAR FloatRegister d4  = {FloatRegisters::d4};
 static MOZ_CONSTEXPR_VAR FloatRegister d5  = {FloatRegisters::d5};
 static MOZ_CONSTEXPR_VAR FloatRegister d6  = {FloatRegisters::d6};
 static MOZ_CONSTEXPR_VAR FloatRegister d7  = {FloatRegisters::d7};
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -685,18 +685,16 @@ enum AsmJSImmKind
     AsmJSImm_ReportOverRecursed,
     AsmJSImm_HandleExecutionInterrupt,
     AsmJSImm_InvokeFromAsmJS_Ignore,
     AsmJSImm_InvokeFromAsmJS_ToInt32,
     AsmJSImm_InvokeFromAsmJS_ToNumber,
     AsmJSImm_CoerceInPlace_ToInt32,
     AsmJSImm_CoerceInPlace_ToNumber,
     AsmJSImm_ToInt32,
-    AsmJSImm_EnableActivationFromAsmJS,
-    AsmJSImm_DisableActivationFromAsmJS,
 #if defined(JS_CODEGEN_ARM)
     AsmJSImm_aeabi_idivmod,
     AsmJSImm_aeabi_uidivmod,
 #endif
     AsmJSImm_ModD,
     AsmJSImm_SinD,
     AsmJSImm_CosD,
     AsmJSImm_TanD,
--- a/js/src/jit/x64/Assembler-x64.h
+++ b/js/src/jit/x64/Assembler-x64.h
@@ -131,16 +131,30 @@ static MOZ_CONSTEXPR_VAR FloatRegister F
 
 // The convention used by the ForkJoinGetSlice stub. None of these can be rax
 // or rdx, which the stub also needs for cmpxchg and div, respectively.
 static MOZ_CONSTEXPR_VAR Register ForkJoinGetSliceReg_cx = rdi;
 static MOZ_CONSTEXPR_VAR Register ForkJoinGetSliceReg_temp0 = rbx;
 static MOZ_CONSTEXPR_VAR Register ForkJoinGetSliceReg_temp1 = rcx;
 static MOZ_CONSTEXPR_VAR Register ForkJoinGetSliceReg_output = rsi;
 
+// Registers used in the GenerateFFIIonExit Enable Activation block.
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = r10;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = rax;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = rdi;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = rbx;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = rsi;
+
+// Registers used in the GenerateFFIIonExit Disable Activation block.
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = ecx;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = ecx;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = rax;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = rdi;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = rbx;
+
 class ABIArgGenerator
 {
 #if defined(XP_WIN)
     unsigned regIndex_;
 #else
     unsigned intRegIndex_;
     unsigned floatRegIndex_;
 #endif
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -82,16 +82,30 @@ class ABIArgGenerator
     static const Register NonArgReturnVolatileReg0;
     static const Register NonArgReturnVolatileReg1;
     static const Register NonVolatileReg;
 };
 
 static MOZ_CONSTEXPR_VAR Register OsrFrameReg = edx;
 static MOZ_CONSTEXPR_VAR Register PreBarrierReg = edx;
 
+// Registers used in the GenerateFFIIonExit Enable Activation block.
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegCallee = ecx;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE0 = edi;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE1 = eax;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE2 = ebx;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegE3 = edx;
+
+// Registers used in the GenerateFFIIonExit Disable Activation block.
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnData = edx;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegReturnType = ecx;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD0 = edi;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD1 = eax;
+static MOZ_CONSTEXPR_VAR Register AsmJSIonExitRegD2 = esi;
+
 // GCC stack is aligned on 16 bytes, but we don't maintain the invariant in
 // jitted code.
 #if defined(__GNUC__)
 static const uint32_t StackAlignment = 16;
 #else
 static const uint32_t StackAlignment = 4;
 #endif
 static const bool StackKeptAligned = false;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -403,16 +403,20 @@ struct JSContext : public js::ExclusiveC
                    public mozilla::LinkedListElement<JSContext>
 {
     explicit JSContext(JSRuntime *rt);
     ~JSContext();
 
     JSRuntime *runtime() const { return runtime_; }
     js::PerThreadData &mainThread() const { return runtime()->mainThread; }
 
+    static size_t offsetOfRuntime() {
+        return offsetof(JSContext, runtime_);
+    }
+
     friend class js::ExclusiveContext;
     friend class JS::AutoSaveExceptionState;
 
   private:
     /* Exception state -- the exception member is a GC root by definition. */
     bool                throwing;            /* is there a pending exception? */
     js::Value           unwrappedException_; /* most-recently-thrown exception */
 
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -578,16 +578,19 @@ class PerThreadData : public PerThreadDa
 
   public:
     js::Activation *const *addressOfActivation() const {
         return &activation_;
     }
     static unsigned offsetOfAsmJSActivationStackReadOnly() {
         return offsetof(PerThreadData, asmJSActivationStack_);
     }
+    static unsigned offsetOfActivation() {
+        return offsetof(PerThreadData, activation_);
+    }
 
     js::AsmJSActivation *asmJSActivationStackFromAnyThread() const {
         return asmJSActivationStack_;
     }
     js::AsmJSActivation *asmJSActivationStackFromOwnerThread() const {
         return asmJSActivationStack_;
     }
 
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1511,16 +1511,19 @@ jit::JitActivation::JitActivation(JSCont
 jit::JitActivation::~JitActivation()
 {
     if (active_) {
         cx_->mainThread().ionTop = prevIonTop_;
         cx_->mainThread().jitJSContext = prevJitJSContext_;
     }
 }
 
+// setActive() is inlined in GenerateFFIIonExit() with explicit masm instructions so
+// changes to the logic here need to be reflected in GenerateFFIIonExit() in the enable
+// and disable activation instruction sequences.
 void
 jit::JitActivation::setActive(JSContext *cx, bool active)
 {
     // Only allowed to deactivate/activate if activation is top.
     // (Not tested and will probably fail in other situations.)
     JS_ASSERT(cx->mainThread().activation_ == this);
     JS_ASSERT(active != active_);
     active_ = active;
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1330,16 +1330,26 @@ class JitActivation : public Activation
         return prevIonTop_;
     }
     JSCompartment *compartment() const {
         return compartment_;
     }
     bool firstFrameIsConstructing() const {
         return firstFrameIsConstructing_;
     }
+    static size_t offsetOfPrevIonTop() {
+        return offsetof(JitActivation, prevIonTop_);
+    }
+    static size_t offsetOfPrevJitJSContext() {
+        return offsetof(JitActivation, prevJitJSContext_);
+    }
+    static size_t offsetOfActiveUint8() {
+        JS_ASSERT(sizeof(bool) == 1);
+        return offsetof(JitActivation, active_);
+    }
 
 #ifdef CHECK_OSIPOINT_REGISTERS
     void setCheckRegs(bool check) {
         checkRegs_ = check;
     }
     static size_t offsetOfCheckRegs() {
         return offsetof(JitActivation, checkRegs_);
     }