GetById list caching should use something object-oriented rather than PolymorphicAcce...
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Mar 2014 03:42:42 +0000 (03:42 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Mar 2014 03:42:42 +0000 (03:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=129778

Reviewed by Geoffrey Garen.

Also deduplicate the GetById getter call caching. Also add some small tests for
get stubs.

This change reduces the amount of code involved in GetById access caching and it
creates data structures that can serve as an elegant scaffold for introducing other
kinds of caches or improving current caching styles. It will definitely make getter
performance improvements easier to implement.

* CMakeLists.txt:
* GNUmakefile.list.am:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::printGetByIdCacheStatus):
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeForStubInfo):
* bytecode/PolymorphicGetByIdList.cpp: Added.
(JSC::GetByIdAccess::GetByIdAccess):
(JSC::GetByIdAccess::~GetByIdAccess):
(JSC::GetByIdAccess::fromStructureStubInfo):
(JSC::GetByIdAccess::visitWeak):
(JSC::PolymorphicGetByIdList::PolymorphicGetByIdList):
(JSC::PolymorphicGetByIdList::from):
(JSC::PolymorphicGetByIdList::~PolymorphicGetByIdList):
(JSC::PolymorphicGetByIdList::currentSlowPathTarget):
(JSC::PolymorphicGetByIdList::addAccess):
(JSC::PolymorphicGetByIdList::isFull):
(JSC::PolymorphicGetByIdList::isAlmostFull):
(JSC::PolymorphicGetByIdList::didSelfPatching):
(JSC::PolymorphicGetByIdList::visitWeak):
* bytecode/PolymorphicGetByIdList.h: Added.
(JSC::GetByIdAccess::GetByIdAccess):
(JSC::GetByIdAccess::isSet):
(JSC::GetByIdAccess::operator!):
(JSC::GetByIdAccess::type):
(JSC::GetByIdAccess::structure):
(JSC::GetByIdAccess::chain):
(JSC::GetByIdAccess::chainCount):
(JSC::GetByIdAccess::stubRoutine):
(JSC::GetByIdAccess::doesCalls):
(JSC::PolymorphicGetByIdList::isEmpty):
(JSC::PolymorphicGetByIdList::size):
(JSC::PolymorphicGetByIdList::at):
(JSC::PolymorphicGetByIdList::operator[]):
* bytecode/StructureStubInfo.cpp:
(JSC::StructureStubInfo::deref):
(JSC::StructureStubInfo::visitWeakReferences):
* bytecode/StructureStubInfo.h:
(JSC::isGetByIdAccess):
(JSC::StructureStubInfo::initGetByIdList):
* jit/Repatch.cpp:
(JSC::generateGetByIdStub):
(JSC::tryCacheGetByID):
(JSC::patchJumpToGetByIdStub):
(JSC::tryBuildGetByIDList):
(JSC::tryBuildPutByIdList):
* tests/stress/getter.js: Added.
(foo):
(.o):
* tests/stress/polymorphic-prototype-accesses.js: Added.
(Foo):
(Bar):
(foo):
* tests/stress/prototype-getter.js: Added.
(Foo):
(foo):
* tests/stress/simple-prototype-accesses.js: Added.
(Foo):
(foo):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@165459 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/GNUmakefile.list.am
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/StructureStubInfo.cpp
Source/JavaScriptCore/bytecode/StructureStubInfo.h
Source/JavaScriptCore/jit/Repatch.cpp
Source/JavaScriptCore/tests/stress/getter.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/polymorphic-prototype-accesses.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/prototype-getter.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/simple-prototype-accesses.js [new file with mode: 0644]

index 0c20cdf..4eb9a8e 100644 (file)
@@ -78,6 +78,7 @@ set(JavaScriptCore_SOURCES
     bytecode/LazyOperandValueProfile.cpp
     bytecode/MethodOfGettingAValueProfile.cpp
     bytecode/Opcode.cpp
+    bytecode/PolymorphicGetByIdList.cpp
     bytecode/PolymorphicPutByIdList.cpp
     bytecode/PreciseJumpTargets.cpp
     bytecode/ProfiledCodeBlockJettisoningWatchpoint.cpp
index 32596b8..448f965 100644 (file)
@@ -1,3 +1,80 @@
+2014-03-10  Filip Pizlo  <fpizlo@apple.com>
+
+        GetById list caching should use something object-oriented rather than PolymorphicAccessStructureList
+        https://bugs.webkit.org/show_bug.cgi?id=129778
+
+        Reviewed by Geoffrey Garen.
+        
+        Also deduplicate the GetById getter call caching. Also add some small tests for
+        get stubs.
+        
+        This change reduces the amount of code involved in GetById access caching and it
+        creates data structures that can serve as an elegant scaffold for introducing other
+        kinds of caches or improving current caching styles. It will definitely make getter
+        performance improvements easier to implement.
+
+        * CMakeLists.txt:
+        * GNUmakefile.list.am:
+        * JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * bytecode/CodeBlock.cpp:
+        (JSC::CodeBlock::printGetByIdCacheStatus):
+        * bytecode/GetByIdStatus.cpp:
+        (JSC::GetByIdStatus::computeForStubInfo):
+        * bytecode/PolymorphicGetByIdList.cpp: Added.
+        (JSC::GetByIdAccess::GetByIdAccess):
+        (JSC::GetByIdAccess::~GetByIdAccess):
+        (JSC::GetByIdAccess::fromStructureStubInfo):
+        (JSC::GetByIdAccess::visitWeak):
+        (JSC::PolymorphicGetByIdList::PolymorphicGetByIdList):
+        (JSC::PolymorphicGetByIdList::from):
+        (JSC::PolymorphicGetByIdList::~PolymorphicGetByIdList):
+        (JSC::PolymorphicGetByIdList::currentSlowPathTarget):
+        (JSC::PolymorphicGetByIdList::addAccess):
+        (JSC::PolymorphicGetByIdList::isFull):
+        (JSC::PolymorphicGetByIdList::isAlmostFull):
+        (JSC::PolymorphicGetByIdList::didSelfPatching):
+        (JSC::PolymorphicGetByIdList::visitWeak):
+        * bytecode/PolymorphicGetByIdList.h: Added.
+        (JSC::GetByIdAccess::GetByIdAccess):
+        (JSC::GetByIdAccess::isSet):
+        (JSC::GetByIdAccess::operator!):
+        (JSC::GetByIdAccess::type):
+        (JSC::GetByIdAccess::structure):
+        (JSC::GetByIdAccess::chain):
+        (JSC::GetByIdAccess::chainCount):
+        (JSC::GetByIdAccess::stubRoutine):
+        (JSC::GetByIdAccess::doesCalls):
+        (JSC::PolymorphicGetByIdList::isEmpty):
+        (JSC::PolymorphicGetByIdList::size):
+        (JSC::PolymorphicGetByIdList::at):
+        (JSC::PolymorphicGetByIdList::operator[]):
+        * bytecode/StructureStubInfo.cpp:
+        (JSC::StructureStubInfo::deref):
+        (JSC::StructureStubInfo::visitWeakReferences):
+        * bytecode/StructureStubInfo.h:
+        (JSC::isGetByIdAccess):
+        (JSC::StructureStubInfo::initGetByIdList):
+        * jit/Repatch.cpp:
+        (JSC::generateGetByIdStub):
+        (JSC::tryCacheGetByID):
+        (JSC::patchJumpToGetByIdStub):
+        (JSC::tryBuildGetByIDList):
+        (JSC::tryBuildPutByIdList):
+        * tests/stress/getter.js: Added.
+        (foo):
+        (.o):
+        * tests/stress/polymorphic-prototype-accesses.js: Added.
+        (Foo):
+        (Bar):
+        (foo):
+        * tests/stress/prototype-getter.js: Added.
+        (Foo):
+        (foo):
+        * tests/stress/simple-prototype-accesses.js: Added.
+        (Foo):
+        (foo):
+
 2014-03-11  Mark Hahnenberg  <mhahnenberg@apple.com>
 
         MarkedBlocks that are "full enough" shouldn't be swept after EdenCollections
index 8dfb785..6a7fe97 100644 (file)
@@ -175,6 +175,8 @@ javascriptcore_sources += \
        Source/JavaScriptCore/bytecode/Operands.h \
        Source/JavaScriptCore/bytecode/OperandsInlines.h \
        Source/JavaScriptCore/bytecode/PolymorphicAccessStructureList.h \
+       Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.cpp \
+       Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.h \
        Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.cpp \
        Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.h \
        Source/JavaScriptCore/bytecode/PreciseJumpTargets.cpp \
index b4c299c..50a7c1c 100644 (file)
     <ClCompile Include="..\bytecode\LazyOperandValueProfile.cpp" />
     <ClCompile Include="..\bytecode\MethodOfGettingAValueProfile.cpp" />
     <ClCompile Include="..\bytecode\Opcode.cpp" />
+    <ClCompile Include="..\bytecode\PolymorphicGetByIdList.cpp" />
     <ClCompile Include="..\bytecode\PolymorphicPutByIdList.cpp" />
     <ClCompile Include="..\bytecode\ProfiledCodeBlockJettisoningWatchpoint.cpp" />
     <ClCompile Include="..\bytecode\PreciseJumpTargets.cpp" />
     <ClInclude Include="..\bytecode\MethodOfGettingAValueProfile.h" />
     <ClInclude Include="..\bytecode\Opcode.h" />
     <ClInclude Include="..\bytecode\Operands.h" />
+    <ClInclude Include="..\bytecode\PolymorphicGetByIdList.h" />
     <ClInclude Include="..\bytecode\PolymorphicPutByIdList.h" />
     <ClInclude Include="..\bytecode\ProfiledCodeBlockJettisoningWatchpoint.h" />
     <ClInclude Include="..\bytecode\PreciseJumpTargets.h" />
index 9c49578..7132c50 100644 (file)
                0F48532A187DFDEC0083B687 /* FTLRecoveryOpcode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F485326187DFDEC0083B687 /* FTLRecoveryOpcode.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F493AFA16D0CAD30084508B /* SourceProvider.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F493AF816D0CAD10084508B /* SourceProvider.cpp */; };
                0F4B94DC17B9F07500DD03A4 /* TypedArrayInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4B94DB17B9F07500DD03A4 /* TypedArrayInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0F4CED5E18CEA7AB00802FE0 /* PolymorphicGetByIdList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F4CED5C18CEA7AB00802FE0 /* PolymorphicGetByIdList.cpp */; };
+               0F4CED5F18CEA7AB00802FE0 /* PolymorphicGetByIdList.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4CED5D18CEA7AB00802FE0 /* PolymorphicGetByIdList.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F4F29DF18B6AD1C0057BC15 /* DFGStaticExecutionCountEstimationPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F4F29DD18B6AD1C0057BC15 /* DFGStaticExecutionCountEstimationPhase.cpp */; };
                0F4F29E018B6AD1C0057BC15 /* DFGStaticExecutionCountEstimationPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4F29DE18B6AD1C0057BC15 /* DFGStaticExecutionCountEstimationPhase.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F5541B11613C1FB00CE3E25 /* SpecialPointer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F5541AF1613C1FB00CE3E25 /* SpecialPointer.cpp */; };
                0F485326187DFDEC0083B687 /* FTLRecoveryOpcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLRecoveryOpcode.h; path = ftl/FTLRecoveryOpcode.h; sourceTree = "<group>"; };
                0F493AF816D0CAD10084508B /* SourceProvider.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SourceProvider.cpp; sourceTree = "<group>"; };
                0F4B94DB17B9F07500DD03A4 /* TypedArrayInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TypedArrayInlines.h; sourceTree = "<group>"; };
+               0F4CED5C18CEA7AB00802FE0 /* PolymorphicGetByIdList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PolymorphicGetByIdList.cpp; sourceTree = "<group>"; };
+               0F4CED5D18CEA7AB00802FE0 /* PolymorphicGetByIdList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PolymorphicGetByIdList.h; sourceTree = "<group>"; };
                0F4F29DD18B6AD1C0057BC15 /* DFGStaticExecutionCountEstimationPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGStaticExecutionCountEstimationPhase.cpp; path = dfg/DFGStaticExecutionCountEstimationPhase.cpp; sourceTree = "<group>"; };
                0F4F29DE18B6AD1C0057BC15 /* DFGStaticExecutionCountEstimationPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGStaticExecutionCountEstimationPhase.h; path = dfg/DFGStaticExecutionCountEstimationPhase.h; sourceTree = "<group>"; };
                0F5541AF1613C1FB00CE3E25 /* SpecialPointer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SpecialPointer.cpp; sourceTree = "<group>"; };
                969A078F0ED1D3AE00F1F681 /* bytecode */ = {
                        isa = PBXGroup;
                        children = (
-                               6529FB3118B2D99900C61102 /* BytecodeList.json */,
                                0F8335B41639C1E3001443B5 /* ArrayAllocationProfile.cpp */,
                                0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */,
                                0F63945115D07051006A597C /* ArrayProfile.cpp */,
                                C2FCAE0C17A9C24E0034C735 /* BytecodeBasicBlock.cpp */,
                                C2FCAE0D17A9C24E0034C735 /* BytecodeBasicBlock.h */,
                                0F21C27E14BEAA8000ADC64B /* BytecodeConventions.h */,
+                               6529FB3118B2D99900C61102 /* BytecodeList.json */,
                                C2FCAE0E17A9C24E0034C735 /* BytecodeLivenessAnalysis.cpp */,
                                C2FCAE0F17A9C24E0034C735 /* BytecodeLivenessAnalysis.h */,
                                0F666EBE183566F900D017F1 /* BytecodeLivenessAnalysisInlines.h */,
                                0F2BDC2B151FDE8B00CD8910 /* Operands.h */,
                                A70447E917A0BD4600F5898E /* OperandsInlines.h */,
                                0F34B14B16D43E0C001CDA5A /* PolymorphicAccessStructureList.h */,
+                               0F4CED5C18CEA7AB00802FE0 /* PolymorphicGetByIdList.cpp */,
+                               0F4CED5D18CEA7AB00802FE0 /* PolymorphicGetByIdList.h */,
                                0F9FC8BF14E1B5FB00D52AE0 /* PolymorphicPutByIdList.cpp */,
                                0F9FC8C014E1B5FB00D52AE0 /* PolymorphicPutByIdList.h */,
                                0F98205D16BFE37F00240D02 /* PreciseJumpTargets.cpp */,
                                0FC0977114693AF500CF2442 /* DFGOSRExitCompiler.h in Headers */,
                                0F7025AA1714B0FC00382C0E /* DFGOSRExitCompilerCommon.h in Headers */,
                                0FEFC9AB1681A3B600567F53 /* DFGOSRExitJumpPlaceholder.h in Headers */,
+                               0F4CED5F18CEA7AB00802FE0 /* PolymorphicGetByIdList.h in Headers */,
                                0F235BEE17178E7300690C7F /* DFGOSRExitPreparation.h in Headers */,
                                0FFFC95C14EF90AF00C72532 /* DFGPhase.h in Headers */,
                                A78A977B179738B8009DF744 /* DFGPlan.h in Headers */,
                                0F0B839C14BCF46300885B4F /* LLIntThunks.cpp in Sources */,
                                0FCEFACD1805E75500472CE4 /* LLVMAPI.cpp in Sources */,
                                A7E5AB371799E4B200D2833D /* LLVMDisassembler.cpp in Sources */,
+                               0F4CED5E18CEA7AB00802FE0 /* PolymorphicGetByIdList.cpp in Sources */,
                                14469DDE107EC7E700650446 /* Lookup.cpp in Sources */,
                                0F4680CC14BBB17A00BFE272 /* LowLevelInterpreter.cpp in Sources */,
                                14B723B212D7DA46003BD5ED /* MachineStackMarker.cpp in Sources */,
index 7ded27c..95fbe38 100644 (file)
@@ -50,6 +50,7 @@
 #include "LLIntEntrypoint.h"
 #include "LowLevelInterpreter.h"
 #include "JSCInlines.h"
+#include "PolymorphicGetByIdList.h"
 #include "PolymorphicPutByIdList.h"
 #include "ReduceWhitespace.h"
 #include "Repatch.h"
@@ -341,8 +342,7 @@ void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int l
             Structure* baseStructure = 0;
             Structure* prototypeStructure = 0;
             StructureChain* chain = 0;
-            PolymorphicAccessStructureList* structureList = 0;
-            int listSize = 0;
+            PolymorphicGetByIdList* list = 0;
             
             switch (stubInfo.accessType) {
             case access_get_by_id_self:
@@ -354,10 +354,9 @@ void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int l
                 baseStructure = stubInfo.u.getByIdChain.baseObjectStructure.get();
                 chain = stubInfo.u.getByIdChain.chain.get();
                 break;
-            case access_get_by_id_self_list:
-                out.printf("self_list");
-                structureList = stubInfo.u.getByIdSelfList.structureList;
-                listSize = stubInfo.u.getByIdSelfList.listSize;
+            case access_get_by_id_list:
+                out.printf("list");
+                list = stubInfo.u.getByIdList.list;
                 break;
             case access_unset:
                 out.printf("unset");
@@ -382,16 +381,16 @@ void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int l
                 dumpChain(out, exec, chain, ident);
             }
             
-            if (structureList) {
-                out.printf(", list = %p: [", structureList);
-                for (int i = 0; i < listSize; ++i) {
+            if (list) {
+                out.printf(", list = %p: [", list);
+                for (unsigned i = 0; i < list->size(); ++i) {
                     if (i)
                         out.printf(", ");
                     out.printf("(");
-                    dumpStructure(out, "base", exec, structureList->list[i].base.get(), ident);
-                    if (structureList->list[i].chain.get()) {
+                    dumpStructure(out, "base", exec, list->at(i).structure(), ident);
+                    if (list->at(i).chain()) {
                         out.printf(", ");
-                        dumpChain(out, exec, structureList->list[i].chain.get(), ident);
+                        dumpChain(out, exec, list->at(i).chain(), ident);
                     }
                     out.printf(")");
                 }
index 40872a2..a2142eb 100644 (file)
 #include "GetByIdStatus.h"
 
 #include "CodeBlock.h"
+#include "JSCInlines.h"
 #include "JSScope.h"
 #include "LLIntData.h"
 #include "LowLevelInterpreter.h"
-#include "JSCInlines.h"
+#include "PolymorphicGetByIdList.h"
 #include <wtf/ListDump.h>
 
 namespace JSC {
@@ -174,13 +175,11 @@ GetByIdStatus GetByIdStatus::computeForStubInfo(
     if (stubInfo->resetByGC)
         return GetByIdStatus(TakesSlowPath, true);
 
-    PolymorphicAccessStructureList* list = 0;
-    int listSize = 0;
-    if (stubInfo->accessType == access_get_by_id_self_list) {
-        list = stubInfo->u.getByIdSelfList.structureList;
-        listSize = stubInfo->u.getByIdSelfList.listSize;
-        for (int i = 0; i < listSize; ++i) {
-            if (!list->list[i].isDirect)
+    PolymorphicGetByIdList* list = 0;
+    if (stubInfo->accessType == access_get_by_id_list) {
+        list = stubInfo->u.getByIdList.list;
+        for (unsigned i = 0; i < list->size(); ++i) {
+            if (list->at(i).doesCalls())
                 return GetByIdStatus(MakesCalls, true);
         }
     }
@@ -214,18 +213,18 @@ GetByIdStatus GetByIdStatus::computeForStubInfo(
         return result;
     }
         
-    case access_get_by_id_self_list: {
-        for (int listIndex = 0; listIndex < listSize; ++listIndex) {
-            ASSERT(list->list[listIndex].isDirect);
+    case access_get_by_id_list: {
+        for (unsigned listIndex = 0; listIndex < list->size(); ++listIndex) {
+            ASSERT(!list->at(listIndex).doesCalls());
             
-            Structure* structure = list->list[listIndex].base.get();
+            Structure* structure = list->at(listIndex).structure();
             if (structure->takesSlowPathInDFGForImpureProperty())
                 return GetByIdStatus(TakesSlowPath, true);
             
-            if (list->list[listIndex].chain.get()) {
+            if (list->at(listIndex).chain()) {
                 RefPtr<IntendedStructureChain> chain = adoptRef(new IntendedStructureChain(
-                    profiledBlock, structure, list->list[listIndex].chain.get(),
-                    list->list[listIndex].count));
+                    profiledBlock, structure, list->at(listIndex).chain(),
+                    list->at(listIndex).chainCount()));
                 if (!result.computeForChain(profiledBlock, uid, chain))
                     return GetByIdStatus(TakesSlowPath, true);
                 continue;
diff --git a/Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.cpp b/Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.cpp
new file mode 100644 (file)
index 0000000..fadf5fd
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "PolymorphicGetByIdList.h"
+
+#if ENABLE(JIT)
+
+#include "CodeBlock.h"
+#include "Heap.h"
+#include "JSCInlines.h"
+#include "StructureStubInfo.h"
+
+namespace JSC {
+
+GetByIdAccess::GetByIdAccess(
+    VM& vm, JSCell* owner, AccessType type, PassRefPtr<JITStubRoutine> stubRoutine,
+    Structure* structure, StructureChain* chain, unsigned chainCount)
+    : m_type(type)
+    , m_chainCount(chainCount)
+    , m_structure(vm, owner, structure)
+    , m_stubRoutine(stubRoutine)
+{
+    if (chain)
+        m_chain.set(vm, owner, chain);
+}
+
+GetByIdAccess::~GetByIdAccess()
+{
+}
+
+GetByIdAccess GetByIdAccess::fromStructureStubInfo(StructureStubInfo& stubInfo)
+{
+    MacroAssemblerCodePtr initialSlowPath =
+        stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase);
+    
+    GetByIdAccess result;
+    
+    switch (stubInfo.accessType) {
+    case access_get_by_id_self:
+        result.m_type = SimpleInline;
+        result.m_structure.copyFrom(stubInfo.u.getByIdSelf.baseObjectStructure);
+        result.m_stubRoutine = JITStubRoutine::createSelfManagedRoutine(initialSlowPath);
+        break;
+        
+    case access_get_by_id_chain:
+        result.m_structure.copyFrom(stubInfo.u.getByIdChain.baseObjectStructure);
+        result.m_chain.copyFrom(stubInfo.u.getByIdChain.chain);
+        result.m_chainCount = stubInfo.u.getByIdChain.count;
+        result.m_stubRoutine = stubInfo.stubRoutine;
+        if (stubInfo.u.getByIdChain.isDirect)
+            result.m_type = SimpleStub;
+        else
+            result.m_type = Getter;
+        break;
+        
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+    
+    return result;
+}
+
+bool GetByIdAccess::visitWeak() const
+{
+    if (m_structure && !Heap::isMarked(m_structure.get()))
+        return false;
+    if (m_chain && !Heap::isMarked(m_chain.get()))
+        return false;
+    return true;
+}
+
+PolymorphicGetByIdList::PolymorphicGetByIdList(StructureStubInfo& stubInfo)
+{
+    if (stubInfo.accessType == access_unset)
+        return;
+    
+    m_list.append(GetByIdAccess::fromStructureStubInfo(stubInfo));
+}
+
+PolymorphicGetByIdList* PolymorphicGetByIdList::from(StructureStubInfo& stubInfo)
+{
+    if (stubInfo.accessType == access_get_by_id_list)
+        return stubInfo.u.getByIdList.list;
+    
+    ASSERT(
+        stubInfo.accessType == access_get_by_id_self
+        || stubInfo.accessType == access_get_by_id_chain
+        || stubInfo.accessType == access_unset);
+    
+    PolymorphicGetByIdList* result = new PolymorphicGetByIdList(stubInfo);
+    
+    stubInfo.initGetByIdList(result);
+    
+    return result;
+}
+
+PolymorphicGetByIdList::~PolymorphicGetByIdList() { }
+
+MacroAssemblerCodePtr PolymorphicGetByIdList::currentSlowPathTarget(
+    StructureStubInfo& stubInfo) const
+{
+    if (isEmpty())
+        return stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase);
+    return m_list.last().stubRoutine()->code().code();
+}
+
+void PolymorphicGetByIdList::addAccess(const GetByIdAccess& access)
+{
+    ASSERT(!isFull());
+    // Make sure that the resizing optimizes for space, not time.
+    m_list.resize(m_list.size() + 1);
+    m_list.last() = access;
+}
+
+bool PolymorphicGetByIdList::isFull() const
+{
+    ASSERT(size() <= POLYMORPHIC_LIST_CACHE_SIZE);
+    return size() == POLYMORPHIC_LIST_CACHE_SIZE;
+}
+
+bool PolymorphicGetByIdList::isAlmostFull() const
+{
+    ASSERT(size() <= POLYMORPHIC_LIST_CACHE_SIZE);
+    return size() >= POLYMORPHIC_LIST_CACHE_SIZE - 1;
+}
+
+bool PolymorphicGetByIdList::didSelfPatching() const
+{
+    for (unsigned i = size(); i--;) {
+        if (at(i).type() == GetByIdAccess::SimpleInline)
+            return true;
+    }
+    return false;
+}
+
+bool PolymorphicGetByIdList::visitWeak() const
+{
+    for (unsigned i = size(); i--;) {
+        if (!at(i).visitWeak())
+            return false;
+    }
+    return true;
+}
+
+} // namespace JSC
+
+#endif // ENABLE(JIT)
+
+
diff --git a/Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.h b/Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.h
new file mode 100644 (file)
index 0000000..8f4a164
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef PolymorphicGetByIdList_h
+#define PolymorphicGetByIdList_h
+
+#if ENABLE(JIT)
+
+#include "CodeOrigin.h"
+#include "MacroAssembler.h"
+#include "Opcode.h"
+#include "Structure.h"
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+class CodeBlock;
+struct StructureStubInfo;
+
+class GetByIdAccess {
+public:
+    enum AccessType {
+        Invalid,
+        SimpleInline, // This is the patched inline access.
+        SimpleStub, // This is a stub.
+        Getter,
+    };
+    
+    GetByIdAccess()
+        : m_type(Invalid)
+        , m_chainCount(0)
+    {
+    }
+    
+    GetByIdAccess(
+        VM&, JSCell* owner, AccessType, PassRefPtr<JITStubRoutine>, Structure*,
+        StructureChain* = 0, unsigned chainCount = 0);
+    
+    ~GetByIdAccess();
+    
+    static GetByIdAccess fromStructureStubInfo(StructureStubInfo&);
+    
+    bool isSet() const { return m_type != Invalid; }
+    bool operator!() const { return !isSet(); }
+    
+    AccessType type() const { return m_type; }
+    
+    Structure* structure() const { return m_structure.get(); }
+    
+    StructureChain* chain() const { return m_chain.get(); }
+    unsigned chainCount() const { return m_chainCount; }
+    
+    JITStubRoutine* stubRoutine() const
+    {
+        ASSERT(isSet());
+        return m_stubRoutine.get();
+    }
+    
+    bool doesCalls() const { return type() == Getter; }
+    
+    bool visitWeak() const;
+
+private:
+    friend class CodeBlock;
+    
+    AccessType m_type;
+    unsigned m_chainCount;
+    WriteBarrier<Structure> m_structure;
+    WriteBarrier<StructureChain> m_chain;
+    RefPtr<JITStubRoutine> m_stubRoutine;
+};
+
+class PolymorphicGetByIdList {
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    // Either creates a new polymorphic get list, or returns the one that is already in
+    // place.
+    static PolymorphicGetByIdList* from(StructureStubInfo&);
+    
+    ~PolymorphicGetByIdList();
+    
+    MacroAssemblerCodePtr currentSlowPathTarget(StructureStubInfo& stubInfo) const;
+    
+    void addAccess(const GetByIdAccess&);
+    
+    bool isEmpty() const { return m_list.isEmpty(); }
+    unsigned size() const { return m_list.size(); }
+    bool isFull() const;
+    bool isAlmostFull() const; // True if adding an element would make isFull() true.
+    const GetByIdAccess& at(unsigned i) const { return m_list[i]; }
+    const GetByIdAccess& operator[](unsigned i) const { return m_list[i]; }
+    
+    bool didSelfPatching() const; // Are any of the accesses SimpleInline?
+    
+    bool visitWeak() const;
+
+private:
+    friend class CodeBlock;
+    
+    PolymorphicGetByIdList(StructureStubInfo&);
+    
+    Vector<GetByIdAccess, 2> m_list;
+};
+
+} // namespace JSC
+
+#endif // ENABLE(JIT)
+
+#endif // PolymorphicGetByIdList_h
+
index 28ce412..7b0878e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include "StructureStubInfo.h"
 
 #include "JSObject.h"
+#include "PolymorphicGetByIdList.h"
 #include "PolymorphicPutByIdList.h"
 
-
 namespace JSC {
 
 #if ENABLE(JIT)
 void StructureStubInfo::deref()
 {
     switch (accessType) {
-    case access_get_by_id_self_list: {
-        PolymorphicAccessStructureList* polymorphicStructures = u.getByIdSelfList.structureList;
-        delete polymorphicStructures;
+    case access_get_by_id_list: {
+        delete u.getByIdList.list;
         return;
     }
     case access_put_by_id_list:
@@ -74,9 +73,8 @@ bool StructureStubInfo::visitWeakReferences()
             || !Heap::isMarked(u.getByIdChain.chain.get()))
             return false;
         break;
-    case access_get_by_id_self_list: {
-        PolymorphicAccessStructureList* polymorphicStructures = u.getByIdSelfList.structureList;
-        if (!polymorphicStructures->visitWeak(u.getByIdSelfList.listSize))
+    case access_get_by_id_list: {
+        if (!u.getByIdList.list->visitWeak())
             return false;
         break;
     }
index 52b3ed0..117cea9 100644 (file)
@@ -42,12 +42,13 @@ namespace JSC {
 
 #if ENABLE(JIT)
 
+class PolymorphicGetByIdList;
 class PolymorphicPutByIdList;
 
 enum AccessType {
     access_get_by_id_self,
     access_get_by_id_chain,
-    access_get_by_id_self_list,
+    access_get_by_id_list,
     access_put_by_id_transition_normal,
     access_put_by_id_transition_direct,
     access_put_by_id_replace,
@@ -61,7 +62,7 @@ inline bool isGetByIdAccess(AccessType accessType)
     switch (accessType) {
     case access_get_by_id_self:
     case access_get_by_id_chain:
-    case access_get_by_id_self_list:
+    case access_get_by_id_list:
         return true;
     default:
         return false;
@@ -116,13 +117,10 @@ struct StructureStubInfo {
         u.getByIdChain.isDirect = isDirect;
     }
 
-    void initGetByIdSelfList(PolymorphicAccessStructureList* structureList, int listSize, bool didSelfPatching = false)
+    void initGetByIdList(PolymorphicGetByIdList* list)
     {
-        accessType = access_get_by_id_self_list;
-
-        u.getByIdSelfList.structureList = structureList;
-        u.getByIdSelfList.listSize = listSize;
-        u.getByIdSelfList.didSelfPatching = didSelfPatching;
+        accessType = access_get_by_id_list;
+        u.getByIdList.list = list;
     }
 
     // PutById*
@@ -233,14 +231,8 @@ struct StructureStubInfo {
             bool isDirect : 1;
         } getByIdChain;
         struct {
-            PolymorphicAccessStructureList* structureList;
-            int listSize : 31;
-            bool didSelfPatching : 1;
-        } getByIdSelfList;
-        struct {
-            PolymorphicAccessStructureList* structureList;
-            int listSize;
-        } getByIdProtoList;
+            PolymorphicGetByIdList* list;
+        } getByIdList;
         struct {
             WriteBarrierBase<Structure> previousStructure;
             WriteBarrierBase<Structure> structure;
index 580174a..41f2b96 100644 (file)
@@ -37,6 +37,7 @@
 #include "JITInlines.h"
 #include "LinkBuffer.h"
 #include "JSCInlines.h"
+#include "PolymorphicGetByIdList.h"
 #include "PolymorphicPutByIdList.h"
 #include "RepatchBuffer.h"
 #include "ScratchRegisterAllocator.h"
@@ -219,13 +220,11 @@ static void linkRestoreScratch(LinkBuffer& patchBuffer, bool needToRestoreScratc
     linkRestoreScratch(patchBuffer, needToRestoreScratch, success, fail, failureCases, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone), stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase));
 }
 
-enum ProtoChainGenerationResult {
-    ProtoChainGenerationFailed,
-    ProtoChainGenerationSucceeded
-};
-
-static ProtoChainGenerationResult generateProtoChainAccessStub(ExecState*, const PropertySlot&, const Identifier&, StructureStubInfo&, StructureChain*, size_t, PropertyOffset, Structure*, CodeLocationLabel, CodeLocationLabel, RefPtr<JITStubRoutine>&) WARN_UNUSED_RETURN;
-static ProtoChainGenerationResult generateProtoChainAccessStub(ExecState* exec, const PropertySlot& slot, const Identifier& propertyName, StructureStubInfo& stubInfo, StructureChain* chain, size_t count, PropertyOffset offset, Structure* structure, CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel, RefPtr<JITStubRoutine>& stubRoutine)
+static void generateGetByIdStub(
+    ExecState* exec, const PropertySlot& slot, const Identifier& propertyName,
+    StructureStubInfo& stubInfo, StructureChain* chain, size_t count, PropertyOffset offset,
+    Structure* structure, CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel,
+    RefPtr<JITStubRoutine>& stubRoutine)
 {
     VM* vm = &exec->vm();
     GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
@@ -235,8 +234,7 @@ static ProtoChainGenerationResult generateProtoChainAccessStub(ExecState* exec,
     GPRReg resultGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
     GPRReg scratchGPR = TempRegisterSet(stubInfo.patch.usedRegisters).getFreeGPR();
     bool needToRestoreScratch = scratchGPR == InvalidGPRReg;
-    if (needToRestoreScratch && !slot.isCacheableValue())
-        return ProtoChainGenerationFailed;
+    RELEASE_ASSERT(!needToRestoreScratch || slot.isCacheableValue());
     
     CCallHelpers stubJit(&exec->vm(), exec->codeBlock());
     if (needToRestoreScratch) {
@@ -261,41 +259,51 @@ static ProtoChainGenerationResult generateProtoChainAccessStub(ExecState* exec,
         vm->registerWatchpointForImpureProperty(propertyName, stubInfo.addWatchpoint(codeBlock));
 
     Structure* currStructure = structure;
-    WriteBarrier<Structure>* it = chain->head();
     JSObject* protoObject = 0;
-    for (unsigned i = 0; i < count; ++i, ++it) {
-        protoObject = asObject(currStructure->prototypeForLookup(exec));
-        Structure* protoStructure = protoObject->structure();
-        if (protoStructure->typeInfo().newImpurePropertyFiresWatchpoints())
-            vm->registerWatchpointForImpureProperty(propertyName, stubInfo.addWatchpoint(codeBlock));
-        addStructureTransitionCheck(
-            protoObject, protoStructure, codeBlock, stubInfo, stubJit,
-            failureCases, scratchGPR);
-        currStructure = it->get();
+    if (chain) {
+        WriteBarrier<Structure>* it = chain->head();
+        for (unsigned i = 0; i < count; ++i, ++it) {
+            protoObject = asObject(currStructure->prototypeForLookup(exec));
+            Structure* protoStructure = protoObject->structure();
+            if (protoStructure->typeInfo().newImpurePropertyFiresWatchpoints())
+                vm->registerWatchpointForImpureProperty(propertyName, stubInfo.addWatchpoint(codeBlock));
+            addStructureTransitionCheck(
+                protoObject, protoStructure, codeBlock, stubInfo, stubJit,
+                failureCases, scratchGPR);
+            currStructure = it->get();
+        }
     }
     
     bool isAccessor = slot.isCacheableGetter() || slot.isCacheableCustom();
-    if (isAccessor)
-        stubJit.move(baseGPR, scratchGPR);
-
+    
+    GPRReg baseForAccessGPR;
+    if (chain) {
+        stubJit.move(MacroAssembler::TrustedImmPtr(protoObject), scratchGPR);
+        baseForAccessGPR = scratchGPR;
+    } else
+        baseForAccessGPR = baseGPR;
+    
+    GPRReg loadedValueGPR = InvalidGPRReg;
     if (!slot.isCacheableCustom()) {
-        if (isInlineOffset(offset)) {
-#if USE(JSVALUE64)
-            stubJit.load64(protoObject->locationForOffset(offset), resultGPR);
-#elif USE(JSVALUE32_64)
-            stubJit.move(MacroAssembler::TrustedImmPtr(protoObject->locationForOffset(offset)), resultGPR);
-            stubJit.load32(MacroAssembler::Address(resultGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR);
-            stubJit.load32(MacroAssembler::Address(resultGPR, OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR);
-#endif
-        } else {
-            stubJit.loadPtr(protoObject->butterflyAddress(), resultGPR);
+        if (slot.isCacheableValue())
+            loadedValueGPR = resultGPR;
+        else
+            loadedValueGPR = scratchGPR;
+        
+        GPRReg storageGPR;
+        if (isInlineOffset(offset))
+            storageGPR = baseForAccessGPR;
+        else {
+            stubJit.loadPtr(MacroAssembler::Address(baseForAccessGPR, JSObject::butterflyOffset()), loadedValueGPR);
+            storageGPR = loadedValueGPR;
+        }
+        
 #if USE(JSVALUE64)
-            stubJit.load64(MacroAssembler::Address(resultGPR, offsetInButterfly(offset) * sizeof(WriteBarrier<Unknown>)), resultGPR);
-#elif USE(JSVALUE32_64)
-            stubJit.load32(MacroAssembler::Address(resultGPR, offsetInButterfly(offset) * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR);
-            stubJit.load32(MacroAssembler::Address(resultGPR, offsetInButterfly(offset) * sizeof(WriteBarrier<Unknown>) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR);
+        stubJit.load64(MacroAssembler::Address(storageGPR, offsetRelativeToBase(offset)), loadedValueGPR);
+#else
+        stubJit.load32(MacroAssembler::Address(storageGPR, offsetRelativeToBase(offset) + TagOffset), resultTagGPR);
+        stubJit.load32(MacroAssembler::Address(storageGPR, offsetRelativeToBase(offset) + PayloadOffset), loadedValueGPR);
 #endif
-        }
     }
 
     MacroAssembler::Call operationCall;
@@ -304,14 +312,14 @@ static ProtoChainGenerationResult generateProtoChainAccessStub(ExecState* exec,
     MacroAssembler::Jump success, fail;
     if (isAccessor) {
         if (slot.isCacheableGetter()) {
-            stubJit.setupArgumentsWithExecState(scratchGPR, resultGPR);
+            stubJit.setupArgumentsWithExecState(baseGPR, loadedValueGPR);
             operationFunction = operationCallGetter;
         } else {
             // EncodedJSValue (*GetValueFunc)(ExecState*, JSObject* slotBase, EncodedJSValue thisValue, PropertyName);
 #if USE(JSVALUE64)
-            stubJit.setupArgumentsWithExecState(MacroAssembler::TrustedImmPtr(protoObject), scratchGPR, MacroAssembler::TrustedImmPtr(propertyName.impl()));
+            stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseGPR, MacroAssembler::TrustedImmPtr(propertyName.impl()));
 #else
-            stubJit.setupArgumentsWithExecState(MacroAssembler::TrustedImmPtr(protoObject), scratchGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), MacroAssembler::TrustedImmPtr(propertyName.impl()));
+            stubJit.setupArgumentsWithExecState(baseForAccessGPR, baseGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), MacroAssembler::TrustedImmPtr(propertyName.impl()));
 #endif
             operationFunction = FunctionPtr(slot.customGetter());
         }
@@ -349,9 +357,8 @@ static ProtoChainGenerationResult generateProtoChainAccessStub(ExecState* exec,
     
     stubRoutine = FINALIZE_CODE_FOR_STUB(
         exec->codeBlock(), patchBuffer,
-        ("Prototype chain access stub for %s, return point %p",
+        ("Get access stub for %s, return point %p",
             toCString(*exec->codeBlock()).data(), successLabel.executableAddress()));
-    return ProtoChainGenerationSucceeded;
 }
 
 static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
@@ -463,10 +470,11 @@ static bool tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier
         return false;
 
     StructureChain* prototypeChain = structure->prototypeChain(exec);
-    if (generateProtoChainAccessStub(exec, slot, propertyName, stubInfo, prototypeChain, count, offset,
-        structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
-        stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase), stubInfo.stubRoutine) == ProtoChainGenerationFailed)
-        return false;
+    generateGetByIdStub(
+        exec, slot, propertyName, stubInfo, prototypeChain, count, offset, structure,
+        stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
+        stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase),
+        stubInfo.stubRoutine);
     
     RepatchBuffer repatchBuffer(codeBlock);
     replaceWithJump(repatchBuffer, stubInfo, stubInfo.stubRoutine->code().code());
@@ -485,49 +493,11 @@ void repatchGetByID(ExecState* exec, JSValue baseValue, const Identifier& proper
         repatchCall(exec->codeBlock(), stubInfo.callReturnLocation, operationGetById);
 }
 
-static bool getPolymorphicStructureList(
-    VM* vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo,
-    PolymorphicAccessStructureList*& polymorphicStructureList, int& listIndex,
-    CodeLocationLabel& slowCase)
-{
-    slowCase = stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase);
-    
-    if (stubInfo.accessType == access_unset) {
-        RELEASE_ASSERT(!stubInfo.stubRoutine);
-        polymorphicStructureList = new PolymorphicAccessStructureList();
-        stubInfo.initGetByIdSelfList(polymorphicStructureList, 0, false);
-        listIndex = 0;
-    } else if (stubInfo.accessType == access_get_by_id_self) {
-        RELEASE_ASSERT(!stubInfo.stubRoutine);
-        polymorphicStructureList = new PolymorphicAccessStructureList(*vm, codeBlock->ownerExecutable(), JITStubRoutine::createSelfManagedRoutine(slowCase), stubInfo.u.getByIdSelf.baseObjectStructure.get(), true);
-        stubInfo.initGetByIdSelfList(polymorphicStructureList, 1, true);
-        listIndex = 1;
-    } else if (stubInfo.accessType == access_get_by_id_chain) {
-        RELEASE_ASSERT(!!stubInfo.stubRoutine);
-        slowCase = CodeLocationLabel(stubInfo.stubRoutine->code().code());
-        polymorphicStructureList = new PolymorphicAccessStructureList(*vm, codeBlock->ownerExecutable(), stubInfo.stubRoutine, stubInfo.u.getByIdChain.baseObjectStructure.get(), stubInfo.u.getByIdChain.chain.get(), stubInfo.u.getByIdChain.isDirect, stubInfo.u.getByIdChain.count);
-        stubInfo.stubRoutine.clear();
-        stubInfo.initGetByIdSelfList(polymorphicStructureList, 1, false);
-        listIndex = 1;
-    } else {
-        RELEASE_ASSERT(stubInfo.accessType == access_get_by_id_self_list);
-        polymorphicStructureList = stubInfo.u.getByIdSelfList.structureList;
-        listIndex = stubInfo.u.getByIdSelfList.listSize;
-        slowCase = CodeLocationLabel(polymorphicStructureList->list[listIndex - 1].stubRoutine->code().code());
-    }
-    
-    if (listIndex == POLYMORPHIC_LIST_CACHE_SIZE)
-        return false;
-    
-    RELEASE_ASSERT(listIndex < POLYMORPHIC_LIST_CACHE_SIZE);
-    return true;
-}
-
 static void patchJumpToGetByIdStub(CodeBlock* codeBlock, StructureStubInfo& stubInfo, JITStubRoutine* stubRoutine)
 {
-    RELEASE_ASSERT(stubInfo.accessType == access_get_by_id_self_list);
+    RELEASE_ASSERT(stubInfo.accessType == access_get_by_id_list);
     RepatchBuffer repatchBuffer(codeBlock);
-    if (stubInfo.u.getByIdSelfList.didSelfPatching) {
+    if (stubInfo.u.getByIdList.list->didSelfPatching()) {
         repatchBuffer.relink(
             stubInfo.callReturnLocation.jumpAtOffset(
                 stubInfo.patch.deltaCallToJump),
@@ -550,158 +520,6 @@ static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identi
     JSCell* baseCell = baseValue.asCell();
     Structure* structure = baseCell->structure();
     
-    if (slot.slotBase() == baseValue) {
-        if (stubInfo.patch.spillMode == NeedToSpill) {
-            // We cannot do as much inline caching if the registers were not flushed prior to this GetById. In particular,
-            // non-Value cached properties require planting calls, which requires registers to have been flushed. Thus,
-            // if registers were not flushed, don't do non-Value caching.
-            if (!slot.isCacheableValue())
-                return false;
-        }
-    
-        PolymorphicAccessStructureList* polymorphicStructureList;
-        int listIndex;
-        CodeLocationLabel slowCase;
-
-        if (!getPolymorphicStructureList(vm, codeBlock, stubInfo, polymorphicStructureList, listIndex, slowCase))
-            return false;
-        
-        stubInfo.u.getByIdSelfList.listSize++;
-        
-        GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
-#if USE(JSVALUE32_64)
-        GPRReg resultTagGPR = static_cast<GPRReg>(stubInfo.patch.valueTagGPR);
-#endif
-        GPRReg resultGPR = static_cast<GPRReg>(stubInfo.patch.valueGPR);
-        GPRReg scratchGPR = TempRegisterSet(stubInfo.patch.usedRegisters).getFreeGPR();
-        
-        CCallHelpers stubJit(vm, codeBlock);
-        
-        MacroAssembler::Jump wrongStruct = branchStructure(stubJit,
-            MacroAssembler::NotEqual, 
-            MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()), 
-            structure);
-        
-        // The strategy we use for stubs is as follows:
-        // 1) Call DFG helper that calls the getter.
-        // 2) Check if there was an exception, and if there was, call yet another
-        //    helper.
-        
-        bool isDirect = false;
-        MacroAssembler::Call operationCall;
-        MacroAssembler::Call handlerCall;
-        FunctionPtr operationFunction;
-        MacroAssembler::Jump success;
-        
-        if (slot.isCacheableGetter() || slot.isCacheableCustom()) {
-            // FIXME: This code shouldn't be assuming that the top of stack is set up for JSC
-            // JIT-style C calls, since we may be currently on top of an FTL frame.
-            // https://bugs.webkit.org/show_bug.cgi?id=125711
-            
-            if (slot.isCacheableGetter()) {
-                ASSERT(scratchGPR != InvalidGPRReg);
-                ASSERT(baseGPR != scratchGPR);
-                if (isInlineOffset(slot.cachedOffset())) {
-#if USE(JSVALUE64)
-                    stubJit.load64(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset())), scratchGPR);
-#else
-                    stubJit.load32(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset())), scratchGPR);
-#endif
-                } else {
-                    stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), scratchGPR);
-#if USE(JSVALUE64)
-                    stubJit.load64(MacroAssembler::Address(scratchGPR, offsetRelativeToBase(slot.cachedOffset())), scratchGPR);
-#else
-                    stubJit.load32(MacroAssembler::Address(scratchGPR, offsetRelativeToBase(slot.cachedOffset())), scratchGPR);
-#endif
-                }
-                stubJit.setupArgumentsWithExecState(baseGPR, scratchGPR);
-                operationFunction = operationCallGetter;
-            } else {
-#if USE(JSVALUE64)
-                // EncodedJSValue (*GetValueFunc)(ExecState*, JSObject* slotBase, EncodedJSValue thisValue, PropertyName);
-                stubJit.setupArgumentsWithExecState(baseGPR, baseGPR, MacroAssembler::TrustedImmPtr(ident.impl()));
-#else
-                stubJit.setupArgumentsWithExecState(baseGPR, baseGPR, MacroAssembler::TrustedImm32(JSValue::CellTag), MacroAssembler::TrustedImmPtr(ident.impl()));
-#endif
-                operationFunction = FunctionPtr(slot.customGetter());
-            }
-            
-            // Need to make sure that whenever this call is made in the future, we remember the
-            // place that we made it from. It just so happens to be the place that we are at
-            // right now!
-            stubJit.store32(
-                MacroAssembler::TrustedImm32(exec->locationAsRawBits()),
-                CCallHelpers::tagFor(static_cast<VirtualRegister>(JSStack::ArgumentCount)));
-            stubJit.storePtr(GPRInfo::callFrameRegister, &vm->topCallFrame);
-            
-            operationCall = stubJit.call();
-#if USE(JSVALUE64)
-            stubJit.move(GPRInfo::returnValueGPR, resultGPR);
-#else
-            stubJit.setupResults(resultGPR, resultTagGPR);
-#endif
-            success = stubJit.emitExceptionCheck(CCallHelpers::InvertedExceptionCheck);
-            
-            stubJit.setupArguments(CCallHelpers::TrustedImmPtr(vm), GPRInfo::callFrameRegister);
-            handlerCall = stubJit.call();
-            stubJit.jumpToExceptionHandler();
-        } else {
-            if (isInlineOffset(slot.cachedOffset())) {
-#if USE(JSVALUE64)
-                stubJit.load64(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset())), resultGPR);
-#else
-                if (baseGPR == resultTagGPR) {
-                    stubJit.load32(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR);
-                    stubJit.load32(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR);
-                } else {
-                    stubJit.load32(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR);
-                    stubJit.load32(MacroAssembler::Address(baseGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR);
-                }
-#endif
-            } else {
-                stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSObject::butterflyOffset()), resultGPR);
-#if USE(JSVALUE64)
-                stubJit.load64(MacroAssembler::Address(resultGPR, offsetRelativeToBase(slot.cachedOffset())), resultGPR);
-#else
-                stubJit.load32(MacroAssembler::Address(resultGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), resultTagGPR);
-                stubJit.load32(MacroAssembler::Address(resultGPR, offsetRelativeToBase(slot.cachedOffset()) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), resultGPR);
-#endif
-            }
-            success = stubJit.jump();
-            isDirect = true;
-        }
-
-        LinkBuffer patchBuffer(*vm, &stubJit, codeBlock);
-        
-        patchBuffer.link(wrongStruct, slowCase);
-        patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
-        if (!isDirect) {
-            patchBuffer.link(operationCall, operationFunction);
-            patchBuffer.link(handlerCall, lookupExceptionHandler);
-        }
-        
-        RefPtr<JITStubRoutine> stubRoutine =
-            createJITStubRoutine(
-                FINALIZE_CODE_FOR(
-                    exec->codeBlock(), patchBuffer,
-                    ("GetById polymorphic list access for %s, return point %p",
-                        toCString(*exec->codeBlock()).data(), stubInfo.callReturnLocation.labelAtOffset(
-                            stubInfo.patch.deltaCallToDone).executableAddress())),
-                *vm,
-                codeBlock->ownerExecutable(),
-                slot.isCacheableGetter() || slot.isCacheableCustom());
-        
-        polymorphicStructureList->list[listIndex].set(*vm, codeBlock->ownerExecutable(), stubRoutine, structure, isDirect);
-        
-        patchJumpToGetByIdStub(codeBlock, stubInfo, stubRoutine.get());
-        return listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1);
-    }
-    
-    if (baseValue.asCell()->structure()->typeInfo().prohibitsPropertyCaching()
-        || baseValue.asCell()->structure()->isDictionary())
-        return false;
-    
     if (stubInfo.patch.spillMode == NeedToSpill) {
         // We cannot do as much inline caching if the registers were not flushed prior to this GetById. In particular,
         // non-Value cached properties require planting calls, which requires registers to have been flushed. Thus,
@@ -710,34 +528,42 @@ static bool tryBuildGetByIDList(ExecState* exec, JSValue baseValue, const Identi
             return false;
     }
     
-
     PropertyOffset offset = slot.cachedOffset();
-    size_t count = normalizePrototypeChainForChainAccess(exec, baseValue, slot.slotBase(), ident, offset);
-    if (count == InvalidPrototypeChain)
-        return false;
-
-    StructureChain* prototypeChain = structure->prototypeChain(exec);
+    StructureChain* prototypeChain = 0;
+    size_t count = 0;
     
-    PolymorphicAccessStructureList* polymorphicStructureList;
-    int listIndex;
-    CodeLocationLabel slowCase;
-    if (!getPolymorphicStructureList(vm, codeBlock, stubInfo, polymorphicStructureList, listIndex, slowCase))
-        return false;
+    if (slot.slotBase() != baseValue) {
+        if (baseValue.asCell()->structure()->typeInfo().prohibitsPropertyCaching()
+            || baseValue.asCell()->structure()->isDictionary())
+            return false;
+        
+        count = normalizePrototypeChainForChainAccess(
+            exec, baseValue, slot.slotBase(), ident, offset);
+        if (count == InvalidPrototypeChain)
+            return false;
+        prototypeChain = structure->prototypeChain(exec);
+    }
     
-    stubInfo.u.getByIdProtoList.listSize++;
+    PolymorphicGetByIdList* list = PolymorphicGetByIdList::from(stubInfo);
+    if (list->isFull()) {
+        // We need this extra check because of recursion.
+        return false;
+    }
     
     RefPtr<JITStubRoutine> stubRoutine;
-    
-    if (generateProtoChainAccessStub(exec, slot, ident, stubInfo, prototypeChain, count, offset, structure,
+    generateGetByIdStub(
+        exec, slot, ident, stubInfo, prototypeChain, count, offset, structure,
         stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
-        slowCase, stubRoutine) == ProtoChainGenerationFailed)
-        return false;
+        CodeLocationLabel(list->currentSlowPathTarget(stubInfo)), stubRoutine);
     
-    polymorphicStructureList->list[listIndex].set(*vm, codeBlock->ownerExecutable(), stubRoutine, structure, prototypeChain, slot.isCacheableValue(), count);
+    list->addAccess(GetByIdAccess(
+        *vm, codeBlock->ownerExecutable(),
+        slot.isCacheableValue() ? GetByIdAccess::SimpleStub : GetByIdAccess::Getter,
+        stubRoutine, structure, prototypeChain, count));
     
     patchJumpToGetByIdStub(codeBlock, stubInfo, stubRoutine.get());
     
-    return listIndex < (POLYMORPHIC_LIST_CACHE_SIZE - 1);
+    return !list->isFull();
 }
 
 void buildGetByIDList(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo)
@@ -1263,9 +1089,11 @@ static bool tryBuildPutByIdList(ExecState* exec, JSValue baseValue, const Identi
             
             StructureChain* prototypeChain = structure->prototypeChain(exec);
             
-            // We're now committed to creating the stub. Mogrify the meta-data accordingly.
             list = PolymorphicPutByIdList::from(putKind, stubInfo);
+            if (list->isFull())
+                return false; // Will get here due to recursion.
             
+            // We're now committed to creating the stub. Mogrify the meta-data accordingly.
             emitPutTransitionStub(
                 exec, baseValue, propertyName, slot, stubInfo, putKind,
                 structure, oldStructure, prototypeChain,
@@ -1278,9 +1106,11 @@ static bool tryBuildPutByIdList(ExecState* exec, JSValue baseValue, const Identi
                     oldStructure, structure, prototypeChain,
                     stubRoutine));
         } else {
-            // We're now committed to creating the stub. Mogrify the meta-data accordingly.
             list = PolymorphicPutByIdList::from(putKind, stubInfo);
+            if (list->isFull())
+                return false; // Will get here due to recursion.
             
+            // We're now committed to creating the stub. Mogrify the meta-data accordingly.
             emitPutReplaceStub(
                 exec, baseValue, propertyName, slot, stubInfo, putKind,
                 structure, CodeLocationLabel(list->currentSlowPathTarget()), stubRoutine);
diff --git a/Source/JavaScriptCore/tests/stress/getter.js b/Source/JavaScriptCore/tests/stress/getter.js
new file mode 100644 (file)
index 0000000..cf232a5
--- /dev/null
@@ -0,0 +1,18 @@
+function foo(o) {
+    return o.f + o.k * 1000;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100; ++i) {
+    var o = {g_: 5};
+    o.__defineGetter__("f", function() { return 42 + this.g_; });
+    o.__defineGetter__("g", function() { return 43 + this.g_; });
+    o.__defineGetter__("h", function() { return 44 + this.g_; });
+    o.__defineGetter__("i", function() { return 45 + this.g_; });
+    o.__defineGetter__("j", function() { return 46 + this.g_; });
+    o.__defineGetter__("k", function() { return 47 + this.g_; });
+    var result = foo(o);
+    if (result != (42 + 5) + 1000 * (47 + 5))
+        throw "Error: bad result: " + result;
+}
diff --git a/Source/JavaScriptCore/tests/stress/polymorphic-prototype-accesses.js b/Source/JavaScriptCore/tests/stress/polymorphic-prototype-accesses.js
new file mode 100644 (file)
index 0000000..f206551
--- /dev/null
@@ -0,0 +1,28 @@
+function Foo() {
+}
+Foo.prototype.f = 42;
+Foo.prototype.g = 43;
+Foo.prototype.h = 44;
+Foo.prototype.i = 45;
+Foo.prototype.j = 46;
+Foo.prototype.k = 47;
+
+function Bar() {
+}
+Bar.prototype.k = 23;
+Bar.prototype.f = 24;
+
+function foo(o) {
+    return o.f + o.k;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100; ++i) {
+    var result = foo(new Foo());
+    if (result != 89)
+        throw "Error: bad result for Foo: " + result;
+    result = foo(new Bar());
+    if (result != 47)
+        throw "Error: bad result for Bar: " + result;
+}
diff --git a/Source/JavaScriptCore/tests/stress/prototype-getter.js b/Source/JavaScriptCore/tests/stress/prototype-getter.js
new file mode 100644 (file)
index 0000000..07f5167
--- /dev/null
@@ -0,0 +1,21 @@
+function Foo(g) {
+    this.g_ = g;
+}
+Foo.prototype.__defineGetter__("f", function() { return this.g_ + 32; });
+Foo.prototype.__defineGetter__("g", function() { return this.g_ + 33; });
+Foo.prototype.__defineGetter__("h", function() { return this.g_ + 34; });
+Foo.prototype.__defineGetter__("i", function() { return this.g_ + 35; });
+Foo.prototype.__defineGetter__("j", function() { return this.g_ + 36; });
+Foo.prototype.__defineGetter__("k", function() { return this.g_ + 37; });
+
+function foo(o) {
+    return o.f + o.k * 1000;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100; ++i) {
+    var result = foo(new Foo(5));
+    if (result != (32 + 5) + (37 + 5) * 1000)
+        throw "Error: bad result: " + result;
+}
diff --git a/Source/JavaScriptCore/tests/stress/simple-prototype-accesses.js b/Source/JavaScriptCore/tests/stress/simple-prototype-accesses.js
new file mode 100644 (file)
index 0000000..a580879
--- /dev/null
@@ -0,0 +1,20 @@
+function Foo() {
+}
+Foo.prototype.f = 42;
+Foo.prototype.g = 43;
+Foo.prototype.h = 44;
+Foo.prototype.i = 45;
+Foo.prototype.j = 46;
+Foo.prototype.k = 47;
+
+function foo(o) {
+    return o.f + o.k;
+}
+
+noInline(foo);
+
+for (var i = 0; i < 100; ++i) {
+    var result = foo(new Foo());
+    if (result != 89)
+        throw "Error: bad result for Foo: " + result;
+}