Bug 1070962 part 4 - JitFrameIterator use BailoutData when it starts on a bailout frame. r=jandem
--- 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++();