[JAEGER] Backout merge.
[JAEGER] Backout merge.
--- 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;
}