Bug 1070962 part 4 - JitFrameIterator use BailoutData when it starts on a bailout frame. r=jandem
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Mon, 13 Oct 2014 17:34:00 +0200
changeset 210193 67f89c977264c9337100ae13ac2c096f084030ab
parent 210192 553feb23aa52b8e3560657d2bd0968ecd58a838f
child 210194 3fe372c4fe4e1f89e234bd2f19eb198d7baa9c3a
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjandem
bugs1070962
milestone35.0a1
Bug 1070962 part 4 - JitFrameIterator use BailoutData when it starts on a bailout frame. r=jandem
js/src/jit/Ion.cpp
js/src/jit/IonFrames.cpp
js/src/jit/JitFrameIterator.h
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -2697,19 +2697,26 @@ InvalidateActivation(FreeOp *fop, const 
 
 #ifdef DEBUG
         switch (it.type()) {
           case JitFrame_Exit:
             JitSpew(JitSpew_IonInvalidate, "#%d exit frame @ %p", frameno, it.fp());
             break;
           case JitFrame_BaselineJS:
           case JitFrame_IonJS:
+          case JitFrame_Bailout:
           {
             MOZ_ASSERT(it.isScripted());
-            const char *type = it.isIonJS() ? "Optimized" : "Baseline";
+            const char *type = "Unknown";
+            if (it.isIonJS())
+                type = "Optimized";
+            else if (it.isBaselineJS())
+                type = "Baseline";
+            else if (it.isBailoutJS())
+                type = "Bailing";
             JitSpew(JitSpew_IonInvalidate, "#%d %s JS frame @ %p, %s:%d (fun: %p, script: %p, pc %p)",
                     frameno, type, it.fp(), it.script()->filename(), it.script()->lineno(),
                     it.maybeCallee(), (JSScript *)it.script(), it.returnAddressToFp());
             break;
           }
           case JitFrame_BaselineStub:
             JitSpew(JitSpew_IonInvalidate, "#%d baseline stub frame @ %p", frameno, it.fp());
             break;
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -97,29 +97,37 @@ JitFrameIterator::JitFrameIterator(Threa
     type_(JitFrame_Exit),
     returnAddressToFp_(nullptr),
     frameSize_(0),
     mode_(cx->isForkJoinContext() ? ParallelExecution : SequentialExecution),
     kind_(Kind_FrameIterator),
     cachedSafepointIndex_(nullptr),
     activation_(cx->perThreadData->activation()->asJit())
 {
+    if (activation_->bailoutData()) {
+        current_ = activation_->bailoutData()->fp();
+        type_ = JitFrame_Bailout;
+    }
 }
 
 JitFrameIterator::JitFrameIterator(const ActivationIterator &activations)
   : current_(activations.jitTop()),
     type_(JitFrame_Exit),
     returnAddressToFp_(nullptr),
     frameSize_(0),
     mode_(activations->asJit()->cx()->isForkJoinContext() ? ParallelExecution
                                                           : SequentialExecution),
     kind_(Kind_FrameIterator),
     cachedSafepointIndex_(nullptr),
     activation_(activations->asJit())
 {
+    if (activation_->bailoutData()) {
+        current_ = activation_->bailoutData()->fp();
+        type_ = JitFrame_Bailout;
+    }
 }
 
 IonBailoutIterator *
 JitFrameIterator::asBailoutIterator()
 {
     MOZ_ASSERT(isBailoutIterator());
     return static_cast<IonBailoutIterator *>(this);
 }
@@ -270,16 +278,17 @@ JitFrameIterator::actualArgs() const
 static inline size_t
 SizeOfFramePrefix(FrameType type)
 {
     switch (type) {
       case JitFrame_Entry:
         return IonEntryFrameLayout::Size();
       case JitFrame_BaselineJS:
       case JitFrame_IonJS:
+      case JitFrame_Bailout:
       case JitFrame_Unwound_IonJS:
         return IonJSFrameLayout::Size();
       case JitFrame_BaselineStub:
         return IonBaselineStubFrameLayout::Size();
       case JitFrame_Rectifier:
         return IonRectifierFrameLayout::Size();
       case JitFrame_Unwound_Rectifier:
         return IonUnwoundRectifierFrameLayout::Size();
@@ -334,26 +343,34 @@ JitFrameIterator::operator++()
 
 
     return *this;
 }
 
 uintptr_t *
 JitFrameIterator::spillBase() const
 {
+    MOZ_ASSERT(isIonJS());
+
     // Get the base address to where safepoint registers are spilled.
     // Out-of-line calls do not unwind the extra padding space used to
     // aggregate bailout tables, so we use frameSize instead of frameLocals,
     // which would only account for local stack slots.
     return reinterpret_cast<uintptr_t *>(fp() - ionScript()->frameSize());
 }
 
 MachineState
 JitFrameIterator::machineState() const
 {
+    MOZ_ASSERT(isIonScripted());
+
+    // The MachineState is used by GCs for marking call-sites.
+    if (MOZ_UNLIKELY(isBailoutJS()))
+        return activation_->bailoutData()->machineState();
+
     SafepointReader reader(ionScript(), safepoint());
     uintptr_t *spill = spillBase();
 
     MachineState machine;
     for (GeneralRegisterBackwardIterator iter(reader.allGprSpills()); iter.more(); iter++)
         machine.setRegisterLocation(*iter, --spill);
 
     uint8_t *spillAlign = alignDoubleSpillWithOffset(reinterpret_cast<uint8_t *>(spill), 0);
@@ -1527,17 +1544,17 @@ SnapshotIterator::SnapshotIterator(IonSc
     ionScript_(ionScript),
     instructionResults_(nullptr)
 {
     MOZ_ASSERT(snapshotOffset < ionScript->snapshotsListSize());
 }
 
 SnapshotIterator::SnapshotIterator(const JitFrameIterator &iter)
   : snapshot_(iter.ionScript()->snapshots(),
-              iter.osiIndex()->snapshotOffset(),
+              iter.snapshotOffset(),
               iter.ionScript()->snapshotsRVATableSize(),
               iter.ionScript()->snapshotsListSize()),
     recover_(snapshot_,
              iter.ionScript()->recovers(),
              iter.ionScript()->recoversSize()),
     fp_(iter.jsFrame()),
     machine_(iter.machineState()),
     ionScript_(iter.ionScript()),
@@ -1911,54 +1928,77 @@ SnapshotIterator::maybeReadAllocByIndex(
     }
 
     while (moreAllocations())
         skip();
 
     return s;
 }
 
+IonJSFrameLayout *
+JitFrameIterator::jsFrame() const
+{
+    MOZ_ASSERT(isScripted());
+    if (isBailoutJS())
+        return activation_->bailoutData()->jsFrame();
+
+    return (IonJSFrameLayout *) fp();
+}
+
 IonScript *
 JitFrameIterator::ionScript() const
 {
-    MOZ_ASSERT(type() == JitFrame_IonJS);
+    MOZ_ASSERT(isIonScripted());
+    if (isBailoutJS())
+        return activation_->bailoutData()->ionScript();
 
     IonScript *ionScript = nullptr;
     if (checkInvalidation(&ionScript))
         return ionScript;
     return ionScriptFromCalleeToken();
 }
 
 IonScript *
 JitFrameIterator::ionScriptFromCalleeToken() const
 {
-    MOZ_ASSERT(type() == JitFrame_IonJS);
+    MOZ_ASSERT(isIonJS());
     MOZ_ASSERT(!checkInvalidation());
 
     switch (mode_) {
       case SequentialExecution:
         return script()->ionScript();
       case ParallelExecution:
         return script()->parallelIonScript();
       default:
         MOZ_CRASH("No such execution mode");
     }
 }
 
 const SafepointIndex *
 JitFrameIterator::safepoint() const
 {
+    MOZ_ASSERT(isIonJS());
     if (!cachedSafepointIndex_)
         cachedSafepointIndex_ = ionScript()->getSafepointIndex(returnAddressToFp());
     return cachedSafepointIndex_;
 }
 
+SnapshotOffset
+JitFrameIterator::snapshotOffset() const
+{
+    MOZ_ASSERT(isIonScripted());
+    if (isBailoutJS())
+        return activation_->bailoutData()->snapshotOffset();
+    return osiIndex()->snapshotOffset();
+}
+
 const OsiIndex *
 JitFrameIterator::osiIndex() const
 {
+    MOZ_ASSERT(isIonJS());
     SafepointReader reader(ionScript(), safepoint());
     return ionScript()->getOsiIndex(reader.osiReturnPointOffset());
 }
 
 InlineFrameIterator::InlineFrameIterator(ThreadSafeContext *cx, const JitFrameIterator *iter)
   : callee_(cx),
     script_(cx)
 {
@@ -2330,16 +2370,17 @@ JitFrameIterator::dump() const
       case JitFrame_BaselineJS:
         dumpBaseline();
         break;
       case JitFrame_BaselineStub:
       case JitFrame_Unwound_BaselineStub:
         fprintf(stderr, " Baseline stub frame\n");
         fprintf(stderr, "  Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
         break;
+      case JitFrame_Bailout:
       case JitFrame_IonJS:
       {
         InlineFrameIterator frames(GetJSContextFromJitCode(), this);
         for (;;) {
             frames.dump();
             if (!frames.more())
                 break;
             ++frames;
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -52,17 +52,23 @@ enum FrameType
 
     // An unwound rectifier frame is a rectifier frame signalling that its callee
     // frame has been turned into an exit frame (see EnsureExitFrame).
     JitFrame_Unwound_Rectifier,
 
     // An exit frame is necessary for transitioning from a JS frame into C++.
     // From within C++, an exit frame is always the last frame in any
     // JitActivation.
-    JitFrame_Exit
+    JitFrame_Exit,
+
+    // A bailout frame is a special IonJS jit frame after a bailout, and before
+    // the reconstruction of the BaselineJS frame. From within C++, a bailout
+    // frame is always the last frame in a JitActivation iff the bailout frame
+    // information is recorded on the JitActivation.
+    JitFrame_Bailout
 };
 
 enum ReadFrameArgsBehavior {
     // Only read formals (i.e. [0 ... callee()->nargs]
     ReadFrame_Formals,
 
     // Only read overflown args (i.e. [callee()->nargs ... numActuals()]
     ReadFrame_Overflown,
@@ -122,40 +128,45 @@ class JitFrameIterator
     }
 
     IonCommonFrameLayout *current() const {
         return (IonCommonFrameLayout *)current_;
     }
 
     inline uint8_t *returnAddress() const;
 
-    IonJSFrameLayout *jsFrame() const {
-        MOZ_ASSERT(isScripted());
-        return (IonJSFrameLayout *) fp();
-    }
+    // Return the pointer of the JitFrame, the iterator is assumed to be settled
+    // on a scripted frame.
+    IonJSFrameLayout *jsFrame() const;
 
     // Returns true iff this exit frame was created using EnsureExitFrame.
     inline bool isFakeExitFrame() const;
 
     inline IonExitFrameLayout *exitFrame() const;
 
     // Returns whether the JS frame has been invalidated and, if so,
     // places the invalidated Ion script in |ionScript|.
     bool checkInvalidation(IonScript **ionScript) const;
     bool checkInvalidation() const;
 
     bool isScripted() const {
-        return type_ == JitFrame_BaselineJS || type_ == JitFrame_IonJS;
+        return type_ == JitFrame_BaselineJS || type_ == JitFrame_IonJS || type_ == JitFrame_Bailout;
     }
     bool isBaselineJS() const {
         return type_ == JitFrame_BaselineJS;
     }
+    bool isIonScripted() const {
+        return type_ == JitFrame_IonJS || type_ == JitFrame_Bailout;
+    }
     bool isIonJS() const {
         return type_ == JitFrame_IonJS;
     }
+    bool isBailoutJS() const {
+        return type_ == JitFrame_Bailout;
+    }
     bool isBaselineStub() const {
         return type_ == JitFrame_BaselineStub;
     }
     bool isBareExit() const;
     template <typename T> bool isExitFrameLayout() const;
 
     bool isEntry() const {
         return type_ == JitFrame_Entry;
@@ -211,16 +222,20 @@ class JitFrameIterator
     // Returns the Safepoint associated with this JS frame. Incurs a lookup
     // overhead.
     const SafepointIndex *safepoint() const;
 
     // Returns the OSI index associated with this JS frame. Incurs a lookup
     // overhead.
     const OsiIndex *osiIndex() const;
 
+    // Returns the Snapshot offset associated with this JS frame. Incurs a
+    // lookup overhead.
+    SnapshotOffset snapshotOffset() const;
+
     uintptr_t *spillBase() const;
     MachineState machineState() const;
 
     template <class Op>
     void unaliasedForEachActual(Op op, ReadFrameArgsBehavior behavior) const {
         MOZ_ASSERT(isBaselineJS());
 
         unsigned nactual = numActualArgs();
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -346,16 +346,25 @@ FrameIter::unaliasedForEachActual(JSCont
         break;
       case INTERP:
         interpFrame()->unaliasedForEachActual(op);
         return;
       case JIT:
         if (data_.jitFrames_.isIonJS()) {
             jit::MaybeReadFallback recover(cx, activation()->asJit(), &data_.jitFrames_);
             ionInlineFrames_.unaliasedForEachActual(cx, op, jit::ReadFrame_Actuals, recover);
+        } else if (data_.jitFrames_.isBailoutJS()) {
+            // :TODO: (Bug 1070962) If we are introspecting the frame which is
+            // being bailed, then we might be in the middle of recovering
+            // instructions. Stacking computeInstructionResults implies that we
+            // might be recovering result twice. In the mean time, to avoid
+            // that, we just return Undefined values for instruction results
+            // which are not yet recovered.
+            jit::MaybeReadFallback fallback;
+            ionInlineFrames_.unaliasedForEachActual(cx, op, jit::ReadFrame_Actuals, fallback);
         } else {
             MOZ_ASSERT(data_.jitFrames_.isBaselineJS());
             data_.jitFrames_.unaliasedForEachActual(op, jit::ReadFrame_Actuals);
         }
         return;
     }
     MOZ_CRASH("Unexpected state");
 }
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -674,50 +674,50 @@ FrameIter::FrameIter(JSContext *cx, Cont
     ionInlineFrames_(cx, (js::jit::JitFrameIterator*) nullptr)
 {
     settleOnActivation();
 }
 
 FrameIter::FrameIter(const FrameIter &other)
   : data_(other.data_),
     ionInlineFrames_(other.data_.cx_,
-                     data_.jitFrames_.isIonJS() ? &other.ionInlineFrames_ : nullptr)
+                     data_.jitFrames_.isIonScripted() ? &other.ionInlineFrames_ : nullptr)
 {
 }
 
 FrameIter::FrameIter(const Data &data)
   : data_(data),
-    ionInlineFrames_(data.cx_, data_.jitFrames_.isIonJS() ? &data_.jitFrames_ : nullptr)
+    ionInlineFrames_(data.cx_, data_.jitFrames_.isIonScripted() ? &data_.jitFrames_ : nullptr)
 {
     MOZ_ASSERT(data.cx_);
 
-    if (data_.jitFrames_.isIonJS()) {
+    if (data_.jitFrames_.isIonScripted()) {
         while (ionInlineFrames_.frameNo() != data.ionInlineFrameNo_)
             ++ionInlineFrames_;
     }
 }
 
 void
 FrameIter::nextJitFrame()
 {
-    if (data_.jitFrames_.isIonJS()) {
+    if (data_.jitFrames_.isIonScripted()) {
         ionInlineFrames_.resetOn(&data_.jitFrames_);
         data_.pc_ = ionInlineFrames_.pc();
     } else {
         MOZ_ASSERT(data_.jitFrames_.isBaselineJS());
         data_.jitFrames_.baselineScriptAndPc(nullptr, &data_.pc_);
     }
 }
 
 void
 FrameIter::popJitFrame()
 {
     MOZ_ASSERT(data_.state_ == JIT);
 
-    if (data_.jitFrames_.isIonJS() && ionInlineFrames_.more()) {
+    if (data_.jitFrames_.isIonScripted() && ionInlineFrames_.more()) {
         ++ionInlineFrames_;
         data_.pc_ = ionInlineFrames_.pc();
         return;
     }
 
     ++data_.jitFrames_;
     while (!data_.jitFrames_.done() && !data_.jitFrames_.isScripted())
         ++data_.jitFrames_;
@@ -784,17 +784,17 @@ FrameIter::operator++()
     return *this;
 }
 
 FrameIter::Data *
 FrameIter::copyData() const
 {
     Data *data = data_.cx_->new_<Data>(data_);
     MOZ_ASSERT(data_.state_ != ASMJS);
-    if (data && data_.jitFrames_.isIonJS())
+    if (data && data_.jitFrames_.isIonScripted())
         data->ionInlineFrameNo_ = ionInlineFrames_.frameNo();
     return data;
 }
 
 AbstractFramePtr
 FrameIter::copyDataAsAbstractFramePtr() const
 {
     AbstractFramePtr frame;
@@ -992,17 +992,17 @@ FrameIter::mutedErrors() const
 bool
 FrameIter::isConstructing() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
-        if (data_.jitFrames_.isIonJS())
+        if (data_.jitFrames_.isIonScripted())
             return ionInlineFrames_.isConstructing();
         MOZ_ASSERT(data_.jitFrames_.isBaselineJS());
         return data_.jitFrames_.isConstructing();
       case INTERP:
         return interpFrame()->isConstructing();
     }
 
     MOZ_CRASH("Unexpected state");
@@ -1021,17 +1021,17 @@ FrameIter::hasUsableAbstractFramePtr() c
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         return false;
       case JIT:
         if (data_.jitFrames_.isBaselineJS())
             return true;
 
-        MOZ_ASSERT(data_.jitFrames_.isIonJS());
+        MOZ_ASSERT(data_.jitFrames_.isIonScripted());
         return !!activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
                                                                   ionInlineFrames_.frameNo());
         break;
       case INTERP:
         return true;
     }
     MOZ_CRASH("Unexpected state");
 }
@@ -1043,17 +1043,17 @@ FrameIter::abstractFramePtr() const
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT: {
         if (data_.jitFrames_.isBaselineJS())
             return data_.jitFrames_.baselineFrame();
 
-        MOZ_ASSERT(data_.jitFrames_.isIonJS());
+        MOZ_ASSERT(data_.jitFrames_.isIonScripted());
         return activation()->asJit()->lookupRematerializedFrame(data_.jitFrames_.fp(),
                                                                 ionInlineFrames_.frameNo());
         break;
       }
       case INTERP:
         MOZ_ASSERT(interpFrame());
         return AbstractFramePtr(interpFrame());
     }
@@ -1115,17 +1115,17 @@ FrameIter::callee() const
       case ASMJS:
         break;
       case INTERP:
         MOZ_ASSERT(isFunctionFrame());
         return &interpFrame()->callee();
       case JIT:
         if (data_.jitFrames_.isBaselineJS())
             return data_.jitFrames_.callee();
-        MOZ_ASSERT(data_.jitFrames_.isIonJS());
+        MOZ_ASSERT(data_.jitFrames_.isIonScripted());
         return ionInlineFrames_.callee();
     }
     MOZ_CRASH("Unexpected state");
 }
 
 Value
 FrameIter::calleev() const
 {
@@ -1148,17 +1148,17 @@ FrameIter::numActualArgs() const
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case INTERP:
         MOZ_ASSERT(isFunctionFrame());
         return interpFrame()->numActualArgs();
       case JIT:
-        if (data_.jitFrames_.isIonJS())
+        if (data_.jitFrames_.isIonScripted())
             return ionInlineFrames_.numActualArgs();
 
         MOZ_ASSERT(data_.jitFrames_.isBaselineJS());
         return data_.jitFrames_.numActualArgs();
     }
     MOZ_CRASH("Unexpected state");
 }
 
@@ -1177,17 +1177,17 @@ FrameIter::unaliasedActual(unsigned i, M
 JSObject *
 FrameIter::scopeChain() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
-        if (data_.jitFrames_.isIonJS())
+        if (data_.jitFrames_.isIonScripted())
             return ionInlineFrames_.scopeChain();
         return data_.jitFrames_.baselineFrame()->scopeChain();
       case INTERP:
         return interpFrame()->scopeChain();
     }
     MOZ_CRASH("Unexpected state");
 }
 
@@ -1232,17 +1232,17 @@ FrameIter::computedThisValue() const
 Value
 FrameIter::thisv(JSContext *cx)
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
-        if (data_.jitFrames_.isIonJS()) {
+        if (data_.jitFrames_.isIonScripted()) {
             jit::MaybeReadFallback recover(cx, activation()->asJit(), &data_.jitFrames_);
             return ionInlineFrames_.thisValue(recover);
         }
         return data_.jitFrames_.baselineFrame()->thisValue();
       case INTERP:
         return interpFrame()->thisValue();
     }
     MOZ_CRASH("Unexpected state");
@@ -1288,17 +1288,17 @@ FrameIter::setReturnValue(const Value &v
 size_t
 FrameIter::numFrameSlots() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT: {
-        if (data_.jitFrames_.isIonJS()) {
+        if (data_.jitFrames_.isIonScripted()) {
             return ionInlineFrames_.snapshotIterator().numAllocations() -
                 ionInlineFrames_.script()->nfixed();
         }
         jit::BaselineFrame *frame = data_.jitFrames_.baselineFrame();
         return frame->numValueSlots() - data_.jitFrames_.script()->nfixed();
       }
       case INTERP:
         MOZ_ASSERT(data_.interpFrames_.sp() >= interpFrame()->base());
@@ -1310,17 +1310,17 @@ FrameIter::numFrameSlots() const
 Value
 FrameIter::frameSlotValue(size_t index) const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT:
-        if (data_.jitFrames_.isIonJS()) {
+        if (data_.jitFrames_.isIonScripted()) {
             jit::SnapshotIterator si(ionInlineFrames_.snapshotIterator());
             index += ionInlineFrames_.script()->nfixed();
             return si.maybeReadAllocByIndex(index);
         }
 
         index += data_.jitFrames_.script()->nfixed();
         return *data_.jitFrames_.baselineFrame()->valueSlot(index);
       case INTERP:
@@ -1509,17 +1509,17 @@ jit::JitActivation::clearRematerializedF
 
 template <class T>
 jit::RematerializedFrame *
 jit::JitActivation::getRematerializedFrame(ThreadSafeContext *cx, const T &iter, size_t inlineDepth)
 {
     // Only allow rematerializing from the same thread.
     MOZ_ASSERT(cx->perThreadData == cx_->perThreadData);
     MOZ_ASSERT(iter.activation() == this);
-    MOZ_ASSERT(iter.isIonJS());
+    MOZ_ASSERT(iter.isIonScripted());
 
     if (!rematerializedFrames_) {
         rematerializedFrames_ = cx->new_<RematerializedFrameTable>(cx);
         if (!rematerializedFrames_ || !rematerializedFrames_->init()) {
             rematerializedFrames_ = nullptr;
             return nullptr;
         }
     }
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1424,16 +1424,19 @@ class JitActivation : public Activation
     class RegisterBailoutIterator
     {
         JitActivation &activation_;
 
       public:
         RegisterBailoutIterator(JitActivation &activation, IonBailoutIterator *iter);
         ~RegisterBailoutIterator();
     };
+
+    // Return the bailout information if it is registered.
+    const IonBailoutIterator *bailoutData() const { return ionBailoutIterator_; }
 };
 
 // A filtering of the ActivationIterator to only stop at JitActivations.
 class JitActivationIterator : public ActivationIterator
 {
     void settle() {
         while (!done() && !activation_->isJit())
             ActivationIterator::operator++();