michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/Bailouts.h" michael@0: michael@0: #include "jscntxt.h" michael@0: michael@0: #include "jit/BaselineJIT.h" michael@0: #include "jit/Ion.h" michael@0: #include "jit/IonSpewer.h" michael@0: #include "jit/JitCompartment.h" michael@0: #include "jit/Snapshots.h" michael@0: #include "vm/TraceLogging.h" michael@0: michael@0: #include "jit/JitFrameIterator-inl.h" michael@0: #include "vm/Stack-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: // These constructor are exactly the same except for the type of the iterator michael@0: // which is given to the SnapshotIterator constructor. Doing so avoid the michael@0: // creation of virtual functions for the IonIterator but may introduce some michael@0: // weirdness as IonInlineIterator is using a JitFrameIterator reference. michael@0: // michael@0: // If a function relies on ionScript() or to use OsiIndex(), due to the michael@0: // lack of virtual, these functions will use the JitFrameIterator reference michael@0: // contained in the InlineFrameIterator and thus are not able to recover michael@0: // correctly the data stored in IonBailoutIterator. michael@0: // michael@0: // Currently, such cases should not happen because our only use case of the michael@0: // JitFrameIterator within InlineFrameIterator is to read the frame content, or michael@0: // to clone it to find the parent scripted frame. Both use cases are fine and michael@0: // should not cause any issue since the only potential issue is to read the michael@0: // bailed out frame. michael@0: michael@0: SnapshotIterator::SnapshotIterator(const IonBailoutIterator &iter) michael@0: : snapshot_(iter.ionScript()->snapshots(), michael@0: iter.snapshotOffset(), michael@0: iter.ionScript()->snapshotsRVATableSize(), michael@0: iter.ionScript()->snapshotsListSize()), michael@0: recover_(snapshot_, michael@0: iter.ionScript()->recovers(), michael@0: iter.ionScript()->recoversSize()), michael@0: fp_(iter.jsFrame()), michael@0: machine_(iter.machineState()), michael@0: ionScript_(iter.ionScript()) michael@0: { michael@0: } michael@0: michael@0: void michael@0: IonBailoutIterator::dump() const michael@0: { michael@0: if (type_ == JitFrame_IonJS) { michael@0: InlineFrameIterator frames(GetJSContextFromJitCode(), this); michael@0: for (;;) { michael@0: frames.dump(); michael@0: if (!frames.more()) michael@0: break; michael@0: ++frames; michael@0: } michael@0: } else { michael@0: JitFrameIterator::dump(); michael@0: } michael@0: } michael@0: michael@0: uint32_t michael@0: jit::Bailout(BailoutStack *sp, BaselineBailoutInfo **bailoutInfo) michael@0: { michael@0: JSContext *cx = GetJSContextFromJitCode(); michael@0: JS_ASSERT(bailoutInfo); michael@0: michael@0: // We don't have an exit frame. michael@0: cx->mainThread().ionTop = nullptr; michael@0: JitActivationIterator jitActivations(cx->runtime()); michael@0: IonBailoutIterator iter(jitActivations, sp); michael@0: JitActivation *activation = jitActivations->asJit(); michael@0: michael@0: TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); michael@0: TraceLogTimestamp(logger, TraceLogger::Bailout); michael@0: michael@0: IonSpew(IonSpew_Bailouts, "Took bailout! Snapshot offset: %d", iter.snapshotOffset()); michael@0: michael@0: JS_ASSERT(IsBaselineEnabled(cx)); michael@0: michael@0: *bailoutInfo = nullptr; michael@0: uint32_t retval = BailoutIonToBaseline(cx, activation, iter, false, bailoutInfo); michael@0: JS_ASSERT(retval == BAILOUT_RETURN_OK || michael@0: retval == BAILOUT_RETURN_FATAL_ERROR || michael@0: retval == BAILOUT_RETURN_OVERRECURSED); michael@0: JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr); michael@0: michael@0: if (retval != BAILOUT_RETURN_OK) michael@0: EnsureExitFrame(iter.jsFrame()); michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: uint32_t michael@0: jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, michael@0: BaselineBailoutInfo **bailoutInfo) michael@0: { michael@0: sp->checkInvariants(); michael@0: michael@0: JSContext *cx = GetJSContextFromJitCode(); michael@0: michael@0: // We don't have an exit frame. michael@0: cx->mainThread().ionTop = nullptr; michael@0: JitActivationIterator jitActivations(cx->runtime()); michael@0: IonBailoutIterator iter(jitActivations, sp); michael@0: JitActivation *activation = jitActivations->asJit(); michael@0: michael@0: TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); michael@0: TraceLogTimestamp(logger, TraceLogger::Invalidation); michael@0: michael@0: IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset()); michael@0: michael@0: // Note: the frame size must be computed before we return from this function. michael@0: *frameSizeOut = iter.topFrameSize(); michael@0: michael@0: JS_ASSERT(IsBaselineEnabled(cx)); michael@0: michael@0: *bailoutInfo = nullptr; michael@0: uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo); michael@0: JS_ASSERT(retval == BAILOUT_RETURN_OK || michael@0: retval == BAILOUT_RETURN_FATAL_ERROR || michael@0: retval == BAILOUT_RETURN_OVERRECURSED); michael@0: JS_ASSERT_IF(retval == BAILOUT_RETURN_OK, *bailoutInfo != nullptr); michael@0: michael@0: if (retval != BAILOUT_RETURN_OK) { michael@0: IonJSFrameLayout *frame = iter.jsFrame(); michael@0: IonSpew(IonSpew_Invalidate, "converting to exit frame"); michael@0: IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken()); michael@0: IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize())); michael@0: IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress()); michael@0: michael@0: frame->replaceCalleeToken(nullptr); michael@0: EnsureExitFrame(frame); michael@0: michael@0: IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken()); michael@0: IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); michael@0: IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress()); michael@0: } michael@0: michael@0: iter.ionScript()->decref(cx->runtime()->defaultFreeOp()); michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: IonBailoutIterator::IonBailoutIterator(const JitActivationIterator &activations, michael@0: const JitFrameIterator &frame) michael@0: : JitFrameIterator(activations), michael@0: machine_(frame.machineState()) michael@0: { michael@0: returnAddressToFp_ = frame.returnAddressToFp(); michael@0: topIonScript_ = frame.ionScript(); michael@0: const OsiIndex *osiIndex = frame.osiIndex(); michael@0: michael@0: current_ = (uint8_t *) frame.fp(); michael@0: type_ = JitFrame_IonJS; michael@0: topFrameSize_ = frame.frameSize(); michael@0: snapshotOffset_ = osiIndex->snapshotOffset(); michael@0: } michael@0: michael@0: uint32_t michael@0: jit::ExceptionHandlerBailout(JSContext *cx, const InlineFrameIterator &frame, michael@0: ResumeFromException *rfe, michael@0: const ExceptionBailoutInfo &excInfo, michael@0: bool *overrecursed) michael@0: { michael@0: // We can be propagating debug mode exceptions without there being an michael@0: // actual exception pending. For instance, when we return false from an michael@0: // operation callback like a timeout handler. michael@0: MOZ_ASSERT_IF(!excInfo.propagatingIonExceptionForDebugMode(), cx->isExceptionPending()); michael@0: michael@0: cx->mainThread().ionTop = nullptr; michael@0: JitActivationIterator jitActivations(cx->runtime()); michael@0: IonBailoutIterator iter(jitActivations, frame.frame()); michael@0: JitActivation *activation = jitActivations->asJit(); michael@0: michael@0: BaselineBailoutInfo *bailoutInfo = nullptr; michael@0: uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, &bailoutInfo, &excInfo); michael@0: michael@0: if (retval == BAILOUT_RETURN_OK) { michael@0: MOZ_ASSERT(bailoutInfo); michael@0: michael@0: // Overwrite the kind so HandleException after the bailout returns michael@0: // false, jumping directly to the exception tail. michael@0: if (excInfo.propagatingIonExceptionForDebugMode()) michael@0: bailoutInfo->bailoutKind = Bailout_IonExceptionDebugMode; michael@0: michael@0: rfe->kind = ResumeFromException::RESUME_BAILOUT; michael@0: rfe->target = cx->runtime()->jitRuntime()->getBailoutTail()->raw(); michael@0: rfe->bailoutInfo = bailoutInfo; michael@0: } else { michael@0: // Bailout failed. If there was a fatal error, clear the michael@0: // exception to turn this into an uncatchable error. If the michael@0: // overrecursion check failed, continue popping all inline michael@0: // frames and have the caller report an overrecursion error. michael@0: MOZ_ASSERT(!bailoutInfo); michael@0: michael@0: if (!excInfo.propagatingIonExceptionForDebugMode()) michael@0: cx->clearPendingException(); michael@0: michael@0: if (retval == BAILOUT_RETURN_OVERRECURSED) michael@0: *overrecursed = true; michael@0: else michael@0: MOZ_ASSERT(retval == BAILOUT_RETURN_FATAL_ERROR); michael@0: } michael@0: michael@0: return retval; michael@0: } michael@0: michael@0: // Initialize the decl env Object, call object, and any arguments obj of the current frame. michael@0: bool michael@0: jit::EnsureHasScopeObjects(JSContext *cx, AbstractFramePtr fp) michael@0: { michael@0: if (fp.isFunctionFrame() && michael@0: fp.fun()->isHeavyweight() && michael@0: !fp.hasCallObj()) michael@0: { michael@0: return fp.initFunctionScopeObjects(cx); michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: jit::CheckFrequentBailouts(JSContext *cx, JSScript *script) michael@0: { michael@0: if (script->hasIonScript()) { michael@0: // Invalidate if this script keeps bailing out without invalidation. Next time michael@0: // we compile this script LICM will be disabled. michael@0: IonScript *ionScript = script->ionScript(); michael@0: michael@0: if (ionScript->numBailouts() >= js_JitOptions.frequentBailoutThreshold && michael@0: !script->hadFrequentBailouts()) michael@0: { michael@0: script->setHadFrequentBailouts(); michael@0: michael@0: IonSpew(IonSpew_Invalidate, "Invalidating due to too many bailouts"); michael@0: michael@0: if (!Invalidate(cx, script)) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: }