Bug 1364520: Remove the jitTop optimization; r=jandem
authorBenjamin Bouvier <benj@benj.me>
Fri, 02 Jun 2017 19:34:05 +0200
changeset 410667 2688b23e8e9a09b738450679a172332e4cb532e0
parent 410666 278a95776dd62224625f73627f99e9356b5609a4
child 410668 84ac08cff36262137054f793700569dd0781541b
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1364520
milestone55.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 1364520: Remove the jitTop optimization; r=jandem MozReview-Commit-ID: 1ifOuh90QEK
js/public/ProfilingFrameIterator.h
js/src/gdb/mozilla/unwind.py
js/src/jit/Bailouts.cpp
js/src/jit/Bailouts.h
js/src/jit/BaselineIC.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/IonCacheIRCompiler.cpp
js/src/jit/JitFrames.cpp
js/src/jit/MacroAssembler-inl.h
js/src/jit/MacroAssembler.cpp
js/src/jit/MacroAssembler.h
js/src/jit/VMFunctions.cpp
js/src/jit/arm/Trampoline-arm.cpp
js/src/jit/arm64/Trampoline-arm64.cpp
js/src/jit/mips32/Trampoline-mips32.cpp
js/src/jit/mips64/Trampoline-mips64.cpp
js/src/jit/x64/Trampoline-x64.cpp
js/src/jit/x86/Trampoline-x86.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/vm/GeckoProfiler.cpp
js/src/vm/GeckoProfiler.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/src/wasm/WasmStubs.cpp
--- a/js/public/ProfilingFrameIterator.h
+++ b/js/public/ProfilingFrameIterator.h
@@ -32,34 +32,29 @@ namespace js {
 } // namespace js
 
 namespace JS {
 
 struct ForEachTrackedOptimizationAttemptOp;
 struct ForEachTrackedOptimizationTypeInfoOp;
 
 // This iterator can be used to walk the stack of a thread suspended at an
-// arbitrary pc. To provide acurate results, profiling must have been enabled
+// arbitrary pc. To provide accurate results, profiling must have been enabled
 // (via EnableRuntimeProfilingStack) before executing the callstack being
 // unwound.
 //
 // Note that the caller must not do anything that could cause GC to happen while
 // the iterator is alive, since this could invalidate Ion code and cause its
 // contents to become out of date.
 class MOZ_NON_PARAM JS_PUBLIC_API(ProfilingFrameIterator)
 {
     JSContext* cx_;
     uint32_t sampleBufferGen_;
     js::Activation* activation_;
 
-    // When moving past a JitActivation, we need to save the prevJitTop
-    // from it to use as the exit-frame pointer when the next caller jit
-    // activation (if any) comes around.
-    void* savedPrevJitTop_;
-
     static const unsigned StorageSpace = 8 * sizeof(void*);
     alignas(void*) unsigned char storage_[StorageSpace];
 
     void* storage() { return storage_; }
     const void* storage() const { return storage_; }
 
     js::wasm::ProfilingFrameIterator& wasmIter() {
         MOZ_ASSERT(!done());
--- a/js/src/gdb/mozilla/unwind.py
+++ b/js/src/gdb/mozilla/unwind.py
@@ -429,27 +429,26 @@ class UnwinderState(object):
     # otherwise returns the newly-created unwind info for gdb.
     def unwind_exit_frame(self, pc, pending_frame):
         if self.activation == 0:
             # Reached the end of the list.
             return None
         elif self.activation is None:
             cx = self.get_tls_context()
             self.activation = cx['jitActivation']
-            jittop = cx['jitTop']
         else:
-            jittop = self.activation['prevJitTop_']
             self.activation = self.activation['prevJitActivation_']
 
-        if jittop == 0:
+        exitFP = self.activation['exitFP_']
+        if exitFP == 0:
             return None
 
         exit_sp = pending_frame.read_register(self.SP_REGISTER)
         frame_type = self.typecache.JitFrame_Exit
-        return self.create_frame(pc, exit_sp, jittop, frame_type, pending_frame)
+        return self.create_frame(pc, exit_sp, exitFP, frame_type, pending_frame)
 
     # A wrapper for unwind_entry_frame_registers that handles
     # architecture-independent boilerplate.
     def unwind_entry_frame(self, pc, pending_frame):
         sp = self.next_sp
         # Notify the frame filter.
         self.add_frame(sp, name = 'JitFrame_Entry')
         # Make an unwind_info for the per-architecture code to fill in.
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -28,20 +28,21 @@ using mozilla::IsInRange;
 
 uint32_t
 jit::Bailout(BailoutStack* sp, BaselineBailoutInfo** bailoutInfo)
 {
     JSContext* cx = TlsContext.get();
     MOZ_ASSERT(bailoutInfo);
 
     // We don't have an exit frame.
-    MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) &&
-               IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000),
-               "Fake jitTop pointer should be within the first page.");
-    cx->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
+    MOZ_ASSERT(IsInRange(FAKE_EXITFP_FOR_BAILOUT, 0, 0x1000) &&
+               IsInRange(FAKE_EXITFP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000),
+               "Fake exitfp pointer should be within the first page.");
+
+    cx->activation()->asJit()->setExitFP(FAKE_EXITFP_FOR_BAILOUT);
 
     JitActivationIterator jitActivations(cx);
     BailoutFrameInfo bailoutData(jitActivations, sp);
     JitFrameIterator iter(jitActivations);
     MOZ_ASSERT(!iter.ionScript()->invalidated());
     CommonFrameLayout* currentFramePtr = iter.current();
 
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
@@ -103,17 +104,17 @@ uint32_t
 jit::InvalidationBailout(InvalidationBailoutStack* sp, size_t* frameSizeOut,
                          BaselineBailoutInfo** bailoutInfo)
 {
     sp->checkInvariants();
 
     JSContext* cx = TlsContext.get();
 
     // We don't have an exit frame.
-    cx->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
+    cx->activation()->asJit()->setExitFP(FAKE_EXITFP_FOR_BAILOUT);
 
     JitActivationIterator jitActivations(cx);
     BailoutFrameInfo bailoutData(jitActivations, sp);
     JitFrameIterator iter(jitActivations);
     CommonFrameLayout* currentFramePtr = iter.current();
 
     TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
     TraceLogTimestamp(logger, TraceLogger_Invalidation);
@@ -187,19 +188,20 @@ jit::ExceptionHandlerBailout(JSContext* 
                              const ExceptionBailoutInfo& excInfo,
                              bool* overrecursed)
 {
     // We can be propagating debug mode exceptions without there being an
     // actual exception pending. For instance, when we return false from an
     // operation callback like a timeout handler.
     MOZ_ASSERT_IF(!excInfo.propagatingIonExceptionForDebugMode(), cx->isExceptionPending());
 
-    uint8_t* prevJitTop = cx->jitTop;
-    auto restoreJitTop = mozilla::MakeScopeExit([&]() { cx->jitTop = prevJitTop; });
-    cx->jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
+    JitActivation* act = cx->activation()->asJit();
+    uint8_t* prevExitFP = act->exitFP();
+    auto restoreExitFP = mozilla::MakeScopeExit([&]() { act->setExitFP(prevExitFP); });
+    act->setExitFP(FAKE_EXITFP_FOR_BAILOUT);
 
     gc::AutoSuppressGC suppress(cx);
 
     JitActivationIterator jitActivations(cx);
     BailoutFrameInfo bailoutData(jitActivations, frame.frame());
     JitFrameIterator iter(jitActivations);
     CommonFrameLayout* currentFramePtr = iter.current();
 
@@ -298,17 +300,17 @@ jit::CheckFrequentBailouts(JSContext* cx
             Invalidate(cx, script);
         }
     }
 }
 
 void
 BailoutFrameInfo::attachOnJitActivation(const JitActivationIterator& jitActivations)
 {
-    MOZ_ASSERT(jitActivations.jitTop() == FAKE_JIT_TOP_FOR_BAILOUT);
+    MOZ_ASSERT(jitActivations.exitFP() == FAKE_EXITFP_FOR_BAILOUT);
     activation_ = jitActivations->asJit();
     activation_->setBailoutData(this);
 }
 
 BailoutFrameInfo::~BailoutFrameInfo()
 {
     activation_->cleanBailoutData();
 }
--- a/js/src/jit/Bailouts.h
+++ b/js/src/jit/Bailouts.h
@@ -97,17 +97,17 @@ static const uint32_t BAILOUT_TABLE_SIZE
 // Bailout return codes.
 // N.B. the relative order of these values is hard-coded into ::GenerateBailoutThunk.
 static const uint32_t BAILOUT_RETURN_OK = 0;
 static const uint32_t BAILOUT_RETURN_FATAL_ERROR = 1;
 static const uint32_t BAILOUT_RETURN_OVERRECURSED = 2;
 
 // This address is a magic number made to cause crashes while indicating that we
 // are making an attempt to mark the stack during a bailout.
-static uint8_t * const FAKE_JIT_TOP_FOR_BAILOUT = reinterpret_cast<uint8_t*>(0xba1);
+static uint8_t* const FAKE_EXITFP_FOR_BAILOUT = reinterpret_cast<uint8_t*>(0xba1);
 
 // BailoutStack is an architecture specific pointer to the stack, given by the
 // bailout handler.
 class BailoutStack;
 class InvalidationBailoutStack;
 
 // Must be implemented by each architecture.
 
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -3455,17 +3455,17 @@ ICCall_Native::Compiler::generateStubCod
     // Construct a native exit frame.
     masm.push(argcReg);
 
     Register scratch = regs.takeAny();
     EmitBaselineCreateStubFrameDescriptor(masm, scratch, ExitFrameLayout::Size());
     masm.push(scratch);
     masm.push(ICTailCallReg);
     masm.loadJSContext(scratch);
-    masm.enterFakeExitFrameForNative(scratch, isConstructing_);
+    masm.enterFakeExitFrameForNative(scratch, scratch, isConstructing_);
 
     // Execute call.
     masm.setupUnalignedABICall(scratch);
     masm.loadJSContext(scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(argcReg);
     masm.passABIArg(vpReg);
 
@@ -3553,17 +3553,17 @@ ICCall_ClassHook::Compiler::generateStub
 
     // Construct a native exit frame.
     masm.push(argcReg);
 
     EmitBaselineCreateStubFrameDescriptor(masm, scratch, ExitFrameLayout::Size());
     masm.push(scratch);
     masm.push(ICTailCallReg);
     masm.loadJSContext(scratch);
-    masm.enterFakeExitFrameForNative(scratch, isConstructing_);
+    masm.enterFakeExitFrameForNative(scratch, scratch, isConstructing_);
 
     // Execute call.
     masm.setupUnalignedABICall(scratch);
     masm.loadJSContext(scratch);
     masm.passABIArg(scratch);
     masm.passABIArg(argcReg);
     masm.passABIArg(vpReg);
     masm.callWithABI(Address(ICStubReg, ICCall_ClassHook::offsetOfNative()));
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3913,17 +3913,17 @@ CodeGenerator::visitCallNative(LCallNati
     masm.loadJSContext(argContextReg);
     masm.move32(Imm32(call->numActualArgs()), argUintNReg);
     masm.moveStackPtrTo(argVpReg);
 
     masm.Push(argUintNReg);
 
     // Construct native exit frame.
     uint32_t safepointOffset = masm.buildFakeExitFrame(tempReg);
-    masm.enterFakeExitFrameForNative(argContextReg, call->mir()->isConstructing());
+    masm.enterFakeExitFrameForNative(argContextReg, tempReg, call->mir()->isConstructing());
 
     markSafepointAt(safepointOffset, call);
 
     emitTracelogStartEvent(TraceLogger_Call);
 
     // Construct and execute call.
     masm.setupUnalignedABICall(tempReg);
     masm.passABIArg(argContextReg);
@@ -4042,17 +4042,17 @@ CodeGenerator::visitCallDOMNative(LCallD
     // maintain the same sp-relative location of the object pointer with other
     // DOMExitFrames.
     masm.Push(argObj);
     masm.moveStackPtrTo(argObj);
 
     // Construct native exit frame.
     uint32_t safepointOffset = masm.buildFakeExitFrame(argJSContext);
     masm.loadJSContext(argJSContext);
-    masm.enterFakeExitFrame(argJSContext, IonDOMMethodExitFrameLayoutToken);
+    masm.enterFakeExitFrame(argJSContext, argJSContext, IonDOMMethodExitFrameLayoutToken);
 
     markSafepointAt(safepointOffset, call);
 
     // Construct and execute call.
     masm.setupUnalignedABICall(argJSContext);
     masm.loadJSContext(argJSContext);
     masm.passABIArg(argJSContext);
     masm.passABIArg(argObj);
@@ -7940,17 +7940,17 @@ JitRuntime::generateLazyLinkStub(JSConte
 #ifdef JS_USE_LINK_REGISTER
     masm.pushReturnAddress();
 #endif
 
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::Volatile());
     Register temp0 = regs.takeAny();
 
     masm.loadJSContext(temp0);
-    masm.enterFakeExitFrame(temp0, LazyLinkExitFrameLayoutToken);
+    masm.enterFakeExitFrame(temp0, temp0, LazyLinkExitFrameLayoutToken);
     masm.PushStubCode();
 
     masm.setupUnalignedABICall(temp0);
     masm.passABIArg(temp0);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, LazyLinkTopActivation));
 
     masm.leaveExitFrame(/* stub code */ sizeof(JitCode*));
 
@@ -11564,17 +11564,17 @@ CodeGenerator::visitGetDOMProperty(LGetD
 
     LoadDOMPrivate(masm, ObjectReg, PrivateReg);
 
     // Rooting will happen at GC time.
     masm.moveStackPtrTo(ObjectReg);
 
     uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
     masm.loadJSContext(JSContextReg);
-    masm.enterFakeExitFrame(JSContextReg, IonDOMExitFrameLayoutGetterToken);
+    masm.enterFakeExitFrame(JSContextReg, JSContextReg, IonDOMExitFrameLayoutGetterToken);
 
     markSafepointAt(safepointOffset, ins);
 
     masm.setupUnalignedABICall(JSContextReg);
     masm.loadJSContext(JSContextReg);
     masm.passABIArg(JSContextReg);
     masm.passABIArg(ObjectReg);
     masm.passABIArg(PrivateReg);
@@ -11662,17 +11662,17 @@ CodeGenerator::visitSetDOMProperty(LSetD
 
     LoadDOMPrivate(masm, ObjectReg, PrivateReg);
 
     // Rooting will happen at GC time.
     masm.moveStackPtrTo(ObjectReg);
 
     uint32_t safepointOffset = masm.buildFakeExitFrame(JSContextReg);
     masm.loadJSContext(JSContextReg);
-    masm.enterFakeExitFrame(JSContextReg, IonDOMExitFrameLayoutSetterToken);
+    masm.enterFakeExitFrame(JSContextReg, JSContextReg, IonDOMExitFrameLayoutSetterToken);
 
     markSafepointAt(safepointOffset, ins);
 
     masm.setupUnalignedABICall(JSContextReg);
     masm.loadJSContext(JSContextReg);
     masm.passABIArg(JSContextReg);
     masm.passABIArg(ObjectReg);
     masm.passABIArg(PrivateReg);
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -940,17 +940,17 @@ IonCacheIRCompiler::emitCallNativeGetter
     masm.moveStackPtrTo(argVp.get());
 
     // Push marking data for later use.
     masm.Push(argUintN);
     pushStubCodePointer();
 
     if (!masm.icBuildOOLFakeExitFrame(GetReturnAddressToIonCode(cx_), save))
         return false;
-    masm.enterFakeExitFrame(argJSContext, IonOOLNativeExitFrameLayoutToken);
+    masm.enterFakeExitFrame(argJSContext, scratch, IonOOLNativeExitFrameLayoutToken);
 
     // Construct and execute call.
     masm.setupUnalignedABICall(scratch);
     masm.passABIArg(argJSContext);
     masm.passABIArg(argUintN);
     masm.passABIArg(argVp);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
 
@@ -997,17 +997,17 @@ IonCacheIRCompiler::emitCallProxyGetResu
     // Push the proxy. Also used as receiver.
     masm.Push(obj);
     masm.moveStackPtrTo(argProxy.get());
 
     masm.loadJSContext(argJSContext);
 
     if (!masm.icBuildOOLFakeExitFrame(GetReturnAddressToIonCode(cx_), save))
         return false;
-    masm.enterFakeExitFrame(argJSContext, IonOOLProxyExitFrameLayoutToken);
+    masm.enterFakeExitFrame(argJSContext, scratch, IonOOLProxyExitFrameLayoutToken);
 
     // Make the call.
     masm.setupUnalignedABICall(scratch);
     masm.passABIArg(argJSContext);
     masm.passABIArg(argProxy);
     masm.passABIArg(argId);
     masm.passABIArg(argVp);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, ProxyGetProperty));
@@ -1761,17 +1761,17 @@ IonCacheIRCompiler::emitCallNativeSetter
     masm.move32(Imm32(1), argUintN);
 
     // Push marking data for later use.
     masm.Push(argUintN);
     pushStubCodePointer();
 
     if (!masm.icBuildOOLFakeExitFrame(GetReturnAddressToIonCode(cx_), save))
         return false;
-    masm.enterFakeExitFrame(argJSContext, IonOOLNativeExitFrameLayoutToken);
+    masm.enterFakeExitFrame(argJSContext, scratch, IonOOLNativeExitFrameLayoutToken);
 
     // Make the call.
     masm.setupUnalignedABICall(scratch);
     masm.passABIArg(argJSContext);
     masm.passABIArg(argUintN);
     masm.passABIArg(argVp);
     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, target->native()));
 
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -102,32 +102,32 @@ JitFrameIterator::JitFrameIterator()
     returnAddressToFp_(nullptr),
     frameSize_(0),
     cachedSafepointIndex_(nullptr),
     activation_(nullptr)
 {
 }
 
 JitFrameIterator::JitFrameIterator(JSContext* cx)
-  : current_(cx->jitTop),
+  : current_(cx->activation()->asJit()->exitFP()),
     type_(JitFrame_Exit),
     returnAddressToFp_(nullptr),
     frameSize_(0),
     cachedSafepointIndex_(nullptr),
     activation_(cx->activation()->asJit())
 {
     if (activation_->bailoutData()) {
         current_ = activation_->bailoutData()->fp();
         frameSize_ = activation_->bailoutData()->topFrameSize();
         type_ = JitFrame_Bailout;
     }
 }
 
 JitFrameIterator::JitFrameIterator(const ActivationIterator& activations)
-  : current_(activations.jitTop()),
+  : current_(activations->asJit()->exitFP()),
     type_(JitFrame_Exit),
     returnAddressToFp_(nullptr),
     frameSize_(0),
     cachedSafepointIndex_(nullptr),
     activation_(activations->asJit())
 {
     if (activation_->bailoutData()) {
         current_ = activation_->bailoutData()->fp();
@@ -943,17 +943,17 @@ HandleException(ResumeFromException* rfe
                 return;
         }
 
         JitFrameLayout* current = iter.isScripted() ? iter.jsFrame() : nullptr;
 
         ++iter;
 
         if (current) {
-            // Unwind the frame by updating jitTop. This is necessary so that
+            // Unwind the frame by updating exitFP. This is necessary so that
             // (1) debugger exception unwind and leave frame hooks don't see this
             // frame when they use ScriptFrameIter, and (2) ScriptFrameIter does
             // not crash when accessing an IonScript that's destroyed by the
             // ionScript->decref call.
             EnsureBareExitFrame(cx, current);
         }
 
         if (overrecursed) {
@@ -967,34 +967,35 @@ HandleException(ResumeFromException* rfe
 
 // Turns a JitFrameLayout into an ExitFrameLayout. Note that it has to be a
 // bare exit frame so it's ignored by TraceJitExitFrame.
 void
 EnsureBareExitFrame(JSContext* cx, JitFrameLayout* frame)
 {
     ExitFrameLayout* exitFrame = reinterpret_cast<ExitFrameLayout*>(frame);
 
-    if (cx->jitTop == (uint8_t*)frame) {
+    if (cx->activation()->asJit()->exitFP() == (uint8_t*)frame) {
         // If we already called this function for the current frame, do
         // nothing.
         MOZ_ASSERT(exitFrame->isBareExit());
         return;
     }
 
 #ifdef DEBUG
     JitFrameIterator iter(cx);
     while (!iter.isScripted())
         ++iter;
     MOZ_ASSERT(iter.current() == frame, "|frame| must be the top JS frame");
 
-    MOZ_ASSERT((uint8_t*)exitFrame->footer() >= cx->jitTop,
-               "Must have space for ExitFooterFrame before jitTop");
+    MOZ_ASSERT(!!cx->activation()->asJit()->exitFP());
+    MOZ_ASSERT((uint8_t*)exitFrame->footer() >= cx->activation()->asJit()->exitFP(),
+               "Must have space for ExitFooterFrame before exitFP");
 #endif
 
-    cx->jitTop = (uint8_t*)frame;
+    cx->activation()->asJit()->setExitFP((uint8_t*)frame);
     *exitFrame->footer()->addressOfJitCode() = ExitFrameLayout::BareToken();
     MOZ_ASSERT(exitFrame->isBareExit());
 }
 
 CalleeToken
 TraceCalleeToken(JSTracer* trc, CalleeToken token)
 {
     switch (CalleeTokenTag tag = GetCalleeTokenTag(token)) {
--- a/js/src/jit/MacroAssembler-inl.h
+++ b/js/src/jit/MacroAssembler-inl.h
@@ -286,38 +286,38 @@ void
 MacroAssembler::PushStubCode()
 {
     // Make sure that we do not erase an existing self-reference.
     MOZ_ASSERT(!hasSelfReference());
     selfReferencePatch_ = PushWithPatch(ImmWord(-1));
 }
 
 void
-MacroAssembler::enterExitFrame(Register cxreg, const VMFunction* f)
+MacroAssembler::enterExitFrame(Register cxreg, Register scratch, const VMFunction* f)
 {
-    linkExitFrame(cxreg);
+    linkExitFrame(cxreg, scratch);
     // Push the JitCode pointer. (Keep the code alive, when on the stack)
     PushStubCode();
     // Push VMFunction pointer, to mark arguments.
     Push(ImmPtr(f));
 }
 
 void
-MacroAssembler::enterFakeExitFrame(Register cxreg, enum ExitFrameTokenValues token)
+MacroAssembler::enterFakeExitFrame(Register cxreg, Register scratch, enum ExitFrameTokenValues token)
 {
-    linkExitFrame(cxreg);
+    linkExitFrame(cxreg, scratch);
     Push(Imm32(token));
     Push(ImmPtr(nullptr));
 }
 
 void
-MacroAssembler::enterFakeExitFrameForNative(Register cxreg, bool isConstructing)
+MacroAssembler::enterFakeExitFrameForNative(Register cxreg, Register scratch, bool isConstructing)
 {
-    enterFakeExitFrame(cxreg, isConstructing ? ConstructNativeExitFrameLayoutToken
-                                             : CallNativeExitFrameLayoutToken);
+    enterFakeExitFrame(cxreg, scratch, isConstructing ? ConstructNativeExitFrameLayoutToken
+                                                      : CallNativeExitFrameLayoutToken);
 }
 
 void
 MacroAssembler::leaveExitFrame(size_t extraFrame)
 {
     freeStack(ExitFooterFrame::Size() + extraFrame);
 }
 
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -1505,17 +1505,17 @@ BailoutReportOverRecursed(JSContext* cx)
 {
     ReportOverRecursed(cx);
 }
 
 void
 MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo)
 {
     loadJSContext(scratch);
-    enterExitFrame(scratch);
+    enterExitFrame(scratch, scratch);
 
     Label baseline;
 
     // The return value from Bailout is tagged as:
     // - 0x0: done (enter baseline)
     // - 0x1: error (handle exception)
     // - 0x2: overrecursed
     JS_STATIC_ASSERT(BAILOUT_RETURN_OK == 0);
@@ -1567,17 +1567,17 @@ MacroAssembler::generateBailoutTail(Regi
         // Enter exit frame for the FinishBailoutToBaseline call.
         loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr)), temp);
         load32(Address(temp, BaselineFrame::reverseOffsetOfFrameSize()), temp);
         makeFrameDescriptor(temp, JitFrame_BaselineJS, ExitFrameLayout::Size());
         push(temp);
         push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr)));
         // No GC things to mark on the stack, push a bare token.
         loadJSContext(scratch);
-        enterFakeExitFrame(scratch, ExitFrameLayoutBareToken);
+        enterFakeExitFrame(scratch, scratch, ExitFrameLayoutBareToken);
 
         // If monitorStub is non-null, handle resumeAddr appropriately.
         Label noMonitor;
         Label done;
         branchPtr(Assembler::Equal,
                   Address(bailoutInfo, offsetof(BaselineBailoutInfo, monitorStub)),
                   ImmPtr(nullptr),
                   &noMonitor);
@@ -2817,19 +2817,20 @@ MacroAssembler::callWithABI(wasm::Byteco
 
     Pop(WasmTlsReg);
 }
 
 // ===============================================================
 // Exit frame footer.
 
 void
-MacroAssembler::linkExitFrame(Register cxreg)
+MacroAssembler::linkExitFrame(Register cxreg, Register scratch)
 {
-    storeStackPtr(Address(cxreg, offsetof(JSContext, jitTop)));
+    loadPtr(Address(cxreg, JSContext::offsetOfActivation()), scratch);
+    storeStackPtr(Address(scratch, JitActivation::offsetOfExitFP()));
 }
 
 void
 MacroAssembler::linkSelfReference(JitCode* code)
 {
     // If this code can transition to C++ code and witness a GC, then we need to store
     // the JitCode onto the stack in order to GC it correctly.  exitCodePatch should
     // be unset if the code never needed to push its JitCode*.
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -693,32 +693,32 @@ class MacroAssembler : public MacroAssem
     // is linked.
     inline void PushStubCode();
 
     // Return true if the code contains a self-reference which needs to be
     // patched when the code is linked.
     inline bool hasSelfReference() const;
 
     // Push stub code and the VMFunction pointer.
-    inline void enterExitFrame(Register cxreg, const VMFunction* f = nullptr);
+    inline void enterExitFrame(Register cxreg, Register scratch, const VMFunction* f = nullptr);
 
     // Push an exit frame token to identify which fake exit frame this footer
     // corresponds to.
-    inline void enterFakeExitFrame(Register cxreg, enum ExitFrameTokenValues token);
+    inline void enterFakeExitFrame(Register cxreg, Register scratch, enum ExitFrameTokenValues token);
 
     // Push an exit frame token for a native call.
-    inline void enterFakeExitFrameForNative(Register cxreg, bool isConstructing);
+    inline void enterFakeExitFrameForNative(Register cxreg, Register scratch, bool isConstructing);
 
     // Pop ExitFrame footer in addition to the extra frame.
     inline void leaveExitFrame(size_t extraFrame = 0);
 
   private:
-    // Save the top of the stack into JSontext::jitTop of the current thread,
-    // which should be the location of the latest exit frame.
-    void linkExitFrame(Register cxreg);
+    // Save the top of the stack into JitActivation::exitFP of the current
+    // thread, which should be the location of the latest exit frame.
+    void linkExitFrame(Register cxreg, Register scratch);
 
     // Patch the value of PushStubCode with the pointer to the finalized code.
     void linkSelfReference(JitCode* code);
 
     // If the JitCode that created this assembler needs to transition into the VM,
     // we want to store the JitCode on the stack in order to mark it during a GC.
     // This is a reference to a patch location where the JitCode* will be written.
     CodeOffset selfReferencePatch_;
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -805,18 +805,18 @@ DebugPrologue(JSContext* cx, BaselineFra
         MOZ_CRASH("bad Debugger::onEnterFrame status");
     }
 }
 
 bool
 DebugEpilogueOnBaselineReturn(JSContext* cx, BaselineFrame* frame, jsbytecode* pc)
 {
     if (!DebugEpilogue(cx, frame, pc, true)) {
-        // DebugEpilogue popped the frame by updating jitTop, so run the stop event
-        // here before we enter the exception handler.
+        // DebugEpilogue popped the frame by updating exitFP, so run the stop
+        // event here before we enter the exception handler.
         TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
         TraceLogStopEvent(logger, TraceLogger_Baseline);
         TraceLogStopEvent(logger, TraceLogger_Scripts);
         return false;
     }
 
     return true;
 }
@@ -832,19 +832,18 @@ DebugEpilogue(JSContext* cx, BaselineFra
     // Unwind to the outermost environment and set pc to the end of the
     // script, regardless of error.
     EnvironmentIter ei(cx, frame, pc);
     UnwindAllEnvironmentsInFrame(cx, ei);
     JSScript* script = frame->script();
     frame->setOverridePc(script->lastPC());
 
     if (!ok) {
-        // Pop this frame by updating jitTop, so that the exception handling
+        // Pop this frame by updating exitFP, so that the exception handling
         // code will start at the previous frame.
-
         JitFrameLayout* prefix = frame->framePrefix();
         EnsureBareExitFrame(cx, prefix);
         return false;
     }
 
     // Clear the override pc. This is not necessary for correctness: the frame
     // will return immediately, but this simplifies the check we emit in debug
     // builds after each callVM, to ensure this flag is not set.
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -283,17 +283,17 @@ JitRuntime::generateEnterJIT(JSContext* 
 
         // Enter exit frame.
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch);
         masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS, ExitFrameLayout::Size());
         masm.push(scratch);
         masm.push(Imm32(0)); // Fake return address.
         // No GC things to mark on the stack, push a bare token.
         masm.loadJSContext(scratch);
-        masm.enterFakeExitFrame(scratch, ExitFrameLayoutBareToken);
+        masm.enterFakeExitFrame(scratch, scratch, ExitFrameLayoutBareToken);
 
         masm.push(framePtr); // BaselineFrame
         masm.push(r0); // jitcode
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(r11); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
@@ -772,35 +772,35 @@ JitRuntime::generateVMWrapper(JSContext*
     VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f);
     if (p)
         return p->value();
 
     // Generate a separated code for the wrapper.
     MacroAssembler masm(cx);
     AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
 
-    // Wrapper register set is a superset of Volatile register set.
-    JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0);
+    static_assert((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0,
+                  "Wrapper register set must be a superset of Volatile register set.");
 
     // The context is the first argument; r0 is the first argument register.
     Register cxreg = r0;
     regs.take(cxreg);
 
     // Stack is:
     //    ... frame ...
     //  +8  [args] + argPadding
     //  +0  ExitFrame
     //
     // We're aligned to an exit frame, so link it up.
     // If it isn't a tail call, then the return address needs to be saved
     if (f.expectTailCall == NonTailCall)
         masm.pushReturnAddress();
 
     masm.loadJSContext(cxreg);
-    masm.enterExitFrame(cxreg, &f);
+    masm.enterExitFrame(cxreg, regs.getAny(), &f);
 
     // Save the base of the argument set stored on the stack.
     Register argsBase = InvalidReg;
     if (f.explicitArgs) {
         argsBase = r5;
         regs.take(argsBase);
         ScratchRegisterScope scratch(masm);
         masm.ma_add(sp, Imm32(ExitFrameLayout::SizeWithFooter()), argsBase, scratch);
--- a/js/src/jit/arm64/Trampoline-arm64.cpp
+++ b/js/src/jit/arm64/Trampoline-arm64.cpp
@@ -185,17 +185,17 @@ JitRuntime::generateEnterJIT(JSContext* 
         masm.subFromStackPtr(r19);
 
         // Enter exit frame.
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), r19);
         masm.makeFrameDescriptor(r19, JitFrame_BaselineJS, ExitFrameLayout::Size());
         masm.asVIXL().Push(x19, xzr); // Push xzr for a fake return address.
         // No GC things to mark: push a bare token.
         masm.loadJSContext(r19);
-        masm.enterFakeExitFrame(r19, ExitFrameLayoutBareToken);
+        masm.enterFakeExitFrame(r19, r19, ExitFrameLayoutBareToken);
 
         masm.push(BaselineFrameReg, reg_code);
 
         // Initialize the frame, including filling in the slots.
         masm.setupUnalignedABICall(r19);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame.
         masm.passABIArg(reg_osrFrame); // InterpreterFrame.
         masm.passABIArg(reg_osrNStack);
@@ -562,18 +562,18 @@ JitRuntime::generateVMWrapper(JSContext*
         return p->value();
 
     MacroAssembler masm(cx);
 
     // Avoid conflicts with argument registers while discarding the result after
     // the function call.
     AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
 
-    // Wrapper register set is a superset of the Volatile register set.
-    JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0);
+    static_assert((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0,
+                  "Wrapper register set must be a superset of the Volatile register set.");
 
     // Unlike on other platforms, it is the responsibility of the VM *callee* to
     // push the return address, while the caller must ensure that the address
     // is stored in lr on entry. This allows the VM wrapper to work with both direct
     // calls and tail calls.
     masm.push(lr);
 
     // First argument is the JSContext.
@@ -583,17 +583,17 @@ JitRuntime::generateVMWrapper(JSContext*
     // Stack is:
     //    ... frame ...
     //  +12 [args]
     //  +8  descriptor
     //  +0  returnAddress (pushed by this function, caller sets as lr)
     //
     //  We're aligned to an exit frame, so link it up.
     masm.loadJSContext(reg_cx);
-    masm.enterExitFrame(reg_cx, &f);
+    masm.enterExitFrame(reg_cx, regs.getAny(), &f);
 
     // Save the current stack pointer as the base for copying arguments.
     Register argsBase = InvalidReg;
     if (f.explicitArgs) {
         // argsBase can't be an argument register. Bad things would happen if
         // the MoveResolver didn't throw an assertion failure first.
         argsBase = r8;
         regs.take(argsBase);
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -250,17 +250,17 @@ JitRuntime::generateEnterJIT(JSContext* 
 
         // Push frame descriptor and fake return address.
         masm.reserveStack(2 * sizeof(uintptr_t));
         masm.storePtr(scratch, Address(StackPointer, sizeof(uintptr_t))); // Frame descriptor
         masm.storePtr(zero, Address(StackPointer, 0)); // fake return address
 
         // No GC things to mark, push a bare token.
         masm.loadJSContext(scratch);
-        masm.enterFakeExitFrame(scratch, ExitFrameLayoutBareToken);
+        masm.enterFakeExitFrame(scratch, scratch, ExitFrameLayoutBareToken);
 
         masm.reserveStack(2 * sizeof(uintptr_t));
         masm.storePtr(framePtr, Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame
         masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
@@ -733,17 +733,17 @@ JitRuntime::generateVMWrapper(JSContext*
     regs.take(cxreg);
 
     // If it isn't a tail call, then the return address needs to be saved
     if (f.expectTailCall == NonTailCall)
         masm.pushReturnAddress();
 
     // We're aligned to an exit frame, so link it up.
     masm.loadJSContext(cxreg);
-    masm.enterExitFrame(cxreg, &f);
+    masm.enterExitFrame(cxreg, regs.getAny(), &f);
 
     // Save the base of the argument set stored on the stack.
     Register argsBase = InvalidReg;
     if (f.explicitArgs) {
         argsBase = t1; // Use temporary register.
         regs.take(argsBase);
         masm.ma_addu(argsBase, StackPointer, Imm32(ExitFrameLayout::SizeWithFooter()));
     }
--- a/js/src/jit/mips64/Trampoline-mips64.cpp
+++ b/js/src/jit/mips64/Trampoline-mips64.cpp
@@ -267,17 +267,17 @@ JitRuntime::generateEnterJIT(JSContext* 
 
         // Push frame descriptor and fake return address.
         masm.reserveStack(2 * sizeof(uintptr_t));
         masm.storePtr(scratch, Address(StackPointer, sizeof(uintptr_t))); // Frame descriptor
         masm.storePtr(zero, Address(StackPointer, 0)); // fake return address
 
         // No GC things to mark, push a bare token.
         masm.loadJSContext(scratch);
-        masm.enterFakeExitFrame(scratch, ExitFrameLayoutBareToken);
+        masm.enterFakeExitFrame(scratch, scratch, ExitFrameLayoutBareToken);
 
         masm.reserveStack(2 * sizeof(uintptr_t));
         masm.storePtr(framePtr, Address(StackPointer, sizeof(uintptr_t))); // BaselineFrame
         masm.storePtr(reg_code, Address(StackPointer, 0)); // jitcode
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
@@ -703,17 +703,17 @@ JitRuntime::generateVMWrapper(JSContext*
     regs.take(cxreg);
 
     // If it isn't a tail call, then the return address needs to be saved
     if (f.expectTailCall == NonTailCall)
         masm.pushReturnAddress();
 
     // We're aligned to an exit frame, so link it up.
     masm.loadJSContext(cxreg);
-    masm.enterExitFrame(cxreg, &f);
+    masm.enterExitFrame(cxreg, regs.getAny(), &f);
 
     // Save the base of the argument set stored on the stack.
     Register argsBase = InvalidReg;
     if (f.explicitArgs) {
         argsBase = t1; // Use temporary register.
         regs.take(argsBase);
         masm.ma_daddu(argsBase, StackPointer, Imm32(ExitFrameLayout::SizeWithFooter()));
     }
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -226,17 +226,17 @@ JitRuntime::generateEnterJIT(JSContext* 
 
         // Enter exit frame.
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), valuesSize);
         masm.makeFrameDescriptor(valuesSize, JitFrame_BaselineJS, ExitFrameLayout::Size());
         masm.push(valuesSize);
         masm.push(Imm32(0)); // Fake return address.
         // No GC things to mark, push a bare token.
         masm.loadJSContext(scratch);
-        masm.enterFakeExitFrame(scratch, ExitFrameLayoutBareToken);
+        masm.enterFakeExitFrame(scratch, scratch, ExitFrameLayoutBareToken);
 
         regs.add(valuesSize);
 
         masm.push(framePtr);
         masm.push(reg_code);
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(framePtr); // BaselineFrame
@@ -662,32 +662,32 @@ JitRuntime::generateVMWrapper(JSContext*
 
     // Generate a separated code for the wrapper.
     MacroAssembler masm;
 
     // Avoid conflicts with argument registers while discarding the result after
     // the function call.
     AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
 
-    // Wrapper register set is a superset of Volatile register set.
-    JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0);
+    static_assert((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0,
+                   "Wrapper register set must be a superset of Volatile register set");
 
     // The context is the first argument.
     Register cxreg = IntArgReg0;
     regs.take(cxreg);
 
     // Stack is:
     //    ... frame ...
     //  +12 [args]
     //  +8  descriptor
     //  +0  returnAddress
     //
     // We're aligned to an exit frame, so link it up.
     masm.loadJSContext(cxreg);
-    masm.enterExitFrame(cxreg, &f);
+    masm.enterExitFrame(cxreg, regs.getAny(), &f);
 
     // Save the current stack pointer as the base for copying arguments.
     Register argsBase = InvalidReg;
     if (f.explicitArgs) {
         argsBase = r10;
         regs.take(argsBase);
         masm.lea(Operand(rsp, ExitFrameLayout::SizeWithFooter()), argsBase);
     }
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -221,17 +221,17 @@ JitRuntime::generateEnterJIT(JSContext* 
 
         // Enter exit frame.
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch);
         masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS, ExitFrameLayout::Size());
         masm.push(scratch); // Fake return address.
         masm.push(Imm32(0));
         // No GC things to mark on the stack, push a bare token.
         masm.loadJSContext(scratch);
-        masm.enterFakeExitFrame(scratch, ExitFrameLayoutBareToken);
+        masm.enterFakeExitFrame(scratch, scratch, ExitFrameLayoutBareToken);
 
         masm.push(framePtr);
         masm.push(jitcode);
 
         masm.setupUnalignedABICall(scratch);
         masm.passABIArg(framePtr); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
@@ -692,31 +692,31 @@ JitRuntime::generateVMWrapper(JSContext*
 
     // Generate a separated code for the wrapper.
     MacroAssembler masm;
 
     // Avoid conflicts with argument registers while discarding the result after
     // the function call.
     AllocatableGeneralRegisterSet regs(Register::Codes::WrapperMask);
 
-    // Wrapper register set is a superset of Volatile register set.
-    JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0);
+    static_assert((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0,
+                   "Wrapper register set must be a superset of Volatile register set.");
 
     // The context is the first argument.
     Register cxreg = regs.takeAny();
 
     // Stack is:
     //    ... frame ...
     //  +8  [args]
     //  +4  descriptor
     //  +0  returnAddress
     //
     // We're aligned to an exit frame, so link it up.
     masm.loadJSContext(cxreg);
-    masm.enterExitFrame(cxreg, &f);
+    masm.enterExitFrame(cxreg, regs.getAny(), &f);
 
     // Save the current stack pointer as the base for copying arguments.
     Register argsBase = InvalidReg;
     if (f.explicitArgs) {
         argsBase = regs.takeAny();
         masm.lea(Operand(esp, ExitFrameLayout::SizeWithFooter()), argsBase);
     }
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1387,17 +1387,16 @@ JSContext::JSContext(JSRuntime* runtime,
     jitIsBroken(false),
     asyncCauseForNewActivations(nullptr),
     asyncCallIsExplicit(false),
     interruptCallbackDisabled(false),
     interrupt_(false),
     handlingJitInterrupt_(false),
     osrTempData_(nullptr),
     ionReturnOverride_(MagicValue(JS_ARG_POISON)),
-    jitTop(nullptr),
     jitStackLimit(UINTPTR_MAX),
     jitStackLimitNoInterrupt(UINTPTR_MAX),
     getIncumbentGlobalCallback(nullptr),
     enqueuePromiseJobCallback(nullptr),
     enqueuePromiseJobCallbackData(nullptr),
     jobQueue(nullptr),
     drainingJobQueue(false),
     stopDrainingJobQueue(false),
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -304,22 +304,16 @@ struct JSContext : public JS::RootingCon
 
     // Methods specific to any HelperThread for the context.
     bool addPendingCompileError(js::CompileError** err);
     void addPendingOverRecursed();
     void addPendingOutOfMemory();
 
     JSRuntime* runtime() { return runtime_; }
 
-    static size_t offsetOfActivation() {
-        return offsetof(JSContext, activation_);
-    }
-    static size_t offsetOfProfilingActivation() {
-        return offsetof(JSContext, profilingActivation_);
-     }
     static size_t offsetOfCompartment() {
         return offsetof(JSContext, compartment_);
     }
 
     friend class JS::AutoSaveExceptionState;
     friend class js::jit::DebugModeOSRVolatileJitFrameIterator;
     friend void js::ReportOverRecursed(JSContext*, unsigned errorNumber);
 
@@ -383,22 +377,29 @@ struct JSContext : public JS::RootingCon
      * thread.
      */
     js::Activation* volatile profilingActivation_;
 
   public:
     js::Activation* activation() const {
         return activation_;
     }
+    static size_t offsetOfActivation() {
+        return offsetof(JSContext, activation_);
+    }
+
     js::Activation* profilingActivation() const {
         return profilingActivation_;
     }
     void* addressOfProfilingActivation() {
         return (void*) &profilingActivation_;
     }
+    static size_t offsetOfProfilingActivation() {
+        return offsetof(JSContext, profilingActivation_);
+     }
 
   private:
     /* Space for interpreter frames. */
     js::ThreadLocalData<js::InterpreterStack> interpreterStack_;
 
   public:
     js::InterpreterStack& interpreterStack() {
         return interpreterStack_.ref();
@@ -905,22 +906,16 @@ struct JSContext : public JS::RootingCon
         return v;
     }
     void setIonReturnOverride(const js::Value& v) {
         MOZ_ASSERT(!hasIonReturnOverride());
         MOZ_ASSERT(!v.isMagic());
         ionReturnOverride_ = v;
     }
 
-    /*
-     * If Baseline or Ion code is on the stack, and has called into C++, this
-     * will be aligned to an exit frame.
-     */
-    js::ThreadLocalData<uint8_t*> jitTop;
-
     mozilla::Atomic<uintptr_t, mozilla::Relaxed> jitStackLimit;
 
     // Like jitStackLimit, but not reset to trigger interrupts.
     js::ThreadLocalData<uintptr_t> jitStackLimitNoInterrupt;
 
     // Promise callbacks.
     js::ThreadLocalData<JSGetIncumbentGlobalCallback> getIncumbentGlobalCallback;
     js::ThreadLocalData<JSEnqueuePromiseJobCallback> enqueuePromiseJobCallback;
--- a/js/src/vm/GeckoProfiler.cpp
+++ b/js/src/vm/GeckoProfiler.cpp
@@ -56,16 +56,33 @@ GeckoProfiler::setProfilingStack(PseudoS
 }
 
 void
 GeckoProfiler::setEventMarker(void (*fn)(const char*))
 {
     eventMarker_ = fn;
 }
 
+/* Get a pointer to the top-most profiling frame, given the exit frame pointer. */
+static void*
+GetTopProfilingJitFrame(Activation* act)
+{
+    if (!act || !act->isJit())
+        return nullptr;
+
+    // For null exitFrame, there is no previous exit frame, just return.
+    uint8_t* exitFP = act->asJit()->exitFP();
+    if (!exitFP)
+        return nullptr;
+
+    jit::JitProfilingFrameIterator iter(exitFP);
+    MOZ_ASSERT(!iter.done());
+    return iter.fp();
+}
+
 bool
 GeckoProfiler::enable(bool enabled)
 {
     MOZ_ASSERT(installed());
 
     if (enabled_ == enabled)
         return true;
 
@@ -114,24 +131,26 @@ GeckoProfiler::enable(bool enabled)
 
     /* Update lastProfilingFrame to point to the top-most JS jit-frame currently on
      * stack.
      */
     for (const CooperatingContext& target : rt->cooperatingContexts()) {
         if (target.context()->jitActivation) {
             // Walk through all activations, and set their lastProfilingFrame appropriately.
             if (enabled) {
-                void* lastProfilingFrame = GetTopProfilingJitFrame(target.context()->jitTop);
+                Activation* act = target.context()->activation();
+                void* lastProfilingFrame = GetTopProfilingJitFrame(act);
+
                 jit::JitActivation* jitActivation = target.context()->jitActivation;
                 while (jitActivation) {
                     jitActivation->setLastProfilingFrame(lastProfilingFrame);
                     jitActivation->setLastProfilingCallSite(nullptr);
 
-                    lastProfilingFrame = GetTopProfilingJitFrame(jitActivation->prevJitTop());
                     jitActivation = jitActivation->prevJitActivation();
+                    lastProfilingFrame = GetTopProfilingJitFrame(jitActivation);
                 }
             } else {
                 jit::JitActivation* jitActivation = target.context()->jitActivation;
                 while (jitActivation) {
                     jitActivation->setLastProfilingFrame(nullptr);
                     jitActivation->setLastProfilingCallSite(nullptr);
                     jitActivation = jitActivation->prevJitActivation();
                 }
@@ -539,20 +558,8 @@ AutoSuppressProfilerSampling::AutoSuppre
         cx_->disableProfilerSampling();
 }
 
 AutoSuppressProfilerSampling::~AutoSuppressProfilerSampling()
 {
     if (previouslyEnabled_)
         cx_->enableProfilerSampling();
 }
-
-void*
-js::GetTopProfilingJitFrame(uint8_t* exitFramePtr)
-{
-    // For null exitFrame, there is no previous exit frame, just return.
-    if (!exitFramePtr)
-        return nullptr;
-
-    jit::JitProfilingFrameIterator iter(exitFramePtr);
-    MOZ_ASSERT(!iter.done());
-    return iter.fp();
-}
--- a/js/src/vm/GeckoProfiler.h
+++ b/js/src/vm/GeckoProfiler.h
@@ -289,15 +289,11 @@ class GeckoProfilerInstrumentation
     explicit GeckoProfilerInstrumentation(GeckoProfiler* profiler) : profiler_(profiler) {}
 
     /* Small proxies around GeckoProfiler */
     bool enabled() { return profiler_ && profiler_->enabled(); }
     GeckoProfiler* profiler() { MOZ_ASSERT(enabled()); return profiler_; }
     void disable() { profiler_ = nullptr; }
 };
 
-
-/* Get a pointer to the top-most profiling frame, given the exit frame pointer. */
-void* GetTopProfilingJitFrame(uint8_t* exitFramePtr);
-
 } /* namespace js */
 
 #endif /* vm_GeckoProfiler_h */
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1014,17 +1014,17 @@ FrameIter::updatePcQuadratic()
         data_.pc_ = data_.interpFrames_.pc();
         return;
       }
       case JIT:
         if (data_.jitFrames_.isBaselineJS()) {
             jit::BaselineFrame* frame = data_.jitFrames_.baselineFrame();
             jit::JitActivation* activation = data_.activations_->asJit();
 
-            // ActivationIterator::jitTop_ may be invalid, so create a new
+            // activation's exitFP may be invalid, so create a new
             // activation iterator.
             data_.activations_ = ActivationIterator(data_.cx_);
             while (data_.activations_.activation() != activation)
                 ++data_.activations_;
 
             // Look for the current frame.
             data_.jitFrames_ = jit::JitFrameIterator(data_.activations_);
             while (!data_.jitFrames_.isBaselineJS() || data_.jitFrames_.baselineFrame() != frame)
@@ -1400,17 +1400,17 @@ ActivationEntryMonitor::ActivationEntryM
             entryMonitor_->Entry(cx_, jit::CalleeTokenToScript(entryToken), stack, asyncCause);
     }
 }
 
 /*****************************************************************************/
 
 jit::JitActivation::JitActivation(JSContext* cx, bool active)
   : Activation(cx, Jit),
-    prevJitTop_(cx->jitTop),
+    exitFP_(nullptr),
     prevJitActivation_(cx->jitActivation),
     active_(active),
     rematerializedFrames_(nullptr),
     ionRecovery_(cx),
     bailoutData_(nullptr),
     lastProfilingFrame_(nullptr),
     lastProfilingCallSite_(nullptr)
 {
@@ -1420,42 +1420,32 @@ jit::JitActivation::JitActivation(JSCont
     }
 }
 
 jit::JitActivation::~JitActivation()
 {
     if (active_) {
         if (isProfiling())
             unregisterProfiling();
-
-        cx_->jitTop = prevJitTop_;
         cx_->jitActivation = prevJitActivation_;
     } else {
-        MOZ_ASSERT(cx_->jitTop == prevJitTop_);
         MOZ_ASSERT(cx_->jitActivation == prevJitActivation_);
     }
 
     // All reocvered value are taken from activation during the bailout.
     MOZ_ASSERT(ionRecovery_.empty());
 
     // The BailoutFrameInfo should have unregistered itself from the
     // JitActivations.
     MOZ_ASSERT(!bailoutData_);
 
     clearRematerializedFrames();
     js_delete(rematerializedFrames_);
 }
 
-bool
-jit::JitActivation::isProfiling() const
-{
-    // All JitActivations can be profiled.
-    return true;
-}
-
 void
 jit::JitActivation::setBailoutData(jit::BailoutFrameInfo* bailoutData)
 {
     MOZ_ASSERT(!bailoutData_);
     bailoutData_ = bailoutData;
 }
 
 void
@@ -1473,28 +1463,24 @@ jit::JitActivation::setActive(JSContext*
 {
     // Only allowed to deactivate/activate if activation is top.
     // (Not tested and will probably fail in other situations.)
     MOZ_ASSERT(cx->activation_ == this);
     MOZ_ASSERT(active != active_);
 
     if (active) {
         *((volatile bool*) active_) = true;
-        MOZ_ASSERT(prevJitTop_ == cx->jitTop);
         MOZ_ASSERT(prevJitActivation_ == cx->jitActivation);
         cx->jitActivation = this;
 
         registerProfiling();
-
     } else {
         unregisterProfiling();
 
-        cx->jitTop = prevJitTop_;
         cx->jitActivation = prevJitActivation_;
-
         *((volatile bool*) active_) = false;
     }
 }
 
 void
 jit::JitActivation::removeRematerializedFrame(uint8_t* top)
 {
     if (!rematerializedFrames_)
@@ -1756,18 +1742,17 @@ Activation::unregisterProfiling()
     Activation* prevProfiling = prevProfiling_;
     while (prevProfiling && prevProfiling->isJit() && !prevProfiling->asJit()->isActive())
         prevProfiling = prevProfiling->prevProfiling_;
 
     cx_->profilingActivation_ = prevProfiling;
 }
 
 ActivationIterator::ActivationIterator(JSContext* cx)
-    : jitTop_(cx->jitTop)
-    , activation_(cx->activation_)
+  : activation_(cx->activation_)
 {
     MOZ_ASSERT(cx == TlsContext.get());
     settle();
 }
 
 ActivationIterator::ActivationIterator(JSContext* cx, const CooperatingContext& target)
 {
     MOZ_ASSERT(cx == TlsContext.get());
@@ -1776,48 +1761,43 @@ ActivationIterator::ActivationIterator(J
     // we must be in a scope where changes of the active context are prohibited.
     // Otherwise our state would be corrupted if the target thread resumed
     // execution while we are iterating over its state.
     MOZ_ASSERT(cx->runtime()->activeContextChangeProhibited() ||
                !cx->runtime()->gc.canChangeActiveContext(cx));
 
     // Tolerate a null target context, in case we are iterating over the
     // activations for a zone group that is not in use by any thread.
-    jitTop_ = target.context() ? target.context()->jitTop.ref() : nullptr;
     activation_ = target.context() ? target.context()->activation_.ref() : nullptr;
 
     settle();
 }
 
 ActivationIterator&
 ActivationIterator::operator++()
 {
     MOZ_ASSERT(activation_);
-    if (activation_->isJit() && activation_->asJit()->isActive())
-        jitTop_ = activation_->asJit()->prevJitTop();
     activation_ = activation_->prev();
     settle();
     return *this;
 }
 
 void
 ActivationIterator::settle()
 {
-    // Stop at the next active activation. No need to update jitTop_, since
-    // we don't iterate over an active jit activation.
+    // Stop at the next active activation.
     while (!done() && activation_->isJit() && !activation_->asJit()->isActive())
         activation_ = activation_->prev();
 }
 
 JS::ProfilingFrameIterator::ProfilingFrameIterator(JSContext* cx, const RegisterState& state,
                                                    uint32_t sampleBufferGen)
   : cx_(cx),
     sampleBufferGen_(sampleBufferGen),
-    activation_(nullptr),
-    savedPrevJitTop_(nullptr)
+    activation_(nullptr)
 {
     if (!cx->runtime()->geckoProfiler().enabled())
         MOZ_CRASH("ProfilingFrameIterator called when geckoProfiler not enabled for runtime.");
 
     if (!cx->profilingActivation())
         return;
 
     // If profiler sampling is not enabled, skip.
@@ -1883,19 +1863,16 @@ JS::ProfilingFrameIterator::settle()
 void
 JS::ProfilingFrameIterator::iteratorConstruct(const RegisterState& state)
 {
     MOZ_ASSERT(!done());
     MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
 
     if (activation_->isWasm()) {
         new (storage()) wasm::ProfilingFrameIterator(*activation_->asWasm(), state);
-        // Set savedPrevJitTop_ to the actual jitTop_ from the runtime.
-        AutoNoteSingleThreadedRegion anstr;
-        savedPrevJitTop_ = activation_->cx()->jitTop;
         return;
     }
 
     MOZ_ASSERT(activation_->asJit()->isActive());
     new (storage()) jit::JitProfilingFrameIterator(cx_, state);
 }
 
 void
@@ -1905,33 +1882,30 @@ JS::ProfilingFrameIterator::iteratorCons
     MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
 
     if (activation_->isWasm()) {
         new (storage()) wasm::ProfilingFrameIterator(*activation_->asWasm());
         return;
     }
 
     MOZ_ASSERT(activation_->asJit()->isActive());
-    MOZ_ASSERT(savedPrevJitTop_ != nullptr);
-    new (storage()) jit::JitProfilingFrameIterator(savedPrevJitTop_);
+    new (storage()) jit::JitProfilingFrameIterator(activation_->asJit()->exitFP());
 }
 
 void
 JS::ProfilingFrameIterator::iteratorDestroy()
 {
     MOZ_ASSERT(!done());
     MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
 
     if (activation_->isWasm()) {
         wasmIter().~ProfilingFrameIterator();
         return;
     }
 
-    // Save prevjitTop for later use
-    savedPrevJitTop_ = activation_->asJit()->prevJitTop();
     jitIter().~JitProfilingFrameIterator();
 }
 
 bool
 JS::ProfilingFrameIterator::iteratorDone()
 {
     MOZ_ASSERT(!done());
     MOZ_ASSERT(activation_->isWasm() || activation_->isJit());
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1450,18 +1450,16 @@ class InterpreterActivation : public Act
     void clearInterruptsMask() {
         opMask_ = 0;
     }
 };
 
 // Iterates over a thread's activation list.
 class ActivationIterator
 {
-    uint8_t* jitTop_;
-
   protected:
     Activation* activation_;
 
   private:
     void settle();
 
   public:
     explicit ActivationIterator(JSContext* cx);
@@ -1474,38 +1472,37 @@ class ActivationIterator
     ActivationIterator& operator++();
 
     Activation* operator->() const {
         return activation_;
     }
     Activation* activation() const {
         return activation_;
     }
-    uint8_t* jitTop() const {
-        MOZ_ASSERT(activation_->isJit());
-        return jitTop_;
-    }
     bool done() const {
         return activation_ == nullptr;
     }
 };
 
 namespace jit {
 
 class BailoutFrameInfo;
 
 // A JitActivation is used for frames running in Baseline or Ion.
 class JitActivation : public Activation
 {
-    uint8_t* prevJitTop_;
+    // If Baseline or Ion code is on the stack, and has called into C++, this
+    // will be aligned to an ExitFrame.
+    uint8_t* exitFP_;
+
     JitActivation* prevJitActivation_;
     bool active_;
 
     // Rematerialized Ion frames which has info copied out of snapshots. Maps
-    // frame pointers (i.e. jitTop) to a vector of rematerializations of all
+    // frame pointers (i.e. exitFP_) to a vector of rematerializations of all
     // inline frames associated with that frame.
     //
     // This table is lazily initialized by calling getRematerializedFrame.
     typedef GCVector<RematerializedFrame*> RematerializedFrameVector;
     typedef HashMap<uint8_t*, RematerializedFrameVector> RematerializedFrameTable;
     RematerializedFrameTable* rematerializedFrames_;
 
     // This vector is used to remember the outcome of the evaluation of recover
@@ -1547,30 +1544,38 @@ class JitActivation : public Activation
     explicit JitActivation(JSContext* cx, bool active = true);
     ~JitActivation();
 
     bool isActive() const {
         return active_;
     }
     void setActive(JSContext* cx, bool active = true);
 
-    bool isProfiling() const;
+    bool isProfiling() const {
+        // All JitActivations can be profiled.
+        return true;
+    }
 
-    uint8_t* prevJitTop() const {
-        return prevJitTop_;
-    }
     JitActivation* prevJitActivation() const {
         return prevJitActivation_;
     }
-    static size_t offsetOfPrevJitTop() {
-        return offsetof(JitActivation, prevJitTop_);
-    }
     static size_t offsetOfPrevJitActivation() {
         return offsetof(JitActivation, prevJitActivation_);
     }
+
+    void setExitFP(uint8_t* fp) {
+        exitFP_ = fp;
+    }
+    uint8_t* exitFP() const {
+        return exitFP_;
+    }
+    static size_t offsetOfExitFP() {
+        return offsetof(JitActivation, exitFP_);
+    }
+
     static size_t offsetOfActiveUint8() {
         MOZ_ASSERT(sizeof(bool) == 1);
         return offsetof(JitActivation, active_);
     }
 
 #ifdef CHECK_OSIPOINT_REGISTERS
     void setCheckRegs(bool check) {
         checkRegs_ = check;
@@ -1673,16 +1678,21 @@ class JitActivationIterator : public Act
         settle();
     }
 
     JitActivationIterator& operator++() {
         ActivationIterator::operator++();
         settle();
         return *this;
     }
+
+    uint8_t* exitFP() const {
+        MOZ_ASSERT(activation_->isJit());
+        return activation_->asJit()->exitFP();
+    }
 };
 
 } // namespace jit
 
 // Iterates over the frames of a single InterpreterActivation.
 class InterpreterFrameIterator
 {
     InterpreterActivation* activation_;
--- a/js/src/wasm/WasmStubs.cpp
+++ b/js/src/wasm/WasmStubs.cpp
@@ -772,20 +772,16 @@ wasm::GenerateImportJitExit(MacroAssembl
         Register act = WasmIonExitRegD1;
         Register tmp = WasmIonExitRegD2;
 
         // JitActivation* act = cx->activation();
         masm.loadPtr(Address(WasmTlsReg, offsetof(TlsData, addressOfContext)), cx);
         masm.loadPtr(Address(cx, 0), cx);
         masm.loadPtr(Address(cx, JSContext::offsetOfActivation()), act);
 
-        // cx->jitTop = act->prevJitTop_;
-        masm.loadPtr(Address(act, JitActivation::offsetOfPrevJitTop()), tmp);
-        masm.storePtr(tmp, Address(cx, offsetof(JSContext, jitTop)));
-
         // cx->jitActivation = act->prevJitActivation_;
         masm.loadPtr(Address(act, JitActivation::offsetOfPrevJitActivation()), tmp);
         masm.storePtr(tmp, Address(cx, offsetof(JSContext, jitActivation)));
 
         // cx->profilingActivation = act->prevProfilingActivation_;
         masm.loadPtr(Address(act, Activation::offsetOfPrevProfiling()), tmp);
         masm.storePtr(tmp, Address(cx, JSContext::offsetOfProfilingActivation()));