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/arm/BaselineHelpers-arm.h" michael@0: #include "jit/Bailouts.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: michael@0: #include "jit/ExecutionMode-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: static const FloatRegisterSet NonVolatileFloatRegs = michael@0: FloatRegisterSet((1 << FloatRegisters::d8) | michael@0: (1 << FloatRegisters::d9) | michael@0: (1 << FloatRegisters::d10) | michael@0: (1 << FloatRegisters::d11) | michael@0: (1 << FloatRegisters::d12) | michael@0: (1 << FloatRegisters::d13) | michael@0: (1 << FloatRegisters::d14) | michael@0: (1 << FloatRegisters::d15)); michael@0: michael@0: static void michael@0: GenerateReturn(MacroAssembler &masm, int returnCode, SPSProfiler *prof) michael@0: { michael@0: // Restore non-volatile floating point registers michael@0: masm.transferMultipleByRuns(NonVolatileFloatRegs, IsLoad, StackPointer, IA); michael@0: michael@0: // Unwind the sps mark. michael@0: masm.spsUnmarkJit(prof, r8); michael@0: michael@0: // Set up return value michael@0: masm.ma_mov(Imm32(returnCode), r0); michael@0: michael@0: // Pop and return michael@0: masm.startDataTransferM(IsLoad, sp, IA, WriteBack); michael@0: masm.transferReg(r4); michael@0: masm.transferReg(r5); michael@0: masm.transferReg(r6); michael@0: masm.transferReg(r7); michael@0: masm.transferReg(r8); michael@0: masm.transferReg(r9); michael@0: masm.transferReg(r10); michael@0: masm.transferReg(r11); michael@0: // r12 isn't saved, so it shouldn't be restored. michael@0: masm.transferReg(pc); michael@0: masm.finishDataTransfer(); michael@0: masm.dumpPool(); michael@0: } michael@0: michael@0: struct EnterJITStack michael@0: { michael@0: double d8; michael@0: double d9; michael@0: double d10; michael@0: double d11; michael@0: double d12; michael@0: double d13; michael@0: double d14; michael@0: double d15; michael@0: michael@0: size_t hasSPSMark; michael@0: michael@0: // non-volatile registers. michael@0: void *r4; michael@0: void *r5; michael@0: void *r6; michael@0: void *r7; michael@0: void *r8; michael@0: void *r9; michael@0: void *r10; michael@0: void *r11; michael@0: // The abi does not expect r12 (ip) to be preserved michael@0: void *lr; michael@0: michael@0: // Arguments. michael@0: // code == r0 michael@0: // argc == r1 michael@0: // argv == r2 michael@0: // frame == r3 michael@0: CalleeToken token; michael@0: JSObject *scopeChain; michael@0: size_t numStackValues; michael@0: Value *vp; michael@0: }; michael@0: michael@0: /* michael@0: * This method generates a trampoline for a c++ function with the following michael@0: * signature: michael@0: * void enter(void *code, int argc, Value *argv, InterpreterFrame *fp, CalleeToken michael@0: * calleeToken, JSObject *scopeChain, Value *vp) michael@0: * ...using standard EABI calling convention michael@0: */ michael@0: JitCode * michael@0: JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type) michael@0: { michael@0: const Address slot_token(sp, offsetof(EnterJITStack, token)); michael@0: const Address slot_vp(sp, offsetof(EnterJITStack, vp)); michael@0: michael@0: JS_ASSERT(OsrFrameReg == r3); michael@0: michael@0: MacroAssembler masm(cx); michael@0: Assembler *aasm = &masm; 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.startDataTransferM(IsStore, sp, DB, WriteBack); michael@0: masm.transferReg(r4); // [sp,0] michael@0: masm.transferReg(r5); // [sp,4] michael@0: masm.transferReg(r6); // [sp,8] michael@0: masm.transferReg(r7); // [sp,12] michael@0: masm.transferReg(r8); // [sp,16] michael@0: masm.transferReg(r9); // [sp,20] michael@0: masm.transferReg(r10); // [sp,24] michael@0: masm.transferReg(r11); // [sp,28] michael@0: // The abi does not expect r12 (ip) to be preserved michael@0: masm.transferReg(lr); // [sp,32] michael@0: // The 5th argument is located at [sp, 36] michael@0: masm.finishDataTransfer(); michael@0: michael@0: // Push the EnterJIT sps mark. "Frame pointer" = start of saved core regs. michael@0: masm.movePtr(sp, r8); michael@0: masm.spsMarkJit(&cx->runtime()->spsProfiler, r8, r9); michael@0: michael@0: // Push the float registers. michael@0: masm.transferMultipleByRuns(NonVolatileFloatRegs, IsStore, sp, DB); michael@0: michael@0: // Save stack pointer into r8 michael@0: masm.movePtr(sp, r8); michael@0: michael@0: // Load calleeToken into r9. michael@0: masm.loadPtr(slot_token, r9); michael@0: michael@0: // Save stack pointer. michael@0: if (type == EnterJitBaseline) michael@0: masm.movePtr(sp, r11); michael@0: michael@0: // Load the number of actual arguments into r10. michael@0: masm.loadPtr(slot_vp, r10); michael@0: masm.unboxInt32(Address(r10, 0), r10); michael@0: michael@0: // Subtract off the size of the arguments from the stack pointer, store elsewhere michael@0: aasm->as_sub(r4, sp, O2RegImmShift(r1, LSL, 3)); //r4 = sp - argc*8 michael@0: // Get the final position of the stack pointer into the stack pointer michael@0: aasm->as_sub(sp, r4, Imm8(16)); // sp' = sp - argc*8 - 16 michael@0: // Get a copy of the number of args to use as a decrement counter, also michael@0: // Set the zero condition code michael@0: aasm->as_mov(r5, O2Reg(r1), SetCond); michael@0: michael@0: // Loop over arguments, copying them from an unknown buffer onto the Ion michael@0: // stack so they can be accessed from JIT'ed code. michael@0: { michael@0: Label header, footer; michael@0: // If there aren't any arguments, don't do anything michael@0: aasm->as_b(&footer, Assembler::Zero); michael@0: // Get the top of the loop michael@0: masm.bind(&header); michael@0: aasm->as_sub(r5, r5, Imm8(1), SetCond); michael@0: // We could be more awesome, and unroll this, using a loadm michael@0: // (particularly since the offset is effectively 0) michael@0: // but that seems more error prone, and complex. michael@0: // BIG FAT WARNING: this loads both r6 and r7. michael@0: aasm->as_extdtr(IsLoad, 64, true, PostIndex, r6, EDtrAddr(r2, EDtrOffImm(8))); michael@0: aasm->as_extdtr(IsStore, 64, true, PostIndex, r6, EDtrAddr(r4, EDtrOffImm(8))); michael@0: aasm->as_b(&header, Assembler::NonZero); michael@0: masm.bind(&footer); michael@0: } michael@0: michael@0: masm.ma_sub(r8, sp, r8); michael@0: masm.makeFrameDescriptor(r8, JitFrame_Entry); michael@0: michael@0: masm.startDataTransferM(IsStore, sp, IB, NoWriteBack); michael@0: // [sp] = return address (written later) michael@0: masm.transferReg(r8); // [sp',4] = descriptor, argc*8+20 michael@0: masm.transferReg(r9); // [sp',8] = callee token michael@0: masm.transferReg(r10); // [sp',12] = actual arguments michael@0: masm.finishDataTransfer(); michael@0: michael@0: Label 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(r11); michael@0: regs.take(ReturnReg); michael@0: michael@0: const Address slot_numStackValues(r11, offsetof(EnterJITStack, numStackValues)); michael@0: michael@0: Label notOsr; michael@0: masm.branchTestPtr(Assembler::Zero, OsrFrameReg, OsrFrameReg, ¬Osr); michael@0: michael@0: Register scratch = regs.takeAny(); michael@0: michael@0: Register numStackValues = regs.takeAny(); michael@0: masm.load32(slot_numStackValues, numStackValues); michael@0: michael@0: // Write return address. On ARM, CodeLabel is only used for tableswitch, michael@0: // so we can't use it here to get the return address. Instead, we use michael@0: // pc + a fixed offset to a jump to returnLabel. The pc register holds michael@0: // pc + 8, so we add the size of 2 instructions to skip the instructions michael@0: // emitted by storePtr and jump(&skipJump). michael@0: { michael@0: AutoForbidPools afp(&masm); michael@0: Label skipJump; michael@0: masm.mov(pc, scratch); michael@0: masm.addPtr(Imm32(2 * sizeof(uint32_t)), scratch); michael@0: masm.storePtr(scratch, Address(sp, 0)); michael@0: masm.jump(&skipJump); michael@0: masm.jump(&returnLabel); michael@0: masm.bind(&skipJump); michael@0: } michael@0: michael@0: // Push previous frame pointer. michael@0: masm.push(r11); michael@0: michael@0: // Reserve frame. michael@0: Register framePtr = r11; michael@0: masm.subPtr(Imm32(BaselineFrame::Size()), sp); michael@0: masm.mov(sp, 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.ma_lsl(Imm32(3), numStackValues, scratch); michael@0: masm.subPtr(scratch, framePtr); michael@0: { michael@0: masm.ma_sub(sp, 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(sp, framePtr); michael@0: #endif michael@0: michael@0: // Reserve space for locals and stack values. michael@0: masm.ma_lsl(Imm32(3), numStackValues, scratch); michael@0: masm.ma_sub(sp, scratch, sp); 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); // BaselineFrame michael@0: masm.push(r0); // jitcode michael@0: michael@0: masm.setupUnalignedABICall(3, scratch); michael@0: masm.passABIArg(r11); // 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: Register jitcode = regs.takeAny(); 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()), sp); 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, sp); michael@0: masm.addPtr(Imm32(2 * sizeof(uintptr_t)), sp); michael@0: masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); michael@0: masm.jump(&returnLabel); michael@0: michael@0: masm.bind(¬Osr); michael@0: // Load the scope chain in R1. michael@0: JS_ASSERT(R1.scratchReg() != r0); michael@0: masm.loadPtr(Address(r11, offsetof(EnterJITStack, scopeChain)), R1.scratchReg()); michael@0: } michael@0: michael@0: // Call the function. michael@0: masm.ma_callIonNoPush(r0); michael@0: michael@0: if (type == EnterJitBaseline) { michael@0: // Baseline OSR will return here. michael@0: masm.bind(&returnLabel); michael@0: } michael@0: michael@0: // The top of the stack now points to the address of the field following michael@0: // the return address because the return address is popped for the michael@0: // return, so we need to remove the size of the return address field. michael@0: aasm->as_sub(sp, sp, Imm8(4)); michael@0: michael@0: // Load off of the stack the size of our local stack michael@0: masm.loadPtr(Address(sp, IonJSFrameLayout::offsetOfDescriptor()), r5); michael@0: aasm->as_add(sp, sp, lsr(r5, FRAMESIZE_SHIFT)); michael@0: michael@0: // Store the returned value into the slot_vp michael@0: masm.loadPtr(slot_vp, r5); michael@0: masm.storeValue(JSReturnOperand, Address(r5, 0)); michael@0: michael@0: // :TODO: Optimize storeValue with: michael@0: // We're using a load-double here. In order for that to work, michael@0: // the data needs to be stored in two consecutive registers, michael@0: // make sure this is the case michael@0: // ASSERT(JSReturnReg_Type.code() == JSReturnReg_Data.code()+1); michael@0: // aasm->as_extdtr(IsStore, 64, true, Offset, michael@0: // JSReturnReg_Data, EDtrAddr(r5, EDtrOffImm(0))); michael@0: michael@0: // Restore non-volatile registers and return. michael@0: GenerateReturn(masm, true, &cx->runtime()->spsProfiler); michael@0: michael@0: Linker linker(masm); michael@0: AutoFlushICache afc("EnterJIT"); 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: // See large comment in x86's JitRuntime::generateInvalidator. michael@0: MacroAssembler masm(cx); michael@0: //masm.as_bkpt(); michael@0: // At this point, one of two things has happened. michael@0: // 1) Execution has just returned from C code, which left the stack aligned michael@0: // 2) Execution has just returned from Ion code, which left the stack unaligned. michael@0: // The old return address should not matter, but we still want the michael@0: // stack to be aligned, and there is no good reason to automatically align it with michael@0: // a call to setupUnalignedABICall. michael@0: masm.ma_and(Imm32(~7), sp, sp); michael@0: masm.startDataTransferM(IsStore, sp, DB, WriteBack); michael@0: // We don't have to push everything, but this is likely easier. michael@0: // setting regs_ michael@0: for (uint32_t i = 0; i < Registers::Total; i++) michael@0: masm.transferReg(Register::FromCode(i)); michael@0: masm.finishDataTransfer(); michael@0: michael@0: masm.startFloatTransferM(IsStore, sp, DB, WriteBack); michael@0: for (uint32_t i = 0; i < FloatRegisters::Total; i++) michael@0: masm.transferFloatReg(FloatRegister::FromCode(i)); michael@0: masm.finishFloatTransfer(); michael@0: michael@0: masm.ma_mov(sp, r0); michael@0: const int sizeOfRetval = sizeof(size_t)*2; michael@0: masm.reserveStack(sizeOfRetval); michael@0: masm.mov(sp, r1); michael@0: const int sizeOfBailoutInfo = sizeof(void *)*2; michael@0: masm.reserveStack(sizeOfBailoutInfo); michael@0: masm.mov(sp, r2); michael@0: masm.setupAlignedABICall(3); michael@0: masm.passABIArg(r0); michael@0: masm.passABIArg(r1); michael@0: masm.passABIArg(r2); michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InvalidationBailout)); michael@0: michael@0: masm.ma_ldr(Address(sp, 0), r2); michael@0: masm.ma_ldr(Address(sp, sizeOfBailoutInfo), r1); michael@0: // Remove the return address, the IonScript, the register state michael@0: // (InvaliationBailoutStack) and the space that was allocated for the return value michael@0: masm.ma_add(sp, Imm32(sizeof(InvalidationBailoutStack) + sizeOfRetval + sizeOfBailoutInfo), sp); michael@0: // remove the space that this frame was using before the bailout michael@0: // (computed by InvalidationBailout) michael@0: masm.ma_add(sp, r1, sp); michael@0: michael@0: // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2. michael@0: JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail(); michael@0: masm.branch(bailoutTail); michael@0: michael@0: Linker linker(masm); michael@0: AutoFlushICache afc("Invalidator"); 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: // 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: // Copy number of actual arguments into r0 michael@0: masm.ma_ldr(DTRAddr(sp, DtrOffImm(IonRectifierFrameLayout::offsetOfNumActualArgs())), r0); michael@0: michael@0: // Load the number of |undefined|s to push into r6. michael@0: masm.ma_ldr(DTRAddr(sp, DtrOffImm(IonRectifierFrameLayout::offsetOfCalleeToken())), r1); michael@0: masm.ma_ldrh(EDtrAddr(r1, EDtrOffImm(JSFunction::offsetOfNargs())), r6); michael@0: michael@0: masm.ma_sub(r6, r8, r2); michael@0: michael@0: masm.moveValue(UndefinedValue(), r5, r4); michael@0: michael@0: masm.ma_mov(sp, r3); // Save %sp. michael@0: masm.ma_mov(sp, r7); // Save %sp again. michael@0: michael@0: // Push undefined. michael@0: { michael@0: Label undefLoopTop; michael@0: masm.bind(&undefLoopTop); michael@0: masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex); michael@0: masm.ma_sub(r2, Imm32(1), r2, SetCond); michael@0: michael@0: masm.ma_b(&undefLoopTop, Assembler::NonZero); michael@0: } michael@0: michael@0: // Get the topmost argument. michael@0: michael@0: masm.ma_alu(r3, lsl(r8, 3), r3, op_add); // r3 <- r3 + nargs * 8 michael@0: masm.ma_add(r3, Imm32(sizeof(IonRectifierFrameLayout)), r3); michael@0: michael@0: // Push arguments, |nargs| + 1 times (to include |this|). michael@0: { michael@0: Label copyLoopTop; michael@0: masm.bind(©LoopTop); michael@0: masm.ma_dataTransferN(IsLoad, 64, true, r3, Imm32(-8), r4, PostIndex); michael@0: masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex); michael@0: michael@0: masm.ma_sub(r8, Imm32(1), r8, SetCond); michael@0: masm.ma_b(©LoopTop, Assembler::NotSigned); michael@0: } michael@0: michael@0: // translate the framesize from values into bytes michael@0: masm.ma_add(r6, Imm32(1), r6); michael@0: masm.ma_lsl(Imm32(3), r6, r6); michael@0: michael@0: // Construct sizeDescriptor. michael@0: masm.makeFrameDescriptor(r6, JitFrame_Rectifier); michael@0: michael@0: // Construct IonJSFrameLayout. michael@0: masm.ma_push(r0); // actual arguments. michael@0: masm.ma_push(r1); // callee token michael@0: masm.ma_push(r6); // frame descriptor. michael@0: michael@0: // Call the target function. michael@0: // Note that this code assumes the function is JITted. michael@0: masm.ma_ldr(DTRAddr(r1, DtrOffImm(JSFunction::offsetOfNativeOrScript())), r3); michael@0: masm.loadBaselineOrIonRaw(r3, r3, mode, nullptr); michael@0: masm.ma_callIonHalfPush(r3); michael@0: michael@0: uint32_t returnOffset = masm.currentOffset(); michael@0: michael@0: // arg1 michael@0: // ... michael@0: // argN michael@0: // num actual args michael@0: // callee token michael@0: // sizeDescriptor <- sp now michael@0: // return address michael@0: michael@0: // Remove the rectifier frame. michael@0: masm.ma_dtr(IsLoad, sp, Imm32(12), r4, PostIndex); michael@0: michael@0: // arg1 michael@0: // ... michael@0: // argN <- sp now; r4 <- frame descriptor michael@0: // num actual args michael@0: // callee token michael@0: // sizeDescriptor michael@0: // return address michael@0: michael@0: // Discard pushed arguments. michael@0: masm.ma_alu(sp, lsr(r4, FRAMESIZE_SHIFT), sp, op_add); michael@0: michael@0: masm.ret(); michael@0: Linker linker(masm); michael@0: AutoFlushICache afc("ArgumentsRectifier"); michael@0: JitCode *code = linker.newCode(cx, JSC::OTHER_CODE); 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: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier"); michael@0: #endif michael@0: 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: // the stack should look like: michael@0: // [IonFrame] michael@0: // bailoutFrame.registersnapshot michael@0: // bailoutFrame.fpsnapshot michael@0: // bailoutFrame.snapshotOffset michael@0: // bailoutFrame.frameSize michael@0: michael@0: // STEP 1a: save our register sets to the stack so Bailout() can michael@0: // read everything. michael@0: // sp % 8 == 0 michael@0: masm.startDataTransferM(IsStore, sp, DB, WriteBack); michael@0: // We don't have to push everything, but this is likely easier. michael@0: // setting regs_ michael@0: for (uint32_t i = 0; i < Registers::Total; i++) michael@0: masm.transferReg(Register::FromCode(i)); michael@0: masm.finishDataTransfer(); michael@0: michael@0: masm.startFloatTransferM(IsStore, sp, DB, WriteBack); michael@0: for (uint32_t i = 0; i < FloatRegisters::Total; i++) michael@0: masm.transferFloatReg(FloatRegister::FromCode(i)); michael@0: masm.finishFloatTransfer(); michael@0: michael@0: // STEP 1b: Push both the "return address" of the function call (the michael@0: // address of the instruction after the call that we used to get michael@0: // here) as well as the callee token onto the stack. The return michael@0: // address is currently in r14. We will proceed by loading the michael@0: // callee token into a sacrificial register <= r14, then pushing michael@0: // both onto the stack michael@0: michael@0: // now place the frameClass onto the stack, via a register michael@0: masm.ma_mov(Imm32(frameClass), r4); michael@0: // And onto the stack. Since the stack is full, we need to put this michael@0: // one past the end of the current stack. Sadly, the ABI says that we need michael@0: // to always point to the lowest place that has been written. the OS is michael@0: // free to do whatever it wants below sp. michael@0: masm.startDataTransferM(IsStore, sp, DB, WriteBack); michael@0: // set frameClassId_ michael@0: masm.transferReg(r4); michael@0: // Set tableOffset_; higher registers are stored at higher locations on michael@0: // the stack. michael@0: masm.transferReg(lr); michael@0: masm.finishDataTransfer(); michael@0: michael@0: // SP % 8 == 4 michael@0: // STEP 1c: Call the bailout function, giving a pointer to the michael@0: // structure we just blitted onto the stack michael@0: masm.ma_mov(sp, r0); michael@0: const int sizeOfBailoutInfo = sizeof(void *)*2; michael@0: masm.reserveStack(sizeOfBailoutInfo); michael@0: masm.mov(sp, r1); michael@0: masm.setupAlignedABICall(2); michael@0: michael@0: // Decrement sp by another 4, so we keep alignment michael@0: // Not Anymore! pushing both the snapshotoffset as well as the michael@0: // masm.as_sub(sp, sp, Imm8(4)); michael@0: michael@0: // Set the old (4-byte aligned) value of the sp as the first argument michael@0: masm.passABIArg(r0); michael@0: masm.passABIArg(r1); michael@0: michael@0: // Sp % 8 == 0 michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Bailout)); michael@0: masm.ma_ldr(Address(sp, 0), r2); michael@0: masm.ma_add(sp, Imm32(sizeOfBailoutInfo), sp); michael@0: // Common size of a bailout frame. michael@0: uint32_t bailoutFrameSize = sizeof(void *) + // frameClass michael@0: sizeof(double) * FloatRegisters::Total + michael@0: sizeof(void *) * Registers::Total; michael@0: michael@0: if (frameClass == NO_FRAME_SIZE_CLASS_ID) { michael@0: // Make sure the bailout frame size fits into the offset for a load michael@0: masm.as_dtr(IsLoad, 32, Offset, michael@0: r4, DTRAddr(sp, DtrOffImm(4))); michael@0: // used to be: offsetof(BailoutStack, frameSize_) michael@0: // this structure is no longer available to us :( michael@0: // We add 12 to the bailoutFrameSize because: michael@0: // sizeof(uint32_t) for the tableOffset that was pushed onto the stack michael@0: // sizeof(uintptr_t) for the snapshotOffset; michael@0: // alignment to round the uintptr_t up to a multiple of 8 bytes. michael@0: masm.ma_add(sp, Imm32(bailoutFrameSize+12), sp); michael@0: masm.as_add(sp, sp, O2Reg(r4)); michael@0: } else { michael@0: uint32_t frameSize = FrameSizeClass::FromClass(frameClass).frameSize(); michael@0: masm.ma_add(Imm32(frameSize // the frame that was added when we entered the most recent function michael@0: + sizeof(void*) // the size of the "return address" that was dumped on the stack michael@0: + bailoutFrameSize) // everything else that was pushed on the stack michael@0: , sp); michael@0: } michael@0: michael@0: // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2. michael@0: JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail(); michael@0: masm.branch(bailoutTail); michael@0: } michael@0: michael@0: JitCode * michael@0: JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass) michael@0: { michael@0: MacroAssembler masm(cx); michael@0: michael@0: Label bailout; michael@0: for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++) michael@0: masm.ma_bl(&bailout); michael@0: masm.bind(&bailout); michael@0: michael@0: GenerateBailoutThunk(cx, masm, frameClass); michael@0: michael@0: Linker linker(masm); michael@0: AutoFlushICache afc("BailoutTable"); michael@0: JitCode *code = linker.newCode(cx, JSC::OTHER_CODE); michael@0: michael@0: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(code, "BailoutTable"); 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(cx); michael@0: GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID); michael@0: michael@0: Linker linker(masm); michael@0: AutoFlushICache afc("BailoutHandler"); 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(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(cx); 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; r0 is the first argument register. michael@0: Register cxreg = r0; michael@0: regs.take(cxreg); michael@0: michael@0: // Stack is: michael@0: // ... frame ... michael@0: // +8 [args] + argPadding michael@0: // +0 ExitFrame 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 base of the argument set stored on the stack. michael@0: Register argsBase = InvalidReg; michael@0: if (f.explicitArgs) { michael@0: argsBase = r5; michael@0: regs.take(argsBase); michael@0: masm.ma_add(sp, Imm32(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 = r4; michael@0: regs.take(outReg); michael@0: masm.reserveStack(sizeof(Value)); michael@0: masm.ma_mov(sp, outReg); michael@0: break; michael@0: michael@0: case Type_Handle: michael@0: outReg = r4; michael@0: regs.take(outReg); michael@0: masm.PushEmptyRooted(f.outParamRootType); michael@0: masm.ma_mov(sp, outReg); michael@0: break; michael@0: michael@0: case Type_Int32: michael@0: case Type_Pointer: michael@0: case Type_Bool: michael@0: outReg = r4; michael@0: regs.take(outReg); michael@0: masm.reserveStack(sizeof(int32_t)); michael@0: masm.ma_mov(sp, outReg); michael@0: break; michael@0: michael@0: case Type_Double: michael@0: outReg = r4; michael@0: regs.take(outReg); michael@0: masm.reserveStack(sizeof(double)); michael@0: masm.ma_mov(sp, 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 any 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: // Values should be passed by reference, not by value, so we michael@0: // assert that the argument is a double-precision float. michael@0: JS_ASSERT(f.argPassedInFloatReg(explicitArg)); michael@0: masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE); michael@0: argDisp += sizeof(double); michael@0: break; michael@0: case VMFunction::WordByRef: michael@0: masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), 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), 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, r0, r0, masm.failureLabel(f.executionMode)); michael@0: break; michael@0: case Type_Bool: michael@0: masm.branchIfFalseBool(r0, 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(sp, 0), JSReturnOperand); michael@0: masm.freeStack(sizeof(Value)); michael@0: break; michael@0: michael@0: case Type_Int32: michael@0: case Type_Pointer: michael@0: masm.load32(Address(sp, 0), ReturnReg); michael@0: masm.freeStack(sizeof(int32_t)); michael@0: break; michael@0: michael@0: case Type_Bool: michael@0: masm.load8ZeroExtend(Address(sp, 0), ReturnReg); michael@0: masm.freeStack(sizeof(int32_t)); michael@0: break; michael@0: michael@0: case Type_Double: michael@0: if (cx->runtime()->jitSupportsFloatingPoint) michael@0: masm.loadDouble(Address(sp, 0), ReturnFloatReg); michael@0: else michael@0: masm.assumeUnreachable("Unable to load into float reg, with no FP support."); michael@0: masm.freeStack(sizeof(double)); 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: AutoFlushICache afc("VMWrapper"); michael@0: JitCode *wrapper = linker.newCode(cx, JSC::OTHER_CODE); michael@0: if (!wrapper) michael@0: return nullptr; 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: #ifdef JS_ION_PERF michael@0: writePerfSpewerJitCodeProfile(wrapper, "VMWrapper"); michael@0: #endif 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(cx); 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 == r1); michael@0: masm.movePtr(ImmPtr(cx->runtime()), r0); michael@0: michael@0: masm.setupUnalignedABICall(2, r2); michael@0: masm.passABIArg(r0); michael@0: masm.passABIArg(r1); 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: AutoFlushICache afc("PreBarrier"); 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 = r0; michael@0: Register scratch2 = r1; michael@0: michael@0: // Load BaselineFrame pointer in scratch1. michael@0: masm.mov(r11, scratch1); michael@0: masm.subPtr(Imm32(BaselineFrame::Size()), scratch1); 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, scratch2); 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(lr); michael@0: masm.push(scratch1); 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.mov(lr, pc); michael@0: michael@0: masm.bind(&forcedReturn); michael@0: masm.loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()), michael@0: JSReturnOperand); michael@0: masm.mov(r11, sp); michael@0: masm.pop(r11); michael@0: masm.ret(); michael@0: michael@0: Linker linker(masm); michael@0: AutoFlushICache afc("DebugTrapHandler"); 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: AutoFlushICache afc("ExceptionTailStub"); 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(r1, r2); michael@0: michael@0: Linker linker(masm); michael@0: AutoFlushICache afc("BailoutTailStub"); 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: }