Bug 1064835 - Fix SPS crash. r=jandem
authorKannan Vijayan <kvijayan@mozilla.com>
Thu, 16 Oct 2014 15:36:47 -0400
changeset 210815 53d8fbf54227d012ac0a88c49de6bd783fa04c35
parent 210814 a5f4b36c2cba99feba390c8a3ce4e318675b8d9e
child 210816 075cf4911854af4fdc5dba789cc518a30b929313
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjandem
bugs1064835
milestone36.0a1
Bug 1064835 - Fix SPS crash. r=jandem
js/src/jit/Bailouts.cpp
js/src/jit/BaselineBailouts.cpp
js/src/jit/BaselineJIT.h
--- 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);