ICs for scripted calls (bug 587698, r=dmandelin).
[mozilla-central.git] / js / src / methodjit / InvokeHelpers.cpp
blob3faa7853e60eea5a5b4fc7d67f449cf39f3da464
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
15 * License.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
23 * Contributor(s):
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 ***** */
41 #include "jscntxt.h"
42 #include "jsscope.h"
43 #include "jsobj.h"
44 #include "jslibmath.h"
45 #include "jsiter.h"
46 #include "jsnum.h"
47 #include "jsxml.h"
48 #include "jsstaticcheck.h"
49 #include "jsbool.h"
50 #include "assembler/assembler/MacroAssemblerCodeRef.h"
51 #include "assembler/assembler/CodeLocation.h"
52 #include "assembler/assembler/RepatchBuffer.h"
53 #include "jsiter.h"
54 #include "jstypes.h"
55 #include "methodjit/StubCalls.h"
56 #include "jstracer.h"
57 #include "jspropertycache.h"
58 #include "methodjit/MonoIC.h"
60 #include "jspropertycacheinlines.h"
61 #include "jsscopeinlines.h"
62 #include "jsscriptinlines.h"
63 #include "jsstrinlines.h"
64 #include "jsobjinlines.h"
65 #include "jscntxtinlines.h"
66 #include "jsatominlines.h"
68 #include "jsautooplen.h"
70 using namespace js;
71 using namespace js::mjit;
72 using namespace JSC;
74 #define THROW() \
75 do { \
76 void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
77 *f.returnAddressLocation() = ptr; \
78 return; \
79 } while (0)
81 #define THROWV(v) \
82 do { \
83 void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
84 *f.returnAddressLocation() = ptr; \
85 return v; \
86 } while (0)
88 static bool
89 InlineReturn(VMFrame &f, JSBool ok);
91 static jsbytecode *
92 FindExceptionHandler(JSContext *cx)
94 JSStackFrame *fp = cx->fp();
95 JSScript *script = fp->getScript();
97 top:
98 if (cx->throwing && script->trynotesOffset) {
99 // The PC is updated before every stub call, so we can use it here.
100 unsigned offset = cx->regs->pc - script->main;
102 JSTryNoteArray *tnarray = script->trynotes();
103 for (unsigned i = 0; i < tnarray->length; ++i) {
104 JSTryNote *tn = &tnarray->vector[i];
105 JS_ASSERT(offset < script->length);
106 // The following if condition actually tests two separate conditions:
107 // (1) offset - tn->start >= tn->length
108 // means the PC is not in the range of this try note, so we
109 // should continue searching, after considering:
110 // (2) offset - tn->start == tn->length
111 // means the PC is at the first op of the exception handler
112 // for this try note. This happens when an exception is thrown
113 // during recording: the interpreter sets the PC to the handler
114 // and then exits. In this case, we are in fact at the right
115 // exception handler.
117 // Hypothetically, the op we are at might have thrown an
118 // exception, in which case this would not be the right handler.
119 // But the first ops of exception handlers generated by our
120 // bytecode compiler cannot throw, so this is not possible.
121 if (offset - tn->start > tn->length)
122 continue;
123 if (tn->stackDepth > cx->regs->sp - fp->base())
124 continue;
126 jsbytecode *pc = script->main + tn->start + tn->length;
127 JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
128 JS_ASSERT(cx->regs->sp == fp->base() + tn->stackDepth);
130 switch (tn->kind) {
131 case JSTRY_CATCH:
132 JS_ASSERT(js_GetOpcode(cx, fp->getScript(), pc) == JSOP_ENTERBLOCK);
134 #if JS_HAS_GENERATORS
135 /* Catch cannot intercept the closing of a generator. */
136 if (JS_UNLIKELY(cx->exception.isMagic(JS_GENERATOR_CLOSING)))
137 break;
138 #endif
141 * Don't clear cx->throwing to save cx->exception from GC
142 * until it is pushed to the stack via [exception] in the
143 * catch block.
145 return pc;
147 case JSTRY_FINALLY:
149 * Push (true, exception) pair for finally to indicate that
150 * [retsub] should rethrow the exception.
152 cx->regs->sp[0].setBoolean(true);
153 cx->regs->sp[1] = cx->exception;
154 cx->regs->sp += 2;
155 cx->throwing = JS_FALSE;
156 return pc;
158 case JSTRY_ITER:
161 * This is similar to JSOP_ENDITER in the interpreter loop,
162 * except the code now uses the stack slot normally used by
163 * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
164 * adjustment and regs.sp[1] after, to save and restore the
165 * pending exception.
167 AutoValueRooter tvr(cx, cx->exception);
168 JS_ASSERT(js_GetOpcode(cx, fp->getScript(), pc) == JSOP_ENDITER);
169 cx->throwing = JS_FALSE;
170 ok = !!js_CloseIterator(cx, &cx->regs->sp[-1].toObject());
171 cx->regs->sp -= 1;
172 if (!ok)
173 goto top;
174 cx->throwing = JS_TRUE;
175 cx->exception = tvr.value();
181 return NULL;
184 static bool
185 InlineReturn(VMFrame &f, JSBool ok)
187 JSContext *cx = f.cx;
188 JSStackFrame *fp = f.regs.fp;
190 JS_ASSERT(f.fp() != f.entryFp);
192 JS_ASSERT(!fp->hasBlockChain());
193 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->getScopeChain(), 0));
195 // Marker for debug support.
196 if (JS_UNLIKELY(fp->hasHookData())) {
197 JSInterpreterHook hook;
198 JSBool status;
200 hook = cx->debugHooks->callHook;
201 if (hook) {
203 * Do not pass &ok directly as exposing the address inhibits
204 * optimizations and uninitialised warnings.
206 status = ok;
207 hook(cx, fp, JS_FALSE, &status, fp->getHookData());
208 ok = (status == JS_TRUE);
209 // CHECK_INTERRUPT_HANDLER();
213 fp->putActivationObjects(cx);
215 /* :TODO: version stuff */
217 if (fp->flags & JSFRAME_CONSTRUCTING && fp->getReturnValue().isPrimitive())
218 fp->setReturnValue(fp->getThisValue());
220 Value *newsp = fp->argv - 1;
222 cx->stack().popInlineFrame(cx, fp, fp->down);
224 cx->regs->sp = newsp;
225 cx->regs->sp[-1] = fp->getReturnValue();
227 JS_ASSERT(cx->regs->pc != JSStackFrame::sInvalidPC);
229 return ok;
232 JSBool JS_FASTCALL
233 stubs::NewObject(VMFrame &f, uint32 argc)
235 JSContext *cx = f.cx;
236 Value *vp = f.regs.sp - (argc + 2);
238 JSObject *funobj = &vp[0].toObject();
239 JS_ASSERT(funobj->isFunction());
241 jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
242 if (!funobj->getProperty(cx, id, &vp[1]))
243 THROWV(JS_FALSE);
245 JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL;
246 JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_ObjectClass, proto, funobj->getParent());
247 if (!obj)
248 THROWV(JS_FALSE);
250 vp[1].setObject(*obj);
252 return JS_TRUE;
255 void JS_FASTCALL
256 stubs::SlowCall(VMFrame &f, uint32 argc)
258 Value *vp = f.regs.sp - (argc + 2);
260 if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
261 THROW();
264 void JS_FASTCALL
265 stubs::SlowNew(VMFrame &f, uint32 argc)
267 JSContext *cx = f.cx;
268 Value *vp = f.regs.sp - (argc + 2);
270 if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
271 THROW();
274 static inline void
275 RemovePartialFrame(VMFrame &f)
277 /* Unwind the half-pushed frame. */
278 f.regs.pc = f.fp()->down->savedPC;
279 f.regs.sp = f.fp()->argv + f.fp()->argc;
280 #ifdef DEBUG
281 f.fp()->down->savedPC = JSStackFrame::sInvalidPC;
282 #endif
283 f.regs.fp = f.fp()->down;
286 void JS_FASTCALL
287 stubs::CheckStackQuota(VMFrame &f)
289 if (JS_LIKELY(f.ensureSpace(0, f.fp()->getScript()->nslots)))
290 return;
292 RemovePartialFrame(f);
294 js_ReportOverRecursed(f.cx);
295 THROW();
298 void * JS_FASTCALL
299 stubs::CheckArity(VMFrame &f)
301 JSContext *cx = f.cx;
302 JSStackFrame *fp = f.fp();
303 uint32 argc = fp->argc;
304 JSFunction *fun = fp->getFunction();
306 JS_ASSERT(argc < fun->nargs);
309 * Grossssss! *move* the stack frame. If this ends up being perf-critical,
310 * we can figure out how to spot-optimize it. As the frame shrinks it will
311 * matter less.
313 uint32 flags = fp->flags;
314 JSObject *scopeChain = fp->getScopeChain();
315 Value *argv = fp->argv;
316 JSStackFrame *down = fp->down;
317 void *ncode = fp->ncode;
319 /* Pop the inline frame. */
320 RemovePartialFrame(f);
322 uint32 missing = fun->nargs - argc;
324 /* Include an extra stack frame for callees. */
325 if (!f.ensureSpace(missing, fun->u.i.script->nslots + VALUES_PER_STACK_FRAME)) {
326 js_ReportOverRecursed(cx);
327 THROWV(NULL);
330 #ifdef DEBUG
331 down->savedPC = f.regs.pc;
332 #endif
334 SetValueRangeToUndefined(f.regs.sp, missing);
335 f.regs.sp += missing;
337 JSStackFrame *newfp = (JSStackFrame *)f.regs.sp;
338 newfp->argc = argc;
339 newfp->setFunction(fun);
340 newfp->flags = flags;
341 newfp->argv = argv;
342 newfp->setScopeChain(scopeChain);
343 newfp->down = down;
344 newfp->ncode = ncode;
345 newfp->setThisValue(argv[-1]);
347 return newfp;
350 void * JS_FASTCALL
351 stubs::CompileFunction(VMFrame &f)
354 * We have a partially constructed frame. That's not really good enough to
355 * compile though because we could throw, so get a full, adjusted frame.
357 JSContext *cx = f.cx;
358 JSStackFrame *fp = f.fp();
359 uint32 argc = fp->argc;
361 JSObject *obj = &fp->argv[-2].toObject();
362 JSFunction *fun = obj->getFunctionPrivate();
363 JSScript *script = fun->u.i.script;
365 bool callingNew = !!(fp->flags & JSFRAME_CONSTRUCTING);
367 /* Empty script does nothing. */
368 if (script->isEmpty()) {
369 RemovePartialFrame(f);
370 Value *vp = f.regs.sp - argc;
371 if (callingNew)
372 vp[-2] = vp[-1];
373 else
374 vp[-2].setUndefined();
375 return NULL;
378 /* CheckArity expects fun to be set. */
379 fp->setFunction(fun);
381 if (argc < fun->nargs) {
382 fp = (JSStackFrame *)CheckArity(f);
383 if (!fp)
384 return NULL;
387 fp->setCallObj(NULL);
388 fp->setArgsObj(NULL);
389 fp->setBlockChain(NULL);
390 fp->setHookData(NULL);
391 fp->setAnnotation(NULL);
392 fp->setCallerVersion(fp->down->getCallerVersion());
393 fp->setScript(script);
394 fp->clearReturnValue();
395 #ifdef DEBUG
396 fp->savedPC = JSStackFrame::sInvalidPC;
397 #endif
399 f.regs.fp = fp;
400 f.regs.sp = fp->base();
401 f.regs.pc = script->code;
403 SetValueRangeToUndefined(fp->slots(), script->nfixed);
405 if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
406 THROWV(NULL);
408 CompileStatus status = CanMethodJIT(cx, script, fun, fp->getScopeChain());
409 if (status == Compile_Okay)
410 return script->jit->invoke;
412 /* Function did not compile... interpret it. */
413 JSBool ok = Interpret(cx, fp);
414 InlineReturn(f, ok);
416 if (!ok)
417 THROWV(NULL);
419 return NULL;
422 /* Preserved for when calls need to be slow (debug mode, no ICs) */
423 static bool
424 CreateFrame(VMFrame &f, uint32 flags, uint32 argc)
426 JSContext *cx = f.cx;
427 JSStackFrame *fp = f.fp();
428 Value *vp = f.regs.sp - (argc + 2);
429 JSObject *funobj = &vp->toObject();
430 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
432 JS_ASSERT(FUN_INTERPRETED(fun));
434 JSScript *newscript = fun->u.i.script;
436 /* Allocate the frame. */
437 StackSpace &stack = cx->stack();
438 uintN nslots = newscript->nslots;
439 uintN funargs = fun->nargs;
440 Value *argv = vp + 2;
441 JSStackFrame *newfp;
442 if (argc < funargs) {
443 uintN missing = funargs - argc;
444 if (!f.ensureSpace(missing, nslots))
445 return false;
446 newfp = stack.getInlineFrameUnchecked(cx, f.regs.sp, missing);
447 if (!newfp)
448 return false;
449 for (Value *v = argv + argc, *end = v + missing; v != end; ++v)
450 v->setUndefined();
451 } else {
452 if (!f.ensureSpace(0, nslots))
453 return false;
454 newfp = stack.getInlineFrameUnchecked(cx, f.regs.sp, 0);
455 if (!newfp)
456 return false;
459 /* Initialize the frame. */
460 newfp->ncode = NULL;
461 newfp->setCallObj(NULL);
462 newfp->setArgsObj(NULL);
463 newfp->setScript(newscript);
464 newfp->setFunction(fun);
465 newfp->argc = argc;
466 newfp->argv = vp + 2;
467 newfp->clearReturnValue();
468 newfp->setAnnotation(NULL);
469 newfp->setScopeChain(funobj->getParent());
470 newfp->flags = flags;
471 newfp->setBlockChain(NULL);
472 newfp->setThisValue(vp[1]);
473 JS_ASSERT(!fp->hasIMacroPC());
475 /* Push void to initialize local variables. */
476 Value *newslots = newfp->slots();
477 Value *newsp = newslots + fun->u.i.nvars;
478 for (Value *v = newslots; v != newsp; ++v)
479 v->setUndefined();
481 /* Scope with a call object parented by callee's parent. */
482 if (fun->isHeavyweight() && !js_GetCallObject(cx, newfp))
483 return false;
485 /* :TODO: Switch version if currentVersion wasn't overridden. */
486 newfp->setCallerVersion((JSVersion)cx->version);
488 // Marker for debug support.
489 if (JSInterpreterHook hook = cx->debugHooks->callHook) {
490 newfp->setHookData(hook(cx, fp, JS_TRUE, 0,
491 cx->debugHooks->callHookData));
492 } else {
493 newfp->setHookData(NULL);
496 stack.pushInlineFrame(cx, fp, cx->regs->pc, newfp);
497 f.regs.fp = newfp;
499 return true;
502 static inline bool
503 UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
505 if (!CreateFrame(f, flags, argc))
506 return false;
508 JSContext *cx = f.cx;
509 JSStackFrame *fp = cx->fp();
510 JSScript *script = fp->getScript();
511 f.regs.pc = script->code;
512 f.regs.sp = fp->base();
514 if (cx->options & JSOPTION_METHODJIT) {
515 if (!script->ncode) {
516 if (mjit::TryCompile(cx, script, fp->getFunction(), fp->getScopeChain()) == Compile_Error) {
517 InlineReturn(f, JS_FALSE);
518 return false;
521 JS_ASSERT(script->ncode);
522 if (script->ncode != JS_UNJITTABLE_METHOD) {
523 *pret = script->jit->invoke;
524 return true;
528 bool ok = !!Interpret(cx, cx->fp());
529 InlineReturn(f, JS_TRUE);
531 *pret = NULL;
532 return ok;
535 void * JS_FASTCALL
536 stubs::UncachedNew(VMFrame &f, uint32 argc)
538 JSContext *cx = f.cx;
540 Value *vp = f.regs.sp - (argc + 2);
542 JSObject *obj;
543 if (IsFunctionObject(*vp, &obj)) {
544 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
546 if (fun->isInterpreted()) {
547 JSScript *script = fun->u.i.script;
548 if (!stubs::NewObject(f, argc))
549 THROWV(NULL);
551 if (script->isEmpty()) {
552 vp[0] = vp[1];
553 return NULL;
556 void *ret;
557 if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ret, argc))
558 THROWV(NULL);
560 return ret;
563 if (fun->isConstructor()) {
564 vp[1].setMagicWithObjectOrNullPayload(NULL);
565 Native fn = fun->u.n.native;
566 if (!fn(cx, argc, vp))
567 THROWV(NULL);
568 JS_ASSERT(!vp->isPrimitive());
569 return NULL;
573 if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
574 THROWV(NULL);
576 return NULL;
579 void * JS_FASTCALL
580 stubs::UncachedCall(VMFrame &f, uint32 argc)
582 JSContext *cx = f.cx;
584 Value *vp = f.regs.sp - (argc + 2);
586 JSObject *obj;
587 if (IsFunctionObject(*vp, &obj)) {
588 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
590 if (fun->isInterpreted()) {
591 void *ret;
593 if (fun->u.i.script->isEmpty()) {
594 vp->setUndefined();
595 f.regs.sp = vp + 1;
596 return NULL;
599 if (!UncachedInlineCall(f, 0, &ret, argc))
600 THROWV(NULL);
602 return ret;
605 if (fun->isNative()) {
606 if (!fun->u.n.native(cx, argc, vp))
607 THROWV(NULL);
608 return NULL;
612 if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
613 THROWV(NULL);
615 return NULL;
618 void JS_FASTCALL
619 stubs::PutCallObject(VMFrame &f)
621 JS_ASSERT(f.fp()->hasCallObj());
622 js_PutCallObject(f.cx, f.fp());
623 JS_ASSERT(!f.fp()->hasArgsObj());
626 void JS_FASTCALL
627 stubs::PutArgsObject(VMFrame &f)
629 js_PutArgsObject(f.cx, f.fp());
632 extern "C" void *
633 js_InternalThrow(VMFrame &f)
635 JSContext *cx = f.cx;
637 // Make sure sp is up to date.
638 JS_ASSERT(cx->regs == &f.regs);
640 // Call the throw hook if necessary
641 JSThrowHook handler = f.cx->debugHooks->throwHook;
642 if (handler) {
643 Value rval;
644 switch (handler(cx, cx->fp()->getScript(), cx->regs->pc, Jsvalify(&rval),
645 cx->debugHooks->throwHookData)) {
646 case JSTRAP_ERROR:
647 cx->throwing = JS_FALSE;
648 return NULL;
650 case JSTRAP_RETURN:
651 cx->throwing = JS_FALSE;
652 cx->fp()->setReturnValue(rval);
653 return JS_FUNC_TO_DATA_PTR(void *,
654 JS_METHODJIT_DATA(cx).trampolines.forceReturn);
656 case JSTRAP_THROW:
657 cx->exception = rval;
658 break;
660 default:
661 break;
665 jsbytecode *pc = NULL;
666 for (;;) {
667 pc = FindExceptionHandler(cx);
668 if (pc)
669 break;
671 // If on the 'topmost' frame (where topmost means the first frame
672 // called into through js_Interpret). In this case, we still unwind,
673 // but we shouldn't return from a JS function, because we're not in a
674 // JS function.
675 bool lastFrame = (f.entryFp == f.fp());
676 js_UnwindScope(cx, 0, cx->throwing);
677 if (lastFrame)
678 break;
680 JS_ASSERT(f.regs.sp == cx->regs->sp);
681 InlineReturn(f, JS_FALSE);
684 JS_ASSERT(f.regs.sp == cx->regs->sp);
686 if (!pc) {
687 *f.oldRegs = f.regs;
688 f.cx->setCurrentRegs(f.oldRegs);
689 return NULL;
692 return cx->fp()->getScript()->pcToNative(pc);
695 void JS_FASTCALL
696 stubs::GetCallObject(VMFrame &f)
698 JS_ASSERT(f.fp()->getFunction()->isHeavyweight());
699 if (!js_GetCallObject(f.cx, f.fp()))
700 THROW();
703 static inline void
704 AdvanceReturnPC(JSContext *cx)
706 /* Simulate an inline_return by advancing the pc. */
707 JS_ASSERT(*cx->regs->pc == JSOP_CALL ||
708 *cx->regs->pc == JSOP_NEW ||
709 *cx->regs->pc == JSOP_EVAL ||
710 *cx->regs->pc == JSOP_APPLY);
711 cx->regs->pc += JSOP_CALL_LENGTH;
714 #ifdef JS_TRACER
716 static inline bool
717 SwallowErrors(VMFrame &f, JSStackFrame *stopFp)
719 JSContext *cx = f.cx;
721 /* Remove the bottom frame. */
722 bool ok = false;
723 for (;;) {
724 JSStackFrame *fp = cx->fp();
726 /* Look for an imacro with hard-coded exception handlers. */
727 if (fp->hasIMacroPC() && cx->throwing) {
728 cx->regs->pc = fp->getIMacroPC();
729 fp->clearIMacroPC();
730 if (ok)
731 break;
733 JS_ASSERT(!fp->hasIMacroPC());
735 /* If there's an exception and a handler, set the pc and leave. */
736 jsbytecode *pc = FindExceptionHandler(cx);
737 if (pc) {
738 cx->regs->pc = pc;
739 ok = true;
740 break;
743 /* Don't unwind if this was the entry frame. */
744 if (fp == stopFp)
745 break;
747 /* Unwind and return. */
748 ok &= bool(js_UnwindScope(cx, 0, cx->throwing));
749 InlineReturn(f, ok);
752 /* Update the VMFrame before leaving. */
753 JS_ASSERT(&f.regs == cx->regs);
755 JS_ASSERT_IF(!ok, cx->fp() == stopFp);
756 return ok;
759 static inline bool
760 AtSafePoint(JSContext *cx)
762 JSStackFrame *fp = cx->fp();
763 if (fp->hasIMacroPC())
764 return false;
766 JSScript *script = fp->getScript();
767 if (!script->nmap)
768 return false;
770 JS_ASSERT(cx->regs->pc >= script->code && cx->regs->pc < script->code + script->length);
771 return !!script->nmap[cx->regs->pc - script->code];
774 static inline JSBool
775 PartialInterpret(VMFrame &f)
777 JSContext *cx = f.cx;
778 JSStackFrame *fp = cx->fp();
780 JS_ASSERT(fp->hasIMacroPC() || !fp->getScript()->nmap ||
781 !fp->getScript()->nmap[cx->regs->pc - fp->getScript()->code]);
783 JSBool ok = JS_TRUE;
784 fp->flags |= JSFRAME_BAILING;
785 ok = Interpret(cx, fp);
786 fp->flags &= ~JSFRAME_BAILING;
788 f.fp() = cx->fp();
790 return ok;
793 JS_STATIC_ASSERT(JSOP_NOP == 0);
795 static inline JSOp
796 FrameIsFinished(JSContext *cx)
798 JSOp op = JSOp(*cx->regs->pc);
799 return (op == JSOP_RETURN ||
800 op == JSOP_RETRVAL ||
801 op == JSOP_STOP)
802 ? op
803 : JSOP_NOP;
806 static bool
807 RemoveExcessFrames(VMFrame &f, JSStackFrame *entryFrame)
809 JSContext *cx = f.cx;
810 while (cx->fp() != entryFrame) {
811 JSStackFrame *fp = cx->fp();
812 fp->flags &= ~JSFRAME_RECORDING;
814 if (AtSafePoint(cx)) {
815 JSScript *script = fp->getScript();
816 if (!JaegerShotAtSafePoint(cx, script->nmap[cx->regs->pc - script->code])) {
817 if (!SwallowErrors(f, entryFrame))
818 return false;
820 /* Could be anywhere - restart outer loop. */
821 continue;
823 InlineReturn(f, JS_TRUE);
824 AdvanceReturnPC(cx);
825 } else {
826 if (!PartialInterpret(f)) {
827 if (!SwallowErrors(f, entryFrame))
828 return false;
829 } else {
831 * Partial interpret could have dropped us anywhere. Deduce the
832 * edge case: at a RETURN, needing to pop a frame.
834 if (!cx->fp()->hasIMacroPC() && FrameIsFinished(cx)) {
835 JSOp op = JSOp(*cx->regs->pc);
836 if (op == JSOP_RETURN && !(cx->fp()->flags & JSFRAME_BAILED_AT_RETURN))
837 fp->setReturnValue(f.regs.sp[-1]);
838 InlineReturn(f, JS_TRUE);
839 AdvanceReturnPC(cx);
845 return true;
848 #if JS_MONOIC
849 static void
850 DisableTraceHint(VMFrame &f, ic::MICInfo &mic)
852 JS_ASSERT(mic.kind == ic::MICInfo::TRACER);
855 * Hack: The value that will be patched is before the executable address,
856 * so to get protection right, just unprotect the general region around
857 * the jump.
859 uint8 *addr = (uint8 *)(mic.traceHint.executableAddress());
860 JSC::RepatchBuffer repatch(addr - 64, 128);
861 repatch.relink(mic.traceHint, mic.load);
863 JaegerSpew(JSpew_PICs, "relinking trace hint %p to %p\n", mic.traceHint.executableAddress(),
864 mic.load.executableAddress());
866 if (mic.u.hasSlowTraceHint) {
867 addr = (uint8 *)(mic.slowTraceHint.executableAddress());
868 JSC::RepatchBuffer repatch(addr - 64, 128);
869 repatch.relink(mic.slowTraceHint, mic.load);
871 JaegerSpew(JSpew_PICs, "relinking trace hint %p to %p\n",
872 mic.slowTraceHint.executableAddress(),
873 mic.load.executableAddress());
876 #endif
878 #if JS_MONOIC
879 void *
880 RunTracer(VMFrame &f, ic::MICInfo &mic)
881 #else
882 void *
883 RunTracer(VMFrame &f)
884 #endif
886 JSContext *cx = f.cx;
887 JSStackFrame *entryFrame = f.fp();
888 TracePointAction tpa;
890 /* :TODO: nuke PIC? */
891 if (!cx->jitEnabled)
892 return NULL;
894 bool blacklist;
895 uintN inlineCallCount = 0;
896 tpa = MonitorTracePoint(f.cx, inlineCallCount, blacklist);
897 JS_ASSERT(!TRACE_RECORDER(cx));
899 #if JS_MONOIC
900 if (blacklist)
901 DisableTraceHint(f, mic);
902 #endif
904 if ((tpa == TPA_RanStuff || tpa == TPA_Recorded) && cx->throwing)
905 tpa = TPA_Error;
907 /* Sync up the VMFrame's view of cx->fp(). */
908 f.fp() = cx->fp();
910 switch (tpa) {
911 case TPA_Nothing:
912 return NULL;
914 case TPA_Error:
915 if (!SwallowErrors(f, entryFrame))
916 THROWV(NULL);
917 JS_ASSERT(!cx->fp()->hasIMacroPC());
918 break;
920 case TPA_RanStuff:
921 case TPA_Recorded:
922 break;
926 * The tracer could have dropped us off on any frame at any position.
927 * Well, it could not have removed frames (recursion is disabled).
929 * Frames after the entryFrame cannot be entered via JaegerShotAtSafePoint()
930 * unless each is at a safe point. We can JaegerShotAtSafePoint these
931 * frames individually, but we must unwind to the entryFrame.
933 * Note carefully that JaegerShotAtSafePoint can resume methods at
934 * arbitrary safe points whereas JaegerShot cannot.
936 * If we land on entryFrame without a safe point in sight, we'll end up
937 * at the RETURN op. This is an edge case with two paths:
939 * 1) The entryFrame is the last inline frame. If it fell on a RETURN,
940 * move the return value down.
941 * 2) The entryFrame is NOT the last inline frame. Pop the frame.
943 * In both cases, we hijack the stub to return to InjectJaegerReturn. This
944 * moves |oldFp->rval| into the scripted return registers.
947 restart:
948 /* Step 1. Initial removal of excess frames. */
949 if (!RemoveExcessFrames(f, entryFrame))
950 THROWV(NULL);
952 /* Step 2. If there's an imacro on the entry frame, remove it. */
953 entryFrame->flags &= ~JSFRAME_RECORDING;
954 while (entryFrame->hasIMacroPC()) {
955 if (!PartialInterpret(f)) {
956 if (!SwallowErrors(f, entryFrame))
957 THROWV(NULL);
960 /* After partial interpreting, we could have more frames again. */
961 goto restart;
964 /* Step 3.1. If entryFrame is at a safe point, just leave. */
965 if (AtSafePoint(cx)) {
966 uint32 offs = uint32(cx->regs->pc - entryFrame->getScript()->code);
967 JS_ASSERT(entryFrame->getScript()->nmap[offs]);
968 return entryFrame->getScript()->nmap[offs];
971 /* Step 3.2. If entryFrame is at a RETURN, then leave slightly differently. */
972 if (JSOp op = FrameIsFinished(cx)) {
973 /* We're not guaranteed that the RETURN was run. */
974 if (op == JSOP_RETURN && !(entryFrame->flags & JSFRAME_BAILED_AT_RETURN))
975 entryFrame->setReturnValue(f.regs.sp[-1]);
977 /* Don't pop the frame if it's maybe owned by an Invoke. */
978 if (f.fp() != f.entryFp) {
979 if (!InlineReturn(f, JS_TRUE))
980 THROWV(NULL);
982 void *retPtr = JS_FUNC_TO_DATA_PTR(void *, InjectJaegerReturn);
983 *f.returnAddressLocation() = retPtr;
984 return NULL;
987 /* Step 3.3. Do a partial interp, then restart the whole process. */
988 if (!PartialInterpret(f)) {
989 if (!SwallowErrors(f, entryFrame))
990 THROWV(NULL);
993 goto restart;
996 #endif /* JS_TRACER */
998 #if defined JS_TRACER
999 # if defined JS_MONOIC
1000 void *JS_FASTCALL
1001 stubs::InvokeTracer(VMFrame &f, uint32 index)
1003 JSScript *script = f.fp()->getScript();
1004 ic::MICInfo &mic = script->mics[index];
1006 JS_ASSERT(mic.kind == ic::MICInfo::TRACER);
1008 return RunTracer(f, mic);
1011 # else
1013 void *JS_FASTCALL
1014 stubs::InvokeTracer(VMFrame &f)
1016 return RunTracer(f);
1018 # endif /* JS_MONOIC */
1019 #endif /* JS_TRACER */