Commit 8ac6c9f6 authored by oliver@apple.com's avatar oliver@apple.com

2010-02-24 Oliver Hunt <oliver@apple.com>

        Reviewed by Gavin Barraclough.

        Speed up getter performance in the jit
        https://bugs.webkit.org/show_bug.cgi?id=35332

        Implement getter lookup caching in the interpreter.
        The getter stubs are generated through basically the
        same code paths as the normal get_by_id caching.
        Instead of simply loading a property and returning,
        we load the getter slot, and pass the getter, base value
        and return address to a shared stub used for getter
        dispatch.

        * jit/JIT.h:
        (JSC::JIT::compileGetByIdProto):
        (JSC::JIT::compileGetByIdSelfList):
        (JSC::JIT::compileGetByIdProtoList):
        (JSC::JIT::compileGetByIdChainList):
        (JSC::JIT::compileGetByIdChain):
        * jit/JITPropertyAccess.cpp:
        (JSC::JIT::privateCompileGetByIdProto):
        (JSC::JIT::privateCompileGetByIdSelfList):
        (JSC::JIT::privateCompileGetByIdProtoList):
        (JSC::JIT::privateCompileGetByIdChainList):
        (JSC::JIT::privateCompileGetByIdChain):
        * jit/JITPropertyAccess32_64.cpp:
        (JSC::JIT::privateCompileGetByIdProto):
        (JSC::JIT::privateCompileGetByIdSelfList):
        (JSC::JIT::privateCompileGetByIdProtoList):
        (JSC::JIT::privateCompileGetByIdChainList):
        (JSC::JIT::privateCompileGetByIdChain):
        * jit/JITStubs.cpp:
        (JSC::JITThunks::tryCacheGetByID):
        (JSC::DEFINE_STUB_FUNCTION):
        * jit/JITStubs.h:
        (JSC::):
        * runtime/GetterSetter.h:

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@55185 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 8c77c4a3
2010-02-24 Oliver Hunt <oliver@apple.com>
Reviewed by Gavin Barraclough.
Speed up getter performance in the jit
https://bugs.webkit.org/show_bug.cgi?id=35332
Implement getter lookup caching in the interpreter.
The getter stubs are generated through basically the
same code paths as the normal get_by_id caching.
Instead of simply loading a property and returning,
we load the getter slot, and pass the getter, base value
and return address to a shared stub used for getter
dispatch.
* jit/JIT.h:
(JSC::JIT::compileGetByIdProto):
(JSC::JIT::compileGetByIdSelfList):
(JSC::JIT::compileGetByIdProtoList):
(JSC::JIT::compileGetByIdChainList):
(JSC::JIT::compileGetByIdChain):
* jit/JITPropertyAccess.cpp:
(JSC::JIT::privateCompileGetByIdProto):
(JSC::JIT::privateCompileGetByIdSelfList):
(JSC::JIT::privateCompileGetByIdProtoList):
(JSC::JIT::privateCompileGetByIdChainList):
(JSC::JIT::privateCompileGetByIdChain):
* jit/JITPropertyAccess32_64.cpp:
(JSC::JIT::privateCompileGetByIdProto):
(JSC::JIT::privateCompileGetByIdSelfList):
(JSC::JIT::privateCompileGetByIdProtoList):
(JSC::JIT::privateCompileGetByIdChainList):
(JSC::JIT::privateCompileGetByIdChain):
* jit/JITStubs.cpp:
(JSC::JITThunks::tryCacheGetByID):
(JSC::DEFINE_STUB_FUNCTION):
* jit/JITStubs.h:
(JSC::):
* runtime/GetterSetter.h:
2010-02-23 Oliver Hunt <oliver@apple.com>
Reviewed by Maciej Stachowiak.
......
......@@ -283,32 +283,32 @@ namespace JSC {
return JIT(globalData, codeBlock).privateCompile();
}
static void compileGetByIdProto(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, ReturnAddressPtr returnAddress)
static void compileGetByIdProto(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, bool isGetter, size_t cachedOffset, ReturnAddressPtr returnAddress)
{
JIT jit(globalData, codeBlock);
jit.privateCompileGetByIdProto(stubInfo, structure, prototypeStructure, cachedOffset, returnAddress, callFrame);
jit.privateCompileGetByIdProto(stubInfo, structure, prototypeStructure, isGetter, cachedOffset, returnAddress, callFrame);
}
static void compileGetByIdSelfList(JSGlobalData* globalData, CodeBlock* codeBlock, StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, size_t cachedOffset)
static void compileGetByIdSelfList(JSGlobalData* globalData, CodeBlock* codeBlock, StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, bool isGetter, size_t cachedOffset)
{
JIT jit(globalData, codeBlock);
jit.privateCompileGetByIdSelfList(stubInfo, polymorphicStructures, currentIndex, structure, cachedOffset);
jit.privateCompileGetByIdSelfList(stubInfo, polymorphicStructures, currentIndex, structure, isGetter, cachedOffset);
}
static void compileGetByIdProtoList(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructureList, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset)
static void compileGetByIdProtoList(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructureList, int currentIndex, Structure* structure, Structure* prototypeStructure, bool isGetter, size_t cachedOffset)
{
JIT jit(globalData, codeBlock);
jit.privateCompileGetByIdProtoList(stubInfo, prototypeStructureList, currentIndex, structure, prototypeStructure, cachedOffset, callFrame);
jit.privateCompileGetByIdProtoList(stubInfo, prototypeStructureList, currentIndex, structure, prototypeStructure, isGetter, cachedOffset, callFrame);
}
static void compileGetByIdChainList(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructureList, int currentIndex, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset)
static void compileGetByIdChainList(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructureList, int currentIndex, Structure* structure, StructureChain* chain, size_t count, bool isGetter, size_t cachedOffset)
{
JIT jit(globalData, codeBlock);
jit.privateCompileGetByIdChainList(stubInfo, prototypeStructureList, currentIndex, structure, chain, count, cachedOffset, callFrame);
jit.privateCompileGetByIdChainList(stubInfo, prototypeStructureList, currentIndex, structure, chain, count, isGetter, cachedOffset, callFrame);
}
static void compileGetByIdChain(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, ReturnAddressPtr returnAddress)
static void compileGetByIdChain(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, bool isGetter, size_t cachedOffset, ReturnAddressPtr returnAddress)
{
JIT jit(globalData, codeBlock);
jit.privateCompileGetByIdChain(stubInfo, structure, chain, count, cachedOffset, returnAddress, callFrame);
jit.privateCompileGetByIdChain(stubInfo, structure, chain, count, isGetter, cachedOffset, returnAddress, callFrame);
}
static void compilePutByIdTransition(JSGlobalData* globalData, CodeBlock* codeBlock, StructureStubInfo* stubInfo, Structure* oldStructure, Structure* newStructure, size_t cachedOffset, StructureChain* chain, ReturnAddressPtr returnAddress)
......@@ -354,11 +354,11 @@ namespace JSC {
void privateCompileLinkPass();
void privateCompileSlowCases();
JITCode privateCompile();
void privateCompileGetByIdProto(StructureStubInfo*, Structure*, Structure* prototypeStructure, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame);
void privateCompileGetByIdSelfList(StructureStubInfo*, PolymorphicAccessStructureList*, int, Structure*, size_t cachedOffset);
void privateCompileGetByIdProtoList(StructureStubInfo*, PolymorphicAccessStructureList*, int, Structure*, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame);
void privateCompileGetByIdChainList(StructureStubInfo*, PolymorphicAccessStructureList*, int, Structure*, StructureChain* chain, size_t count, size_t cachedOffset, CallFrame* callFrame);
void privateCompileGetByIdChain(StructureStubInfo*, Structure*, StructureChain*, size_t count, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame);
void privateCompileGetByIdProto(StructureStubInfo*, Structure*, Structure* prototypeStructure, bool isGetter, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame);
void privateCompileGetByIdSelfList(StructureStubInfo*, PolymorphicAccessStructureList*, int, Structure*, bool isGetter, size_t cachedOffset);
void privateCompileGetByIdProtoList(StructureStubInfo*, PolymorphicAccessStructureList*, int, Structure*, Structure* prototypeStructure, bool isGetter, size_t cachedOffset, CallFrame* callFrame);
void privateCompileGetByIdChainList(StructureStubInfo*, PolymorphicAccessStructureList*, int, Structure*, StructureChain* chain, size_t count, bool isGetter, size_t cachedOffset, CallFrame* callFrame);
void privateCompileGetByIdChain(StructureStubInfo*, Structure*, StructureChain*, size_t count, bool isGetter, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame);
void privateCompilePutByIdTransition(StructureStubInfo*, Structure*, Structure*, size_t cachedOffset, StructureChain*, ReturnAddressPtr returnAddress);
void privateCompileCTIMachineTrampolines(RefPtr<ExecutablePool>* executablePool, JSGlobalData* data, TrampolineStructure *trampolines);
......
......@@ -32,6 +32,7 @@
#if ENABLE(JIT)
#include "CodeBlock.h"
#include "GetterSetter.h"
#include "JITInlineMethods.h"
#include "JITStubCall.h"
#include "JSArray.h"
......@@ -695,7 +696,7 @@ void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress)
repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_array_fail));
}
void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, bool isGetter, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
{
// The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
// referencing the prototype object - let's speculatively load it's table nice and early!)
......@@ -713,11 +714,17 @@ void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* str
Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure));
#endif
// Checks out okay! - getDirectOffset
compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
// Checks out okay!
if (isGetter) {
compileGetDirectOffset(protoObject, regT1, regT1, cachedOffset);
JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
stubCall.addArgument(regT1);
stubCall.addArgument(regT0);
stubCall.addArgument(ImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
} else
compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
Jump success = jump();
LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
// Use the patch information to link the failure cases back to the original slow case routine.
......@@ -728,6 +735,12 @@ void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* str
// On success return back to the hot patch code, at a point it will perform the store to dest for us.
patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
if (isGetter) {
for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
if (iter->to)
patchBuffer.link(iter->from, FunctionPtr(iter->to));
}
}
// Track the stub we have created so that it will be deleted later.
CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
stubInfo->stubRoutine = entryLabel;
......@@ -741,14 +754,29 @@ void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* str
repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_proto_list));
}
void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, size_t cachedOffset)
void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, bool isGetter, size_t cachedOffset)
{
Jump failureCase = checkStructure(regT0, structure);
compileGetDirectOffset(regT0, regT0, structure, cachedOffset);
if (isGetter) {
compileGetDirectOffset(regT0, regT1, structure, cachedOffset);
JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
stubCall.addArgument(regT1);
stubCall.addArgument(regT0);
stubCall.addArgument(ImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
} else
compileGetDirectOffset(regT0, regT0, structure, cachedOffset);
Jump success = jump();
LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
if (isGetter) {
for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
if (iter->to)
patchBuffer.link(iter->from, FunctionPtr(iter->to));
}
}
// Use the patch information to link the failure cases back to the original slow case routine.
CodeLocationLabel lastProtoBegin = polymorphicStructures->list[currentIndex - 1].stubRoutine;
if (!lastProtoBegin)
......@@ -770,7 +798,7 @@ void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, Polymorphic
repatchBuffer.relink(jumpLocation, entryLabel);
}
void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame)
void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, bool isGetter, size_t cachedOffset, CallFrame* callFrame)
{
// The prototype object definitely exists (if this stub exists the CodeBlock is referencing a Structure that is
// referencing the prototype object - let's speculatively load it's table nice and early!)
......@@ -788,13 +816,28 @@ void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, Polymorphi
Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure));
#endif
// Checks out okay! - getDirectOffset
compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
// Checks out okay!
if (isGetter) {
compileGetDirectOffset(protoObject, regT1, regT1, cachedOffset);
JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
stubCall.addArgument(regT1);
stubCall.addArgument(regT0);
stubCall.addArgument(ImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
} else
compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
Jump success = jump();
LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
if (isGetter) {
for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
if (iter->to)
patchBuffer.link(iter->from, FunctionPtr(iter->to));
}
}
// Use the patch information to link the failure cases back to the original slow case routine.
CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine;
patchBuffer.link(failureCases1, lastProtoBegin);
......@@ -815,10 +858,9 @@ void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, Polymorphi
repatchBuffer.relink(jumpLocation, entryLabel);
}
void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, CallFrame* callFrame)
void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, bool isGetter, size_t cachedOffset, CallFrame* callFrame)
{
ASSERT(count);
JumpList bucketsOfFail;
// Check eax is an object of the right Structure.
......@@ -842,11 +884,26 @@ void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, Polymorphi
#endif
}
ASSERT(protoObject);
compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
if (isGetter) {
compileGetDirectOffset(protoObject, regT1, regT1, cachedOffset);
JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
stubCall.addArgument(regT1);
stubCall.addArgument(regT0);
stubCall.addArgument(ImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
} else
compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
Jump success = jump();
LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
if (isGetter) {
for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
if (iter->to)
patchBuffer.link(iter->from, FunctionPtr(iter->to));
}
}
// Use the patch information to link the failure cases back to the original slow case routine.
CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine;
......@@ -869,10 +926,10 @@ void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, Polymorphi
repatchBuffer.relink(jumpLocation, entryLabel);
}
void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, bool isGetter, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
{
ASSERT(count);
JumpList bucketsOfFail;
// Check eax is an object of the right Structure.
......@@ -895,12 +952,27 @@ void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* str
#endif
}
ASSERT(protoObject);
compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
if (isGetter) {
compileGetDirectOffset(protoObject, regT1, regT1, cachedOffset);
JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
stubCall.addArgument(regT1);
stubCall.addArgument(regT0);
stubCall.addArgument(ImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
} else
compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
Jump success = jump();
LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
if (isGetter) {
for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
if (iter->to)
patchBuffer.link(iter->from, FunctionPtr(iter->to));
}
}
// Use the patch information to link the failure cases back to the original slow case routine.
patchBuffer.link(bucketsOfFail, stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall));
......
......@@ -718,7 +718,7 @@ void JIT::privateCompilePatchGetArrayLength(ReturnAddressPtr returnAddress)
repatchBuffer.relinkCallerToFunction(returnAddress, FunctionPtr(cti_op_get_by_id_array_fail));
}
void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* structure, Structure* prototypeStructure, bool isGetter, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
{
// regT0 holds a JSCell*
......@@ -737,8 +737,16 @@ void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* str
Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure));
#endif
// Checks out okay! - getDirectOffset
compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
// Checks out okay!
if (isGetter) {
compileGetDirectOffset(protoObject, regT2, regT2, regT1, cachedOffset);
JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
stubCall.addArgument(regT1);
stubCall.addArgument(regT0);
stubCall.addArgument(ImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
} else
compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
Jump success = jump();
......@@ -751,7 +759,14 @@ void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* str
// On success return back to the hot patch code, at a point it will perform the store to dest for us.
patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
if (isGetter) {
for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
if (iter->to)
patchBuffer.link(iter->from, FunctionPtr(iter->to));
}
}
// Track the stub we have created so that it will be deleted later.
CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
stubInfo->stubRoutine = entryLabel;
......@@ -766,16 +781,29 @@ void JIT::privateCompileGetByIdProto(StructureStubInfo* stubInfo, Structure* str
}
void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, size_t cachedOffset)
void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, bool isGetter, size_t cachedOffset)
{
// regT0 holds a JSCell*
Jump failureCase = checkStructure(regT0, structure);
compileGetDirectOffset(regT0, regT1, regT0, structure, cachedOffset);
if (isGetter) {
compileGetDirectOffset(regT0, regT2, regT1, structure, cachedOffset);
JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
stubCall.addArgument(regT1);
stubCall.addArgument(regT0);
stubCall.addArgument(ImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
} else
compileGetDirectOffset(regT0, regT1, regT0, structure, cachedOffset);
Jump success = jump();
LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
if (isGetter) {
for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
if (iter->to)
patchBuffer.link(iter->from, FunctionPtr(iter->to));
}
}
// Use the patch information to link the failure cases back to the original slow case routine.
CodeLocationLabel lastProtoBegin = polymorphicStructures->list[currentIndex - 1].stubRoutine;
if (!lastProtoBegin)
......@@ -785,7 +813,7 @@ void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, Polymorphic
// On success return back to the hot patch code, at a point it will perform the store to dest for us.
patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
CodeLocationLabel entryLabel = patchBuffer.finalizeCodeAddendum();
structure->ref();
......@@ -797,7 +825,7 @@ void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, Polymorphic
repatchBuffer.relink(jumpLocation, entryLabel);
}
void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame)
void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, bool isGetter, size_t cachedOffset, CallFrame* callFrame)
{
// regT0 holds a JSCell*
......@@ -817,12 +845,25 @@ void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, Polymorphi
Jump failureCases2 = branchPtr(NotEqual, AbsoluteAddress(prototypeStructureAddress), ImmPtr(prototypeStructure));
#endif
compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
if (isGetter) {
compileGetDirectOffset(protoObject, regT2, regT2, regT1, cachedOffset);
JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
stubCall.addArgument(regT1);
stubCall.addArgument(regT0);
stubCall.addArgument(ImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
} else
compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
Jump success = jump();
LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
if (isGetter) {
for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
if (iter->to)
patchBuffer.link(iter->from, FunctionPtr(iter->to));
}
}
// Use the patch information to link the failure cases back to the original slow case routine.
CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine;
patchBuffer.link(failureCases1, lastProtoBegin);
......@@ -843,10 +884,9 @@ void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, Polymorphi
repatchBuffer.relink(jumpLocation, entryLabel);
}
void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, CallFrame* callFrame)
void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, StructureChain* chain, size_t count, bool isGetter, size_t cachedOffset, CallFrame* callFrame)
{
// regT0 holds a JSCell*
ASSERT(count);
JumpList bucketsOfFail;
......@@ -872,11 +912,25 @@ void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, Polymorphi
}
ASSERT(protoObject);
compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
if (isGetter) {
compileGetDirectOffset(protoObject, regT2, regT2, regT1, cachedOffset);
JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
stubCall.addArgument(regT1);
stubCall.addArgument(regT0);
stubCall.addArgument(ImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
} else
compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
Jump success = jump();
LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
if (isGetter) {
for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
if (iter->to)
patchBuffer.link(iter->from, FunctionPtr(iter->to));
}
}
// Use the patch information to link the failure cases back to the original slow case routine.
CodeLocationLabel lastProtoBegin = prototypeStructures->list[currentIndex - 1].stubRoutine;
......@@ -898,10 +952,9 @@ void JIT::privateCompileGetByIdChainList(StructureStubInfo* stubInfo, Polymorphi
repatchBuffer.relink(jumpLocation, entryLabel);
}
void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* structure, StructureChain* chain, size_t count, bool isGetter, size_t cachedOffset, ReturnAddressPtr returnAddress, CallFrame* callFrame)
{
// regT0 holds a JSCell*
ASSERT(count);
JumpList bucketsOfFail;
......@@ -927,11 +980,24 @@ void JIT::privateCompileGetByIdChain(StructureStubInfo* stubInfo, Structure* str
}
ASSERT(protoObject);
compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
if (isGetter) {
compileGetDirectOffset(protoObject, regT2, regT2, regT1, cachedOffset);
JITStubCall stubCall(this, cti_op_get_by_id_getter_stub);
stubCall.addArgument(regT1);
stubCall.addArgument(regT0);
stubCall.addArgument(ImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
} else
compileGetDirectOffset(protoObject, regT2, regT1, regT0, cachedOffset);
Jump success = jump();
LinkBuffer patchBuffer(this, m_codeBlock->executablePool());
if (isGetter) {
for (Vector<CallRecord>::iterator iter = m_calls.begin(); iter != m_calls.end(); ++iter) {
if (iter->to)
patchBuffer.link(iter->from, FunctionPtr(iter->to));
}
}
// Use the patch information to link the failure cases back to the original slow case routine.
patchBuffer.link(bucketsOfFail, stubInfo->callReturnLocation.labelAtOffset(-patchOffsetGetByIdSlowCaseCall));
......
......@@ -38,6 +38,7 @@
#include "Collector.h"
#include "Debugger.h"
#include "ExceptionHelpers.h"
#include "GetterSetter.h"
#include "GlobalEvalFunction.h"
#include "JIT.h"
#include "JSActivation.h"
......@@ -856,11 +857,10 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
}
// Uncacheable: give up.
if (!slot.isCacheableValue()) {
if (!slot.isCacheable()) {
ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_get_by_id_generic));
return;
}
ASSERT(!slot.isGetter());
JSCell* baseCell = asCell(baseValue);
Structure* structure = baseCell->structure();
......@@ -875,8 +875,10 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
if (slot.slotBase() == baseValue) {
// set this up, so derefStructures can do it's job.
stubInfo->initGetByIdSelf(structure);
JIT::patchGetByIdSelf(codeBlock, stubInfo, structure, slot.cachedOffset(), returnAddress);
if (slot.isGetter())
ctiPatchCallByReturnAddress(codeBlock, returnAddress, FunctionPtr(cti_op_get_by_id_self_fail));
else
JIT::patchGetByIdSelf(codeBlock, stubInfo, structure, slot.cachedOffset(), returnAddress);
return;
}
......@@ -902,7 +904,7 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
ASSERT(!structure->isDictionary());
ASSERT(!slotBaseObject->structure()->isDictionary());
JIT::compileGetByIdProto(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, slotBaseObject->structure(), offset, returnAddress);
JIT::compileGetByIdProto(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, slotBaseObject->structure(), slot.isGetter(), offset, returnAddress);
return;
}
......@@ -915,7 +917,7 @@ NEVER_INLINE void JITThunks::tryCacheGetByID(CallFrame* callFrame, CodeBlock* co
StructureChain* prototypeChain = structure->prototypeChain(callFrame);
stubInfo->initGetByIdChain(structure, prototypeChain);
JIT::compileGetByIdChain(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, prototypeChain, count, offset, returnAddress);
JIT::compileGetByIdChain(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, structure, prototypeChain, count, slot.isGetter(), offset, returnAddress);
}
#endif // ENABLE(JIT_OPTIMIZE_PROPERTY_ACCESS)
......@@ -1375,7 +1377,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_self_fail)
CHECK_FOR_EXCEPTION();
if (baseValue.isCell()
&& slot.isCacheableValue()
&& slot.isCacheable()
&& !asCell(baseValue)->structure()->isUncacheableDictionary()
&& slot.slotBase() == baseValue) {
......@@ -1397,7 +1399,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_self_fail)
stubInfo->u.getByIdSelfList.listSize++;
}
JIT::compileGetByIdSelfList(callFrame->scopeChain()->globalData, codeBlock, stubInfo, polymorphicStructureList, listIndex, asCell(baseValue)->structure(), slot.cachedOffset());
JIT::compileGetByIdSelfList(callFrame->scopeChain()->globalData, codeBlock, stubInfo, polymorphicStructureList, listIndex, asCell(baseValue)->structure(), slot.isGetter(), slot.cachedOffset());
if (listIndex == (POLYMORPHIC_LIST_CACHE_SIZE - 1))
ctiPatchCallByReturnAddress(codeBlock, STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_generic));
......@@ -1435,6 +1437,23 @@ static PolymorphicAccessStructureList* getPolymorphicAccessStructureListSlot(Str
return prototypeStructureList;
}
DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_getter_stub)
{
STUB_INIT_STACK_FRAME(stackFrame);
CallFrame* callFrame = stackFrame.callFrame;
GetterSetter* getterSetter = asGetterSetter(stackFrame.args[0].jsObject());
if (!getterSetter->getter())
return JSValue::encode(jsUndefined());
JSObject* getter = asObject(getterSetter->getter());
CallData callData;
CallType callType = getter->getCallData(callData);
JSValue result = call(callFrame, getter, callType, callData, stackFrame.args[1].jsObject(), ArgList());
if (callFrame->hadException())
returnToThrowTrampoline(&callFrame->globalData(), stackFrame.args[2].returnAddress(), STUB_RETURN_ADDRESS);
return JSValue::encode(result);
}
DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_proto_list)
{
STUB_INIT_STACK_FRAME(stackFrame);
......@@ -1448,7 +1467,7 @@ DEFINE_STUB_FUNCTION(EncodedJSValue, op_get_by_id_proto_list)
CHECK_FOR_EXCEPTION();
if (!baseValue.isCell() || !slot.isCacheableValue() || asCell(baseValue)->structure()->isDictionary()) {
if (!baseValue.isCell() || !slot.isCacheable() || asCell(baseValue)->structure()->isDictionary()) {
ctiPatchCallByReturnAddress(callFrame->codeBlock(), STUB_RETURN_ADDRESS, FunctionPtr(cti_op_get_by_id_proto_fail));
return JSValue::encode(result);