uint32 ion::ReflowTypeInfo(uint32 bailoutResult) { JSContext *cx = GetIonContext()->cx; IonActivation *activation = cx->runtime->ionActivation; IonSpew(IonSpew_Bailouts, "reflowing type info"); if (bailoutResult == BAILOUT_RETURN_ARGUMENT_CHECK) { IonSpew(IonSpew_Bailouts, "reflowing type info at argument-checked entry"); ReflowArgTypes(cx); return true; } RootedScript script(cx, cx->fp()->script()); jsbytecode *pc = activation->bailout()->bailoutPc(); JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET); IonSpew(IonSpew_Bailouts, "reflowing type info at %s:%d pcoff %d", script->filename, script->lineno, pc - script->code); types::AutoEnterTypeInference enter(cx); if (bailoutResult == BAILOUT_RETURN_TYPE_BARRIER) script->analysis()->breakTypeBarriers(cx, pc - script->code, false); else JS_ASSERT(bailoutResult == BAILOUT_RETURN_MONITOR); // When a type barrier fails, the bad value is at the top of the stack. Value &result = cx->regs().sp[-1]; types::TypeScript::Monitor(cx, script, pc, result); return true; }
IonBailoutIterator::IonBailoutIterator(const IonActivationIterator &activations, BailoutStack *bailout) : IonFrameIterator(activations), machine_(bailout->machine()) { uint8_t *sp = bailout->parentStackPointer(); uint8_t *fp = sp + bailout->frameSize(); current_ = fp; type_ = IonFrame_OptimizedJS; topFrameSize_ = current_ - sp; topIonScript_ = script()->ion; if (bailout->frameClass() == FrameSizeClass::None()) { snapshotOffset_ = bailout->snapshotOffset(); return; } // Compute the snapshot offset from the bailout ID. IonActivation *activation = activations.activation(); JSCompartment *jsCompartment = activation->compartment(); IonCompartment *ionCompartment = jsCompartment->ionCompartment(); IonCode *code = ionCompartment->getBailoutTable(bailout->frameClass()); uintptr_t tableOffset = bailout->tableOffset(); uintptr_t tableStart = reinterpret_cast<uintptr_t>(code->raw()); JS_ASSERT(tableOffset >= tableStart && tableOffset < tableStart + code->instructionsSize()); JS_ASSERT((tableOffset - tableStart) % BAILOUT_TABLE_ENTRY_SIZE == 0); uint32_t bailoutId = ((tableOffset - tableStart) / BAILOUT_TABLE_ENTRY_SIZE) - 1; JS_ASSERT(bailoutId < BAILOUT_TABLE_SIZE); snapshotOffset_ = topIonScript_->bailoutToSnapshot(bailoutId); }
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::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; }
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; }
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; } }