Changeset 43220 in webkit for trunk/JavaScriptCore/jit


Ignore:
Timestamp:
May 5, 2009, 4:34:23 AM (16 years ago)
Author:
[email protected]
Message:

Bug 25559: Improve native function call performance
<https://bugs.webkit.org/show_bug.cgi?id=25559>

Reviewed by Gavin Barraclough

In order to cache calls to native functions we now make the standard
prototype functions use a small assembly thunk that converts the JS
calling convention into the native calling convention. As this is
only beneficial in the JIT we use the NativeFunctionWrapper typedef
to alternate between PrototypeFunction and JSFunction to keep the
code sane. This change from PrototypeFunction to NativeFunctionWrapper
is the bulk of this patch.

Location:
trunk/JavaScriptCore/jit
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/jit/JIT.cpp

    r43122 r43220  
    17651765}
    17661766
    1767 void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executablePool, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall)
     1767void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executablePool, JSGlobalData* globalData, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall, void** ctiNativeCallThunk)
    17681768{
    17691769#if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
     
    18141814
    18151815    // Load the callee CodeBlock* into eax
    1816     loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT0);
    1817     loadPtr(Address(regT0, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
     1816    loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT3);
     1817    loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
    18181818    Jump hasCodeBlock1 = branchTestPtr(NonZero, regT0);
     1819    // If m_code is null and m_jitCode is not, then we have a native function, so arity is irrelevant
     1820    loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_jitCode)), regT0);
     1821    Jump isNativeFunc1 = branchTestPtr(NonZero, regT0);
    18191822    pop(regT3);
    18201823    restoreArgumentReference();
     
    18371840    push(regT3);
    18381841    arityCheckOkay1.link(this);
     1842    isNativeFunc1.link(this);
    18391843   
    18401844    compileOpCallInitializeCallFrame();
     
    18441848    restoreArgumentReference();
    18451849    Call callDontLazyLinkCall = call();
     1850    emitGetJITStubArg(1, regT2);
    18461851    push(regT3);
    18471852
     
    18511856
    18521857    // Load the callee CodeBlock* into eax
    1853     loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT0);
    1854     loadPtr(Address(regT0, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
     1858    loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT3);
     1859    loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
    18551860    Jump hasCodeBlock2 = branchTestPtr(NonZero, regT0);
     1861    // If m_code is null and m_jitCode is not, then we have a native function, so arity is irrelevant
     1862    loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_jitCode)), regT0);
     1863    Jump isNativeFunc2 = branchTestPtr(NonZero, regT0);
    18561864    pop(regT3);
    18571865    restoreArgumentReference();
     
    18741882    push(regT3);
    18751883    arityCheckOkay2.link(this);
     1884    isNativeFunc2.link(this);
    18761885
    18771886    compileOpCallInitializeCallFrame();
     
    18881897
    18891898    // Load the callee CodeBlock* into eax
    1890     loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT0);
    1891     loadPtr(Address(regT0, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
     1899    loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT3);
     1900    loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
    18921901    Jump hasCodeBlock3 = branchTestPtr(NonZero, regT0);
     1902    // If m_code is null and m_jitCode is not, then we have a native function, so arity is irrelevant
     1903    loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_jitCode)), regT0);
     1904    Jump isNativeFunc3 = branchTestPtr(NonZero, regT0);
    18931905    pop(regT3);
    18941906    restoreArgumentReference();
     
    19111923    push(regT3);
    19121924    arityCheckOkay3.link(this);
    1913 
    1914     compileOpCallInitializeCallFrame();
    1915 
    19161925    // load ctiCode from the new codeBlock.
    19171926    loadPtr(Address(regT0, FIELD_OFFSET(CodeBlock, m_jitCode)), regT0);
    1918 
     1927   
     1928    isNativeFunc3.link(this);
     1929
     1930    compileOpCallInitializeCallFrame();
    19191931    jump(regT0);
     1932
     1933   
     1934    Label nativeCallThunk = align();
     1935    pop(regT0);
     1936    emitPutToCallFrameHeader(regT0, RegisterFile::ReturnPC); // Push return address
     1937
     1938    // Load caller frame's scope chain into this callframe so that whatever we call can
     1939    // get to its global data.
     1940    emitGetFromCallFrameHeader(RegisterFile::CallerFrame, regT1);
     1941    emitGetFromCallFrameHeader(RegisterFile::ScopeChain, regT1, regT1);
     1942    emitPutToCallFrameHeader(regT1, RegisterFile::ScopeChain);
     1943   
     1944
     1945#if PLATFORM(X86_64)
     1946    emitGetFromCallFrameHeader32(RegisterFile::ArgumentCount, X86::ecx);
     1947
     1948    // Allocate stack space for our arglist
     1949    subPtr(Imm32(sizeof(ArgList)), stackPointerRegister);
     1950    COMPILE_ASSERT((sizeof(ArgList) & 0xf) == 0, ArgList_should_by_16byte_aligned);
     1951   
     1952    // Set up arguments
     1953    subPtr(Imm32(1), X86::ecx); // Don't include 'this' in argcount
     1954
     1955    // Push argcount
     1956    storePtr(X86::ecx, Address(stackPointerRegister, FIELD_OFFSET(ArgList, m_argCount)));
     1957
     1958    // Calculate the start of the callframe header, and store in edx
     1959    addPtr(Imm32(-RegisterFile::CallFrameHeaderSize * (int32_t)sizeof(Register)), callFrameRegister, X86::edx);
     1960   
     1961    // Calculate start of arguments as callframe header - sizeof(Register) * argcount (ecx)
     1962    mul32(Imm32(sizeof(Register)), X86::ecx, X86::ecx);
     1963    subPtr(X86::ecx, X86::edx);
     1964
     1965    // push pointer to arguments
     1966    storePtr(X86::edx, Address(stackPointerRegister, FIELD_OFFSET(ArgList, m_args)));
     1967   
     1968    // ArgList is passed by reference so is stackPointerRegister
     1969    move(stackPointerRegister, X86::ecx);
     1970   
     1971    // edx currently points to the first argument, edx-sizeof(Register) points to 'this'
     1972    loadPtr(Address(X86::edx, -(int32_t)sizeof(Register)), X86::edx);
     1973   
     1974    emitGetFromCallFrameHeader(RegisterFile::Callee, X86::esi);
     1975
     1976    move(callFrameRegister, X86::edi);
     1977
     1978    call(Address(X86::esi, FIELD_OFFSET(JSFunction, m_data)));
     1979   
     1980    addPtr(Imm32(sizeof(ArgList)), stackPointerRegister);
     1981#else
     1982    emitGetFromCallFrameHeader(RegisterFile::ArgumentCount, regT0);
     1983
     1984    struct NativeFunctionSignature {
     1985        CallFrame* callFrame;
     1986        JSObject* callee;
     1987        JSValue thisValue;
     1988        ArgList* argPointer;
     1989        ArgList args;
     1990    };
     1991    const int NativeCallFrameSize = (sizeof(NativeFunctionSignature) + 15) & ~15;
     1992    // Allocate system stack frame
     1993    subPtr(Imm32(NativeCallFrameSize), stackPointerRegister);
     1994
     1995    // Set up arguments
     1996    subPtr(Imm32(1), regT0); // Don't include 'this' in argcount
     1997
     1998    // push argcount
     1999    storePtr(regT0, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, args) + FIELD_OFFSET(ArgList, m_argCount)));
     2000   
     2001    // Calculate the start of the callframe header, and store in regT1
     2002    addPtr(Imm32(-RegisterFile::CallFrameHeaderSize * sizeof(Register)), callFrameRegister, regT1);
     2003   
     2004    // Calculate start of arguments as callframe header - sizeof(Register) * argcount (regT0)
     2005    mul32(Imm32(sizeof(Register)), regT0, regT0);
     2006    subPtr(regT0, regT1);
     2007    storePtr(regT1, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, args) + FIELD_OFFSET(ArgList, m_args)));
     2008
     2009    // ArgList is passed by reference so is stackPointerRegister + 4 * sizeof(Register)
     2010    addPtr(Imm32(FIELD_OFFSET(NativeFunctionSignature, args)), stackPointerRegister, regT0);
     2011    storePtr(regT0, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, argPointer)));
     2012
     2013    // regT1 currently points to the first argument, regT1 - sizeof(Register) points to 'this'
     2014    loadPtr(Address(regT1, -sizeof(Register)), regT1);
     2015    poke(regT1, 2);
     2016    storePtr(regT1, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, thisValue)));
     2017
     2018    // Plant callee
     2019    emitGetFromCallFrameHeader(RegisterFile::Callee, regT2);
     2020    storePtr(regT2, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, callee)));
     2021
     2022    // Plant callframe
     2023    storePtr(callFrameRegister, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, callFrame)));
     2024
     2025    call(Address(regT2, FIELD_OFFSET(JSFunction, m_data)));
     2026    addPtr(Imm32(NativeCallFrameSize), stackPointerRegister);
     2027#endif
     2028
     2029    // Check for an exception
     2030    loadPtr(&(globalData->exception), regT2);
     2031    Jump exceptionHandler = branchTestPtr(NonZero, regT2);
     2032
     2033    // Grab the return address.
     2034    emitGetFromCallFrameHeader(RegisterFile::ReturnPC, regT1);
     2035   
     2036    // Restore our caller's "r".
     2037    emitGetFromCallFrameHeader(RegisterFile::CallerFrame, callFrameRegister);
     2038   
     2039    // Return.
     2040    push(regT1);
     2041    ret();
     2042
     2043    // Handle an exception
     2044    exceptionHandler.link(this);
     2045    // Grab the return address.
     2046    emitGetFromCallFrameHeader(RegisterFile::ReturnPC, regT1);
     2047    move(ImmPtr(&globalData->exceptionLocation), regT2);
     2048    storePtr(regT1, regT2);
     2049    move(ImmPtr(reinterpret_cast<void*>(ctiVMThrowTrampoline)), regT2);
     2050    emitGetFromCallFrameHeader(RegisterFile::CallerFrame, callFrameRegister);
     2051    emitPutCTIParam(callFrameRegister, STUB_ARGS_callFrame);
     2052    push(regT2);
     2053    ret();
     2054   
    19202055
    19212056#if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
     
    19592094    *ctiVirtualCallLink = patchBuffer.trampolineAt(virtualCallLinkBegin);
    19602095    *ctiVirtualCall = patchBuffer.trampolineAt(virtualCallBegin);
     2096    *ctiNativeCallThunk = patchBuffer.trampolineAt(nativeCallThunk);
    19612097}
    19622098
  • trunk/JavaScriptCore/jit/JIT.h

    r43122 r43220  
    363363        }
    364364
    365         static void compileCTIMachineTrampolines(JSGlobalData* globalData, RefPtr<ExecutablePool>* executablePool, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall)
     365        static void compileCTIMachineTrampolines(JSGlobalData* globalData, RefPtr<ExecutablePool>* executablePool, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall, void** ctiNativeCallThunk)
    366366
    367367        {
    368368            JIT jit(globalData);
    369             jit.privateCompileCTIMachineTrampolines(executablePool, ctiArrayLengthTrampoline, ctiStringLengthTrampoline, ctiVirtualCallPreLink, ctiVirtualCallLink, ctiVirtualCall);
     369            jit.privateCompileCTIMachineTrampolines(executablePool, globalData, ctiArrayLengthTrampoline, ctiStringLengthTrampoline, ctiVirtualCallPreLink, ctiVirtualCallLink, ctiVirtualCall, ctiNativeCallThunk);
    370370        }
    371371
     
    400400        void privateCompilePutByIdTransition(StructureStubInfo*, Structure*, Structure*, size_t cachedOffset, StructureChain*, ProcessorReturnAddress returnAddress);
    401401
    402         void privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executablePool, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall);
     402        void privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executablePool, JSGlobalData* data, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall, void** ctiNativeCallThunk);
    403403        void privateCompilePatchGetArrayLength(ProcessorReturnAddress returnAddress);
    404404
     
    468468        void emitPutToCallFrameHeader(RegisterID from, RegisterFile::CallFrameHeaderEntry entry);
    469469        void emitPutImmediateToCallFrameHeader(void* value, RegisterFile::CallFrameHeaderEntry entry);
    470         void emitGetFromCallFrameHeader(RegisterFile::CallFrameHeaderEntry entry, RegisterID to);
     470        void emitGetFromCallFrameHeader(RegisterFile::CallFrameHeaderEntry entry, RegisterID to, RegisterID from = callFrameRegister);
     471        void emitGetFromCallFrameHeader32(RegisterFile::CallFrameHeaderEntry entry, RegisterID to);
    471472
    472473        JSValue getConstantOperand(unsigned src);
  • trunk/JavaScriptCore/jit/JITCall.cpp

    r43153 r43220  
    5757{
    5858    // Currently we only link calls with the exact number of arguments.
    59     if (callerArgCount == calleeCodeBlock->m_numParameters) {
     59    // If this is a native call calleeCodeBlock is null so the number of parameters is unimportant
     60    if (!calleeCodeBlock || callerArgCount == calleeCodeBlock->m_numParameters) {
    6061        ASSERT(!callLinkInfo->isLinked());
    6162   
    62         calleeCodeBlock->addCaller(callLinkInfo);
     63        if (calleeCodeBlock)
     64            calleeCodeBlock->addCaller(callLinkInfo);
    6365   
    6466        callLinkInfo->hotPathBegin.repatch(callee);
     
    7476    store32(regT1, Address(callFrameRegister, RegisterFile::ArgumentCount * static_cast<int>(sizeof(Register))));
    7577
    76     loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_scopeChain) + FIELD_OFFSET(ScopeChain, m_node)), regT1); // newScopeChain
     78    loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_data) + FIELD_OFFSET(ScopeChain, m_node)), regT1); // newScopeChain
    7779
    7880    storePtr(ImmPtr(JSValue::encode(JSValue())), Address(callFrameRegister, RegisterFile::OptionalCalleeArguments * static_cast<int>(sizeof(Register))));
     
    243245    storePtr(ImmPtr(JSValue::encode(JSValue())), Address(callFrameRegister, (registerOffset + RegisterFile::OptionalCalleeArguments) * static_cast<int>(sizeof(Register))));
    244246    storePtr(regT2, Address(callFrameRegister, (registerOffset + RegisterFile::Callee) * static_cast<int>(sizeof(Register))));
    245     loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_scopeChain) + FIELD_OFFSET(ScopeChain, m_node)), regT1); // newScopeChain
     247    loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_data) + FIELD_OFFSET(ScopeChain, m_node)), regT1); // newScopeChain
    246248    store32(Imm32(argCount), Address(callFrameRegister, (registerOffset + RegisterFile::ArgumentCount) * static_cast<int>(sizeof(Register))));
    247249    storePtr(callFrameRegister, Address(callFrameRegister, (registerOffset + RegisterFile::CallerFrame) * static_cast<int>(sizeof(Register))));
  • trunk/JavaScriptCore/jit/JITCode.h

    r43122 r43220  
    5757        }
    5858
    59         operator bool()
     59        operator bool() const
    6060        {
    6161            return code != 0;
  • trunk/JavaScriptCore/jit/JITInlineMethods.h

    r43122 r43220  
    169169}
    170170
    171 ALWAYS_INLINE void JIT::emitGetFromCallFrameHeader(RegisterFile::CallFrameHeaderEntry entry, RegisterID to)
    172 {
    173     loadPtr(Address(callFrameRegister, entry * sizeof(Register)), to);
     171ALWAYS_INLINE void JIT::emitGetFromCallFrameHeader(RegisterFile::CallFrameHeaderEntry entry, RegisterID to, RegisterID from)
     172{
     173    loadPtr(Address(from, entry * sizeof(Register)), to);
     174    killLastResultRegister();
     175}
     176
     177ALWAYS_INLINE void JIT::emitGetFromCallFrameHeader32(RegisterFile::CallFrameHeaderEntry entry, RegisterID to)
     178{
     179    load32(Address(callFrameRegister, entry * sizeof(Register)), to);
    174180    killLastResultRegister();
    175181}
  • trunk/JavaScriptCore/jit/JITStubs.cpp

    r43153 r43220  
    7575    , m_ctiVirtualCallLink(0)
    7676    , m_ctiVirtualCall(0)
    77 {
    78     JIT::compileCTIMachineTrampolines(globalData, &m_executablePool, &m_ctiArrayLengthTrampoline, &m_ctiStringLengthTrampoline, &m_ctiVirtualCallPreLink, &m_ctiVirtualCallLink, &m_ctiVirtualCall);
     77    , m_ctiNativeCallThunk(0)
     78{
     79    JIT::compileCTIMachineTrampolines(globalData, &m_executablePool, &m_ctiArrayLengthTrampoline, &m_ctiStringLengthTrampoline, &m_ctiVirtualCallPreLink, &m_ctiVirtualCallLink, &m_ctiVirtualCall, &m_ctiNativeCallThunk);
    7980}
    8081
     
    820821#endif
    821822
    822     ScopeChainNode* callDataScopeChain = asFunction(ARG_src1)->scope().node();
    823     CodeBlock* newCodeBlock = &asFunction(ARG_src1)->body()->bytecode(callDataScopeChain);
    824 
    825     if (!newCodeBlock->jitCode())
    826         JIT::compile(ARG_globalData, newCodeBlock);
    827 
    828     return newCodeBlock;
     823    JSFunction* function = asFunction(ARG_src1);
     824    FunctionBodyNode* body = function->body();
     825    ScopeChainNode* callDataScopeChain = function->scope().node();
     826    body->jitCode(callDataScopeChain);
     827
     828    return &(body->generatedBytecode());
    829829}
    830830
     
    880880    JSGlobalData* globalData = ARG_globalData;
    881881    JSFunction* callee = asFunction(ARG_src1);
    882     CodeBlock* codeBlock = &callee->body()->bytecode(callee->scope().node());
    883     if (!codeBlock->jitCode())
    884         JIT::compile(globalData, codeBlock);
     882    JITCode jitCode = callee->body()->generatedJITCode();
     883    ASSERT(jitCode);
    885884
    886885    ctiPatchNearCallByReturnAddress(ARG_returnAddress2, globalData->jitStubs.ctiVirtualCallLink());
    887886
    888     return codeBlock->jitCode().addressForCall();
     887    return jitCode.addressForCall();
    889888}
    890889
     
    894893
    895894    JSFunction* callee = asFunction(ARG_src1);
    896     CodeBlock* codeBlock = &callee->body()->bytecode(callee->scope().node());
    897     if (!codeBlock->jitCode())
    898         JIT::compile(ARG_globalData, codeBlock);
     895    JITCode jitCode = callee->body()->generatedJITCode();
     896    ASSERT(jitCode);
     897   
     898    CodeBlock* codeBlock = 0;
     899    if (!callee->isHostFunction())
     900        codeBlock = &callee->body()->bytecode(callee->scope().node());
    899901
    900902    CallLinkInfo* callLinkInfo = &ARG_callFrame->callerFrame()->codeBlock()->getCallLinkInfo(ARG_returnAddress2);
    901     JIT::linkCall(callee, codeBlock, codeBlock->jitCode(), callLinkInfo, ARG_int3);
    902 
    903     return codeBlock->jitCode().addressForCall();
     903    JIT::linkCall(callee, codeBlock, jitCode, callLinkInfo, ARG_int3);
     904
     905    return jitCode.addressForCall();
    904906}
    905907
     
    10601062    BEGIN_STUB_FUNCTION();
    10611063
     1064    JSFunction* constructor = asFunction(ARG_src1);
     1065    if (constructor->isHostFunction()) {
     1066        CallFrame* callFrame = ARG_callFrame;
     1067        CodeBlock* codeBlock = callFrame->codeBlock();
     1068        unsigned vPCIndex = codeBlock->getBytecodeIndex(callFrame, STUB_RETURN_ADDRESS);
     1069        ARG_globalData->exception = createNotAConstructorError(callFrame, constructor, vPCIndex, codeBlock);
     1070        VM_THROW_EXCEPTION();
     1071    }
     1072
    10621073#ifndef NDEBUG
    10631074    ConstructData constructData;
    1064     ASSERT(asFunction(ARG_src1)->getConstructData(constructData) == ConstructTypeJS);
     1075    ASSERT(constructor->getConstructData(constructData) == ConstructTypeJS);
    10651076#endif
    10661077
     
    10691080        structure = asObject(ARG_src4)->inheritorID();
    10701081    else
    1071         structure = asFunction(ARG_src1)->scope().node()->globalObject()->emptyObjectStructure();
     1082        structure = constructor->scope().node()->globalObject()->emptyObjectStructure();
    10721083    return new (ARG_globalData) JSObject(structure);
    10731084}
  • trunk/JavaScriptCore/jit/JITStubs.h

    r43122 r43220  
    211211        void* ctiVirtualCallLink() { return m_ctiVirtualCallLink; }
    212212        void* ctiVirtualCall() { return m_ctiVirtualCall; }
     213        void* ctiNativeCallThunk() { return m_ctiNativeCallThunk; }
    213214
    214215    private:
     
    220221        void* m_ctiVirtualCallLink;
    221222        void* m_ctiVirtualCall;
     223        void* m_ctiNativeCallThunk;
    222224    };
    223225
Note: See TracChangeset for help on using the changeset viewer.