js/src/jit/x86/Trampoline-x86.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/Bailouts.h"
    11 #include "jit/BaselineJIT.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"
    20 #include "jit/x86/BaselineHelpers-x86.h"
    22 #include "jsscriptinlines.h"
    24 #include "jit/ExecutionMode-inl.h"
    26 using namespace js;
    27 using namespace js::jit;
    29 // All registers to save and restore. This includes the stack pointer, since we
    30 // use the ability to reference register values on the stack by index.
    31 static const RegisterSet AllRegs =
    32   RegisterSet(GeneralRegisterSet(Registers::AllMask),
    33               FloatRegisterSet(FloatRegisters::AllMask));
    35 enum EnterJitEbpArgumentOffset {
    36     ARG_JITCODE         = 2 * sizeof(void *),
    37     ARG_ARGC            = 3 * sizeof(void *),
    38     ARG_ARGV            = 4 * sizeof(void *),
    39     ARG_STACKFRAME      = 5 * sizeof(void *),
    40     ARG_CALLEETOKEN     = 6 * sizeof(void *),
    41     ARG_SCOPECHAIN      = 7 * sizeof(void *),
    42     ARG_STACKVALUES     = 8 * sizeof(void *),
    43     ARG_RESULT          = 9 * sizeof(void *)
    44 };
    46 /*
    47  * Generates a trampoline for a C++ function with the EnterJitCode signature,
    48  * using the standard cdecl calling convention.
    49  */
    50 JitCode *
    51 JitRuntime::generateEnterJIT(JSContext *cx, EnterJitType type)
    52 {
    53     MacroAssembler masm(cx);
    55     // Save old stack frame pointer, set new stack frame pointer.
    56     masm.push(ebp);
    57     masm.movl(esp, ebp);
    59     // Save non-volatile registers. These must be saved by the trampoline,
    60     // rather than the JIT'd code, because they are scanned by the conservative
    61     // scanner.
    62     masm.push(ebx);
    63     masm.push(esi);
    64     masm.push(edi);
    66     // Push the EnterJIT sps mark.
    67     masm.spsMarkJit(&cx->runtime()->spsProfiler, ebp, ebx);
    69     // Keep track of the stack which has to be unwound after returning from the
    70     // compiled function.
    71     masm.movl(esp, esi);
    73     // eax <- 8*argc, eax is now the offset betwen argv and the last
    74     masm.loadPtr(Address(ebp, ARG_ARGC), eax);
    75     masm.shll(Imm32(3), eax);
    77     // We need to ensure that the stack is aligned on a 12-byte boundary, so
    78     // inside the JIT function the stack is 16-byte aligned. Our stack right
    79     // now might not be aligned on some platforms (win32, gcc) so we factor
    80     // this possibility in, and simulate what the new stack address would be.
    81     //   +argc * 8 for arguments
    82     //   +4 for pushing alignment
    83     //   +4 for pushing the callee token
    84     //   +4 for pushing the return address
    85     masm.movl(esp, ecx);
    86     masm.subl(eax, ecx);
    87     masm.subl(Imm32(4 * 3), ecx);
    89     // ecx = ecx & 15, holds alignment.
    90     masm.andl(Imm32(15), ecx);
    91     masm.subl(ecx, esp);
    93     /***************************************************************
    94     Loop over argv vector, push arguments onto stack in reverse order
    95     ***************************************************************/
    97     // ebx = argv   --argv pointer is in ebp + 16
    98     masm.loadPtr(Address(ebp, ARG_ARGV), ebx);
   100     // eax = argv[8(argc)]  --eax now points one value past the last argument
   101     masm.addl(ebx, eax);
   103     // while (eax > ebx)  --while still looping through arguments
   104     {
   105         Label header, footer;
   106         masm.bind(&header);
   108         masm.cmpl(eax, ebx);
   109         masm.j(Assembler::BelowOrEqual, &footer);
   111         // eax -= 8  --move to previous argument
   112         masm.subl(Imm32(8), eax);
   114         // Push what eax points to on stack, a Value is 2 words
   115         masm.push(Operand(eax, 4));
   116         masm.push(Operand(eax, 0));
   118         masm.jmp(&header);
   119         masm.bind(&footer);
   120     }
   123     // Push the number of actual arguments.  |result| is used to store the
   124     // actual number of arguments without adding an extra argument to the enter
   125     // JIT.
   126     masm.mov(Operand(ebp, ARG_RESULT), eax);
   127     masm.unboxInt32(Address(eax, 0x0), eax);
   128     masm.push(eax);
   130     // Push the callee token.
   131     masm.push(Operand(ebp, ARG_CALLEETOKEN));
   133     // Load the InterpreterFrame address into the OsrFrameReg.
   134     // This address is also used for setting the constructing bit on all paths.
   135     masm.loadPtr(Address(ebp, ARG_STACKFRAME), OsrFrameReg);
   137     /*****************************************************************
   138     Push the number of bytes we've pushed so far on the stack and call
   139     *****************************************************************/
   140     // Create a frame descriptor.
   141     masm.subl(esp, esi);
   142     masm.makeFrameDescriptor(esi, JitFrame_Entry);
   143     masm.push(esi);
   145     CodeLabel returnLabel;
   146     if (type == EnterJitBaseline) {
   147         // Handle OSR.
   148         GeneralRegisterSet regs(GeneralRegisterSet::All());
   149         regs.take(JSReturnOperand);
   150         regs.takeUnchecked(OsrFrameReg);
   151         regs.take(ebp);
   152         regs.take(ReturnReg);
   154         Register scratch = regs.takeAny();
   156         Label notOsr;
   157         masm.branchTestPtr(Assembler::Zero, OsrFrameReg, OsrFrameReg, &notOsr);
   159         Register numStackValues = regs.takeAny();
   160         masm.loadPtr(Address(ebp, ARG_STACKVALUES), numStackValues);
   162         Register jitcode = regs.takeAny();
   163         masm.loadPtr(Address(ebp, ARG_JITCODE), jitcode);
   165         // Push return address, previous frame pointer.
   166         masm.mov(returnLabel.dest(), scratch);
   167         masm.push(scratch);
   168         masm.push(ebp);
   170         // Reserve frame.
   171         Register framePtr = ebp;
   172         masm.subPtr(Imm32(BaselineFrame::Size()), esp);
   173         masm.mov(esp, framePtr);
   175 #ifdef XP_WIN
   176         // Can't push large frames blindly on windows.  Touch frame memory incrementally.
   177         masm.mov(numStackValues, scratch);
   178         masm.shll(Imm32(3), scratch);
   179         masm.subPtr(scratch, framePtr);
   180         {
   181             masm.movePtr(esp, scratch);
   182             masm.subPtr(Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch);
   184             Label touchFrameLoop;
   185             Label touchFrameLoopEnd;
   186             masm.bind(&touchFrameLoop);
   187             masm.branchPtr(Assembler::Below, scratch, framePtr, &touchFrameLoopEnd);
   188             masm.store32(Imm32(0), Address(scratch, 0));
   189             masm.subPtr(Imm32(WINDOWS_BIG_FRAME_TOUCH_INCREMENT), scratch);
   190             masm.jump(&touchFrameLoop);
   191             masm.bind(&touchFrameLoopEnd);
   192         }
   193         masm.mov(esp, framePtr);
   194 #endif
   196         // Reserve space for locals and stack values.
   197         masm.mov(numStackValues, scratch);
   198         masm.shll(Imm32(3), scratch);
   199         masm.subPtr(scratch, esp);
   201         // Enter exit frame.
   202         masm.addPtr(Imm32(BaselineFrame::Size() + BaselineFrame::FramePointerOffset), scratch);
   203         masm.makeFrameDescriptor(scratch, JitFrame_BaselineJS);
   204         masm.push(scratch);
   205         masm.push(Imm32(0)); // Fake return address.
   206         masm.enterFakeExitFrame();
   208         masm.push(framePtr);
   209         masm.push(jitcode);
   211         masm.setupUnalignedABICall(3, scratch);
   212         masm.passABIArg(framePtr); // BaselineFrame
   213         masm.passABIArg(OsrFrameReg); // InterpreterFrame
   214         masm.passABIArg(numStackValues);
   215         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, jit::InitBaselineFrameForOsr));
   217         masm.pop(jitcode);
   218         masm.pop(framePtr);
   220         JS_ASSERT(jitcode != ReturnReg);
   222         Label error;
   223         masm.addPtr(Imm32(IonExitFrameLayout::SizeWithFooter()), esp);
   224         masm.addPtr(Imm32(BaselineFrame::Size()), framePtr);
   225         masm.branchIfFalseBool(ReturnReg, &error);
   227         masm.jump(jitcode);
   229         // OOM: load error value, discard return address and previous frame
   230         // pointer and return.
   231         masm.bind(&error);
   232         masm.mov(framePtr, esp);
   233         masm.addPtr(Imm32(2 * sizeof(uintptr_t)), esp);
   234         masm.moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand);
   235         masm.mov(returnLabel.dest(), scratch);
   236         masm.jump(scratch);
   238         masm.bind(&notOsr);
   239         masm.loadPtr(Address(ebp, ARG_SCOPECHAIN), R1.scratchReg());
   240     }
   242     /***************************************************************
   243         Call passed-in code, get return value and fill in the
   244         passed in return value pointer
   245     ***************************************************************/
   246     masm.call(Operand(ebp, ARG_JITCODE));
   248     if (type == EnterJitBaseline) {
   249         // Baseline OSR will return here.
   250         masm.bind(returnLabel.src());
   251         if (!masm.addCodeLabel(returnLabel))
   252             return nullptr;
   253     }
   255     // Pop arguments off the stack.
   256     // eax <- 8*argc (size of all arguments we pushed on the stack)
   257     masm.pop(eax);
   258     masm.shrl(Imm32(FRAMESIZE_SHIFT), eax); // Unmark EntryFrame.
   259     masm.addl(eax, esp);
   261     // |ebp| could have been clobbered by the inner function.
   262     // Grab the address for the Value result from the argument stack.
   263     //  +24 ... arguments ...
   264     //  +20 <return>
   265     //  +16 ebp <- original %ebp pointing here.
   266     //  +12 ebx
   267     //  +8  esi
   268     //  +4  edi
   269     //  +0  hasSPSFrame
   270     masm.loadPtr(Address(esp, ARG_RESULT + 4 * sizeof(void *)), eax);
   271     masm.storeValue(JSReturnOperand, Operand(eax, 0));
   273     /**************************************************************
   274         Return stack and registers to correct state
   275     **************************************************************/
   276     // Unwind the sps mark.
   277     masm.spsUnmarkJit(&cx->runtime()->spsProfiler, ebx);
   279     // Restore non-volatile registers
   280     masm.pop(edi);
   281     masm.pop(esi);
   282     masm.pop(ebx);
   284     // Restore old stack frame pointer
   285     masm.pop(ebp);
   286     masm.ret();
   288     Linker linker(masm);
   289     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   291 #ifdef JS_ION_PERF
   292     writePerfSpewerJitCodeProfile(code, "EnterJIT");
   293 #endif
   295     return code;
   296 }
   298 JitCode *
   299 JitRuntime::generateInvalidator(JSContext *cx)
   300 {
   301     AutoIonContextAlloc aica(cx);
   302     MacroAssembler masm(cx);
   304     // We do the minimum amount of work in assembly and shunt the rest
   305     // off to InvalidationBailout. Assembly does:
   306     //
   307     // - Pop the return address from the invalidation epilogue call.
   308     // - Push the machine state onto the stack.
   309     // - Call the InvalidationBailout routine with the stack pointer.
   310     // - Now that the frame has been bailed out, convert the invalidated
   311     //   frame into an exit frame.
   312     // - Do the normal check-return-code-and-thunk-to-the-interpreter dance.
   314     masm.addl(Imm32(sizeof(uintptr_t)), esp);
   316     // Push registers such that we can access them from [base + code].
   317     masm.PushRegsInMask(AllRegs);
   319     masm.movl(esp, eax); // Argument to jit::InvalidationBailout.
   321     // Make space for InvalidationBailout's frameSize outparam.
   322     masm.reserveStack(sizeof(size_t));
   323     masm.movl(esp, ebx);
   325     // Make space for InvalidationBailout's bailoutInfo outparam.
   326     masm.reserveStack(sizeof(void *));
   327     masm.movl(esp, ecx);
   329     masm.setupUnalignedABICall(3, edx);
   330     masm.passABIArg(eax);
   331     masm.passABIArg(ebx);
   332     masm.passABIArg(ecx);
   333     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, InvalidationBailout));
   335     masm.pop(ecx); // Get bailoutInfo outparam.
   336     masm.pop(ebx); // Get the frameSize outparam.
   338     // Pop the machine state and the dead frame.
   339     masm.lea(Operand(esp, ebx, TimesOne, sizeof(InvalidationBailoutStack)), esp);
   341     // Jump to shared bailout tail. The BailoutInfo pointer has to be in ecx.
   342     JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
   343     masm.jmp(bailoutTail);
   345     Linker linker(masm);
   346     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   347     IonSpew(IonSpew_Invalidate, "   invalidation thunk created at %p", (void *) code->raw());
   349 #ifdef JS_ION_PERF
   350     writePerfSpewerJitCodeProfile(code, "Invalidator");
   351 #endif
   353     return code;
   354 }
   356 JitCode *
   357 JitRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut)
   358 {
   359     MacroAssembler masm(cx);
   361     // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame.
   362     // Including |this|, there are (|nargs| + 1) arguments to copy.
   363     JS_ASSERT(ArgumentsRectifierReg == esi);
   365     // Load the number of |undefined|s to push into %ecx.
   366     masm.loadPtr(Address(esp, IonRectifierFrameLayout::offsetOfCalleeToken()), eax);
   367     masm.movzwl(Operand(eax, JSFunction::offsetOfNargs()), ecx);
   368     masm.subl(esi, ecx);
   370     // Copy the number of actual arguments.
   371     masm.loadPtr(Address(esp, IonRectifierFrameLayout::offsetOfNumActualArgs()), edx);
   373     masm.moveValue(UndefinedValue(), ebx, edi);
   375     // NOTE: The fact that x86 ArgumentsRectifier saves the FramePointer is relied upon
   376     // by the baseline bailout code.  If this changes, fix that code!  See
   377     // BaselineJIT.cpp/BaselineStackBuilder::calculatePrevFramePtr, and
   378     // BaselineJIT.cpp/InitFromBailout.  Check for the |#if defined(JS_CODEGEN_X86)| portions.
   379     masm.push(FramePointer);
   380     masm.movl(esp, FramePointer); // Save %esp.
   382     // Push undefined.
   383     {
   384         Label undefLoopTop;
   385         masm.bind(&undefLoopTop);
   387         masm.push(ebx); // type(undefined);
   388         masm.push(edi); // payload(undefined);
   389         masm.subl(Imm32(1), ecx);
   390         masm.j(Assembler::NonZero, &undefLoopTop);
   391     }
   393     // Get the topmost argument. We did a push of %ebp earlier, so be sure to
   394     // account for this in the offset
   395     BaseIndex b = BaseIndex(FramePointer, esi, TimesEight,
   396                             sizeof(IonRectifierFrameLayout) + sizeof(void*));
   397     masm.lea(Operand(b), ecx);
   399     // Push arguments, |nargs| + 1 times (to include |this|).
   400     masm.addl(Imm32(1), esi);
   401     {
   402         Label copyLoopTop;
   404         masm.bind(&copyLoopTop);
   405         masm.push(Operand(ecx, sizeof(Value)/2));
   406         masm.push(Operand(ecx, 0x0));
   407         masm.subl(Imm32(sizeof(Value)), ecx);
   408         masm.subl(Imm32(1), esi);
   409         masm.j(Assembler::NonZero, &copyLoopTop);
   410     }
   412     // Construct descriptor, accounting for pushed frame pointer above
   413     masm.lea(Operand(FramePointer, sizeof(void*)), ebx);
   414     masm.subl(esp, ebx);
   415     masm.makeFrameDescriptor(ebx, JitFrame_Rectifier);
   417     // Construct IonJSFrameLayout.
   418     masm.push(edx); // number of actual arguments
   419     masm.push(eax); // callee token
   420     masm.push(ebx); // descriptor
   422     // Call the target function.
   423     // Note that this assumes the function is JITted.
   424     masm.loadPtr(Address(eax, JSFunction::offsetOfNativeOrScript()), eax);
   425     masm.loadBaselineOrIonRaw(eax, eax, mode, nullptr);
   426     masm.call(eax);
   427     uint32_t returnOffset = masm.currentOffset();
   429     // Remove the rectifier frame.
   430     masm.pop(ebx);            // ebx <- descriptor with FrameType.
   431     masm.shrl(Imm32(FRAMESIZE_SHIFT), ebx); // ebx <- descriptor.
   432     masm.pop(edi);            // Discard calleeToken.
   433     masm.pop(edi);            // Discard number of actual arguments.
   435     // Discard pushed arguments, but not the pushed frame pointer.
   436     BaseIndex unwind = BaseIndex(esp, ebx, TimesOne, -int32_t(sizeof(void*)));
   437     masm.lea(Operand(unwind), esp);
   439     masm.pop(FramePointer);
   440     masm.ret();
   442     Linker linker(masm);
   443     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   445 #ifdef JS_ION_PERF
   446     writePerfSpewerJitCodeProfile(code, "ArgumentsRectifier");
   447 #endif
   449     CodeOffsetLabel returnLabel(returnOffset);
   450     returnLabel.fixup(&masm);
   451     if (returnAddrOut)
   452         *returnAddrOut = (void *) (code->raw() + returnLabel.offset());
   453     return code;
   454 }
   456 static void
   457 GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
   458 {
   459     // Push registers such that we can access them from [base + code].
   460     masm.PushRegsInMask(AllRegs);
   462     // Push the bailout table number.
   463     masm.push(Imm32(frameClass));
   465     // The current stack pointer is the first argument to jit::Bailout.
   466     masm.movl(esp, eax);
   468     // Make space for Bailout's baioutInfo outparam.
   469     masm.reserveStack(sizeof(void *));
   470     masm.movl(esp, ebx);
   472     // Call the bailout function. This will correct the size of the bailout.
   473     masm.setupUnalignedABICall(2, ecx);
   474     masm.passABIArg(eax);
   475     masm.passABIArg(ebx);
   476     masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, Bailout));
   478     masm.pop(ecx); // Get bailoutInfo outparam.
   480     // Common size of stuff we've pushed.
   481     const uint32_t BailoutDataSize = sizeof(void *) + // frameClass
   482                                    sizeof(double) * FloatRegisters::Total +
   483                                    sizeof(void *) * Registers::Total;
   485     // Remove both the bailout frame and the topmost Ion frame's stack.
   486     if (frameClass == NO_FRAME_SIZE_CLASS_ID) {
   487         // We want the frameSize. Stack is:
   488         //    ... frame ...
   489         //    snapshotOffset
   490         //    frameSize
   491         //    ... bailoutFrame ...
   492         masm.addl(Imm32(BailoutDataSize), esp);
   493         masm.pop(ebx);
   494         masm.addl(Imm32(sizeof(uint32_t)), esp);
   495         masm.addl(ebx, esp);
   496     } else {
   497         // Stack is:
   498         //    ... frame ...
   499         //    bailoutId
   500         //    ... bailoutFrame ...
   501         uint32_t frameSize = FrameSizeClass::FromClass(frameClass).frameSize();
   502         masm.addl(Imm32(BailoutDataSize + sizeof(void *) + frameSize), esp);
   503     }
   505     // Jump to shared bailout tail. The BailoutInfo pointer has to be in ecx.
   506     JitCode *bailoutTail = cx->runtime()->jitRuntime()->getBailoutTail();
   507     masm.jmp(bailoutTail);
   508 }
   510 JitCode *
   511 JitRuntime::generateBailoutTable(JSContext *cx, uint32_t frameClass)
   512 {
   513     MacroAssembler masm;
   515     Label bailout;
   516     for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++)
   517         masm.call(&bailout);
   518     masm.bind(&bailout);
   520     GenerateBailoutThunk(cx, masm, frameClass);
   522     Linker linker(masm);
   523     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   525 #ifdef JS_ION_PERF
   526     writePerfSpewerJitCodeProfile(code, "BailoutHandler");
   527 #endif
   529     return code;
   530 }
   532 JitCode *
   533 JitRuntime::generateBailoutHandler(JSContext *cx)
   534 {
   535     MacroAssembler masm;
   537     GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
   539     Linker linker(masm);
   540     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   542 #ifdef JS_ION_PERF
   543     writePerfSpewerJitCodeProfile(code, "BailoutHandler");
   544 #endif
   546     return code;
   547 }
   549 JitCode *
   550 JitRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
   551 {
   552     JS_ASSERT(!StackKeptAligned);
   553     JS_ASSERT(functionWrappers_);
   554     JS_ASSERT(functionWrappers_->initialized());
   555     VMWrapperMap::AddPtr p = functionWrappers_->lookupForAdd(&f);
   556     if (p)
   557         return p->value();
   559     // Generate a separated code for the wrapper.
   560     MacroAssembler masm;
   562     // Avoid conflicts with argument registers while discarding the result after
   563     // the function call.
   564     GeneralRegisterSet regs = GeneralRegisterSet(Register::Codes::WrapperMask);
   566     // Wrapper register set is a superset of Volatile register set.
   567     JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0);
   569     // The context is the first argument.
   570     Register cxreg = regs.takeAny();
   572     // Stack is:
   573     //    ... frame ...
   574     //  +8  [args]
   575     //  +4  descriptor
   576     //  +0  returnAddress
   577     //
   578     // We're aligned to an exit frame, so link it up.
   579     masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode);
   581     // Save the current stack pointer as the base for copying arguments.
   582     Register argsBase = InvalidReg;
   583     if (f.explicitArgs) {
   584         argsBase = regs.takeAny();
   585         masm.lea(Operand(esp, IonExitFrameLayout::SizeWithFooter()), argsBase);
   586     }
   588     // Reserve space for the outparameter.
   589     Register outReg = InvalidReg;
   590     switch (f.outParam) {
   591       case Type_Value:
   592         outReg = regs.takeAny();
   593         masm.Push(UndefinedValue());
   594         masm.movl(esp, outReg);
   595         break;
   597       case Type_Handle:
   598         outReg = regs.takeAny();
   599         masm.PushEmptyRooted(f.outParamRootType);
   600         masm.movl(esp, outReg);
   601         break;
   603       case Type_Int32:
   604       case Type_Pointer:
   605       case Type_Bool:
   606         outReg = regs.takeAny();
   607         masm.reserveStack(sizeof(int32_t));
   608         masm.movl(esp, outReg);
   609         break;
   611       case Type_Double:
   612         outReg = regs.takeAny();
   613         masm.reserveStack(sizeof(double));
   614         masm.movl(esp, outReg);
   615         break;
   617       default:
   618         JS_ASSERT(f.outParam == Type_Void);
   619         break;
   620     }
   622     masm.setupUnalignedABICall(f.argc(), regs.getAny());
   623     masm.passABIArg(cxreg);
   625     size_t argDisp = 0;
   627     // Copy arguments.
   628     for (uint32_t explicitArg = 0; explicitArg < f.explicitArgs; explicitArg++) {
   629         MoveOperand from;
   630         switch (f.argProperties(explicitArg)) {
   631           case VMFunction::WordByValue:
   632             masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL);
   633             argDisp += sizeof(void *);
   634             break;
   635           case VMFunction::DoubleByValue:
   636             // We don't pass doubles in float registers on x86, so no need
   637             // to check for argPassedInFloatReg.
   638             masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL);
   639             argDisp += sizeof(void *);
   640             masm.passABIArg(MoveOperand(argsBase, argDisp), MoveOp::GENERAL);
   641             argDisp += sizeof(void *);
   642             break;
   643           case VMFunction::WordByRef:
   644             masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS),
   645                             MoveOp::GENERAL);
   646             argDisp += sizeof(void *);
   647             break;
   648           case VMFunction::DoubleByRef:
   649             masm.passABIArg(MoveOperand(argsBase, argDisp, MoveOperand::EFFECTIVE_ADDRESS),
   650                             MoveOp::GENERAL);
   651             argDisp += 2 * sizeof(void *);
   652             break;
   653         }
   654     }
   656     // Copy the implicit outparam, if any.
   657     if (outReg != InvalidReg)
   658         masm.passABIArg(outReg);
   660     masm.callWithABI(f.wrapped);
   662     // Test for failure.
   663     switch (f.failType()) {
   664       case Type_Object:
   665         masm.branchTestPtr(Assembler::Zero, eax, eax, masm.failureLabel(f.executionMode));
   666         break;
   667       case Type_Bool:
   668         masm.testb(eax, eax);
   669         masm.j(Assembler::Zero, masm.failureLabel(f.executionMode));
   670         break;
   671       default:
   672         MOZ_ASSUME_UNREACHABLE("unknown failure kind");
   673     }
   675     // Load the outparam and free any allocated stack.
   676     switch (f.outParam) {
   677       case Type_Handle:
   678         masm.popRooted(f.outParamRootType, ReturnReg, JSReturnOperand);
   679         break;
   681       case Type_Value:
   682         masm.Pop(JSReturnOperand);
   683         break;
   685       case Type_Int32:
   686       case Type_Pointer:
   687         masm.Pop(ReturnReg);
   688         break;
   690       case Type_Bool:
   691         masm.Pop(ReturnReg);
   692         masm.movzbl(ReturnReg, ReturnReg);
   693         break;
   695       case Type_Double:
   696         if (cx->runtime()->jitSupportsFloatingPoint)
   697             masm.Pop(ReturnFloatReg);
   698         else
   699             masm.assumeUnreachable("Unable to pop to float reg, with no FP support.");
   700         break;
   702       default:
   703         JS_ASSERT(f.outParam == Type_Void);
   704         break;
   705     }
   706     masm.leaveExitFrame();
   707     masm.retn(Imm32(sizeof(IonExitFrameLayout) +
   708                     f.explicitStackSlots() * sizeof(void *) +
   709                     f.extraValuesToPop * sizeof(Value)));
   711     Linker linker(masm);
   712     JitCode *wrapper = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   713     if (!wrapper)
   714         return nullptr;
   716 #ifdef JS_ION_PERF
   717     writePerfSpewerJitCodeProfile(wrapper, "VMWrapper");
   718 #endif
   720     // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to
   721     // use relookupOrAdd instead of add.
   722     if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
   723         return nullptr;
   725     return wrapper;
   726 }
   728 JitCode *
   729 JitRuntime::generatePreBarrier(JSContext *cx, MIRType type)
   730 {
   731     MacroAssembler masm;
   733     RegisterSet save;
   734     if (cx->runtime()->jitSupportsFloatingPoint) {
   735         save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
   736                            FloatRegisterSet(FloatRegisters::VolatileMask));
   737     } else {
   738         save = RegisterSet(GeneralRegisterSet(Registers::VolatileMask),
   739                            FloatRegisterSet());
   740     }
   741     masm.PushRegsInMask(save);
   743     JS_ASSERT(PreBarrierReg == edx);
   744     masm.movl(ImmPtr(cx->runtime()), ecx);
   746     masm.setupUnalignedABICall(2, eax);
   747     masm.passABIArg(ecx);
   748     masm.passABIArg(edx);
   750     if (type == MIRType_Value) {
   751         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkValueFromIon));
   752     } else {
   753         JS_ASSERT(type == MIRType_Shape);
   754         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon));
   755     }
   757     masm.PopRegsInMask(save);
   758     masm.ret();
   760     Linker linker(masm);
   761     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   763 #ifdef JS_ION_PERF
   764     writePerfSpewerJitCodeProfile(code, "PreBarrier");
   765 #endif
   767     return code;
   768 }
   770 typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *);
   771 static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap);
   773 JitCode *
   774 JitRuntime::generateDebugTrapHandler(JSContext *cx)
   775 {
   776     MacroAssembler masm;
   778     Register scratch1 = eax;
   779     Register scratch2 = ecx;
   780     Register scratch3 = edx;
   782     // Load the return address in scratch1.
   783     masm.loadPtr(Address(esp, 0), scratch1);
   785     // Load BaselineFrame pointer in scratch2.
   786     masm.mov(ebp, scratch2);
   787     masm.subPtr(Imm32(BaselineFrame::Size()), scratch2);
   789     // Enter a stub frame and call the HandleDebugTrap VM function. Ensure
   790     // the stub frame has a nullptr ICStub pointer, since this pointer is
   791     // marked during GC.
   792     masm.movePtr(ImmPtr(nullptr), BaselineStubReg);
   793     EmitEnterStubFrame(masm, scratch3);
   795     JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(HandleDebugTrapInfo);
   796     if (!code)
   797         return nullptr;
   799     masm.push(scratch1);
   800     masm.push(scratch2);
   801     EmitCallVM(code, masm);
   803     EmitLeaveStubFrame(masm);
   805     // If the stub returns |true|, we have to perform a forced return
   806     // (return from the JS frame). If the stub returns |false|, just return
   807     // from the trap stub so that execution continues at the current pc.
   808     Label forcedReturn;
   809     masm.branchTest32(Assembler::NonZero, ReturnReg, ReturnReg, &forcedReturn);
   810     masm.ret();
   812     masm.bind(&forcedReturn);
   813     masm.loadValue(Address(ebp, BaselineFrame::reverseOffsetOfReturnValue()),
   814                    JSReturnOperand);
   815     masm.mov(ebp, esp);
   816     masm.pop(ebp);
   817     masm.ret();
   819     Linker linker(masm);
   820     JitCode *codeDbg = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   822 #ifdef JS_ION_PERF
   823     writePerfSpewerJitCodeProfile(codeDbg, "DebugTrapHandler");
   824 #endif
   826     return codeDbg;
   827 }
   829 JitCode *
   830 JitRuntime::generateExceptionTailStub(JSContext *cx)
   831 {
   832     MacroAssembler masm;
   834     masm.handleFailureWithHandlerTail();
   836     Linker linker(masm);
   837     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   839 #ifdef JS_ION_PERF
   840     writePerfSpewerJitCodeProfile(code, "ExceptionTailStub");
   841 #endif
   843     return code;
   844 }
   846 JitCode *
   847 JitRuntime::generateBailoutTailStub(JSContext *cx)
   848 {
   849     MacroAssembler masm;
   851     masm.generateBailoutTail(edx, ecx);
   853     Linker linker(masm);
   854     JitCode *code = linker.newCode<NoGC>(cx, JSC::OTHER_CODE);
   856 #ifdef JS_ION_PERF
   857     writePerfSpewerJitCodeProfile(code, "BailoutTailStub");
   858 #endif
   860     return code;
   861 }

mercurial