[JAEGER] Backout merge.
authorDavid Mandelin <dmandelin@mozilla.com>
Thu, 19 Aug 2010 16:27:04 -0700
changeset 53465 22c103069d7c105adc3457093fbc5482e5557df8
parent 53463 b0fa07efbce2cc488adf62335bb5c28e0f378d0a (current diff)
parent 53464 56d0d08bd835de1cd6578741c2f64d7ca9191f03 (diff)
child 53466 a6f55b452f916635e8cc51be5a4418f41d08c410
push idunknown
push userunknown
push dateunknown
milestone2.0b5pre
[JAEGER] Backout merge.
js/src/methodjit/Compiler.cpp
js/src/methodjit/MethodJIT.cpp
js/src/methodjit/TrampolineCompiler.cpp
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -162,44 +162,43 @@ 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();
-
-    saveReturnAddress();
+#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);
 
     /*
      * 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();
-        saveReturnAddress();
+#ifdef JS_CPU_ARM
+        masm.push(JSC::ARMRegisters::lr);
+#endif
+        restoreFrameRegs(masm);
 
         /* 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. */
@@ -1557,37 +1556,26 @@ mjit::Compiler::jsop_getglobal(uint32 in
 
     RegisterID reg = frame.allocReg();
     Address address = masm.objSlotRef(globalObj, reg, slot);
     frame.freeReg(reg);
     frame.push(address);
 }
 
 void
-mjit::Compiler::restoreReturnAddress(Assembler &masm)
-{
-#ifndef JS_CPU_ARM
-    masm.push(Address(JSFrameReg, offsetof(JSStackFrame, ncode)));
-#else
-    masm.move(Address(JSFrameReg, offsetof(JSStackFrame, ncode)), JSC::ARMRegisters::lr);
-#endif
-}
-
-void
 mjit::Compiler::emitReturn()
 {
     /*
      * if (!f.inlineCallCount)
      *     return;
      */
     Jump noInlineCalls = masm.branchPtr(Assembler::Equal,
                                         FrameAddress(offsetof(VMFrame, entryFp)),
                                         JSFrameReg);
     stubcc.linkExit(noInlineCalls, Uses(frame.frameDepth()));
-    restoreReturnAddress(stubcc.masm);
     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
@@ -1638,22 +1626,23 @@ 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);
-    restoreReturnAddress(masm);
     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);
@@ -1830,23 +1819,31 @@ 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,18 +220,16 @@ 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,16 +164,17 @@ 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;
@@ -494,16 +495,17 @@ 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);
@@ -627,16 +629,17 @@ 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;
@@ -760,18 +763,17 @@ 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)) {
-            JSScript *script = fp->getScript();
-            if (!JaegerShotAtSafePoint(cx, script->nmap[cx->regs->pc - script->code])) {
+            if (!JaegerShot(cx)) {
                 if (!SwallowErrors(f, entryFrame))
                     return false;
 
                 /* Could be anywhere - restart outer loop. */
                 continue;
             }
             InlineReturn(f, JS_TRUE);
             AdvanceReturnPC(cx);
@@ -839,16 +841,19 @@ 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);
@@ -874,32 +879,31 @@ 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 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.
+     * 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.
      *
      * 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 InjectJaegerReturn. This
-     * moves |oldFp->rval| into the scripted return registers.
+     * 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.
      */
 
   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. */
@@ -927,17 +931,18 @@ 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);
         }
-        void *retPtr = JS_FUNC_TO_DATA_PTR(void *, InjectJaegerReturn);
+        entryFrame->ncode = f.fp->ncode;
+        void *retPtr = JS_FUNC_TO_DATA_PTR(void *, JaegerFromTracer);
         *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,66 +44,16 @@
 #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++ functions JaegerShot and JaegerBomb.
- *  - 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 JaegerBomb, 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)
 {
@@ -195,42 +145,37 @@ 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  $0x28, %rsp"                  "\n"
+    "subq  $0x30, %rsp"                  "\n"
 
-    /*
-     * 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. */
+    /* Set cx->regs and set the active frame (requires saving rdx). */
     "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.
      */
-    "call *0(%rsp)"                      "\n"
-    "movq %rsp, %rdi"                    "\n"
+    "call *%rdx"                         "\n"
+    "leaq -8(%rsp), %rdi"                "\n"
     "call " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
-    "movq %rsp, %rdi"                    "\n"
+    "leaq -8(%rsp), %rdi"                "\n"
     "call " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
 
-    "addq $0x58, %rsp"                   "\n"
+    "addq $0x50, %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"
@@ -262,38 +207,28 @@ 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(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"
+".globl " SYMBOL_STRING(JaegerFromTracer)   "\n"
+SYMBOL_STRING(JaegerFromTracer) ":"         "\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
@@ -313,36 +248,38 @@ 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"   /* load fp */
+    "movl  12(%ebp), %ebx"               "\n"   /* fp */
     "pushl %ebx"                         "\n"   /* entryFp */
-    "pushl 20(%ebp)"                     "\n"   /* stackLimit */
-    "pushl 8(%ebp)"                      "\n"   /* cx */
-    "pushl %ebx"                         "\n"   /* fp */
-    "subl $0x1C, %esp"                   "\n"
+    "pushl 20(%ebp)"                     "\n"   /* inlineCallCount */
+    "pushl 8(%ebp)"                      "\n"
+    "pushl %ebx"                         "\n"
+    "subl $0x18, %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  *16(%ebp)"                    "\n"
-    "movl  %esp, %ecx"                   "\n"
+    "call  *%edx"                        "\n"
+    "leal  -4(%esp), %ecx"               "\n"
     "call " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
-    "movl  %esp, %ecx"                   "\n"
+    "leal  -4(%esp), %ecx"               "\n"
     "call " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
 
-    "addl $0x2C, %esp"                   "\n"
+    "addl $0x28, %esp"                   "\n"
     "popl %ebx"                          "\n"
     "popl %edi"                          "\n"
     "popl %esi"                          "\n"
     "popl %ebp"                          "\n"
     "movl $1, %eax"                      "\n"
     "ret"                                "\n"
 );
 
@@ -375,76 +312,48 @@ 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(InjectJaegerReturn)   "\n"
-SYMBOL_STRING(InjectJaegerReturn) ":"         "\n"
+".globl " SYMBOL_STRING(JaegerFromTracer)   "\n"
+SYMBOL_STRING(JaegerFromTracer) ":"         "\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);
-JS_STATIC_ASSERT(offsetof(JSStackFrame, rval) == 40);
 
 asm volatile (
 ".text\n"
-".globl " SYMBOL_STRING(InjectJaegerReturn)   "\n"
-SYMBOL_STRING(InjectJaegerReturn) ":"         "\n"
+".globl " SYMBOL_STRING(JaegerFromTracer)   "\n"
+SYMBOL_STRING(JaegerFromTracer) ":"         "\n"
     /* Restore frame regs. */
-    "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 */
+    "ldr r11, [sp, #32]"                    "\n"
     "bx  r0"                                "\n"
 );
 
 asm volatile (
 ".text\n"
-".globl " SYMBOL_STRING(SafePointTrampoline)  "\n"
-SYMBOL_STRING(SafePointTrampoline) ":"
-    "str lr, [r11, #60]"                    "\n"
-    /* This should load the fifth parameter from JaegerTrampoline and jump to it. */
-    ""                                 "\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
@@ -485,18 +394,21 @@ SYMBOL_STRING(JaegerTrampoline) ":"     
 "   sub     sp, sp, #(4*7)"                     "\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. */
+    /* 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"
 "   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). */
@@ -556,39 +468,29 @@ 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 InjectJaegerReturn()
+    __declspec(naked) void JaegerFromTracer()
     {
         __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, void *safePoint)
+                                              Value *stackLimit)
     {
         __asm {
             /* Prologue. */
             push ebp;
             mov ebp, esp;
             /* Save non-volatile registers. */
             push esi;
             push edi;
@@ -596,31 +498,33 @@ 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, 0x1C;
+            sub  esp, 0x18;
 
             /* Jump into into the JIT'd code. */
+            push [ebp+16];
             mov  ecx, esp;
             call SetVMFrameRegs;
             mov  ecx, esp;
             call PushActiveVMFrame;
+            pop  edx;
 
-            call [ebp + 16];
-            mov  ecx, esp;
+            call edx;
+            lea  ecx, [esp-4];
             call PopActiveVMFrame;
-            mov  ecx, esp;
+            lea  ecx, [esp-4];
             call UnsetVMFrameRegs;
 
-            add esp, 0x2C;
+            add esp, 0x28
 
             pop ebx;
             pop edi;
             pop esi;
             pop ebp;
             mov eax, 1;
             ret;
         }
@@ -709,85 +613,71 @@ 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, void *safePoint);
-extern "C" void SafePointTrampoline();
+                                   Value *stackLimit);
 
-static inline JSBool
-EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, void *safePoint)
+JSBool
+mjit::JaegerShot(JSContext *cx)
 {
     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, "%s jaeger script: %s, line %d\n",
-               safePoint ? "dropping" : "entering",
-               script->filename, script->lineno);
+    JaegerSpew(JSpew_Prof, "entering jaeger script: %s, line %d\n", 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, safePoint);
+    JSBool ok = JaegerTrampoline(cx, fp, code, stackLimit);
 
     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)
@@ -825,16 +715,23 @@ 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,21 +52,23 @@
 #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;
@@ -166,21 +168,18 @@ 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 {
 
-/* 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);
+JSBool
+JaegerShot(JSContext *cx);
 
 enum CompileStatus
 {
     Compile_Okay,
     Compile_Abort,
     Compile_Error
 };
 
@@ -220,12 +219,12 @@ union CallSite
 
 } /* namespace js */
 
 #ifdef _MSC_VER
 extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame);
 #else
 extern "C" void JaegerThrowpoline();
 #endif
-extern "C" void InjectJaegerReturn();
+extern "C" void JaegerFromTracer();
 
 #endif /* jsjaeger_h__ */
 
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -131,16 +131,21 @@ 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,24 +132,18 @@ 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);
-
-#ifndef JS_CPU_ARM
-    masm.push(Address(JSFrameReg, offsetof(JSStackFrame, ncode)));
-#else
-    masm.move(Address(JSFrameReg, offsetof(JSStackFrame, ncode)), JSC::ARMRegisters::lr);
-#endif
-
     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;
 }