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"
52 #include "assembler/assembler/RepatchBuffer.h"
55 #include "methodjit/StubCalls.h"
57 #include "jspropertycache.h"
58 #include "methodjit/MonoIC.h"
59 #include "jsanalyze.h"
61 #include "jsinterpinlines.h"
62 #include "jspropertycacheinlines.h"
63 #include "jsscopeinlines.h"
64 #include "jsscriptinlines.h"
65 #include "jsstrinlines.h"
66 #include "jsobjinlines.h"
67 #include "jscntxtinlines.h"
68 #include "jsatominlines.h"
69 #include "StubCalls-inl.h"
71 #include "jsautooplen.h"
74 using namespace js::mjit
;
78 FindExceptionHandler(JSContext
*cx
)
80 JSStackFrame
*fp
= cx
->fp();
81 JSScript
*script
= fp
->script();
84 if (cx
->throwing
&& script
->trynotesOffset
) {
85 // The PC is updated before every stub call, so we can use it here.
86 unsigned offset
= cx
->regs
->pc
- script
->main
;
88 JSTryNoteArray
*tnarray
= script
->trynotes();
89 for (unsigned i
= 0; i
< tnarray
->length
; ++i
) {
90 JSTryNote
*tn
= &tnarray
->vector
[i
];
91 JS_ASSERT(offset
< script
->length
);
92 // The following if condition actually tests two separate conditions:
93 // (1) offset - tn->start >= tn->length
94 // means the PC is not in the range of this try note, so we
95 // should continue searching, after considering:
96 // (2) offset - tn->start == tn->length
97 // means the PC is at the first op of the exception handler
98 // for this try note. This happens when an exception is thrown
99 // during recording: the interpreter sets the PC to the handler
100 // and then exits. In this case, we are in fact at the right
101 // exception handler.
103 // Hypothetically, the op we are at might have thrown an
104 // exception, in which case this would not be the right handler.
105 // But the first ops of exception handlers generated by our
106 // bytecode compiler cannot throw, so this is not possible.
107 if (offset
- tn
->start
> tn
->length
)
109 if (tn
->stackDepth
> cx
->regs
->sp
- fp
->base())
112 jsbytecode
*pc
= script
->main
+ tn
->start
+ tn
->length
;
113 JSBool ok
= js_UnwindScope(cx
, tn
->stackDepth
, JS_TRUE
);
114 JS_ASSERT(cx
->regs
->sp
== fp
->base() + tn
->stackDepth
);
118 JS_ASSERT(js_GetOpcode(cx
, fp
->script(), pc
) == JSOP_ENTERBLOCK
);
120 #if JS_HAS_GENERATORS
121 /* Catch cannot intercept the closing of a generator. */
122 if (JS_UNLIKELY(cx
->exception
.isMagic(JS_GENERATOR_CLOSING
)))
127 * Don't clear cx->throwing to save cx->exception from GC
128 * until it is pushed to the stack via [exception] in the
135 * Push (true, exception) pair for finally to indicate that
136 * [retsub] should rethrow the exception.
138 cx
->regs
->sp
[0].setBoolean(true);
139 cx
->regs
->sp
[1] = cx
->exception
;
141 cx
->throwing
= JS_FALSE
;
147 * This is similar to JSOP_ENDITER in the interpreter loop,
148 * except the code now uses the stack slot normally used by
149 * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
150 * adjustment and regs.sp[1] after, to save and restore the
153 AutoValueRooter
tvr(cx
, cx
->exception
);
154 JS_ASSERT(js_GetOpcode(cx
, fp
->script(), pc
) == JSOP_ENDITER
);
155 cx
->throwing
= JS_FALSE
;
156 ok
= !!js_CloseIterator(cx
, &cx
->regs
->sp
[-1].toObject());
160 cx
->throwing
= JS_TRUE
;
161 cx
->exception
= tvr
.value();
171 * Clean up a frame and return. popFrame indicates whether to additionally pop
172 * the frame and store the return value on the caller's stack. The frame will
173 * normally be popped by the caller on return from a call into JIT code,
174 * so must be popped here when that caller code will not execute. This can be
175 * either because of a call into an un-JITable script, or because the call is
176 * throwing an exception.
179 InlineReturn(VMFrame
&f
)
181 JSContext
*cx
= f
.cx
;
182 JSStackFrame
*fp
= f
.regs
.fp
;
184 JS_ASSERT(f
.fp() != f
.entryFp
);
186 JS_ASSERT(!js_IsActiveWithOrBlock(cx
, &fp
->scopeChain(), 0));
188 Value
*newsp
= fp
->actualArgs() - 1;
189 newsp
[-1] = fp
->returnValue();
190 cx
->stack().popInlineFrame(cx
, fp
->prev(), newsp
);
194 stubs::SlowCall(VMFrame
&f
, uint32 argc
)
196 Value
*vp
= f
.regs
.sp
- (argc
+ 2);
198 if (!Invoke(f
.cx
, InvokeArgsAlreadyOnTheStack(vp
, argc
), 0))
203 stubs::SlowNew(VMFrame
&f
, uint32 argc
)
205 JSContext
*cx
= f
.cx
;
206 Value
*vp
= f
.regs
.sp
- (argc
+ 2);
208 if (!InvokeConstructor(cx
, InvokeArgsAlreadyOnTheStack(vp
, argc
)))
213 * This function must only be called after the early prologue, since it depends
217 RemovePartialFrame(JSContext
*cx
, JSStackFrame
*fp
)
219 JSStackFrame
*prev
= fp
->prev();
220 Value
*newsp
= (Value
*)fp
;
221 cx
->stack().popInlineFrame(cx
, prev
, newsp
);
225 * HitStackQuota is called after the early prologue pushing the new frame would
226 * overflow f.stackLimit.
229 stubs::HitStackQuota(VMFrame
&f
)
231 /* Include space to push another frame. */
232 uintN nvals
= f
.fp()->script()->nslots
+ VALUES_PER_STACK_FRAME
;
233 JS_ASSERT(f
.regs
.sp
== f
.fp()->base());
234 if (f
.cx
->stack().bumpCommitAndLimit(f
.entryFp
, f
.regs
.sp
, nvals
, &f
.stackLimit
))
237 /* Remove the current partially-constructed frame before throwing. */
238 RemovePartialFrame(f
.cx
, f
.fp());
239 js_ReportOverRecursed(f
.cx
);
244 * This function must only be called after the early prologue, since it depends
248 stubs::FixupArity(VMFrame
&f
, uint32 nactual
)
250 JSContext
*cx
= f
.cx
;
251 JSStackFrame
*oldfp
= f
.fp();
253 JS_ASSERT(nactual
!= oldfp
->numFormalArgs());
256 * Grossssss! *move* the stack frame. If this ends up being perf-critical,
257 * we can figure out how to spot-optimize it. Be careful to touch only the
258 * members that have been initialized by initCallFrameCallerHalf and the
261 uint32 flags
= oldfp
->isConstructingFlag();
262 JSFunction
*fun
= oldfp
->fun();
263 void *ncode
= oldfp
->nativeReturnAddress();
265 /* Pop the inline frame. */
266 f
.fp() = oldfp
->prev();
267 f
.regs
.sp
= (Value
*) oldfp
;
269 /* Reserve enough space for a callee frame. */
270 JSStackFrame
*newfp
= cx
->stack().getInlineFrameWithinLimit(cx
, (Value
*) oldfp
, nactual
,
271 fun
, fun
->script(), &flags
,
272 f
.entryFp
, &f
.stackLimit
);
276 /* Reset the part of the stack frame set by the caller. */
277 newfp
->initCallFrameCallerHalf(cx
, nactual
, flags
);
279 /* Reset the part of the stack frame set by the prologue up to now. */
280 newfp
->initCallFrameEarlyPrologue(fun
, ncode
);
282 /* The caller takes care of assigning fp to regs. */
287 stubs::CompileFunction(VMFrame
&f
, uint32 nactual
)
290 * We have a partially constructed frame. That's not really good enough to
291 * compile though because we could throw, so get a full, adjusted frame.
293 JSContext
*cx
= f
.cx
;
294 JSStackFrame
*fp
= f
.fp();
297 * Since we can only use members set by initCallFrameCallerHalf,
298 * we must carefully extract the callee from the nactual.
300 JSObject
&callee
= fp
->formalArgsEnd()[-(int(nactual
) + 2)].toObject();
301 JSFunction
*fun
= callee
.getFunctionPrivate();
302 JSScript
*script
= fun
->script();
305 * FixupArity/RemovePartialFrame expect to be called after the early
306 * prologue. Pass the existing value for ncode, it has already been set
307 * by the jit code calling into this stub.
309 fp
->initCallFrameEarlyPrologue(fun
, fp
->nativeReturnAddress());
311 /* Empty script does nothing. */
312 bool callingNew
= fp
->isConstructing();
313 if (script
->isEmpty()) {
314 RemovePartialFrame(cx
, fp
);
315 Value
*vp
= f
.regs
.sp
- (nactual
+ 2);
319 vp
[0].setUndefined();
323 if (nactual
!= fp
->numFormalArgs()) {
324 fp
= (JSStackFrame
*)FixupArity(f
, nactual
);
329 /* Finish frame initialization. */
330 fp
->initCallFrameLatePrologue();
332 /* These would have been initialized by the prologue. */
334 f
.regs
.sp
= fp
->base();
335 f
.regs
.pc
= script
->code
;
337 if (fun
->isHeavyweight() && !js_GetCallObject(cx
, fp
))
340 CompileStatus status
= CanMethodJIT(cx
, script
, fp
);
341 if (status
== Compile_Okay
)
342 return script
->getJIT(callingNew
)->invokeEntry
;
344 /* Function did not compile... interpret it. */
345 JSBool ok
= Interpret(cx
, fp
);
355 UncachedInlineCall(VMFrame
&f
, uint32 flags
, void **pret
, uint32 argc
)
357 JSContext
*cx
= f
.cx
;
358 Value
*vp
= f
.regs
.sp
- (argc
+ 2);
359 JSObject
&callee
= vp
->toObject();
360 JSFunction
*newfun
= callee
.getFunctionPrivate();
361 JSScript
*newscript
= newfun
->script();
363 /* Get pointer to new frame/slots, prepare arguments. */
364 StackSpace
&stack
= cx
->stack();
365 JSStackFrame
*newfp
= stack
.getInlineFrameWithinLimit(cx
, f
.regs
.sp
, argc
,
366 newfun
, newscript
, &flags
,
367 f
.entryFp
, &f
.stackLimit
);
368 if (JS_UNLIKELY(!newfp
))
370 JS_ASSERT_IF(!vp
[1].isPrimitive() && !(flags
& JSFRAME_CONSTRUCTING
),
371 IsSaneThisObject(vp
[1].toObject()));
373 /* Initialize frame, locals. */
374 newfp
->initCallFrame(cx
, callee
, newfun
, argc
, flags
);
375 SetValueRangeToUndefined(newfp
->slots(), newscript
->nfixed
);
377 /* Officially push the frame. */
378 stack
.pushInlineFrame(cx
, newscript
, newfp
, &f
.regs
);
379 JS_ASSERT(newfp
== f
.regs
.fp
);
381 /* Scope with a call object parented by callee's parent. */
382 if (newfun
->isHeavyweight() && !js_GetCallObject(cx
, newfp
))
385 /* Try to compile if not already compiled. */
386 if (newscript
->getJITStatus(newfp
->isConstructing()) == JITScript_None
) {
387 if (mjit::TryCompile(cx
, newfp
) == Compile_Error
) {
388 /* A runtime exception was thrown, get out. */
394 /* If newscript was successfully compiled, run it. */
395 if (JITScript
*jit
= newscript
->getJIT(newfp
->isConstructing())) {
396 *pret
= jit
->invokeEntry
;
400 /* Otherwise, run newscript in the interpreter. */
401 bool ok
= !!Interpret(cx
, cx
->fp());
409 stubs::UncachedNew(VMFrame
&f
, uint32 argc
)
411 UncachedCallResult ucr
;
412 UncachedNewHelper(f
, argc
, &ucr
);
417 stubs::UncachedNewHelper(VMFrame
&f
, uint32 argc
, UncachedCallResult
*ucr
)
421 JSContext
*cx
= f
.cx
;
422 Value
*vp
= f
.regs
.sp
- (argc
+ 2);
424 /* Try to do a fast inline call before the general Invoke path. */
425 if (IsFunctionObject(*vp
, &ucr
->fun
) && ucr
->fun
->isInterpreted() &&
426 !ucr
->fun
->script()->isEmpty())
428 ucr
->callee
= &vp
->toObject();
429 if (!UncachedInlineCall(f
, JSFRAME_CONSTRUCTING
, &ucr
->codeAddr
, argc
))
432 if (!InvokeConstructor(cx
, InvokeArgsAlreadyOnTheStack(vp
, argc
)))
438 stubs::UncachedCall(VMFrame
&f
, uint32 argc
)
440 UncachedCallResult ucr
;
441 UncachedCallHelper(f
, argc
, &ucr
);
446 stubs::UncachedCallHelper(VMFrame
&f
, uint32 argc
, UncachedCallResult
*ucr
)
450 JSContext
*cx
= f
.cx
;
451 Value
*vp
= f
.regs
.sp
- (argc
+ 2);
453 if (IsFunctionObject(*vp
, &ucr
->callee
)) {
454 ucr
->callee
= &vp
->toObject();
455 ucr
->fun
= GET_FUNCTION_PRIVATE(cx
, ucr
->callee
);
457 if (ucr
->fun
->isInterpreted()) {
458 if (ucr
->fun
->u
.i
.script
->isEmpty()) {
464 if (!UncachedInlineCall(f
, 0, &ucr
->codeAddr
, argc
))
469 if (ucr
->fun
->isNative()) {
470 if (!CallJSNative(cx
, ucr
->fun
->u
.n
.native
, argc
, vp
))
476 if (!Invoke(f
.cx
, InvokeArgsAlreadyOnTheStack(vp
, argc
), 0))
483 stubs::PutCallObject(VMFrame
&f
)
485 JS_ASSERT(f
.fp()->hasCallObj());
486 js_PutCallObject(f
.cx
, f
.fp());
490 stubs::PutActivationObjects(VMFrame
&f
)
492 JS_ASSERT(f
.fp()->hasCallObj() || f
.fp()->hasArgsObj());
493 js::PutActivationObjects(f
.cx
, f
.fp());
497 js_InternalThrow(VMFrame
&f
)
499 JSContext
*cx
= f
.cx
;
501 // Make sure sp is up to date.
502 JS_ASSERT(cx
->regs
== &f
.regs
);
504 // Call the throw hook if necessary
505 JSThrowHook handler
= f
.cx
->debugHooks
->throwHook
;
508 switch (handler(cx
, cx
->fp()->script(), cx
->regs
->pc
, Jsvalify(&rval
),
509 cx
->debugHooks
->throwHookData
)) {
511 cx
->throwing
= JS_FALSE
;
515 cx
->throwing
= JS_FALSE
;
516 cx
->fp()->setReturnValue(rval
);
517 return JS_FUNC_TO_DATA_PTR(void *,
518 JS_METHODJIT_DATA(cx
).trampolines
.forceReturn
);
521 cx
->exception
= rval
;
529 jsbytecode
*pc
= NULL
;
531 pc
= FindExceptionHandler(cx
);
535 // If on the 'topmost' frame (where topmost means the first frame
536 // called into through js_Interpret). In this case, we still unwind,
537 // but we shouldn't return from a JS function, because we're not in a
539 bool lastFrame
= (f
.entryFp
== f
.fp());
540 js_UnwindScope(cx
, 0, cx
->throwing
);
542 // For consistency with Interpret(), always run the script epilogue.
543 // This simplifies interactions with RunTracer(), since it can assume
544 // no matter how a function exited (error or not), that the epilogue
545 // does not need to be run.
546 ScriptEpilogue(f
.cx
, f
.fp(), false);
551 JS_ASSERT(f
.regs
.sp
== cx
->regs
->sp
);
555 JS_ASSERT(f
.regs
.sp
== cx
->regs
->sp
);
560 JSStackFrame
*fp
= cx
->fp();
561 JSScript
*script
= fp
->script();
562 return script
->nativeCodeForPC(fp
->isConstructing(), pc
);
566 stubs::GetCallObject(VMFrame
&f
)
568 JS_ASSERT(f
.fp()->fun()->isHeavyweight());
569 if (!js_GetCallObject(f
.cx
, f
.fp()))
574 stubs::CreateThis(VMFrame
&f
, JSObject
*proto
)
576 JSContext
*cx
= f
.cx
;
577 JSStackFrame
*fp
= f
.fp();
578 JSObject
*callee
= &fp
->callee();
579 JSObject
*obj
= js_CreateThisForFunctionWithProto(cx
, callee
, proto
);
582 fp
->formalArgs()[-1].setObject(*obj
);
586 stubs::EnterScript(VMFrame
&f
)
588 JSStackFrame
*fp
= f
.fp();
589 JSContext
*cx
= f
.cx
;
590 JSInterpreterHook hook
= cx
->debugHooks
->callHook
;
591 if (JS_UNLIKELY(hook
!= NULL
) && !fp
->isExecuteFrame()) {
592 fp
->setHookData(hook(cx
, fp
, JS_TRUE
, 0, cx
->debugHooks
->callHookData
));
595 Probes::enterJSFun(cx
, fp
->maybeFun());
599 stubs::LeaveScript(VMFrame
&f
)
601 JSStackFrame
*fp
= f
.fp();
602 JSContext
*cx
= f
.cx
;
603 Probes::exitJSFun(cx
, fp
->maybeFun());
604 JSInterpreterHook hook
= cx
->debugHooks
->callHook
;
606 if (hook
&& fp
->hasHookData() && !fp
->isExecuteFrame()) {
608 hook(cx
, fp
, JS_FALSE
, &ok
, fp
->hookData());
617 * Called when an error is in progress and the topmost frame could not handle
618 * it. This will unwind to a given frame, or find and align to an exception
619 * handler in the process.
622 HandleErrorInExcessFrame(VMFrame
&f
, JSStackFrame
*stopFp
, bool searchedTopmostFrame
= true)
624 JSContext
*cx
= f
.cx
;
627 * Callers of this called either Interpret() or JaegerShot(), which would
628 * have searched for exception handlers already. If we see stopFp, just
629 * return false. Otherwise, pop the frame, since it's guaranteed useless.
631 * Note that this also guarantees ScriptEpilogue() has been called.
633 JSStackFrame
*fp
= cx
->fp();
634 if (searchedTopmostFrame
) {
641 /* Remove the bottom frame. */
642 bool returnOK
= false;
647 if (fp
->hasImacropc()) {
648 cx
->regs
->pc
= fp
->imacropc();
651 JS_ASSERT(!fp
->hasImacropc());
653 /* If there's an exception and a handler, set the pc and leave. */
655 jsbytecode
*pc
= FindExceptionHandler(cx
);
663 /* Don't unwind if this was the entry frame. */
667 /* Unwind and return. */
668 returnOK
&= bool(js_UnwindScope(cx
, 0, returnOK
|| cx
->throwing
));
669 returnOK
= ScriptEpilogue(cx
, fp
, returnOK
);
673 JS_ASSERT(&f
.regs
== cx
->regs
);
674 JS_ASSERT_IF(!returnOK
, cx
->fp() == stopFp
);
679 /* Returns whether the current PC has method JIT'd code. */
681 AtSafePoint(JSContext
*cx
)
683 JSStackFrame
*fp
= cx
->fp();
684 if (fp
->hasImacropc())
687 JSScript
*script
= fp
->script();
688 return script
->maybeNativeCodeForPC(fp
->isConstructing(), cx
->regs
->pc
);
692 * Interprets until either a safe point is reached that has method JIT'd
693 * code, or the current frame tries to return.
696 PartialInterpret(VMFrame
&f
)
698 JSContext
*cx
= f
.cx
;
699 JSStackFrame
*fp
= cx
->fp();
702 JSScript
*script
= fp
->script();
703 JS_ASSERT(!fp
->finishedInInterpreter());
704 JS_ASSERT(fp
->hasImacropc() ||
705 !script
->maybeNativeCodeForPC(fp
->isConstructing(), cx
->regs
->pc
));
709 ok
= Interpret(cx
, fp
, 0, JSINTERP_SAFEPOINT
);
714 JS_STATIC_ASSERT(JSOP_NOP
== 0);
716 /* Returns whether the current PC would return, popping the frame. */
718 FrameIsFinished(JSContext
*cx
)
720 JSOp op
= JSOp(*cx
->regs
->pc
);
721 return (op
== JSOP_RETURN
||
722 op
== JSOP_RETRVAL
||
729 /* Simulate an inline_return by advancing the pc. */
731 AdvanceReturnPC(JSContext
*cx
)
733 JS_ASSERT(*cx
->regs
->pc
== JSOP_CALL
||
734 *cx
->regs
->pc
== JSOP_NEW
||
735 *cx
->regs
->pc
== JSOP_EVAL
||
736 *cx
->regs
->pc
== JSOP_APPLY
);
737 cx
->regs
->pc
+= JSOP_CALL_LENGTH
;
742 * Given a frame that is about to return, make sure its return value and
743 * activation objects are fixed up. Then, pop the frame and advance the
744 * current PC. Note that while we could enter the JIT at this point, the
745 * logic would still be necessary for the interpreter, so it's easier
746 * (and faster) to finish frames in C++ even if at a safe point here.
749 HandleFinishedFrame(VMFrame
&f
, JSStackFrame
*entryFrame
)
751 JSContext
*cx
= f
.cx
;
753 JS_ASSERT(FrameIsFinished(cx
));
756 * This is the most difficult and complicated piece of the tracer
757 * integration, and historically has been very buggy. The problem is that
758 * although this frame has to be popped (see RemoveExcessFrames), it may
759 * be at a JSOP_RETURN opcode, and it might not have ever been executed.
760 * That is, fp->rval may not be set to the top of the stack, and if it
761 * has, the stack has already been decremented. Note that fp->rval is not
762 * the only problem: the epilogue may never have been executed.
764 * Here are the edge cases and whether the frame has been exited cleanly:
765 * 1. No: A trace exited directly before a RETURN op, and the
766 * interpreter never ran.
767 * 2. Yes: The interpreter exited cleanly.
768 * 3. No: The interpreter exited on a safe point. LEAVE_ON_SAFE_POINT
769 * is not used in between JSOP_RETURN and advancing the PC,
770 * therefore, it cannot have been run if at a safe point.
771 * 4. No: Somewhere in the RunTracer call tree, we removed a frame,
772 * and we returned to a JSOP_RETURN opcode. Note carefully
773 * that in this situation, FrameIsFinished() returns true!
774 * 5. Yes: The function exited in the method JIT. However, in this
775 * case, we'll never enter HandleFinishedFrame(): we always
776 * immediately pop JIT'd frames.
778 * Since the only scenario where this fixup is NOT needed is a normal exit
779 * from the interpreter, we can cleanly check for this scenario by checking
780 * a bit it sets in the frame.
782 bool returnOK
= true;
783 if (!cx
->fp()->finishedInInterpreter()) {
784 if (JSOp(*cx
->regs
->pc
) == JSOP_RETURN
)
785 cx
->fp()->setReturnValue(f
.regs
.sp
[-1]);
787 returnOK
= ScriptEpilogue(cx
, cx
->fp(), true);
790 JS_ASSERT_IF(cx
->fp()->isFunctionFrame() &&
791 !cx
->fp()->isEvalFrame(),
792 !cx
->fp()->hasCallObj());
794 if (cx
->fp() != entryFrame
) {
803 * Given a frame newer than the entry frame, try to finish it. If it's at a
804 * return position, pop the frame. If it's at a safe point, execute it in
805 * Jaeger code. Otherwise, try to interpret until a safe point.
807 * While this function is guaranteed to make progress, it may not actually
808 * finish or pop the current frame. It can either:
809 * 1) Finalize a finished frame, or
810 * 2) Finish and finalize the frame in the Method JIT, or
811 * 3) Interpret, which can:
812 * a) Propagate an error, or
813 * b) Finish the frame, but not finalize it, or
814 * c) Abruptly leave at any point in the frame, or in a newer frame
815 * pushed by a call, that has method JIT'd code.
818 EvaluateExcessFrame(VMFrame
&f
, JSStackFrame
*entryFrame
)
820 JSContext
*cx
= f
.cx
;
821 JSStackFrame
*fp
= cx
->fp();
824 * A "finished" frame is when the interpreter rested on a STOP,
825 * RETURN, RETRVAL, etc. We check for finished frames BEFORE looking
826 * for a safe point. If the frame was finished, we could have already
827 * called ScriptEpilogue(), and entering the JIT could call it twice.
829 if (!fp
->hasImacropc() && FrameIsFinished(cx
))
830 return HandleFinishedFrame(f
, entryFrame
);
832 if (void *ncode
= AtSafePoint(cx
)) {
833 if (!JaegerShotAtSafePoint(cx
, ncode
))
840 return PartialInterpret(f
);
844 * Evaluate frames newer than the entry frame until all are gone. This will
845 * always leave f.regs.fp == entryFrame.
848 FinishExcessFrames(VMFrame
&f
, JSStackFrame
*entryFrame
)
850 JSContext
*cx
= f
.cx
;
852 while (cx
->fp() != entryFrame
|| entryFrame
->hasImacropc()) {
853 if (!EvaluateExcessFrame(f
, entryFrame
)) {
854 if (!HandleErrorInExcessFrame(f
, entryFrame
))
864 UpdateTraceHintSingle(JSC::CodeLocationJump jump
, JSC::CodeLocationLabel target
)
867 * Hack: The value that will be patched is before the executable address,
868 * so to get protection right, just unprotect the general region around
871 uint8
*addr
= (uint8
*)(jump
.executableAddress());
872 JSC::RepatchBuffer
repatch(addr
- 64, 128);
873 repatch
.relink(jump
, target
);
875 JaegerSpew(JSpew_PICs
, "relinking trace hint %p to %p\n",
876 jump
.executableAddress(), target
.executableAddress());
880 DisableTraceHint(VMFrame
&f
, ic::TraceICInfo
&tic
)
882 UpdateTraceHintSingle(tic
.traceHint
, tic
.jumpTarget
);
884 if (tic
.hasSlowTraceHint
)
885 UpdateTraceHintSingle(tic
.slowTraceHint
, tic
.jumpTarget
);
889 EnableTraceHintAt(JSScript
*script
, js::mjit::JITScript
*jit
, jsbytecode
*pc
, uint16_t index
)
891 JS_ASSERT(index
< jit
->nTraceICs
);
892 ic::TraceICInfo
&tic
= jit
->traceICs
[index
];
894 JS_ASSERT(tic
.jumpTargetPC
== pc
);
896 JaegerSpew(JSpew_PICs
, "Enabling trace IC %u in script %p\n", index
, script
);
898 UpdateTraceHintSingle(tic
.traceHint
, tic
.stubEntry
);
900 if (tic
.hasSlowTraceHint
)
901 UpdateTraceHintSingle(tic
.slowTraceHint
, tic
.stubEntry
);
906 js::mjit::EnableTraceHint(JSScript
*script
, jsbytecode
*pc
, uint16_t index
)
909 if (script
->jitNormal
)
910 EnableTraceHintAt(script
, script
->jitNormal
, pc
, index
);
913 EnableTraceHintAt(script
, script
->jitCtor
, pc
, index
);
919 RunTracer(VMFrame
&f
, ic::TraceICInfo
&tic
)
922 RunTracer(VMFrame
&f
)
925 JSContext
*cx
= f
.cx
;
926 JSStackFrame
*entryFrame
= f
.fp();
927 TracePointAction tpa
;
929 /* :TODO: nuke PIC? */
930 if (!cx
->traceJitEnabled
)
934 * Force initialization of the entry frame's scope chain and return value,
935 * if necessary. The tracer can query the scope chain without needing to
936 * check the HAS_SCOPECHAIN flag, and the frame is guaranteed to have the
937 * correct return value stored if we trace/interpret through to the end
940 entryFrame
->scopeChain();
941 entryFrame
->returnValue();
944 uintN inlineCallCount
= 0;
948 traceData
= &tic
.traceData
;
949 traceEpoch
= &tic
.traceEpoch
;
954 tpa
= MonitorTracePoint(f
.cx
, inlineCallCount
, &blacklist
, traceData
, traceEpoch
);
955 JS_ASSERT(!TRACE_RECORDER(cx
));
959 DisableTraceHint(f
, tic
);
962 // Even though ExecuteTree() bypasses the interpreter, it should propagate
963 // error failures correctly.
964 JS_ASSERT_IF(cx
->throwing
, tpa
== TPA_Error
);
967 JS_ASSERT(f
.fp() == cx
->fp());
973 if (!HandleErrorInExcessFrame(f
, entryFrame
, f
.fp()->finishedInInterpreter()))
975 JS_ASSERT(!cx
->fp()->hasImacropc());
984 * The tracer could have dropped us off on any frame at any position.
985 * Well, it could not have removed frames (recursion is disabled).
987 * Frames after the entryFrame cannot be entered via JaegerShotAtSafePoint()
988 * unless each is at a safe point. We can JaegerShotAtSafePoint these
989 * frames individually, but we must unwind to the entryFrame.
991 * Note carefully that JaegerShotAtSafePoint can resume methods at
992 * arbitrary safe points whereas JaegerShot cannot.
994 * If we land on entryFrame without a safe point in sight, we'll end up
995 * at the RETURN op. This is an edge case with two paths:
997 * 1) The entryFrame is the last inline frame. If it fell on a RETURN,
998 * move the return value down.
999 * 2) The entryFrame is NOT the last inline frame. Pop the frame.
1001 * In both cases, we hijack the stub to return to InjectJaegerReturn. This
1002 * moves |oldFp->rval| into the scripted return registers.
1006 /* Step 1. Finish frames created after the entry frame. */
1007 if (!FinishExcessFrames(f
, entryFrame
))
1010 /* IMacros are guaranteed to have been removed by now. */
1011 JS_ASSERT(f
.fp() == entryFrame
);
1012 JS_ASSERT(!entryFrame
->hasImacropc());
1014 /* Step 2. If entryFrame is done, use a special path to return to EnterMethodJIT(). */
1015 if (FrameIsFinished(cx
)) {
1016 if (!HandleFinishedFrame(f
, entryFrame
))
1019 void *retPtr
= JS_FUNC_TO_DATA_PTR(void *, InjectJaegerReturn
);
1020 *f
.returnAddressLocation() = retPtr
;
1024 /* Step 3. If entryFrame is at a safe point, just leave. */
1025 if (void *ncode
= AtSafePoint(cx
))
1028 /* Step 4. Do a partial interp, then restart the whole process. */
1029 if (!PartialInterpret(f
)) {
1030 if (!HandleErrorInExcessFrame(f
, entryFrame
))
1037 #endif /* JS_TRACER */
1039 #if defined JS_TRACER
1040 # if defined JS_MONOIC
1042 stubs::InvokeTracer(VMFrame
&f
, ic::TraceICInfo
*tic
)
1044 return RunTracer(f
, *tic
);
1050 stubs::InvokeTracer(VMFrame
&f
)
1052 return RunTracer(f
);
1054 # endif /* JS_MONOIC */
1055 #endif /* JS_TRACER */