mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 15:15:23 +00:00
[JAEGER] Merge.
This commit is contained in:
commit
948466366e
@ -313,17 +313,17 @@ static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::ARMRegister
|
||||
return MacroAssembler::call(reg);
|
||||
}
|
||||
|
||||
#if defined(JS_CPU_ARM)
|
||||
void ret() {
|
||||
/* The return sequence emitted by the ARM macro-assembler assumes that the return address
|
||||
* is in LR. We could load it into LR before calling it, but it's probably better to simply
|
||||
* pop into the PC. (Note that JaegerTrampoline expects the return sequence to pop this
|
||||
* single word from the stack, so the stack will be unaligned on return from JIT code.
|
||||
* JaegerTrampoline fixes this up.) */
|
||||
MacroAssembler::pop(JSC::ARMRegisters::pc);
|
||||
}
|
||||
// #else fall back to the inherited implementation in MacroAssembler::ret().
|
||||
void restoreReturnAddress()
|
||||
{
|
||||
#ifndef JS_CPU_ARM
|
||||
/* X86 and X64's "ret" instruction expects a return address on the stack. */
|
||||
push(Address(JSFrameReg, offsetof(JSStackFrame, ncode)));
|
||||
#else
|
||||
/* ARM returns either using its link register (LR) or directly from the stack, but masm.ret()
|
||||
* always emits a return to LR. */
|
||||
load32(Address(JSFrameReg, offsetof(JSStackFrame, ncode)), JSC::ARMRegisters::lr);
|
||||
#endif
|
||||
}
|
||||
|
||||
void finalize(uint8 *ncode) {
|
||||
JSC::JITCode jc(ncode, size());
|
||||
|
@ -167,21 +167,25 @@ mjit::TryCompile(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *sco
|
||||
return status;
|
||||
}
|
||||
|
||||
void
|
||||
mjit::Compiler::saveReturnAddress()
|
||||
{
|
||||
#ifndef JS_CPU_ARM
|
||||
masm.pop(Registers::ReturnReg);
|
||||
restoreFrameRegs(masm);
|
||||
masm.storePtr(Registers::ReturnReg, Address(JSFrameReg, offsetof(JSStackFrame, ncode)));
|
||||
#else
|
||||
restoreFrameRegs(masm);
|
||||
masm.storePtr(JSC::ARMRegisters::lr, Address(JSFrameReg, offsetof(JSStackFrame, ncode)));
|
||||
#endif
|
||||
}
|
||||
|
||||
CompileStatus
|
||||
mjit::Compiler::generatePrologue()
|
||||
{
|
||||
invokeLabel = masm.label();
|
||||
#ifdef JS_CPU_ARM
|
||||
/*
|
||||
* Unlike x86/x64, the return address is not automatically pushed onto the stack during a call
|
||||
* (blx). To compensate, we explicitly push it here.
|
||||
*
|
||||
* NOTE: The ABI requires that we maintain 8-byte stack alignment at function boundaries. The
|
||||
* trampoline always enters this function with an unaligned stack so we can re-align it.
|
||||
*/
|
||||
masm.push(JSC::ARMRegisters::lr);
|
||||
#endif
|
||||
restoreFrameRegs(masm);
|
||||
|
||||
saveReturnAddress();
|
||||
|
||||
/*
|
||||
* If there is no function, then this can only be called via JaegerShot(),
|
||||
@ -190,10 +194,7 @@ mjit::Compiler::generatePrologue()
|
||||
if (fun) {
|
||||
Jump j = masm.jump();
|
||||
invokeLabel = masm.label();
|
||||
#ifdef JS_CPU_ARM
|
||||
masm.push(JSC::ARMRegisters::lr);
|
||||
#endif
|
||||
restoreFrameRegs(masm);
|
||||
saveReturnAddress();
|
||||
|
||||
/* Set locals to undefined. */
|
||||
for (uint32 i = 0; i < script->nslots; i++) {
|
||||
@ -1571,6 +1572,7 @@ mjit::Compiler::emitReturn()
|
||||
FrameAddress(offsetof(VMFrame, entryFp)),
|
||||
JSFrameReg);
|
||||
stubcc.linkExit(noInlineCalls, Uses(frame.frameDepth()));
|
||||
stubcc.masm.restoreReturnAddress();
|
||||
stubcc.masm.ret();
|
||||
|
||||
JS_ASSERT_IF(!fun, JSOp(*PC) == JSOP_STOP);
|
||||
@ -1631,13 +1633,12 @@ mjit::Compiler::emitReturn()
|
||||
Address rval(JSFrameReg, JSStackFrame::offsetReturnValue());
|
||||
masm.loadPayload(rval, JSReturnReg_Data);
|
||||
masm.loadTypeTag(rval, JSReturnReg_Type);
|
||||
masm.restoreReturnAddress();
|
||||
masm.move(Registers::ReturnReg, JSFrameReg);
|
||||
masm.loadPtr(Address(JSFrameReg, offsetof(JSStackFrame, ncode)), Registers::ReturnReg);
|
||||
#ifdef DEBUG
|
||||
masm.storePtr(ImmPtr(JSStackFrame::sInvalidPC),
|
||||
Address(JSFrameReg, offsetof(JSStackFrame, savedPC)));
|
||||
#endif
|
||||
|
||||
masm.ret();
|
||||
}
|
||||
|
||||
@ -1824,20 +1825,12 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew)
|
||||
}
|
||||
|
||||
/* 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.
|
||||
|
@ -225,6 +225,8 @@ class Compiler
|
||||
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);
|
||||
|
@ -169,7 +169,6 @@ top:
|
||||
static inline void
|
||||
FixVMFrame(VMFrame &f, JSStackFrame *fp)
|
||||
{
|
||||
f.fp->ncode = f.scriptedReturn;
|
||||
JS_ASSERT(f.fp == fp->down);
|
||||
f.fp = fp;
|
||||
}
|
||||
@ -500,7 +499,6 @@ CreateLightFrame(VMFrame &f, uint32 flags, uint32 argc)
|
||||
}
|
||||
|
||||
/* Initialize the frame. */
|
||||
newfp->ncode = NULL;
|
||||
newfp->setCallObj(NULL);
|
||||
newfp->setArgsObj(NULL);
|
||||
newfp->setScript(newscript);
|
||||
@ -634,7 +632,6 @@ js_InternalThrow(VMFrame &f)
|
||||
|
||||
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);
|
||||
@ -768,7 +765,8 @@ RemoveExcessFrames(VMFrame &f, JSStackFrame *entryFrame)
|
||||
fp->flags &= ~JSFRAME_RECORDING;
|
||||
|
||||
if (AtSafePoint(cx)) {
|
||||
if (!JaegerShot(cx)) {
|
||||
JSScript *script = fp->getScript();
|
||||
if (!JaegerShotAtSafePoint(cx, script->nmap[cx->regs->pc - script->code])) {
|
||||
if (!SwallowErrors(f, entryFrame))
|
||||
return false;
|
||||
|
||||
@ -846,9 +844,6 @@ RunTracer(VMFrame &f)
|
||||
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);
|
||||
@ -884,9 +879,12 @@ RunTracer(VMFrame &f)
|
||||
* The tracer could have dropped us off on any frame at any position.
|
||||
* Well, it could not have removed frames (recursion is disabled).
|
||||
*
|
||||
* Frames after the entryFrame cannot be entered via JaegerShot()
|
||||
* unless each is at a safe point. We can JaegerShot these frames
|
||||
* individually, but we must unwind to the entryFrame.
|
||||
* Frames after the entryFrame cannot be entered via JaegerShotAtSafePoint()
|
||||
* unless each is at a safe point. We can JaegerShotAtSafePoint these
|
||||
* frames individually, but we must unwind to the entryFrame.
|
||||
*
|
||||
* Note carefully that JaegerShotAtSafePoint can resume methods at
|
||||
* arbitrary safe points whereas JaegerShot cannot.
|
||||
*
|
||||
* If we land on entryFrame without a safe point in sight, we'll end up
|
||||
* at the RETURN op. This is an edge case with two paths:
|
||||
@ -895,10 +893,8 @@ RunTracer(VMFrame &f)
|
||||
* move the return value down.
|
||||
* 2) The entryFrame is NOT the last inline frame. Pop the frame.
|
||||
*
|
||||
* In both cases, we hijack the stub to return to JaegerFromTracer. This
|
||||
* moves |oldFp->rval| into the scripted return registers, places the
|
||||
* new f.scriptedReturn in the machine return register, and returns to its
|
||||
* caller safely.
|
||||
* In both cases, we hijack the stub to return to InjectJaegerReturn. This
|
||||
* moves |oldFp->rval| into the scripted return registers.
|
||||
*/
|
||||
|
||||
restart:
|
||||
@ -936,8 +932,7 @@ RunTracer(VMFrame &f)
|
||||
if (!InlineReturn(f, JS_TRUE))
|
||||
THROWV(NULL);
|
||||
}
|
||||
entryFrame->ncode = f.fp->ncode;
|
||||
void *retPtr = JS_FUNC_TO_DATA_PTR(void *, JaegerFromTracer);
|
||||
void *retPtr = JS_FUNC_TO_DATA_PTR(void *, InjectJaegerReturn);
|
||||
*f.returnAddressLocation() = retPtr;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -49,6 +49,55 @@
|
||||
using namespace js;
|
||||
using namespace js::mjit;
|
||||
|
||||
/*
|
||||
* Explanation of VMFrame activation and various helper thunks below.
|
||||
*
|
||||
* JaegerTrampoline - Executes a method JIT-compiled JSFunction. This function
|
||||
* creates a VMFrame on the machine stack and calls into JIT'd code. The JIT'd
|
||||
* code will eventually return to the VMFrame.
|
||||
*
|
||||
* - Called from C++ function EnterMethodJIT.
|
||||
* - Parameters: cx, fp, code, stackLimit, safePoint
|
||||
* - Notes: safePoint is used in combination with SafePointTrampoline,
|
||||
* explained further down.
|
||||
*
|
||||
* JaegerThrowpoline - Calls into an exception handler from JIT'd code, and if a
|
||||
* scripted exception handler is not found, unwinds the VMFrame and returns
|
||||
* to C++.
|
||||
*
|
||||
* - To start exception handling, we return from a stub call to the throwpoline.
|
||||
* - On entry to the throwpoline, the normal conditions of the jit-code ABI
|
||||
* are satisfied.
|
||||
* - To do the unwinding and find out where to continue executing, we call
|
||||
* js_InternalThrow.
|
||||
* - js_InternalThrow may return 0, which means the place to continue, if any,
|
||||
* is above this JaegerShot activation, so we just return, in the same way
|
||||
* the trampoline does.
|
||||
* - Otherwise, js_InternalThrow returns a jit-code address to continue execution
|
||||
* at. Because the jit-code ABI conditions are satisfied, we can just jump to
|
||||
* that point.
|
||||
*
|
||||
*
|
||||
* SafePointTrampoline - Inline script calls link their return addresses through
|
||||
* JSStackFrame::ncode. This includes the return address that unwinds back
|
||||
* to JaegerTrampoline. However, the tracer integration code often wants to
|
||||
* enter a method JIT'd function at an arbitrary safe point. Safe points
|
||||
* do not have the return address linking code that the method prologue has.
|
||||
* SafePointTrampoline is a thunk which correctly links the initial return
|
||||
* address. It is used in JaegerShotAtSafePoint, and passed as the "script
|
||||
* code" parameter. Using the "safePoint" parameter to JaegerTrampoline, it
|
||||
* correctly jumps to the intended point in the method.
|
||||
*
|
||||
* - Used by JaegerTrampoline()
|
||||
*
|
||||
* InjectJaegerReturn - Implements the tail of InlineReturn. This is needed for
|
||||
* tracer integration, where a "return" opcode might not be a safe-point,
|
||||
* and thus the return path must be injected by hijacking the stub return
|
||||
* address.
|
||||
*
|
||||
* - Used by RunTracer()
|
||||
*/
|
||||
|
||||
#ifdef JS_METHODJIT_PROFILE_STUBS
|
||||
static const size_t STUB_CALLS_FOR_OP_COUNT = 255;
|
||||
static uint32 StubCallsForOp[STUB_CALLS_FOR_OP_COUNT];
|
||||
@ -150,27 +199,31 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n"
|
||||
"movq %rsi, %rbx" "\n"
|
||||
|
||||
/* Space for the rest of the VMFrame. */
|
||||
"subq $0x30, %rsp" "\n"
|
||||
"subq $0x28, %rsp" "\n"
|
||||
|
||||
/* Set cx->regs and set the active frame (requires saving rdx). */
|
||||
/*
|
||||
* This is actually part of the VMFrame, but we need to save |r8| for
|
||||
* SafePointTrampoline.
|
||||
*/
|
||||
"pushq %r8" "\n"
|
||||
|
||||
/* Set cx->regs and set the active frame. Save rdx and align frame in one. */
|
||||
"pushq %rdx" "\n"
|
||||
"movq %rsp, %rdi" "\n"
|
||||
"call " SYMBOL_STRING_RELOC(SetVMFrameRegs) "\n"
|
||||
"movq %rsp, %rdi" "\n"
|
||||
"call " SYMBOL_STRING_RELOC(PushActiveVMFrame) "\n"
|
||||
"popq %rdx" "\n"
|
||||
|
||||
/*
|
||||
* Jump into into the JIT'd code. The call implicitly fills in
|
||||
* the precious f.scriptedReturn member of VMFrame.
|
||||
* Jump into into the JIT'd code.
|
||||
*/
|
||||
"call *%rdx" "\n"
|
||||
"leaq -8(%rsp), %rdi" "\n"
|
||||
"call *0(%rsp)" "\n"
|
||||
"movq %rsp, %rdi" "\n"
|
||||
"call " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
|
||||
"leaq -8(%rsp), %rdi" "\n"
|
||||
"movq %rsp, %rdi" "\n"
|
||||
"call " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
|
||||
|
||||
"addq $0x50, %rsp" "\n"
|
||||
"addq $0x58, %rsp" "\n"
|
||||
"popq %rbx" "\n"
|
||||
"popq %r15" "\n"
|
||||
"popq %r14" "\n"
|
||||
@ -212,8 +265,17 @@ JS_STATIC_ASSERT(JSVAL_PAYLOAD_MASK == 0x00007FFFFFFFFFFFLL);
|
||||
|
||||
asm volatile (
|
||||
".text\n"
|
||||
".globl " SYMBOL_STRING(JaegerFromTracer) "\n"
|
||||
SYMBOL_STRING(JaegerFromTracer) ":" "\n"
|
||||
".globl " SYMBOL_STRING(SafePointTrampoline) "\n"
|
||||
SYMBOL_STRING(SafePointTrampoline) ":" "\n"
|
||||
"popq %rax" "\n"
|
||||
"movq %rax, 0x60(%rbx)" "\n"
|
||||
"jmp *8(%rsp)" "\n"
|
||||
);
|
||||
|
||||
asm volatile (
|
||||
".text\n"
|
||||
".globl " SYMBOL_STRING(InjectJaegerReturn) "\n"
|
||||
SYMBOL_STRING(InjectJaegerReturn) ":" "\n"
|
||||
"movq 0x40(%rbx), %rcx" "\n" /* fp->rval type (as value) */
|
||||
"movq $0xFFFF800000000000, %r11" "\n" /* load type mask (JSVAL_TAG_MASK) */
|
||||
"andq %r11, %rcx" "\n" /* extract type */
|
||||
@ -224,6 +286,7 @@ SYMBOL_STRING(JaegerFromTracer) ":" "\n"
|
||||
|
||||
"movq 0x60(%rbx), %rax" "\n" /* fp->ncode */
|
||||
"movq 0x38(%rsp), %rbx" "\n" /* f.fp */
|
||||
"pushq %rax" "\n"
|
||||
"ret" "\n"
|
||||
);
|
||||
|
||||
@ -253,28 +316,26 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n"
|
||||
|
||||
/* Build the JIT frame. Push fields in order,
|
||||
* then align the stack to form esp == VMFrame. */
|
||||
"movl 12(%ebp), %ebx" "\n" /* fp */
|
||||
"movl 12(%ebp), %ebx" "\n" /* load fp */
|
||||
"pushl %ebx" "\n" /* entryFp */
|
||||
"pushl 20(%ebp)" "\n" /* inlineCallCount */
|
||||
"pushl 8(%ebp)" "\n"
|
||||
"pushl %ebx" "\n"
|
||||
"subl $0x18, %esp" "\n"
|
||||
"pushl 20(%ebp)" "\n" /* stackLimit */
|
||||
"pushl 8(%ebp)" "\n" /* cx */
|
||||
"pushl %ebx" "\n" /* fp */
|
||||
"subl $0x1C, %esp" "\n"
|
||||
|
||||
/* Jump into the JIT'd code. */
|
||||
"pushl 16(%ebp)" "\n"
|
||||
"movl %esp, %ecx" "\n"
|
||||
"call " SYMBOL_STRING_RELOC(SetVMFrameRegs) "\n"
|
||||
"movl %esp, %ecx" "\n"
|
||||
"call " SYMBOL_STRING_RELOC(PushActiveVMFrame) "\n"
|
||||
"popl %edx" "\n"
|
||||
|
||||
"call *%edx" "\n"
|
||||
"leal -4(%esp), %ecx" "\n"
|
||||
"call *16(%ebp)" "\n"
|
||||
"movl %esp, %ecx" "\n"
|
||||
"call " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
|
||||
"leal -4(%esp), %ecx" "\n"
|
||||
"movl %esp, %ecx" "\n"
|
||||
"call " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
|
||||
|
||||
"addl $0x28, %esp" "\n"
|
||||
"addl $0x2C, %esp" "\n"
|
||||
"popl %ebx" "\n"
|
||||
"popl %edi" "\n"
|
||||
"popl %esi" "\n"
|
||||
@ -317,15 +378,29 @@ JS_STATIC_ASSERT(offsetof(VMFrame, fp) == 0x1C);
|
||||
|
||||
asm volatile (
|
||||
".text\n"
|
||||
".globl " SYMBOL_STRING(JaegerFromTracer) "\n"
|
||||
SYMBOL_STRING(JaegerFromTracer) ":" "\n"
|
||||
".globl " SYMBOL_STRING(InjectJaegerReturn) "\n"
|
||||
SYMBOL_STRING(InjectJaegerReturn) ":" "\n"
|
||||
"movl 0x28(%ebx), %edx" "\n" /* fp->rval data */
|
||||
"movl 0x2C(%ebx), %ecx" "\n" /* fp->rval type */
|
||||
"movl 0x3C(%ebx), %eax" "\n" /* fp->ncode */
|
||||
"movl 0x1C(%esp), %ebx" "\n" /* f.fp */
|
||||
"pushl %eax" "\n"
|
||||
"ret" "\n"
|
||||
);
|
||||
|
||||
/*
|
||||
* Take the fifth parameter from JaegerShot() and jump to it. This makes it so
|
||||
* we can jump into arbitrary JIT code, which won't have the frame-fixup prologue.
|
||||
*/
|
||||
asm volatile (
|
||||
".text\n"
|
||||
".globl " SYMBOL_STRING(SafePointTrampoline) "\n"
|
||||
SYMBOL_STRING(SafePointTrampoline) ":" "\n"
|
||||
"popl %eax" "\n"
|
||||
"movl %eax, 0x3C(%ebx)" "\n"
|
||||
"jmp *24(%ebp)" "\n"
|
||||
);
|
||||
|
||||
# elif defined(JS_CPU_ARM)
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(VMFrame) == 80);
|
||||
@ -336,27 +411,48 @@ JS_STATIC_ASSERT(offsetof(VMFrame, cx) == (4*8));
|
||||
JS_STATIC_ASSERT(offsetof(VMFrame, fp) == (4*7));
|
||||
JS_STATIC_ASSERT(offsetof(VMFrame, oldRegs) == (4*4));
|
||||
JS_STATIC_ASSERT(offsetof(VMFrame, previous) == (4*3));
|
||||
JS_STATIC_ASSERT(offsetof(VMFrame, scriptedReturn) == (4*0));
|
||||
JS_STATIC_ASSERT(offsetof(JSStackFrame, ncode) == 60);
|
||||
|
||||
asm volatile (
|
||||
".text\n"
|
||||
".globl " SYMBOL_STRING(JaegerFromTracer) "\n"
|
||||
SYMBOL_STRING(JaegerFromTracer) ":" "\n"
|
||||
".globl " SYMBOL_STRING(InjectJaegerReturn) "\n"
|
||||
SYMBOL_STRING(InjectJaegerReturn) ":" "\n"
|
||||
/* Restore frame regs. */
|
||||
"ldr r11, [sp, #32]" "\n"
|
||||
"ldr r1, [r11, #40]" "\n" /* fp->rval data */
|
||||
"ldr r2, [r11, #44]" "\n" /* fp->rval type */
|
||||
"ldr r0, [r11, #60]" "\n" /* fp->ncode */
|
||||
"ldr r11, [sp, #28]" "\n" /* load f.fp */
|
||||
"bx r0" "\n"
|
||||
);
|
||||
|
||||
asm volatile (
|
||||
".text\n"
|
||||
".globl " SYMBOL_STRING(SafePointTrampoline) "\n"
|
||||
SYMBOL_STRING(SafePointTrampoline) ":"
|
||||
/*
|
||||
* On entry to SafePointTrampoline:
|
||||
* r11 = fp
|
||||
* sp[80] = safePoint
|
||||
*/
|
||||
"ldr ip, [sp, #80]" "\n"
|
||||
/* Save the return address (in JaegerTrampoline) to fp->ncode. */
|
||||
"str lr, [r11, #60]" "\n"
|
||||
/* Jump to 'safePoint' via 'ip' because a load into the PC from an address on
|
||||
* the stack looks like a return, and may upset return stack prediction. */
|
||||
"bx ip" "\n"
|
||||
);
|
||||
|
||||
asm volatile (
|
||||
".text\n"
|
||||
".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
|
||||
SYMBOL_STRING(JaegerTrampoline) ":" "\n"
|
||||
/*
|
||||
* On entry to JaegerTrampoline:
|
||||
* r0 = cx
|
||||
* r1 = fp
|
||||
* r2 = code
|
||||
* r3 = inlineCallCount
|
||||
* r0 = cx
|
||||
* r1 = fp
|
||||
* r2 = code
|
||||
* r3 = stackLimit
|
||||
* sp[0] = safePoint
|
||||
*
|
||||
* The VMFrame for ARM looks like this:
|
||||
* [ lr ] \
|
||||
@ -376,14 +472,12 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n"
|
||||
* [ regs.pc ]
|
||||
* [ oldRegs ]
|
||||
* [ previous ]
|
||||
* [ args.ptr ]
|
||||
* [ args.ptr3 ]
|
||||
* [ args.ptr2 ]
|
||||
* [ srpt. ret ] } Scripted return.
|
||||
* [ args.ptr ]
|
||||
*/
|
||||
|
||||
/* Push callee-saved registers. TODO: Do we actually need to push all of them? If the
|
||||
* compiled JavaScript function is EABI-compliant, we only need to push what we use in
|
||||
* JaegerTrampoline. */
|
||||
/* Push callee-saved registers. */
|
||||
" push {r4-r11,lr}" "\n"
|
||||
/* Push interesting VMFrame content. */
|
||||
" push {r1}" "\n" /* entryFp */
|
||||
@ -393,17 +487,18 @@ SYMBOL_STRING(JaegerTrampoline) ":" "\n"
|
||||
/* Remaining fields are set elsewhere, but we need to leave space for them. */
|
||||
" sub sp, sp, #(4*7)" "\n"
|
||||
|
||||
/* Preserve 'code' (r2) in an arbitrary callee-saved register. */
|
||||
" mov r4, r2" "\n"
|
||||
/* Preserve 'fp' (r1) in r11 (JSFrameReg) for SafePointTrampoline. */
|
||||
" mov r11, r1" "\n"
|
||||
|
||||
" mov r0, sp" "\n"
|
||||
" mov r4, r2" "\n" /* Preserve r2 ('code') in a callee-saved register. */
|
||||
" bl " SYMBOL_STRING_RELOC(SetVMFrameRegs) "\n"
|
||||
" mov r0, sp" "\n"
|
||||
" bl " SYMBOL_STRING_RELOC(PushActiveVMFrame)"\n"
|
||||
|
||||
/* Call the compiled JavaScript function. We do this with an unaligned sp because the compiled
|
||||
* script explicitly pushes the return value into f->scriptedReturn. */
|
||||
" add sp, sp, #(4*1)" "\n"
|
||||
/* Call the compiled JavaScript function. */
|
||||
" blx r4" "\n"
|
||||
" sub sp, sp, #(4*1)" "\n"
|
||||
|
||||
/* Tidy up. */
|
||||
" mov r0, sp" "\n"
|
||||
@ -423,20 +518,22 @@ asm volatile (
|
||||
".text\n"
|
||||
".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
|
||||
SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
|
||||
/* Restore 'f', as it will have been clobbered. */
|
||||
/* Find the VMFrame pointer for js_InternalThrow. */
|
||||
" mov r0, sp" "\n"
|
||||
|
||||
/* Call the utility function that sets up the internal throw routine. */
|
||||
" bl " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
|
||||
|
||||
/* If 0 was returned, just bail out as normal. Otherwise, we have a 'catch' or 'finally' clause
|
||||
* to execute. */
|
||||
/* If js_InternalThrow found a scripted handler, jump to it. Otherwise, tidy
|
||||
* up and return. */
|
||||
" cmp r0, #0" "\n"
|
||||
" bxne r0" "\n"
|
||||
|
||||
/* Skip past the parameters we pushed (such as cx and the like). */
|
||||
" add sp, sp, #(4*7 + 4*4)" "\n"
|
||||
|
||||
/* Tidy up, then return '0' to represent an unhandled exception. */
|
||||
" mov r0, sp" "\n"
|
||||
" bl " SYMBOL_STRING_RELOC(PopActiveVMFrame) "\n"
|
||||
" add sp, sp, #(4*7 + 4*4)" "\n"
|
||||
" mov r0, #0" "\n"
|
||||
" pop {r4-r11,pc}" "\n"
|
||||
);
|
||||
|
||||
@ -473,19 +570,29 @@ JS_STATIC_ASSERT(offsetof(VMFrame, fp) == 0x1C);
|
||||
|
||||
extern "C" {
|
||||
|
||||
__declspec(naked) void JaegerFromTracer()
|
||||
__declspec(naked) void InjectJaegerReturn()
|
||||
{
|
||||
__asm {
|
||||
mov edx, [ebx + 0x28];
|
||||
mov ecx, [ebx + 0x2C];
|
||||
mov eax, [ebx + 0x3C];
|
||||
mov ebx, [esp + 0x1C];
|
||||
push eax;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) void SafePointTrampoline()
|
||||
{
|
||||
__asm {
|
||||
pop eax;
|
||||
mov eax, [ebx + 0x3C];
|
||||
jmp [ebp + 24];
|
||||
}
|
||||
}
|
||||
|
||||
__declspec(naked) JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
|
||||
Value *stackLimit)
|
||||
Value *stackLimit, void *safePoint)
|
||||
{
|
||||
__asm {
|
||||
/* Prologue. */
|
||||
@ -503,23 +610,21 @@ extern "C" {
|
||||
push [ebp + 20];
|
||||
push [ebp + 8];
|
||||
push ebx;
|
||||
sub esp, 0x18;
|
||||
sub esp, 0x1C;
|
||||
|
||||
/* Jump into into the JIT'd code. */
|
||||
push [ebp+16];
|
||||
mov ecx, esp;
|
||||
call SetVMFrameRegs;
|
||||
mov ecx, esp;
|
||||
call PushActiveVMFrame;
|
||||
pop edx;
|
||||
|
||||
call edx;
|
||||
lea ecx, [esp-4];
|
||||
call [ebp + 16];
|
||||
mov ecx, esp;
|
||||
call PopActiveVMFrame;
|
||||
lea ecx, [esp-4];
|
||||
mov ecx, esp;
|
||||
call UnsetVMFrameRegs;
|
||||
|
||||
add esp, 0x28
|
||||
add esp, 0x2C;
|
||||
|
||||
pop ebx;
|
||||
pop edi;
|
||||
@ -618,39 +723,22 @@ ThreadData::Finish()
|
||||
}
|
||||
|
||||
extern "C" JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
|
||||
Value *stackLimit);
|
||||
Value *stackLimit, void *safePoint);
|
||||
extern "C" void SafePointTrampoline();
|
||||
|
||||
JSBool
|
||||
mjit::JaegerShot(JSContext *cx)
|
||||
static inline JSBool
|
||||
EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, void *safePoint)
|
||||
{
|
||||
JS_ASSERT(cx->regs);
|
||||
|
||||
JS_CHECK_RECURSION(cx, return JS_FALSE;);
|
||||
|
||||
void *code;
|
||||
jsbytecode *pc = cx->regs->pc;
|
||||
JSStackFrame *fp = cx->fp;
|
||||
JSScript *script = fp->getScript();
|
||||
|
||||
JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD);
|
||||
|
||||
#ifdef JS_TRACER
|
||||
if (TRACE_RECORDER(cx))
|
||||
AbortRecording(cx, "attempt to enter method JIT while recording");
|
||||
#endif
|
||||
|
||||
if (pc == script->code)
|
||||
code = script->nmap[-1];
|
||||
else
|
||||
code = script->nmap[pc - script->code];
|
||||
|
||||
JS_ASSERT(code);
|
||||
|
||||
#ifdef JS_METHODJIT_SPEW
|
||||
Profiler prof;
|
||||
JSScript *script = fp->getScript();
|
||||
|
||||
JaegerSpew(JSpew_Prof, "entering jaeger script: %s, line %d\n", script->filename,
|
||||
script->lineno);
|
||||
JaegerSpew(JSpew_Prof, "%s jaeger script: %s, line %d\n",
|
||||
safePoint ? "dropping" : "entering",
|
||||
script->filename, script->lineno);
|
||||
prof.start();
|
||||
#endif
|
||||
|
||||
@ -661,7 +749,7 @@ mjit::JaegerShot(JSContext *cx)
|
||||
Value *stackLimit = cx->stack().makeStackLimit(reinterpret_cast<Value*>(fp));
|
||||
|
||||
JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
|
||||
JSBool ok = JaegerTrampoline(cx, fp, code, stackLimit);
|
||||
JSBool ok = JaegerTrampoline(cx, fp, code, stackLimit, safePoint);
|
||||
|
||||
JS_ASSERT(checkFp == cx->fp);
|
||||
|
||||
@ -673,6 +761,37 @@ mjit::JaegerShot(JSContext *cx)
|
||||
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)
|
||||
{
|
||||
@ -720,13 +839,6 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
|
||||
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
|
||||
|
@ -57,13 +57,11 @@ 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;
|
||||
|
||||
@ -173,8 +171,11 @@ typedef void (JS_FASTCALL *VoidStubPC)(VMFrame &, jsbytecode *);
|
||||
|
||||
namespace mjit {
|
||||
|
||||
JSBool
|
||||
JaegerShot(JSContext *cx);
|
||||
/* Execute a method that has been JIT compiled. */
|
||||
JSBool JaegerShot(JSContext *cx);
|
||||
|
||||
/* Drop into the middle of a method at an arbitrary point, and execute. */
|
||||
JSBool JaegerShotAtSafePoint(JSContext *cx, void *safePoint);
|
||||
|
||||
enum CompileStatus
|
||||
{
|
||||
@ -224,7 +225,7 @@ extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame);
|
||||
#else
|
||||
extern "C" void JaegerThrowpoline();
|
||||
#endif
|
||||
extern "C" void JaegerFromTracer();
|
||||
extern "C" void InjectJaegerReturn();
|
||||
|
||||
#endif /* jsjaeger_h__ */
|
||||
|
||||
|
@ -136,11 +136,6 @@ Recompiler::recompile()
|
||||
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)))
|
||||
|
@ -137,8 +137,10 @@ TrampolineCompiler::generateForceReturn(Assembler &masm)
|
||||
Address rval(JSFrameReg, JSStackFrame::offsetReturnValue());
|
||||
masm.loadPayload(rval, JSReturnReg_Data);
|
||||
masm.loadTypeTag(rval, JSReturnReg_Type);
|
||||
|
||||
masm.restoreReturnAddress();
|
||||
|
||||
masm.move(Registers::ReturnReg, JSFrameReg);
|
||||
masm.loadPtr(Address(JSFrameReg, offsetof(JSStackFrame, ncode)), Registers::ReturnReg);
|
||||
#ifdef DEBUG
|
||||
masm.storePtr(ImmPtr(JSStackFrame::sInvalidPC),
|
||||
Address(JSFrameReg, offsetof(JSStackFrame, savedPC)));
|
||||
|
Loading…
Reference in New Issue
Block a user