Bug 1006473 - Refactor fake exit frame handling and fix fake exit frames pushed by bailout and OSR. (r=jandem)
authorShu-yu Guo <shu@rfrn.org>
Fri, 09 May 2014 13:35:40 -0700
changeset 182482 aa8a3701eac386bffbec3d5fcfd0f23640b6dede
parent 182481 967e21a26f35d8e07afed1a6c2387999ad877a57
child 182483 91b7dcd04e658ed0d628fae9b23ae8dcaeab72d2
push id26764
push usercbook@mozilla.com
push dateMon, 12 May 2014 11:35:17 +0000
treeherdermozilla-central@a64ed5aba131 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1006473
milestone32.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 1006473 - Refactor fake exit frame handling and fix fake exit frames pushed by bailout and OSR. (r=jandem)
js/src/jit-test/tests/debug/bug1006473.js
js/src/jit/BaselineIC.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/IonCaches.cpp
js/src/jit/IonFrames.cpp
js/src/jit/IonFrames.h
js/src/jit/IonMacroAssembler.cpp
js/src/jit/IonMacroAssembler.h
js/src/jit/JitFrameIterator-inl.h
js/src/jit/JitFrameIterator.h
js/src/jit/arm/Trampoline-arm.cpp
js/src/jit/mips/Trampoline-mips.cpp
js/src/jit/x64/Trampoline-x64.cpp
js/src/jit/x86/Trampoline-x86.cpp
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/bug1006473.js
@@ -0,0 +1,19 @@
+// |jit-test| error: ReferenceError
+
+var lfcode = new Array();
+lfcode.push("gczeal(4);");
+lfcode.push("setJitCompilerOption('ion.usecount.trigger', 30);");
+lfcode.push("\
+var g = newGlobal();\
+g.parent = this;\
+g.eval('function f(frame, exc) { f2 = function () { return exc; }; exc = 123; }');\
+g.eval('new Debugger(parent).onExceptionUnwind = f;');\
+var obj = int8  ('oops');\
+");
+while (true) {
+    var file = lfcode.shift(); if (file == undefined) { break; }
+    loadFile(file)
+}
+function loadFile(lfVarx) {
+    evaluate(lfVarx);
+}
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -8805,17 +8805,17 @@ ICCall_Native::Compiler::generateStubCod
 
     // Construct a native exit frame.
     masm.push(argcReg);
 
     Register scratch = regs.takeAny();
     EmitCreateStubFrameDescriptor(masm, scratch);
     masm.push(scratch);
     masm.push(BaselineTailCallReg);
-    masm.enterFakeExitFrame();
+    masm.enterFakeExitFrame(IonNativeExitFrameLayout::Token());
 
     // If needed, update SPS Profiler frame entry.  At this point, BaselineTailCallReg
     // and scratch can be clobbered.
     emitProfilingUpdate(masm, BaselineTailCallReg, scratch, ICCall_Native::offsetOfPCOffset());
 
     // Execute call.
     masm.setupUnalignedABICall(3, scratch);
     masm.loadJSContext(scratch);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2037,17 +2037,18 @@ CodeGenerator::visitCallNative(LCallNati
     masm.movePtr(StackPointer, argVpReg);
 
     masm.Push(argUintNReg);
 
     // Construct native exit frame.
     uint32_t safepointOffset;
     if (!masm.buildFakeExitFrame(tempReg, &safepointOffset))
         return false;
-    masm.enterFakeExitFrame(argContextReg, tempReg, executionMode);
+    masm.enterFakeExitFrame(argContextReg, tempReg, executionMode,
+                            IonNativeExitFrameLayout::Token());
 
     if (!markSafepointAt(safepointOffset, call))
         return false;
 
     // Construct and execute call.
     masm.setupUnalignedABICall(3, tempReg);
     masm.passABIArg(argContextReg);
     masm.passABIArg(argUintNReg);
@@ -2147,17 +2148,17 @@ CodeGenerator::visitCallDOMNative(LCallD
     // DOMExitFrames.
     masm.Push(argObj);
     masm.movePtr(StackPointer, argObj);
 
     // Construct native exit frame.
     uint32_t safepointOffset;
     if (!masm.buildFakeExitFrame(argJSContext, &safepointOffset))
         return false;
-    masm.enterFakeExitFrame(ION_FRAME_DOMMETHOD);
+    masm.enterFakeExitFrame(IonDOMMethodExitFrameLayout::Token());
 
     if (!markSafepointAt(safepointOffset, call))
         return false;
 
     // Construct and execute call.
     masm.setupUnalignedABICall(4, argJSContext);
 
     masm.loadJSContext(argJSContext);
@@ -8065,17 +8066,17 @@ CodeGenerator::visitGetDOMProperty(LGetD
     masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg);
 
     // Rooting will happen at GC time.
     masm.movePtr(StackPointer, ObjectReg);
 
     uint32_t safepointOffset;
     if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset))
         return false;
-    masm.enterFakeExitFrame(ION_FRAME_DOMGETTER);
+    masm.enterFakeExitFrame(IonDOMExitFrameLayout::GetterToken());
 
     if (!markSafepointAt(safepointOffset, ins))
         return false;
 
     masm.setupUnalignedABICall(4, JSContextReg);
 
     masm.loadJSContext(JSContextReg);
 
@@ -8142,17 +8143,17 @@ CodeGenerator::visitSetDOMProperty(LSetD
     masm.loadPrivate(Address(ObjectReg, JSObject::getFixedSlotOffset(0)), PrivateReg);
 
     // Rooting will happen at GC time.
     masm.movePtr(StackPointer, ObjectReg);
 
     uint32_t safepointOffset;
     if (!masm.buildFakeExitFrame(JSContextReg, &safepointOffset))
         return false;
-    masm.enterFakeExitFrame(ION_FRAME_DOMSETTER);
+    masm.enterFakeExitFrame(IonDOMExitFrameLayout::SetterToken());
 
     if (!markSafepointAt(safepointOffset, ins))
         return false;
 
     masm.setupUnalignedABICall(4, JSContextReg);
 
     masm.loadJSContext(JSContextReg);
 
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -903,17 +903,17 @@ EmitGetterCall(JSContext *cx, MacroAssem
         masm.movePtr(StackPointer, argVpReg);
 
         // Push marking data for later use.
         masm.Push(argUintNReg);
         attacher.pushStubCodePointer(masm);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
-        masm.enterFakeExitFrame(ION_FRAME_OOL_NATIVE);
+        masm.enterFakeExitFrame(IonOOLNativeExitFrameLayout::Token());
 
         // Construct and execute call.
         masm.setupUnalignedABICall(3, scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argUintNReg);
         masm.passABIArg(argVpReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native()));
 
@@ -948,17 +948,17 @@ EmitGetterCall(JSContext *cx, MacroAssem
 
         masm.Push(object);
         masm.movePtr(StackPointer, argObjReg);
 
         masm.loadJSContext(argJSContextReg);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
-        masm.enterFakeExitFrame(ION_FRAME_OOL_PROPERTY_OP);
+        masm.enterFakeExitFrame(IonOOLPropertyOpExitFrameLayout::Token());
 
         // Make the call.
         masm.setupUnalignedABICall(4, scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argObjReg);
         masm.passABIArg(argIdReg);
         masm.passABIArg(argVpReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target));
@@ -1332,17 +1332,17 @@ EmitCallProxyGet(JSContext *cx, MacroAss
     masm.Push(object);
     masm.Push(object);
     masm.movePtr(StackPointer, argProxyReg);
 
     masm.loadJSContext(argJSContextReg);
 
     if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
         return false;
-    masm.enterFakeExitFrame(ION_FRAME_OOL_PROXY);
+    masm.enterFakeExitFrame(IonOOLProxyExitFrameLayout::Token());
 
     // Make the call.
     masm.setupUnalignedABICall(5, scratch);
     masm.passABIArg(argJSContextReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argIdReg);
     masm.passABIArg(argVpReg);
@@ -2107,17 +2107,17 @@ EmitCallProxySet(JSContext *cx, MacroAss
     masm.Push(object);
     masm.movePtr(StackPointer, argProxyReg);
 
     masm.loadJSContext(argJSContextReg);
     masm.move32(Imm32(strict? 1 : 0), argStrictReg);
 
     if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
         return false;
-    masm.enterFakeExitFrame(ION_FRAME_OOL_PROXY);
+    masm.enterFakeExitFrame(IonOOLProxyExitFrameLayout::Token());
 
     // Make the call.
     masm.setupUnalignedABICall(6, scratch);
     masm.passABIArg(argJSContextReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argProxyReg);
     masm.passABIArg(argIdReg);
     masm.passABIArg(argStrictReg);
@@ -2315,17 +2315,17 @@ GenerateCallSetter(JSContext *cx, IonScr
         masm.move32(Imm32(1), argUintNReg);
 
         // Push data for GC marking
         masm.Push(argUintNReg);
         attacher.pushStubCodePointer(masm);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
-        masm.enterFakeExitFrame(ION_FRAME_OOL_NATIVE);
+        masm.enterFakeExitFrame(IonOOLNativeExitFrameLayout::Token());
 
         // Make the call
         masm.setupUnalignedABICall(3, scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argUintNReg);
         masm.passABIArg(argVpReg);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, target->native()));
 
@@ -2361,17 +2361,17 @@ GenerateCallSetter(JSContext *cx, IonScr
 
         masm.Push(object);
         masm.movePtr(StackPointer, argObjReg);
 
         masm.loadJSContext(argJSContextReg);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
-        masm.enterFakeExitFrame(ION_FRAME_OOL_PROPERTY_OP);
+        masm.enterFakeExitFrame(IonOOLPropertyOpExitFrameLayout::Token());
 
         // Make the call.
         masm.setupUnalignedABICall(5, scratchReg);
         masm.passABIArg(argJSContextReg);
         masm.passABIArg(argObjReg);
         masm.passABIArg(argIdReg);
         masm.passABIArg(argStrictReg);
         masm.passABIArg(argVpReg);
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -157,53 +157,21 @@ JSFunction *
 JitFrameIterator::maybeCallee() const
 {
     if (isScripted() && (isFunctionFrame()))
         return callee();
     return nullptr;
 }
 
 bool
-JitFrameIterator::isNative() const
-{
-    if (type_ != JitFrame_Exit || isFakeExitFrame())
-        return false;
-    return exitFrame()->footer()->jitCode() == nullptr;
-}
-
-bool
-JitFrameIterator::isOOLNative() const
+JitFrameIterator::isBareExit() const
 {
     if (type_ != JitFrame_Exit)
         return false;
-    return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_NATIVE;
-}
-
-bool
-JitFrameIterator::isOOLPropertyOp() const
-{
-    if (type_ != JitFrame_Exit)
-        return false;
-    return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROPERTY_OP;
-}
-
-bool
-JitFrameIterator::isOOLProxy() const
-{
-    if (type_ != JitFrame_Exit)
-        return false;
-    return exitFrame()->footer()->jitCode() == ION_FRAME_OOL_PROXY;
-}
-
-bool
-JitFrameIterator::isDOMExit() const
-{
-    if (type_ != JitFrame_Exit)
-        return false;
-    return exitFrame()->isDomExit();
+    return exitFrame()->isBareExit();
 }
 
 bool
 JitFrameIterator::isFunctionFrame() const
 {
     return CalleeTokenIsFunction(calleeToken());
 }
 
@@ -1051,67 +1019,75 @@ MarkJitExitFrame(JSTracer *trc, const Ji
     // invalidation and relocation data are no longer reliable.  So the VM
     // wrapper or the invalidation code may be GC if no JitCode keep reference
     // on them.
     JS_ASSERT(uintptr_t(footer->jitCode()) != uintptr_t(-1));
 
     // This correspond to the case where we have build a fake exit frame in
     // CodeGenerator.cpp which handle the case of a native function call. We
     // need to mark the argument vector of the function call.
-    if (frame.isNative()) {
-        IonNativeExitFrameLayout *native = frame.exitFrame()->nativeExit();
+    if (frame.isExitFrameLayout<IonNativeExitFrameLayout>()) {
+        IonNativeExitFrameLayout *native = frame.exitFrame()->as<IonNativeExitFrameLayout>();
         size_t len = native->argc() + 2;
         Value *vp = native->vp();
         gc::MarkValueRootRange(trc, len, vp, "ion-native-args");
         return;
     }
 
-    if (frame.isOOLNative()) {
-        IonOOLNativeExitFrameLayout *oolnative = frame.exitFrame()->oolNativeExit();
+    if (frame.isExitFrameLayout<IonOOLNativeExitFrameLayout>()) {
+        IonOOLNativeExitFrameLayout *oolnative =
+            frame.exitFrame()->as<IonOOLNativeExitFrameLayout>();
         gc::MarkJitCodeRoot(trc, oolnative->stubCode(), "ion-ool-native-code");
         gc::MarkValueRoot(trc, oolnative->vp(), "iol-ool-native-vp");
         size_t len = oolnative->argc() + 1;
         gc::MarkValueRootRange(trc, len, oolnative->thisp(), "ion-ool-native-thisargs");
         return;
     }
 
-    if (frame.isOOLPropertyOp()) {
-        IonOOLPropertyOpExitFrameLayout *oolgetter = frame.exitFrame()->oolPropertyOpExit();
+    if (frame.isExitFrameLayout<IonOOLPropertyOpExitFrameLayout>()) {
+        IonOOLPropertyOpExitFrameLayout *oolgetter =
+            frame.exitFrame()->as<IonOOLPropertyOpExitFrameLayout>();
         gc::MarkJitCodeRoot(trc, oolgetter->stubCode(), "ion-ool-property-op-code");
         gc::MarkValueRoot(trc, oolgetter->vp(), "ion-ool-property-op-vp");
         gc::MarkIdRoot(trc, oolgetter->id(), "ion-ool-property-op-id");
         gc::MarkObjectRoot(trc, oolgetter->obj(), "ion-ool-property-op-obj");
         return;
     }
 
-    if (frame.isOOLProxy()) {
-        IonOOLProxyExitFrameLayout *oolproxy = frame.exitFrame()->oolProxyExit();
+    if (frame.isExitFrameLayout<IonOOLProxyExitFrameLayout>()) {
+        IonOOLProxyExitFrameLayout *oolproxy = frame.exitFrame()->as<IonOOLProxyExitFrameLayout>();
         gc::MarkJitCodeRoot(trc, oolproxy->stubCode(), "ion-ool-proxy-code");
         gc::MarkValueRoot(trc, oolproxy->vp(), "ion-ool-proxy-vp");
         gc::MarkIdRoot(trc, oolproxy->id(), "ion-ool-proxy-id");
         gc::MarkObjectRoot(trc, oolproxy->proxy(), "ion-ool-proxy-proxy");
         gc::MarkObjectRoot(trc, oolproxy->receiver(), "ion-ool-proxy-receiver");
         return;
     }
 
-    if (frame.isDOMExit()) {
-        IonDOMExitFrameLayout *dom = frame.exitFrame()->DOMExit();
+    if (frame.isExitFrameLayout<IonDOMExitFrameLayout>()) {
+        IonDOMExitFrameLayout *dom = frame.exitFrame()->as<IonDOMExitFrameLayout>();
         gc::MarkObjectRoot(trc, dom->thisObjAddress(), "ion-dom-args");
         if (dom->isMethodFrame()) {
             IonDOMMethodExitFrameLayout *method =
                 reinterpret_cast<IonDOMMethodExitFrameLayout *>(dom);
             size_t len = method->argc() + 2;
             Value *vp = method->vp();
             gc::MarkValueRootRange(trc, len, vp, "ion-dom-args");
         } else {
             gc::MarkValueRoot(trc, dom->vp(), "ion-dom-args");
         }
         return;
     }
 
+    if (frame.isBareExit()) {
+        // Nothing to mark. Fake exit frame pushed for VM functions with
+        // nothing to mark on the stack.
+        return;
+    }
+
     MarkJitCodeRoot(trc, footer->addressOfJitCode(), "ion-exit-code");
 
     const VMFunction *f = footer->function();
     if (f == nullptr)
         return;
 
     // Mark arguments of the VM wrapper.
     uint8_t *argBase = frame.exitFrame()->argBase();
@@ -1872,18 +1848,18 @@ JitFrameIterator::isConstructing() const
 }
 
 unsigned
 JitFrameIterator::numActualArgs() const
 {
     if (isScripted())
         return jsFrame()->numActualArgs();
 
-    JS_ASSERT(isNative());
-    return exitFrame()->nativeExit()->argc();
+    JS_ASSERT(isExitFrameLayout<IonNativeExitFrameLayout>());
+    return exitFrame()->as<IonNativeExitFrameLayout>()->argc();
 }
 
 void
 SnapshotIterator::warnUnreadableAllocation()
 {
     fprintf(stderr, "Warning! Tried to access unreadable value allocation (possible f.arguments).\n");
 }
 
--- a/js/src/jit/IonFrames.h
+++ b/js/src/jit/IonFrames.h
@@ -297,23 +297,16 @@ GetTopIonJSScript(uint8_t *jitTop, void 
         ++iter;
         JS_ASSERT(iter.isBaselineJS());
     }
 
     JS_ASSERT(iter.isScripted());
     return iter.script();
 }
 
-static JitCode *const ION_FRAME_DOMGETTER       = (JitCode *)0x1;
-static JitCode *const ION_FRAME_DOMSETTER       = (JitCode *)0x2;
-static JitCode *const ION_FRAME_DOMMETHOD       = (JitCode *)0x3;
-static JitCode *const ION_FRAME_OOL_NATIVE      = (JitCode *)0x4;
-static JitCode *const ION_FRAME_OOL_PROPERTY_OP = (JitCode *)0x5;
-static JitCode *const ION_FRAME_OOL_PROXY       = (JitCode *)0x6;
-
 // Layout of the frame prefix. This assumes the stack architecture grows down.
 // If this is ever not the case, we'll have to refactor.
 class IonCommonFrameLayout
 {
     uint8_t *returnAddress_;
     uintptr_t descriptor_;
 
     static const uintptr_t FrameTypeMask = (1 << FRAMETYPE_BITS) - 1;
@@ -464,16 +457,20 @@ class IonDOMExitFrameLayout;
 // this is the frame layout when we are exiting ion code, and about to enter platform ABI code
 class IonExitFrameLayout : public IonCommonFrameLayout
 {
     inline uint8_t *top() {
         return reinterpret_cast<uint8_t *>(this + 1);
     }
 
   public:
+    // Pushed for "bare" fake exit frames that have no GC things on stack to be
+    // marked.
+    static JitCode *const BareToken() { return (JitCode *)0xFF; }
+
     static inline size_t Size() {
         return sizeof(IonExitFrameLayout);
     }
     static inline size_t SizeWithFooter() {
         return Size() + IonExitFooterFrame::Size();
     }
 
     inline IonExitFooterFrame *footer() {
@@ -487,74 +484,47 @@ class IonExitFrameLayout : public IonCom
     inline uint8_t *argBase() {
         JS_ASSERT(footer()->jitCode() != nullptr);
         return top();
     }
 
     inline bool isWrapperExit() {
         return footer()->function() != nullptr;
     }
-    inline bool isNativeExit() {
-        return footer()->jitCode() == nullptr;
-    }
-    inline bool isOOLNativeExit() {
-        return footer()->jitCode() == ION_FRAME_OOL_NATIVE;
-    }
-    inline bool isOOLPropertyOpExit() {
-        return footer()->jitCode() == ION_FRAME_OOL_PROPERTY_OP;
-    }
-    inline bool isOOLProxyExit() {
-        return footer()->jitCode() == ION_FRAME_OOL_PROXY;
-    }
-    inline bool isDomExit() {
-        JitCode *code = footer()->jitCode();
-        return
-            code == ION_FRAME_DOMGETTER ||
-            code == ION_FRAME_DOMSETTER ||
-            code == ION_FRAME_DOMMETHOD;
+    inline bool isBareExit() {
+        return footer()->jitCode() == BareToken();
     }
 
-    inline IonNativeExitFrameLayout *nativeExit() {
-        // see CodeGenerator::visitCallNative
-        JS_ASSERT(isNativeExit());
-        return reinterpret_cast<IonNativeExitFrameLayout *>(footer());
-    }
-    inline IonOOLNativeExitFrameLayout *oolNativeExit() {
-        JS_ASSERT(isOOLNativeExit());
-        return reinterpret_cast<IonOOLNativeExitFrameLayout *>(footer());
+    // See the various exit frame layouts below.
+    template <typename T> inline bool is() {
+        return footer()->jitCode() == T::Token();
     }
-    inline IonOOLPropertyOpExitFrameLayout *oolPropertyOpExit() {
-        JS_ASSERT(isOOLPropertyOpExit());
-        return reinterpret_cast<IonOOLPropertyOpExitFrameLayout *>(footer());
-    }
-    inline IonOOLProxyExitFrameLayout *oolProxyExit() {
-        JS_ASSERT(isOOLProxyExit());
-        return reinterpret_cast<IonOOLProxyExitFrameLayout *>(footer());
-    }
-    inline IonDOMExitFrameLayout *DOMExit() {
-        JS_ASSERT(isDomExit());
-        return reinterpret_cast<IonDOMExitFrameLayout *>(footer());
+    template <typename T> inline T *as() {
+        MOZ_ASSERT(is<T>());
+        return reinterpret_cast<T *>(footer());
     }
 };
 
-// Cannot inherit implementa<tion since we need to extend the top of
+// Cannot inherit implementation since we need to extend the top of
 // IonExitFrameLayout.
 class IonNativeExitFrameLayout
 {
   protected: // only to silence a clang warning about unused private fields
     IonExitFooterFrame footer_;
     IonExitFrameLayout exit_;
     uintptr_t argc_;
 
     // We need to split the Value into 2 fields of 32 bits, otherwise the C++
     // compiler may add some padding between the fields.
     uint32_t loCalleeResult_;
     uint32_t hiCalleeResult_;
 
   public:
+    static JitCode *const Token() { return (JitCode *)0x0; }
+
     static inline size_t Size() {
         return sizeof(IonNativeExitFrameLayout);
     }
 
     static size_t offsetOfResult() {
         return offsetof(IonNativeExitFrameLayout, loCalleeResult_);
     }
     inline Value *vp() {
@@ -581,16 +551,18 @@ class IonOOLNativeExitFrameLayout
     uint32_t loCalleeResult_;
     uint32_t hiCalleeResult_;
 
     // Split Value for |this| and args above.
     uint32_t loThis_;
     uint32_t hiThis_;
 
   public:
+    static JitCode *const Token() { return (JitCode *)0x4; }
+
     static inline size_t Size(size_t argc) {
         // The frame accounts for the callee/result and |this|, so we only need args.
         return sizeof(IonOOLNativeExitFrameLayout) + (argc * sizeof(Value));
     }
 
     static size_t offsetOfResult() {
         return offsetof(IonOOLNativeExitFrameLayout, loCalleeResult_);
     }
@@ -625,16 +597,18 @@ class IonOOLPropertyOpExitFrameLayout
     // use two uint32_t so compiler doesn't align.
     uint32_t vp0_;
     uint32_t vp1_;
 
     // pointer to root the stub's JitCode
     JitCode *stubCode_;
 
   public:
+    static JitCode *const Token() { return (JitCode *)0x5; }
+
     static inline size_t Size() {
         return sizeof(IonOOLPropertyOpExitFrameLayout);
     }
 
     static size_t offsetOfResult() {
         return offsetof(IonOOLPropertyOpExitFrameLayout, vp0_);
     }
 
@@ -675,16 +649,18 @@ class IonOOLProxyExitFrameLayout
     // use two uint32_t so compiler doesn't align.
     uint32_t vp0_;
     uint32_t vp1_;
 
     // pointer to root the stub's JitCode
     JitCode *stubCode_;
 
   public:
+    static JitCode *const Token() { return (JitCode *)0x6; }
+
     static inline size_t Size() {
         return sizeof(IonOOLProxyExitFrameLayout);
     }
 
     static size_t offsetOfResult() {
         return offsetof(IonOOLProxyExitFrameLayout, vp0_);
     }
 
@@ -713,32 +689,33 @@ class IonDOMExitFrameLayout
     JSObject *thisObj;
 
     // We need to split the Value into 2 fields of 32 bits, otherwise the C++
     // compiler may add some padding between the fields.
     uint32_t loCalleeResult_;
     uint32_t hiCalleeResult_;
 
   public:
+    static JitCode *const GetterToken() { return (JitCode *)0x1; }
+    static JitCode *const SetterToken() { return (JitCode *)0x2; }
+
     static inline size_t Size() {
         return sizeof(IonDOMExitFrameLayout);
     }
 
     static size_t offsetOfResult() {
         return offsetof(IonDOMExitFrameLayout, loCalleeResult_);
     }
     inline Value *vp() {
         return reinterpret_cast<Value*>(&loCalleeResult_);
     }
     inline JSObject **thisObjAddress() {
         return &thisObj;
     }
-    inline bool isMethodFrame() {
-        return footer_.jitCode() == ION_FRAME_DOMMETHOD;
-    }
+    inline bool isMethodFrame();
 };
 
 struct IonDOMMethodExitFrameLayoutTraits;
 
 class IonDOMMethodExitFrameLayout
 {
   protected: // only to silence a clang warning about unused private fields
     IonExitFooterFrame footer_;
@@ -752,16 +729,18 @@ class IonDOMMethodExitFrameLayout
     // We need to split the Value into 2 fields of 32 bits, otherwise the C++
     // compiler may add some padding between the fields.
     uint32_t loCalleeResult_;
     uint32_t hiCalleeResult_;
 
     friend struct IonDOMMethodExitFrameLayoutTraits;
 
   public:
+    static JitCode *const Token() { return (JitCode *)0x3; }
+
     static inline size_t Size() {
         return sizeof(IonDOMMethodExitFrameLayout);
     }
 
     static size_t offsetOfResult() {
         return offsetof(IonDOMMethodExitFrameLayout, loCalleeResult_);
     }
 
@@ -774,16 +753,41 @@ class IonDOMMethodExitFrameLayout
     inline JSObject **thisObjAddress() {
         return &thisObj_;
     }
     inline uintptr_t argc() {
         return argc_;
     }
 };
 
+inline bool
+IonDOMExitFrameLayout::isMethodFrame()
+{
+    return footer_.jitCode() == IonDOMMethodExitFrameLayout::Token();
+}
+
+template <>
+inline bool
+IonExitFrameLayout::is<IonDOMExitFrameLayout>()
+{
+    JitCode *code = footer()->jitCode();
+    return
+        code == IonDOMExitFrameLayout::GetterToken() ||
+        code == IonDOMExitFrameLayout::SetterToken() ||
+        code == IonDOMMethodExitFrameLayout::Token();
+}
+
+template <>
+inline IonDOMExitFrameLayout *
+IonExitFrameLayout::as<IonDOMExitFrameLayout>()
+{
+    MOZ_ASSERT(is<IonDOMExitFrameLayout>());
+    return reinterpret_cast<IonDOMExitFrameLayout *>(footer());
+}
+
 struct IonDOMMethodExitFrameLayoutTraits {
     static const size_t offsetOfArgcFromArgv =
         offsetof(IonDOMMethodExitFrameLayout, argc_) -
         offsetof(IonDOMMethodExitFrameLayout, argv_);
 };
 
 class ICStub;
 
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -789,17 +789,18 @@ 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);
         push(temp);
         push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr)));
-        enterFakeExitFrame();
+        // No GC things to mark on the stack, push a bare token.
+        enterFakeExitFrame(IonExitFrameLayout::BareToken());
 
         // If monitorStub is non-null, handle resumeAddr appropriately.
         Label noMonitor;
         Label done;
         branchPtr(Assembler::Equal,
                   Address(bailoutInfo, offsetof(BaselineBailoutInfo, monitorStub)),
                   ImmPtr(nullptr),
                   &noMonitor);
--- a/js/src/jit/IonMacroAssembler.h
+++ b/js/src/jit/IonMacroAssembler.h
@@ -831,17 +831,20 @@ class MacroAssembler : public MacroAssem
   public:
     void enterExitFrame(const VMFunction *f = nullptr) {
         linkExitFrame();
         // Push the ioncode. (Bailout or VM wrapper)
         exitCodePatch_ = PushWithPatch(ImmWord(-1));
         // Push VMFunction pointer, to mark arguments.
         Push(ImmPtr(f));
     }
-    void enterFakeExitFrame(JitCode *codeVal = nullptr) {
+
+    // The JitCode * argument here is one of the tokens defined in the various
+    // exit frame layout classes, e.g. IonNativeExitFrameLayout::Token().
+    void enterFakeExitFrame(JitCode *codeVal) {
         linkExitFrame();
         Push(ImmPtr(codeVal));
         Push(ImmPtr(nullptr));
     }
 
     void loadThreadPool(Register pool) {
         // JitRuntimes are tied to JSRuntimes and there is one ThreadPool per
         // JSRuntime, so we can hardcode the ThreadPool address here.
@@ -852,22 +855,20 @@ class MacroAssembler : public MacroAssem
     void loadContext(Register cxReg, Register scratch, ExecutionMode executionMode);
 
     void enterParallelExitFrameAndLoadContext(const VMFunction *f, Register cx,
                                               Register scratch);
 
     void enterExitFrameAndLoadContext(const VMFunction *f, Register cxReg, Register scratch,
                                       ExecutionMode executionMode);
 
-    void enterFakeParallelExitFrame(Register cx, Register scratch,
-                                    JitCode *codeVal = nullptr);
+    void enterFakeParallelExitFrame(Register cx, Register scratch, JitCode *codeVal);
 
-    void enterFakeExitFrame(Register cxReg, Register scratch,
-                            ExecutionMode executionMode,
-                            JitCode *codeVal = nullptr);
+    void enterFakeExitFrame(Register cxReg, Register scratch, ExecutionMode executionMode,
+                            JitCode *codeVal);
 
     void leaveExitFrame() {
         freeStack(IonExitFooterFrame::Size());
     }
 
     bool hasEnteredExitFrame() const {
         return exitCodePatch_.offset() != 0;
     }
--- a/js/src/jit/JitFrameIterator-inl.h
+++ b/js/src/jit/JitFrameIterator-inl.h
@@ -8,16 +8,17 @@
 #define jit_JitFrameIterator_inl_h
 
 #ifdef JS_ION
 
 #include "jit/JitFrameIterator.h"
 
 #include "jit/Bailouts.h"
 #include "jit/BaselineFrame.h"
+#include "jit/IonFrames.h"
 
 namespace js {
 namespace jit {
 
 template <AllowGC allowGC>
 inline
 InlineFrameIteratorMaybeGC<allowGC>::InlineFrameIteratorMaybeGC(ThreadSafeContext *cx,
                                                                 const IonBailoutIterator *iter)
@@ -35,14 +36,23 @@ InlineFrameIteratorMaybeGC<allowGC>::Inl
 
 inline BaselineFrame *
 JitFrameIterator::baselineFrame() const
 {
     JS_ASSERT(isBaselineJS());
     return (BaselineFrame *)(fp() - BaselineFrame::FramePointerOffset - BaselineFrame::Size());
 }
 
+template <typename T>
+bool
+JitFrameIterator::isExitFrameLayout() const
+{
+    if (type_ != JitFrame_Exit || isFakeExitFrame())
+        return false;
+    return exitFrame()->is<T>();
+}
+
 } // namespace jit
 } // namespace js
 
 #endif // JS_ION
 
 #endif /* jit_JitFrameIterator_inl_h */
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -150,21 +150,19 @@ class JitFrameIterator
         return type_ == JitFrame_BaselineJS;
     }
     bool isIonJS() const {
         return type_ == JitFrame_IonJS;
     }
     bool isBaselineStub() const {
         return type_ == JitFrame_BaselineStub;
     }
-    bool isNative() const;
-    bool isOOLNative() const;
-    bool isOOLPropertyOp() const;
-    bool isOOLProxy() const;
-    bool isDOMExit() const;
+    bool isBareExit() const;
+    template <typename T> bool isExitFrameLayout() const;
+
     bool isEntry() const {
         return type_ == JitFrame_Entry;
     }
     bool isFunctionFrame() const;
 
     bool isConstructing() const;
 
     void *calleeToken() const;
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -257,17 +257,18 @@ JitRuntime::generateEnterJIT(JSContext *
         masm.ma_lsl(Imm32(3), numStackValues, scratch);
         masm.ma_sub(sp, scratch, sp);
 
         // Enter exit frame.
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch);
         masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
         masm.push(scratch);
         masm.push(Imm32(0)); // Fake return address.
-        masm.enterFakeExitFrame();
+        // No GC things to mark on the stack, push a bare token.
+        masm.enterFakeExitFrame(IonExitFrameLayout::BareToken());
 
         masm.push(framePtr); // BaselineFrame
         masm.push(r0); // jitcode
 
         masm.setupUnalignedABICall(3, scratch);
         masm.passABIArg(r11); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);
--- a/js/src/jit/mips/Trampoline-mips.cpp
+++ b/js/src/jit/mips/Trampoline-mips.cpp
@@ -233,17 +233,18 @@ JitRuntime::generateEnterJIT(JSContext *
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch);
         masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
 
         // 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
 
-        masm.enterFakeExitFrame();
+        // No GC things to mark, push a bare token.
+        masm.enterFakeExitFrame(IonExitFrameLayout::BareToken());
 
         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(3, scratch);
         masm.passABIArg(BaselineFrameReg); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -202,17 +202,18 @@ JitRuntime::generateEnterJIT(JSContext *
         masm.shll(Imm32(3), valuesSize);
         masm.subPtr(valuesSize, rsp);
 
         // Enter exit frame.
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), valuesSize);
         masm.makeFrameDescriptor(valuesSize, JitFrame_BaselineJS);
         masm.push(valuesSize);
         masm.push(Imm32(0)); // Fake return address.
-        masm.enterFakeExitFrame();
+        // No GC things to mark, push a bare token.
+        masm.enterFakeExitFrame(IonExitFrameLayout::BareToken());
 
         regs.add(valuesSize);
 
         masm.push(framePtr);
         masm.push(reg_code);
 
         masm.setupUnalignedABICall(3, scratch);
         masm.passABIArg(framePtr); // BaselineFrame
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -196,19 +196,20 @@ JitRuntime::generateEnterJIT(JSContext *
         // Reserve space for locals and stack values.
         masm.mov(numStackValues, scratch);
         masm.shll(Imm32(3), scratch);
         masm.subPtr(scratch, esp);
 
         // Enter exit frame.
         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch);
         masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
-        masm.push(scratch);
-        masm.push(Imm32(0)); // Fake return address.
-        masm.enterFakeExitFrame();
+        masm.push(scratch); // Fake return address.
+        masm.push(Imm32(0));
+        // No GC things to mark on the stack, push a bare token.
+        masm.enterFakeExitFrame(IonExitFrameLayout::BareToken());
 
         masm.push(framePtr);
         masm.push(jitcode);
 
         masm.setupUnalignedABICall(3, scratch);
         masm.passABIArg(framePtr); // BaselineFrame
         masm.passABIArg(OsrFrameReg); // InterpreterFrame
         masm.passABIArg(numStackValues);