Bug 1064835 - Fix SPS crash. r=jandem
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -44,17 +44,19 @@ jit::Bailout(BailoutStack *sp, BaselineB
TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
TraceLogTimestamp(logger, TraceLogger::Bailout);
JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset());
MOZ_ASSERT(IsBaselineEnabled(cx));
*bailoutInfo = nullptr;
- uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo);
+ bool poppedLastSPSFrame = false;
+ uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, false, bailoutInfo,
+ /* excInfo = */ nullptr, &poppedLastSPSFrame);
MOZ_ASSERT(retval == BAILOUT_RETURN_OK ||
retval == BAILOUT_RETURN_FATAL_ERROR ||
retval == BAILOUT_RETURN_OVERRECURSED);
MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);
if (retval != BAILOUT_RETURN_OK) {
// If the bailout failed, then bailout trampoline will pop the
// current frame and jump straight to exception handling code when
@@ -63,17 +65,18 @@ jit::Bailout(BailoutStack *sp, BaselineB
//
// We call ExitScript here to ensure that if the ionScript had SPS
// instrumentation, then the SPS entry for it is popped.
//
// However, if the bailout was during argument check, then a
// pseudostack frame would not have been pushed in the first
// place, so don't pop anything in that case.
bool popSPSFrame = iter.ionScript()->hasSPSInstrumentation() &&
- (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck);
+ (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck) &&
+ !poppedLastSPSFrame;
JSScript *script = iter.script();
probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame);
EnsureExitFrame(iter.jsFrame());
}
return retval;
}
@@ -100,17 +103,19 @@ jit::InvalidationBailout(InvalidationBai
JitSpew(JitSpew_IonBailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset());
// Note: the frame size must be computed before we return from this function.
*frameSizeOut = iter.frameSize();
MOZ_ASSERT(IsBaselineEnabled(cx));
*bailoutInfo = nullptr;
- uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, bailoutInfo);
+ bool poppedLastSPSFrame = false;
+ uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, bailoutInfo,
+ /* excInfo = */ nullptr, &poppedLastSPSFrame);
MOZ_ASSERT(retval == BAILOUT_RETURN_OK ||
retval == BAILOUT_RETURN_FATAL_ERROR ||
retval == BAILOUT_RETURN_OVERRECURSED);
MOZ_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr);
if (retval != BAILOUT_RETURN_OK) {
// If the bailout failed, then bailout trampoline will pop the
// current frame and jump straight to exception handling code when
@@ -119,17 +124,18 @@ jit::InvalidationBailout(InvalidationBai
//
// We call ExitScript here to ensure that if the ionScript had SPS
// instrumentation, then the SPS entry for it is popped.
//
// However, if the bailout was during argument check, then a
// pseudostack frame would not have been pushed in the first
// place, so don't pop anything in that case.
bool popSPSFrame = iter.ionScript()->hasSPSInstrumentation() &&
- (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck);
+ (SnapshotIterator(iter).bailoutKind() != Bailout_ArgumentCheck) &&
+ !poppedLastSPSFrame;
JSScript *script = iter.script();
probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame);
IonJSFrameLayout *frame = iter.jsFrame();
JitSpew(JitSpew_IonInvalidate, "Bailout failed (%s): converting to exit frame",
(retval == BAILOUT_RETURN_FATAL_ERROR) ? "Fatal Error" : "Over Recursion");
JitSpew(JitSpew_IonInvalidate, " orig calleeToken %p", (void *) frame->calleeToken());
JitSpew(JitSpew_IonInvalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize()));
@@ -175,17 +181,19 @@ jit::ExceptionHandlerBailout(JSContext *
cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT;
gc::AutoSuppressGC suppress(cx);
JitActivationIterator jitActivations(cx->runtime());
BailoutFrameInfo bailoutData(jitActivations, frame.frame());
JitFrameIterator iter(jitActivations);
BaselineBailoutInfo *bailoutInfo = nullptr;
- uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true, &bailoutInfo, &excInfo);
+ bool poppedLastSPSFrame = false;
+ uint32_t retval = BailoutIonToBaseline(cx, bailoutData.activation(), iter, true,
+ &bailoutInfo, &excInfo, &poppedLastSPSFrame);
if (retval == BAILOUT_RETURN_OK) {
MOZ_ASSERT(bailoutInfo);
// Overwrite the kind so HandleException after the bailout returns
// false, jumping directly to the exception tail.
if (excInfo.propagatingIonExceptionForDebugMode())
bailoutInfo->bailoutKind = Bailout_IonExceptionDebugMode;
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -510,19 +510,22 @@ GetNextNonLoopEntryPc(jsbytecode *pc)
// | ReturnAddr | <-- return into ArgumentsRectifier after call
// +===============+
//
static bool
InitFromBailout(JSContext *cx, HandleScript caller, jsbytecode *callerPC,
HandleFunction fun, HandleScript script, IonScript *ionScript,
SnapshotIterator &iter, bool invalidate, BaselineStackBuilder &builder,
AutoValueVector &startFrameFormals, MutableHandleFunction nextCallee,
- jsbytecode **callPC, const ExceptionBailoutInfo *excInfo)
+ jsbytecode **callPC, const ExceptionBailoutInfo *excInfo,
+ bool *poppedLastSPSFrameOut)
{
MOZ_ASSERT(script->hasBaselineScript());
+ MOZ_ASSERT(poppedLastSPSFrameOut);
+ MOZ_ASSERT(!*poppedLastSPSFrameOut);
// Are we catching an exception?
bool catchingException = excInfo && excInfo->catchingException();
// If we are catching an exception, we are bailing out to a catch or
// finally block and this is the frame where we will resume. Usually the
// expression stack should be empty in this case but there can be
// iterators on the stack.
@@ -1030,16 +1033,21 @@ InitFromBailout(JSContext *cx, HandleScr
// 4. If resuming into top-level code main body, an SPS entry will
// have been pushed, and can be left alone.
//
// Only need to handle case 3 here.
if (!caller && bailoutKind != Bailout_ArgumentCheck) {
JitSpew(JitSpew_BaselineBailouts,
" Popping SPS entry for outermost frame");
cx->runtime()->spsProfiler.exit(script, fun);
+
+ // Notify caller that the last SPS frame was popped, so not
+ // to do it again.
+ if (poppedLastSPSFrameOut)
+ *poppedLastSPSFrameOut = true;
}
}
} else {
opReturnAddr = nativeCodeForPC;
}
builder.setResumeAddr(opReturnAddr);
JitSpew(JitSpew_BaselineBailouts, " Set resumeAddr=%p", opReturnAddr);
}
@@ -1286,25 +1294,28 @@ InitFromBailout(JSContext *cx, HandleScr
return false;
return true;
}
uint32_t
jit::BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIterator &iter,
bool invalidate, BaselineBailoutInfo **bailoutInfo,
- const ExceptionBailoutInfo *excInfo)
+ const ExceptionBailoutInfo *excInfo, bool *poppedLastSPSFrameOut)
{
// The Baseline frames we will reconstruct on the heap are not rooted, so GC
// must be suppressed here.
MOZ_ASSERT(cx->mainThread().suppressGC);
MOZ_ASSERT(bailoutInfo != nullptr);
MOZ_ASSERT(*bailoutInfo == nullptr);
+ MOZ_ASSERT(poppedLastSPSFrameOut);
+ MOZ_ASSERT(!*poppedLastSPSFrameOut);
+
TraceLogger *logger = TraceLoggerForMainThread(cx->runtime());
TraceLogStopEvent(logger, TraceLogger::IonMonkey);
TraceLogStartEvent(logger, TraceLogger::Baseline);
// The caller of the top frame must be one of the following:
// IonJS - Ion calling into Ion.
// BaselineStub - Baseline calling into Ion.
// Entry - Interpreter or other calling into Ion.
@@ -1424,17 +1435,18 @@ jit::BailoutIonToBaseline(JSContext *cx,
// We also need to pass excInfo if we're bailing out in place for
// debug mode.
bool passExcInfo = handleException || propagatingExceptionForDebugMode;
jsbytecode *callPC = nullptr;
RootedFunction nextCallee(cx, nullptr);
if (!InitFromBailout(cx, caller, callerPC, fun, scr, iter.ionScript(),
snapIter, invalidate, builder, startFrameFormals,
- &nextCallee, &callPC, passExcInfo ? excInfo : nullptr))
+ &nextCallee, &callPC, passExcInfo ? excInfo : nullptr,
+ poppedLastSPSFrameOut))
{
return BAILOUT_RETURN_FATAL_ERROR;
}
if (!snapIter.moreFrames()) {
MOZ_ASSERT(!callPC);
break;
}
--- a/js/src/jit/BaselineJIT.h
+++ b/js/src/jit/BaselineJIT.h
@@ -434,17 +434,18 @@ struct BaselineBailoutInfo
// The bailout kind.
BailoutKind bailoutKind;
};
uint32_t
BailoutIonToBaseline(JSContext *cx, JitActivation *activation, JitFrameIterator &iter,
bool invalidate, BaselineBailoutInfo **bailoutInfo,
- const ExceptionBailoutInfo *exceptionInfo = nullptr);
+ const ExceptionBailoutInfo *exceptionInfo,
+ bool *poppedLastSPSFrame);
// Mark baseline scripts on the stack as active, so that they are not discarded
// during GC.
void
MarkActiveBaselineScripts(Zone *zone);
MethodStatus
BaselineCompile(JSContext *cx, JSScript *script);