/* * Given a frame newer than the entry frame, try to finish it. If it's at a * return position, pop the frame. If it's at a safe point, execute it in * Jaeger code. Otherwise, try to interpret until a safe point. * * While this function is guaranteed to make progress, it may not actually * finish or pop the current frame. It can either: * 1) Finalize a finished frame, or * 2) Finish and finalize the frame in the Method JIT, or * 3) Interpret, which can: * a) Propagate an error, or * b) Finish the frame, but not finalize it, or * c) Abruptly leave at any point in the frame, or in a newer frame * pushed by a call, that has method JIT'd code. */ static bool EvaluateExcessFrame(VMFrame &f, JSStackFrame *entryFrame) { JSContext *cx = f.cx; JSStackFrame *fp = cx->fp(); /* * A "finished" frame is when the interpreter rested on a STOP, * RETURN, RETRVAL, etc. We check for finished frames BEFORE looking * for a safe point. If the frame was finished, we could have already * called ScriptEpilogue(), and entering the JIT could call it twice. */ if (!fp->hasImacropc() && FrameIsFinished(cx)) return HandleFinishedFrame(f, entryFrame); if (void *ncode = AtSafePoint(cx)) { if (!JaegerShotAtSafePoint(cx, ncode)) return false; InlineReturn(f); AdvanceReturnPC(cx); return true; } return PartialInterpret(f); }
/* Returns whether the current PC has method JIT'd code. */ static inline void * AtSafePoint(JSContext *cx) { JSStackFrame *fp = cx->fp(); if (fp->hasImacropc()) return false; JSScript *script = fp->script(); return script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc); }
/* * Interprets until either a safe point is reached that has method JIT'd * code, or the current frame tries to return. */ static inline JSBool PartialInterpret(VMFrame &f) { JSContext *cx = f.cx; JSStackFrame *fp = cx->fp(); #ifdef DEBUG JSScript *script = fp->script(); JS_ASSERT(!fp->finishedInInterpreter()); JS_ASSERT(fp->hasImacropc() || !script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc)); #endif JSBool ok = JS_TRUE; ok = Interpret(cx, fp, 0, JSINTERP_SAFEPOINT); return ok; }
void * RunTracer(VMFrame &f) #endif { JSContext *cx = f.cx; JSStackFrame *entryFrame = f.fp(); TracePointAction tpa; /* :TODO: nuke PIC? */ if (!cx->traceJitEnabled) return NULL; /* * Force initialization of the entry frame's scope chain and return value, * if necessary. The tracer can query the scope chain without needing to * check the HAS_SCOPECHAIN flag, and the frame is guaranteed to have the * correct return value stored if we trace/interpret through to the end * of the frame. */ entryFrame->scopeChain(); entryFrame->returnValue(); bool blacklist; uintN inlineCallCount = 0; void **traceData; uintN *traceEpoch; uint32 *loopCounter; uint32 hits; #if JS_MONOIC traceData = &ic.traceData; traceEpoch = &ic.traceEpoch; loopCounter = &ic.loopCounter; *loopCounter = 1; hits = ic.loopCounterStart; #else traceData = NULL; traceEpoch = NULL; loopCounter = NULL; hits = 1; #endif tpa = MonitorTracePoint(f.cx, inlineCallCount, &blacklist, traceData, traceEpoch, loopCounter, hits); JS_ASSERT(!TRACE_RECORDER(cx)); #if JS_MONOIC ic.loopCounterStart = *loopCounter; if (blacklist) DisableTraceHint(entryFrame->jit(), ic); #endif // Even though ExecuteTree() bypasses the interpreter, it should propagate // error failures correctly. JS_ASSERT_IF(cx->isExceptionPending(), tpa == TPA_Error); f.fp() = cx->fp(); JS_ASSERT(f.fp() == cx->fp()); switch (tpa) { case TPA_Nothing: return NULL; case TPA_Error: if (!HandleErrorInExcessFrame(f, entryFrame, f.fp()->finishedInInterpreter())) THROWV(NULL); JS_ASSERT(!cx->fp()->hasImacropc()); break; case TPA_RanStuff: case TPA_Recorded: break; } /* * The tracer could have dropped us off on any frame at any position. * Well, it could not have removed frames (recursion is disabled). * * Frames after the entryFrame cannot be entered via JaegerShotAtSafePoint() * unless each is at a safe point. We can JaegerShotAtSafePoint these * frames individually, but we must unwind to the entryFrame. * * Note carefully that JaegerShotAtSafePoint can resume methods at * arbitrary safe points whereas JaegerShot cannot. * * If we land on entryFrame without a safe point in sight, we'll end up * at the RETURN op. This is an edge case with two paths: * * 1) The entryFrame is the last inline frame. If it fell on a RETURN, * move the return value down. * 2) The entryFrame is NOT the last inline frame. Pop the frame. * * In both cases, we hijack the stub to return to the force-return * trampoline. This trampoline simulates the frame-popping portion of * emitReturn (except without the benefit of the FrameState) and will * produce the necessary register state to return to the caller. */ restart: /* Step 1. Finish frames created after the entry frame. */ if (!FinishExcessFrames(f, entryFrame)) THROWV(NULL); /* IMacros are guaranteed to have been removed by now. */ JS_ASSERT(f.fp() == entryFrame); JS_ASSERT(!entryFrame->hasImacropc()); /* Step 2. If entryFrame is done, use a special path to return to EnterMethodJIT(). */ if (FrameIsFinished(cx)) { if (!HandleFinishedFrame(f, entryFrame)) THROWV(NULL); void *retPtr = JS_FUNC_TO_DATA_PTR(void *, cx->jaegerCompartment()->forceReturnTrampoline()); *f.returnAddressLocation() = retPtr; return NULL; } /* Step 3. If entryFrame is at a safe point, just leave. */ if (void *ncode = AtSafePoint(cx)) return ncode; /* Step 4. Do a partial interp, then restart the whole process. */ if (!PartialInterpret(f)) { if (!HandleErrorInExcessFrame(f, entryFrame)) THROWV(NULL); } goto restart; }
/* * Called when an error is in progress and the topmost frame could not handle * it. This will unwind to a given frame, or find and align to an exception * handler in the process. */ static inline bool HandleErrorInExcessFrame(VMFrame &f, JSStackFrame *stopFp, bool searchedTopmostFrame = true) { JSContext *cx = f.cx; /* * Callers of this called either Interpret() or JaegerShot(), which would * have searched for exception handlers already. If we see stopFp, just * return false. Otherwise, pop the frame, since it's guaranteed useless. * * Note that this also guarantees ScriptEpilogue() has been called. */ JSStackFrame *fp = cx->fp(); if (searchedTopmostFrame) { /* * This is a special case meaning that fp->finishedInInterpreter() is * true. If so, and fp == stopFp, our only choice is to propagate this * error up, back to the method JIT, and then to js_InternalThrow, * where this becomes a special case. See the comment there and bug * 624100. */ if (fp == stopFp) return false; /* * Otherwise, the protocol here (like Invoke) is to assume that the * execution mode finished the frame, and to just pop it. */ InlineReturn(f); } /* Remove the bottom frame. */ bool returnOK = false; for (;;) { fp = cx->fp(); /* Clear imacros. */ if (fp->hasImacropc()) { cx->regs->pc = fp->imacropc(); fp->clearImacropc(); } JS_ASSERT(!fp->hasImacropc()); /* If there's an exception and a handler, set the pc and leave. */ if (cx->isExceptionPending()) { jsbytecode *pc = FindExceptionHandler(cx); if (pc) { cx->regs->pc = pc; returnOK = true; break; } } /* Don't unwind if this was the entry frame. */ if (fp == stopFp) break; /* Unwind and return. */ returnOK &= bool(js_UnwindScope(cx, 0, returnOK || cx->isExceptionPending())); returnOK = ScriptEpilogue(cx, fp, returnOK); InlineReturn(f); } JS_ASSERT(&f.regs == cx->regs); JS_ASSERT_IF(!returnOK, cx->fp() == stopFp); return returnOK; }