Changeset 43220 in webkit for trunk/JavaScriptCore/jit/JIT.cpp


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.

File:
1 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
Note: See TracChangeset for help on using the changeset viewer.