uint32_t jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) { JSContext *cx = GetJSContextFromJitCode(); JS_ASSERT(bailoutInfo); // We don't have an exit frame. MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) && IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(IonCommonFrameLayout), 0, 0x1000), "Fake jitTop pointer should be within the first page."); cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT; gc::AutoSuppressGC suppress(cx); JitActivationIterator jitActivations(cx->runtime()); IonBailoutIterator iter(jitActivations, sp); JitActivation *activation = jitActivations->asJit(); TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); TraceLogTimestamp(logger, TraceLogger::Bailout); JitSpew(JitSpew_IonBailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset()); JS_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; uint32_t retval = BailoutIonToBaseline(cx, activation, iter, false, bailoutInfo); JS_ASSERT(retval == BAILOUT_RETURN_OK || retval == BAILOUT_RETURN_FATAL_ERROR || retval == BAILOUT_RETURN_OVERRECURSED); JS_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 // this function returns. Any SPS entry pushed for this frame will // be silently forgotten. // // 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); JSScript *script = iter.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); EnsureExitFrame(iter.jsFrame()); } return retval; }
uint32_t jit::Bailout(BailoutStack* sp, BaselineBailoutInfo** bailoutInfo) { JSContext* cx = GetJSContextFromJitCode(); MOZ_ASSERT(bailoutInfo); // We don't have an exit frame. MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) && IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000), "Fake jitTop pointer should be within the first page."); cx->runtime()->jitTop = FAKE_JIT_TOP_FOR_BAILOUT; JitActivationIterator jitActivations(cx->runtime()); BailoutFrameInfo bailoutData(jitActivations, sp); JitFrameIterator iter(jitActivations); MOZ_ASSERT(!iter.ionScript()->invalidated()); CommonFrameLayout* currentFramePtr = iter.current(); TraceLoggerThread* 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, /* excInfo = */ nullptr); 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) { JSScript* script = iter.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), /* popSPSFrame = */ false); EnsureExitFrame(iter.jsFrame()); } // This condition was wrong when we entered this bailout function, but it // might be true now. A GC might have reclaimed all the Jit code and // invalidated all frames which are currently on the stack. As we are // already in a bailout, we could not switch to an invalidation // bailout. When the code of an IonScript which is on the stack is // invalidated (see InvalidateActivation), we remove references to it and // increment the reference counter for each activation that appear on the // stack. As the bailed frame is one of them, we have to decrement it now. if (iter.ionScript()->invalidated()) iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); // NB: Commentary on how |lastProfilingFrame| is set from bailouts. // // Once we return to jitcode, any following frames might get clobbered, // but the current frame will not (as it will be clobbered "in-place" // with a baseline frame that will share the same frame prefix). // However, there may be multiple baseline frames unpacked from this // single Ion frame, which means we will need to once again reset // |lastProfilingFrame| to point to the correct unpacked last frame // in |FinishBailoutToBaseline|. // // In the case of error, the jitcode will jump immediately to an // exception handler, which will unwind the frames and properly set // the |lastProfilingFrame| to point to the frame being resumed into // (see |AutoResetLastProfilerFrameOnReturnFromException|). // // In both cases, we want to temporarily set the |lastProfilingFrame| // to the current frame being bailed out, and then fix it up later. if (cx->runtime()->jitRuntime()->isProfilerInstrumentationEnabled(cx->runtime())) cx->runtime()->jitActivation->setLastProfilingFrame(currentFramePtr); return retval; }
static void TestIsInRangeClass() { void* nul = nullptr; Base* baseBegin = nullptr; Base* baseEnd = baseBegin + 1; Base* baseEnd2 = baseBegin + 2; MOZ_RELEASE_ASSERT(IsInRange(nul, baseBegin, baseEnd)); MOZ_RELEASE_ASSERT(!IsInRange(nul, baseEnd, baseEnd2)); MOZ_RELEASE_ASSERT(IsInRange(baseBegin, baseBegin, baseEnd)); MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, baseBegin, baseEnd)); MOZ_RELEASE_ASSERT(IsInRange(baseBegin, baseBegin, baseEnd2)); MOZ_RELEASE_ASSERT(IsInRange(baseEnd, baseBegin, baseEnd2)); MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, baseBegin, baseEnd2)); uintptr_t ubaseBegin = uintptr_t(baseBegin); uintptr_t ubaseEnd = uintptr_t(baseEnd); uintptr_t ubaseEnd2 = uintptr_t(baseEnd2); MOZ_RELEASE_ASSERT(IsInRange(nul, ubaseBegin, ubaseEnd)); MOZ_RELEASE_ASSERT(!IsInRange(nul, ubaseEnd, ubaseEnd2)); MOZ_RELEASE_ASSERT(IsInRange(baseBegin, ubaseBegin, ubaseEnd)); MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, ubaseBegin, ubaseEnd)); MOZ_RELEASE_ASSERT(IsInRange(baseBegin, ubaseBegin, ubaseEnd2)); MOZ_RELEASE_ASSERT(IsInRange(baseEnd, ubaseBegin, ubaseEnd2)); MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, ubaseBegin, ubaseEnd2)); }
static void TestIsInRangeVoid() { int* intBegin = nullptr; int* intEnd = intBegin + 1; int* intEnd2 = intBegin + 2; void* voidBegin = intBegin; void* voidEnd = intEnd; void* voidEnd2 = intEnd2; MOZ_RELEASE_ASSERT(IsInRange(voidBegin, intBegin, intEnd)); MOZ_RELEASE_ASSERT(!IsInRange(voidEnd, intBegin, intEnd)); MOZ_RELEASE_ASSERT(IsInRange(voidBegin, voidBegin, voidEnd)); MOZ_RELEASE_ASSERT(!IsInRange(voidEnd, voidBegin, voidEnd)); MOZ_RELEASE_ASSERT(IsInRange(voidBegin, intBegin, intEnd2)); MOZ_RELEASE_ASSERT(IsInRange(voidEnd, intBegin, intEnd2)); MOZ_RELEASE_ASSERT(!IsInRange(voidEnd2, intBegin, intEnd2)); MOZ_RELEASE_ASSERT(IsInRange(voidBegin, voidBegin, voidEnd2)); MOZ_RELEASE_ASSERT(IsInRange(voidEnd, voidBegin, voidEnd2)); MOZ_RELEASE_ASSERT(!IsInRange(voidEnd2, voidBegin, voidEnd2)); uintptr_t uintBegin = uintptr_t(intBegin); uintptr_t uintEnd = uintptr_t(intEnd); uintptr_t uintEnd2 = uintptr_t(intEnd2); MOZ_RELEASE_ASSERT(IsInRange(voidBegin, uintBegin, uintEnd)); MOZ_RELEASE_ASSERT(!IsInRange(voidEnd, uintBegin, uintEnd)); MOZ_RELEASE_ASSERT(IsInRange(voidBegin, uintBegin, uintEnd2)); MOZ_RELEASE_ASSERT(IsInRange(voidEnd, uintBegin, uintEnd2)); MOZ_RELEASE_ASSERT(!IsInRange(voidEnd2, uintBegin, uintEnd2)); }
static void TestIsInRangeClassExtraDerivedEmpty() { void* nul = nullptr; ExtraDerivedEmpty* derivedBegin = nullptr; ExtraDerivedEmpty* derivedEnd = derivedBegin + 1; ExtraDerivedEmpty* derivedEnd2 = derivedBegin + 2; EmptyBase* baseBegin = static_cast<EmptyBase*>(derivedBegin); EmptyBase* baseEnd = static_cast<EmptyBase*>(derivedEnd); EmptyBase* baseEnd2 = static_cast<EmptyBase*>(derivedEnd2); MOZ_RELEASE_ASSERT(IsInRange(nul, derivedBegin, derivedEnd)); MOZ_RELEASE_ASSERT(!IsInRange(nul, derivedEnd, derivedEnd2)); MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd)); MOZ_RELEASE_ASSERT(!IsInRange(baseEnd, derivedBegin, derivedEnd)); MOZ_RELEASE_ASSERT(IsInRange(baseBegin, derivedBegin, derivedEnd2)); MOZ_RELEASE_ASSERT(IsInRange(baseEnd, derivedBegin, derivedEnd2)); MOZ_RELEASE_ASSERT(!IsInRange(baseEnd2, derivedBegin, derivedEnd2)); uintptr_t uderivedBegin = uintptr_t(derivedBegin); uintptr_t uderivedEnd = uintptr_t(derivedEnd); uintptr_t uderivedEnd2 = uintptr_t(derivedEnd2); MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd)); MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd, uderivedBegin, uderivedEnd)); MOZ_RELEASE_ASSERT(IsInRange(derivedBegin, uderivedBegin, uderivedEnd2)); MOZ_RELEASE_ASSERT(IsInRange(derivedEnd, uderivedBegin, uderivedEnd2)); MOZ_RELEASE_ASSERT(!IsInRange(derivedEnd2, uderivedBegin, uderivedEnd2)); }
static void TestIsInRangeNonClass() { void* nul = nullptr; int* intBegin = nullptr; int* intEnd = intBegin + 1; int* intEnd2 = intBegin + 2; MOZ_RELEASE_ASSERT(IsInRange(nul, intBegin, intEnd)); MOZ_RELEASE_ASSERT(!IsInRange(nul, intEnd, intEnd2)); MOZ_RELEASE_ASSERT(IsInRange(intBegin, intBegin, intEnd)); MOZ_RELEASE_ASSERT(!IsInRange(intEnd, intBegin, intEnd)); MOZ_RELEASE_ASSERT(IsInRange(intBegin, intBegin, intEnd2)); MOZ_RELEASE_ASSERT(IsInRange(intEnd, intBegin, intEnd2)); MOZ_RELEASE_ASSERT(!IsInRange(intEnd2, intBegin, intEnd2)); uintptr_t uintBegin = uintptr_t(intBegin); uintptr_t uintEnd = uintptr_t(intEnd); uintptr_t uintEnd2 = uintptr_t(intEnd2); MOZ_RELEASE_ASSERT(IsInRange(nul, uintBegin, uintEnd)); MOZ_RELEASE_ASSERT(!IsInRange(nul, uintEnd, uintEnd2)); MOZ_RELEASE_ASSERT(IsInRange(intBegin, uintBegin, uintEnd)); MOZ_RELEASE_ASSERT(!IsInRange(intEnd, uintBegin, uintEnd)); MOZ_RELEASE_ASSERT(IsInRange(intBegin, uintBegin, uintEnd2)); MOZ_RELEASE_ASSERT(IsInRange(intEnd, uintBegin, uintEnd2)); MOZ_RELEASE_ASSERT(!IsInRange(intEnd2, uintBegin, uintEnd2)); }
uint32_t jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) { JSContext *cx = GetJSContextFromJitCode(); MOZ_ASSERT(bailoutInfo); // We don't have an exit frame. MOZ_ASSERT(IsInRange(FAKE_JIT_TOP_FOR_BAILOUT, 0, 0x1000) && IsInRange(FAKE_JIT_TOP_FOR_BAILOUT + sizeof(CommonFrameLayout), 0, 0x1000), "Fake jitTop pointer should be within the first page."); cx->mainThread().jitTop = FAKE_JIT_TOP_FOR_BAILOUT; JitActivationIterator jitActivations(cx->runtime()); BailoutFrameInfo bailoutData(jitActivations, sp); JitFrameIterator iter(jitActivations); MOZ_ASSERT(!iter.ionScript()->invalidated()); 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; 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 // this function returns. Any SPS entry pushed for this frame will // be silently forgotten. // // 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) && !poppedLastSPSFrame; JSScript *script = iter.script(); probes::ExitScript(cx, script, script->functionNonDelazifying(), popSPSFrame); EnsureExitFrame(iter.jsFrame()); } // This condition was wrong when we entered this bailout function, but it // might be true now. A GC might have reclaimed all the Jit code and // invalidated all frames which are currently on the stack. As we are // already in a bailout, we could not switch to an invalidation // bailout. When the code of an IonScript which is on the stack is // invalidated (see InvalidateActivation), we remove references to it and // increment the reference counter for each activation that appear on the // stack. As the bailed frame is one of them, we have to decrement it now. if (iter.ionScript()->invalidated()) iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp()); return retval; }