2008-11-21 Gavin Barraclough <barraclough@apple.com>
Reviewed by Oliver Hunt.
Add (really) polymorphic caching for get by id self.
Very similar to caching of prototype accesses, described below.
Oh, also, probably shouldn't have been leaking those structure list objects.
4% preogression on deltablue.
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::dump):
(JSC::CodeBlock::derefStructures):
(JSC::PrototypeStructureList::derefStructures):
* bytecode/Instruction.h:
* bytecode/Opcode.h:
* interpreter/Interpreter.cpp:
(JSC::Interpreter::privateExecute):
(JSC::Interpreter::cti_op_get_by_id_self_fail):
* jit/JIT.cpp:
(JSC::JIT::privateCompileMainPass):
(JSC::JIT::privateCompileGetByIdSelfList):
(JSC::JIT::patchGetByIdSelf):
* jit/JIT.h:
(JSC::JIT::compileGetByIdSelfList):
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@38688 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JavaScriptCore/ChangeLog b/JavaScriptCore/ChangeLog
index a3c0971..3268b27 100644
--- a/JavaScriptCore/ChangeLog
+++ b/JavaScriptCore/ChangeLog
@@ -1,3 +1,30 @@
+2008-11-21 Gavin Barraclough <barraclough@apple.com>
+
+ Reviewed by Oliver Hunt.
+
+ Add (really) polymorphic caching for get by id self.
+ Very similar to caching of prototype accesses, described below.
+
+ Oh, also, probably shouldn't have been leaking those structure list objects.
+
+ 4% preogression on deltablue.
+
+ * bytecode/CodeBlock.cpp:
+ (JSC::CodeBlock::dump):
+ (JSC::CodeBlock::derefStructures):
+ (JSC::PrototypeStructureList::derefStructures):
+ * bytecode/Instruction.h:
+ * bytecode/Opcode.h:
+ * interpreter/Interpreter.cpp:
+ (JSC::Interpreter::privateExecute):
+ (JSC::Interpreter::cti_op_get_by_id_self_fail):
+ * jit/JIT.cpp:
+ (JSC::JIT::privateCompileMainPass):
+ (JSC::JIT::privateCompileGetByIdSelfList):
+ (JSC::JIT::patchGetByIdSelf):
+ * jit/JIT.h:
+ (JSC::JIT::compileGetByIdSelfList):
+
2008-11-21 Geoffrey Garen <ggaren@apple.com>
Reviewed by Sam Weinig.
diff --git a/JavaScriptCore/bytecode/CodeBlock.cpp b/JavaScriptCore/bytecode/CodeBlock.cpp
index e4e2fab..ad783e2 100644
--- a/JavaScriptCore/bytecode/CodeBlock.cpp
+++ b/JavaScriptCore/bytecode/CodeBlock.cpp
@@ -642,6 +642,10 @@
printGetByIdOp(location, it, identifiers, "get_by_id_self");
break;
}
+ case op_get_by_id_self_list: {
+ printGetByIdOp(location, it, identifiers, "get_by_id_self_list");
+ break;
+ }
case op_get_by_id_proto: {
printGetByIdOp(location, it, identifiers, "get_by_id_proto");
break;
@@ -1023,18 +1027,11 @@
vPC[4].u.structure->deref();
return;
}
- if (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_proto_list)) {
- PrototypeStructureList* prototypeStructures = vPC[4].u.prototypeStructure;
- int count = vPC[5].u.operand;
- for (int i = 0; i < count; ++i) {
- PrototypeStructureList::ProtoStubInfo& info = prototypeStructures->list[i];
- ASSERT(info.base);
- ASSERT(info.proto);
- ASSERT(info.stubRoutine);
- info.base->deref();
- info.proto->deref();
- WTF::fastFreeExecutable(info.stubRoutine);
- }
+ if ((vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_proto_list))
+ || (vPC[0].u.opcode == interpreter->getOpcode(op_get_by_id_self_list))) {
+ PolymorphicAccessStructureList* polymorphicStructures = vPC[4].u.polymorphicStructures;
+ polymorphicStructures->derefStructures(vPC[5].u.operand);
+ delete polymorphicStructures;
return;
}
diff --git a/JavaScriptCore/bytecode/Instruction.h b/JavaScriptCore/bytecode/Instruction.h
index cb29d91..7f8f088 100644
--- a/JavaScriptCore/bytecode/Instruction.h
+++ b/JavaScriptCore/bytecode/Instruction.h
@@ -33,7 +33,7 @@
#include "ResultType.h"
#include <wtf/VectorTraits.h>
-#define PROTOTYPE_LIST_CACHE_SIZE 4
+#define POLYMORPHIC_LIST_CACHE_SIZE 4
namespace JSC {
@@ -42,8 +42,8 @@
class StructureChain;
// Structure used by op_get_by_id_proto_list instruction to hold data off the main opcode stream.
- struct PrototypeStructureList {
- struct ProtoStubInfo {
+ struct PolymorphicAccessStructureList {
+ struct PolymorphicStubInfo {
Structure* base;
Structure* proto;
int cachedOffset;
@@ -56,12 +56,28 @@
cachedOffset = _cachedOffset;
stubRoutine = _stubRoutine;
}
- } list[PROTOTYPE_LIST_CACHE_SIZE];
+ } list[POLYMORPHIC_LIST_CACHE_SIZE];
- PrototypeStructureList(Structure* firstBase, Structure* firstProto, int cachedOffset, void* stubRoutine)
+ PolymorphicAccessStructureList(Structure* firstBase, Structure* firstProto, int cachedOffset, void* stubRoutine)
{
list[0].set(firstBase, firstProto, cachedOffset, stubRoutine);
}
+
+ void derefStructures(int count)
+ {
+ for (int i = 0; i < count; ++i) {
+ PolymorphicStubInfo& info = list[i];
+
+ ASSERT(info.base);
+ info.base->deref();
+
+ if (info.proto)
+ info.proto->deref();
+
+ if (info.stubRoutine)
+ WTF::fastFreeExecutable(info.stubRoutine);
+ }
+ }
};
struct Instruction {
@@ -77,7 +93,7 @@
Instruction(Structure* structure) { u.structure = structure; }
Instruction(StructureChain* structureChain) { u.structureChain = structureChain; }
Instruction(JSCell* jsCell) { u.jsCell = jsCell; }
- Instruction(PrototypeStructureList* prototypeStructure) { u.prototypeStructure = prototypeStructure; }
+ Instruction(PolymorphicAccessStructureList* polymorphicStructures) { u.polymorphicStructures = polymorphicStructures; }
union {
Opcode opcode;
@@ -86,7 +102,7 @@
StructureChain* structureChain;
JSCell* jsCell;
ResultType::Type resultType;
- PrototypeStructureList* prototypeStructure;
+ PolymorphicAccessStructureList* polymorphicStructures;
} u;
};
diff --git a/JavaScriptCore/bytecode/Opcode.h b/JavaScriptCore/bytecode/Opcode.h
index 0d78845..de8827a 100644
--- a/JavaScriptCore/bytecode/Opcode.h
+++ b/JavaScriptCore/bytecode/Opcode.h
@@ -101,6 +101,7 @@
macro(op_resolve_func) \
macro(op_get_by_id) \
macro(op_get_by_id_self) \
+ macro(op_get_by_id_self_list) \
macro(op_get_by_id_proto) \
macro(op_get_by_id_proto_list) \
macro(op_get_by_id_chain) \
diff --git a/JavaScriptCore/interpreter/Interpreter.cpp b/JavaScriptCore/interpreter/Interpreter.cpp
index b01ff4d..1c214ef 100644
--- a/JavaScriptCore/interpreter/Interpreter.cpp
+++ b/JavaScriptCore/interpreter/Interpreter.cpp
@@ -2586,6 +2586,13 @@
uncacheGetByID(callFrame->codeBlock(), vPC);
NEXT_INSTRUCTION();
}
+ DEFINE_OPCODE(op_get_by_id_self_list) {
+ // Polymorphic self access caching currently only supported when JITting.
+ ASSERT_NOT_REACHED();
+ // This case of the switch must not be empty, else (op_get_by_id_self_list == op_get_by_id_chain)!
+ vPC += 8;
+ NEXT_INSTRUCTION();
+ }
DEFINE_OPCODE(op_get_by_id_proto_list) {
// Polymorphic prototype access caching currently only supported when JITting.
ASSERT_NOT_REACHED();
@@ -4593,7 +4600,45 @@
PropertySlot slot(baseValue);
JSValue* result = baseValue->get(callFrame, ident, slot);
- CHECK_FOR_EXCEPTION_AT_END();
+ CHECK_FOR_EXCEPTION();
+
+ if (baseValue->isObject()
+ && slot.isCacheable()
+ && !asCell(baseValue)->structure()->isDictionary()
+ && slot.slotBase() == baseValue) {
+
+ CodeBlock* codeBlock = callFrame->codeBlock();
+ unsigned vPCIndex = codeBlock->ctiReturnAddressVPCMap.get(CTI_RETURN_ADDRESS);
+ Instruction* vPC = codeBlock->instructions.begin() + vPCIndex;
+
+ ASSERT(slot.slotBase()->isObject());
+
+ StructureStubInfo* stubInfo = &codeBlock->getStubInfo(CTI_RETURN_ADDRESS);
+
+ PolymorphicAccessStructureList* polymorphicStructureList;
+ int listIndex = 1;
+
+ if (vPC[0].u.opcode == ARG_globalData->interpreter->getOpcode(op_get_by_id_self)) {
+ ASSERT(!stubInfo->stubRoutine);
+ polymorphicStructureList = new PolymorphicAccessStructureList(vPC[4].u.structure, 0, vPC[5].u.operand, 0);
+
+ vPC[0] = ARG_globalData->interpreter->getOpcode(op_get_by_id_self_list);
+ vPC[4] = polymorphicStructureList;
+ vPC[5] = 2;
+ } else {
+ polymorphicStructureList = vPC[4].u.polymorphicStructures;
+ listIndex = vPC[5].u.operand;
+
+ vPC[5] = listIndex + 1;
+ }
+
+ JIT::compileGetByIdSelfList(callFrame->scopeChain()->globalData, codeBlock, stubInfo, polymorphicStructureList, listIndex, asCell(baseValue)->structure(), slot.cachedOffset());
+
+ if (listIndex == (POLYMORPHIC_LIST_CACHE_SIZE - 1))
+ ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_generic));
+ } else {
+ ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_generic));
+ }
return result;
}
@@ -4634,18 +4679,18 @@
StructureStubInfo* stubInfo = &codeBlock->getStubInfo(CTI_RETURN_ADDRESS);
- PrototypeStructureList* prototypeStructureList;
+ PolymorphicAccessStructureList* prototypeStructureList;
int listIndex = 1;
if (vPC[0].u.opcode == ARG_globalData->interpreter->getOpcode(op_get_by_id_proto)) {
- prototypeStructureList = new PrototypeStructureList(vPC[4].u.structure, vPC[5].u.structure, vPC[6].u.operand, stubInfo->stubRoutine);
+ prototypeStructureList = new PolymorphicAccessStructureList(vPC[4].u.structure, vPC[5].u.structure, vPC[6].u.operand, stubInfo->stubRoutine);
stubInfo->stubRoutine = 0;
vPC[0] = ARG_globalData->interpreter->getOpcode(op_get_by_id_proto_list);
vPC[4] = prototypeStructureList;
vPC[5] = 2;
} else {
- prototypeStructureList = vPC[4].u.prototypeStructure;
+ prototypeStructureList = vPC[4].u.polymorphicStructures;
listIndex = vPC[5].u.operand;
vPC[5] = listIndex + 1;
@@ -4653,7 +4698,7 @@
JIT::compileGetByIdProtoList(callFrame->scopeChain()->globalData, callFrame, codeBlock, stubInfo, prototypeStructureList, listIndex, structure, slotBaseObject->structure(), slot.cachedOffset());
- if (listIndex == (PROTOTYPE_LIST_CACHE_SIZE - 1))
+ if (listIndex == (POLYMORPHIC_LIST_CACHE_SIZE - 1))
ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_list_full));
} else {
ctiRepatchCallByReturnAddress(CTI_RETURN_ADDRESS, reinterpret_cast<void*>(cti_op_get_by_id_proto_fail));
diff --git a/JavaScriptCore/jit/JIT.cpp b/JavaScriptCore/jit/JIT.cpp
index a5ae73a..85f1d25 100644
--- a/JavaScriptCore/jit/JIT.cpp
+++ b/JavaScriptCore/jit/JIT.cpp
@@ -2359,6 +2359,7 @@
case op_get_by_id_proto:
case op_get_by_id_proto_list:
case op_get_by_id_self:
+ case op_get_by_id_self_list:
case op_get_string_length:
case op_put_by_id_generic:
case op_put_by_id_replace:
@@ -3230,7 +3231,36 @@
}
#if USE(CTI_REPATCH_PIC)
-void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PrototypeStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame)
+void JIT::privateCompileGetByIdSelfList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, size_t cachedOffset)
+{
+ JmpSrc failureCase = checkStructure(X86::eax, structure);
+ __ movl_mr(FIELD_OFFSET(JSObject, m_propertyStorage), X86::eax, X86::eax);
+ __ movl_mr(cachedOffset * sizeof(JSValue*), X86::eax, X86::eax);
+ JmpSrc success = __ jmp();
+
+ void* code = __ executableCopy();
+ ASSERT(code);
+
+ // Use the repatch information to link the failure cases back to the original slow case routine.
+ void* lastProtoBegin = polymorphicStructures->list[currentIndex - 1].stubRoutine;
+ if (!lastProtoBegin)
+ lastProtoBegin = reinterpret_cast<char*>(stubInfo->callReturnLocation) - repatchOffsetGetByIdSlowCaseCall;
+
+ X86Assembler::link(code, failureCase, lastProtoBegin);
+
+ // On success return back to the hot patch code, at a point it will perform the store to dest for us.
+ intptr_t successDest = reinterpret_cast<intptr_t>(stubInfo->hotPathBegin) + repatchOffsetGetByIdPropertyMapOffset;
+ X86Assembler::link(code, success, reinterpret_cast<void*>(successDest));
+
+ structure->ref();
+ polymorphicStructures->list[currentIndex].set(structure, 0, cachedOffset, 0/*code*/);
+
+ // Finally repatch the jump to slow case back in the hot path to jump here instead.
+ intptr_t jmpLocation = reinterpret_cast<intptr_t>(stubInfo->hotPathBegin) + repatchOffsetGetByIdBranchToSlowCase;
+ X86Assembler::repatchBranchOffset(jmpLocation, code);
+}
+
+void JIT::privateCompileGetByIdProtoList(StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructures, int currentIndex, Structure* structure, Structure* prototypeStructure, 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!)
@@ -3666,7 +3696,7 @@
// We don't want to repatch more than once - in future go to cti_op_get_by_id_generic.
// Should probably go to Interpreter::cti_op_get_by_id_fail, but that doesn't do anything interesting right now.
- ctiRepatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_generic));
+ ctiRepatchCallByReturnAddress(returnAddress, reinterpret_cast<void*>(Interpreter::cti_op_get_by_id_self_fail));
// Repatch the offset into the propoerty map to load from, then repatch the Structure to look for.
X86Assembler::repatchDisplacement(reinterpret_cast<intptr_t>(info.hotPathBegin) + repatchOffsetGetByIdPropertyMapOffset, cachedOffset * sizeof(JSValue*));
diff --git a/JavaScriptCore/jit/JIT.h b/JavaScriptCore/jit/JIT.h
index 8f3e649..f72d33d 100644
--- a/JavaScriptCore/jit/JIT.h
+++ b/JavaScriptCore/jit/JIT.h
@@ -109,7 +109,7 @@
struct CallLinkInfo;
struct Instruction;
struct OperandTypes;
- struct PrototypeStructureList;
+ struct PolymorphicAccessStructureList;
struct StructureStubInfo;
typedef JSValue* (SFX_CALL *CTIHelper_j)(CTI_ARGS);
@@ -320,7 +320,12 @@
}
#if USE(CTI_REPATCH_PIC)
- static void compileGetByIdProtoList(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, StructureStubInfo* stubInfo, PrototypeStructureList* prototypeStructureList, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset)
+ static void compileGetByIdSelfList(JSGlobalData* globalData, CodeBlock* codeBlock, StructureStubInfo* stubInfo, PolymorphicAccessStructureList* polymorphicStructures, int currentIndex, Structure* structure, size_t cachedOffset)
+ {
+ JIT jit(globalData, codeBlock);
+ jit.privateCompileGetByIdSelfList(stubInfo, polymorphicStructures, currentIndex, structure, cachedOffset);
+ }
+ static void compileGetByIdProtoList(JSGlobalData* globalData, CallFrame* callFrame, CodeBlock* codeBlock, StructureStubInfo* stubInfo, PolymorphicAccessStructureList* prototypeStructureList, int currentIndex, Structure* structure, Structure* prototypeStructure, size_t cachedOffset)
{
JIT jit(globalData, codeBlock);
jit.privateCompileGetByIdProtoList(stubInfo, prototypeStructureList, currentIndex, structure, prototypeStructure, cachedOffset, callFrame);
@@ -379,7 +384,8 @@
void privateCompileGetByIdSelf(Structure*, size_t cachedOffset, void* returnAddress);
void privateCompileGetByIdProto(Structure*, Structure* prototypeStructure, size_t cachedOffset, void* returnAddress, CallFrame* callFrame);
#if USE(CTI_REPATCH_PIC)
- void privateCompileGetByIdProtoList(StructureStubInfo*, PrototypeStructureList*, int, Structure*, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame);
+ void privateCompileGetByIdSelfList(StructureStubInfo*, PolymorphicAccessStructureList*, int, Structure*, size_t cachedOffset);
+ void privateCompileGetByIdProtoList(StructureStubInfo*, PolymorphicAccessStructureList*, int, Structure*, Structure* prototypeStructure, size_t cachedOffset, CallFrame* callFrame);
#endif
void privateCompileGetByIdChain(Structure*, StructureChain*, size_t count, size_t cachedOffset, void* returnAddress, CallFrame* callFrame);
void privateCompilePutByIdReplace(Structure*, size_t cachedOffset, void* returnAddress);