diff --git a/js/src/jit/Ion.cpp b/js/src/jit/Ion.cpp index 55cb42f07c6..10f82ef79e0 100644 --- a/js/src/jit/Ion.cpp +++ b/js/src/jit/Ion.cpp @@ -2702,9 +2702,16 @@ InvalidateActivation(FreeOp *fop, const JitActivationIterator &activations, bool 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()); diff --git a/js/src/jit/IonFrames.cpp b/js/src/jit/IonFrames.cpp index cbbde9f857a..5393c6f580a 100644 --- a/js/src/jit/IonFrames.cpp +++ b/js/src/jit/IonFrames.cpp @@ -102,6 +102,10 @@ JitFrameIterator::JitFrameIterator(ThreadSafeContext *cx) cachedSafepointIndex_(nullptr), activation_(cx->perThreadData->activation()->asJit()) { + if (activation_->bailoutData()) { + current_ = activation_->bailoutData()->fp(); + type_ = JitFrame_Bailout; + } } JitFrameIterator::JitFrameIterator(const ActivationIterator &activations) @@ -115,6 +119,10 @@ JitFrameIterator::JitFrameIterator(const ActivationIterator &activations) cachedSafepointIndex_(nullptr), activation_(activations->asJit()) { + if (activation_->bailoutData()) { + current_ = activation_->bailoutData()->fp(); + type_ = JitFrame_Bailout; + } } IonBailoutIterator * @@ -275,6 +283,7 @@ SizeOfFramePrefix(FrameType type) return IonEntryFrameLayout::Size(); case JitFrame_BaselineJS: case JitFrame_IonJS: + case JitFrame_Bailout: case JitFrame_Unwound_IonJS: return IonJSFrameLayout::Size(); case JitFrame_BaselineStub: @@ -339,6 +348,8 @@ JitFrameIterator::operator++() 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, @@ -349,6 +360,12 @@ JitFrameIterator::spillBase() const 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(); @@ -1532,7 +1549,7 @@ SnapshotIterator::SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshot SnapshotIterator::SnapshotIterator(const JitFrameIterator &iter) : snapshot_(iter.ionScript()->snapshots(), - iter.osiIndex()->snapshotOffset(), + iter.snapshotOffset(), iter.ionScript()->snapshotsRVATableSize(), iter.ionScript()->snapshotsListSize()), recover_(snapshot_, @@ -1916,10 +1933,22 @@ SnapshotIterator::maybeReadAllocByIndex(size_t index) 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)) @@ -1930,7 +1959,7 @@ JitFrameIterator::ionScript() const IonScript * JitFrameIterator::ionScriptFromCalleeToken() const { - MOZ_ASSERT(type() == JitFrame_IonJS); + MOZ_ASSERT(isIonJS()); MOZ_ASSERT(!checkInvalidation()); switch (mode_) { @@ -1946,14 +1975,25 @@ JitFrameIterator::ionScriptFromCalleeToken() const 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()); } @@ -2335,6 +2375,7 @@ JitFrameIterator::dump() const 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); diff --git a/js/src/jit/JitFrameIterator.h b/js/src/jit/JitFrameIterator.h index a15384d0495..74a5f81e96b 100644 --- a/js/src/jit/JitFrameIterator.h +++ b/js/src/jit/JitFrameIterator.h @@ -57,7 +57,13 @@ enum FrameType // 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 { @@ -127,10 +133,9 @@ class JitFrameIterator 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; @@ -143,14 +148,20 @@ class JitFrameIterator 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; } @@ -216,6 +227,10 @@ class JitFrameIterator // 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; diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 662b20f7b1e..d13b46ac771 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -351,6 +351,15 @@ FrameIter::unaliasedForEachActual(JSContext *cx, Op op) 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); diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 52b7f273213..dc1e5a92111 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -679,17 +679,17 @@ FrameIter::FrameIter(JSContext *cx, ContextOption contextOption, 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_; } @@ -698,7 +698,7 @@ FrameIter::FrameIter(const Data &data) void FrameIter::nextJitFrame() { - if (data_.jitFrames_.isIonJS()) { + if (data_.jitFrames_.isIonScripted()) { ionInlineFrames_.resetOn(&data_.jitFrames_); data_.pc_ = ionInlineFrames_.pc(); } else { @@ -712,7 +712,7 @@ 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; @@ -789,7 +789,7 @@ FrameIter::copyData() const { Data *data = data_.cx_->new_(data_); MOZ_ASSERT(data_.state_ != ASMJS); - if (data && data_.jitFrames_.isIonJS()) + if (data && data_.jitFrames_.isIonScripted()) data->ionInlineFrameNo_ = ionInlineFrames_.frameNo(); return data; } @@ -997,7 +997,7 @@ FrameIter::isConstructing() const 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(); @@ -1026,7 +1026,7 @@ FrameIter::hasUsableAbstractFramePtr() const 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; @@ -1048,7 +1048,7 @@ FrameIter::abstractFramePtr() const 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; @@ -1120,7 +1120,7 @@ FrameIter::callee() const 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"); @@ -1153,7 +1153,7 @@ FrameIter::numActualArgs() const MOZ_ASSERT(isFunctionFrame()); return interpFrame()->numActualArgs(); case JIT: - if (data_.jitFrames_.isIonJS()) + if (data_.jitFrames_.isIonScripted()) return ionInlineFrames_.numActualArgs(); MOZ_ASSERT(data_.jitFrames_.isBaselineJS()); @@ -1182,7 +1182,7 @@ FrameIter::scopeChain() const case ASMJS: break; case JIT: - if (data_.jitFrames_.isIonJS()) + if (data_.jitFrames_.isIonScripted()) return ionInlineFrames_.scopeChain(); return data_.jitFrames_.baselineFrame()->scopeChain(); case INTERP: @@ -1237,7 +1237,7 @@ FrameIter::thisv(JSContext *cx) 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); } @@ -1293,7 +1293,7 @@ FrameIter::numFrameSlots() const case ASMJS: break; case JIT: { - if (data_.jitFrames_.isIonJS()) { + if (data_.jitFrames_.isIonScripted()) { return ionInlineFrames_.snapshotIterator().numAllocations() - ionInlineFrames_.script()->nfixed(); } @@ -1315,7 +1315,7 @@ FrameIter::frameSlotValue(size_t index) const 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); @@ -1514,7 +1514,7 @@ jit::JitActivation::getRematerializedFrame(ThreadSafeContext *cx, const T &iter, // 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_(cx); diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 2deb4ca6aa9..25d2325a80b 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -1429,6 +1429,9 @@ class JitActivation : public Activation 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.