michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/Bailouts.h" michael@0: #include "jit/IonFrames.h" michael@0: #include "jit/IonLinker.h" michael@0: #include "jit/JitCompartment.h" michael@0: #ifdef JS_ION_PERF michael@0: # include "jit/PerfSpewer.h" michael@0: #endif michael@0: #include "jit/VMFunctions.h" michael@0: #include "jit/x64/BaselineHelpers-x64.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: // All registers to save and restore. This includes the stack pointer, since we michael@0: // use the ability to reference register values on the stack by index. michael@0: static const RegisterSet AllRegs = michael@0: RegisterSet(GeneralRegisterSet(Registers::AllMask), michael@0: FloatRegisterSet(FloatRegisters::AllMask)); michael@0: michael@0: /* This method generates a trampoline on x64 for a c++ function with michael@0: * the following signature: michael@0: * bool blah(void *code, int argc, Value *argv, JSObject *scopeChain, michael@0: * Value *vp) michael@0: * ...using standard x64 fastcall calling convention michael@0: */ michael@0: JitCode * michael@0: JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) michael@0: { michael@0: MacroAssembler masm(cx); michael@0: michael@0: const Register reg_code = IntArgReg0; michael@0: const Register reg_argc = IntArgReg1; michael@0: const Register reg_argv = IntArgReg2; michael@0: JS_ASSERT(OsrFrameReg == IntArgReg3); michael@0: michael@0: #if defined(_WIN64) michael@0: const Operand token = Operand(rbp, 16 + ShadowStackSpace); michael@0: const Operand scopeChain = Operand(rbp, 24 + ShadowStackSpace); michael@0: const Operand numStackValuesAddr = Operand(rbp, 32 + ShadowStackSpace); michael@0: const Operand result = Operand(rbp, 40 + ShadowStackSpace); michael@0: #else michael@0: const Register token = IntArgReg4; michael@0: const Register scopeChain = IntArgReg5; michael@0: const Operand numStackValuesAddr = Operand(rbp, 16 + ShadowStackSpace); michael@0: const Operand result = Operand(rbp, 24 + ShadowStackSpace); michael@0: #endif michael@0: michael@0: // Save old stack frame pointer, set new stack frame pointer. michael@0: masm.push(rbp); michael@0: masm.mov(rsp, rbp); michael@0: michael@0: // Save non-volatile registers. These must be saved by the trampoline, rather michael@0: // than by the JIT'd code, because they are scanned by the conservative scanner. michael@0: masm.push(rbx); michael@0: masm.push(r12); michael@0: masm.push(r13); michael@0: masm.push(r14); michael@0: masm.push(r15); michael@0: #if defined(_WIN64) michael@0: masm.push(rdi); michael@0: masm.push(rsi); michael@0: michael@0: // 16-byte aligment for movdqa michael@0: masm.subq(Imm32(16 * 10 + 8), rsp); michael@0: michael@0: masm.movdqa(xmm6, Operand(rsp, 16 * 0)); michael@0: masm.movdqa(xmm7, Operand(rsp, 16 * 1)); michael@0: masm.movdqa(xmm8, Operand(rsp, 16 * 2)); michael@0: masm.movdqa(xmm9, Operand(rsp, 16 * 3)); michael@0: masm.movdqa(xmm10, Operand(rsp, 16 * 4)); michael@0: masm.movdqa(xmm11, Operand(rsp, 16 * 5)); michael@0: masm.movdqa(xmm12, Operand(rsp, 16 * 6)); michael@0: masm.movdqa(xmm13, Operand(rsp, 16 * 7)); michael@0: masm.movdqa(xmm14, Operand(rsp, 16 * 8)); michael@0: masm.movdqa(xmm15, Operand(rsp, 16 * 9)); michael@0: #endif michael@0: michael@0: // Push the EnterJIT sps mark. michael@0: masm.spsMarkJit(&cx->runtime()->spsProfiler, rbp, rbx); michael@0: michael@0: // Save arguments passed in registers needed after function call. michael@0: masm.push(result); michael@0: michael@0: // Remember stack depth without padding and arguments. michael@0: masm.mov(rsp, r14); michael@0: michael@0: // Remember number of bytes occupied by argument vector michael@0: masm.mov(reg_argc, r13); michael@0: masm.shll(Imm32(3), r13); michael@0: michael@0: // Guarantee 16-byte alignment. michael@0: // We push argc, callee token, frame size, and return address. michael@0: // The latter two are 16 bytes together, so we only consider argc and the michael@0: // token. michael@0: masm.mov(rsp, r12); michael@0: masm.subq(r13, r12); michael@0: masm.subq(Imm32(8), r12); michael@0: masm.andl(Imm32(0xf), r12); michael@0: masm.subq(r12, rsp); michael@0: michael@0: /*************************************************************** michael@0: Loop over argv vector, push arguments onto stack in reverse order michael@0: ***************************************************************/ michael@0: michael@0: // r13 still stores the number of bytes in the argument vector. michael@0: masm.addq(reg_argv, r13); // r13 points above last argument. michael@0: michael@0: // while r13 > rdx, push arguments. michael@0: { michael@0: Label header, footer; michael@0: masm.bind(&header); michael@0: michael@0: masm.cmpq(r13, reg_argv); michael@0: masm.j(AssemblerX86Shared::BelowOrEqual, &footer); michael@0: michael@0: masm.subq(Imm32(8), r13); michael@0: masm.push(Operand(r13, 0)); michael@0: masm.jmp(&header); michael@0: michael@0: masm.bind(&footer); michael@0: } michael@0: michael@0: // Push the number of actual arguments. |result| is used to store the michael@0: // actual number of arguments without adding an extra argument to the enter michael@0: // JIT. michael@0: masm.movq(result, reg_argc); michael@0: masm.unboxInt32(Operand(reg_argc, 0), reg_argc); michael@0: masm.push(reg_argc); michael@0: michael@0: // Push the callee token. michael@0: masm.push(token); michael@0: michael@0: /***************************************************************** michael@0: Push the number of bytes we've pushed so far on the stack and call michael@0: *****************************************************************/ michael@0: masm.subq(rsp, r14); michael@0: michael@0: // Create a frame descriptor. michael@0: masm.makeFrameDescriptor(r14, JitFrame_Entry); michael@0: masm.push(r14); michael@0: michael@0: CodeLabel returnLabel; michael@0: if (type == EnterJitBaseline) { michael@0: // Handle OSR. michael@0: GeneralRegisterSet regs(GeneralRegisterSet::All()); michael@0: regs.takeUnchecked(OsrFrameReg); michael@0: regs.take(rbp); michael@0: regs.take(reg_code); michael@0: michael@0: // Ensure that |scratch| does not end up being JSReturnOperand. michael@0: // Do takeUnchecked because on Win64/x64, reg_code (IntArgReg0) and JSReturnOperand are michael@0: // the same (rcx). See bug 849398. michael@0: regs.takeUnchecked(JSReturnOperand); michael@0: Register scratch = regs.takeAny(); michael@0: michael@0: Label notOsr; michael@0: masm.branchTestPtr(Assembler::Zero, OsrFrameReg, OsrFrameReg, ¬Osr); michael@0: michael@0: Register numStackValues = regs.takeAny(); michael@0: masm.movq(numStackValuesAddr, numStackValues); michael@0: michael@0: // Push return address, previous frame pointer. michael@0: masm.mov(returnLabel.dest(), scratch); michael@0: masm.push(scratch); michael@0: masm.push(rbp); michael@0: michael@0: // Reserve frame. michael@0: Register framePtr = rbp; michael@0: masm.subPtr(Imm32(BaselineFrame::Size()), rsp); michael@0: masm.mov(rsp, framePtr); michael@0: michael@0: #ifdef XP_WIN michael@0: // Can't push large frames blindly on windows. Touch frame memory incrementally. michael@0: masm.mov(numStackValues, scratch); michael@0: masm.lshiftPtr(Imm32(3), scratch); michael@0: masm.subPtr(scratch, framePtr); michael@0: { michael@0: masm.movePtr(rsp, scratch); michael@0: masm.subPtr(Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch); michael@0: michael@0: Label touchFrameLoop; michael@0: Label touchFrameLoopEnd; michael@0: masm.bind(&touchFrameLoop); michael@0: masm.branchPtr(Assembler::Below, scratch, framePtr, &touchFrameLoopEnd); michael@0: masm.store32(Imm32(0), Address(scratch, 0)); michael@0: masm.subPtr(Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch); michael@0: masm.jump(&touchFrameLoop); michael@0: masm.bind(&touchFrameLoopEnd); michael@0: } michael@0: masm.mov(rsp, framePtr); michael@0: #endif michael@0: michael@0: // Reserve space for locals and stack values. michael@0: Register valuesSize = regs.takeAny(); michael@0: masm.mov(numStackValues, valuesSize); michael@0: masm.shll(Imm32(3), valuesSize); michael@0: masm.subPtr(valuesSize, rsp); michael@0: michael@0: // Enter exit frame. michael@0: masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), valuesSize); michael@0: masm.makeFrameDescriptor(valuesSize, JitFrame_BaselineJS); michael@0: masm.push(valuesSize); michael@0: masm.push(Imm32(0)); // Fake return address. michael@0: masm.enterFakeExitFrame(); michael@0: michael@0: regs.add(valuesSize); michael@0: michael@0: masm.push(framePtr); michael@0: masm.push(reg_code); michael@0: michael@0: masm.setupUnalignedABICall(3, scratch); michael@0: masm.passABIArg(framePtr); // BaselineFrame michael@0: masm.passABIArg(OsrFrameReg); // InterpreterFrame michael@0: masm.passABIArg(numStackValues); michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, jit::InitBaselineFrameForOsr)); michael@0: michael@0: masm.pop(reg_code); michael@0: masm.pop(framePtr); michael@0: michael@0: JS_ASSERT(reg_code != ReturnReg); michael@0: michael@0: Label error; michael@0: masm.addPtr(Imm32(IonExitFrameLayout::SizeWithFooter()), rsp); michael@0: masm.addPtr(Imm32(BaselineFrame::Size()), framePtr); michael@0: masm.branchIfFalseBool(ReturnReg, &error); michael@0: michael@0: masm.jump(reg_code); michael@0: michael@0: // OOM: load error value, discard return address and previous frame michael@0: // pointer and return. michael@0: masm.bind(&error); michael@0: masm.mov(framePtr, rsp); michael@0: masm.addPtr(Imm32(2 * sizeof(uintptr_t)), rsp); michael@0: masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); michael@0: masm.mov(returnLabel.dest(), scratch); michael@0: masm.jump(scratch); michael@0: michael@0: masm.bind(¬Osr); michael@0: masm.movq(scopeChain, R1.scratchReg()); michael@0: } michael@0: michael@0: // Call function. michael@0: masm.call(reg_code); michael@0: michael@0: if (type == EnterJitBaseline) { michael@0: // Baseline OSR will return here. michael@0: masm.bind(returnLabel.src()); michael@0: if (!masm.addCodeLabel(returnLabel)) michael@0: return nullptr; michael@0: } michael@0: michael@0: // Pop arguments and padding from stack. michael@0: masm.pop(r14); // Pop and decode descriptor. michael@0: masm.shrq(Imm32(FRAMESIZE_SHIFT), r14); michael@0: masm.addq(r14, rsp); // Remove arguments. michael@0: michael@0: /***************************************************************** michael@0: Place return value where it belongs, pop all saved registers michael@0: *****************************************************************/ michael@0: masm.pop(r12); // vp michael@0: masm.storeValue(JSReturnOperand, Operand(r12, 0)); michael@0: michael@0: // Unwind the sps mark. michael@0: masm.spsUnmarkJit(&cx->runtime()->spsProfiler, rbx); michael@0: michael@0: // Restore non-volatile registers. michael@0: #if defined(_WIN64) michael@0: masm.movdqa(Operand(rsp, 16 * 0), xmm6); michael@0: masm.movdqa(Operand(rsp, 16 * 1), xmm7); michael@0: masm.movdqa(Operand(rsp, 16 * 2), xmm8); michael@0: masm.movdqa(Operand(rsp, 16 * 3), xmm9); michael@0: masm.movdqa(Operand(rsp, 16 * 4), xmm10); michael@0: masm.movdqa(Operand(rsp, 16 * 5), xmm11); michael@0: masm.movdqa(Operand(rsp, 16 * 6), xmm12); michael@0: masm.movdqa(Operand(rsp, 16 * 7), xmm13); michael@0: masm.movdqa(Operand(rsp, 16 * 8), xmm14); michael@0: masm.movdqa(Operand(rsp, 16 * 9), xmm15); michael@0: michael@0: masm.addq(Imm32(16 * 10 + 8), rsp); michael@0: michael@0: masm.pop(rsi); michael@0: masm.pop(rdi); michael@0: #endif michael@0: masm.pop(r15); michael@0: masm.pop(r14); michael@0: masm.pop(r13); michael@0: masm.pop(r12); michael@0: masm.pop(rbx); michael@0: michael@0: // Restore frame pointer and return. michael@0: masm.pop(rbp); michael@0: masm.ret(); michael@0: michael@0: Linker linker(masm); michael@0: JitCode *code = linker.newCode(cx, JSC::OTHER_CODE); michael@0: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(code, "EnterJIT"); michael@0: #endif michael@0: michael@0: return code; michael@0: } michael@0: michael@0: JitCode * michael@0: JitRuntime::generateInvalidator(JSContext *cx) michael@0: { michael@0: AutoIonContextAlloc aica(cx); michael@0: MacroAssembler masm(cx); michael@0: michael@0: // See explanatory comment in x86's JitRuntime::generateInvalidator. michael@0: michael@0: masm.addq(Imm32(sizeof(uintptr_t)), rsp); michael@0: michael@0: // Push registers such that we can access them from [base + code]. michael@0: masm.PushRegsInMask(AllRegs); michael@0: michael@0: masm.movq(rsp, rax); // Argument to jit::InvalidationBailout. michael@0: michael@0: // Make space for InvalidationBailout's frameSize outparam. michael@0: masm.reserveStack(sizeof(size_t)); michael@0: masm.movq(rsp, rbx); michael@0: michael@0: // Make space for InvalidationBailout's bailoutInfo outparam. michael@0: masm.reserveStack(sizeof(void *)); michael@0: masm.movq(rsp, r9); michael@0: michael@0: masm.setupUnalignedABICall(3, rdx); michael@0: masm.passABIArg(rax); michael@0: masm.passABIArg(rbx); michael@0: masm.passABIArg(r9); michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InvalidationBailout)); michael@0: michael@0: masm.pop(r9); // Get the bailoutInfo outparam. michael@0: masm.pop(rbx); // Get the frameSize outparam. michael@0: michael@0: // Pop the machine state and the dead frame. michael@0: masm.lea(Operand(rsp, rbx, TimesOne, sizeof(InvalidationBailoutStack)), rsp); michael@0: michael@0: // Jump to shared bailout tail. The BailoutInfo pointer has to be in r9. michael@0: JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail(); michael@0: masm.jmp(bailoutTail); michael@0: michael@0: Linker linker(masm); michael@0: JitCode *code = linker.newCode(cx, JSC::OTHER_CODE); michael@0: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(code, "Invalidator"); michael@0: #endif michael@0: michael@0: return code; michael@0: } michael@0: michael@0: JitCode * michael@0: JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut) michael@0: { michael@0: // Do not erase the frame pointer in this function. michael@0: michael@0: MacroAssembler masm(cx); michael@0: michael@0: // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame. michael@0: // Including |this|, there are (|nargs| + 1) arguments to copy. michael@0: JS_ASSERT(ArgumentsRectifierReg == r8); michael@0: michael@0: // Load the number of |undefined|s to push into %rcx. michael@0: masm.loadPtr(Address(rsp, IonRectifierFrameLayout::offsetOfCalleeToken()), rax); michael@0: masm.movzwl(Operand(rax, JSFunction::offsetOfNargs()), rcx); michael@0: masm.subq(r8, rcx); michael@0: michael@0: // Copy the number of actual arguments michael@0: masm.loadPtr(Address(rsp, IonRectifierFrameLayout::offsetOfNumActualArgs()), rdx); michael@0: michael@0: masm.moveValue(UndefinedValue(), r10); michael@0: michael@0: masm.movq(rsp, r9); // Save %rsp. michael@0: michael@0: // Push undefined. michael@0: { michael@0: Label undefLoopTop; michael@0: masm.bind(&undefLoopTop); michael@0: michael@0: masm.push(r10); michael@0: masm.subl(Imm32(1), rcx); michael@0: masm.j(Assembler::NonZero, &undefLoopTop); michael@0: } michael@0: michael@0: // Get the topmost argument. michael@0: BaseIndex b = BaseIndex(r9, r8, TimesEight, sizeof(IonRectifierFrameLayout)); michael@0: masm.lea(Operand(b), rcx); michael@0: michael@0: // Push arguments, |nargs| + 1 times (to include |this|). michael@0: masm.addl(Imm32(1), r8); michael@0: { michael@0: Label copyLoopTop; michael@0: michael@0: masm.bind(©LoopTop); michael@0: masm.push(Operand(rcx, 0x0)); michael@0: masm.subq(Imm32(sizeof(Value)), rcx); michael@0: masm.subl(Imm32(1), r8); michael@0: masm.j(Assembler::NonZero, ©LoopTop); michael@0: } michael@0: michael@0: // Construct descriptor. michael@0: masm.subq(rsp, r9); michael@0: masm.makeFrameDescriptor(r9, JitFrame_Rectifier); michael@0: michael@0: // Construct IonJSFrameLayout. michael@0: masm.push(rdx); // numActualArgs michael@0: masm.push(rax); // callee token michael@0: masm.push(r9); // descriptor michael@0: michael@0: // Call the target function. michael@0: // Note that this code assumes the function is JITted. michael@0: masm.loadPtr(Address(rax, JSFunction::offsetOfNativeOrScript()), rax); michael@0: masm.loadBaselineOrIonRaw(rax, rax, mode, nullptr); michael@0: masm.call(rax); michael@0: uint32_t returnOffset = masm.currentOffset(); michael@0: michael@0: // Remove the rectifier frame. michael@0: masm.pop(r9); // r9 <- descriptor with FrameType. michael@0: masm.shrq(Imm32(FRAMESIZE_SHIFT), r9); michael@0: masm.pop(r11); // Discard calleeToken. michael@0: masm.pop(r11); // Discard numActualArgs. michael@0: masm.addq(r9, rsp); // Discard pushed arguments. michael@0: michael@0: masm.ret(); michael@0: michael@0: Linker linker(masm); michael@0: JitCode *code = linker.newCode(cx, JSC::OTHER_CODE); michael@0: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier"); michael@0: #endif michael@0: michael@0: CodeOffsetLabel returnLabel(returnOffset); michael@0: returnLabel.fixup(&masm); michael@0: if (returnAddrOut) michael@0: *returnAddrOut = (void *) (code->raw() + returnLabel.offset()); michael@0: return code; michael@0: } michael@0: michael@0: static void michael@0: GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass) michael@0: { michael@0: // Push registers such that we can access them from [base + code]. michael@0: masm.PushRegsInMask(AllRegs); michael@0: michael@0: // Get the stack pointer into a register, pre-alignment. michael@0: masm.movq(rsp, r8); michael@0: michael@0: // Make space for Bailout's bailoutInfo outparam. michael@0: masm.reserveStack(sizeof(void *)); michael@0: masm.movq(rsp, r9); michael@0: michael@0: // Call the bailout function. michael@0: masm.setupUnalignedABICall(2, rax); michael@0: masm.passABIArg(r8); michael@0: masm.passABIArg(r9); michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Bailout)); michael@0: michael@0: masm.pop(r9); // Get the bailoutInfo outparam. michael@0: michael@0: // Stack is: michael@0: // [frame] michael@0: // snapshotOffset michael@0: // frameSize michael@0: // [bailoutFrame] michael@0: // michael@0: // Remove both the bailout frame and the topmost Ion frame's stack. michael@0: static const uint32_t BailoutDataSize = sizeof(void *) * Registers::Total + michael@0: sizeof(double) * FloatRegisters::Total; michael@0: masm.addq(Imm32(BailoutDataSize), rsp); michael@0: masm.pop(rcx); michael@0: masm.lea(Operand(rsp, rcx, TimesOne, sizeof(void *)), rsp); michael@0: michael@0: // Jump to shared bailout tail. The BailoutInfo pointer has to be in r9. michael@0: JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail(); michael@0: masm.jmp(bailoutTail); michael@0: } michael@0: michael@0: JitCode * michael@0: JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass) michael@0: { michael@0: MOZ_ASSUME_UNREACHABLE("x64 does not use bailout tables"); michael@0: } michael@0: michael@0: JitCode * michael@0: JitRuntime::generateBailoutHandler(JSContext *cx) michael@0: { michael@0: MacroAssembler masm; michael@0: michael@0: GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID); michael@0: michael@0: Linker linker(masm); michael@0: JitCode *code = linker.newCode(cx, JSC::OTHER_CODE); michael@0: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(code, "BailoutHandler"); michael@0: #endif michael@0: michael@0: return code; michael@0: } michael@0: michael@0: JitCode * michael@0: JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f) michael@0: { michael@0: JS_ASSERT(!StackKeptAligned); michael@0: JS_ASSERT(functionWrappers_); michael@0: JS_ASSERT(functionWrappers_->initialized()); michael@0: VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f); michael@0: if (p) michael@0: return p->value(); michael@0: michael@0: // Generate a separated code for the wrapper. michael@0: MacroAssembler masm; michael@0: michael@0: // Avoid conflicts with argument registers while discarding the result after michael@0: // the function call. michael@0: GeneralRegisterSet regs = GeneralRegisterSet(Register::Codes::WrapperMask); michael@0: michael@0: // Wrapper register set is a superset of Volatile register set. michael@0: JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0); michael@0: michael@0: // The context is the first argument. michael@0: Register cxreg = IntArgReg0; michael@0: regs.take(cxreg); michael@0: michael@0: // Stack is: michael@0: // ... frame ... michael@0: // +12 [args] michael@0: // +8 descriptor michael@0: // +0 returnAddress michael@0: // michael@0: // We're aligned to an exit frame, so link it up. michael@0: masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode); michael@0: michael@0: // Save the current stack pointer as the base for copying arguments. michael@0: Register argsBase = InvalidReg; michael@0: if (f.explicitArgs) { michael@0: argsBase = r10; michael@0: regs.take(argsBase); michael@0: masm.lea(Operand(rsp,IonExitFrameLayout::SizeWithFooter()), argsBase); michael@0: } michael@0: michael@0: // Reserve space for the outparameter. michael@0: Register outReg = InvalidReg; michael@0: switch (f.outParam) { michael@0: case Type_Value: michael@0: outReg = regs.takeAny(); michael@0: masm.reserveStack(sizeof(Value)); michael@0: masm.movq(esp, outReg); michael@0: break; michael@0: michael@0: case Type_Handle: michael@0: outReg = regs.takeAny(); michael@0: masm.PushEmptyRooted(f.outParamRootType); michael@0: masm.movq(esp, outReg); michael@0: break; michael@0: michael@0: case Type_Int32: michael@0: case Type_Bool: michael@0: outReg = regs.takeAny(); michael@0: masm.reserveStack(sizeof(int32_t)); michael@0: masm.movq(esp, outReg); michael@0: break; michael@0: michael@0: case Type_Double: michael@0: outReg = regs.takeAny(); michael@0: masm.reserveStack(sizeof(double)); michael@0: masm.movq(esp, outReg); michael@0: break; michael@0: michael@0: case Type_Pointer: michael@0: outReg = regs.takeAny(); michael@0: masm.reserveStack(sizeof(uintptr_t)); michael@0: masm.movq(esp, outReg); michael@0: break; michael@0: michael@0: default: michael@0: JS_ASSERT(f.outParam == Type_Void); michael@0: break; michael@0: } michael@0: michael@0: masm.setupUnalignedABICall(f.argc(), regs.getAny()); michael@0: masm.passABIArg(cxreg); michael@0: michael@0: size_t argDisp = 0; michael@0: michael@0: // Copy arguments. michael@0: for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) { michael@0: MoveOperand from; michael@0: switch (f.argProperties(explicitArg)) { michael@0: case VMFunction::WordByValue: michael@0: if (f.argPassedInFloatReg(explicitArg)) michael@0: masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE); michael@0: else michael@0: masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); michael@0: argDisp += sizeof(void *); michael@0: break; michael@0: case VMFunction::WordByRef: michael@0: masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), michael@0: MoveOp::GENERAL); michael@0: argDisp += sizeof(void *); michael@0: break; michael@0: case VMFunction::DoubleByValue: michael@0: case VMFunction::DoubleByRef: michael@0: MOZ_ASSUME_UNREACHABLE("NYI: x64 callVM should not be used with 128bits values."); michael@0: } michael@0: } michael@0: michael@0: // Copy the implicit outparam, if any. michael@0: if (outReg != InvalidReg) michael@0: masm.passABIArg(outReg); michael@0: michael@0: masm.callWithABI(f.wrapped); michael@0: michael@0: // Test for failure. michael@0: switch (f.failType()) { michael@0: case Type_Object: michael@0: masm.branchTestPtr(Assembler::Zero, rax, rax, masm.failureLabel(f.executionMode)); michael@0: break; michael@0: case Type_Bool: michael@0: masm.testb(rax, rax); michael@0: masm.j(Assembler::Zero, masm.failureLabel(f.executionMode)); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("unknown failure kind"); michael@0: } michael@0: michael@0: // Load the outparam and free any allocated stack. michael@0: switch (f.outParam) { michael@0: case Type_Handle: michael@0: masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand); michael@0: break; michael@0: michael@0: case Type_Value: michael@0: masm.loadValue(Address(esp, 0), JSReturnOperand); michael@0: masm.freeStack(sizeof(Value)); michael@0: break; michael@0: michael@0: case Type_Int32: michael@0: masm.load32(Address(esp, 0), ReturnReg); michael@0: masm.freeStack(sizeof(int32_t)); michael@0: break; michael@0: michael@0: case Type_Bool: michael@0: masm.load8ZeroExtend(Address(esp, 0), ReturnReg); michael@0: masm.freeStack(sizeof(int32_t)); michael@0: break; michael@0: michael@0: case Type_Double: michael@0: JS_ASSERT(cx->runtime()->jitSupportsFloatingPoint); michael@0: masm.loadDouble(Address(esp, 0), ReturnFloatReg); michael@0: masm.freeStack(sizeof(double)); michael@0: break; michael@0: michael@0: case Type_Pointer: michael@0: masm.loadPtr(Address(esp, 0), ReturnReg); michael@0: masm.freeStack(sizeof(uintptr_t)); michael@0: break; michael@0: michael@0: default: michael@0: JS_ASSERT(f.outParam == Type_Void); michael@0: break; michael@0: } michael@0: masm.leaveExitFrame(); michael@0: masm.retn(Imm32(sizeof(IonExitFrameLayout) + michael@0: f.explicitStackSlots() * sizeof(void *) + michael@0: f.extraValuesToPop * sizeof(Value))); michael@0: michael@0: Linker linker(masm); michael@0: JitCode *wrapper = linker.newCode(cx, JSC::OTHER_CODE); michael@0: if (!wrapper) michael@0: return nullptr; michael@0: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(wrapper, "VMWrapper"); michael@0: #endif michael@0: michael@0: // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to michael@0: // use relookupOrAdd instead of add. michael@0: if (!functionWrappers_->relookupOrAdd(p, &f, wrapper)) michael@0: return nullptr; michael@0: michael@0: return wrapper; michael@0: } michael@0: michael@0: JitCode * michael@0: JitRuntime::generatePreBarrier(JSContext *cx, MIRType type) michael@0: { michael@0: MacroAssembler masm; michael@0: michael@0: RegisterSet regs = RegisterSet(GeneralRegisterSet(Registers::VolatileMask), michael@0: FloatRegisterSet(FloatRegisters::VolatileMask)); michael@0: masm.PushRegsInMask(regs); michael@0: michael@0: JS_ASSERT(PreBarrierReg == rdx); michael@0: masm.mov(ImmPtr(cx->runtime()), rcx); michael@0: michael@0: masm.setupUnalignedABICall(2, rax); michael@0: masm.passABIArg(rcx); michael@0: masm.passABIArg(rdx); michael@0: if (type == MIRType_Value) { michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkValueFromIon)); michael@0: } else { michael@0: JS_ASSERT(type == MIRType_Shape); michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon)); michael@0: } michael@0: michael@0: masm.PopRegsInMask(regs); michael@0: masm.ret(); michael@0: michael@0: Linker linker(masm); michael@0: JitCode *code = linker.newCode(cx, JSC::OTHER_CODE); michael@0: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(code, "PreBarrier"); michael@0: #endif michael@0: michael@0: return code; michael@0: } michael@0: michael@0: typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *); michael@0: static const VMFunction HandleDebugTrapInfo = FunctionInfo(HandleDebugTrap); michael@0: michael@0: JitCode * michael@0: JitRuntime::generateDebugTrapHandler(JSContext *cx) michael@0: { michael@0: MacroAssembler masm; michael@0: michael@0: Register scratch1 = rax; michael@0: Register scratch2 = rcx; michael@0: Register scratch3 = rdx; michael@0: michael@0: // Load the return address in scratch1. michael@0: masm.loadPtr(Address(rsp, 0), scratch1); michael@0: michael@0: // Load BaselineFrame pointer in scratch2. michael@0: masm.mov(rbp, scratch2); michael@0: masm.subPtr(Imm32(BaselineFrame::Size()), scratch2); michael@0: michael@0: // Enter a stub frame and call the HandleDebugTrap VM function. Ensure michael@0: // the stub frame has a nullptr ICStub pointer, since this pointer is marked michael@0: // during GC. michael@0: masm.movePtr(ImmPtr(nullptr), BaselineStubReg); michael@0: EmitEnterStubFrame(masm, scratch3); michael@0: michael@0: JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo); michael@0: if (!code) michael@0: return nullptr; michael@0: michael@0: masm.push(scratch1); michael@0: masm.push(scratch2); michael@0: EmitCallVM(code, masm); michael@0: michael@0: EmitLeaveStubFrame(masm); michael@0: michael@0: // If the stub returns |true|, we have to perform a forced return michael@0: // (return from the JS frame). If the stub returns |false|, just return michael@0: // from the trap stub so that execution continues at the current pc. michael@0: Label forcedReturn; michael@0: masm.branchTest32(Assembler::NonZero, ReturnReg, ReturnReg, &forcedReturn); michael@0: masm.ret(); michael@0: michael@0: masm.bind(&forcedReturn); michael@0: masm.loadValue(Address(ebp, BaselineFrame::reverseOffsetOfReturnValue()), michael@0: JSReturnOperand); michael@0: masm.mov(rbp, rsp); michael@0: masm.pop(rbp); michael@0: masm.ret(); michael@0: michael@0: Linker linker(masm); michael@0: JitCode *codeDbg = linker.newCode(cx, JSC::OTHER_CODE); michael@0: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler"); michael@0: #endif michael@0: michael@0: return codeDbg; michael@0: } michael@0: michael@0: JitCode * michael@0: JitRuntime::generateExceptionTailStub(JSContext *cx) michael@0: { michael@0: MacroAssembler masm; michael@0: michael@0: masm.handleFailureWithHandlerTail(); michael@0: michael@0: Linker linker(masm); michael@0: JitCode *code = linker.newCode(cx, JSC::OTHER_CODE); michael@0: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(code, "ExceptionTailStub"); michael@0: #endif michael@0: michael@0: return code; michael@0: } michael@0: michael@0: JitCode * michael@0: JitRuntime::generateBailoutTailStub(JSContext *cx) michael@0: { michael@0: MacroAssembler masm; michael@0: michael@0: masm.generateBailoutTail(rdx, r9); michael@0: michael@0: Linker linker(masm); michael@0: JitCode *code = linker.newCode(cx, JSC::OTHER_CODE); michael@0: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(code, "BailoutTailStub"); michael@0: #endif michael@0: michael@0: return code; michael@0: }