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 "jscompartment.h" michael@0: michael@0: #include "assembler/assembler/MacroAssembler.h" michael@0: #include "jit/Bailouts.h" michael@0: #include "jit/BaselineJIT.h" michael@0: #include "jit/IonFrames.h" michael@0: #include "jit/IonLinker.h" michael@0: #include "jit/IonSpewer.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/x86/BaselineHelpers-x86.h" michael@0: michael@0: #include "jsscriptinlines.h" michael@0: michael@0: #include "jit/ExecutionMode-inl.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: enum EnterJitEbpArgumentOffset { michael@0: ARG_JITCODE = 2 * sizeof(void *), michael@0: ARG_ARGC = 3 * sizeof(void *), michael@0: ARG_ARGV = 4 * sizeof(void *), michael@0: ARG_STACKFRAME = 5 * sizeof(void *), michael@0: ARG_CALLEETOKEN = 6 * sizeof(void *), michael@0: ARG_SCOPECHAIN = 7 * sizeof(void *), michael@0: ARG_STACKVALUES = 8 * sizeof(void *), michael@0: ARG_RESULT = 9 * sizeof(void *) michael@0: }; michael@0: michael@0: /* michael@0: * Generates a trampoline for a C++ function with the EnterJitCode signature, michael@0: * using the standard cdecl 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: // Save old stack frame pointer, set new stack frame pointer. michael@0: masm.push(ebp); michael@0: masm.movl(esp, ebp); michael@0: michael@0: // Save non-volatile registers. These must be saved by the trampoline, michael@0: // rather than the JIT'd code, because they are scanned by the conservative michael@0: // scanner. michael@0: masm.push(ebx); michael@0: masm.push(esi); michael@0: masm.push(edi); michael@0: michael@0: // Push the EnterJIT sps mark. michael@0: masm.spsMarkJit(&cx->runtime()->spsProfiler, ebp, ebx); michael@0: michael@0: // Keep track of the stack which has to be unwound after returning from the michael@0: // compiled function. michael@0: masm.movl(esp, esi); michael@0: michael@0: // eax <- 8*argc, eax is now the offset betwen argv and the last michael@0: masm.loadPtr(Address(ebp, ARG_ARGC), eax); michael@0: masm.shll(Imm32(3), eax); michael@0: michael@0: // We need to ensure that the stack is aligned on a 12-byte boundary, so michael@0: // inside the JIT function the stack is 16-byte aligned. Our stack right michael@0: // now might not be aligned on some platforms (win32, gcc) so we factor michael@0: // this possibility in, and simulate what the new stack address would be. michael@0: // +argc * 8 for arguments michael@0: // +4 for pushing alignment michael@0: // +4 for pushing the callee token michael@0: // +4 for pushing the return address michael@0: masm.movl(esp, ecx); michael@0: masm.subl(eax, ecx); michael@0: masm.subl(Imm32(4 * 3), ecx); michael@0: michael@0: // ecx = ecx & 15, holds alignment. michael@0: masm.andl(Imm32(15), ecx); michael@0: masm.subl(ecx, esp); michael@0: michael@0: /*************************************************************** michael@0: Loop over argv vector, push arguments onto stack in reverse order michael@0: ***************************************************************/ michael@0: michael@0: // ebx = argv --argv pointer is in ebp + 16 michael@0: masm.loadPtr(Address(ebp, ARG_ARGV), ebx); michael@0: michael@0: // eax = argv[8(argc)] --eax now points one value past the last argument michael@0: masm.addl(ebx, eax); michael@0: michael@0: // while (eax > ebx) --while still looping through arguments michael@0: { michael@0: Label header, footer; michael@0: masm.bind(&header); michael@0: michael@0: masm.cmpl(eax, ebx); michael@0: masm.j(Assembler::BelowOrEqual, &footer); michael@0: michael@0: // eax -= 8 --move to previous argument michael@0: masm.subl(Imm32(8), eax); michael@0: michael@0: // Push what eax points to on stack, a Value is 2 words michael@0: masm.push(Operand(eax, 4)); michael@0: masm.push(Operand(eax, 0)); michael@0: michael@0: masm.jmp(&header); michael@0: masm.bind(&footer); michael@0: } 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.mov(Operand(ebp, ARG_RESULT), eax); michael@0: masm.unboxInt32(Address(eax, 0x0), eax); michael@0: masm.push(eax); michael@0: michael@0: // Push the callee token. michael@0: masm.push(Operand(ebp, ARG_CALLEETOKEN)); michael@0: michael@0: // Load the InterpreterFrame address into the OsrFrameReg. michael@0: // This address is also used for setting the constructing bit on all paths. michael@0: masm.loadPtr(Address(ebp, ARG_STACKFRAME), OsrFrameReg); 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: // Create a frame descriptor. michael@0: masm.subl(esp, esi); michael@0: masm.makeFrameDescriptor(esi, JitFrame_Entry); michael@0: masm.push(esi); michael@0: michael@0: CodeLabel returnLabel; michael@0: if (type == EnterJitBaseline) { michael@0: // Handle OSR. michael@0: GeneralRegisterSet regs(GeneralRegisterSet::All()); michael@0: regs.take(JSReturnOperand); michael@0: regs.takeUnchecked(OsrFrameReg); michael@0: regs.take(ebp); michael@0: regs.take(ReturnReg); michael@0: 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.loadPtr(Address(ebp, ARG_STACKVALUES), numStackValues); michael@0: michael@0: Register jitcode = regs.takeAny(); michael@0: masm.loadPtr(Address(ebp, ARG_JITCODE), jitcode); 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(ebp); michael@0: michael@0: // Reserve frame. michael@0: Register framePtr = ebp; michael@0: masm.subPtr(Imm32(BaselineFrame::Size()), esp); michael@0: masm.mov(esp, 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.shll(Imm32(3), scratch); michael@0: masm.subPtr(scratch, framePtr); michael@0: { michael@0: masm.movePtr(esp, 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(esp, framePtr); michael@0: #endif michael@0: michael@0: // Reserve space for locals and stack values. michael@0: masm.mov(numStackValues, scratch); michael@0: masm.shll(Imm32(3), scratch); michael@0: masm.subPtr(scratch, esp); michael@0: michael@0: // Enter exit frame. michael@0: masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch); michael@0: masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS); michael@0: masm.push(scratch); michael@0: masm.push(Imm32(0)); // Fake return address. michael@0: masm.enterFakeExitFrame(); michael@0: michael@0: masm.push(framePtr); michael@0: masm.push(jitcode); 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(jitcode); michael@0: masm.pop(framePtr); michael@0: michael@0: JS_ASSERT(jitcode != ReturnReg); michael@0: michael@0: Label error; michael@0: masm.addPtr(Imm32(IonExitFrameLayout::SizeWithFooter()), esp); michael@0: masm.addPtr(Imm32(BaselineFrame::Size()), framePtr); michael@0: masm.branchIfFalseBool(ReturnReg, &error); michael@0: michael@0: masm.jump(jitcode); 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, esp); michael@0: masm.addPtr(Imm32(2 * sizeof(uintptr_t)), esp); 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.loadPtr(Address(ebp, ARG_SCOPECHAIN), R1.scratchReg()); michael@0: } michael@0: michael@0: /*************************************************************** michael@0: Call passed-in code, get return value and fill in the michael@0: passed in return value pointer michael@0: ***************************************************************/ michael@0: masm.call(Operand(ebp, ARG_JITCODE)); 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 off the stack. michael@0: // eax <- 8*argc (size of all arguments we pushed on the stack) michael@0: masm.pop(eax); michael@0: masm.shrl(Imm32(FRAMESIZE_SHIFT), eax); // Unmark EntryFrame. michael@0: masm.addl(eax, esp); michael@0: michael@0: // |ebp| could have been clobbered by the inner function. michael@0: // Grab the address for the Value result from the argument stack. michael@0: // +24 ... arguments ... michael@0: // +20 michael@0: // +16 ebp <- original %ebp pointing here. michael@0: // +12 ebx michael@0: // +8 esi michael@0: // +4 edi michael@0: // +0 hasSPSFrame michael@0: masm.loadPtr(Address(esp, ARG_RESULT + 4 * sizeof(void *)), eax); michael@0: masm.storeValue(JSReturnOperand, Operand(eax, 0)); michael@0: michael@0: /************************************************************** michael@0: Return stack and registers to correct state michael@0: **************************************************************/ michael@0: // Unwind the sps mark. michael@0: masm.spsUnmarkJit(&cx->runtime()->spsProfiler, ebx); michael@0: michael@0: // Restore non-volatile registers michael@0: masm.pop(edi); michael@0: masm.pop(esi); michael@0: masm.pop(ebx); michael@0: michael@0: // Restore old stack frame pointer michael@0: masm.pop(ebp); 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: // We do the minimum amount of work in assembly and shunt the rest michael@0: // off to InvalidationBailout. Assembly does: michael@0: // michael@0: // - Pop the return address from the invalidation epilogue call. michael@0: // - Push the machine state onto the stack. michael@0: // - Call the InvalidationBailout routine with the stack pointer. michael@0: // - Now that the frame has been bailed out, convert the invalidated michael@0: // frame into an exit frame. michael@0: // - Do the normal check-return-code-and-thunk-to-the-interpreter dance. michael@0: michael@0: masm.addl(Imm32(sizeof(uintptr_t)), esp); 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.movl(esp, eax); // 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.movl(esp, ebx); michael@0: michael@0: // Make space for InvalidationBailout's bailoutInfo outparam. michael@0: masm.reserveStack(sizeof(void *)); michael@0: masm.movl(esp, ecx); michael@0: michael@0: masm.setupUnalignedABICall(3, edx); michael@0: masm.passABIArg(eax); michael@0: masm.passABIArg(ebx); michael@0: masm.passABIArg(ecx); michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InvalidationBailout)); michael@0: michael@0: masm.pop(ecx); // Get bailoutInfo outparam. michael@0: masm.pop(ebx); // Get the frameSize outparam. michael@0: michael@0: // Pop the machine state and the dead frame. michael@0: masm.lea(Operand(esp, ebx, TimesOne, sizeof(InvalidationBailoutStack)), esp); michael@0: michael@0: // Jump to shared bailout tail. The BailoutInfo pointer has to be in ecx. 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: IonSpew(IonSpew_Invalidate, " invalidation thunk created at %p", (void *) code->raw()); 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: 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 == esi); michael@0: michael@0: // Load the number of |undefined|s to push into %ecx. michael@0: masm.loadPtr(Address(esp, IonRectifierFrameLayout::offsetOfCalleeToken()), eax); michael@0: masm.movzwl(Operand(eax, JSFunction::offsetOfNargs()), ecx); michael@0: masm.subl(esi, ecx); michael@0: michael@0: // Copy the number of actual arguments. michael@0: masm.loadPtr(Address(esp, IonRectifierFrameLayout::offsetOfNumActualArgs()), edx); michael@0: michael@0: masm.moveValue(UndefinedValue(), ebx, edi); michael@0: michael@0: // NOTE: The fact that x86 ArgumentsRectifier saves the FramePointer is relied upon michael@0: // by the baseline bailout code. If this changes, fix that code! See michael@0: // BaselineJIT.cpp/BaselineStackBuilder::calculatePrevFramePtr, and michael@0: // BaselineJIT.cpp/InitFromBailout. Check for the |#if defined(JS_CODEGEN_X86)| portions. michael@0: masm.push(FramePointer); michael@0: masm.movl(esp, FramePointer); // Save %esp. michael@0: michael@0: // Push undefined. michael@0: { michael@0: Label undefLoopTop; michael@0: masm.bind(&undefLoopTop); michael@0: michael@0: masm.push(ebx); // type(undefined); michael@0: masm.push(edi); // payload(undefined); michael@0: masm.subl(Imm32(1), ecx); michael@0: masm.j(Assembler::NonZero, &undefLoopTop); michael@0: } michael@0: michael@0: // Get the topmost argument. We did a push of %ebp earlier, so be sure to michael@0: // account for this in the offset michael@0: BaseIndex b = BaseIndex(FramePointer, esi, TimesEight, michael@0: sizeof(IonRectifierFrameLayout) + sizeof(void*)); michael@0: masm.lea(Operand(b), ecx); michael@0: michael@0: // Push arguments, |nargs| + 1 times (to include |this|). michael@0: masm.addl(Imm32(1), esi); michael@0: { michael@0: Label copyLoopTop; michael@0: michael@0: masm.bind(©LoopTop); michael@0: masm.push(Operand(ecx, sizeof(Value)/2)); michael@0: masm.push(Operand(ecx, 0x0)); michael@0: masm.subl(Imm32(sizeof(Value)), ecx); michael@0: masm.subl(Imm32(1), esi); michael@0: masm.j(Assembler::NonZero, ©LoopTop); michael@0: } michael@0: michael@0: // Construct descriptor, accounting for pushed frame pointer above michael@0: masm.lea(Operand(FramePointer, sizeof(void*)), ebx); michael@0: masm.subl(esp, ebx); michael@0: masm.makeFrameDescriptor(ebx, JitFrame_Rectifier); michael@0: michael@0: // Construct IonJSFrameLayout. michael@0: masm.push(edx); // number of actual arguments michael@0: masm.push(eax); // callee token michael@0: masm.push(ebx); // descriptor michael@0: michael@0: // Call the target function. michael@0: // Note that this assumes the function is JITted. michael@0: masm.loadPtr(Address(eax, JSFunction::offsetOfNativeOrScript()), eax); michael@0: masm.loadBaselineOrIonRaw(eax, eax, mode, nullptr); michael@0: masm.call(eax); michael@0: uint32_t returnOffset = masm.currentOffset(); michael@0: michael@0: // Remove the rectifier frame. michael@0: masm.pop(ebx); // ebx <- descriptor with FrameType. michael@0: masm.shrl(Imm32(FRAMESIZE_SHIFT), ebx); // ebx <- descriptor. michael@0: masm.pop(edi); // Discard calleeToken. michael@0: masm.pop(edi); // Discard number of actual arguments. michael@0: michael@0: // Discard pushed arguments, but not the pushed frame pointer. michael@0: BaseIndex unwind = BaseIndex(esp, ebx, TimesOne, -int32_t(sizeof(void*))); michael@0: masm.lea(Operand(unwind), esp); michael@0: michael@0: masm.pop(FramePointer); 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: // Push the bailout table number. michael@0: masm.push(Imm32(frameClass)); michael@0: michael@0: // The current stack pointer is the first argument to jit::Bailout. michael@0: masm.movl(esp, eax); michael@0: michael@0: // Make space for Bailout's baioutInfo outparam. michael@0: masm.reserveStack(sizeof(void *)); michael@0: masm.movl(esp, ebx); michael@0: michael@0: // Call the bailout function. This will correct the size of the bailout. michael@0: masm.setupUnalignedABICall(2, ecx); michael@0: masm.passABIArg(eax); michael@0: masm.passABIArg(ebx); michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Bailout)); michael@0: michael@0: masm.pop(ecx); // Get bailoutInfo outparam. michael@0: michael@0: // Common size of stuff we've pushed. michael@0: const uint32_t BailoutDataSize = sizeof(void *) + // frameClass michael@0: sizeof(double) * FloatRegisters::Total + michael@0: sizeof(void *) * Registers::Total; michael@0: michael@0: // Remove both the bailout frame and the topmost Ion frame's stack. michael@0: if (frameClass == NO_FRAME_SIZE_CLASS_ID) { michael@0: // We want the frameSize. Stack is: michael@0: // ... frame ... michael@0: // snapshotOffset michael@0: // frameSize michael@0: // ... bailoutFrame ... michael@0: masm.addl(Imm32(BailoutDataSize), esp); michael@0: masm.pop(ebx); michael@0: masm.addl(Imm32(sizeof(uint32_t)), esp); michael@0: masm.addl(ebx, esp); michael@0: } else { michael@0: // Stack is: michael@0: // ... frame ... michael@0: // bailoutId michael@0: // ... bailoutFrame ... michael@0: uint32_t frameSize = FrameSizeClass::FromClass(frameClass).frameSize(); michael@0: masm.addl(Imm32(BailoutDataSize + sizeof(void *) + frameSize), esp); michael@0: } michael@0: michael@0: // Jump to shared bailout tail. The BailoutInfo pointer has to be in ecx. 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: MacroAssembler masm; michael@0: michael@0: Label bailout; michael@0: for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++) michael@0: masm.call(&bailout); michael@0: masm.bind(&bailout); michael@0: michael@0: GenerateBailoutThunk(cx, masm, frameClass); 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::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 = regs.takeAny(); michael@0: michael@0: // Stack is: michael@0: // ... frame ... michael@0: // +8 [args] michael@0: // +4 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 = regs.takeAny(); michael@0: masm.lea(Operand(esp, 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.Push(UndefinedValue()); michael@0: masm.movl(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.movl(esp, outReg); michael@0: break; michael@0: michael@0: case Type_Int32: michael@0: case Type_Pointer: michael@0: case Type_Bool: michael@0: outReg = regs.takeAny(); michael@0: masm.reserveStack(sizeof(int32_t)); michael@0: masm.movl(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.movl(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: masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); michael@0: argDisp += sizeof(void *); michael@0: break; michael@0: case VMFunction::DoubleByValue: michael@0: // We don't pass doubles in float registers on x86, so no need michael@0: // to check for argPassedInFloatReg. michael@0: masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL); michael@0: argDisp += sizeof(void *); 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::DoubleByRef: michael@0: masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), michael@0: MoveOp::GENERAL); michael@0: argDisp += 2 * sizeof(void *); michael@0: break; 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, eax, eax, masm.failureLabel(f.executionMode)); michael@0: break; michael@0: case Type_Bool: michael@0: masm.testb(eax, eax); 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.Pop(JSReturnOperand); michael@0: break; michael@0: michael@0: case Type_Int32: michael@0: case Type_Pointer: michael@0: masm.Pop(ReturnReg); michael@0: break; michael@0: michael@0: case Type_Bool: michael@0: masm.Pop(ReturnReg); michael@0: masm.movzbl(ReturnReg, ReturnReg); michael@0: break; michael@0: michael@0: case Type_Double: michael@0: if (cx->runtime()->jitSupportsFloatingPoint) michael@0: masm.Pop(ReturnFloatReg); michael@0: else michael@0: masm.assumeUnreachable("Unable to pop to float reg, with no FP support."); 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 save; michael@0: if (cx->runtime()->jitSupportsFloatingPoint) { michael@0: save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask), michael@0: FloatRegisterSet(FloatRegisters::VolatileMask)); michael@0: } else { michael@0: save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask), michael@0: FloatRegisterSet()); michael@0: } michael@0: masm.PushRegsInMask(save); michael@0: michael@0: JS_ASSERT(PreBarrierReg == edx); michael@0: masm.movl(ImmPtr(cx->runtime()), ecx); michael@0: michael@0: masm.setupUnalignedABICall(2, eax); michael@0: masm.passABIArg(ecx); michael@0: masm.passABIArg(edx); michael@0: 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(save); 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 = eax; michael@0: Register scratch2 = ecx; michael@0: Register scratch3 = edx; michael@0: michael@0: // Load the return address in scratch1. michael@0: masm.loadPtr(Address(esp, 0), scratch1); michael@0: michael@0: // Load BaselineFrame pointer in scratch2. michael@0: masm.mov(ebp, 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 michael@0: // marked 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(ebp, esp); michael@0: masm.pop(ebp); 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(edx, ecx); 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: }