[JAEGER] Merge.
authorDavid Mandelin <dmandelin@mozilla.com>
Thu, 19 Aug 2010 11:12:06 -0700
changeset 53459 ab92f8f395a74bf19d61bac3e7d0f1cbaf221adb
parent 53458 2ead9f3860ddd404dfd829858988b97f5274e205 (current diff)
parent 53457 b88bab8e77c5b3710db92f166c8e7187b68f7ea6 (diff)
child 53460 79ef58bbf0e897cbff440b5045b6e23643fe73e1
push idunknown
push userunknown
push dateunknown
milestone2.0b5pre
[JAEGER] Merge.
js/src/methodjit/Compiler.cpp
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -308,27 +308,27 @@ static const JSC::MacroAssembler::Regist
         callPatches.append(CallPatch(differenceBetween(startLabel, cl), fun));
         return cl;
     }
 
     Call call(RegisterID reg) {
         return MacroAssembler::call(reg);
     }
 
-#if defined(JS_CPU_ARM)
-    void ret() {
-        /* The return sequence emitted by the ARM macro-assembler assumes that the return address
-         * is in LR. We could load it into LR before calling it, but it's probably better to simply
-         * pop into the PC. (Note that JaegerTrampoline expects the return sequence to pop this
-         * single word from the stack, so the stack will be unaligned on return from JIT code.
-         * JaegerTrampoline fixes this up.) */
-        MacroAssembler::pop(JSC::ARMRegisters::pc);
+    void restoreReturnAddress()
+    {
+#ifndef JS_CPU_ARM
+        /* X86 and X64's "ret" instruction expects a return address on the stack. */
+        push(Address(JSFrameReg, offsetof(JSStackFrame, ncode)));
+#else
+        /* ARM returns either using its link register (LR) or directly from the stack, but masm.ret()
+         * always emits a return to LR. */
+        load32(Address(JSFrameReg, offsetof(JSStackFrame, ncode)), JSC::ARMRegisters::lr);
+#endif
     }
-    // #else fall back to the inherited implementation in MacroAssembler::ret().
-#endif
 
     void finalize(uint8 *ncode) {
         JSC::JITCode jc(ncode, size());
         JSC::CodeBlock cb(jc);
         JSC::RepatchBuffer repatchBuffer(&cb);
 
         for (size_t i = 0; i < callPatches.length(); i++) {
             JSC::MacroAssemblerCodePtr cp(ncode + callPatches[i].distance);
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -162,43 +162,44 @@ mjit::TryCompile(JSContext *cx, JSScript
 
     CompileStatus status = cc.Compile();
     if (status != Compile_Okay)
         script->ncode = JS_UNJITTABLE_METHOD;
 
     return status;
 }
 
+void
+mjit::Compiler::saveReturnAddress()
+{
+#ifndef JS_CPU_ARM
+    masm.pop(Registers::ReturnReg);
+    restoreFrameRegs(masm);
+    masm.storePtr(Registers::ReturnReg, Address(JSFrameReg, offsetof(JSStackFrame, ncode)));
+#else
+    restoreFrameRegs(masm);
+    masm.storePtr(JSC::ARMRegisters::lr, Address(JSFrameReg, offsetof(JSStackFrame, ncode)));
+#endif
+}
+
 CompileStatus
 mjit::Compiler::generatePrologue()
 {
     invokeLabel = masm.label();
-#ifdef JS_CPU_ARM
-    /* 
-     * Unlike x86/x64, the return address is not automatically pushed onto the stack during a call
-     * (blx). To compensate, we explicitly push it here.
-     *
-     * NOTE: The ABI requires that we maintain 8-byte stack alignment at function boundaries. The
-     * trampoline always enters this function with an unaligned stack so we can re-align it.
-     */
-    masm.push(JSC::ARMRegisters::lr);
-#endif
-    restoreFrameRegs(masm);
+
+    saveReturnAddress();
 
     /*
      * If there is no function, then this can only be called via JaegerShot(),
      * which expects an existing frame to be initialized like the interpreter.
      */
     if (fun) {
         Jump j = masm.jump();
         invokeLabel = masm.label();
-#ifdef JS_CPU_ARM
-        masm.push(JSC::ARMRegisters::lr);
-#endif
-        restoreFrameRegs(masm);
+        saveReturnAddress();
 
         /* Set locals to undefined. */
         for (uint32 i = 0; i < script->nslots; i++) {
             Address local(JSFrameReg, sizeof(JSStackFrame) + i * sizeof(Value));
             masm.storeValue(UndefinedValue(), local);
         }
 
         /* Create the call object. */
@@ -1566,16 +1567,17 @@ mjit::Compiler::emitReturn()
     /*
      * if (!f.inlineCallCount)
      *     return;
      */
     Jump noInlineCalls = masm.branchPtr(Assembler::Equal,
                                         FrameAddress(offsetof(VMFrame, entryFp)),
                                         JSFrameReg);
     stubcc.linkExit(noInlineCalls, Uses(frame.frameDepth()));
+    stubcc.masm.restoreReturnAddress();
     stubcc.masm.ret();
 
     JS_ASSERT_IF(!fun, JSOp(*PC) == JSOP_STOP);
 
     /*
      * If there's a function object, deal with the fact that it can escape.
      * Note that after we've placed the call object, all tracked state can
      * be thrown away. This will happen anyway because the next live opcode
@@ -1626,23 +1628,22 @@ mjit::Compiler::emitReturn()
     masm.storePtr(Registers::ReturnReg, Address(Registers::ArgReg1, offsetof(JSContext, fp)));
 
     JS_STATIC_ASSERT(Registers::ReturnReg != JSReturnReg_Data);
     JS_STATIC_ASSERT(Registers::ReturnReg != JSReturnReg_Type);
 
     Address rval(JSFrameReg, JSStackFrame::offsetReturnValue());
     masm.loadPayload(rval, JSReturnReg_Data);
     masm.loadTypeTag(rval, JSReturnReg_Type);
+    masm.restoreReturnAddress();
     masm.move(Registers::ReturnReg, JSFrameReg);
-    masm.loadPtr(Address(JSFrameReg, offsetof(JSStackFrame, ncode)), Registers::ReturnReg);
 #ifdef DEBUG
     masm.storePtr(ImmPtr(JSStackFrame::sInvalidPC),
                   Address(JSFrameReg, offsetof(JSStackFrame, savedPC)));
 #endif
-
     masm.ret();
 }
 
 void
 mjit::Compiler::prepareStubCall(Uses uses)
 {
     JaegerSpew(JSpew_Insns, " ---- STUB CALL, SYNCING FRAME ---- \n");
     frame.syncAndKill(Registers(Registers::TempRegs), uses);
@@ -1819,31 +1820,23 @@ mjit::Compiler::inlineCallHelper(uint32 
          */
         Jump j = stubcc.masm.branchTestPtr(Assembler::NonZero, Registers::ReturnReg, Registers::ReturnReg);
         stubcc.crossJump(j, masm.label());
         if (callingNew)
             invokeCallDone = stubcc.masm.jump();
     }
 
     /* Fast-path: return address contains scripted call. */
-
-    masm.addPtr(Imm32(sizeof(void*)), Registers::StackPointer);
     masm.call(Registers::ReturnReg);
 #if defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)
     masm.callLabel = masm.label();
 #endif
     ADD_CALLSITE(false);
 
     /*
-     * The scripted call returns a register triplet, containing the jsval and
-     * the current f.scriptedReturn.
-     */
-    masm.push(Registers::ReturnReg);
-
-    /*
      * Functions invoked with |new| can return, for some reason, primitive
      * values. Just deal with this here.
      */
     if (callingNew) {
         Jump primitive = masm.testPrimitive(Assembler::Equal, JSReturnReg_Type);
         stubcc.linkExitDirect(primitive, stubcc.masm.label());
         FrameEntry *fe = frame.peek(-int(argc + 1));
         Address thisv(frame.addressOf(fe));
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -220,16 +220,18 @@ class Compiler
     /* Non-emitting helpers. */
     uint32 fullAtomIndex(jsbytecode *pc);
     void jumpInScript(Jump j, jsbytecode *pc);
     JSC::ExecutablePool *getExecPool(size_t size);
     bool compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const Value &rhs);
     void addCallSite(uint32 id, bool stub);
 
     /* Emitting helpers. */
+    void saveReturnAddress();
+    void restoreReturnAddress(Assembler &masm);
     void restoreFrameRegs(Assembler &masm);
     void emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused);
     void iter(uintN flags);
     void iterNext();
     void iterMore();
     void iterEnd();
     MaybeJump loadDouble(FrameEntry *fe, FPRegisterID fpReg);
 
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -164,17 +164,16 @@ top:
     }
 
     return NULL;
 }
 
 static inline void
 FixVMFrame(VMFrame &f, JSStackFrame *fp)
 {
-    f.fp->ncode = f.scriptedReturn;
     JS_ASSERT(f.fp == fp->down);
     f.fp = fp;
 }
 
 static inline bool
 CreateFrame(VMFrame &f, uint32 flags, uint32 argc)
 {
     JSContext *cx = f.cx;
@@ -495,17 +494,16 @@ CreateLightFrame(VMFrame &f, uint32 flag
         if (!f.ensureSpace(0, nslots))
             return false;
         newfp = stack.getInlineFrameUnchecked(cx, f.regs.sp, 0);
         if (!newfp)
             return false;
     }
 
     /* Initialize the frame. */
-    newfp->ncode = NULL;
     newfp->setCallObj(NULL);
     newfp->setArgsObj(NULL);
     newfp->setScript(newscript);
     newfp->setFunction(fun);
     newfp->argc = argc;
     newfp->argv = vp + 2;
     newfp->clearReturnValue();
     newfp->setAnnotation(NULL);
@@ -629,17 +627,16 @@ js_InternalThrow(VMFrame &f)
         // JS function.
         bool lastFrame = (f.entryFp == f.fp);
         js_UnwindScope(cx, 0, cx->throwing);
         if (lastFrame)
             break;
 
         JS_ASSERT(f.regs.sp == cx->regs->sp);
         InlineReturn(f, JS_FALSE);
-        f.scriptedReturn = cx->fp->ncode;
     }
 
     JS_ASSERT(f.regs.sp == cx->regs->sp);
 
     if (!pc) {
         *f.oldRegs = f.regs;
         f.cx->setCurrentRegs(f.oldRegs);
         return NULL;
@@ -763,17 +760,18 @@ static bool
 RemoveExcessFrames(VMFrame &f, JSStackFrame *entryFrame)
 {
     JSContext *cx = f.cx;
     while (cx->fp != entryFrame) {
         JSStackFrame *fp = cx->fp;
         fp->flags &= ~JSFRAME_RECORDING;
 
         if (AtSafePoint(cx)) {
-            if (!JaegerShot(cx)) {
+            JSScript *script = fp->getScript();
+            if (!JaegerShotAtSafePoint(cx, script->nmap[cx->regs->pc - script->code])) {
                 if (!SwallowErrors(f, entryFrame))
                     return false;
 
                 /* Could be anywhere - restart outer loop. */
                 continue;
             }
             InlineReturn(f, JS_TRUE);
             AdvanceReturnPC(cx);
@@ -841,19 +839,16 @@ RunTracer(VMFrame &f)
     JSContext *cx = f.cx;
     JSStackFrame *entryFrame = f.fp;
     TracePointAction tpa;
 
     /* :TODO: nuke PIC? */
     if (!cx->jitEnabled)
         return NULL;
 
-    JS_ASSERT_IF(f.fp != f.entryFp,
-                 entryFrame->down->getScript()->isValidJitCode(f.scriptedReturn));
-
     bool blacklist;
     uintN inlineCallCount = 0;
     tpa = MonitorTracePoint(f.cx, inlineCallCount, blacklist);
     JS_ASSERT(!TRACE_RECORDER(cx));
 
 #if JS_MONOIC
     if (blacklist)
         DisableTraceHint(f, mic);
@@ -879,31 +874,32 @@ RunTracer(VMFrame &f)
       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 JaegerShot()
-     * unless each is at a safe point. We can JaegerShot these frames
-     * individually, but we must unwind to the entryFrame.
+     * 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 JaegerFromTracer. This
-     * moves |oldFp->rval| into the scripted return registers, places the
-     * new f.scriptedReturn in the machine return register, and returns to its
-     * caller safely.
+     * In both cases, we hijack the stub to return to InjectJaegerReturn. This
+     * moves |oldFp->rval| into the scripted return registers.
      */
 
   restart:
     /* Step 1. Initial removal of excess frames. */
     if (!RemoveExcessFrames(f, entryFrame))
         THROWV(NULL);
 
     /* Step 2. If there's an imacro on the entry frame, remove it. */
@@ -931,18 +927,17 @@ RunTracer(VMFrame &f)
         if (op == JSOP_RETURN && !(entryFrame->flags & JSFRAME_BAILED_AT_RETURN))
             entryFrame->setReturnValue(f.regs.sp[-1]);
 
         /* Don't pop the frame if it's maybe owned by an Invoke. */
         if (f.fp != f.entryFp) {
             if (!InlineReturn(f, JS_TRUE))
                 THROWV(NULL);
         }
-        entryFrame->ncode = f.fp->ncode;
-        void *retPtr = JS_FUNC_TO_DATA_PTR(void *, JaegerFromTracer);
+        void *retPtr = JS_FUNC_TO_DATA_PTR(void *, InjectJaegerReturn);
         *f.returnAddressLocation() = retPtr;
         return NULL;
     }
 
     /* Step 3.3. Do a partial interp, then restart the whole process. */
     if (!PartialInterpret(f)) {
         if (!SwallowErrors(f, entryFrame))
             THROWV(NULL);
--- a/js/src/methodjit/MethodJIT.cpp
+++ b/js/src/methodjit/MethodJIT.cpp
@@ -44,16 +44,65 @@
 #include "MonoIC.h"
 #include "PolyIC.h"
 #include "TrampolineCompiler.h"
 #include "jscntxtinlines.h"
 
 using namespace js;
 using namespace js::mjit;
 
+/*
+ * Explanation of VMFrame activation and various helper thunks below.
+ *
+ * JaegerTrampoline  - Executes a method JIT-compiled JSFunction. This function
+ *    creates a VMFrame on the machine stack and calls into JIT'd code. The JIT'd
+ *    code will eventually return to the VMFrame.
+ *
+ *  - Called from C++ function EnterMethodJIT.
+ *  - Parameters: cx, fp, code, stackLimit, safePoint
+ *  - Notes: safePoint is used in combination with SafePointTrampoline,
+ *           explained further down.
+ *
+ * JaegerThrowpoline - Calls into an exception handler from JIT'd code, and if a
+ *    scripted exception handler is not found, unwinds the VMFrame and returns
+ *    to C++.
+ *
+ *  - To start exception handling, we return from a stub call to the throwpoline.
+ *  - On entry to the throwpoline, the normal conditions of the jit-code ABI
+ *    are satisfied.
+ *  - To do the unwinding and find out where to continue executing, we call
+ *    js_InternalThrow.
+ *  - js_InternalThrow may return 0, which means the place to continue, if any,
+ *    is above this JaegerShot activation, so we just return, in the same way
+ *    the trampoline does.
+ *  - Otherwise, js_InternalThrow returns a jit-code address to continue execution
+ *    at. Because the jit-code ABI conditions are satisfied, we can just jump to
+ *    that point.
+ *
+ *
+ * SafePointTrampoline  - Inline script calls link their return addresses through
+ *    JSStackFrame::ncode. This includes the return address that unwinds back
+ *    to JaegerTrampoline. However, the tracer integration code often wants to
+ *    enter a method JIT'd function at an arbitrary safe point. Safe points
+ *    do not have the return address linking code that the method prologue has.
+ *    SafePointTrampoline is a thunk which correctly links the initial return
+ *    address. It is used in JaegerShotAtSafePoint, and passed as the "script
+ *    code" parameter. Using the "safePoint" parameter to JaegerTrampoline, it
+ *    correctly jumps to the intended point in the method.
+ *
+ *  - Used by JaegerTrampoline()
+ *
+ * InjectJaegerReturn - Implements the tail of InlineReturn. This is needed for
+ *    tracer integration, where a "return" opcode might not be a safe-point,
+ *    and thus the return path must be injected by hijacking the stub return
+ *    address.
+ *
+ *  - Used by RunTracer()
+ */
+
 #ifdef JS_METHODJIT_PROFILE_STUBS
 static const size_t STUB_CALLS_FOR_OP_COUNT = 255;
 static uint32 StubCallsForOp[STUB_CALLS_FOR_OP_COUNT];
 #endif
 
 extern "C" void JS_FASTCALL
 PushActiveVMFrame(VMFrame &f)
 {
@@ -145,37 +194,41 @@ SYMBOL_STRING(JaegerTrampoline) ":"     
      */
     "pushq %rsi"                         "\n" /* entryFp */
     "pushq %rcx"                         "\n" /* inlineCallCount */
     "pushq %rdi"                         "\n" /* cx */
     "pushq %rsi"                         "\n" /* fp */
     "movq  %rsi, %rbx"                   "\n"
 
     /* Space for the rest of the VMFrame. */
-    "subq  $0x30, %rsp"                  "\n"
+    "subq  $0x28, %rsp"                  "\n"
 
-    /* Set cx->regs and set the active frame (requires saving rdx). */
+    /*
+     * This is actually part of the VMFrame, but we need to save |r8| for
+     * SafePointTrampoline.
+     */
+    "pushq %r8"                          "\n"
+
+    /* Set cx->regs and set the active frame. Save rdx and align frame in one. */
     "pushq %rdx"                         "\n"
     "movq  %rsp, %rdi"                   "\n"
     "call " SYMBOL_STRING_RELOC(SetVMFrameRegs) "\n"
     "movq  %rsp, %rdi"                   "\n"
     "call " SYMBOL_STRING_RELOC(PushActiveVMFrame) "\n"
-    "popq  %rdx"                         "\n"
 
     /*
-     * Jump into into the JIT'd code. The call implicitly fills in
-     * the precious f.scriptedReturn member of VMFrame.
+     * Jump into into the JIT'd code.
      */
-    "call *%rdx"                         "\n"
-    "leaq -8(%rsp), %rdi"                "\n"
+    "call *0(%rsp)"                      "\n"
+    "movq %rsp, %rdi"                    "\n"
     "call " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
-    "leaq -8(%rsp), %rdi"                "\n"
+    "movq %rsp, %rdi"                    "\n"
     "call " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
 
-    "addq $0x50, %rsp"                   "\n"
+    "addq $0x58, %rsp"                   "\n"
     "popq %rbx"                          "\n"
     "popq %r15"                          "\n"
     "popq %r14"                          "\n"
     "popq %r13"                          "\n"
     "popq %r12"                          "\n"
     "popq %rbp"                          "\n"
     "movq $1, %rax"                      "\n"
     "ret"                                "\n"
@@ -207,28 +260,38 @@ SYMBOL_STRING(JaegerThrowpoline) ":"    
 JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode) == 0x60);
 JS_STATIC_ASSERT(offsetof(VMFrame, fp) == 0x38);
 
 JS_STATIC_ASSERT(JSVAL_TAG_MASK == 0xFFFF800000000000LL);
 JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL);
 
 asm volatile (
 ".text\n"
-".globl " SYMBOL_STRING(JaegerFromTracer)   "\n"
-SYMBOL_STRING(JaegerFromTracer) ":"         "\n"
+".globl " SYMBOL_STRING(SafePointTrampoline)   "\n"
+SYMBOL_STRING(SafePointTrampoline) ":"         "\n"
+    "popq %rax"                             "\n"
+    "movq %rax, 0x60(%rbx)"                 "\n"
+    "jmp  *8(%rsp)"                         "\n"
+);
+
+asm volatile (
+".text\n"
+".globl " SYMBOL_STRING(InjectJaegerReturn)   "\n"
+SYMBOL_STRING(InjectJaegerReturn) ":"         "\n"
     "movq 0x40(%rbx), %rcx"                 "\n" /* fp->rval type (as value) */
     "movq $0xFFFF800000000000, %r11"         "\n" /* load type mask (JSVAL_TAG_MASK) */
     "andq %r11, %rcx"                       "\n" /* extract type */
 
     "movq 0x40(%rbx), %rdx"                 "\n" /* fp->rval type */
     "movq $0x00007FFFFFFFFFFF, %r11"        "\n" /* load payload mask (JSVAL_PAYLOAD_MASK) */
     "andq %r11, %rdx"                       "\n" /* extract payload */
 
     "movq 0x60(%rbx), %rax"                 "\n" /* fp->ncode */
     "movq 0x38(%rsp), %rbx"                 "\n" /* f.fp */
+    "pushq %rax"                            "\n"
     "ret"                                   "\n"
 );
 
 # elif defined(JS_CPU_X86)
 
 /*
  *    *** DANGER ***
  * If these assertions break, update the constants below. The throwpoline
@@ -248,38 +311,36 @@ SYMBOL_STRING(JaegerTrampoline) ":"     
     "movl %esp, %ebp"                    "\n"
     /* Save non-volatile registers. */
     "pushl %esi"                         "\n"
     "pushl %edi"                         "\n"
     "pushl %ebx"                         "\n"
 
     /* Build the JIT frame. Push fields in order, 
      * then align the stack to form esp == VMFrame. */
-    "movl  12(%ebp), %ebx"               "\n"   /* fp */
+    "movl  12(%ebp), %ebx"               "\n"   /* load fp */
     "pushl %ebx"                         "\n"   /* entryFp */
-    "pushl 20(%ebp)"                     "\n"   /* inlineCallCount */
-    "pushl 8(%ebp)"                      "\n"
-    "pushl %ebx"                         "\n"
-    "subl $0x18, %esp"                   "\n"
+    "pushl 20(%ebp)"                     "\n"   /* stackLimit */
+    "pushl 8(%ebp)"                      "\n"   /* cx */
+    "pushl %ebx"                         "\n"   /* fp */
+    "subl $0x1C, %esp"                   "\n"
 
     /* Jump into the JIT'd code. */
-    "pushl 16(%ebp)"                     "\n"
     "movl  %esp, %ecx"                   "\n"
     "call " SYMBOL_STRING_RELOC(SetVMFrameRegs) "\n"
     "movl  %esp, %ecx"                   "\n"
     "call " SYMBOL_STRING_RELOC(PushActiveVMFrame) "\n"
-    "popl  %edx"                         "\n"
 
-    "call  *%edx"                        "\n"
-    "leal  -4(%esp), %ecx"               "\n"
+    "call  *16(%ebp)"                    "\n"
+    "movl  %esp, %ecx"                   "\n"
     "call " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
-    "leal  -4(%esp), %ecx"               "\n"
+    "movl  %esp, %ecx"                   "\n"
     "call " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
 
-    "addl $0x28, %esp"                   "\n"
+    "addl $0x2C, %esp"                   "\n"
     "popl %ebx"                          "\n"
     "popl %edi"                          "\n"
     "popl %esi"                          "\n"
     "popl %ebp"                          "\n"
     "movl $1, %eax"                      "\n"
     "ret"                                "\n"
 );
 
@@ -312,56 +373,91 @@ SYMBOL_STRING(JaegerThrowpoline) ":"    
     "ret"                                "\n"
 );
 
 JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode) == 0x3C);
 JS_STATIC_ASSERT(offsetof(VMFrame, fp) == 0x1C);
 
 asm volatile (
 ".text\n"
-".globl " SYMBOL_STRING(JaegerFromTracer)   "\n"
-SYMBOL_STRING(JaegerFromTracer) ":"         "\n"
+".globl " SYMBOL_STRING(InjectJaegerReturn)   "\n"
+SYMBOL_STRING(InjectJaegerReturn) ":"         "\n"
     "movl 0x28(%ebx), %edx"                 "\n" /* fp->rval data */
     "movl 0x2C(%ebx), %ecx"                 "\n" /* fp->rval type */
     "movl 0x3C(%ebx), %eax"                 "\n" /* fp->ncode */
     "movl 0x1C(%esp), %ebx"                 "\n" /* f.fp */
+    "pushl %eax"                            "\n"
     "ret"                                   "\n"
 );
 
+/*
+ * Take the fifth parameter from JaegerShot() and jump to it. This makes it so
+ * we can jump into arbitrary JIT code, which won't have the frame-fixup prologue.
+ */
+asm volatile (
+".text\n"
+".globl " SYMBOL_STRING(SafePointTrampoline)   "\n"
+SYMBOL_STRING(SafePointTrampoline) ":"         "\n"
+    "popl %eax"                             "\n"
+    "movl %eax, 0x3C(%ebx)"                 "\n"
+    "jmp  *24(%ebp)"                        "\n"
+);
+
 # elif defined(JS_CPU_ARM)
 
 JS_STATIC_ASSERT(sizeof(VMFrame) == 80);
 JS_STATIC_ASSERT(offsetof(VMFrame, savedLR) ==          (4*19));
 JS_STATIC_ASSERT(offsetof(VMFrame, entryFp) ==          (4*10));
 JS_STATIC_ASSERT(offsetof(VMFrame, stackLimit) ==       (4*9));
 JS_STATIC_ASSERT(offsetof(VMFrame, cx) ==               (4*8));
 JS_STATIC_ASSERT(offsetof(VMFrame, fp) ==               (4*7));
 JS_STATIC_ASSERT(offsetof(VMFrame, oldRegs) ==          (4*4));
 JS_STATIC_ASSERT(offsetof(VMFrame, previous) ==         (4*3));
-JS_STATIC_ASSERT(offsetof(VMFrame, scriptedReturn) ==   (4*0));
+JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode) == 60);
 
 asm volatile (
 ".text\n"
-".globl " SYMBOL_STRING(JaegerFromTracer)   "\n"
-SYMBOL_STRING(JaegerFromTracer) ":"         "\n"
+".globl " SYMBOL_STRING(InjectJaegerReturn)   "\n"
+SYMBOL_STRING(InjectJaegerReturn) ":"         "\n"
     /* Restore frame regs. */
-    "ldr r11, [sp, #32]"                    "\n"
+    "ldr r1, [r11, #40]"                    "\n" /* fp->rval data */
+    "ldr r2, [r11, #44]"                    "\n" /* fp->rval type */
+    "ldr r0, [r11, #60]"                    "\n" /* fp->ncode */
+    "ldr r11, [sp, #28]"                    "\n" /* load f.fp */
     "bx  r0"                                "\n"
 );
 
 asm volatile (
 ".text\n"
+".globl " SYMBOL_STRING(SafePointTrampoline)  "\n"
+SYMBOL_STRING(SafePointTrampoline) ":"
+    /*
+     * On entry to SafePointTrampoline:
+     *         r11 = fp
+     *      sp[80] = safePoint
+     */
+    "ldr    ip, [sp, #80]"                  "\n"
+    /* Save the return address (in JaegerTrampoline) to fp->ncode. */
+    "str    lr, [r11, #60]"                 "\n"
+    /* Jump to 'safePoint' via 'ip' because a load into the PC from an address on
+     * the stack looks like a return, and may upset return stack prediction. */
+    "bx     ip"                             "\n"
+);
+
+asm volatile (
+".text\n"
 ".globl " SYMBOL_STRING(JaegerTrampoline)   "\n"
 SYMBOL_STRING(JaegerTrampoline) ":"         "\n"
     /*
      * On entry to JaegerTrampoline:
-     *      r0 = cx
-     *      r1 = fp
-     *      r2 = code
-     *      r3 = inlineCallCount
+     *         r0 = cx
+     *         r1 = fp
+     *         r2 = code
+     *         r3 = stackLimit
+     *      sp[0] = safePoint
      *
      * The VMFrame for ARM looks like this:
      *  [ lr        ]   \
      *  [ r11       ]   |
      *  [ r10       ]   |
      *  [ r9        ]   | Callee-saved registers.                             
      *  [ r8        ]   | VFP registers d8-d15 may be required here too, but  
      *  [ r7        ]   | unconditionally preserving them might be expensive
@@ -371,44 +467,43 @@ SYMBOL_STRING(JaegerTrampoline) ":"     
      *  [ entryFp   ]
      *  [ stkLimit  ]
      *  [ cx        ]
      *  [ fp        ]
      *  [ regs.sp   ]
      *  [ regs.pc   ]
      *  [ oldRegs   ]
      *  [ previous  ]
-     *  [ args.ptr  ]
+     *  [ args.ptr3 ]
      *  [ args.ptr2 ]
-     *  [ srpt. ret ]   } Scripted return.
+     *  [ args.ptr  ]
      */
     
-    /* Push callee-saved registers. TODO: Do we actually need to push all of them? If the
-     * compiled JavaScript function is EABI-compliant, we only need to push what we use in
-     * JaegerTrampoline. */
+    /* Push callee-saved registers. */
 "   push    {r4-r11,lr}"                        "\n"
     /* Push interesting VMFrame content. */
 "   push    {r1}"                               "\n"    /* entryFp */
 "   push    {r3}"                               "\n"    /* stackLimit */
 "   push    {r0}"                               "\n"    /* cx */
 "   push    {r1}"                               "\n"    /* fp */
     /* Remaining fields are set elsewhere, but we need to leave space for them. */
 "   sub     sp, sp, #(4*7)"                     "\n"
 
+    /* Preserve 'code' (r2) in an arbitrary callee-saved register. */
+"   mov     r4, r2"                             "\n"
+    /* Preserve 'fp' (r1) in r11 (JSFrameReg) for SafePointTrampoline. */
+"   mov     r11, r1"                            "\n"
+
 "   mov     r0, sp"                             "\n"
-"   mov     r4, r2"                             "\n"    /* Preserve r2 ('code') in a callee-saved register. */
 "   bl  " SYMBOL_STRING_RELOC(SetVMFrameRegs)   "\n"
 "   mov     r0, sp"                             "\n"
 "   bl  " SYMBOL_STRING_RELOC(PushActiveVMFrame)"\n"
 
-    /* Call the compiled JavaScript function. We do this with an unaligned sp because the compiled
-     * script explicitly pushes the return value into f->scriptedReturn. */
-"   add     sp, sp, #(4*1)"                     "\n"
+    /* Call the compiled JavaScript function. */
 "   blx     r4"                                 "\n"
-"   sub     sp, sp, #(4*1)"                     "\n"
 
     /* Tidy up. */
 "   mov     r0, sp"                             "\n"
 "   bl  " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
 "   mov     r0, sp"                             "\n"
 "   bl  " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
 
     /* Skip past the parameters we pushed (such as cx and the like). */
@@ -418,30 +513,32 @@ SYMBOL_STRING(JaegerTrampoline) ":"     
 "   mov     r0, #1"                         "\n"
 "   pop     {r4-r11,pc}"                    "\n"
 );
 
 asm volatile (
 ".text\n"
 ".globl " SYMBOL_STRING(JaegerThrowpoline)  "\n"
 SYMBOL_STRING(JaegerThrowpoline) ":"        "\n"
-    /* Restore 'f', as it will have been clobbered. */
+    /* Find the VMFrame pointer for js_InternalThrow. */
 "   mov     r0, sp"                         "\n"
 
     /* Call the utility function that sets up the internal throw routine. */
 "   bl  " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
     
-    /* If 0 was returned, just bail out as normal. Otherwise, we have a 'catch' or 'finally' clause
-     * to execute. */
+    /* If js_InternalThrow found a scripted handler, jump to it. Otherwise, tidy
+     * up and return. */
 "   cmp     r0, #0"                         "\n"
 "   bxne    r0"                             "\n"
 
-    /* Skip past the parameters we pushed (such as cx and the like). */
-"   add     sp, sp, #(4*7 + 4*4)"           "\n"
-
+    /* Tidy up, then return '0' to represent an unhandled exception. */
+"   mov     r0, sp"                             "\n"
+"   bl  " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
+"   add     sp, sp, #(4*7 + 4*4)"               "\n"
+"   mov     r0, #0"                         "\n"
 "   pop     {r4-r11,pc}"                    "\n"
 );
 
 asm volatile (
 ".text\n"
 ".globl " SYMBOL_STRING(JaegerStubVeneer)   "\n"
 SYMBOL_STRING(JaegerStubVeneer) ":"         "\n"
     /* We enter this function as a veneer between a compiled method and one of the js_ stubs. We
@@ -468,29 +565,39 @@ SYMBOL_STRING(JaegerStubVeneer) ":"     
  * up the argument.
  *    *** DANGER ***
  */
 JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x2c);
 JS_STATIC_ASSERT(offsetof(VMFrame, fp) == 0x1C);
 
 extern "C" {
 
-    __declspec(naked) void JaegerFromTracer()
+    __declspec(naked) void InjectJaegerReturn()
     {
         __asm {
             mov edx, [ebx + 0x28];
             mov ecx, [ebx + 0x2C];
             mov eax, [ebx + 0x3C];
             mov ebx, [esp + 0x1C];
+            push eax;
             ret;
         }
     }
 
+    __declspec(naked) void SafePointTrampoline()
+    {
+        __asm {
+            pop eax;
+            mov eax, [ebx + 0x3C];
+            jmp [ebp + 24];
+        }
+    }
+
     __declspec(naked) JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
-                                              Value *stackLimit)
+                                              Value *stackLimit, void *safePoint)
     {
         __asm {
             /* Prologue. */
             push ebp;
             mov ebp, esp;
             /* Save non-volatile registers. */
             push esi;
             push edi;
@@ -498,33 +605,31 @@ extern "C" {
 
             /* Build the JIT frame. Push fields in order, 
              * then align the stack to form esp == VMFrame. */
             mov  ebx, [ebp + 12];
             push ebx;
             push [ebp + 20];
             push [ebp + 8];
             push ebx;
-            sub  esp, 0x18;
+            sub  esp, 0x1C;
 
             /* Jump into into the JIT'd code. */
-            push [ebp+16];
             mov  ecx, esp;
             call SetVMFrameRegs;
             mov  ecx, esp;
             call PushActiveVMFrame;
-            pop  edx;
 
-            call edx;
-            lea  ecx, [esp-4];
+            call [ebp + 16];
+            mov  ecx, esp;
             call PopActiveVMFrame;
-            lea  ecx, [esp-4];
+            mov  ecx, esp;
             call UnsetVMFrameRegs;
 
-            add esp, 0x28
+            add esp, 0x2C;
 
             pop ebx;
             pop edi;
             pop esi;
             pop ebp;
             mov eax, 1;
             ret;
         }
@@ -613,71 +718,85 @@ ThreadData::Finish()
     fprintf(fp, "%03d %s %d\n", val, #op, StubCallsForOp[val]);
 # include "jsopcode.tbl"
 # undef OPDEF
     fclose(fp);
 #endif
 }
 
 extern "C" JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
-                                   Value *stackLimit);
+                                   Value *stackLimit, void *safePoint);
+extern "C" void SafePointTrampoline();
 
-JSBool
-mjit::JaegerShot(JSContext *cx)
+static inline JSBool
+EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, void *safePoint)
 {
     JS_ASSERT(cx->regs);
-
     JS_CHECK_RECURSION(cx, return JS_FALSE;);
 
-    void *code;
-    jsbytecode *pc = cx->regs->pc;
-    JSStackFrame *fp = cx->fp;
-    JSScript *script = fp->getScript();
-
-    JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD);
-
-#ifdef JS_TRACER
-    if (TRACE_RECORDER(cx))
-        AbortRecording(cx, "attempt to enter method JIT while recording");
-#endif
-
-    if (pc == script->code)
-        code = script->nmap[-1];
-    else
-        code = script->nmap[pc - script->code];
-
-    JS_ASSERT(code);
-
 #ifdef JS_METHODJIT_SPEW
     Profiler prof;
+    JSScript *script = fp->getScript();
 
-    JaegerSpew(JSpew_Prof, "entering jaeger script: %s, line %d\n", script->filename,
-               script->lineno);
+    JaegerSpew(JSpew_Prof, "%s jaeger script: %s, line %d\n",
+               safePoint ? "dropping" : "entering",
+               script->filename, script->lineno);
     prof.start();
 #endif
 
 #ifdef DEBUG
     JSStackFrame *checkFp = fp;
 #endif
 
     Value *stackLimit = cx->stack().makeStackLimit(reinterpret_cast<Value*>(fp));
 
     JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
-    JSBool ok = JaegerTrampoline(cx, fp, code, stackLimit);
+    JSBool ok = JaegerTrampoline(cx, fp, code, stackLimit, safePoint);
 
     JS_ASSERT(checkFp == cx->fp);
 
 #ifdef JS_METHODJIT_SPEW
     prof.stop();
     JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms());
 #endif
 
     return ok;
 }
 
+JSBool
+mjit::JaegerShot(JSContext *cx)
+{
+    JSScript *script = cx->fp->getScript();
+
+    JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD);
+
+#ifdef JS_TRACER
+    if (TRACE_RECORDER(cx))
+        AbortRecording(cx, "attempt to enter method JIT while recording");
+#endif
+
+    JS_ASSERT(cx->regs->pc == script->code);
+
+    void *code = script->nmap[-1];
+
+    return EnterMethodJIT(cx, cx->fp, code, NULL);
+}
+
+JSBool
+js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint)
+{
+#ifdef JS_TRACER
+    JS_ASSERT(!TRACE_RECORDER(cx));
+#endif
+
+    void *code = JS_FUNC_TO_DATA_PTR(void *, SafePointTrampoline);
+
+    return EnterMethodJIT(cx, cx->fp, code, safePoint);
+}
+
 template <typename T>
 static inline void Destroy(T &t)
 {
     t.~T();
 }
 
 void
 mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
@@ -715,23 +834,16 @@ mjit::ReleaseScriptCode(JSContext *cx, J
         script->callSites = NULL;
     }
 #if defined JS_MONOIC
     if (script->mics) {
         cx->free((uint8*)script->mics - sizeof(uint32));
         script->mics = NULL;
     }
 #endif
-
-# if 0 /* def JS_TRACER */
-    if (script->trees) {
-        cx->free(script->trees);
-        script->trees = NULL;
-    }
-# endif
 }
 
 #ifdef JS_METHODJIT_PROFILE_STUBS
 void JS_FASTCALL
 mjit::ProfileStubCall(VMFrame &f)
 {
     JSOp op = JSOp(*f.regs.pc);
     StubCallsForOp[op]++;
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -52,23 +52,21 @@
 #if !defined(JS_NUNBOX32) && !defined(JS_PUNBOX64)
 # error "No boxing format selected."
 #endif
 
 namespace js {
 
 struct VMFrame
 {
-    /* This must be the first entry on CPUs which push return addresses. */
-    void *scriptedReturn;
-
     union Arguments {
         struct {
             void *ptr;
             void *ptr2;
+            void *ptr3;
         } x;
     } u;
 
     VMFrame      *previous;
     JSFrameRegs  *oldRegs;
     JSFrameRegs  regs;
     JSStackFrame *fp;
     JSContext    *cx;
@@ -168,18 +166,21 @@ typedef JSString * (JS_FASTCALL *JSStrSt
 typedef JSString * (JS_FASTCALL *JSStrStubUInt32)(VMFrame &, uint32);
 typedef void (JS_FASTCALL *VoidStubJSObj)(VMFrame &, JSObject *);
 typedef void (JS_FASTCALL *VoidStubPC)(VMFrame &, jsbytecode *);
 
 #define JS_UNJITTABLE_METHOD (reinterpret_cast<void*>(1))
 
 namespace mjit {
 
-JSBool
-JaegerShot(JSContext *cx);
+/* Execute a method that has been JIT compiled. */
+JSBool JaegerShot(JSContext *cx);
+
+/* Drop into the middle of a method at an arbitrary point, and execute. */
+JSBool JaegerShotAtSafePoint(JSContext *cx, void *safePoint);
 
 enum CompileStatus
 {
     Compile_Okay,
     Compile_Abort,
     Compile_Error
 };
 
@@ -219,12 +220,12 @@ union CallSite
 
 } /* namespace js */
 
 #ifdef _MSC_VER
 extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame);
 #else
 extern "C" void JaegerThrowpoline();
 #endif
-extern "C" void JaegerFromTracer();
+extern "C" void InjectJaegerReturn();
 
 #endif /* jsjaeger_h__ */
 
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -131,21 +131,16 @@ Recompiler::recompile()
         }
     }
 
     /* Iterate over VMFrames saving the machine and scripted return. */
     for (VMFrame *f = JS_METHODJIT_DATA(cx).activeFrame;
          f != NULL;
          f = f->previous) {
 
-        if (script->isValidJitCode(f->scriptedReturn)) {
-            if (!toPatch.append(findPatch(&f->scriptedReturn)))
-                return false;
-        }
-
         void **machineReturn = f->returnAddressLocation();
         if (script->isValidJitCode(*machineReturn)) {
             if (!toPatch.append(findPatch(machineReturn)))
                 return false;
         }
     }
 
     ReleaseScriptCode(cx, script);
--- a/js/src/methodjit/TrampolineCompiler.cpp
+++ b/js/src/methodjit/TrampolineCompiler.cpp
@@ -132,18 +132,20 @@ TrampolineCompiler::generateForceReturn(
     masm.loadPtr(Address(JSFrameReg, offsetof(JSStackFrame, down)), Registers::ReturnReg);
     masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), Registers::ArgReg1);
     masm.storePtr(Registers::ReturnReg, FrameAddress(offsetof(VMFrame, fp)));
     masm.storePtr(Registers::ReturnReg, Address(Registers::ArgReg1, offsetof(JSContext, fp)));
 
     Address rval(JSFrameReg, JSStackFrame::offsetReturnValue());
     masm.loadPayload(rval, JSReturnReg_Data);
     masm.loadTypeTag(rval, JSReturnReg_Type);
+
+    masm.restoreReturnAddress();
+
     masm.move(Registers::ReturnReg, JSFrameReg);
-    masm.loadPtr(Address(JSFrameReg, offsetof(JSStackFrame, ncode)), Registers::ReturnReg);
 #ifdef DEBUG
     masm.storePtr(ImmPtr(JSStackFrame::sInvalidPC),
                   Address(JSFrameReg, offsetof(JSStackFrame, savedPC)));
 #endif
 
     masm.ret();
     return true;
 }