js/src/jit/arm/Trampoline-arm.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "jscompartment.h"
     9 #include "assembler/assembler/MacroAssembler.h"
    10 #include "jit/arm/BaselineHelpers-arm.h"
    11 #include "jit/Bailouts.h"
    12 #include "jit/IonFrames.h"
    13 #include "jit/IonLinker.h"
    14 #include "jit/IonSpewer.h"
    15 #include "jit/JitCompartment.h"
    16 #ifdef JS_ION_PERF
    17 # include "jit/PerfSpewer.h"
    18 #endif
    19 #include "jit/VMFunctions.h"
    21 #include "jit/ExecutionMode-inl.h"
    23 using namespace js;
    24 using namespace js::jit;
    26 static const FloatRegisterSet NonVolatileFloatRegs =
    27     FloatRegisterSet((1 << FloatRegisters::d8) |
    28                      (1 << FloatRegisters::d9) |
    29                      (1 << FloatRegisters::d10) |
    30                      (1 << FloatRegisters::d11) |
    31                      (1 << FloatRegisters::d12) |
    32                      (1 << FloatRegisters::d13) |
    33                      (1 << FloatRegisters::d14) |
    34                      (1 << FloatRegisters::d15));
    36 static void
    37 GenerateReturn(MacroAssembler &masm, int returnCode, SPSProfiler *prof)
    38 {
    39     // Restore non-volatile floating point registers
    40     masm.transferMultipleByRuns(NonVolatileFloatRegs, IsLoad, StackPointer, IA);
    42     // Unwind the sps mark.
    43     masm.spsUnmarkJit(prof, r8);
    45     // Set up return value
    46     masm.ma_mov(Imm32(returnCode), r0);
    48     // Pop and return
    49     masm.startDataTransferM(IsLoad, sp, IA, WriteBack);
    50     masm.transferReg(r4);
    51     masm.transferReg(r5);
    52     masm.transferReg(r6);
    53     masm.transferReg(r7);
    54     masm.transferReg(r8);
    55     masm.transferReg(r9);
    56     masm.transferReg(r10);
    57     masm.transferReg(r11);
    58     // r12 isn't saved, so it shouldn't be restored.
    59     masm.transferReg(pc);
    60     masm.finishDataTransfer();
    61     masm.dumpPool();
    62 }
    64 struct EnterJITStack
    65 {
    66     double d8;
    67     double d9;
    68     double d10;
    69     double d11;
    70     double d12;
    71     double d13;
    72     double d14;
    73     double d15;
    75     size_t hasSPSMark;
    77     // non-volatile registers.
    78     void *r4;
    79     void *r5;
    80     void *r6;
    81     void *r7;
    82     void *r8;
    83     void *r9;
    84     void *r10;
    85     void *r11;
    86     // The abi does not expect r12 (ip) to be preserved
    87     void *lr;
    89     // Arguments.
    90     // code == r0
    91     // argc == r1
    92     // argv == r2
    93     // frame == r3
    94     CalleeToken token;
    95     JSObject *scopeChain;
    96     size_t numStackValues;
    97     Value *vp;
    98 };
   100 /*
   101  * This method generates a trampoline for a c++ function with the following
   102  * signature:
   103  *   void enter(void *code, int argc, Value *argv, InterpreterFrame *fp, CalleeToken
   104  *              calleeToken, JSObject *scopeChain, Value *vp)
   105  *   ...using standard EABI calling convention
   106  */
   107 JitCode *
   108 JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type)
   109 {
   110     const Address slot_token(sp, offsetof(EnterJITStack, token));
   111     const Address slot_vp(sp, offsetof(EnterJITStack, vp));
   113     JS_ASSERT(OsrFrameReg == r3);
   115     MacroAssembler masm(cx);
   116     Assembler *aasm = &masm;
   118     // Save non-volatile registers. These must be saved by the trampoline,
   119     // rather than the JIT'd code, because they are scanned by the conservative
   120     // scanner.
   121     masm.startDataTransferM(IsStore, sp, DB, WriteBack);
   122     masm.transferReg(r4); // [sp,0]
   123     masm.transferReg(r5); // [sp,4]
   124     masm.transferReg(r6); // [sp,8]
   125     masm.transferReg(r7); // [sp,12]
   126     masm.transferReg(r8); // [sp,16]
   127     masm.transferReg(r9); // [sp,20]
   128     masm.transferReg(r10); // [sp,24]
   129     masm.transferReg(r11); // [sp,28]
   130     // The abi does not expect r12 (ip) to be preserved
   131     masm.transferReg(lr);  // [sp,32]
   132     // The 5th argument is located at [sp, 36]
   133     masm.finishDataTransfer();
   135     // Push the EnterJIT sps mark.  "Frame pointer" = start of saved core regs.
   136     masm.movePtr(sp, r8);
   137     masm.spsMarkJit(&cx->runtime()->spsProfiler, r8, r9);
   139     // Push the float registers.
   140     masm.transferMultipleByRuns(NonVolatileFloatRegs, IsStore, sp, DB);
   142     // Save stack pointer into r8
   143     masm.movePtr(sp, r8);
   145     // Load calleeToken into r9.
   146     masm.loadPtr(slot_token, r9);
   148     // Save stack pointer.
   149     if (type == EnterJitBaseline)
   150         masm.movePtr(sp, r11);
   152     // Load the number of actual arguments into r10.
   153     masm.loadPtr(slot_vp, r10);
   154     masm.unboxInt32(Address(r10, 0), r10);
   156     // Subtract off the size of the arguments from the stack pointer, store elsewhere
   157     aasm->as_sub(r4, sp, O2RegImmShift(r1, LSL, 3)); //r4 = sp - argc*8
   158     // Get the final position of the stack pointer into the stack pointer
   159     aasm->as_sub(sp, r4, Imm8(16)); // sp' = sp - argc*8 - 16
   160     // Get a copy of the number of args to use as a decrement counter, also
   161     // Set the zero condition code
   162     aasm->as_mov(r5, O2Reg(r1), SetCond);
   164     // Loop over arguments, copying them from an unknown buffer onto the Ion
   165     // stack so they can be accessed from JIT'ed code.
   166     {
   167         Label header, footer;
   168         // If there aren't any arguments, don't do anything
   169         aasm->as_b(&footer, Assembler::Zero);
   170         // Get the top of the loop
   171         masm.bind(&header);
   172         aasm->as_sub(r5, r5, Imm8(1), SetCond);
   173         // We could be more awesome, and unroll this, using a loadm
   174         // (particularly since the offset is effectively 0)
   175         // but that seems more error prone, and complex.
   176         // BIG FAT WARNING: this loads both r6 and r7.
   177         aasm->as_extdtr(IsLoad,  64, true, PostIndex, r6, EDtrAddr(r2, EDtrOffImm(8)));
   178         aasm->as_extdtr(IsStore, 64, true, PostIndex, r6, EDtrAddr(r4, EDtrOffImm(8)));
   179         aasm->as_b(&header, Assembler::NonZero);
   180         masm.bind(&footer);
   181     }
   183     masm.ma_sub(r8, sp, r8);
   184     masm.makeFrameDescriptor(r8, JitFrame_Entry);
   186     masm.startDataTransferM(IsStore, sp, IB, NoWriteBack);
   187                            // [sp]    = return address (written later)
   188     masm.transferReg(r8);  // [sp',4] = descriptor, argc*8+20
   189     masm.transferReg(r9);  // [sp',8]  = callee token
   190     masm.transferReg(r10); // [sp',12]  = actual arguments
   191     masm.finishDataTransfer();
   193     Label returnLabel;
   194     if (type == EnterJitBaseline) {
   195         // Handle OSR.
   196         GeneralRegisterSet regs(GeneralRegisterSet::All());
   197         regs.take(JSReturnOperand);
   198         regs.takeUnchecked(OsrFrameReg);
   199         regs.take(r11);
   200         regs.take(ReturnReg);
   202         const Address slot_numStackValues(r11, offsetof(EnterJITStack, numStackValues));
   204         Label notOsr;
   205         masm.branchTestPtr(Assembler::Zero, OsrFrameReg, OsrFrameReg, &notOsr);
   207         Register scratch = regs.takeAny();
   209         Register numStackValues = regs.takeAny();
   210         masm.load32(slot_numStackValues, numStackValues);
   212         // Write return address. On ARM, CodeLabel is only used for tableswitch,
   213         // so we can't use it here to get the return address. Instead, we use
   214         // pc + a fixed offset to a jump to returnLabel. The pc register holds
   215         // pc + 8, so we add the size of 2 instructions to skip the instructions
   216         // emitted by storePtr and jump(&skipJump).
   217         {
   218             AutoForbidPools afp(&masm);
   219             Label skipJump;
   220             masm.mov(pc, scratch);
   221             masm.addPtr(Imm32(2 * sizeof(uint32_t)), scratch);
   222             masm.storePtr(scratch, Address(sp, 0));
   223             masm.jump(&skipJump);
   224             masm.jump(&returnLabel);
   225             masm.bind(&skipJump);
   226         }
   228         // Push previous frame pointer.
   229         masm.push(r11);
   231         // Reserve frame.
   232         Register framePtr = r11;
   233         masm.subPtr(Imm32(BaselineFrame::Size()), sp);
   234         masm.mov(sp, framePtr);
   236 #ifdef XP_WIN
   237         // Can't push large frames blindly on windows.  Touch frame memory incrementally.
   238         masm.ma_lsl(Imm32(3), numStackValues, scratch);
   239         masm.subPtr(scratch, framePtr);
   240         {
   241             masm.ma_sub(sp, Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch);
   243             Label touchFrameLoop;
   244             Label touchFrameLoopEnd;
   245             masm.bind(&touchFrameLoop);
   246             masm.branchPtr(Assembler::Below, scratch, framePtr, &touchFrameLoopEnd);
   247             masm.store32(Imm32(0), Address(scratch, 0));
   248             masm.subPtr(Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch);
   249             masm.jump(&touchFrameLoop);
   250             masm.bind(&touchFrameLoopEnd);
   251         }
   252         masm.mov(sp, framePtr);
   253 #endif
   255         // Reserve space for locals and stack values.
   256         masm.ma_lsl(Imm32(3), numStackValues, scratch);
   257         masm.ma_sub(sp, scratch, sp);
   259         // Enter exit frame.
   260         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch);
   261         masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
   262         masm.push(scratch);
   263         masm.push(Imm32(0)); // Fake return address.
   264         masm.enterFakeExitFrame();
   266         masm.push(framePtr); // BaselineFrame
   267         masm.push(r0); // jitcode
   269         masm.setupUnalignedABICall(3, scratch);
   270         masm.passABIArg(r11); // BaselineFrame
   271         masm.passABIArg(OsrFrameReg); // InterpreterFrame
   272         masm.passABIArg(numStackValues);
   273         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, jit::InitBaselineFrameForOsr));
   275         Register jitcode = regs.takeAny();
   276         masm.pop(jitcode);
   277         masm.pop(framePtr);
   279         JS_ASSERT(jitcode != ReturnReg);
   281         Label error;
   282         masm.addPtr(Imm32(IonExitFrameLayout::SizeWithFooter()), sp);
   283         masm.addPtr(Imm32(BaselineFrame::Size()), framePtr);
   284         masm.branchIfFalseBool(ReturnReg, &error);
   286         masm.jump(jitcode);
   288         // OOM: load error value, discard return address and previous frame
   289         // pointer and return.
   290         masm.bind(&error);
   291         masm.mov(framePtr, sp);
   292         masm.addPtr(Imm32(2 * sizeof(uintptr_t)), sp);
   293         masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
   294         masm.jump(&returnLabel);
   296         masm.bind(&notOsr);
   297         // Load the scope chain in R1.
   298         JS_ASSERT(R1.scratchReg() != r0);
   299         masm.loadPtr(Address(r11, offsetof(EnterJITStack, scopeChain)), R1.scratchReg());
   300     }
   302     // Call the function.
   303     masm.ma_callIonNoPush(r0);
   305     if (type == EnterJitBaseline) {
   306         // Baseline OSR will return here.
   307         masm.bind(&returnLabel);
   308     }
   310     // The top of the stack now points to the address of the field following
   311     // the return address because the return address is popped for the
   312     // return, so we need to remove the size of the return address field.
   313     aasm->as_sub(sp, sp, Imm8(4));
   315     // Load off of the stack the size of our local stack
   316     masm.loadPtr(Address(sp, IonJSFrameLayout::offsetOfDescriptor()), r5);
   317     aasm->as_add(sp, sp, lsr(r5, FRAMESIZE_SHIFT));
   319     // Store the returned value into the slot_vp
   320     masm.loadPtr(slot_vp, r5);
   321     masm.storeValue(JSReturnOperand, Address(r5, 0));
   323     // :TODO: Optimize storeValue with:
   324     // We're using a load-double here.  In order for that to work,
   325     // the data needs to be stored in two consecutive registers,
   326     // make sure this is the case
   327     //   ASSERT(JSReturnReg_Type.code() == JSReturnReg_Data.code()+1);
   328     //   aasm->as_extdtr(IsStore, 64, true, Offset,
   329     //                   JSReturnReg_Data, EDtrAddr(r5, EDtrOffImm(0)));
   331     // Restore non-volatile registers and return.
   332     GenerateReturn(masm, true, &cx->runtime()->spsProfiler);
   334     Linker linker(masm);
   335     AutoFlushICache afc("EnterJIT");
   336     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   338 #ifdef JS_ION_PERF
   339     writePerfSpewerJitCodeProfile(code, "EnterJIT");
   340 #endif
   342     return code;
   343 }
   345 JitCode *
   346 JitRuntime::generateInvalidator(JSContext *cx)
   347 {
   348     // See large comment in x86's JitRuntime::generateInvalidator.
   349     MacroAssembler masm(cx);
   350     //masm.as_bkpt();
   351     // At this point, one of two things has happened.
   352     // 1) Execution has just returned from C code, which left the stack aligned
   353     // 2) Execution has just returned from Ion code, which left the stack unaligned.
   354     // The old return address should not matter, but we still want the
   355     // stack to be aligned, and there is no good reason to automatically align it with
   356     // a call to setupUnalignedABICall.
   357     masm.ma_and(Imm32(~7), sp, sp);
   358     masm.startDataTransferM(IsStore, sp, DB, WriteBack);
   359     // We don't have to push everything, but this is likely easier.
   360     // setting regs_
   361     for (uint32_t i = 0; i < Registers::Total; i++)
   362         masm.transferReg(Register::FromCode(i));
   363     masm.finishDataTransfer();
   365     masm.startFloatTransferM(IsStore, sp, DB, WriteBack);
   366     for (uint32_t i = 0; i < FloatRegisters::Total; i++)
   367         masm.transferFloatReg(FloatRegister::FromCode(i));
   368     masm.finishFloatTransfer();
   370     masm.ma_mov(sp, r0);
   371     const int sizeOfRetval = sizeof(size_t)*2;
   372     masm.reserveStack(sizeOfRetval);
   373     masm.mov(sp, r1);
   374     const int sizeOfBailoutInfo = sizeof(void *)*2;
   375     masm.reserveStack(sizeOfBailoutInfo);
   376     masm.mov(sp, r2);
   377     masm.setupAlignedABICall(3);
   378     masm.passABIArg(r0);
   379     masm.passABIArg(r1);
   380     masm.passABIArg(r2);
   381     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InvalidationBailout));
   383     masm.ma_ldr(Address(sp, 0), r2);
   384     masm.ma_ldr(Address(sp, sizeOfBailoutInfo), r1);
   385     // Remove the return address, the IonScript, the register state
   386     // (InvaliationBailoutStack) and the space that was allocated for the return value
   387     masm.ma_add(sp, Imm32(sizeof(InvalidationBailoutStack) + sizeOfRetval + sizeOfBailoutInfo), sp);
   388     // remove the space that this frame was using before the bailout
   389     // (computed by InvalidationBailout)
   390     masm.ma_add(sp, r1, sp);
   392     // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2.
   393     JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
   394     masm.branch(bailoutTail);
   396     Linker linker(masm);
   397     AutoFlushICache afc("Invalidator");
   398     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   399     IonSpew(IonSpew_Invalidate, "   invalidation thunk created at %p", (void *) code->raw());
   401 #ifdef JS_ION_PERF
   402     writePerfSpewerJitCodeProfile(code, "Invalidator");
   403 #endif
   405     return code;
   406 }
   408 JitCode *
   409 JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut)
   410 {
   411     MacroAssembler masm(cx);
   412     // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame.
   413     // Including |this|, there are (|nargs| + 1) arguments to copy.
   414     JS_ASSERT(ArgumentsRectifierReg == r8);
   416     // Copy number of actual arguments into r0
   417     masm.ma_ldr(DTRAddr(sp, DtrOffImm(IonRectifierFrameLayout::offsetOfNumActualArgs())), r0);
   419     // Load the number of |undefined|s to push into r6.
   420     masm.ma_ldr(DTRAddr(sp, DtrOffImm(IonRectifierFrameLayout::offsetOfCalleeToken())), r1);
   421     masm.ma_ldrh(EDtrAddr(r1, EDtrOffImm(JSFunction::offsetOfNargs())), r6);
   423     masm.ma_sub(r6, r8, r2);
   425     masm.moveValue(UndefinedValue(), r5, r4);
   427     masm.ma_mov(sp, r3); // Save %sp.
   428     masm.ma_mov(sp, r7); // Save %sp again.
   430     // Push undefined.
   431     {
   432         Label undefLoopTop;
   433         masm.bind(&undefLoopTop);
   434         masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex);
   435         masm.ma_sub(r2, Imm32(1), r2, SetCond);
   437         masm.ma_b(&undefLoopTop, Assembler::NonZero);
   438     }
   440     // Get the topmost argument.
   442     masm.ma_alu(r3, lsl(r8, 3), r3, op_add); // r3 <- r3 + nargs * 8
   443     masm.ma_add(r3, Imm32(sizeof(IonRectifierFrameLayout)), r3);
   445     // Push arguments, |nargs| + 1 times (to include |this|).
   446     {
   447         Label copyLoopTop;
   448         masm.bind(&copyLoopTop);
   449         masm.ma_dataTransferN(IsLoad, 64, true, r3, Imm32(-8), r4, PostIndex);
   450         masm.ma_dataTransferN(IsStore, 64, true, sp, Imm32(-8), r4, PreIndex);
   452         masm.ma_sub(r8, Imm32(1), r8, SetCond);
   453         masm.ma_b(&copyLoopTop, Assembler::NotSigned);
   454     }
   456     // translate the framesize from values into bytes
   457     masm.ma_add(r6, Imm32(1), r6);
   458     masm.ma_lsl(Imm32(3), r6, r6);
   460     // Construct sizeDescriptor.
   461     masm.makeFrameDescriptor(r6, JitFrame_Rectifier);
   463     // Construct IonJSFrameLayout.
   464     masm.ma_push(r0); // actual arguments.
   465     masm.ma_push(r1); // callee token
   466     masm.ma_push(r6); // frame descriptor.
   468     // Call the target function.
   469     // Note that this code assumes the function is JITted.
   470     masm.ma_ldr(DTRAddr(r1, DtrOffImm(JSFunction::offsetOfNativeOrScript())), r3);
   471     masm.loadBaselineOrIonRaw(r3, r3, mode, nullptr);
   472     masm.ma_callIonHalfPush(r3);
   474     uint32_t returnOffset = masm.currentOffset();
   476     // arg1
   477     //  ...
   478     // argN
   479     // num actual args
   480     // callee token
   481     // sizeDescriptor     <- sp now
   482     // return address
   484     // Remove the rectifier frame.
   485     masm.ma_dtr(IsLoad, sp, Imm32(12), r4, PostIndex);
   487     // arg1
   488     //  ...
   489     // argN               <- sp now; r4 <- frame descriptor
   490     // num actual args
   491     // callee token
   492     // sizeDescriptor
   493     // return address
   495     // Discard pushed arguments.
   496     masm.ma_alu(sp, lsr(r4, FRAMESIZE_SHIFT), sp, op_add);
   498     masm.ret();
   499     Linker linker(masm);
   500     AutoFlushICache afc("ArgumentsRectifier");
   501     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   503     CodeOffsetLabel returnLabel(returnOffset);
   504     returnLabel.fixup(&masm);
   505     if (returnAddrOut)
   506         *returnAddrOut = (void *) (code->raw() + returnLabel.offset());
   508 #ifdef JS_ION_PERF
   509     writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
   510 #endif
   512     return code;
   513 }
   515 static void
   516 GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
   517 {
   518     // the stack should look like:
   519     // [IonFrame]
   520     // bailoutFrame.registersnapshot
   521     // bailoutFrame.fpsnapshot
   522     // bailoutFrame.snapshotOffset
   523     // bailoutFrame.frameSize
   525     // STEP 1a: save our register sets to the stack so Bailout() can
   526     // read everything.
   527     // sp % 8 == 0
   528     masm.startDataTransferM(IsStore, sp, DB, WriteBack);
   529     // We don't have to push everything, but this is likely easier.
   530     // setting regs_
   531     for (uint32_t i = 0; i < Registers::Total; i++)
   532         masm.transferReg(Register::FromCode(i));
   533     masm.finishDataTransfer();
   535     masm.startFloatTransferM(IsStore, sp, DB, WriteBack);
   536     for (uint32_t i = 0; i < FloatRegisters::Total; i++)
   537         masm.transferFloatReg(FloatRegister::FromCode(i));
   538     masm.finishFloatTransfer();
   540     // STEP 1b: Push both the "return address" of the function call (the
   541     //          address of the instruction after the call that we used to get
   542     //          here) as well as the callee token onto the stack.  The return
   543     //          address is currently in r14.  We will proceed by loading the
   544     //          callee token into a sacrificial register <= r14, then pushing
   545     //          both onto the stack
   547     // now place the frameClass onto the stack, via a register
   548     masm.ma_mov(Imm32(frameClass), r4);
   549     // And onto the stack.  Since the stack is full, we need to put this
   550     // one past the end of the current stack. Sadly, the ABI says that we need
   551     // to always point to the lowest place that has been written.  the OS is
   552     // free to do whatever it wants below sp.
   553     masm.startDataTransferM(IsStore, sp, DB, WriteBack);
   554     // set frameClassId_
   555     masm.transferReg(r4);
   556     // Set tableOffset_; higher registers are stored at higher locations on
   557     // the stack.
   558     masm.transferReg(lr);
   559     masm.finishDataTransfer();
   561     // SP % 8 == 4
   562     // STEP 1c: Call the bailout function, giving a pointer to the
   563     //          structure we just blitted onto the stack
   564     masm.ma_mov(sp, r0);
   565     const int sizeOfBailoutInfo = sizeof(void *)*2;
   566     masm.reserveStack(sizeOfBailoutInfo);
   567     masm.mov(sp, r1);
   568     masm.setupAlignedABICall(2);
   570     // Decrement sp by another 4, so we keep alignment
   571     // Not Anymore!  pushing both the snapshotoffset as well as the
   572     // masm.as_sub(sp, sp, Imm8(4));
   574     // Set the old (4-byte aligned) value of the sp as the first argument
   575     masm.passABIArg(r0);
   576     masm.passABIArg(r1);
   578     // Sp % 8 == 0
   579     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Bailout));
   580     masm.ma_ldr(Address(sp, 0), r2);
   581     masm.ma_add(sp, Imm32(sizeOfBailoutInfo), sp);
   582     // Common size of a bailout frame.
   583     uint32_t bailoutFrameSize = sizeof(void *) + // frameClass
   584                               sizeof(double) * FloatRegisters::Total +
   585                               sizeof(void *) * Registers::Total;
   587     if (frameClass == NO_FRAME_SIZE_CLASS_ID) {
   588         // Make sure the bailout frame size fits into the offset for a load
   589         masm.as_dtr(IsLoad, 32, Offset,
   590                     r4, DTRAddr(sp, DtrOffImm(4)));
   591         // used to be: offsetof(BailoutStack, frameSize_)
   592         // this structure is no longer available to us :(
   593         // We add 12 to the bailoutFrameSize because:
   594         // sizeof(uint32_t) for the tableOffset that was pushed onto the stack
   595         // sizeof(uintptr_t) for the snapshotOffset;
   596         // alignment to round the uintptr_t up to a multiple of 8 bytes.
   597         masm.ma_add(sp, Imm32(bailoutFrameSize+12), sp);
   598         masm.as_add(sp, sp, O2Reg(r4));
   599     } else {
   600         uint32_t frameSize = FrameSizeClass::FromClass(frameClass).frameSize();
   601         masm.ma_add(Imm32(frameSize // the frame that was added when we entered the most recent function
   602                           + sizeof(void*) // the size of the "return address" that was dumped on the stack
   603                           + bailoutFrameSize) // everything else that was pushed on the stack
   604                     , sp);
   605     }
   607     // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2.
   608     JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
   609     masm.branch(bailoutTail);
   610 }
   612 JitCode *
   613 JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
   614 {
   615     MacroAssembler masm(cx);
   617     Label bailout;
   618     for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++)
   619         masm.ma_bl(&bailout);
   620     masm.bind(&bailout);
   622     GenerateBailoutThunk(cx, masm, frameClass);
   624     Linker linker(masm);
   625     AutoFlushICache afc("BailoutTable");
   626     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   628 #ifdef JS_ION_PERF
   629     writePerfSpewerJitCodeProfile(code, "BailoutTable");
   630 #endif
   632     return code;
   633 }
   635 JitCode *
   636 JitRuntime::generateBailoutHandler(JSContext *cx)
   637 {
   638     MacroAssembler masm(cx);
   639     GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
   641     Linker linker(masm);
   642     AutoFlushICache afc("BailoutHandler");
   643     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   645 #ifdef JS_ION_PERF
   646     writePerfSpewerJitCodeProfile(code, "BailoutHandler");
   647 #endif
   649     return code;
   650 }
   652 JitCode *
   653 JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
   654 {
   655     JS_ASSERT(functionWrappers_);
   656     JS_ASSERT(functionWrappers_->initialized());
   657     VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f);
   658     if (p)
   659         return p->value();
   661     // Generate a separated code for the wrapper.
   662     MacroAssembler masm(cx);
   663     GeneralRegisterSet regs = GeneralRegisterSet(Register::Codes::WrapperMask);
   665     // Wrapper register set is a superset of Volatile register set.
   666     JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0);
   668     // The context is the first argument; r0 is the first argument register.
   669     Register cxreg = r0;
   670     regs.take(cxreg);
   672     // Stack is:
   673     //    ... frame ...
   674     //  +8  [args] + argPadding
   675     //  +0  ExitFrame
   676     //
   677     // We're aligned to an exit frame, so link it up.
   678     masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode);
   680     // Save the base of the argument set stored on the stack.
   681     Register argsBase = InvalidReg;
   682     if (f.explicitArgs) {
   683         argsBase = r5;
   684         regs.take(argsBase);
   685         masm.ma_add(sp, Imm32(IonExitFrameLayout::SizeWithFooter()), argsBase);
   686     }
   688     // Reserve space for the outparameter.
   689     Register outReg = InvalidReg;
   690     switch (f.outParam) {
   691       case Type_Value:
   692         outReg = r4;
   693         regs.take(outReg);
   694         masm.reserveStack(sizeof(Value));
   695         masm.ma_mov(sp, outReg);
   696         break;
   698       case Type_Handle:
   699         outReg = r4;
   700         regs.take(outReg);
   701         masm.PushEmptyRooted(f.outParamRootType);
   702         masm.ma_mov(sp, outReg);
   703         break;
   705       case Type_Int32:
   706       case Type_Pointer:
   707       case Type_Bool:
   708         outReg = r4;
   709         regs.take(outReg);
   710         masm.reserveStack(sizeof(int32_t));
   711         masm.ma_mov(sp, outReg);
   712         break;
   714       case Type_Double:
   715         outReg = r4;
   716         regs.take(outReg);
   717         masm.reserveStack(sizeof(double));
   718         masm.ma_mov(sp, outReg);
   719         break;
   721       default:
   722         JS_ASSERT(f.outParam == Type_Void);
   723         break;
   724     }
   726     masm.setupUnalignedABICall(f.argc(), regs.getAny());
   727     masm.passABIArg(cxreg);
   729     size_t argDisp = 0;
   731     // Copy any arguments.
   732     for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
   733         MoveOperand from;
   734         switch (f.argProperties(explicitArg)) {
   735           case VMFunction::WordByValue:
   736             masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL);
   737             argDisp += sizeof(void *);
   738             break;
   739           case VMFunction::DoubleByValue:
   740             // Values should be passed by reference, not by value, so we
   741             // assert that the argument is a double-precision float.
   742             JS_ASSERT(f.argPassedInFloatReg(explicitArg));
   743             masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::DOUBLE);
   744             argDisp += sizeof(double);
   745             break;
   746           case VMFunction::WordByRef:
   747             masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), MoveOp::GENERAL);
   748             argDisp += sizeof(void *);
   749             break;
   750           case VMFunction::DoubleByRef:
   751             masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS), MoveOp::GENERAL);
   752             argDisp += 2 * sizeof(void *);
   753             break;
   754         }
   755     }
   757     // Copy the implicit outparam, if any.
   758     if (outReg != InvalidReg)
   759         masm.passABIArg(outReg);
   761     masm.callWithABI(f.wrapped);
   763     // Test for failure.
   764     switch (f.failType()) {
   765       case Type_Object:
   766         masm.branchTestPtr(Assembler::Zero, r0, r0, masm.failureLabel(f.executionMode));
   767         break;
   768       case Type_Bool:
   769         masm.branchIfFalseBool(r0, masm.failureLabel(f.executionMode));
   770         break;
   771       default:
   772         MOZ_ASSUME_UNREACHABLE("unknown failure kind");
   773     }
   775     // Load the outparam and free any allocated stack.
   776     switch (f.outParam) {
   777       case Type_Handle:
   778         masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand);
   779         break;
   781       case Type_Value:
   782         masm.loadValue(Address(sp, 0), JSReturnOperand);
   783         masm.freeStack(sizeof(Value));
   784         break;
   786       case Type_Int32:
   787       case Type_Pointer:
   788         masm.load32(Address(sp, 0), ReturnReg);
   789         masm.freeStack(sizeof(int32_t));
   790         break;
   792       case Type_Bool:
   793         masm.load8ZeroExtend(Address(sp, 0), ReturnReg);
   794         masm.freeStack(sizeof(int32_t));
   795         break;
   797       case Type_Double:
   798         if (cx->runtime()->jitSupportsFloatingPoint)
   799             masm.loadDouble(Address(sp, 0), ReturnFloatReg);
   800         else
   801             masm.assumeUnreachable("Unable to load into float reg, with no FP support.");
   802         masm.freeStack(sizeof(double));
   803         break;
   805       default:
   806         JS_ASSERT(f.outParam == Type_Void);
   807         break;
   808     }
   809     masm.leaveExitFrame();
   810     masm.retn(Imm32(sizeof(IonExitFrameLayout) +
   811                     f.explicitStackSlots() * sizeof(void *) +
   812                     f.extraValuesToPop * sizeof(Value)));
   814     Linker linker(masm);
   815     AutoFlushICache afc("VMWrapper");
   816     JitCode *wrapper = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   817     if (!wrapper)
   818         return nullptr;
   820     // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to
   821     // use relookupOrAdd instead of add.
   822     if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
   823         return nullptr;
   825 #ifdef JS_ION_PERF
   826     writePerfSpewerJitCodeProfile(wrapper, "VMWrapper");
   827 #endif
   829     return wrapper;
   830 }
   832 JitCode *
   833 JitRuntime::generatePreBarrier(JSContext *cx, MIRType type)
   834 {
   835     MacroAssembler masm(cx);
   837     RegisterSet save;
   838     if (cx->runtime()->jitSupportsFloatingPoint) {
   839         save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
   840                            FloatRegisterSet(FloatRegisters::VolatileMask));
   841     } else {
   842         save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
   843                            FloatRegisterSet());
   844     }
   845     masm.PushRegsInMask(save);
   847     JS_ASSERT(PreBarrierReg == r1);
   848     masm.movePtr(ImmPtr(cx->runtime()), r0);
   850     masm.setupUnalignedABICall(2, r2);
   851     masm.passABIArg(r0);
   852     masm.passABIArg(r1);
   853     if (type == MIRType_Value) {
   854         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkValueFromIon));
   855     } else {
   856         JS_ASSERT(type == MIRType_Shape);
   857         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon));
   858     }
   860     masm.PopRegsInMask(save);
   861     masm.ret();
   863     Linker linker(masm);
   864     AutoFlushICache afc("PreBarrier");
   865     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   867 #ifdef JS_ION_PERF
   868     writePerfSpewerJitCodeProfile(code, "PreBarrier");
   869 #endif
   871     return code;
   872 }
   874 typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *);
   875 static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap);
   877 JitCode *
   878 JitRuntime::generateDebugTrapHandler(JSContext *cx)
   879 {
   880     MacroAssembler masm;
   882     Register scratch1 = r0;
   883     Register scratch2 = r1;
   885     // Load BaselineFrame pointer in scratch1.
   886     masm.mov(r11, scratch1);
   887     masm.subPtr(Imm32(BaselineFrame::Size()), scratch1);
   889     // Enter a stub frame and call the HandleDebugTrap VM function. Ensure
   890     // the stub frame has a nullptr ICStub pointer, since this pointer is
   891     // marked during GC.
   892     masm.movePtr(ImmPtr(nullptr), BaselineStubReg);
   893     EmitEnterStubFrame(masm, scratch2);
   895     JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo);
   896     if (!code)
   897         return nullptr;
   899     masm.push(lr);
   900     masm.push(scratch1);
   901     EmitCallVM(code, masm);
   903     EmitLeaveStubFrame(masm);
   905     // If the stub returns |true|, we have to perform a forced return
   906     // (return from the JS frame). If the stub returns |false|, just return
   907     // from the trap stub so that execution continues at the current pc.
   908     Label forcedReturn;
   909     masm.branchTest32(Assembler::NonZero, ReturnReg, ReturnReg, &forcedReturn);
   910     masm.mov(lr, pc);
   912     masm.bind(&forcedReturn);
   913     masm.loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()),
   914                    JSReturnOperand);
   915     masm.mov(r11, sp);
   916     masm.pop(r11);
   917     masm.ret();
   919     Linker linker(masm);
   920     AutoFlushICache afc("DebugTrapHandler");
   921     JitCode *codeDbg = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   923 #ifdef JS_ION_PERF
   924     writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler");
   925 #endif
   927     return codeDbg;
   928 }
   930 JitCode *
   931 JitRuntime::generateExceptionTailStub(JSContext *cx)
   932 {
   933     MacroAssembler masm;
   935     masm.handleFailureWithHandlerTail();
   937     Linker linker(masm);
   938     AutoFlushICache afc("ExceptionTailStub");
   939     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   941 #ifdef JS_ION_PERF
   942     writePerfSpewerJitCodeProfile(code, "ExceptionTailStub");
   943 #endif
   945     return code;
   946 }
   948 JitCode *
   949 JitRuntime::generateBailoutTailStub(JSContext *cx)
   950 {
   951     MacroAssembler masm;
   953     masm.generateBailoutTail(r1, r2);
   955     Linker linker(masm);
   956     AutoFlushICache afc("BailoutTailStub");
   957     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   959 #ifdef JS_ION_PERF
   960     writePerfSpewerJitCodeProfile(code, "BailoutTailStub");
   961 #endif
   963     return code;
   964 }

mercurial