uint32_t jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, BaselineBailoutInfo **bailoutInfo) { sp->checkInvariants(); JSContext *cx = GetJSContextFromJitCode(); // We don't have an exit frame. cx->mainThread().ionTop = nullptr; JitActivationIterator jitActivations(cx->runtime()); IonBailoutIterator iter(jitActivations, sp); JitActivation *activation = jitActivations.activation()->asJit(); TraceLogger *logger = TraceLoggerForMainThread(cx->runtime()); TraceLogTimestamp(logger, TraceLogger::Invalidation); IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset()); // Note: the frame size must be computed before we return from this function. *frameSizeOut = iter.topFrameSize(); JS_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, 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) { IonJSFrameLayout *frame = iter.jsFrame(); IonSpew(IonSpew_Invalidate, "converting to exit frame"); IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress()); frame->replaceCalleeToken(nullptr); EnsureExitFrame(frame); IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress()); } iter.ionScript()->decref(cx->runtime()->defaultFreeOp()); return retval; }
static JSScript* GetBailedJSScript(JSContext *cx) { // Just after the frame conversion, we can safely interpret the ionTop as JS // frame because it targets the bailed JS frame converted to an exit frame. IonJSFrameLayout *frame = reinterpret_cast<IonJSFrameLayout*>(cx->runtime->ionTop); switch (GetCalleeTokenTag(frame->calleeToken())) { case CalleeToken_Function: { JSFunction *fun = CalleeTokenToFunction(frame->calleeToken()); return fun->script(); } case CalleeToken_Script: return CalleeTokenToScript(frame->calleeToken()); default: JS_NOT_REACHED("unexpected callee token kind"); return NULL; } }
void InvalidationBailoutStack::checkInvariants() const { #ifdef DEBUG IonJSFrameLayout *frame = fp(); CalleeToken token = frame->calleeToken(); JS_ASSERT(token); uint8_t *rawBase = ionScript()->method()->raw(); uint8_t *rawLimit = rawBase + ionScript()->method()->instructionsSize(); uint8_t *osiPoint = osiPointReturnAddress(); JS_ASSERT(rawBase <= osiPoint && osiPoint <= rawLimit); #endif }
uint32 ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut) { AssertCanGC(); sp->checkInvariants(); JSContext *cx = GetIonContext()->cx; // We don't have an exit frame. cx->runtime->ionTop = NULL; IonActivationIterator ionActivations(cx); IonBailoutIterator iter(ionActivations, sp); IonActivation *activation = ionActivations.activation(); IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset()); // Note: the frame size must be computed before we return from this function. *frameSizeOut = iter.topFrameSize(); uint32 retval = ConvertFrames(cx, activation, iter); { IonJSFrameLayout *frame = iter.jsFrame(); IonSpew(IonSpew_Invalidate, "converting to exit frame"); IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress()); frame->replaceCalleeToken(NULL); EnsureExitFrame(frame); IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress()); } iter.ionScript()->decref(cx->runtime->defaultFreeOp()); if (cx->runtime->hasIonReturnOverride()) cx->regs().sp[-1] = cx->runtime->takeIonReturnOverride(); if (retval != BAILOUT_RETURN_FATAL_ERROR) { if (activation->entryfp()) { if (void *annotation = activation->entryfp()->annotation()) { // If the entry frame has an annotation, then we invalidated and have // immediately returned into this bailout. Transfer the annotation to // the new topmost frame. activation->entryfp()->setAnnotation(NULL); cx->fp()->setAnnotation(annotation); } } // If invalidation was triggered inside a stub call, we may still have to // monitor the result, since the bailout happens before the MMonitorTypes // instruction is executed. jsbytecode *pc = activation->bailout()->bailoutPc(); // If this is not a ResumeAfter bailout, there's nothing to monitor, // we will redo the op in the interpreter. bool isResumeAfter = GetNextPc(pc) == cx->regs().pc; if ((js_CodeSpec[*pc].format & JOF_TYPESET) && isResumeAfter) { JS_ASSERT(retval == BAILOUT_RETURN_OK); return BAILOUT_RETURN_MONITOR; } return retval; } return BAILOUT_RETURN_FATAL_ERROR; }
static void MarkIonJSFrame(JSTracer *trc, const IonFrameIterator &frame) { // Currently, this code only executes in sequential execution. CompileMode compileMode = COMPILE_MODE_SEQ; IonJSFrameLayout *layout = (IonJSFrameLayout *)frame.fp(); MarkCalleeToken(trc, layout->calleeToken()); IonScript *ionScript; if (frame.checkInvalidation(&ionScript)) { // This frame has been invalidated, meaning that its IonScript is no // longer reachable through the callee token (JSFunction/JSScript->ion // is now NULL or recompiled). Manually trace it here. IonScript::Trace(trc, ionScript); } else if (CalleeTokenIsFunction(layout->calleeToken())) { ionScript = CalleeTokenToFunction(layout->calleeToken())->script()->ions[compileMode]; } else { ionScript = CalleeTokenToScript(layout->calleeToken())->ions[compileMode]; } if (CalleeTokenIsFunction(layout->calleeToken())) { // (NBP) We do not need to mark formal arguments since they are covered // by the safepoint. size_t nargs = frame.numActualArgs(); // Trace function arguments. Note + 1 for thisv. Value *argv = layout->argv(); for (size_t i = 0; i < nargs + 1; i++) gc::MarkValueRoot(trc, &argv[i], "ion-argv"); } const SafepointIndex *si = ionScript->getSafepointIndex(frame.returnAddressToFp()); SafepointReader safepoint(ionScript, si); // Scan through slots which contain pointers (or on punboxing systems, // actual values). uint32 slot; while (safepoint.getGcSlot(&slot)) { uintptr_t *ref = layout->slotRef(slot); gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(ref), "ion-gc-slot"); } while (safepoint.getValueSlot(&slot)) { Value *v = (Value *)layout->slotRef(slot); gc::MarkValueRoot(trc, v, "ion-gc-slot"); } uintptr_t *spill = frame.spillBase(); GeneralRegisterSet gcRegs = safepoint.gcSpills(); GeneralRegisterSet valueRegs = safepoint.valueSpills(); for (GeneralRegisterIterator iter(safepoint.allSpills()); iter.more(); iter++) { --spill; if (gcRegs.has(*iter)) gc::MarkGCThingRoot(trc, reinterpret_cast<void **>(spill), "ion-gc-spill"); else if (valueRegs.has(*iter)) gc::MarkValueRoot(trc, reinterpret_cast<Value *>(spill), "ion-value-spill"); } #ifdef JS_NUNBOX32 LAllocation type, payload; while (safepoint.getNunboxSlot(&type, &payload)) { jsval_layout layout; layout.s.tag = (JSValueTag)ReadAllocation(frame, &type); layout.s.payload.uintptr = ReadAllocation(frame, &payload); Value v = IMPL_TO_JSVAL(layout); gc::MarkValueRoot(trc, &v, "ion-torn-value"); JS_ASSERT(v == IMPL_TO_JSVAL(layout)); } #endif }
uint32_t jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, BaselineBailoutInfo **bailoutInfo) { sp->checkInvariants(); JSContext *cx = GetJSContextFromJitCode(); // We don't have an exit frame. 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::Invalidation); 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.topFrameSize(); JS_ASSERT(IsBaselineEnabled(cx)); *bailoutInfo = nullptr; uint32_t retval = BailoutIonToBaseline(cx, activation, iter, true, 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); 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())); JitSpew(JitSpew_IonInvalidate, " orig ra %p", (void *) frame->returnAddress()); frame->replaceCalleeToken(nullptr); EnsureExitFrame(frame); JitSpew(JitSpew_IonInvalidate, " new calleeToken %p", (void *) frame->calleeToken()); JitSpew(JitSpew_IonInvalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); JitSpew(JitSpew_IonInvalidate, " new ra %p", (void *) frame->returnAddress()); } iter.ionScript()->decref(cx->runtime()->defaultFreeOp()); return retval; }
uint32_t ion::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut, BaselineBailoutInfo **bailoutInfo) { sp->checkInvariants(); JSContext *cx = GetIonContext()->cx; // We don't have an exit frame. cx->mainThread().ionTop = NULL; IonActivationIterator ionActivations(cx); IonBailoutIterator iter(ionActivations, sp); IonActivation *activation = ionActivations.activation(); IonSpew(IonSpew_Bailouts, "Took invalidation bailout! Snapshot offset: %d", iter.snapshotOffset()); // Note: the frame size must be computed before we return from this function. *frameSizeOut = iter.topFrameSize(); uint32_t retval; if (IsBaselineEnabled(cx)) { *bailoutInfo = NULL; retval = BailoutIonToBaseline(cx, activation, iter, true, bailoutInfo); JS_ASSERT(retval == BAILOUT_RETURN_BASELINE || retval == BAILOUT_RETURN_FATAL_ERROR || retval == BAILOUT_RETURN_OVERRECURSED); JS_ASSERT_IF(retval == BAILOUT_RETURN_BASELINE, *bailoutInfo != NULL); if (retval != BAILOUT_RETURN_BASELINE) { IonJSFrameLayout *frame = iter.jsFrame(); IonSpew(IonSpew_Invalidate, "converting to exit frame"); IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress()); frame->replaceCalleeToken(NULL); EnsureExitFrame(frame); IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress()); } iter.ionScript()->decref(cx->runtime->defaultFreeOp()); return retval; } else { retval = ConvertFrames(cx, activation, iter); IonJSFrameLayout *frame = iter.jsFrame(); IonSpew(IonSpew_Invalidate, "converting to exit frame"); IonSpew(IonSpew_Invalidate, " orig calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " orig frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " orig ra %p", (void *) frame->returnAddress()); frame->replaceCalleeToken(NULL); EnsureExitFrame(frame); IonSpew(IonSpew_Invalidate, " new calleeToken %p", (void *) frame->calleeToken()); IonSpew(IonSpew_Invalidate, " new frameSize %u", unsigned(frame->prevFrameLocalSize())); IonSpew(IonSpew_Invalidate, " new ra %p", (void *) frame->returnAddress()); iter.ionScript()->decref(cx->runtime->defaultFreeOp()); // Only need to take ion return override if resuming to interpreter. if (cx->runtime->hasIonReturnOverride()) cx->regs().sp[-1] = cx->runtime->takeIonReturnOverride(); if (retval != BAILOUT_RETURN_FATAL_ERROR) { // If invalidation was triggered inside a stub call, we may still have to // monitor the result, since the bailout happens before the MMonitorTypes // instruction is executed. jsbytecode *pc = activation->bailout()->bailoutPc(); // If this is not a ResumeAfter bailout, there's nothing to monitor, // we will redo the op in the interpreter. bool isResumeAfter = GetNextPc(pc) == cx->regs().pc; if ((js_CodeSpec[*pc].format & JOF_TYPESET) && isResumeAfter) { JS_ASSERT(retval == BAILOUT_RETURN_OK); return BAILOUT_RETURN_MONITOR; } return retval; } return BAILOUT_RETURN_FATAL_ERROR; } }