+2011-10-02 Filip Pizlo <fpizlo@apple.com>
+
+ DFG should speculate more aggressively on obvious cases on
+ polymorphic get_by_id
+ https://bugs.webkit.org/show_bug.cgi?id=69235
+
+ Reviewed by Oliver Hunt.
+
+ This implements trivial polymorphic get_by_id. It also fixes
+ problems in the CSE for CheckStructure in the put_by_id
+ transition case.
+
+ Doing this required knowing whether a polymorphic get_by_id stub
+ was doing a direct access rather than a call of some kind.
+
+ Slight speed-up on Kraken and SunSpider. 0.5% speed-up in the
+ scaled mean of all benchmarks.
+
+ * GNUmakefile.list.am:
+ * JavaScriptCore.xcodeproj/project.pbxproj:
+ * bytecode/Instruction.h:
+ (JSC::PolymorphicAccessStructureList::PolymorphicStubInfo::set):
+ (JSC::PolymorphicAccessStructureList::PolymorphicAccessStructureList):
+ * dfg/DFGByteCodeParser.cpp:
+ (JSC::DFG::ByteCodeParser::cellConstant):
+ (JSC::DFG::ByteCodeParser::parseBlock):
+ * dfg/DFGGraph.cpp:
+ (JSC::DFG::Graph::dump):
+ * dfg/DFGGraph.h:
+ (JSC::DFG::Graph::addStructureSet):
+ (JSC::DFG::Graph::addStructureTransitionData):
+ * dfg/DFGNode.h:
+ (JSC::DFG::StructureTransitionData::StructureTransitionData):
+ (JSC::DFG::Node::hasStructureTransitionData):
+ (JSC::DFG::Node::structureTransitionData):
+ (JSC::DFG::Node::hasStructureSet):
+ (JSC::DFG::Node::structureSet):
+ * dfg/DFGPropagator.cpp:
+ (JSC::DFG::Propagator::checkStructureLoadElimination):
+ (JSC::DFG::Propagator::performNodeCSE):
+ * dfg/DFGRepatch.cpp:
+ (JSC::DFG::tryBuildGetByIDList):
+ (JSC::DFG::tryBuildGetByIDProtoList):
+ * dfg/DFGSpeculativeJIT32_64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGSpeculativeJIT64.cpp:
+ (JSC::DFG::SpeculativeJIT::compile):
+ * dfg/DFGStructureSet.h: Added.
+ (JSC::DFG::StructureSet::StructureSet):
+ (JSC::DFG::StructureSet::add):
+ (JSC::DFG::StructureSet::addAll):
+ (JSC::DFG::StructureSet::remove):
+ (JSC::DFG::StructureSet::contains):
+ (JSC::DFG::StructureSet::isSubsetOf):
+ (JSC::DFG::StructureSet::isSupersetOf):
+ (JSC::DFG::StructureSet::size):
+ (JSC::DFG::StructureSet::at):
+ (JSC::DFG::StructureSet::operator[]):
+ (JSC::DFG::StructureSet::last):
+ * jit/JITPropertyAccess.cpp:
+ (JSC::JIT::privateCompileGetByIdSelfList):
+ (JSC::JIT::privateCompileGetByIdProtoList):
+ (JSC::JIT::privateCompileGetByIdChainList):
+ * jit/JITPropertyAccess32_64.cpp:
+ (JSC::JIT::privateCompileGetByIdSelfList):
+ (JSC::JIT::privateCompileGetByIdProtoList):
+ (JSC::JIT::privateCompileGetByIdChainList):
+ * jit/JITStubs.cpp:
+ (JSC::DEFINE_STUB_FUNCTION):
+ (JSC::getPolymorphicAccessStructureListSlot):
+
2011-10-03 Jer Noble <jer.noble@apple.com>
Enable WEB_AUDIO by default in the WebKit/mac port.
Source/JavaScriptCore/dfg/DFGScoreBoard.h \
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp \
Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h \
+ Source/JavaScriptCore/dfg/DFGStructureSet.h \
Source/JavaScriptCore/heap/AllocationSpace.cpp \
Source/JavaScriptCore/heap/AllocationSpace.h \
Source/JavaScriptCore/heap/CardSet.h \
0FD82F4B142806A100179C94 /* BitVector.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD82F491428069200179C94 /* BitVector.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FE228ED1436AB2700196C48 /* Heuristics.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE228EB1436AB2300196C48 /* Heuristics.h */; settings = {ATTRIBUTES = (Private, ); }; };
0FE228EE1436AB2C00196C48 /* Heuristics.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FE228EA1436AB2300196C48 /* Heuristics.cpp */; };
+ 0FFF4BB4143955E900655BC0 /* DFGStructureSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FFF4BB2143955E600655BC0 /* DFGStructureSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
1400067712A6F7830064D123 /* OSAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 1400067612A6F7830064D123 /* OSAllocator.h */; settings = {ATTRIBUTES = (Private, ); }; };
1400069312A6F9E10064D123 /* OSAllocatorPosix.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1400069212A6F9E10064D123 /* OSAllocatorPosix.cpp */; };
140566C4107EC255005DBC8D /* JSAPIValueWrapper.cpp in Sources */ = {isa = PBXBuildFile; fileRef = BC0894D50FAFBA2D00001865 /* JSAPIValueWrapper.cpp */; };
0FD82F491428069200179C94 /* BitVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BitVector.h; sourceTree = "<group>"; };
0FE228EA1436AB2300196C48 /* Heuristics.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Heuristics.cpp; sourceTree = "<group>"; };
0FE228EB1436AB2300196C48 /* Heuristics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Heuristics.h; sourceTree = "<group>"; };
+ 0FFF4BB2143955E600655BC0 /* DFGStructureSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGStructureSet.h; path = dfg/DFGStructureSet.h; sourceTree = "<group>"; };
1400067612A6F7830064D123 /* OSAllocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OSAllocator.h; sourceTree = "<group>"; };
1400069212A6F9E10064D123 /* OSAllocatorPosix.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OSAllocatorPosix.cpp; sourceTree = "<group>"; };
140D17D60E8AD4A9000CD17D /* JSBasePrivate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSBasePrivate.h; sourceTree = "<group>"; };
86EC9DB31328DF44002B2AD7 /* dfg */ = {
isa = PBXGroup;
children = (
+ 0FFF4BB2143955E600655BC0 /* DFGStructureSet.h */,
86EC9DB41328DF82002B2AD7 /* DFGByteCodeParser.cpp */,
86EC9DB51328DF82002B2AD7 /* DFGByteCodeParser.h */,
0FD82E1E14172C2F00179C94 /* DFGCapabilities.cpp */,
0FD52AAE143035A00026DC9F /* UnionFind.h in Headers */,
86880F1E14328BB900B08D42 /* DFGJITCompilerInlineMethods.h in Headers */,
0FE228ED1436AB2700196C48 /* Heuristics.h in Headers */,
+ 0FFF4BB4143955E900655BC0 /* DFGStructureSet.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
public:
struct PolymorphicStubInfo {
bool isChain;
+ bool isDirect;
PolymorphicAccessStructureListStubRoutineType stubRoutine;
WriteBarrier<Structure> base;
union {
u.proto.clear();
}
- void set(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType _stubRoutine, Structure* _base)
+ void set(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType _stubRoutine, Structure* _base, bool isDirect)
{
stubRoutine = _stubRoutine;
base.set(globalData, owner, _base);
u.proto.clear();
isChain = false;
+ this->isDirect = isDirect;
}
- void set(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType _stubRoutine, Structure* _base, Structure* _proto)
+ void set(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType _stubRoutine, Structure* _base, Structure* _proto, bool isDirect)
{
stubRoutine = _stubRoutine;
base.set(globalData, owner, _base);
u.proto.set(globalData, owner, _proto);
isChain = false;
+ this->isDirect = isDirect;
}
- void set(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType _stubRoutine, Structure* _base, StructureChain* _chain)
+ void set(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType _stubRoutine, Structure* _base, StructureChain* _chain, bool isDirect)
{
stubRoutine = _stubRoutine;
base.set(globalData, owner, _base);
u.chain.set(globalData, owner, _chain);
isChain = true;
+ this->isDirect = isDirect;
}
} list[POLYMORPHIC_LIST_CACHE_SIZE];
- PolymorphicAccessStructureList(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType stubRoutine, Structure* firstBase)
+ PolymorphicAccessStructureList(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType stubRoutine, Structure* firstBase, bool isDirect)
{
- list[0].set(globalData, owner, stubRoutine, firstBase);
+ list[0].set(globalData, owner, stubRoutine, firstBase, isDirect);
}
- PolymorphicAccessStructureList(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType stubRoutine, Structure* firstBase, Structure* firstProto)
+ PolymorphicAccessStructureList(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType stubRoutine, Structure* firstBase, Structure* firstProto, bool isDirect)
{
- list[0].set(globalData, owner, stubRoutine, firstBase, firstProto);
+ list[0].set(globalData, owner, stubRoutine, firstBase, firstProto, isDirect);
}
- PolymorphicAccessStructureList(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType stubRoutine, Structure* firstBase, StructureChain* firstChain)
+ PolymorphicAccessStructureList(JSGlobalData& globalData, JSCell* owner, PolymorphicAccessStructureListStubRoutineType stubRoutine, Structure* firstBase, StructureChain* firstChain, bool isDirect)
{
- list[0].set(globalData, owner, stubRoutine, firstBase, firstChain);
+ list[0].set(globalData, owner, stubRoutine, firstBase, firstChain, isDirect);
}
void visitAggregate(SlotVisitor& visitor, int count)
m_constants.append(ConstantRecord());
ASSERT(m_constants.size() == m_codeBlock->numberOfConstantRegisters());
+ m_cellConstants.add(cell, m_codeBlock->numberOfConstantRegisters() - 1);
+
return getJSConstant(m_codeBlock->numberOfConstantRegisters() - 1);
}
NodeIndex base = get(currentInstruction[2].u.operand);
unsigned identifierNumber = currentInstruction[3].u.operand;
+ Identifier identifier = m_codeBlock->identifier(identifierNumber);
StructureStubInfo& stubInfo = m_profiledBlock->getStubInfo(m_currentIndex);
- NodeIndex getById = NoNode;
- if (stubInfo.seen && stubInfo.accessType == access_get_by_id_self) {
- Structure* structure = stubInfo.u.getByIdSelf.baseObjectStructure.get();
- Identifier identifier = m_codeBlock->identifier(identifierNumber);
- size_t offset = structure->get(*m_globalData, identifier);
-
- if (offset != notFound) {
- addToGraph(CheckStructure, OpInfo(structure), base);
- getById = addToGraph(GetByOffset, OpInfo(m_graph.m_storageAccessData.size()), OpInfo(prediction), addToGraph(GetPropertyStorage, base));
+ size_t offset = notFound;
+ StructureSet structureSet;
+ if (stubInfo.seen) {
+ switch (stubInfo.accessType) {
+ case access_get_by_id_self: {
+ Structure* structure = stubInfo.u.getByIdSelf.baseObjectStructure.get();
+ offset = structure->get(*m_globalData, identifier);
- StorageAccessData storageAccessData;
- storageAccessData.offset = offset;
- storageAccessData.identifierNumber = identifierNumber;
- m_graph.m_storageAccessData.append(storageAccessData);
+ if (offset != notFound)
+ structureSet.add(structure);
+
+ if (offset != notFound)
+ ASSERT(structureSet.size());
+ break;
+ }
+
+ case access_get_by_id_self_list: {
+ PolymorphicAccessStructureList* list = stubInfo.u.getByIdProtoList.structureList;
+ unsigned size = stubInfo.u.getByIdProtoList.listSize;
+ for (unsigned i = 0; i < size; ++i) {
+ if (!list->list[i].isDirect) {
+ offset = notFound;
+ break;
+ }
+
+ Structure* structure = list->list[i].base.get();
+ size_t myOffset = structure->get(*m_globalData, identifier);
+
+ if (myOffset == notFound) {
+ offset = notFound;
+ break;
+ }
+
+ if (!i)
+ offset = myOffset;
+ else if (offset != myOffset) {
+ offset = notFound;
+ break;
+ }
+
+ structureSet.add(structure);
+ }
+
+ if (offset != notFound)
+ ASSERT(structureSet.size());
+ break;
+ }
+
+ default:
+ ASSERT(offset == notFound);
+ break;
}
}
-
- if (getById == NoNode)
- getById = addToGraph(GetById, OpInfo(identifierNumber), OpInfo(prediction), base);
-
- set(currentInstruction[1].u.operand, getById);
+
+ if (offset != notFound) {
+ ASSERT(structureSet.size());
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(structureSet)), base);
+ set(currentInstruction[1].u.operand, addToGraph(GetByOffset, OpInfo(m_graph.m_storageAccessData.size()), OpInfo(prediction), addToGraph(GetPropertyStorage, base)));
+
+ StorageAccessData storageAccessData;
+ storageAccessData.offset = offset;
+ storageAccessData.identifierNumber = identifierNumber;
+ m_graph.m_storageAccessData.append(storageAccessData);
+ } else
+ set(currentInstruction[1].u.operand, addToGraph(GetById, OpInfo(identifierNumber), OpInfo(prediction), base));
NEXT_OPCODE(op_get_by_id);
}
size_t offset = structure->get(*m_globalData, identifier);
if (offset != notFound) {
- addToGraph(CheckStructure, OpInfo(structure), base);
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(structure)), base);
addToGraph(PutByOffset, OpInfo(m_graph.m_storageAccessData.size()), base, addToGraph(GetPropertyStorage, base), value);
StorageAccessData storageAccessData;
size_t offset = newStructure->get(*m_globalData, identifier);
if (offset != notFound) {
- addToGraph(CheckStructure, OpInfo(previousStructure), base);
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(previousStructure)), base);
if (!direct) {
for (WriteBarrier<Structure>* it = structureChain->head(); *it; ++it) {
JSValue prototype = (*it)->storedPrototype();
if (prototype.isNull())
continue;
ASSERT(prototype.isCell());
- addToGraph(CheckStructure, OpInfo(prototype.asCell()->structure()), cellConstant(prototype.asCell()));
+ addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(prototype.asCell()->structure())), cellConstant(prototype.asCell()));
}
}
- addToGraph(PutStructure, OpInfo(newStructure), base);
+ addToGraph(PutStructure, OpInfo(m_graph.addStructureTransitionData(StructureTransitionData(previousStructure, newStructure))), base);
addToGraph(PutByOffset, OpInfo(m_graph.m_storageAccessData.size()), base, addToGraph(GetPropertyStorage, base), value);
printf("%sid%u", hasPrinted ? ", " : "", node.identifierNumber());
hasPrinted = true;
}
- if (node.hasStructure()) {
- printf("%sstruct(%p)", hasPrinted ? ", " : "", node.structure());
+ if (node.hasStructureSet()) {
+ for (size_t i = 0; i < node.structureSet().size(); ++i) {
+ printf("%sstruct(%p)", hasPrinted ? ", " : "", node.structureSet()[i]);
+ hasPrinted = true;
+ }
+ }
+ if (node.hasStructureTransitionData()) {
+ printf("%sstruct(%p -> %p)", hasPrinted ? ", " : "", node.structureTransitionData().previousStructure, node.structureTransitionData().newStructure);
hasPrinted = true;
}
if (node.hasStorageAccessData()) {
#endif
void predictArgumentTypes(ExecState*, CodeBlock*);
+
+ StructureSet* addStructureSet(const StructureSet& structureSet)
+ {
+ ASSERT(structureSet.size());
+ m_structureSet.append(structureSet);
+ return &m_structureSet.last();
+ }
+
+ StructureTransitionData* addStructureTransitionData(const StructureTransitionData& structureTransitionData)
+ {
+ m_structureTransitionData.append(structureTransitionData);
+ return &m_structureTransitionData.last();
+ }
Vector< OwnPtr<BasicBlock> , 8> m_blocks;
Vector<NodeIndex, 16> m_varArgChildren;
Vector<ResolveGlobalData> m_resolveGlobalData;
Vector<NodeIndex, 8> m_arguments;
SegmentedVector<VariableAccessData, 16> m_variableAccessData;
+ SegmentedVector<StructureSet, 16> m_structureSet;
+ SegmentedVector<StructureTransitionData, 8> m_structureTransitionData;
unsigned m_preservedVars;
unsigned m_localVars;
unsigned m_parameterSlots;
#ifndef DFGNode_h
#define DFGNode_h
+#include "DFGStructureSet.h"
#include <wtf/BoundsCheckedPointer.h>
#include <wtf/Platform.h>
#include <wtf/UnionFind.h>
PredictedType m_prediction;
};
+struct StructureTransitionData {
+ Structure* previousStructure;
+ Structure* newStructure;
+
+ StructureTransitionData() { }
+
+ StructureTransitionData(Structure* previousStructure, Structure* newStructure)
+ : previousStructure(previousStructure)
+ , newStructure(newStructure)
+ {
+ }
+};
+
typedef unsigned ArithNodeFlags;
#define NodeUseBottom 0x00
#define NodeUsedAsNumber 0x01
return m_opInfo2;
}
- bool hasStructure()
+ bool hasStructureTransitionData()
+ {
+ return op == PutStructure;
+ }
+
+ StructureTransitionData& structureTransitionData()
+ {
+ ASSERT(hasStructureTransitionData());
+ return *reinterpret_cast<StructureTransitionData*>(m_opInfo);
+ }
+
+ bool hasStructureSet()
{
- return op == CheckStructure || op == PutStructure;
+ return op == CheckStructure;
}
- Structure* structure()
+ StructureSet& structureSet()
{
- return reinterpret_cast<Structure*>(m_opInfo);
+ ASSERT(hasStructureSet());
+ return *reinterpret_cast<StructureSet*>(m_opInfo);
}
bool hasStorageAccessData()
return NoNode;
}
- bool checkStructureLoadElimination(Structure* structure, NodeIndex child1)
+ bool checkStructureLoadElimination(const StructureSet& structureSet, NodeIndex child1)
{
NodeIndex start = startIndexForChildren(child1);
for (NodeIndex index = m_compileIndex; index-- > start;) {
switch (node.op) {
case CheckStructure:
if (node.child1() == child1
- && node.structure() == structure)
+ && structureSet.isSupersetOf(node.structureSet()))
return true;
break;
case PutStructure:
if (node.child1() == child1
- && node.structure() == structure)
+ && structureSet.contains(node.structureTransitionData().newStructure))
return true;
- return false;
+ if (structureSet.contains(node.structureTransitionData().previousStructure))
+ return false;
+ break;
case PutByOffset:
// Setting a property cannot change the structure.
break;
case CheckStructure:
- if (checkStructureLoadElimination(node.structure(), node.child1()))
+ if (checkStructureLoadElimination(node.structureSet(), node.child1()))
eliminate();
break;
if (stubInfo.accessType == access_get_by_id_self) {
ASSERT(!stubInfo.stubRoutine);
- polymorphicStructureList = new PolymorphicAccessStructureList(*globalData, codeBlock->ownerExecutable(), MacroAssemblerCodeRef::createSelfManagedCodeRef(stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToSlowCase)), stubInfo.u.getByIdSelf.baseObjectStructure.get());
+ polymorphicStructureList = new PolymorphicAccessStructureList(*globalData, codeBlock->ownerExecutable(), MacroAssemblerCodeRef::createSelfManagedCodeRef(stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToSlowCase)), stubInfo.u.getByIdSelf.baseObjectStructure.get(), true);
stubInfo.initGetByIdSelfList(polymorphicStructureList, 1);
} else {
polymorphicStructureList = stubInfo.u.getByIdSelfList.structureList;
MacroAssemblerCodeRef stubRoutine = patchBuffer.finalizeCode();
- polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure);
+ polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure, true);
CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.deltaCallToStructCheck);
RepatchBuffer repatchBuffer(codeBlock);
if (stubInfo.accessType == access_get_by_id_chain) {
ASSERT(!!stubInfo.stubRoutine);
- polymorphicStructureList = new PolymorphicAccessStructureList(*globalData, codeBlock->ownerExecutable(), stubInfo.stubRoutine, stubInfo.u.getByIdChain.baseObjectStructure.get(), stubInfo.u.getByIdChain.chain.get());
+ polymorphicStructureList = new PolymorphicAccessStructureList(*globalData, codeBlock->ownerExecutable(), stubInfo.stubRoutine, stubInfo.u.getByIdChain.baseObjectStructure.get(), stubInfo.u.getByIdChain.chain.get(), true);
stubInfo.stubRoutine = MacroAssemblerCodeRef();
stubInfo.initGetByIdProtoList(polymorphicStructureList, 1);
} else {
generateProtoChainAccessStub(exec, stubInfo, prototypeChain, count, offset, structure, stubInfo.callReturnLocation.labelAtOffset(stubInfo.deltaCallToDone), lastProtoBegin, stubRoutine);
- polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure);
+ polymorphicStructureList->list[listIndex].set(*globalData, codeBlock->ownerExecutable(), stubRoutine, structure, true);
CodeLocationJump jumpLocation = stubInfo.callReturnLocation.jumpAtOffset(stubInfo.deltaCallToStructCheck);
RepatchBuffer repatchBuffer(codeBlock);
case CheckStructure: {
SpeculateCellOperand base(this, node.child1());
- GPRReg baseGPR = base.gpr();
+ ASSERT(node.structureSet().size());
- speculationCheck(m_jit.branchPtr(JITCompiler::NotEqual, JITCompiler::Address(baseGPR, JSCell::structureOffset()), JITCompiler::TrustedImmPtr(node.structure())));
+ if (node.structureSet().size() == 1)
+ speculationCheck(m_jit.branchPtr(JITCompiler::NotEqual, JITCompiler::Address(base.gpr(), JSCell::structureOffset()), JITCompiler::TrustedImmPtr(node.structureSet()[0])));
+ else {
+ GPRTemporary structure(this);
+
+ m_jit.loadPtr(JITCompiler::Address(base.gpr(), JSCell::structureOffset()), structure.gpr());
+
+ JITCompiler::JumpList done;
+
+ for (size_t i = 0; i < node.structureSet().size() - 1; ++i)
+ done.append(m_jit.branchPtr(JITCompiler::Equal, structure.gpr(), JITCompiler::TrustedImmPtr(node.structureSet()[i])));
+
+ speculationCheck(m_jit.branchPtr(JITCompiler::NotEqual, structure.gpr(), JITCompiler::TrustedImmPtr(node.structureSet().last())));
+
+ done.link(&m_jit);
+ }
noResult(m_compileIndex);
break;
case CheckStructure: {
SpeculateCellOperand base(this, node.child1());
- GPRReg baseGPR = base.gpr();
+ ASSERT(node.structureSet().size());
- speculationCheck(m_jit.branchPtr(JITCompiler::NotEqual, JITCompiler::Address(baseGPR, JSCell::structureOffset()), JITCompiler::TrustedImmPtr(node.structure())));
+ if (node.structureSet().size() == 1)
+ speculationCheck(m_jit.branchPtr(JITCompiler::NotEqual, JITCompiler::Address(base.gpr(), JSCell::structureOffset()), JITCompiler::TrustedImmPtr(node.structureSet()[0])));
+ else {
+ GPRTemporary structure(this);
+
+ m_jit.loadPtr(JITCompiler::Address(base.gpr(), JSCell::structureOffset()), structure.gpr());
+
+ JITCompiler::JumpList done;
+
+ for (size_t i = 0; i < node.structureSet().size() - 1; ++i)
+ done.append(m_jit.branchPtr(JITCompiler::Equal, structure.gpr(), JITCompiler::TrustedImmPtr(node.structureSet()[i])));
+
+ speculationCheck(m_jit.branchPtr(JITCompiler::NotEqual, structure.gpr(), JITCompiler::TrustedImmPtr(node.structureSet().last())));
+
+ done.link(&m_jit);
+ }
noResult(m_compileIndex);
break;
#if ENABLE(GGC) || ENABLE(WRITE_BARRIER_PROFILING)
// Must always emit this write barrier as the structure transition itself requires it
- writeBarrier(baseGPR, node.structure(), WriteBarrierForGenericAccess);
+ writeBarrier(baseGPR, node.structureTransitionData().newStructure, WriteBarrierForGenericAccess);
#endif
- m_jit.storePtr(MacroAssembler::TrustedImmPtr(node.structure()), MacroAssembler::Address(baseGPR, JSCell::structureOffset()));
+ m_jit.storePtr(MacroAssembler::TrustedImmPtr(node.structureTransitionData().newStructure), MacroAssembler::Address(baseGPR, JSCell::structureOffset()));
noResult(m_compileIndex);
break;
--- /dev/null
+/*
+ * Copyright (C) 2011 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 DFGStructureSet_h
+#define DFGStructureSet_h
+
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+class Structure;
+
+namespace DFG {
+
+class StructureSet {
+public:
+ StructureSet() { }
+
+ StructureSet(Structure* structure)
+ {
+ m_structures.append(structure);
+ }
+
+ void add(Structure* structure)
+ {
+ ASSERT(!contains(structure));
+ m_structures.append(structure);
+ }
+
+ bool addAll(const StructureSet& other)
+ {
+ bool changed = false;
+ for (size_t i = 0; i < other.size(); ++i) {
+ if (contains(other[i]))
+ continue;
+ add(other[i]);
+ changed = true;
+ }
+ return changed;
+ }
+
+ void remove(Structure* structure)
+ {
+ for (size_t i = 0; i < m_structures.size(); ++i) {
+ if (m_structures[i] != structure)
+ continue;
+
+ m_structures[i] = m_structures.last();
+ m_structures.removeLast();
+ return;
+ }
+ }
+
+ bool contains(Structure* structure) const
+ {
+ for (size_t i = 0; i < m_structures.size(); ++i) {
+ if (m_structures[i] == structure)
+ return true;
+ }
+ return false;
+ }
+
+ bool isSubsetOf(const StructureSet& other) const
+ {
+ for (size_t i = 0; i < m_structures.size(); ++i) {
+ if (!other.contains(m_structures[i]))
+ return false;
+ }
+ return true;
+ }
+
+ bool isSupersetOf(const StructureSet& other) const
+ {
+ return other.isSubsetOf(*this);
+ }
+
+ size_t size() const { return m_structures.size(); }
+
+ Structure* at(size_t i) const { return m_structures.at(i); }
+
+ Structure* operator[](size_t i) const { return at(i); }
+
+ Structure* last() const { return m_structures.last(); }
+
+private:
+ Vector<Structure*, 2> m_structures;
+};
+
+} } // namespace JSC::DFG
+
+#endif // DFGStructureSet_h
{
Jump failureCase = checkStructure(regT0, structure);
bool needsStubLink = false;
+ bool isDirect = false;
if (slot.cachedPropertyType() == PropertySlot::Getter) {
needsStubLink = true;
compileGetDirectOffset(regT0, regT1, cachedOffset);
stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
- } else
+ } else {
+ isDirect = true;
compileGetDirectOffset(regT0, regT0, cachedOffset);
+ }
Jump success = jump();
LinkBuffer patchBuffer(*m_globalData, this);
MacroAssemblerCodeRef stubCode = patchBuffer.finalizeCode();
- polymorphicStructures->list[currentIndex].set(*m_globalData, m_codeBlock->ownerExecutable(), stubCode, structure);
+ polymorphicStructures->list[currentIndex].set(*m_globalData, m_codeBlock->ownerExecutable(), stubCode, structure, isDirect);
// Finally patch the jump to slow case back in the hot path to jump here instead.
CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
// Checks out okay!
bool needsStubLink = false;
+ bool isDirect = false;
if (slot.cachedPropertyType() == PropertySlot::Getter) {
needsStubLink = true;
compileGetDirectOffset(protoObject, regT1, cachedOffset);
stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
- } else
+ } else {
+ isDirect = true;
compileGetDirectOffset(protoObject, regT0, cachedOffset);
+ }
Jump success = jump();
patchBuffer.link(success, stubInfo->hotPathBegin.labelAtOffset(patchOffsetGetByIdPutResult));
MacroAssemblerCodeRef stubCode = patchBuffer.finalizeCode();
- prototypeStructures->list[currentIndex].set(*m_globalData, m_codeBlock->ownerExecutable(), stubCode, structure, prototypeStructure);
+ prototypeStructures->list[currentIndex].set(*m_globalData, m_codeBlock->ownerExecutable(), stubCode, structure, prototypeStructure, isDirect);
// Finally patch the jump to slow case back in the hot path to jump here instead.
CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
ASSERT(protoObject);
bool needsStubLink = false;
+ bool isDirect = false;
if (slot.cachedPropertyType() == PropertySlot::Getter) {
needsStubLink = true;
compileGetDirectOffset(protoObject, regT1, cachedOffset);
stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
- } else
+ } else {
+ isDirect = true;
compileGetDirectOffset(protoObject, regT0, cachedOffset);
+ }
Jump success = jump();
LinkBuffer patchBuffer(*m_globalData, this);
CodeRef stubRoutine = patchBuffer.finalizeCode();
// Track the stub we have created so that it will be deleted later.
- prototypeStructures->list[currentIndex].set(callFrame->globalData(), m_codeBlock->ownerExecutable(), stubRoutine, structure, chain);
+ prototypeStructures->list[currentIndex].set(callFrame->globalData(), m_codeBlock->ownerExecutable(), stubRoutine, structure, chain, isDirect);
// Finally patch the jump to slow case back in the hot path to jump here instead.
CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
// regT0 holds a JSCell*
Jump failureCase = checkStructure(regT0, structure);
bool needsStubLink = false;
+ bool isDirect = false;
if (slot.cachedPropertyType() == PropertySlot::Getter) {
needsStubLink = true;
compileGetDirectOffset(regT0, regT2, regT1, cachedOffset);
stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
- } else
+ } else {
+ isDirect = true;
compileGetDirectOffset(regT0, regT1, regT0, cachedOffset);
+ }
Jump success = jump();
CodeRef stubRoutine = patchBuffer.finalizeCode();
- polymorphicStructures->list[currentIndex].set(*m_globalData, m_codeBlock->ownerExecutable(), stubRoutine, structure);
+ polymorphicStructures->list[currentIndex].set(*m_globalData, m_codeBlock->ownerExecutable(), stubRoutine, structure, isDirect);
// Finally patch the jump to slow case back in the hot path to jump here instead.
CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
Jump failureCases2 = branchPtr(NotEqual, Address(regT3, JSCell::structureOffset()), TrustedImmPtr(prototypeStructure));
bool needsStubLink = false;
+ bool isDirect = false;
if (slot.cachedPropertyType() == PropertySlot::Getter) {
needsStubLink = true;
compileGetDirectOffset(protoObject, regT2, regT1, cachedOffset);
stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
- } else
+ } else {
+ isDirect = true;
compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
+ }
Jump success = jump();
CodeRef stubRoutine = patchBuffer.finalizeCode();
- prototypeStructures->list[currentIndex].set(callFrame->globalData(), m_codeBlock->ownerExecutable(), stubRoutine, structure, prototypeStructure);
+ prototypeStructures->list[currentIndex].set(callFrame->globalData(), m_codeBlock->ownerExecutable(), stubRoutine, structure, prototypeStructure, isDirect);
// Finally patch the jump to slow case back in the hot path to jump here instead.
CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
ASSERT(protoObject);
bool needsStubLink = false;
+ bool isDirect = false;
if (slot.cachedPropertyType() == PropertySlot::Getter) {
needsStubLink = true;
compileGetDirectOffset(protoObject, regT2, regT1, cachedOffset);
stubCall.addArgument(TrustedImmPtr(const_cast<Identifier*>(&ident)));
stubCall.addArgument(TrustedImmPtr(stubInfo->callReturnLocation.executableAddress()));
stubCall.call();
- } else
+ } else {
+ isDirect = true;
compileGetDirectOffset(protoObject, regT1, regT0, cachedOffset);
+ }
Jump success = jump();
CodeRef stubRoutine = patchBuffer.finalizeCode();
// Track the stub we have created so that it will be deleted later.
- prototypeStructures->list[currentIndex].set(callFrame->globalData(), m_codeBlock->ownerExecutable(), stubRoutine, structure, chain);
+ prototypeStructures->list[currentIndex].set(callFrame->globalData(), m_codeBlock->ownerExecutable(), stubRoutine, structure, chain, isDirect);
// Finally patch the jump to slow case back in the hot path to jump here instead.
CodeLocationJump jumpLocation = stubInfo->hotPathBegin.jumpAtOffset(patchOffsetGetByIdBranchToSlowCase);
if (stubInfo->accessType == access_get_by_id_self) {
ASSERT(!stubInfo->stubRoutine);
- polymorphicStructureList = new PolymorphicAccessStructureList(callFrame->globalData(), codeBlock->ownerExecutable(), MacroAssemblerCodeRef(), stubInfo->u.getByIdSelf.baseObjectStructure.get());
+ polymorphicStructureList = new PolymorphicAccessStructureList(callFrame->globalData(), codeBlock->ownerExecutable(), MacroAssemblerCodeRef(), stubInfo->u.getByIdSelf.baseObjectStructure.get(), true);
stubInfo->initGetByIdSelfList(polymorphicStructureList, 1);
} else {
polymorphicStructureList = stubInfo->u.getByIdSelfList.structureList;
switch (stubInfo->accessType) {
case access_get_by_id_proto:
- prototypeStructureList = new PolymorphicAccessStructureList(globalData, owner, stubInfo->stubRoutine, stubInfo->u.getByIdProto.baseObjectStructure.get(), stubInfo->u.getByIdProto.prototypeStructure.get());
+ prototypeStructureList = new PolymorphicAccessStructureList(globalData, owner, stubInfo->stubRoutine, stubInfo->u.getByIdProto.baseObjectStructure.get(), stubInfo->u.getByIdProto.prototypeStructure.get(), true);
stubInfo->stubRoutine = MacroAssemblerCodeRef();
stubInfo->initGetByIdProtoList(prototypeStructureList, 2);
break;
case access_get_by_id_chain:
- prototypeStructureList = new PolymorphicAccessStructureList(globalData, owner, stubInfo->stubRoutine, stubInfo->u.getByIdChain.baseObjectStructure.get(), stubInfo->u.getByIdChain.chain.get());
+ prototypeStructureList = new PolymorphicAccessStructureList(globalData, owner, stubInfo->stubRoutine, stubInfo->u.getByIdChain.baseObjectStructure.get(), stubInfo->u.getByIdChain.chain.get(), true);
stubInfo->stubRoutine = MacroAssemblerCodeRef();
stubInfo->initGetByIdProtoList(prototypeStructureList, 2);
break;