static uint32 ConvertFrames(JSContext *cx, IonActivation *activation, IonBailoutIterator &it) { AssertCanGC(); IonSpew(IonSpew_Bailouts, "Bailing out %s:%u, IonScript %p", it.script()->filename, it.script()->lineno, (void *) it.ionScript()); IonSpew(IonSpew_Bailouts, " reading from snapshot offset %u size %u", it.snapshotOffset(), it.ionScript()->snapshotsSize()); #ifdef DEBUG // Use count is reset after invalidation. Log use count on bailouts to // determine if we have a critical sequence of bailout. if (it.script()->ion == it.ionScript()) { IonSpew(IonSpew_Bailouts, " Current script use count is %u", it.script()->getUseCount()); } #endif SnapshotIterator iter(it); // Set a flag to avoid bailing out on every iteration or function call. Ion can // compile and run the script again after an invalidation. it.ionScript()->setBailoutExpected(); // We use OffTheBooks instead of cx because at this time we cannot iterate // on the stack safely and the reported error attempts to walk the IonMonkey // frames. We cannot iterate on the stack because we have no exit frame to // start Ion frames iteratons. BailoutClosure *br = js_new<BailoutClosure>(); if (!br) return BAILOUT_RETURN_FATAL_ERROR; activation->setBailout(br); StackFrame *fp; if (it.isEntryJSFrame() && cx->fp()->runningInIon() && activation->entryfp()) { // Avoid creating duplicate interpreter frames. This is necessary to // avoid blowing out the interpreter stack, and must be used in // conjunction with inline-OSR from within bailouts (since each Ion // activation must be tied to a unique JSStackFrame for StackIter to // work). // // Note: If the entry frame is a placeholder (a stub frame pushed for // JM -> Ion calls), then we cannot re-use it as it does not have // enough slots. JS_ASSERT(cx->fp() == activation->entryfp()); fp = cx->fp(); cx->regs().sp = fp->base(); } else { br->constructFrame(); if (!cx->stack.pushBailoutArgs(cx, it, br->argsGuard())) return BAILOUT_RETURN_FATAL_ERROR; fp = cx->stack.pushBailoutFrame(cx, it, *br->argsGuard(), br->frameGuard()); } if (!fp) return BAILOUT_RETURN_OVERRECURSED; br->setEntryFrame(fp); JSFunction *callee = it.maybeCallee(); if (callee) fp->formals()[-2].setObject(*callee); if (it.isConstructing()) fp->setConstructing(); while (true) { IonSpew(IonSpew_Bailouts, " restoring frame"); fp->initFromBailout(cx, iter); if (!iter.moreFrames()) break; iter.nextFrame(); fp = PushInlinedFrame(cx, fp); if (!fp) return BAILOUT_RETURN_OVERRECURSED; } jsbytecode *bailoutPc = fp->script()->code + iter.pcOffset(); br->setBailoutPc(bailoutPc); switch (iter.bailoutKind()) { case Bailout_Normal: return BAILOUT_RETURN_OK; case Bailout_TypeBarrier: return BAILOUT_RETURN_TYPE_BARRIER; case Bailout_Monitor: return BAILOUT_RETURN_MONITOR; case Bailout_RecompileCheck: return BAILOUT_RETURN_RECOMPILE_CHECK; case Bailout_BoundsCheck: return BAILOUT_RETURN_BOUNDS_CHECK; case Bailout_Invalidate: return BAILOUT_RETURN_INVALIDATE; case Bailout_CachedShapeGuard: return BAILOUT_RETURN_CACHED_SHAPE_GUARD; // When bailing out from an argument check, none of the code of the // function has run yet. When profiling, this means that the function // hasn't flagged its entry just yet. It has been "entered," however, so // we flag it here manually that the entry has happened. case Bailout_ArgumentCheck: fp->unsetPushedSPSFrame(); Probes::enterScript(cx, fp->script().unsafeGet(), fp->script()->function(), fp); return BAILOUT_RETURN_ARGUMENT_CHECK; } JS_NOT_REACHED("bad bailout kind"); return BAILOUT_RETURN_FATAL_ERROR; }
uint32 ion::ThunkToInterpreter(Value *vp) { JSContext *cx = GetIonContext()->cx; IonActivation *activation = cx->runtime->ionActivation; BailoutClosure *br = activation->takeBailout(); if (!EnsureHasCallObject(cx, cx->fp())) return Interpret_Error; // By default we set the forbidOsr flag on the ion script, but if a GC // happens just after we re-enter the interpreter, the ion script get // invalidated and we do not see the forbidOsr flag. This may cause a loop // which apear with eager compilation and gc zeal enabled. This code is a // workaround to avoid recompiling with OSR just after a bailout followed by // a GC. (see Bug 746691 & Bug 751383) jsbytecode *pc = cx->regs().pc; while (JSOp(*pc) == JSOP_GOTO) pc += GET_JUMP_OFFSET(pc); if (JSOp(*pc) == JSOP_LOOPENTRY) cx->regs().pc = GetNextPc(pc); // When JSScript::argumentsOptimizationFailed, we cannot access Ion frames // in order to create an arguments object for them. However, there is an // invariant that script->needsArgsObj() implies fp->hasArgsObj() (after the // prologue), so we must create one now for each inlined frame which needs // one. { br->entryfp()->clearRunningInIon(); ScriptFrameIter iter(cx); StackFrame *fp = NULL; Rooted<JSScript*> script(cx, NULL); do { fp = iter.interpFrame(); script = iter.script(); if (script->needsArgsObj()) { // Currently IonMonkey does not compile if the script needs an // arguments object, so the frame should not have any argument // object yet. JS_ASSERT(!fp->hasArgsObj()); ArgumentsObject *argsobj = ArgumentsObject::createExpected(cx, fp); if (!argsobj) return Interpret_Error; InternalBindingsHandle bindings(script, &script->bindings); const unsigned var = Bindings::argumentsVarIndex(cx, bindings); // The arguments is a local binding and needsArgsObj does not // check if it is clobbered. Ensure that the local binding // restored during bailout before storing the arguments object // to the slot. if (fp->unaliasedLocal(var).isMagic(JS_OPTIMIZED_ARGUMENTS)) fp->unaliasedLocal(var) = ObjectValue(*argsobj); } ++iter; } while (fp != br->entryfp()); } if (activation->entryfp() == br->entryfp()) { // If the bailout entry fp is the same as the activation entryfp, then // there are no scripted frames below us. In this case, just shortcut // out with a special return code, and resume interpreting in the // original Interpret activation. vp->setMagic(JS_ION_BAILOUT); js_delete(br); return Interpret_Ok; } InterpretStatus status = Interpret(cx, br->entryfp(), JSINTERP_BAILOUT); if (status == Interpret_OSR) { // The interpreter currently does not ask to perform inline OSR, so // this path is unreachable. JS_NOT_REACHED("invalid"); IonSpew(IonSpew_Bailouts, "Performing inline OSR %s:%d", cx->fp()->script()->filename, PCToLineNumber(cx->fp()->script(), cx->regs().pc)); // We want to OSR again. We need to avoid the problem where frequent // bailouts cause recursive nestings of Interpret and EnterIon. The // interpreter therefore shortcuts out, and now we're responsible for // completing the OSR inline. // // Note that we set runningInIon so that if we re-enter C++ from within // the inlined OSR, StackIter will know to traverse these frames. StackFrame *fp = cx->fp(); fp->setRunningInIon(); vp->setPrivate(fp); js_delete(br); return Interpret_OSR; } if (status == Interpret_Ok) *vp = br->entryfp()->returnValue(); // The BailoutFrameGuard's destructor will ensure that the frame is // removed. js_delete(br); return status; }
uint32 ion::ThunkToInterpreter(Value *vp) { JSContext *cx = GetIonContext()->cx; IonActivation *activation = cx->runtime->ionActivation; BailoutClosure *br = activation->takeBailout(); if (!EnsureHasCallObject(cx, cx->fp())) return Interpret_Error; // By default we set the forbidOsr flag on the ion script, but if a GC // happens just after we re-enter the interpreter, the ion script get // invalidated and we do not see the forbidOsr flag. This may cause a loop // which apear with eager compilation and gc zeal enabled. This code is a // workaround to avoid recompiling with OSR just after a bailout followed by // a GC. (see Bug 746691 & Bug 751383) jsbytecode *pc = cx->regs().pc; while (JSOp(*pc) == JSOP_GOTO) pc += GET_JUMP_OFFSET(pc); if (JSOp(*pc) == JSOP_LOOPENTRY) cx->regs().pc = GetNextPc(pc); if (activation->entryfp() == br->entryfp()) { // If the bailout entry fp is the same as the activation entryfp, then // there are no scripted frames below us. In this case, just shortcut // out with a special return code, and resume interpreting in the // original Interpret activation. vp->setMagic(JS_ION_BAILOUT); js_delete(br); return Interpret_Ok; } InterpretStatus status = Interpret(cx, br->entryfp(), JSINTERP_BAILOUT); if (status == Interpret_OSR) { // The interpreter currently does not ask to perform inline OSR, so // this path is unreachable. JS_NOT_REACHED("invalid"); IonSpew(IonSpew_Bailouts, "Performing inline OSR %s:%d", cx->fp()->script()->filename, PCToLineNumber(cx->fp()->script(), cx->regs().pc)); // We want to OSR again. We need to avoid the problem where frequent // bailouts cause recursive nestings of Interpret and EnterIon. The // interpreter therefore shortcuts out, and now we're responsible for // completing the OSR inline. // // Note that we set runningInIon so that if we re-enter C++ from within // the inlined OSR, StackIter will know to traverse these frames. StackFrame *fp = cx->fp(); fp->setRunningInIon(); vp->setPrivate(fp); js_delete(br); return Interpret_OSR; } if (status == Interpret_Ok) *vp = br->entryfp()->returnValue(); // The BailoutFrameGuard's destructor will ensure that the frame is // removed. js_delete(br); return status; }