[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 id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone2.0b5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
[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;
 }