Commit e79807b9 authored by oliver@apple.com's avatar oliver@apple.com

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.


git-svn-id: http://svn.webkit.org/repository/webkit/trunk@43220 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent b0999f7d
2009-05-05 Oliver Hunt <oliver@apple.com>
Reviewed by Gavin Barraclough.
Bug 25559: Improve native function call performance
<https://bugs.webkit.org/show_bug.cgi?id=25559>
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.
* JavaScriptCore.exp:
* JavaScriptCore.xcodeproj/project.pbxproj:
* assembler/MacroAssemblerX86Common.h:
(JSC::MacroAssemblerX86Common::call):
* assembler/MacroAssemblerX86_64.h:
(JSC::MacroAssemblerX86_64::addPtr):
* assembler/X86Assembler.h:
(JSC::X86Assembler::leaq_mr):
(JSC::X86Assembler::call_m):
* interpreter/Interpreter.cpp:
(JSC::Interpreter::execute):
(JSC::Interpreter::prepareForRepeatCall):
* jit/JIT.cpp:
(JSC::JIT::privateCompileCTIMachineTrampolines):
* jit/JIT.h:
(JSC::JIT::compileCTIMachineTrampolines):
* jit/JITCall.cpp:
(JSC::JIT::linkCall):
(JSC::JIT::compileOpCallInitializeCallFrame):
(JSC::JIT::compileOpCall):
* jit/JITCode.h:
(JSC::JITCode::operator bool):
* jit/JITInlineMethods.h:
(JSC::JIT::emitGetFromCallFrameHeader):
(JSC::JIT::emitGetFromCallFrameHeader32):
* jit/JITStubs.cpp:
(JSC::JITStubs::JITStubs):
(JSC::JITStubs::cti_op_call_JSFunction):
(JSC::JITStubs::cti_vm_dontLazyLinkCall):
(JSC::JITStubs::cti_vm_lazyLinkCall):
(JSC::JITStubs::cti_op_construct_JSConstruct):
* jit/JITStubs.h:
(JSC::JITStubs::ctiNativeCallThunk):
* jsc.cpp:
(GlobalObject::GlobalObject):
* parser/Nodes.cpp:
(JSC::FunctionBodyNode::FunctionBodyNode):
(JSC::FunctionBodyNode::createNativeThunk):
(JSC::FunctionBodyNode::generateJITCode):
* parser/Nodes.h:
(JSC::FunctionBodyNode::):
(JSC::FunctionBodyNode::generatedJITCode):
(JSC::FunctionBodyNode::jitCode):
* profiler/Profiler.cpp:
(JSC::Profiler::createCallIdentifier):
* runtime/ArgList.h:
* runtime/ArrayPrototype.cpp:
(JSC::isNumericCompareFunction):
* runtime/BooleanPrototype.cpp:
(JSC::BooleanPrototype::BooleanPrototype):
* runtime/DateConstructor.cpp:
(JSC::DateConstructor::DateConstructor):
* runtime/ErrorPrototype.cpp:
(JSC::ErrorPrototype::ErrorPrototype):
* runtime/FunctionPrototype.cpp:
(JSC::FunctionPrototype::addFunctionProperties):
(JSC::functionProtoFuncToString):
* runtime/FunctionPrototype.h:
* runtime/JSFunction.cpp:
(JSC::JSFunction::JSFunction):
(JSC::JSFunction::~JSFunction):
(JSC::JSFunction::mark):
(JSC::JSFunction::getCallData):
(JSC::JSFunction::call):
(JSC::JSFunction::argumentsGetter):
(JSC::JSFunction::callerGetter):
(JSC::JSFunction::lengthGetter):
(JSC::JSFunction::getOwnPropertySlot):
(JSC::JSFunction::put):
(JSC::JSFunction::deleteProperty):
(JSC::JSFunction::getConstructData):
(JSC::JSFunction::construct):
* runtime/JSFunction.h:
(JSC::JSFunction::JSFunction):
(JSC::JSFunction::setScope):
(JSC::JSFunction::scope):
(JSC::JSFunction::isHostFunction):
(JSC::JSFunction::scopeChain):
(JSC::JSFunction::clearScopeChain):
(JSC::JSFunction::setScopeChain):
(JSC::JSFunction::nativeFunction):
(JSC::JSFunction::setNativeFunction):
* runtime/JSGlobalData.cpp:
(JSC::JSGlobalData::~JSGlobalData):
(JSC::JSGlobalData::createNativeThunk):
* runtime/JSGlobalData.h:
(JSC::JSGlobalData::nativeFunctionThunk):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::reset):
* runtime/JSGlobalObject.h:
* runtime/Lookup.cpp:
(JSC::setUpStaticFunctionSlot):
* runtime/Lookup.h:
* runtime/NumberPrototype.cpp:
(JSC::NumberPrototype::NumberPrototype):
* runtime/ObjectPrototype.cpp:
(JSC::ObjectPrototype::ObjectPrototype):
* runtime/RegExpPrototype.cpp:
(JSC::RegExpPrototype::RegExpPrototype):
* runtime/StringConstructor.cpp:
(JSC::StringConstructor::StringConstructor):
2009-05-05 Gavin Barraclough <barraclough@apple.com>
Reviewed by Oliver Hunt.
......@@ -96,6 +96,7 @@ __ZN3JSC10Identifier24checkSameIdentifierTableEPNS_9ExecStateEPNS_7UString3RepE
__ZN3JSC10Identifier3addEPNS_9ExecStateEPKc
__ZN3JSC10Identifier5equalEPKNS_7UString3RepEPKc
__ZN3JSC10JSFunction4infoE
__ZN3JSC10JSFunctionC1EPNS_9ExecStateEN3WTF10PassRefPtrINS_9StructureEEEiRKNS_10IdentifierEPFNS_7JSValueES2_PNS_8JSObjectESA_RKNS_7ArgListEE
__ZN3JSC10throwErrorEPNS_9ExecStateENS_9ErrorTypeE
__ZN3JSC10throwErrorEPNS_9ExecStateENS_9ErrorTypeEPKc
__ZN3JSC10throwErrorEPNS_9ExecStateENS_9ErrorTypeERKNS_7UStringE
......
......@@ -124,7 +124,7 @@
86CC85A10EE79A4700288682 /* JITInlineMethods.h in Headers */ = {isa = PBXBuildFile; fileRef = 86CC85A00EE79A4700288682 /* JITInlineMethods.h */; };
86CC85A30EE79B7400288682 /* JITCall.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86CC85A20EE79B7400288682 /* JITCall.cpp */; };
86CC85C40EE7A89400288682 /* JITPropertyAccess.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86CC85C30EE7A89400288682 /* JITPropertyAccess.cpp */; };
86CCEFDE0F413F8900FD7F9E /* JITCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 86CCEFDD0F413F8900FD7F9E /* JITCode.h */; };
86CCEFDE0F413F8900FD7F9E /* JITCode.h in Headers */ = {isa = PBXBuildFile; fileRef = 86CCEFDD0F413F8900FD7F9E /* JITCode.h */; settings = {ATTRIBUTES = (Private, ); }; };
86DB64640F95C6FC00D7D921 /* ExecutableAllocatorFixedVMPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86DB64630F95C6FC00D7D921 /* ExecutableAllocatorFixedVMPool.cpp */; };
86EAC4950F93E8D1008EC948 /* RegexCompiler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86EAC48D0F93E8D1008EC948 /* RegexCompiler.cpp */; };
86EAC4960F93E8D1008EC948 /* RegexCompiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 86EAC48E0F93E8D1008EC948 /* RegexCompiler.h */; };
......@@ -177,6 +177,7 @@
A72701B90DADE94900E548D7 /* ExceptionHelpers.h in Headers */ = {isa = PBXBuildFile; fileRef = A72701B30DADE94900E548D7 /* ExceptionHelpers.h */; };
A727FF6B0DA3092200E548D7 /* JSPropertyNameIterator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A727FF660DA3053B00E548D7 /* JSPropertyNameIterator.cpp */; };
A766B44F0EE8DCD1009518CA /* ExecutableAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = A7B48DB50EE74CFC00DCBDB6 /* ExecutableAllocator.h */; settings = {ATTRIBUTES = (Private, ); }; };
A76EE6590FAE59D5003F069A /* NativeFunctionWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = A76EE6580FAE59D5003F069A /* NativeFunctionWrapper.h */; settings = {ATTRIBUTES = (Private, ); }; };
A782F1A50EEC9FA20036273F /* ExecutableAllocatorPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A782F1A40EEC9FA20036273F /* ExecutableAllocatorPosix.cpp */; };
A791EF280F11E07900AE1F68 /* JSByteArray.h in Headers */ = {isa = PBXBuildFile; fileRef = A791EF260F11E07900AE1F68 /* JSByteArray.h */; settings = {ATTRIBUTES = (Private, ); }; };
A791EF290F11E07900AE1F68 /* JSByteArray.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A791EF270F11E07900AE1F68 /* JSByteArray.cpp */; };
......@@ -693,6 +694,7 @@
A72701B30DADE94900E548D7 /* ExceptionHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExceptionHelpers.h; sourceTree = "<group>"; };
A727FF650DA3053B00E548D7 /* JSPropertyNameIterator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPropertyNameIterator.h; sourceTree = "<group>"; };
A727FF660DA3053B00E548D7 /* JSPropertyNameIterator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPropertyNameIterator.cpp; sourceTree = "<group>"; };
A76EE6580FAE59D5003F069A /* NativeFunctionWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeFunctionWrapper.h; sourceTree = "<group>"; };
A782F1A40EEC9FA20036273F /* ExecutableAllocatorPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExecutableAllocatorPosix.cpp; sourceTree = "<group>"; };
A791EF260F11E07900AE1F68 /* JSByteArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSByteArray.h; sourceTree = "<group>"; };
A791EF270F11E07900AE1F68 /* JSByteArray.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSByteArray.cpp; sourceTree = "<group>"; };
......@@ -1419,6 +1421,7 @@
6507D2970E871E4A00D7D896 /* TypeInfo.h */,
F692A8850255597D01FF60F7 /* UString.cpp */,
F692A8860255597D01FF60F7 /* UString.h */,
A76EE6580FAE59D5003F069A /* NativeFunctionWrapper.h */,
);
path = runtime;
sourceTree = "<group>";
......@@ -1790,6 +1793,7 @@
86EAC49B0F93E8D1008EC948 /* RegexParser.h in Headers */,
86EAC49C0F93E8D1008EC948 /* RegexPattern.h in Headers */,
96DD73790F9DA3100027FBCC /* VMTags.h in Headers */,
A76EE6590FAE59D5003F069A /* NativeFunctionWrapper.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......
......@@ -553,6 +553,11 @@ public:
return Call(m_assembler.call(target), Call::None);
}
void call(Address address)
{
m_assembler.call_m(address.offset, address.base);
}
void ret()
{
m_assembler.ret();
......
......@@ -122,7 +122,7 @@ public:
void addPtr(Imm32 imm, RegisterID src, RegisterID dest)
{
m_assembler.leal_mr(imm.m_value, src, dest);
m_assembler.leaq_mr(imm.m_value, src, dest);
}
void andPtr(RegisterID src, RegisterID dest)
......
......@@ -1008,6 +1008,12 @@ public:
{
m_formatter.oneByteOp(OP_LEA, dst, base, offset);
}
#if PLATFORM(X86_64)
void leaq_mr(int offset, RegisterID base, RegisterID dst)
{
m_formatter.oneByteOp64(OP_LEA, dst, base, offset);
}
#endif
// Flow control:
......@@ -1022,6 +1028,11 @@ public:
m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, dst);
return JmpSrc(m_formatter.size());
}
void call_m(int offset, RegisterID base)
{
m_formatter.oneByteOp(OP_GROUP5_Ev, GROUP5_OP_CALLN, base, offset);
}
JmpSrc jmp()
{
......
......@@ -690,9 +690,7 @@ JSValue Interpreter::execute(FunctionBodyNode* functionBodyNode, CallFrame* call
m_reentryDepth++;
#if ENABLE(JIT)
if (!codeBlock->jitCode())
JIT::compile(scopeChain->globalData, codeBlock);
result = codeBlock->jitCode().execute(&m_registerFile, newCallFrame, scopeChain->globalData, exception);
result = functionBodyNode->jitCode(scopeChain).execute(&m_registerFile, newCallFrame, scopeChain->globalData, exception);
#else
result = privateExecute(Normal, &m_registerFile, newCallFrame, exception);
#endif
......@@ -740,8 +738,7 @@ CallFrameClosure Interpreter::prepareForRepeatCall(FunctionBodyNode* functionBod
// a 0 codeBlock indicates a built-in caller
newCallFrame->init(codeBlock, 0, scopeChain, callFrame->addHostCallFrameFlag(), 0, argc, function);
#if ENABLE(JIT)
if (!codeBlock->jitCode())
JIT::compile(scopeChain->globalData, codeBlock);
functionBodyNode->jitCode(scopeChain);
#endif
CallFrameClosure result = { callFrame, newCallFrame, function, codeBlock, scopeChain->globalData, oldEnd, scopeChain, codeBlock->m_numParameters, argc };
......@@ -761,6 +758,7 @@ JSValue Interpreter::execute(CallFrameClosure& closure, JSValue* exception)
m_reentryDepth++;
#if ENABLE(JIT)
ASSERT(closure.codeBlock->jitCode());
result = closure.codeBlock->jitCode().execute(&m_registerFile, closure.newCallFrame, closure.globalData, exception);
#else
result = privateExecute(Normal, &m_registerFile, closure.newCallFrame, exception);
......
......@@ -1764,7 +1764,7 @@ void JIT::privateCompile()
m_codeBlock->setJITCode(codeRef);
}
void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executablePool, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall)
void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executablePool, JSGlobalData* globalData, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall, void** ctiNativeCallThunk)
{
#if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
// (1) The first function provides fast property access for array length
......@@ -1813,9 +1813,12 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable
Label virtualCallPreLinkBegin = align();
// Load the callee CodeBlock* into eax
loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT0);
loadPtr(Address(regT0, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT3);
loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
Jump hasCodeBlock1 = branchTestPtr(NonZero, regT0);
// If m_code is null and m_jitCode is not, then we have a native function, so arity is irrelevant
loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_jitCode)), regT0);
Jump isNativeFunc1 = branchTestPtr(NonZero, regT0);
pop(regT3);
restoreArgumentReference();
Call callJSFunction1 = call();
......@@ -1836,6 +1839,7 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable
emitGetJITStubArg(3, regT1);
push(regT3);
arityCheckOkay1.link(this);
isNativeFunc1.link(this);
compileOpCallInitializeCallFrame();
......@@ -1843,6 +1847,7 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable
emitPutJITStubArg(regT3, 2);
restoreArgumentReference();
Call callDontLazyLinkCall = call();
emitGetJITStubArg(1, regT2);
push(regT3);
jump(regT0);
......@@ -1850,9 +1855,12 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable
Label virtualCallLinkBegin = align();
// Load the callee CodeBlock* into eax
loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT0);
loadPtr(Address(regT0, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT3);
loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
Jump hasCodeBlock2 = branchTestPtr(NonZero, regT0);
// If m_code is null and m_jitCode is not, then we have a native function, so arity is irrelevant
loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_jitCode)), regT0);
Jump isNativeFunc2 = branchTestPtr(NonZero, regT0);
pop(regT3);
restoreArgumentReference();
Call callJSFunction2 = call();
......@@ -1873,6 +1881,7 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable
emitGetJITStubArg(3, regT1);
push(regT3);
arityCheckOkay2.link(this);
isNativeFunc2.link(this);
compileOpCallInitializeCallFrame();
......@@ -1887,9 +1896,12 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable
Label virtualCallBegin = align();
// Load the callee CodeBlock* into eax
loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT0);
loadPtr(Address(regT0, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_body)), regT3);
loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_code)), regT0);
Jump hasCodeBlock3 = branchTestPtr(NonZero, regT0);
// If m_code is null and m_jitCode is not, then we have a native function, so arity is irrelevant
loadPtr(Address(regT3, FIELD_OFFSET(FunctionBodyNode, m_jitCode)), regT0);
Jump isNativeFunc3 = branchTestPtr(NonZero, regT0);
pop(regT3);
restoreArgumentReference();
Call callJSFunction3 = call();
......@@ -1910,14 +1922,137 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable
emitGetJITStubArg(3, regT1);
push(regT3);
arityCheckOkay3.link(this);
compileOpCallInitializeCallFrame();
// load ctiCode from the new codeBlock.
loadPtr(Address(regT0, FIELD_OFFSET(CodeBlock, m_jitCode)), regT0);
isNativeFunc3.link(this);
compileOpCallInitializeCallFrame();
jump(regT0);
Label nativeCallThunk = align();
pop(regT0);
emitPutToCallFrameHeader(regT0, RegisterFile::ReturnPC); // Push return address
// Load caller frame's scope chain into this callframe so that whatever we call can
// get to its global data.
emitGetFromCallFrameHeader(RegisterFile::CallerFrame, regT1);
emitGetFromCallFrameHeader(RegisterFile::ScopeChain, regT1, regT1);
emitPutToCallFrameHeader(regT1, RegisterFile::ScopeChain);
#if PLATFORM(X86_64)
emitGetFromCallFrameHeader32(RegisterFile::ArgumentCount, X86::ecx);
// Allocate stack space for our arglist
subPtr(Imm32(sizeof(ArgList)), stackPointerRegister);
COMPILE_ASSERT((sizeof(ArgList) & 0xf) == 0, ArgList_should_by_16byte_aligned);
// Set up arguments
subPtr(Imm32(1), X86::ecx); // Don't include 'this' in argcount
// Push argcount
storePtr(X86::ecx, Address(stackPointerRegister, FIELD_OFFSET(ArgList, m_argCount)));
// Calculate the start of the callframe header, and store in edx
addPtr(Imm32(-RegisterFile::CallFrameHeaderSize * (int32_t)sizeof(Register)), callFrameRegister, X86::edx);
// Calculate start of arguments as callframe header - sizeof(Register) * argcount (ecx)
mul32(Imm32(sizeof(Register)), X86::ecx, X86::ecx);
subPtr(X86::ecx, X86::edx);
// push pointer to arguments
storePtr(X86::edx, Address(stackPointerRegister, FIELD_OFFSET(ArgList, m_args)));
// ArgList is passed by reference so is stackPointerRegister
move(stackPointerRegister, X86::ecx);
// edx currently points to the first argument, edx-sizeof(Register) points to 'this'
loadPtr(Address(X86::edx, -(int32_t)sizeof(Register)), X86::edx);
emitGetFromCallFrameHeader(RegisterFile::Callee, X86::esi);
move(callFrameRegister, X86::edi);
call(Address(X86::esi, FIELD_OFFSET(JSFunction, m_data)));
addPtr(Imm32(sizeof(ArgList)), stackPointerRegister);
#else
emitGetFromCallFrameHeader(RegisterFile::ArgumentCount, regT0);
struct NativeFunctionSignature {
CallFrame* callFrame;
JSObject* callee;
JSValue thisValue;
ArgList* argPointer;
ArgList args;
};
const int NativeCallFrameSize = (sizeof(NativeFunctionSignature) + 15) & ~15;
// Allocate system stack frame
subPtr(Imm32(NativeCallFrameSize), stackPointerRegister);
// Set up arguments
subPtr(Imm32(1), regT0); // Don't include 'this' in argcount
// push argcount
storePtr(regT0, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, args) + FIELD_OFFSET(ArgList, m_argCount)));
// Calculate the start of the callframe header, and store in regT1
addPtr(Imm32(-RegisterFile::CallFrameHeaderSize * sizeof(Register)), callFrameRegister, regT1);
// Calculate start of arguments as callframe header - sizeof(Register) * argcount (regT0)
mul32(Imm32(sizeof(Register)), regT0, regT0);
subPtr(regT0, regT1);
storePtr(regT1, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, args) + FIELD_OFFSET(ArgList, m_args)));
// ArgList is passed by reference so is stackPointerRegister + 4 * sizeof(Register)
addPtr(Imm32(FIELD_OFFSET(NativeFunctionSignature, args)), stackPointerRegister, regT0);
storePtr(regT0, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, argPointer)));
// regT1 currently points to the first argument, regT1 - sizeof(Register) points to 'this'
loadPtr(Address(regT1, -sizeof(Register)), regT1);
poke(regT1, 2);
storePtr(regT1, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, thisValue)));
// Plant callee
emitGetFromCallFrameHeader(RegisterFile::Callee, regT2);
storePtr(regT2, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, callee)));
// Plant callframe
storePtr(callFrameRegister, Address(stackPointerRegister, FIELD_OFFSET(NativeFunctionSignature, callFrame)));
call(Address(regT2, FIELD_OFFSET(JSFunction, m_data)));
addPtr(Imm32(NativeCallFrameSize), stackPointerRegister);
#endif
// Check for an exception
loadPtr(&(globalData->exception), regT2);
Jump exceptionHandler = branchTestPtr(NonZero, regT2);
// Grab the return address.
emitGetFromCallFrameHeader(RegisterFile::ReturnPC, regT1);
// Restore our caller's "r".
emitGetFromCallFrameHeader(RegisterFile::CallerFrame, callFrameRegister);
// Return.
push(regT1);
ret();
// Handle an exception
exceptionHandler.link(this);
// Grab the return address.
emitGetFromCallFrameHeader(RegisterFile::ReturnPC, regT1);
move(ImmPtr(&globalData->exceptionLocation), regT2);
storePtr(regT1, regT2);
move(ImmPtr(reinterpret_cast<void*>(ctiVMThrowTrampoline)), regT2);
emitGetFromCallFrameHeader(RegisterFile::CallerFrame, callFrameRegister);
emitPutCTIParam(callFrameRegister, STUB_ARGS_callFrame);
push(regT2);
ret();
#if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
Call array_failureCases1Call = makeTailRecursiveCall(array_failureCases1);
Call array_failureCases2Call = makeTailRecursiveCall(array_failureCases2);
......@@ -1958,6 +2093,7 @@ void JIT::privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executable
*ctiVirtualCallPreLink = patchBuffer.trampolineAt(virtualCallPreLinkBegin);
*ctiVirtualCallLink = patchBuffer.trampolineAt(virtualCallLinkBegin);
*ctiVirtualCall = patchBuffer.trampolineAt(virtualCallBegin);
*ctiNativeCallThunk = patchBuffer.trampolineAt(nativeCallThunk);
}
void JIT::emitGetVariableObjectRegister(RegisterID variableObject, int index, RegisterID dst)
......
......@@ -362,11 +362,11 @@ namespace JSC {
jit.privateCompilePutByIdTransition(stubInfo, oldStructure, newStructure, cachedOffset, chain, returnAddress);
}
static void compileCTIMachineTrampolines(JSGlobalData* globalData, RefPtr<ExecutablePool>* executablePool, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall)
static void compileCTIMachineTrampolines(JSGlobalData* globalData, RefPtr<ExecutablePool>* executablePool, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall, void** ctiNativeCallThunk)
{
JIT jit(globalData);
jit.privateCompileCTIMachineTrampolines(executablePool, ctiArrayLengthTrampoline, ctiStringLengthTrampoline, ctiVirtualCallPreLink, ctiVirtualCallLink, ctiVirtualCall);
jit.privateCompileCTIMachineTrampolines(executablePool, globalData, ctiArrayLengthTrampoline, ctiStringLengthTrampoline, ctiVirtualCallPreLink, ctiVirtualCallLink, ctiVirtualCall, ctiNativeCallThunk);
}
static void patchGetByIdSelf(StructureStubInfo*, Structure*, size_t cachedOffset, ProcessorReturnAddress returnAddress);
......@@ -399,7 +399,7 @@ namespace JSC {
void privateCompilePutByIdReplace(StructureStubInfo*, Structure*, size_t cachedOffset, ProcessorReturnAddress returnAddress);
void privateCompilePutByIdTransition(StructureStubInfo*, Structure*, Structure*, size_t cachedOffset, StructureChain*, ProcessorReturnAddress returnAddress);
void privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executablePool, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall);
void privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executablePool, JSGlobalData* data, void** ctiArrayLengthTrampoline, void** ctiStringLengthTrampoline, void** ctiVirtualCallPreLink, void** ctiVirtualCallLink, void** ctiVirtualCall, void** ctiNativeCallThunk);
void privateCompilePatchGetArrayLength(ProcessorReturnAddress returnAddress);
void addSlowCase(Jump);
......@@ -467,7 +467,8 @@ namespace JSC {
void emitPutToCallFrameHeader(RegisterID from, RegisterFile::CallFrameHeaderEntry entry);
void emitPutImmediateToCallFrameHeader(void* value, RegisterFile::CallFrameHeaderEntry entry);
void emitGetFromCallFrameHeader(RegisterFile::CallFrameHeaderEntry entry, RegisterID to);
void emitGetFromCallFrameHeader(RegisterFile::CallFrameHeaderEntry entry, RegisterID to, RegisterID from = callFrameRegister);
void emitGetFromCallFrameHeader32(RegisterFile::CallFrameHeaderEntry entry, RegisterID to);
JSValue getConstantOperand(unsigned src);
int32_t getConstantOperandImmediateInt(unsigned src);
......
......@@ -56,10 +56,12 @@ void JIT::unlinkCall(CallLinkInfo* callLinkInfo)
void JIT::linkCall(JSFunction* callee, CodeBlock* calleeCodeBlock, JITCode ctiCode, CallLinkInfo* callLinkInfo, int callerArgCount)
{
// Currently we only link calls with the exact number of arguments.
if (callerArgCount == calleeCodeBlock->m_numParameters) {
// If this is a native call calleeCodeBlock is null so the number of parameters is unimportant
if (!calleeCodeBlock || callerArgCount == calleeCodeBlock->m_numParameters) {
ASSERT(!callLinkInfo->isLinked());
calleeCodeBlock->addCaller(callLinkInfo);
if (calleeCodeBlock)
calleeCodeBlock->addCaller(callLinkInfo);
callLinkInfo->hotPathBegin.repatch(callee);
callLinkInfo->hotPathOther.relink(ctiCode.addressForCall());
......@@ -73,7 +75,7 @@ void JIT::compileOpCallInitializeCallFrame()
{
store32(regT1, Address(callFrameRegister, RegisterFile::ArgumentCount * static_cast<int>(sizeof(Register))));
loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_scopeChain) + FIELD_OFFSET(ScopeChain, m_node)), regT1); // newScopeChain
loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_data) + FIELD_OFFSET(ScopeChain, m_node)), regT1); // newScopeChain
storePtr(ImmPtr(JSValue::encode(JSValue())), Address(callFrameRegister, RegisterFile::OptionalCalleeArguments * static_cast<int>(sizeof(Register))));
storePtr(regT2, Address(callFrameRegister, RegisterFile::Callee * static_cast<int>(sizeof(Register))));
......@@ -242,7 +244,7 @@ void JIT::compileOpCall(OpcodeID opcodeID, Instruction* instruction, unsigned ca
// Note that this omits to set up RegisterFile::CodeBlock, which is set in the callee
storePtr(ImmPtr(JSValue::encode(JSValue())), Address(callFrameRegister, (registerOffset + RegisterFile::OptionalCalleeArguments) * static_cast<int>(sizeof(Register))));
storePtr(regT2, Address(callFrameRegister, (registerOffset + RegisterFile::Callee) * static_cast<int>(sizeof(Register))));
loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_scopeChain) + FIELD_OFFSET(ScopeChain, m_node)), regT1); // newScopeChain
loadPtr(Address(regT2, FIELD_OFFSET(JSFunction, m_data) + FIELD_OFFSET(ScopeChain, m_node)), regT1); // newScopeChain
store32(Imm32(argCount), Address(callFrameRegister, (registerOffset + RegisterFile::ArgumentCount) * static_cast<int>(sizeof(Register))));
storePtr(callFrameRegister, Address(callFrameRegister, (registerOffset + RegisterFile::CallerFrame) * static_cast<int>(sizeof(Register))));
storePtr(regT1, Address(callFrameRegister, (registerOffset + RegisterFile::ScopeChain) * static_cast<int>(sizeof(Register))));
......
......@@ -56,7 +56,7 @@ namespace JSC {
{
}
operator bool()
operator bool() const
{
return code != 0;
}
......
......@@ -168,9 +168,15 @@ ALWAYS_INLINE void JIT::emitPutImmediateToCallFrameHeader(void* value, RegisterF
storePtr(ImmPtr(value), Address(callFrameRegister, entry * sizeof(Register)));
}
ALWAYS_INLINE void JIT::emitGetFromCallFrameHeader(RegisterFile::CallFrameHeaderEntry entry, RegisterID to)
ALWAYS_INLINE void JIT::emitGetFromCallFrameHeader(RegisterFile::CallFrameHeaderEntry entry, RegisterID to, RegisterID from)
{
loadPtr(Address(callFrameRegister, entry * sizeof(Register)), to);
loadPtr(Address(from, entry * sizeof(Register)), to);
killLastResultRegister();
}
ALWAYS_INLINE void JIT::emitGetFromCallFrameHeader32(RegisterFile::CallFrameHeaderEntry entry, RegisterID to)
{
load32(Address(callFrameRegister, entry * sizeof(Register)), to);
killLastResultRegister();
}
......
......@@ -74,8 +74,9 @@ JITStubs::JITStubs(JSGlobalData* globalData)
, m_ctiVirtualCallPreLink(0)
, m_ctiVirtualCallLink(0)
, m_ctiVirtualCall(0)
, m_ctiNativeCallThunk(0)
{
JIT::compileCTIMachineTrampolines(globalData, &m_executablePool, &m_ctiArrayLengthTrampoline, &m_ctiStringLengthTrampoline, &m_ctiVirtualCallPreLink, &m_ctiVirtualCallLink, &m_ctiVirtualCall);
JIT::compileCTIMachineTrampolines(globalData, &m_executablePool, &m_ctiArrayLengthTrampoline, &m_ctiStringLengthTrampoline, &m_ctiVirtualCallPreLink, &m_ctiVirtualCallLink, &m_ctiVirtualCall, &m_ctiNativeCallThunk);
}
#if ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
......@@ -819,13 +820,12 @@ void* JITStubs::cti_op_call_JSFunction(STUB_ARGS)
ASSERT(ARG_src1.getCallData(callData) == CallTypeJS);
#endif
ScopeChainNode* callDataScopeChain = asFunction(ARG_src1)->scope().node();
CodeBlock* newCodeBlock = &asFunction(ARG_src1)->body()->bytecode(callDataScopeChain);
JSFunction* function = asFunction(ARG_src1);
FunctionBodyNode* body = function->body();
ScopeChainNode* callDataScopeChain = function->scope().node();
body->jitCode(callDataScopeChain);
if (!newCodeBlock->jitCode())
JIT::compile(ARG_globalData, newCodeBlock);
return newCodeBlock;
return &(body->generatedBytecode());
}
VoidPtrPair JITStubs::cti_op_call_arityCheck(STUB_ARGS)
......@@ -879,13 +879,12 @@ void* JITStubs::cti_vm_dontLazyLinkCall(STUB_ARGS)
JSGlobalData* globalData = ARG_globalData;
JSFunction* callee = asFunction(ARG_src1);
CodeBlock* codeBlock = &callee->body()->bytecode(callee->scope().node());
if (!codeBlock->jitCode())
JIT::compile(globalData, codeBlock);