1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
24 * David Anderson <danderson@mozilla.com>
25 * David Mandelin <dmandelin@mozilla.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
44 #include "jslibmath.h"
48 #include "jsstaticcheck.h"
50 #include "assembler/assembler/MacroAssemblerCodeRef.h"
51 #include "assembler/assembler/CodeLocation.h"
54 #include "methodjit/StubCalls.h"
56 #include "jspropertycache.h"
57 #include "methodjit/MonoIC.h"
58 #include "jsanalyze.h"
59 #include "methodjit/BaseCompiler.h"
60 #include "methodjit/ICRepatcher.h"
62 #include "jsinterpinlines.h"
63 #include "jspropertycacheinlines.h"
64 #include "jsscopeinlines.h"
65 #include "jsscriptinlines.h"
66 #include "jsstrinlines.h"
67 #include "jsobjinlines.h"
68 #include "jscntxtinlines.h"
69 #include "jsatominlines.h"
70 #include "StubCalls-inl.h"
71 #include "MethodJIT-inl.h"
73 #include "jsautooplen.h"
76 using namespace js::mjit;
82 FindExceptionHandler(JSContext *cx)
84 JSStackFrame *fp = cx->fp();
85 JSScript *script = fp->script();
88 if (cx->isExceptionPending() && JSScript::isValidOffset(script->trynotesOffset)) {
89 // The PC is updated before every stub call, so we can use it here.
90 unsigned offset = cx->regs->pc - script->main;
92 JSTryNoteArray *tnarray = script->trynotes();
93 for (unsigned i = 0; i < tnarray->length; ++i) {
94 JSTryNote *tn = &tnarray->vector[i];
96 // The following if condition actually tests two separate conditions:
97 // (1) offset - tn->start >= tn->length
98 // means the PC is not in the range of this try note, so we
99 // should continue searching, after considering:
100 // (2) offset - tn->start == tn->length
101 // means the PC is at the first op of the exception handler
102 // for this try note. This happens when an exception is thrown
103 // during recording: the interpreter sets the PC to the handler
104 // and then exits. In this case, we are in fact at the right
105 // exception handler.
107 // Hypothetically, the op we are at might have thrown an
108 // exception, in which case this would not be the right handler.
109 // But the first ops of exception handlers generated by our
110 // bytecode compiler cannot throw, so this is not possible.
111 if (offset - tn->start > tn->length)
113 if (tn->stackDepth > cx->regs->sp - fp->base())
116 jsbytecode *pc = script->main + tn->start + tn->length;
117 JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
118 JS_ASSERT(cx->regs->sp == fp->base() + tn->stackDepth);
122 JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == JSOP_ENTERBLOCK);
124 #if JS_HAS_GENERATORS
125 /* Catch cannot intercept the closing of a generator. */
126 if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
131 * Don't clear cx->throwing to save cx->exception from GC
132 * until it is pushed to the stack via [exception] in the
139 * Push (true, exception) pair for finally to indicate that
140 * [retsub] should rethrow the exception.
142 cx->regs->sp[0].setBoolean(true);
143 cx->regs->sp[1] = cx->getPendingException();
145 cx->clearPendingException();
151 * This is similar to JSOP_ENDITER in the interpreter loop,
152 * except the code now uses the stack slot normally used by
153 * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
154 * adjustment and regs.sp[1] after, to save and restore the
157 Value v = cx->getPendingException();
158 JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == JSOP_ENDITER);
159 cx->clearPendingException();
160 ok = !!js_CloseIterator(cx, &cx->regs->sp[-1].toObject());
164 cx->setPendingException(v);
174 * Clean up a frame and return.
177 InlineReturn(VMFrame &f)
179 JSContext *cx = f.cx;
180 JSStackFrame *fp = f.regs.fp;
182 JS_ASSERT(f.fp() != f.entryfp);
184 JS_ASSERT(!js_IsActiveWithOrBlock(cx, &fp->scopeChain(), 0));
186 Value *newsp = fp->actualArgs() - 1;
187 newsp[-1] = fp->returnValue();
188 cx->stack().popInlineFrame(cx, fp->prev(), newsp);
192 stubs::SlowCall(VMFrame &f, uint32 argc)
194 Value *vp = f.regs.sp - (argc + 2);
196 if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
201 stubs::SlowNew(VMFrame &f, uint32 argc)
203 JSContext *cx = f.cx;
204 Value *vp = f.regs.sp - (argc + 2);
206 if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
211 * This function must only be called after the early prologue, since it depends
215 RemovePartialFrame(JSContext *cx, JSStackFrame *fp)
217 JSStackFrame *prev = fp->prev();
218 Value *newsp = (Value *)fp;
219 cx->stack().popInlineFrame(cx, prev, newsp);
223 * HitStackQuota is called after the early prologue pushing the new frame would
224 * overflow f.stackLimit.
227 stubs::HitStackQuota(VMFrame &f)
229 /* Include space to push another frame. */
230 uintN nvals = f.fp()->script()->nslots + VALUES_PER_STACK_FRAME;
231 JS_ASSERT(f.regs.sp == f.fp()->base());
232 if (f.cx->stack().bumpCommitAndLimit(f.entryfp, f.regs.sp, nvals, &f.stackLimit))
235 /* Remove the current partially-constructed frame before throwing. */
236 RemovePartialFrame(f.cx, f.fp());
237 js_ReportOverRecursed(f.cx);
242 * This function must only be called after the early prologue, since it depends
246 stubs::FixupArity(VMFrame &f, uint32 nactual)
248 JSContext *cx = f.cx;
249 JSStackFrame *oldfp = f.fp();
251 JS_ASSERT(nactual != oldfp->numFormalArgs());
254 * Grossssss! *move* the stack frame. If this ends up being perf-critical,
255 * we can figure out how to spot-optimize it. Be careful to touch only the
256 * members that have been initialized by initCallFrameCallerHalf and the
259 uint32 flags = oldfp->isConstructingFlag();
260 JSFunction *fun = oldfp->fun();
261 void *ncode = oldfp->nativeReturnAddress();
263 /* Pop the inline frame. */
264 f.fp() = oldfp->prev();
265 f.regs.sp = (Value*) oldfp;
267 /* Reserve enough space for a callee frame. */
268 JSStackFrame *newfp = cx->stack().getInlineFrameWithinLimit(cx, (Value*) oldfp, nactual,
269 fun, fun->script(), &flags,
270 f.entryfp, &f.stackLimit);
273 * The PC is not coherent with the current frame, so fix it up for
274 * exception handling.
276 f.regs.pc = f.jit()->nativeToPC(ncode);
280 /* Reset the part of the stack frame set by the caller. */
281 newfp->initCallFrameCallerHalf(cx, flags, ncode);
283 /* Reset the part of the stack frame set by the prologue up to now. */
284 newfp->initCallFrameEarlyPrologue(fun, nactual);
286 /* The caller takes care of assigning fp to regs. */
291 stubs::CompileFunction(VMFrame &f, uint32 nactual)
294 * We have a partially constructed frame. That's not really good enough to
295 * compile though because we could throw, so get a full, adjusted frame.
297 JSContext *cx = f.cx;
298 JSStackFrame *fp = f.fp();
301 * Since we can only use members set by initCallFrameCallerHalf,
302 * we must carefully extract the callee from the nactual.
304 JSObject &callee = fp->formalArgsEnd()[-(int(nactual) + 2)].toObject();
305 JSFunction *fun = callee.getFunctionPrivate();
306 JSScript *script = fun->script();
309 * FixupArity/RemovePartialFrame expect to be called after the early
312 fp->initCallFrameEarlyPrologue(fun, nactual);
314 if (nactual != fp->numFormalArgs()) {
315 fp = (JSStackFrame *)FixupArity(f, nactual);
320 /* Finish frame initialization. */
321 fp->initCallFrameLatePrologue();
323 /* These would have been initialized by the prologue. */
325 f.regs.sp = fp->base();
326 f.regs.pc = script->code;
328 if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
331 CompileStatus status = CanMethodJIT(cx, script, fp, CompileRequest_JIT);
332 if (status == Compile_Okay)
333 return script->getJIT(fp->isConstructing())->invokeEntry;
335 /* Function did not compile... interpret it. */
336 JSBool ok = Interpret(cx, fp);
346 UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, bool *unjittable, uint32 argc)
348 JSContext *cx = f.cx;
349 Value *vp = f.regs.sp - (argc + 2);
350 JSObject &callee = vp->toObject();
351 JSFunction *newfun = callee.getFunctionPrivate();
352 JSScript *newscript = newfun->script();
354 /* Get pointer to new frame/slots, prepare arguments. */
355 StackSpace &stack = cx->stack();
356 JSStackFrame *newfp = stack.getInlineFrameWithinLimit(cx, f.regs.sp, argc,
357 newfun, newscript, &flags,
358 f.entryfp, &f.stackLimit);
359 if (JS_UNLIKELY(!newfp))
362 /* Initialize frame, locals. */
363 newfp->initCallFrame(cx, callee, newfun, argc, flags);
364 SetValueRangeToUndefined(newfp->slots(), newscript->nfixed);
366 /* Officially push the frame. */
367 stack.pushInlineFrame(cx, newscript, newfp, &f.regs);
368 JS_ASSERT(newfp == f.regs.fp);
370 /* Try to compile if not already compiled. */
371 if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) {
372 CompileStatus status = CanMethodJIT(cx, newscript, newfp, CompileRequest_Interpreter);
373 if (status == Compile_Error) {
374 /* A runtime exception was thrown, get out. */
378 if (status == Compile_Abort)
382 /* Create call object now that we can't fail entering callee. */
383 if (newfun->isHeavyweight() && !js_GetCallObject(cx, newfp))
386 /* If newscript was successfully compiled, run it. */
387 if (JITScript *jit = newscript->getJIT(newfp->isConstructing())) {
388 *pret = jit->invokeEntry;
392 /* Otherwise, run newscript in the interpreter. */
393 bool ok = !!Interpret(cx, cx->fp());
401 stubs::UncachedNew(VMFrame &f, uint32 argc)
403 UncachedCallResult ucr;
404 UncachedNewHelper(f, argc, &ucr);
409 stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
413 JSContext *cx = f.cx;
414 Value *vp = f.regs.sp - (argc + 2);
416 /* Try to do a fast inline call before the general Invoke path. */
417 if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted()) {
418 ucr->callee = &vp->toObject();
419 if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, &ucr->unjittable, argc))
422 if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
428 stubs::UncachedCall(VMFrame &f, uint32 argc)
430 UncachedCallResult ucr;
431 UncachedCallHelper(f, argc, &ucr);
436 stubs::Eval(VMFrame &f, uint32 argc)
438 Value *vp = f.regs.sp - (argc + 2);
443 if (!IsFunctionObject(*vp, &callee) ||
444 !IsBuiltinEvalFunction((fun = callee->getFunctionPrivate())))
446 if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
451 JS_ASSERT(f.regs.fp == f.cx->fp());
452 if (!DirectEval(f.cx, fun, argc, vp))
457 stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
461 JSContext *cx = f.cx;
462 Value *vp = f.regs.sp - (argc + 2);
464 if (IsFunctionObject(*vp, &ucr->callee)) {
465 ucr->callee = &vp->toObject();
466 ucr->fun = GET_FUNCTION_PRIVATE(cx, ucr->callee);
468 if (ucr->fun->isInterpreted()) {
469 if (!UncachedInlineCall(f, 0, &ucr->codeAddr, &ucr->unjittable, argc))
474 if (ucr->fun->isNative()) {
475 if (!CallJSNative(cx, ucr->fun->u.n.native, argc, vp))
481 if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
488 stubs::PutStrictEvalCallObject(VMFrame &f)
490 JS_ASSERT(f.fp()->isEvalFrame());
491 JS_ASSERT(f.fp()->script()->strictModeCode);
492 JS_ASSERT(f.fp()->hasCallObj());
493 js_PutCallObject(f.cx, f.fp());
497 stubs::PutActivationObjects(VMFrame &f)
499 JS_ASSERT(f.fp()->hasCallObj() || f.fp()->hasArgsObj());
500 js::PutActivationObjects(f.cx, f.fp());
504 js_InternalThrow(VMFrame &f)
506 JSContext *cx = f.cx;
508 // It's possible that from within RunTracer(), Interpret() returned with
509 // an error and finished the frame (i.e., called ScriptEpilogue), but has
510 // not yet performed an inline return.
512 // In this case, RunTracer() has no choice but to propagate the error
513 // up to the method JIT, and thus to this function. But ScriptEpilogue()
514 // has already been called. Detect this, and avoid double-finishing the
515 // frame. See HandleErrorInExcessFrame() and bug 624100.
516 if (f.fp()->finishedInInterpreter()) {
517 // If it's the last frame, just propagate the failure up again.
518 if (f.fp() == f.entryfp)
524 // Make sure sp is up to date.
525 JS_ASSERT(cx->regs == &f.regs);
527 // Call the throw hook if necessary
528 JSThrowHook handler = f.cx->debugHooks->throwHook;
531 switch (handler(cx, cx->fp()->script(), cx->regs->pc, Jsvalify(&rval),
532 cx->debugHooks->throwHookData)) {
534 cx->clearPendingException();
538 cx->clearPendingException();
539 cx->fp()->setReturnValue(rval);
540 return JS_FUNC_TO_DATA_PTR(void *,
541 cx->jaegerCompartment()->forceReturnTrampoline());
544 cx->setPendingException(rval);
552 jsbytecode *pc = NULL;
554 pc = FindExceptionHandler(cx);
558 // The JIT guarantees that ScriptEpilogue() has always been run
559 // upon exiting to its caller. This is important for consistency,
560 // where execution modes make similar guarantees about prologues
561 // and epilogues. RunTracer(), Interpret(), and Invoke() all
562 // rely on this property.
563 JS_ASSERT(!f.fp()->finishedInInterpreter());
564 js_UnwindScope(cx, 0, cx->isExceptionPending());
565 ScriptEpilogue(f.cx, f.fp(), false);
567 // Don't remove the last frame, this is the responsibility of
568 // JaegerShot()'s caller. We only guarantee that ScriptEpilogue()
570 if (f.entryfp == f.fp())
573 JS_ASSERT(f.regs.sp == cx->regs->sp);
577 JS_ASSERT(f.regs.sp == cx->regs->sp);
582 JSStackFrame *fp = cx->fp();
583 JSScript *script = fp->script();
584 return script->nativeCodeForPC(fp->isConstructing(), pc);
588 stubs::GetCallObject(VMFrame &f)
590 JS_ASSERT(f.fp()->fun()->isHeavyweight());
591 if (!js_GetCallObject(f.cx, f.fp()))
596 stubs::CreateThis(VMFrame &f, JSObject *proto)
598 JSContext *cx = f.cx;
599 JSStackFrame *fp = f.fp();
600 JSObject *callee = &fp->callee();
601 JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
604 fp->formalArgs()[-1].setObject(*obj);
608 stubs::EnterScript(VMFrame &f)
610 JSStackFrame *fp = f.fp();
611 JSContext *cx = f.cx;
613 if (fp->script()->debugMode) {
614 if (fp->isExecuteFrame()) {
615 JSInterpreterHook hook = cx->debugHooks->executeHook;
616 if (JS_UNLIKELY(hook != NULL))
617 fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->executeHookData));
619 JSInterpreterHook hook = cx->debugHooks->callHook;
620 if (JS_UNLIKELY(hook != NULL))
621 fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
625 Probes::enterJSFun(cx, fp->maybeFun(), fp->script());
629 stubs::LeaveScript(VMFrame &f)
631 JSStackFrame *fp = f.fp();
632 JSContext *cx = f.cx;
633 Probes::exitJSFun(cx, fp->maybeFun(), fp->maybeScript());
635 if (fp->script()->debugMode) {
637 JSInterpreterHook hook =
638 fp->isExecuteFrame() ? cx->debugHooks->executeHook : cx->debugHooks->callHook;
640 if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData())) {
642 hook(cx, fp, JS_FALSE, &ok, hookData);
652 * Called when an error is in progress and the topmost frame could not handle
653 * it. This will unwind to a given frame, or find and align to an exception
654 * handler in the process.
657 HandleErrorInExcessFrame(VMFrame &f, JSStackFrame *stopFp, bool searchedTopmostFrame = true)
659 JSContext *cx = f.cx;
662 * Callers of this called either Interpret() or JaegerShot(), which would
663 * have searched for exception handlers already. If we see stopFp, just
664 * return false. Otherwise, pop the frame, since it's guaranteed useless.
666 * Note that this also guarantees ScriptEpilogue() has been called.
668 JSStackFrame *fp = cx->fp();
669 if (searchedTopmostFrame) {
671 * This is a special case meaning that fp->finishedInInterpreter() is
672 * true. If so, and fp == stopFp, our only choice is to propagate this
673 * error up, back to the method JIT, and then to js_InternalThrow,
674 * where this becomes a special case. See the comment there and bug
681 * Otherwise, the protocol here (like Invoke) is to assume that the
682 * execution mode finished the frame, and to just pop it.
687 /* Remove the bottom frame. */
688 bool returnOK = false;
693 if (fp->hasImacropc()) {
694 cx->regs->pc = fp->imacropc();
697 JS_ASSERT(!fp->hasImacropc());
699 /* If there's an exception and a handler, set the pc and leave. */
700 if (cx->isExceptionPending()) {
701 jsbytecode *pc = FindExceptionHandler(cx);
709 /* Don't unwind if this was the entry frame. */
713 /* Unwind and return. */
714 returnOK &= bool(js_UnwindScope(cx, 0, returnOK || cx->isExceptionPending()));
715 returnOK = ScriptEpilogue(cx, fp, returnOK);
719 JS_ASSERT(&f.regs == cx->regs);
720 JS_ASSERT_IF(!returnOK, cx->fp() == stopFp);
725 /* Returns whether the current PC has method JIT'd code. */
727 AtSafePoint(JSContext *cx)
729 JSStackFrame *fp = cx->fp();
730 if (fp->hasImacropc())
733 JSScript *script = fp->script();
734 return script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc);
738 * Interprets until either a safe point is reached that has method JIT'd
739 * code, or the current frame tries to return.
742 PartialInterpret(VMFrame &f)
744 JSContext *cx = f.cx;
745 JSStackFrame *fp = cx->fp();
748 JSScript *script = fp->script();
749 JS_ASSERT(!fp->finishedInInterpreter());
750 JS_ASSERT(fp->hasImacropc() ||
751 !script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc));
755 ok = Interpret(cx, fp, 0, JSINTERP_SAFEPOINT);
760 JS_STATIC_ASSERT(JSOP_NOP == 0);
763 * Returns whether the current PC would return, or if the frame has already
764 * been completed. This distinction avoids re-entering the interpreter or JIT
765 * to complete a JSOP_RETURN. Instead, that edge case is handled in
766 * HandleFinishedFrame. We could consider reducing complexity, and making this
767 * function return only "finishedInInterpreter", and always using the full VM
768 * machinery to fully finish frames.
771 FrameIsFinished(JSContext *cx)
773 JSOp op = JSOp(*cx->regs->pc);
774 return (op == JSOP_RETURN ||
775 op == JSOP_RETRVAL ||
778 : cx->fp()->finishedInInterpreter();
782 /* Simulate an inline_return by advancing the pc. */
784 AdvanceReturnPC(JSContext *cx)
786 JS_ASSERT(*cx->regs->pc == JSOP_CALL ||
787 *cx->regs->pc == JSOP_NEW ||
788 *cx->regs->pc == JSOP_EVAL ||
789 *cx->regs->pc == JSOP_FUNCALL ||
790 *cx->regs->pc == JSOP_FUNAPPLY);
791 cx->regs->pc += JSOP_CALL_LENGTH;
796 * Given a frame that is about to return, make sure its return value and
797 * activation objects are fixed up. Then, pop the frame and advance the
798 * current PC. Note that while we could enter the JIT at this point, the
799 * logic would still be necessary for the interpreter, so it's easier
800 * (and faster) to finish frames in C++ even if at a safe point here.
803 HandleFinishedFrame(VMFrame &f, JSStackFrame *entryFrame)
805 JSContext *cx = f.cx;
807 JS_ASSERT(FrameIsFinished(cx));
810 * This is the most difficult and complicated piece of the tracer
811 * integration, and historically has been very buggy. The problem is that
812 * although this frame has to be popped (see RemoveExcessFrames), it may
813 * be at a JSOP_RETURN opcode, and it might not have ever been executed.
814 * That is, fp->rval may not be set to the top of the stack, and if it
815 * has, the stack has already been decremented. Note that fp->rval is not
816 * the only problem: the epilogue may never have been executed.
818 * Here are the edge cases and whether the frame has been exited cleanly:
819 * 1. No: A trace exited directly before a RETURN op, and the
820 * interpreter never ran.
821 * 2. Yes: The interpreter exited cleanly.
822 * 3. No: The interpreter exited on a safe point. LEAVE_ON_SAFE_POINT
823 * is not used in between JSOP_RETURN and advancing the PC,
824 * therefore, it cannot have been run if at a safe point.
825 * 4. No: Somewhere in the RunTracer call tree, we removed a frame,
826 * and we returned to a JSOP_RETURN opcode. Note carefully
827 * that in this situation, FrameIsFinished() returns true!
828 * 5. Yes: The function exited in the method JIT, during
829 * FinishExcessFrames() However, in this case, we'll never enter
830 * HandleFinishedFrame(): we always immediately pop JIT'd frames.
832 * Since the only scenario where this fixup is NOT needed is a normal exit
833 * from the interpreter, we can cleanly check for this scenario by checking
834 * a bit it sets in the frame.
836 bool returnOK = true;
837 if (!cx->fp()->finishedInInterpreter()) {
838 if (JSOp(*cx->regs->pc) == JSOP_RETURN)
839 cx->fp()->setReturnValue(f.regs.sp[-1]);
841 returnOK = ScriptEpilogue(cx, cx->fp(), true);
844 JS_ASSERT_IF(cx->fp()->isFunctionFrame() &&
845 !cx->fp()->isEvalFrame(),
846 !cx->fp()->hasCallObj());
848 if (cx->fp() != entryFrame) {
857 * Given a frame newer than the entry frame, try to finish it. If it's at a
858 * return position, pop the frame. If it's at a safe point, execute it in
859 * Jaeger code. Otherwise, try to interpret until a safe point.
861 * While this function is guaranteed to make progress, it may not actually
862 * finish or pop the current frame. It can either:
863 * 1) Finalize a finished frame, or
864 * 2) Finish and finalize the frame in the Method JIT, or
865 * 3) Interpret, which can:
866 * a) Propagate an error, or
867 * b) Finish the frame, but not finalize it, or
868 * c) Abruptly leave at any point in the frame, or in a newer frame
869 * pushed by a call, that has method JIT'd code.
872 EvaluateExcessFrame(VMFrame &f, JSStackFrame *entryFrame)
874 JSContext *cx = f.cx;
875 JSStackFrame *fp = cx->fp();
878 * A "finished" frame is when the interpreter rested on a STOP,
879 * RETURN, RETRVAL, etc. We check for finished frames BEFORE looking
880 * for a safe point. If the frame was finished, we could have already
881 * called ScriptEpilogue(), and entering the JIT could call it twice.
883 if (!fp->hasImacropc() && FrameIsFinished(cx))
884 return HandleFinishedFrame(f, entryFrame);
886 if (void *ncode = AtSafePoint(cx)) {
887 if (!JaegerShotAtSafePoint(cx, ncode))
894 return PartialInterpret(f);
898 * Evaluate frames newer than the entry frame until all are gone. This will
899 * always leave f.regs.fp == entryFrame.
902 FinishExcessFrames(VMFrame &f, JSStackFrame *entryFrame)
904 JSContext *cx = f.cx;
906 while (cx->fp() != entryFrame || entryFrame->hasImacropc()) {
907 if (!EvaluateExcessFrame(f, entryFrame)) {
908 if (!HandleErrorInExcessFrame(f, entryFrame))
916 #if defined JS_MONOIC
918 UpdateTraceHintSingle(Repatcher &repatcher, JSC::CodeLocationJump jump, JSC::CodeLocationLabel target)
921 * Hack: The value that will be patched is before the executable address,
922 * so to get protection right, just unprotect the general region around
925 repatcher.relink(jump, target);
927 JaegerSpew(JSpew_PICs, "relinking trace hint %p to %p\n",
928 jump.executableAddress(), target.executableAddress());
932 DisableTraceHint(JITScript *jit, ic::TraceICInfo &ic)
934 Repatcher repatcher(jit);
935 UpdateTraceHintSingle(repatcher, ic.traceHint, ic.jumpTarget);
937 if (ic.hasSlowTraceHint)
938 UpdateTraceHintSingle(repatcher, ic.slowTraceHint, ic.jumpTarget);
942 ResetTraceHintAt(JSScript *script, js::mjit::JITScript *jit,
943 jsbytecode *pc, uint16_t index, bool full)
945 if (index >= jit->nTraceICs)
947 ic::TraceICInfo &ic = jit->traceICs()[index];
951 JS_ASSERT(ic.jumpTargetPC == pc);
953 JaegerSpew(JSpew_PICs, "Enabling trace IC %u in script %p\n", index, script);
955 Repatcher repatcher(jit);
957 UpdateTraceHintSingle(repatcher, ic.traceHint, ic.stubEntry);
959 if (ic.hasSlowTraceHint)
960 UpdateTraceHintSingle(repatcher, ic.slowTraceHint, ic.stubEntry);
964 ic.loopCounterStart = 1;
965 ic.loopCounter = ic.loopCounterStart;
971 js::mjit::ResetTraceHint(JSScript *script, jsbytecode *pc, uint16_t index, bool full)
974 if (script->jitNormal)
975 ResetTraceHintAt(script, script->jitNormal, pc, index, full);
978 ResetTraceHintAt(script, script->jitCtor, pc, index, full);
984 RunTracer(VMFrame &f, ic::TraceICInfo &ic)
987 RunTracer(VMFrame &f)
990 JSContext *cx = f.cx;
991 JSStackFrame *entryFrame = f.fp();
992 TracePointAction tpa;
994 /* :TODO: nuke PIC? */
995 if (!cx->traceJitEnabled)
999 * Force initialization of the entry frame's scope chain and return value,
1000 * if necessary. The tracer can query the scope chain without needing to
1001 * check the HAS_SCOPECHAIN flag, and the frame is guaranteed to have the
1002 * correct return value stored if we trace/interpret through to the end
1005 entryFrame->scopeChain();
1006 entryFrame->returnValue();
1009 uintN inlineCallCount = 0;
1012 uint32 *loopCounter;
1015 traceData = &ic.traceData;
1016 traceEpoch = &ic.traceEpoch;
1017 loopCounter = &ic.loopCounter;
1019 hits = ic.loopCounterStart;
1026 tpa = MonitorTracePoint(f.cx, inlineCallCount, &blacklist, traceData, traceEpoch,
1028 JS_ASSERT(!TRACE_RECORDER(cx));
1031 ic.loopCounterStart = *loopCounter;
1033 DisableTraceHint(entryFrame->jit(), ic);
1036 // Even though ExecuteTree() bypasses the interpreter, it should propagate
1037 // error failures correctly.
1038 JS_ASSERT_IF(cx->isExceptionPending(), tpa == TPA_Error);
1041 JS_ASSERT(f.fp() == cx->fp());
1047 if (!HandleErrorInExcessFrame(f, entryFrame, f.fp()->finishedInInterpreter()))
1049 JS_ASSERT(!cx->fp()->hasImacropc());
1058 * The tracer could have dropped us off on any frame at any position.
1059 * Well, it could not have removed frames (recursion is disabled).
1061 * Frames after the entryFrame cannot be entered via JaegerShotAtSafePoint()
1062 * unless each is at a safe point. We can JaegerShotAtSafePoint these
1063 * frames individually, but we must unwind to the entryFrame.
1065 * Note carefully that JaegerShotAtSafePoint can resume methods at
1066 * arbitrary safe points whereas JaegerShot cannot.
1068 * If we land on entryFrame without a safe point in sight, we'll end up
1069 * at the RETURN op. This is an edge case with two paths:
1071 * 1) The entryFrame is the last inline frame. If it fell on a RETURN,
1072 * move the return value down.
1073 * 2) The entryFrame is NOT the last inline frame. Pop the frame.
1075 * In both cases, we hijack the stub to return to InjectJaegerReturn. This
1076 * moves |oldFp->rval| into the scripted return registers.
1080 /* Step 1. Finish frames created after the entry frame. */
1081 if (!FinishExcessFrames(f, entryFrame))
1084 /* IMacros are guaranteed to have been removed by now. */
1085 JS_ASSERT(f.fp() == entryFrame);
1086 JS_ASSERT(!entryFrame->hasImacropc());
1088 /* Step 2. If entryFrame is done, use a special path to return to EnterMethodJIT(). */
1089 if (FrameIsFinished(cx)) {
1090 if (!HandleFinishedFrame(f, entryFrame))
1093 void *retPtr = JS_FUNC_TO_DATA_PTR(void *, InjectJaegerReturn);
1094 *f.returnAddressLocation() = retPtr;
1098 /* Step 3. If entryFrame is at a safe point, just leave. */
1099 if (void *ncode = AtSafePoint(cx))
1102 /* Step 4. Do a partial interp, then restart the whole process. */
1103 if (!PartialInterpret(f)) {
1104 if (!HandleErrorInExcessFrame(f, entryFrame))
1111 #endif /* JS_TRACER */
1113 #if defined JS_TRACER
1114 # if defined JS_MONOIC
1116 stubs::InvokeTracer(VMFrame &f, ic::TraceICInfo *ic)
1118 return RunTracer(f, *ic);
1124 stubs::InvokeTracer(VMFrame &f)
1126 return RunTracer(f);
1128 # endif /* JS_MONOIC */
1129 #endif /* JS_TRACER */