WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
using a slightly more complicated approach
blah.patch (text/plain), 139.14 KB, created by
Filip Pizlo
on 2014-05-30 14:35:35 PDT
(
hide
)
Description:
using a slightly more complicated approach
Filename:
MIME Type:
Creator:
Filip Pizlo
Created:
2014-05-30 14:35:35 PDT
Size:
139.14 KB
patch
obsolete
>Index: Source/JavaScriptCore/ChangeLog >=================================================================== >--- Source/JavaScriptCore/ChangeLog (revision 169467) >+++ Source/JavaScriptCore/ChangeLog (working copy) >@@ -1,3 +1,201 @@ >+2014-05-29 Filip Pizlo <fpizlo@apple.com> >+ >+ AI should be able track structure sets larger than 1 >+ https://bugs.webkit.org/show_bug.cgi?id=128073 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This makes two major changes to how AI (abstract interpreter) proves that a value has >+ some structure: >+ >+ - StructureAbstractValue can now track an arbitrary number of structures. A set whose >+ size is greater than one means that the value may have any of the structures, and we >+ don't know which - but we do know that it cannot be any structure not in the set. The >+ structure abstract value can still be TOP, which means the set of all structures. We >+ artificially limit the set size to StructureAbstractValue::polymorphismLimit to guard >+ memory explosion on pathological programs. This limit is big enough that it wouldn't >+ kick in for normal code, since we have other heuristics that limit the number of >+ structures that we would allow an inline cache to know about. >+ >+ - We eagerly set watchpoints on all watchable structures and then we assume that >+ watchable structures are being watched, and that the watchpoint will jettison the code. >+ This allows tracking of watchable structures to be far simpler than before. Previously, >+ a structure being tracked as "future possible" was predicated on it being watchable but >+ we might not actually watch it. This makes algebra over sets of future possible >+ structures quite weird. But watching all watchable structures means that we simple say >+ that a structure set can be in the following states: unclobbered, which means it's just >+ a set of structures and it doesn't matter what is watchable or what isn't because we've >+ proven that the value must have one of these structures right now; and clobbered, which >+ means that we have a set of structures, plus all possible structures temporarily, with >+ invalidation removing the "plus all possible structures". Clobbering a set means that >+ if any of its structures are unwatchable, the set just becomes TOP; but if all >+ structures in the set are watchable then we just set the clobbered bit to add the "plus >+ all possible structures temporarily" thing. This precisely tracks the exact meaning of >+ watchability and invalidation points. >+ >+ Still a work in progress. >+ >+ * JavaScriptCore.xcodeproj/project.pbxproj: >+ * bytecode/StructureSet.cpp: >+ (JSC::StructureSet::clear): >+ (JSC::StructureSet::remove): >+ (JSC::StructureSet::filter): >+ (JSC::StructureSet::copyFrom): >+ * bytecode/StructureSet.h: >+ (JSC::StructureSet::StructureSet): >+ (JSC::StructureSet::isEmpty): >+ (JSC::StructureSet::genericFilter): >+ (JSC::StructureSet::ContainsOutOfLine::ContainsOutOfLine): >+ (JSC::StructureSet::ContainsOutOfLine::operator()): >+ (JSC::StructureSet::deleteStructureListIfNecessary): >+ (JSC::StructureSet::setEmpty): >+ (JSC::StructureSet::set): >+ * dfg/DFGAbstractInterpreter.h: >+ * dfg/DFGAbstractInterpreterInlines.h: >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::booleanResult): >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects): >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::forAllValues): >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::clobberStructures): >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::observeTransition): >+ (JSC::DFG::AbstractInterpreter<AbstractStateType>::dump): >+ * dfg/DFGAbstractValue.cpp: >+ (JSC::DFG::AbstractValue::setMostSpecific): >+ (JSC::DFG::AbstractValue::set): >+ (JSC::DFG::AbstractValue::filter): >+ (JSC::DFG::AbstractValue::shouldBeClear): >+ (JSC::DFG::AbstractValue::normalizeClarity): >+ (JSC::DFG::AbstractValue::checkConsistency): >+ (JSC::DFG::AbstractValue::assertIsWatched): >+ (JSC::DFG::AbstractValue::dumpInContext): >+ (JSC::DFG::AbstractValue::setFuturePossibleStructure): Deleted. >+ * dfg/DFGAbstractValue.h: >+ (JSC::DFG::AbstractValue::clear): >+ (JSC::DFG::AbstractValue::clobberStructures): >+ (JSC::DFG::AbstractValue::clobberStructuresFor): >+ (JSC::DFG::AbstractValue::observeTransition): >+ (JSC::DFG::AbstractValue::TransitionObserver::TransitionObserver): >+ (JSC::DFG::AbstractValue::TransitionObserver::operator()): >+ (JSC::DFG::AbstractValue::isHeapTop): >+ (JSC::DFG::AbstractValue::setType): >+ (JSC::DFG::AbstractValue::operator==): >+ (JSC::DFG::AbstractValue::merge): >+ (JSC::DFG::AbstractValue::validate): >+ (JSC::DFG::AbstractValue::hasClobberableState): >+ (JSC::DFG::AbstractValue::assertIsWatched): >+ (JSC::DFG::AbstractValue::observeIndexingTypeTransition): >+ (JSC::DFG::AbstractValue::makeTop): >+ (JSC::DFG::AbstractValue::bestProvenStructure): Deleted. >+ * dfg/DFGAllocator.h: >+ * dfg/DFGArgumentsSimplificationPhase.cpp: >+ (JSC::DFG::ArgumentsSimplificationPhase::run): >+ * dfg/DFGArrayMode.cpp: >+ (JSC::DFG::ArrayMode::alreadyChecked): >+ * dfg/DFGCFAPhase.cpp: >+ (JSC::DFG::CFAPhase::performBlockCFA): >+ * dfg/DFGCSEPhase.cpp: >+ (JSC::DFG::CSEPhase::checkStructureElimination): >+ (JSC::DFG::CSEPhase::structureTransitionWatchpointElimination): >+ (JSC::DFG::CSEPhase::performNodeCSE): >+ * dfg/DFGClobberize.h: >+ (JSC::DFG::clobberize): >+ * dfg/DFGConstantFoldingPhase.cpp: >+ (JSC::DFG::ConstantFoldingPhase::foldConstants): >+ (JSC::DFG::ConstantFoldingPhase::emitGetByOffset): >+ (JSC::DFG::ConstantFoldingPhase::emitPutByOffset): >+ (JSC::DFG::ConstantFoldingPhase::addStructureTransitionCheck): >+ * dfg/DFGDesiredWatchpoints.h: >+ (JSC::DFG::GenericDesiredWatchpoints::addLazily): >+ (JSC::DFG::GenericDesiredWatchpoints::assertIsWatched): >+ (JSC::DFG::DesiredWatchpoints::assertIsWatched): >+ * dfg/DFGDoesGC.cpp: >+ (JSC::DFG::doesGC): >+ * dfg/DFGFixupPhase.cpp: >+ (JSC::DFG::FixupPhase::fixupNode): >+ * dfg/DFGGraph.cpp: >+ (JSC::DFG::Graph::~Graph): >+ (JSC::DFG::Graph::visitChildren): >+ * dfg/DFGNode.h: >+ (JSC::DFG::Node::hasStructure): >+ (JSC::DFG::Node::convertToStructureTransitionWatchpoint): Deleted. >+ * dfg/DFGNodeType.h: >+ * dfg/DFGPlan.cpp: >+ (JSC::DFG::Plan::compileInThreadImpl): >+ * dfg/DFGPredictionPropagationPhase.cpp: >+ (JSC::DFG::PredictionPropagationPhase::propagate): >+ * dfg/DFGSafeToExecute.h: >+ (JSC::DFG::safeToExecute): >+ * dfg/DFGSpeculativeJIT.h: >+ (JSC::DFG::SpeculativeJIT::speculateStringObjectForStructure): >+ * dfg/DFGSpeculativeJIT32_64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * dfg/DFGSpeculativeJIT64.cpp: >+ (JSC::DFG::SpeculativeJIT::compile): >+ * dfg/DFGStructureAbstractValue.cpp: Added. >+ (JSC::DFG::StructureAbstractValue::makeTop): >+ (JSC::DFG::StructureAbstractValue::assertIsWatched): >+ (JSC::DFG::StructureAbstractValue::clobber): >+ (JSC::DFG::StructureAbstractValue::observeTransition): >+ (JSC::DFG::StructureAbstractValue::add): >+ (JSC::DFG::StructureAbstractValue::merge): >+ (JSC::DFG::StructureAbstractValue::mergeNotTop): >+ (JSC::DFG::StructureAbstractValue::filter): >+ (JSC::DFG::StructureAbstractValue::contains): >+ (JSC::DFG::StructureAbstractValue::isSubsetOf): >+ (JSC::DFG::StructureAbstractValue::isSupersetOf): >+ (JSC::DFG::StructureAbstractValue::overlaps): >+ (JSC::DFG::StructureAbstractValue::operator==): >+ (JSC::DFG::StructureAbstractValue::dumpInContext): >+ (JSC::DFG::StructureAbstractValue::dump): >+ * dfg/DFGStructureAbstractValue.h: >+ (JSC::DFG::StructureAbstractValue::StructureAbstractValue): >+ (JSC::DFG::StructureAbstractValue::clear): >+ (JSC::DFG::StructureAbstractValue::assertIsWatched): >+ (JSC::DFG::StructureAbstractValue::top): >+ (JSC::DFG::StructureAbstractValue::isClear): >+ (JSC::DFG::StructureAbstractValue::isTop): >+ (JSC::DFG::StructureAbstractValue::isNeitherClearNorTop): >+ (JSC::DFG::StructureAbstractValue::isFinite): >+ (JSC::DFG::StructureAbstractValue::size): >+ (JSC::DFG::StructureAbstractValue::at): >+ (JSC::DFG::StructureAbstractValue::operator[]): >+ (JSC::DFG::StructureAbstractValue::onlyStructure): >+ (JSC::DFG::StructureAbstractValue::makeTopWhenThin): >+ (JSC::DFG::StructureAbstractValue::makeTop): Deleted. >+ (JSC::DFG::StructureAbstractValue::add): Deleted. >+ (JSC::DFG::StructureAbstractValue::addAll): Deleted. >+ (JSC::DFG::StructureAbstractValue::contains): Deleted. >+ (JSC::DFG::StructureAbstractValue::isSubsetOf): Deleted. >+ (JSC::DFG::StructureAbstractValue::doesNotContainAnyOtherThan): Deleted. >+ (JSC::DFG::StructureAbstractValue::isSupersetOf): Deleted. >+ (JSC::DFG::StructureAbstractValue::filter): Deleted. >+ (JSC::DFG::StructureAbstractValue::isClearOrTop): Deleted. >+ (JSC::DFG::StructureAbstractValue::last): Deleted. >+ (JSC::DFG::StructureAbstractValue::speculationFromStructures): Deleted. >+ (JSC::DFG::StructureAbstractValue::isValidOffset): Deleted. >+ (JSC::DFG::StructureAbstractValue::hasSingleton): Deleted. >+ (JSC::DFG::StructureAbstractValue::singleton): Deleted. >+ (JSC::DFG::StructureAbstractValue::operator==): Deleted. >+ (JSC::DFG::StructureAbstractValue::dumpInContext): Deleted. >+ (JSC::DFG::StructureAbstractValue::dump): Deleted. >+ (JSC::DFG::StructureAbstractValue::topValue): Deleted. >+ * dfg/DFGTypeCheckHoistingPhase.cpp: >+ (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantStructureChecks): >+ (JSC::DFG::TypeCheckHoistingPhase::identifyRedundantArrayChecks): >+ * dfg/DFGWatchableStructureWatchingPhase.cpp: Added. >+ (JSC::DFG::WatchableStructureWatchingPhase::WatchableStructureWatchingPhase): >+ (JSC::DFG::WatchableStructureWatchingPhase::run): >+ (JSC::DFG::WatchableStructureWatchingPhase::tryWatch): >+ (JSC::DFG::performWatchableStructureWatching): >+ * dfg/DFGWatchableStructureWatchingPhase.h: Added. >+ * dfg/DFGWatchpointCollectionPhase.cpp: >+ (JSC::DFG::WatchpointCollectionPhase::handle): >+ * ftl/FTLCapabilities.cpp: >+ (JSC::FTL::canCompile): >+ * ftl/FTLLowerDFGToLLVM.cpp: >+ (JSC::FTL::LowerDFGToLLVM::compileNode): >+ (JSC::FTL::LowerDFGToLLVM::speculateStringObjectForStructureID): >+ (JSC::FTL::LowerDFGToLLVM::compileStructureTransitionWatchpoint): Deleted. >+ > 2014-05-21 Filip Pizlo <fpizlo@apple.com> > > [ftlopt] DFG::clobberize should be blind to the effects of GC >Index: Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (revision 169200) >+++ Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (working copy) >@@ -353,6 +353,7 @@ > 0F8335B81639C1EA001443B5 /* ArrayAllocationProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F8364B7164B0C110053329A /* DFGBranchDirection.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F885E111849A3BE00F1E3FA /* BytecodeUseDef.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F885E101849A3BE00F1E3FA /* BytecodeUseDef.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0F893BDB1936E23C001211F4 /* DFGStructureAbstractValue.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F893BDA1936E23C001211F4 /* DFGStructureAbstractValue.cpp */; }; > 0F8F2B95172E04A0007DBDA5 /* FTLLink.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F2B93172E049E007DBDA5 /* FTLLink.cpp */; }; > 0F8F2B96172E04A3007DBDA5 /* FTLLink.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F8F2B94172E049E007DBDA5 /* FTLLink.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F8F2B99172F04FF007DBDA5 /* DFGDesiredIdentifiers.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F8F2B97172F04FD007DBDA5 /* DFGDesiredIdentifiers.cpp */; }; >@@ -495,6 +496,8 @@ > 0FCEFADF180738C000472CE4 /* FTLLocation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FCEFADD180738C000472CE4 /* FTLLocation.cpp */; }; > 0FCEFAE0180738C000472CE4 /* FTLLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FCEFADE180738C000472CE4 /* FTLLocation.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FD2C92416D01EE900C7803F /* StructureInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD2C92316D01EE900C7803F /* StructureInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0FD2D4FE192FEE5800A178BF /* DFGWatchableStructureWatchingPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FD2D4FC192FEE5800A178BF /* DFGWatchableStructureWatchingPhase.cpp */; }; >+ 0FD2D4FF192FEE5800A178BF /* DFGWatchableStructureWatchingPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD2D4FD192FEE5800A178BF /* DFGWatchableStructureWatchingPhase.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FD3C82614115D4000FD81CB /* DFGDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FD3C82014115CF800FD81CB /* DFGDriver.cpp */; }; > 0FD3C82814115D4F00FD81CB /* DFGDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD3C82214115D0E00FD81CB /* DFGDriver.h */; }; > 0FD81AD2154FB4EE00983E72 /* DFGDominators.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FD81ACF154FB4EB00983E72 /* DFGDominators.cpp */; }; >@@ -2160,6 +2163,7 @@ > 0F8335B51639C1E3001443B5 /* ArrayAllocationProfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ArrayAllocationProfile.h; sourceTree = "<group>"; }; > 0F8364B5164B0C0E0053329A /* DFGBranchDirection.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGBranchDirection.h; path = dfg/DFGBranchDirection.h; sourceTree = "<group>"; }; > 0F885E101849A3BE00F1E3FA /* BytecodeUseDef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BytecodeUseDef.h; sourceTree = "<group>"; }; >+ 0F893BDA1936E23C001211F4 /* DFGStructureAbstractValue.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGStructureAbstractValue.cpp; path = dfg/DFGStructureAbstractValue.cpp; sourceTree = "<group>"; }; > 0F8F2B93172E049E007DBDA5 /* FTLLink.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = FTLLink.cpp; path = ftl/FTLLink.cpp; sourceTree = "<group>"; }; > 0F8F2B94172E049E007DBDA5 /* FTLLink.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = FTLLink.h; path = ftl/FTLLink.h; sourceTree = "<group>"; }; > 0F8F2B97172F04FD007DBDA5 /* DFGDesiredIdentifiers.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = DFGDesiredIdentifiers.cpp; path = dfg/DFGDesiredIdentifiers.cpp; sourceTree = "<group>"; }; >@@ -2313,6 +2317,8 @@ > 0FCEFADD180738C000472CE4 /* FTLLocation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLLocation.cpp; path = ftl/FTLLocation.cpp; sourceTree = "<group>"; }; > 0FCEFADE180738C000472CE4 /* FTLLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLLocation.h; path = ftl/FTLLocation.h; sourceTree = "<group>"; }; > 0FD2C92316D01EE900C7803F /* StructureInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StructureInlines.h; sourceTree = "<group>"; }; >+ 0FD2D4FC192FEE5800A178BF /* DFGWatchableStructureWatchingPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGWatchableStructureWatchingPhase.cpp; path = dfg/DFGWatchableStructureWatchingPhase.cpp; sourceTree = "<group>"; }; >+ 0FD2D4FD192FEE5800A178BF /* DFGWatchableStructureWatchingPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGWatchableStructureWatchingPhase.h; path = dfg/DFGWatchableStructureWatchingPhase.h; sourceTree = "<group>"; }; > 0FD3C82014115CF800FD81CB /* DFGDriver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGDriver.cpp; path = dfg/DFGDriver.cpp; sourceTree = "<group>"; }; > 0FD3C82214115D0E00FD81CB /* DFGDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGDriver.h; path = dfg/DFGDriver.h; sourceTree = "<group>"; }; > 0FD5652216AB780A00197653 /* DFGBasicBlockInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGBasicBlockInlines.h; path = dfg/DFGBasicBlockInlines.h; sourceTree = "<group>"; }; >@@ -4806,6 +4812,7 @@ > 2ACCF3DD185FE26B0083E2AD /* DFGStoreBarrierElisionPhase.h */, > 0FC20CB31852E2C600C9E954 /* DFGStrengthReductionPhase.cpp */, > 0FC20CB41852E2C600C9E954 /* DFGStrengthReductionPhase.h */, >+ 0F893BDA1936E23C001211F4 /* DFGStructureAbstractValue.cpp */, > 0F63947615DCE347006A597C /* DFGStructureAbstractValue.h */, > 0F2FCCF718A60070001A27F8 /* DFGThreadData.cpp */, > 0F2FCCF818A60070001A27F8 /* DFGThreadData.h */, >@@ -4838,6 +4845,8 @@ > 0F2BDC431522801700CD8910 /* DFGVariableEventStream.h */, > 0FFFC95314EF909500C72532 /* DFGVirtualRegisterAllocationPhase.cpp */, > 0FFFC95414EF909500C72532 /* DFGVirtualRegisterAllocationPhase.h */, >+ 0FD2D4FC192FEE5800A178BF /* DFGWatchableStructureWatchingPhase.cpp */, >+ 0FD2D4FD192FEE5800A178BF /* DFGWatchableStructureWatchingPhase.h */, > 0FC97F3B18202119002C9B26 /* DFGWatchpointCollectionPhase.cpp */, > 0FC97F3C18202119002C9B26 /* DFGWatchpointCollectionPhase.h */, > 0FDB2CE5174830A2007B3C1B /* DFGWorklist.cpp */, >@@ -5776,6 +5785,7 @@ > 0F485328187DFDEC0083B687 /* FTLAvailableRecovery.h in Headers */, > 0FEA0A0A170513DB00BB722C /* FTLCapabilities.h in Headers */, > 0FEA0A231709606900BB722C /* FTLCommonValues.h in Headers */, >+ 0FD2D4FF192FEE5800A178BF /* DFGWatchableStructureWatchingPhase.h in Headers */, > 0FEA0A0C170513DB00BB722C /* FTLCompile.h in Headers */, > 0FE95F7A18B5694700B531FB /* FTLDataSection.h in Headers */, > 2AC922BC18A16182003CE0FB /* FTLDWARFDebugLineInfo.h in Headers */, >@@ -6956,6 +6966,7 @@ > 0FF0F19916B729F6005DF95B /* DFGLongLivedState.cpp in Sources */, > A767B5B517A0B9650063D940 /* DFGLoopPreHeaderCreationPhase.cpp in Sources */, > 0F2BDC4D1522818600CD8910 /* DFGMinifiedNode.cpp in Sources */, >+ 0FD2D4FE192FEE5800A178BF /* DFGWatchableStructureWatchingPhase.cpp in Sources */, > A737810D1799EA2E00817533 /* DFGNaturalLoops.cpp in Sources */, > 0FF0F19C16B72A03005DF95B /* DFGNode.cpp in Sources */, > 0FA581BA150E952C00B9A2D9 /* DFGNodeFlags.cpp in Sources */, >@@ -7174,6 +7185,7 @@ > 95F6E6950E5B5F970091E860 /* JSProfilerPrivate.cpp in Sources */, > 7C184E1A17BEDBD3007CB63A /* JSPromise.cpp in Sources */, > 7C184E2217BEE240007CB63A /* JSPromiseConstructor.cpp in Sources */, >+ 0F893BDB1936E23C001211F4 /* DFGStructureAbstractValue.cpp in Sources */, > 7C008CDA187124BB00955C24 /* JSPromiseDeferred.cpp in Sources */, > 7C008CD2186F8A9300955C24 /* JSPromiseFunctions.cpp in Sources */, > 7C184E1E17BEE22E007CB63A /* JSPromisePrototype.cpp in Sources */, >Index: Source/JavaScriptCore/bytecode/StructureSet.cpp >=================================================================== >--- Source/JavaScriptCore/bytecode/StructureSet.cpp (revision 169200) >+++ Source/JavaScriptCore/bytecode/StructureSet.cpp (working copy) >@@ -47,7 +47,7 @@ StructureSet& StructureSet::operator=(co > void StructureSet::clear() > { > deleteStructureListIfNecessary(); >- set(0, true); >+ setEmpty(); > } > > bool StructureSet::add(Structure* structure) >@@ -75,7 +75,7 @@ bool StructureSet::remove(Structure* str > { > if (isThin()) { > if (singleStructure() == structure) { >- set(0, true); >+ setEmpty(); > return true; > } > return false; >@@ -88,7 +88,7 @@ bool StructureSet::remove(Structure* str > list->list()[i] = list->list()[--list->m_length]; > if (!list->m_length) { > OutOfLineList::destroy(list); >- set(0, true); >+ setEmpty(); > } > return true; > } >@@ -144,23 +144,8 @@ void StructureSet::filter(const Structur > return; > } > >- if (isThin()) { >- if (!singleStructure()) >- return; >- if (other.containsOutOfLine(singleStructure())) >- return; >- clear(); >- return; >- } >- >- OutOfLineList* list = structureList(); >- for (unsigned i = 0; i < list->m_length; ++i) { >- if (other.containsOutOfLine(list->list()[i])) >- continue; >- list->list()[i--] = list->list()[--list->m_length]; >- } >- if (!list->m_length) >- clear(); >+ ContainsOutOfLine containsOutOfLine(other); >+ genericFilter(containsOutOfLine); > } > > void StructureSet::exclude(const StructureSet& other) >@@ -323,8 +308,10 @@ bool StructureSet::containsOutOfLine(Str > > void StructureSet::copyFrom(const StructureSet& other) > { >- if (other.isThin()) { >+ if (other.isThin() || other.m_pointer == reservedValue) { >+ bool value = getReservedFlag(); > m_pointer = other.m_pointer; >+ setReserved(value); > return; > } > >Index: Source/JavaScriptCore/bytecode/StructureSet.h >=================================================================== >--- Source/JavaScriptCore/bytecode/StructureSet.h (revision 169200) >+++ Source/JavaScriptCore/bytecode/StructureSet.h (working copy) >@@ -33,11 +33,15 @@ > > namespace JSC { > >+namespace DFG { >+class StructureAbstractValue; >+} >+ > class StructureSet { > public: > StructureSet() > { >- set(0, true); >+ setEmpty(); > } > > StructureSet(Structure* structure) >@@ -67,7 +71,10 @@ public: > > bool isEmpty() const > { >- return isThin() && !singleStructure(); >+ bool result = isThin() && !singleStructure(); >+ if (result) >+ ASSERT(m_pointer != reservedValue); >+ return result; > } > > bool add(Structure*); >@@ -78,6 +85,28 @@ public: > void filter(const StructureSet&); > void exclude(const StructureSet&); > >+ template<typename Functor> >+ void genericFilter(Functor& functor) >+ { >+ if (isThin()) { >+ if (!singleStructure()) >+ return; >+ if (functor(singleStructure())) >+ return; >+ clear(); >+ return; >+ } >+ >+ OutOfLineList* list = structureList(); >+ for (unsigned i = 0; i < list->m_length; ++i) { >+ if (functor(list->list()[i])) >+ continue; >+ list->list()[i--] = list->list()[--list->m_length]; >+ } >+ if (!list->m_length) >+ clear(); >+ } >+ > bool isSubsetOf(const StructureSet&) const; > bool isSupersetOf(const StructureSet& other) const > { >@@ -124,15 +153,33 @@ public: > void dump(PrintStream&) const; > > private: >+ friend class DFG::StructureAbstractValue; >+ > static const uintptr_t thinFlag = 1; > static const uintptr_t reservedFlag = 2; >- static const uintptr_t flags = 3; >+ static const uintptr_t flags = thinFlag | reservedFlag; > static const uintptr_t reservedValue = 4; > > static const unsigned defaultStartingSize = 4; > > bool addOutOfLine(Structure*); > bool containsOutOfLine(Structure*) const; >+ >+ class ContainsOutOfLine { >+ public: >+ ContainsOutOfLine(const StructureSet& set) >+ : m_set(set) >+ { >+ } >+ >+ bool operator()(Structure* structure) >+ { >+ return m_set.containsOutOfLine(structure); >+ } >+ private: >+ const StructureSet& m_set; >+ }; >+ > void copyFrom(const StructureSet&); > > class OutOfLineList { >@@ -154,7 +201,7 @@ private: > > void deleteStructureListIfNecessary() > { >- if (!isThin()) >+ if (!isThin() && m_pointer != reservedValue) > OutOfLineList::destroy(structureList()); > } > >@@ -185,10 +232,22 @@ private: > { > set(bitwise_cast<uintptr_t>(structures), false); > } >+ void setEmpty() >+ { >+ set(0, true); >+ } > void set(uintptr_t pointer, bool singleStructure) > { > m_pointer = pointer | (singleStructure ? thinFlag : 0) | (m_pointer & reservedFlag); > } >+ bool getReservedFlag() const { return m_pointer & reservedFlag; } >+ void setReservedFlag(bool value) >+ { >+ if (value) >+ m_pointer |= reservedFlag; >+ else >+ m_pointer &= ~reservedFlag; >+ } > > uintptr_t m_pointer; > }; >Index: Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h (working copy) >@@ -114,6 +114,7 @@ public: > bool executeEffects(unsigned indexInBlock); > bool executeEffects(unsigned clobberLimit, Node*); > >+ void dump(PrintStream& out) const; > void dump(PrintStream& out); > > template<typename T> >@@ -148,7 +149,12 @@ public: > private: > void clobberWorld(const CodeOrigin&, unsigned indexInBlock); > void clobberCapturedVars(const CodeOrigin&); >+ >+ template<typename Functor> >+ void forAllValues(unsigned indexInBlock, Functor&); >+ > void clobberStructures(unsigned indexInBlock); >+ void observeTransition(unsigned indexInBlock, Structure* from, Structure* to); > > enum BooleanResult { > UnknownBooleanResult, >Index: Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (working copy) >@@ -62,11 +62,17 @@ AbstractInterpreter<AbstractStateType>:: > } > > // Next check if we can fold because we know that the source is an object or string and does not equal undefined. >- if (isCellSpeculation(value.m_type) >- && value.m_currentKnownStructure.hasSingleton()) { >- Structure* structure = value.m_currentKnownStructure.singleton(); >- if (!structure->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic)) >- && structure->typeInfo().type() != StringType) >+ if (isCellSpeculation(value.m_type) && !value.m_structure.isTop()) { >+ bool allTrue = true; >+ for (unsigned i = value.m_structure.size(); i--;) { >+ Structure* structure = value.m_structure[i]; >+ if (structure->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->origin.semantic)) >+ || structure->typeInfo().type() == StringType) { >+ allTrue = false; >+ break; >+ } >+ } >+ if (allTrue) > return DefinitelyTrue; > } > >@@ -105,9 +111,18 @@ void AbstractInterpreter<AbstractStateTy > } > > template<typename AbstractStateType> >-void AbstractInterpreter<AbstractStateType>::verifyEdge(Node*, Edge edge) >+void AbstractInterpreter<AbstractStateType>::verifyEdge(Node* node, Edge edge) > { >- RELEASE_ASSERT(!(forNode(edge).m_type & ~typeFilterFor(edge.useKind()))); >+ if (!(forNode(edge).m_type & ~typeFilterFor(edge.useKind()))) >+ return; >+ >+ startCrashing(); >+ dataLog("Edge verification error: ", node, "->", edge, " was expected to have type ", SpeculationDump(typeFilterFor(edge.useKind())), " but has type ", SpeculationDump(forNode(edge).m_type), "\n"); >+ dataLog(" Full value: ", forNode(edge), "\n"); >+ dataLog("\n"); >+ dataLog("Graph at time of failure:\n"); >+ m_graph.dump(); >+ RELEASE_ASSERT_NOT_REACHED(); > } > > template<typename AbstractStateType> >@@ -1456,7 +1471,12 @@ bool AbstractInterpreter<AbstractStateTy > break; > } > if (isCellSpeculation(node->child1()->prediction())) { >- if (Structure* structure = forNode(node->child1()).bestProvenStructure()) { >+ // This use of onlyStructure() should be replaced by giving GetByIdStatus the ability >+ // to compute things based on a StructureSet, and then to factor ByteCodeParser's >+ // ability to generate code based on a GetByIdStatus out of ByteCodeParser so that >+ // ConstantFoldingPhase can use it. >+ // https://bugs.webkit.org/show_bug.cgi?id=133229 >+ if (Structure* structure = forNode(node->child1()).m_structure.onlyStructure()) { > GetByIdStatus status = GetByIdStatus::computeFor( > m_graph.m_vm, structure, > m_graph.identifiers()[node->identifierNumber()]); >@@ -1466,9 +1486,14 @@ bool AbstractInterpreter<AbstractStateTy > ASSERT(status[0].structureSet().size() == 1); > ASSERT(!status[0].chain()); > >- if (status[0].specificValue()) >+ if (status[0].specificValue()) { >+ if (status[0].specificValue().isCell()) { >+ Structure* structure = status[0].specificValue().asCell()->structure(); >+ if (structure->transitionWatchpointSetIsStillValid()) >+ m_graph.watchpoints().addLazily(structure->transitionWatchpointSet()); >+ } > setConstant(node, status[0].specificValue()); >- else >+ } else > forNode(node).makeHeapTop(); > filter(node->child1(), status[0].structureSet()); > >@@ -1502,8 +1527,12 @@ bool AbstractInterpreter<AbstractStateTy > ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this. > > StructureSet& set = node->structureSet(); >- >- if (value.m_currentKnownStructure.isSubsetOf(set)) { >+ >+ // It's interesting that we could have proven that the object has a larger structure set >+ // that includes the set we're testing. In that case we could make the structure check >+ // more efficient. We currently don't. >+ >+ if (value.m_structure.isSubsetOf(set)) { > m_state.setFoundConstants(true); > break; > } >@@ -1511,33 +1540,17 @@ bool AbstractInterpreter<AbstractStateTy > node->setCanExit(true); > m_state.setHaveStructures(true); > >- // If this structure check is attempting to prove knowledge already held in >- // the futurePossibleStructure set then the constant folding phase should >- // turn this into a watchpoint instead. >- if (value.m_futurePossibleStructure.isSubsetOf(set) >- && value.m_futurePossibleStructure.hasSingleton()) { >- m_state.setFoundConstants(true); >- filter(value, value.m_futurePossibleStructure.singleton()); >- break; >- } >- > filter(value, set); > break; > } > >- case StructureTransitionWatchpoint: { >- AbstractValue& value = forNode(node->child1()); >- >- filter(value, node->structure()); >- m_state.setHaveStructures(true); >- node->setCanExit(true); >- break; >- } >- > case PutStructure: > case PhantomPutStructure: >- if (!forNode(node->child1()).m_currentKnownStructure.isClear()) { >- clobberStructures(clobberLimit); >+ if (!forNode(node->child1()).m_structure.isClear()) { >+ observeTransition( >+ clobberLimit, >+ node->structureTransitionData().previousStructure, >+ node->structureTransitionData().newStructure); > forNode(node->child1()).set(m_graph, node->structureTransitionData().newStructure); > m_state.setHaveStructures(true); > } >@@ -1616,13 +1629,39 @@ bool AbstractInterpreter<AbstractStateTy > } > case ArrayifyToStructure: { > AbstractValue& value = forNode(node->child1()); >- StructureSet set = node->structure(); >- if (value.m_futurePossibleStructure.isSubsetOf(set) >- || value.m_currentKnownStructure.isSubsetOf(set)) >+ if (value.m_structure.isSubsetOf(StructureSet(node->structure()))) > m_state.setFoundConstants(true); > node->setCanExit(true); > clobberStructures(clobberLimit); >- filter(value, set); >+ >+ // We have a bunch of options of how to express the abstract set at this point. Let set S >+ // be the set of structures that the value had before clobbering and assume that all of >+ // them are watchable. The new value should be the least expressible upper bound of the >+ // intersection of "values that currently have structure = node->structure()" and "values >+ // that have structure in S plus any structure transition-reachable from S". Assume that >+ // node->structure() is not in S but it is transition-reachable from S. Then we would >+ // like to say that the result is "values that have structure = node->structure() until >+ // we invalidate", but there is no way to express this using the AbstractValue syntax. So >+ // we must choose between: >+ // >+ // 1) "values that currently have structure = node->structure()". This is a valid >+ // superset of the value that we really want, and it's specific enough to satisfy the >+ // preconditions of the array access that this is guarding. It's also specific enough >+ // to allow relevant optimizations in the case that we didn't have a contradiction >+ // like in this example. Notice that in the abscence of any contradiction, this result >+ // is precise rather than being a conservative LUB. >+ // >+ // 2) "values that currently hava structure in S plus any structure transition-reachable >+ // from S". This is also a valid superset of the value that we really want, but it's >+ // not specific enough to satisfy the preconditions of the array access that this is >+ // guarding - so playing such shenanigans would preclude us from having assertions on >+ // the typing preconditions of any array accesses. This would also not be a desirable >+ // answer in the absence of a contradiction. >+ // >+ // Note that it's tempting to simply say that the resulting value is BOTTOM because of >+ // the contradiction. That would be wrong, since we haven't hit an invalidation point, >+ // yet. >+ value.set(m_graph, node->structure()); > m_state.setHaveStructures(true); > break; > } >@@ -1651,7 +1690,11 @@ bool AbstractInterpreter<AbstractStateTy > AbstractValue& value = forNode(node->child1()); > ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this. > >- if (Structure* structure = value.bestProvenStructure()) { >+ // This should just filter down the cases in MultiGetByOffset. If that results in all >+ // cases having the same offset then we should strength reduce it to a CheckStructure + >+ // GetByOffset. >+ // https://bugs.webkit.org/show_bug.cgi?id=133229 >+ if (Structure* structure = value.m_structure.onlyStructure()) { > bool done = false; > for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;) { > const GetByIdVariant& variant = node->multiGetByOffsetData().variants[i]; >@@ -1688,7 +1731,12 @@ bool AbstractInterpreter<AbstractStateTy > AbstractValue& value = forNode(node->child1()); > ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this. > >- if (Structure* structure = value.bestProvenStructure()) { >+ // This should just filter down the cases in MultiPutByOffset. If that results in either >+ // one case, or nothing but replace cases and they have the same offset, then we should >+ // just strength reduce it to the appropriate combination of CheckStructure, >+ // [Re]AllocatePropertyStorage, PutStructure, and PutByOffset. >+ // https://bugs.webkit.org/show_bug.cgi?id=133229 >+ if (Structure* structure = value.m_structure.onlyStructure()) { > bool done = false; > for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) { > const PutByIdVariant& variant = node->multiPutByOffsetData().variants[i]; >@@ -1721,12 +1769,12 @@ bool AbstractInterpreter<AbstractStateTy > for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) { > const PutByIdVariant& variant = node->multiPutByOffsetData().variants[i]; > if (variant.kind() == PutByIdVariant::Replace) { >- if (value.m_currentKnownStructure.contains(variant.structure())) >+ if (value.m_structure.contains(variant.structure())) > newSet.add(variant.structure()); > continue; > } > ASSERT(variant.kind() == PutByIdVariant::Transition); >- if (value.m_currentKnownStructure.contains(variant.oldStructure())) >+ if (value.m_structure.contains(variant.oldStructure())) > newSet.add(variant.newStructure()); > } > >@@ -1767,7 +1815,12 @@ bool AbstractInterpreter<AbstractStateTy > case PutByIdFlush: > case PutByIdDirect: > node->setCanExit(true); >- if (Structure* structure = forNode(node->child1()).bestProvenStructure()) { >+ // This use of onlyStructure() should be replaced by giving PutByIdStatus the ability >+ // to compute things based on a StructureSet, and then to factor ByteCodeParser's >+ // ability to generate code based on a PutByIdStatus out of ByteCodeParser so that >+ // ConstantFoldingPhase can use it. >+ // https://bugs.webkit.org/show_bug.cgi?id=133229 >+ if (Structure* structure = forNode(node->child1()).m_structure.onlyStructure()) { > PutByIdStatus status = PutByIdStatus::computeFor( > m_graph.m_vm, > m_graph.globalObjectFor(node->origin.semantic), >@@ -1781,7 +1834,10 @@ bool AbstractInterpreter<AbstractStateTy > m_state.setHaveStructures(true); > break; > } >- if (status[0].kind() == PutByIdVariant::Transition) { >+ if (status[0].kind() == PutByIdVariant::Transition >+ && structure->transitionWatchpointSetHasBeenInvalidated()) { >+ if (status[0].newStructure()->transitionWatchpointSetIsStillValid()) >+ m_graph.watchpoints().addLazily(status[0].newStructure()->transitionWatchpointSet()); > clobberStructures(clobberLimit); > forNode(node->child1()).set(m_graph, status[0].newStructure()); > m_state.setHaveStructures(true); >@@ -1855,6 +1911,7 @@ bool AbstractInterpreter<AbstractStateTy > > case InvalidationPoint: > node->setCanExit(true); >+ forAllValues(clobberLimit, AbstractValue::observeInvalidationPointFor); > break; > > case CheckWatchdogTimer: >@@ -1957,32 +2014,59 @@ void AbstractInterpreter<AbstractStateTy > } > > template<typename AbstractStateType> >-void AbstractInterpreter<AbstractStateType>::clobberStructures(unsigned clobberLimit) >+template<typename Functor> >+void AbstractInterpreter<AbstractStateType>::forAllValues( >+ unsigned clobberLimit, Functor& functor) > { >- if (!m_state.haveStructures()) >- return; > if (clobberLimit >= m_state.block()->size()) > clobberLimit = m_state.block()->size(); > else > clobberLimit++; > ASSERT(clobberLimit <= m_state.block()->size()); > for (size_t i = clobberLimit; i--;) >- forNode(m_state.block()->at(i)).clobberStructures(); >+ functor(forNode(m_state.block()->at(i))); > if (m_graph.m_form == SSA) { > HashSet<Node*>::iterator iter = m_state.block()->ssa->liveAtHead.begin(); > HashSet<Node*>::iterator end = m_state.block()->ssa->liveAtHead.end(); > for (; iter != end; ++iter) >- forNode(*iter).clobberStructures(); >+ functor(forNode(*iter)); > } > for (size_t i = m_state.variables().numberOfArguments(); i--;) >- m_state.variables().argument(i).clobberStructures(); >+ functor(m_state.variables().argument(i)); > for (size_t i = m_state.variables().numberOfLocals(); i--;) >- m_state.variables().local(i).clobberStructures(); >+ functor(m_state.variables().local(i)); > m_state.setHaveStructures(true); > m_state.setDidClobber(true); > } > > template<typename AbstractStateType> >+void AbstractInterpreter<AbstractStateType>::clobberStructures(unsigned clobberLimit) >+{ >+ if (!m_state.haveStructures()) >+ return; >+ forAllValues(clobberLimit, AbstractValue::clobberStructuresFor); >+ m_state.setHaveStructures(false); >+ m_state.setDidClobber(true); >+} >+ >+template<typename AbstractStateType> >+void AbstractInterpreter<AbstractStateType>::observeTransition( >+ unsigned clobberLimit, Structure* from, Structure* to) >+{ >+ if (!m_state.haveStructures()) >+ return; >+ AbstractValue::TransitionObserver transitionObserver(from, to); >+ forAllValues(clobberLimit, transitionObserver); >+ m_state.setDidClobber(true); >+} >+ >+template<typename AbstractStateType> >+void AbstractInterpreter<AbstractStateType>::dump(PrintStream& out) const >+{ >+ const_cast<AbstractInterpreter<AbstractStateType>*>(this)->dump(out); >+} >+ >+template<typename AbstractStateType> > void AbstractInterpreter<AbstractStateType>::dump(PrintStream& out) > { > CommaPrinter comma(" "); >Index: Source/JavaScriptCore/dfg/DFGAbstractValue.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractValue.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGAbstractValue.cpp (working copy) >@@ -37,12 +37,12 @@ void AbstractValue::setMostSpecific(Grap > { > if (!!value && value.isCell()) { > Structure* structure = value.asCell()->structure(); >- m_currentKnownStructure = structure; >- setFuturePossibleStructure(graph, structure); >+ if (structure->transitionWatchpointSetIsStillValid()) >+ graph.watchpoints().addLazily(structure->transitionWatchpointSet()); >+ m_structure = structure; > m_arrayModes = asArrayModes(structure->indexingType()); > } else { >- m_currentKnownStructure.clear(); >- m_futurePossibleStructure.clear(); >+ m_structure.clear(); > m_arrayModes = 0; > } > >@@ -50,19 +50,20 @@ void AbstractValue::setMostSpecific(Grap > m_value = value; > > checkConsistency(); >+ assertIsWatched(graph); > } > > void AbstractValue::set(Graph& graph, JSValue value) > { > if (!!value && value.isCell()) { >- m_currentKnownStructure.makeTop(); >+ m_structure.makeTop(); > Structure* structure = value.asCell()->structure(); >- setFuturePossibleStructure(graph, structure); >+ if (structure->transitionWatchpointSetIsStillValid()) >+ m_structure = structure; > m_arrayModes = asArrayModes(structure->indexingType()); > clobberArrayModes(); > } else { >- m_currentKnownStructure.clear(); >- m_futurePossibleStructure.clear(); >+ m_structure.clear(); > m_arrayModes = 0; > } > >@@ -70,17 +71,18 @@ void AbstractValue::set(Graph& graph, JS > m_value = value; > > checkConsistency(); >+ assertIsWatched(graph); > } > > void AbstractValue::set(Graph& graph, Structure* structure) > { >- m_currentKnownStructure = structure; >- setFuturePossibleStructure(graph, structure); >+ m_structure = structure; > m_arrayModes = asArrayModes(structure->indexingType()); > m_type = speculationFromStructure(structure); > m_value = JSValue(); > > checkConsistency(); >+ assertIsWatched(graph); > } > > void AbstractValue::fixTypeForRepresentation(NodeFlags representation) >@@ -129,21 +131,18 @@ FiltrationResult AbstractValue::filter(G > > m_type &= other.speculationFromStructures(); > m_arrayModes &= other.arrayModesFromStructures(); >- m_currentKnownStructure.filter(other); >+ m_structure.filter(other); > > // It's possible that prior to the above two statements we had (Foo, TOP), where > // Foo is a SpeculatedType that is disjoint with the passed StructureSet. In that > // case, we will now have (None, [someStructure]). In general, we need to make > // sure that new information gleaned from the SpeculatedType needs to be fed back > // into the information gleaned from the StructureSet. >- m_currentKnownStructure.filter(m_type); >+ m_structure.filter(m_type); > >- if (m_currentKnownStructure.hasSingleton()) >- setFuturePossibleStructure(graph, m_currentKnownStructure.singleton()); >- > filterArrayModesByType(); > filterValueByType(); >- return normalizeClarity(); >+ return normalizeClarity(graph); > } > > FiltrationResult AbstractValue::filterArrayModes(ArrayModes arrayModes) >@@ -169,8 +168,7 @@ FiltrationResult AbstractValue::filter(S > // the passed type is Array. At this point we'll have (None, TOP). The best way > // to ensure that the structure filtering does the right thing is to filter on > // the new type (None) rather than the one passed (Array). >- m_currentKnownStructure.filter(m_type); >- m_futurePossibleStructure.filter(m_type); >+ m_structure.filter(type); > filterArrayModesByType(); > filterValueByType(); > return normalizeClarity(); >@@ -184,15 +182,6 @@ FiltrationResult AbstractValue::filterBy > return result; > } > >-void AbstractValue::setFuturePossibleStructure(Graph& graph, Structure* structure) >-{ >- ASSERT(structure); >- if (graph.watchpoints().isStillValid(structure->transitionWatchpointSet())) >- m_futurePossibleStructure = structure; >- else >- m_futurePossibleStructure.makeTop(); >-} >- > void AbstractValue::filterValueByType() > { > // We could go further, and ensure that if the futurePossibleStructure contravenes >@@ -238,8 +227,7 @@ bool AbstractValue::shouldBeClear() cons > return true; > > if (!(m_type & ~SpecCell) >- && (!m_arrayModes >- || m_currentKnownStructure.isClear())) >+ && (!m_arrayModes || m_structure.isClear())) > return true; > > return false; >@@ -263,12 +251,18 @@ FiltrationResult AbstractValue::normaliz > return result; > } > >+FiltrationResult AbstractValue::normalizeClarity(Graph& graph) >+{ >+ FiltrationResult result = normalizeClarity(); >+ assertIsWatched(graph); >+ return result; >+} >+ > #if !ASSERT_DISABLED > void AbstractValue::checkConsistency() const > { > if (!(m_type & SpecCell)) { >- ASSERT(m_currentKnownStructure.isClear()); >- ASSERT(m_futurePossibleStructure.isClear()); >+ ASSERT(m_structure.isClear()); > ASSERT(!m_arrayModes); > } > >@@ -289,6 +283,11 @@ void AbstractValue::checkConsistency() c > // we don't want to get pedantic about this as it would only increase the computational > // complexity of the code. > } >+ >+void AbstractValue::assertIsWatched(Graph& graph) const >+{ >+ m_structure.assertIsWatched(graph); >+} > #endif > > void AbstractValue::dump(PrintStream& out) const >@@ -302,8 +301,7 @@ void AbstractValue::dumpInContext(PrintS > if (m_type & SpecCell) { > out.print( > ", ", ArrayModesDump(m_arrayModes), ", ", >- inContext(m_currentKnownStructure, context), ", ", >- inContext(m_futurePossibleStructure, context)); >+ inContext(m_structure, context)); > } > if (!!m_value) > out.print(", ", inContext(m_value, context)); >Index: Source/JavaScriptCore/dfg/DFGAbstractValue.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractValue.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGAbstractValue.h (working copy) >@@ -53,8 +53,7 @@ struct AbstractValue { > { > m_type = SpecNone; > m_arrayModes = 0; >- m_currentKnownStructure.clear(); >- m_futurePossibleStructure.clear(); >+ m_structure.clear(); > m_value = JSValue(); > checkConsistency(); > } >@@ -75,15 +74,57 @@ struct AbstractValue { > void clobberStructures() > { > if (m_type & SpecCell) { >- m_currentKnownStructure.makeTop(); >+ m_structure.clobber(); > clobberArrayModes(); > } else { >- ASSERT(m_currentKnownStructure.isClear()); >+ ASSERT(m_structure.isClear()); > ASSERT(!m_arrayModes); > } > checkConsistency(); > } >+ >+ static void clobberStructuresFor(AbstractValue& value) >+ { >+ value.clobberStructures(); >+ } >+ >+ void observeInvalidationPoint() >+ { >+ m_structure.observeInvalidationPoint(); >+ checkConsistency(); >+ } >+ >+ static void observeInvalidationPointFor(AbstractValue& value) >+ { >+ value.observeInvalidationPoint(); >+ } >+ >+ void observeTransition(Structure* from, Structure* to) >+ { >+ if (m_type & SpecCell) { >+ m_structure.observeTransition(from, to); >+ observeIndexingTypeTransition(from->indexingType(), to->indexingType()); >+ } >+ checkConsistency(); >+ } >+ >+ class TransitionObserver { >+ public: >+ TransitionObserver(Structure* from, Structure* to) >+ : m_from(from) >+ , m_to(to) >+ { >+ } > >+ void operator()(AbstractValue& value) >+ { >+ value.observeTransition(m_from, m_to); >+ } >+ private: >+ Structure* m_from; >+ Structure* m_to; >+ }; >+ > void clobberValue() > { > m_value = JSValue(); >@@ -91,7 +132,10 @@ struct AbstractValue { > > bool isHeapTop() const > { >- return (m_type | SpecHeapTop) == m_type && m_currentKnownStructure.isTop() && m_futurePossibleStructure.isTop(); >+ return (m_type | SpecHeapTop) == m_type >+ && m_structure.isTop() >+ && m_arrayModes == ALL_ARRAY_MODES >+ && !m_value; > } > > bool valueIsTop() const >@@ -118,12 +162,10 @@ struct AbstractValue { > void setType(SpeculatedType type) > { > if (type & SpecCell) { >- m_currentKnownStructure.makeTop(); >- m_futurePossibleStructure.makeTop(); >+ m_structure.makeTop(); > m_arrayModes = ALL_ARRAY_MODES; > } else { >- m_currentKnownStructure.clear(); >- m_futurePossibleStructure.clear(); >+ m_structure.clear(); > m_arrayModes = 0; > } > m_type = type; >@@ -138,8 +180,7 @@ struct AbstractValue { > { > return m_type == other.m_type > && m_arrayModes == other.m_arrayModes >- && m_currentKnownStructure == other.m_currentKnownStructure >- && m_futurePossibleStructure == other.m_futurePossibleStructure >+ && m_structure == other.m_structure > && m_value == other.m_value; > } > bool operator!=(const AbstractValue& other) const >@@ -162,8 +203,7 @@ struct AbstractValue { > } else { > result |= mergeSpeculation(m_type, other.m_type); > result |= mergeArrayModes(m_arrayModes, other.m_arrayModes); >- result |= m_currentKnownStructure.addAll(other.m_currentKnownStructure); >- result |= m_futurePossibleStructure.addAll(other.m_futurePossibleStructure); >+ result |= m_structure.merge(other.m_structure); > if (m_value != other.m_value) { > result |= !!m_value; > m_value = JSValue(); >@@ -179,8 +219,7 @@ struct AbstractValue { > mergeSpeculation(m_type, type); > > if (type & SpecCell) { >- m_currentKnownStructure.makeTop(); >- m_futurePossibleStructure.makeTop(); >+ m_structure.makeTop(); > m_arrayModes = ALL_ARRAY_MODES; > } > m_value = JSValue(); >@@ -225,75 +264,30 @@ struct AbstractValue { > if (!!value && value.isCell()) { > ASSERT(m_type & SpecCell); > Structure* structure = value.asCell()->structure(); >- return m_currentKnownStructure.contains(structure) >- && m_futurePossibleStructure.contains(structure) >+ return m_structure.contains(structure) > && (m_arrayModes & asArrayModes(structure->indexingType())); > } > > return true; > } > >- Structure* bestProvenStructure() const >- { >- if (m_currentKnownStructure.hasSingleton()) >- return m_currentKnownStructure.singleton(); >- if (m_futurePossibleStructure.hasSingleton()) >- return m_futurePossibleStructure.singleton(); >- return 0; >- } >- > bool hasClobberableState() const > { >- return m_currentKnownStructure.isNeitherClearNorTop() >+ return m_structure.isNeitherClearNorTop() > || !arrayModesAreClearOrTop(m_arrayModes); > } > > #if ASSERT_DISABLED > void checkConsistency() const { } >+ void assertIsWatched(Graph&) const { } > #else > void checkConsistency() const; >+ void assertIsWatched(Graph&) const; > #endif > > void dumpInContext(PrintStream&, DumpContext*) const; > void dump(PrintStream&) const; > >- // A great way to think about the difference between m_currentKnownStructure and >- // m_futurePossibleStructure is to consider these four examples: >- // >- // 1) x = foo(); >- // >- // In this case x's m_currentKnownStructure and m_futurePossibleStructure will >- // both be TOP, since we don't know anything about x for sure, yet. >- // >- // 2) x = foo(); >- // y = x.f; >- // >- // Where x will later have a new property added to it, 'g'. Because of the >- // known but not-yet-executed property addition, x's current structure will >- // not be watchpointable; hence we have no way of statically bounding the set >- // of possible structures that x may have if a clobbering event happens. So, >- // x's m_currentKnownStructure will be whatever structure we check to get >- // property 'f', and m_futurePossibleStructure will be TOP. >- // >- // 3) x = foo(); >- // y = x.f; >- // >- // Where x has a terminal structure that is still watchpointable. In this case, >- // x's m_currentKnownStructure and m_futurePossibleStructure will both be >- // whatever structure we checked for when getting 'f'. >- // >- // 4) x = foo(); >- // y = x.f; >- // bar(); >- // >- // Where x has a terminal structure that is still watchpointable. In this >- // case, m_currentKnownStructure will be TOP because bar() may potentially >- // change x's structure and we have no way of proving otherwise, but >- // x's m_futurePossibleStructure will be whatever structure we had checked >- // when getting property 'f'. >- >- // NB. All fields in this struct must have trivial destructors. >- > // This is a proven constraint on the structures that this value can have right > // now. The structure of the current value must belong to this set. The set may > // be TOP, indicating that it is the set of all possible structures, in which >@@ -301,29 +295,12 @@ struct AbstractValue { > // in which case this value cannot be a cell. This is all subject to change > // anytime a new value is assigned to this one, anytime there is a control flow > // merge, or most crucially, anytime a side-effect or structure check happens. >- // In case of a side-effect, we typically must assume that any value may have >- // had its structure changed, hence contravening our proof. We make the proof >- // valid again by switching this to TOP (i.e. claiming that we have proved that >- // this value may have any structure). Of note is that the proof represented by >- // this field is not subject to structure transition watchpoints - even if one >- // fires, we can be sure that this proof is still valid. >- StructureAbstractValue m_currentKnownStructure; >- >- // This is a proven constraint on the structures that this value can have now >- // or any time in the future subject to the structure transition watchpoints of >- // all members of this set not having fired. This set is impervious to side- >- // effects; even if one happens the side-effect can only cause the value to >- // change to at worst another structure that is also a member of this set. But, >- // the theorem being proved by this field is predicated upon there not being >- // any new structure transitions introduced into any members of this set. In >- // cases where there is no way for us to guard this happening, the set must be >- // TOP. But in cases where we can guard new structure transitions (all members >- // of the set have still-valid structure transition watchpoints) then this set >- // will be finite. Anytime that we make use of the finite nature of this set, >- // we must first issue a structure transition watchpoint, which will effectively >- // result in m_currentKnownStructure being filtered according to >- // m_futurePossibleStructure. >- StructureAbstractValue m_futurePossibleStructure; >+ // In case of a side-effect, we must assume that any value with a structure that >+ // isn't being watched may have had its structure changed, hence contravening >+ // our proof. In such a case we make the proof valid again by switching this to >+ // TOP (i.e. claiming that we have proved that this value may have any >+ // structure). >+ StructureAbstractValue m_structure; > > // This is a proven constraint on the possible types that this value can have > // now or any time in the future, unless it is reassigned. This field is >@@ -332,11 +309,11 @@ struct AbstractValue { > // between this field, and the structure fields above, is as follows. The > // fields above constraint the structures that a cell may have, but they say > // nothing about whether or not the value is known to be a cell. More formally, >- // the m_currentKnownStructure is itself an abstract value that consists of the >+ // the m_structure is itself an abstract value that consists of the > // union of the set of all non-cell values and the set of cell values that have > // the given structure. This abstract value is then the intersection of the >- // m_currentKnownStructure and the set of values whose type is m_type. So, for >- // example if m_type is SpecFinal|SpecInt32 and m_currentKnownStructure is >+ // m_structure and the set of values whose type is m_type. So, for >+ // example if m_type is SpecFinal|SpecInt32 and m_structure is > // [0x12345] then this abstract value corresponds to the set of all integers > // unified with the set of all objects with structure 0x12345. > SpeculatedType m_type; >@@ -364,6 +341,12 @@ private: > m_arrayModes = ALL_ARRAY_MODES; > } > >+ void observeIndexingTypeTransition(IndexingType from, IndexingType to) >+ { >+ if (m_arrayModes & asArrayModes(from)) >+ m_arrayModes |= asArrayModes(to); >+ } >+ > bool validateType(JSValue value) const > { > if (isHeapTop()) >@@ -391,19 +374,17 @@ private: > { > m_type |= top; > m_arrayModes = ALL_ARRAY_MODES; >- m_currentKnownStructure.makeTop(); >- m_futurePossibleStructure.makeTop(); >+ m_structure.makeTop(); > m_value = JSValue(); > checkConsistency(); > } > >- void setFuturePossibleStructure(Graph&, Structure* structure); >- > void filterValueByType(); > void filterArrayModesByType(); > > bool shouldBeClear() const; > FiltrationResult normalizeClarity(); >+ FiltrationResult normalizeClarity(Graph&); > }; > > } } // namespace JSC::DFG >Index: Source/JavaScriptCore/dfg/DFGAllocator.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAllocator.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGAllocator.h (working copy) >@@ -50,7 +50,7 @@ public: > void* allocate(); // Use placement new to allocate, and avoid using this method. > void free(T*); // Call this method to delete; never use 'delete' directly. > >- void freeAll(); // Only call this if T has a trivial destructor. >+ void freeAll(); // Only call this if you've either freed everything or if T has a trivial destructor. > void reset(); // Like freeAll(), but also returns all memory to the OS. > > unsigned indexOf(const T*); >Index: Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp (working copy) >@@ -325,7 +325,6 @@ public: > break; > > case CheckStructure: >- case StructureTransitionWatchpoint: > case CheckArray: > // We don't care about these because if we get uses of the relevant > // variable then we can safely get rid of these, too. This of course >@@ -441,7 +440,6 @@ public: > } > > case CheckStructure: >- case StructureTransitionWatchpoint: > case CheckArray: { > // We can just get rid of this node, if it references a phantom argument. > if (!isOKToOptimize(node->child1().node())) >Index: Source/JavaScriptCore/dfg/DFGArrayMode.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGArrayMode.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGArrayMode.cpp (working copy) >@@ -289,25 +289,48 @@ Structure* ArrayMode::originalArrayStruc > bool ArrayMode::alreadyChecked(Graph& graph, Node* node, AbstractValue& value, IndexingType shape) const > { > switch (arrayClass()) { >- case Array::OriginalArray: >- return value.m_currentKnownStructure.hasSingleton() >- && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape >- && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray) >- && graph.globalObjectFor(node->origin.semantic)->isOriginalArrayStructure(value.m_currentKnownStructure.singleton()); >+ case Array::OriginalArray: { >+ if (value.m_structure.isTop()) >+ return false; >+ for (unsigned i = value.m_structure.size(); i--;) { >+ Structure* structure = value.m_structure[i]; >+ if ((structure->indexingType() & IndexingShapeMask) != shape) >+ return false; >+ if (!(structure->indexingType() & IsArray)) >+ return false; >+ if (!graph.globalObjectFor(node->origin.semantic)->isOriginalArrayStructure(structure)) >+ return false; >+ } >+ return true; >+ } > >- case Array::Array: >+ case Array::Array: { > if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(shape | IsArray))) > return true; >- return value.m_currentKnownStructure.hasSingleton() >- && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape >- && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); >+ if (value.m_structure.isTop()) >+ return false; >+ for (unsigned i = value.m_structure.size(); i--;) { >+ Structure* structure = value.m_structure[i]; >+ if ((structure->indexingType() & IndexingShapeMask) != shape) >+ return false; >+ if (!(structure->indexingType() & IsArray)) >+ return false; >+ } >+ return true; >+ } > >- default: >+ default: { > if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(shape) | asArrayModes(shape | IsArray))) > return true; >- return value.m_currentKnownStructure.hasSingleton() >- && (value.m_currentKnownStructure.singleton()->indexingType() & IndexingShapeMask) == shape; >- } >+ if (value.m_structure.isTop()) >+ return false; >+ for (unsigned i = value.m_structure.size(); i--;) { >+ Structure* structure = value.m_structure[i]; >+ if ((structure->indexingType() & IndexingShapeMask) != shape) >+ return false; >+ } >+ return true; >+ } } > } > > bool ArrayMode::alreadyChecked(Graph& graph, Node* node, AbstractValue& value) const >@@ -336,23 +359,38 @@ bool ArrayMode::alreadyChecked(Graph& gr > > case Array::SlowPutArrayStorage: > switch (arrayClass()) { >- case Array::OriginalArray: >+ case Array::OriginalArray: { > CRASH(); > return false; >+ } > >- case Array::Array: >+ case Array::Array: { > if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(ArrayWithArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage))) > return true; >- return value.m_currentKnownStructure.hasSingleton() >- && hasAnyArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()) >- && (value.m_currentKnownStructure.singleton()->indexingType() & IsArray); >+ if (value.m_structure.isTop()) >+ return false; >+ for (unsigned i = value.m_structure.size(); i--;) { >+ Structure* structure = value.m_structure[i]; >+ if (!hasAnyArrayStorage(structure->indexingType())) >+ return false; >+ if (!(structure->indexingType() & IsArray)) >+ return false; >+ } >+ return true; >+ } > >- default: >+ default: { > if (arrayModesAlreadyChecked(value.m_arrayModes, asArrayModes(NonArrayWithArrayStorage) | asArrayModes(ArrayWithArrayStorage) | asArrayModes(NonArrayWithSlowPutArrayStorage) | asArrayModes(ArrayWithSlowPutArrayStorage))) > return true; >- return value.m_currentKnownStructure.hasSingleton() >- && hasAnyArrayStorage(value.m_currentKnownStructure.singleton()->indexingType()); >- } >+ if (value.m_structure.isTop()) >+ return false; >+ for (unsigned i = value.m_structure.size(); i--;) { >+ Structure* structure = value.m_structure[i]; >+ if (!hasAnyArrayStorage(structure->indexingType())) >+ return false; >+ } >+ return true; >+ } } > > case Array::Arguments: > return speculationChecked(value.m_type, SpecArguments); >Index: Source/JavaScriptCore/dfg/DFGCFAPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGCFAPhase.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGCFAPhase.cpp (working copy) >@@ -102,7 +102,7 @@ private: > if (!safeToExecute(m_state, m_graph, node)) > dataLog("(UNSAFE) "); > >- m_interpreter.dump(WTF::dataFile()); >+ dataLog(m_state.variables(), " ", m_interpreter); > > if (m_state.haveStructures()) > dataLog(" (Have Structures)"); >Index: Source/JavaScriptCore/dfg/DFGCSEPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGCSEPhase.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGCSEPhase.cpp (working copy) >@@ -460,12 +460,6 @@ private: > return true; > break; > >- case StructureTransitionWatchpoint: >- if (node->child1() == child1 >- && structureSet.contains(node->structure())) >- return true; >- break; >- > case PutStructure: > if (node->child1() == child1 > && structureSet.contains(node->structureTransitionData().newStructure)) >@@ -547,11 +541,6 @@ private: > } > return false; > >- case StructureTransitionWatchpoint: >- if (node->structure() == structure && node->child1() == child1) >- return true; >- break; >- > case Arrayify: > case ArrayifyToStructure: > // We could check if the arrayification could affect our structures. >@@ -1387,13 +1376,6 @@ private: > eliminate(); > break; > >- case StructureTransitionWatchpoint: >- if (cseMode == StoreElimination) >- break; >- if (structureTransitionWatchpointElimination(node->structure(), node->child1().node())) >- eliminate(); >- break; >- > case PutStructure: > if (cseMode == NormalCSE) > break; >Index: Source/JavaScriptCore/dfg/DFGClobberize.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGClobberize.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGClobberize.h (working copy) >@@ -418,7 +418,6 @@ void clobberize(Graph& graph, Node* node > } > > case CheckStructure: >- case StructureTransitionWatchpoint: > case InstanceOf: > read(JSCell_structureID); > return; >Index: Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp (working copy) >@@ -97,27 +97,12 @@ private: > set = node->structure(); > else > set = node->structureSet(); >- if (value.m_currentKnownStructure.isSubsetOf(set)) { >+ if (value.m_structure.isSubsetOf(set)) { > m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell. > node->convertToPhantom(); > eliminated = true; > break; > } >- StructureAbstractValue& structureValue = value.m_futurePossibleStructure; >- if (structureValue.isSubsetOf(set) >- && structureValue.hasSingleton()) { >- Structure* structure = structureValue.singleton(); >- m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell. >- AdjacencyList children = node->children; >- children.removeEdge(0); >- if (!!children.child1()) >- m_insertionSet.insertNode(indexInBlock, SpecNone, Phantom, node->origin, children); >- node->children.setChild2(Edge()); >- node->children.setChild3(Edge()); >- node->convertToStructureTransitionWatchpoint(structure); >- eliminated = true; >- break; >- } > break; > } > >@@ -156,7 +141,7 @@ private: > Node* child = childEdge.node(); > MultiGetByOffsetData& data = node->multiGetByOffsetData(); > >- Structure* structure = m_state.forNode(child).bestProvenStructure(); >+ Structure* structure = m_state.forNode(child).m_structure.onlyStructure(); > if (!structure) > break; > >@@ -180,7 +165,7 @@ private: > Node* child = childEdge.node(); > MultiPutByOffsetData& data = node->multiPutByOffsetData(); > >- Structure* structure = m_state.forNode(child).bestProvenStructure(); >+ Structure* structure = m_state.forNode(child).m_structure.onlyStructure(); > if (!structure) > break; > >@@ -205,7 +190,7 @@ private: > if (childEdge.useKind() != CellUse) > break; > >- Structure* structure = m_state.forNode(child).bestProvenStructure(); >+ Structure* structure = m_state.forNode(child).m_structure.onlyStructure(); > if (!structure) > break; > >@@ -232,7 +217,7 @@ private: > > ASSERT(childEdge.useKind() == CellUse); > >- Structure* structure = m_state.forNode(child).bestProvenStructure(); >+ Structure* structure = m_state.forNode(child).m_structure.onlyStructure(); > if (!structure) > break; > >@@ -329,7 +314,6 @@ private: > Edge childEdge = node->child1(); > Node* child = childEdge.node(); > >- bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton(); > bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell; > > ASSERT(!variant.chain()); >@@ -340,11 +324,7 @@ private: > // do any eliminations. > m_interpreter.execute(indexInBlock); > >- if (needsWatchpoint) { >- m_insertionSet.insertNode( >- indexInBlock, SpecNone, StructureTransitionWatchpoint, origin, >- OpInfo(structure), childEdge); >- } else if (needsCellCheck) { >+ if (needsCellCheck) { > m_insertionSet.insertNode( > indexInBlock, SpecNone, Phantom, origin, childEdge); > } >@@ -381,7 +361,6 @@ private: > > ASSERT(variant.oldStructure() == structure); > >- bool needsWatchpoint = !m_state.forNode(child).m_currentKnownStructure.hasSingleton(); > bool needsCellCheck = m_state.forNode(child).m_type & ~SpecCell; > > // Now before we do anything else, push the CFA forward over the PutById >@@ -389,11 +368,7 @@ private: > // do any eliminations. > m_interpreter.execute(indexInBlock); > >- if (needsWatchpoint) { >- m_insertionSet.insertNode( >- indexInBlock, SpecNone, StructureTransitionWatchpoint, origin, >- OpInfo(structure), childEdge); >- } else if (needsCellCheck) { >+ if (needsCellCheck) { > m_insertionSet.insertNode( > indexInBlock, SpecNone, Phantom, origin, childEdge); > } >@@ -475,16 +450,14 @@ private: > > void addStructureTransitionCheck(NodeOrigin origin, unsigned indexInBlock, JSCell* cell) > { >- Node* weakConstant = m_insertionSet.insertNode( >- indexInBlock, speculationFromValue(cell), WeakJSConstant, origin, OpInfo(cell)); >- >- if (m_graph.watchpoints().isStillValid(cell->structure()->transitionWatchpointSet())) { >- m_insertionSet.insertNode( >- indexInBlock, SpecNone, StructureTransitionWatchpoint, origin, >- OpInfo(cell->structure()), Edge(weakConstant, CellUse)); >+ if (cell->structure()->transitionWatchpointSetIsStillValid()) { >+ m_graph.watchpoints().addLazily(cell->structure()->transitionWatchpointSet()); > return; > } > >+ Node* weakConstant = m_insertionSet.insertNode( >+ indexInBlock, speculationFromValue(cell), WeakJSConstant, origin, OpInfo(cell)); >+ > m_insertionSet.insertNode( > indexInBlock, SpecNone, CheckStructure, origin, > OpInfo(m_graph.addStructureSet(cell->structure())), Edge(weakConstant, CellUse)); >Index: Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h (working copy) >@@ -97,6 +97,9 @@ public: > > void addLazily(CodeOrigin codeOrigin, ExitKind exitKind, WatchpointSetType* set) > { >+#if !ASSERT_DISABLED >+ m_profiledSets.add(set); >+#endif > m_profiledWatchpoints.append( > WatchpointForGenericWatchpointSet<WatchpointSetType>(codeOrigin, exitKind, set)); > } >@@ -172,11 +175,17 @@ public: > { > return isStillValid(set) || shouldAssumeMixedState(set); > } >+ >+ void assertIsWatched(WatchpointSetType* set) const >+ { >+ ASSERT(m_sets.contains(set) || m_profiledSets.contains(set)); >+ } > > private: > Vector<WatchpointForGenericWatchpointSet<WatchpointSetType>> m_profiledWatchpoints; > HashSet<WatchpointSetType*> m_sets; > #if !ASSERT_DISABLED >+ HashSet<WatchpointSetType*> m_profiledSets; > StateMap m_firstKnownState; > #endif > bool m_reallyAdded; >@@ -233,6 +242,18 @@ public: > { > return m_bufferViews.isValidOrMixed(view); > } >+ void assertIsWatched(WatchpointSet* set) >+ { >+ m_sets.assertIsWatched(set); >+ } >+ void assertIsWatched(InlineWatchpointSet& set) >+ { >+ m_inlineSets.assertIsWatched(&set); >+ } >+ void assertIsWatched(JSArrayBufferView* view) >+ { >+ m_bufferViews.assertIsWatched(view); >+ } > > private: > GenericDesiredWatchpoints<WatchpointSet> m_sets; >Index: Source/JavaScriptCore/dfg/DFGDoesGC.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGDoesGC.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGDoesGC.cpp (working copy) >@@ -182,7 +182,6 @@ bool doesGC(Graph& graph, Node* node) > case PutByValDirect: > case PutByVal: > case PutByValAlias: >- case StructureTransitionWatchpoint: > case PutStructure: > case PhantomPutStructure: > case GetByOffset: >Index: Source/JavaScriptCore/dfg/DFGFixupPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGFixupPhase.cpp (working copy) >@@ -930,7 +930,6 @@ private: > > case CheckExecutable: > case CheckStructure: >- case StructureTransitionWatchpoint: > case CheckFunction: > case CheckHasInstance: > case CreateThis: >Index: Source/JavaScriptCore/dfg/DFGGraph.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGGraph.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGGraph.cpp (working copy) >@@ -77,6 +77,16 @@ Graph::Graph(VM& vm, Plan& plan, LongLiv > > Graph::~Graph() > { >+ for (BlockIndex blockIndex = numBlocks(); blockIndex--;) { >+ BasicBlock* block = this->block(blockIndex); >+ if (!block) >+ continue; >+ >+ for (unsigned phiIndex = block->phis.size(); phiIndex--;) >+ m_allocator.free(block->phis[phiIndex]); >+ for (unsigned nodeIndex = block->size(); nodeIndex--;) >+ m_allocator.free(block->at(nodeIndex)); >+ } > m_allocator.freeAll(); > } > >@@ -846,7 +856,6 @@ void Graph::visitChildren(SlotVisitor& v > visitor.appendUnbarrieredReadOnlyPointer(node->structureSet()[i]); > break; > >- case StructureTransitionWatchpoint: > case NewObject: > case ArrayifyToStructure: > case NewStringObject: >@@ -863,6 +872,31 @@ void Graph::visitChildren(SlotVisitor& v > node->structureTransitionData().newStructure); > break; > >+ case MultiGetByOffset: >+ for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;) { >+ GetByIdVariant& variant = node->multiGetByOffsetData().variants[i]; >+ visitor.appendUnbarrieredReadOnlyValue(variant.specificValue()); >+ const StructureSet& set = variant.structureSet(); >+ for (unsigned j = set.size(); j--;) >+ visitor.appendUnbarrieredReadOnlyPointer(set[j]); >+ >+ // Don't need to mark anything in the structure chain because that would >+ // have been decomposed into CheckStructure's. Don't need to mark the >+ // callLinkStatus because we wouldn't use MultiGetByOffset if any of the >+ // variants did that. >+ ASSERT(!variant.callLinkStatus()); >+ } >+ break; >+ >+ case MultiPutByOffset: >+ for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) { >+ PutByIdVariant& variant = node->multiPutByOffsetData().variants[i]; >+ visitor.appendUnbarrieredReadOnlyPointer(variant.oldStructure()); >+ if (variant.kind() == PutByIdVariant::Transition) >+ visitor.appendUnbarrieredReadOnlyPointer(variant.newStructure()); >+ } >+ break; >+ > default: > break; > } >Index: Source/JavaScriptCore/dfg/DFGGraph.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGGraph.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGGraph.h (working copy) >@@ -171,6 +171,8 @@ public: > > void convertToConstant(Node* node, JSValue value) > { >+ if (value.isCell() && value.asCell()->structure()->transitionWatchpointSetIsStillValid()) >+ watchpoints().assertIsWatched(value.asCell()->structure()->transitionWatchpointSet()); > if (value.isObject()) > node->convertToWeakConstant(value.asCell()); > else >Index: Source/JavaScriptCore/dfg/DFGNode.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNode.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGNode.h (working copy) >@@ -485,21 +485,6 @@ struct Node { > children.reset(); > } > >- void convertToStructureTransitionWatchpoint(Structure* structure) >- { >- ASSERT(m_op == CheckStructure || m_op == ArrayifyToStructure); >- ASSERT(!child2()); >- ASSERT(!child3()); >- ASSERT(structure); >- m_opInfo = bitwise_cast<uintptr_t>(structure); >- m_op = StructureTransitionWatchpoint; >- } >- >- void convertToStructureTransitionWatchpoint() >- { >- convertToStructureTransitionWatchpoint(structureSet().onlyStructure()); >- } >- > void convertToGetByOffset(unsigned storageAccessDataIndex, Edge storage) > { > ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == MultiGetByOffset); >@@ -1129,7 +1114,6 @@ struct Node { > bool hasStructure() > { > switch (op()) { >- case StructureTransitionWatchpoint: > case ArrayifyToStructure: > case NewObject: > case NewStringObject: >Index: Source/JavaScriptCore/dfg/DFGNodeType.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGNodeType.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGNodeType.h (working copy) >@@ -153,18 +153,6 @@ namespace JSC { namespace DFG { > macro(PutByIdDirect, NodeMustGenerate | NodeClobbersWorld) \ > macro(CheckStructure, NodeMustGenerate) \ > macro(CheckExecutable, NodeMustGenerate) \ >- /* Transition watchpoints are a contract between the party setting the watchpoint */\ >- /* and the runtime system, where the party promises that the child object once had */\ >- /* the structure being watched, and the runtime system in turn promises that the */\ >- /* watchpoint will be turned into an OSR exit if any object with that structure */\ >- /* ever transitions to a different structure. Hence, the child object must have */\ >- /* previously had a CheckStructure executed on it or we're dealing with an object */\ >- /* constant (WeakJSConstant) and the object was known to have that structure at */\ >- /* compile-time. In the latter case this means that no structure checks have to be */\ >- /* performed for this object by JITted code. In the former case this means that*/\ >- /* the object's structure does not need to be rechecked due to side-effecting */\ >- /* (clobbering) operations. */\ >- macro(StructureTransitionWatchpoint, NodeMustGenerate) \ > macro(PutStructure, NodeMustGenerate) \ > macro(PhantomPutStructure, NodeMustGenerate | NodeDoesNotExit) \ > macro(AllocatePropertyStorage, NodeMustGenerate | NodeDoesNotExit | NodeResultStorage) \ >Index: Source/JavaScriptCore/dfg/DFGPlan.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGPlan.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGPlan.cpp (working copy) >@@ -63,6 +63,7 @@ > #include "DFGUnificationPhase.h" > #include "DFGValidate.h" > #include "DFGVirtualRegisterAllocationPhase.h" >+#include "DFGWatchableStructureWatchingPhase.h" > #include "DFGWatchpointCollectionPhase.h" > #include "Debugger.h" > #include "OperandsInlines.h" >@@ -233,6 +234,7 @@ Plan::CompilationPath Plan::compileInThr > performBackwardsPropagation(dfg); > performPredictionPropagation(dfg); > performFixup(dfg); >+ performWatchableStructureWatching(dfg); > performInvalidationPointInjection(dfg); > performTypeCheckHoisting(dfg); > >Index: Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGPredictionPropagationPhase.cpp (working copy) >@@ -576,7 +576,6 @@ private: > case SetArgument: > case CheckStructure: > case CheckExecutable: >- case StructureTransitionWatchpoint: > case CheckFunction: > case PutStructure: > case TearOffActivation: >Index: Source/JavaScriptCore/dfg/DFGSafeToExecute.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSafeToExecute.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGSafeToExecute.h (working copy) >@@ -275,22 +275,26 @@ bool safeToExecute(AbstractStateType& st > return node->arrayMode().modeForPut().alreadyChecked( > graph, node, state.forNode(graph.varArgChild(node, 0))); > >- case StructureTransitionWatchpoint: >- return state.forNode(node->child1()).m_futurePossibleStructure.isSubsetOf( >- StructureSet(node->structure())); >- > case PutStructure: > case PhantomPutStructure: > case AllocatePropertyStorage: > case ReallocatePropertyStorage: >- return state.forNode(node->child1()).m_currentKnownStructure.isSubsetOf( >+ return state.forNode(node->child1()).m_structure.isSubsetOf( > StructureSet(node->structureTransitionData().previousStructure)); > > case GetByOffset: > case GetGetterSetterByOffset: >- case PutByOffset: >- return state.forNode(node->child1()).m_currentKnownStructure.isValidOffset( >- graph.m_storageAccessData[node->storageAccessDataIndex()].offset); >+ case PutByOffset: { >+ StructureAbstractValue& value = state.forNode(node->child1()).m_structure; >+ if (value.isTop()) >+ return false; >+ PropertyOffset offset = graph.m_storageAccessData[node->storageAccessDataIndex()].offset; >+ for (unsigned i = value.size(); i--;) { >+ if (!value[i]->isValidOffset(offset)) >+ return false; >+ } >+ return true; >+ } > > case LastNodeType: > RELEASE_ASSERT_NOT_REACHED(); >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (working copy) >@@ -3697,29 +3697,6 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >- case StructureTransitionWatchpoint: { >- // There is a fascinating question here of what to do about array profiling. >- // We *could* try to tell the OSR exit about where the base of the access is. >- // The DFG will have kept it alive, though it may not be in a register, and >- // we shouldn't really load it since that could be a waste. For now though, >- // we'll just rely on the fact that when a watchpoint fires then that's >- // quite a hint already. >- >- m_jit.addWeakReference(node->structure()); >- >-#if !ASSERT_DISABLED >- SpeculateCellOperand op1(this, node->child1()); >- JITCompiler::Jump isOK = m_jit.branchPtr(JITCompiler::Equal, JITCompiler::Address(op1.gpr(), JSCell::structureIDOffset()), TrustedImmPtr(node->structure())); >- m_jit.breakpoint(); >- isOK.link(&m_jit); >-#else >- speculateCell(node->child1()); >-#endif >- >- noResult(node); >- break; >- } >- > case PhantomPutStructure: { > ASSERT(isKnownCell(node->child1().node())); > m_jit.jitCode()->common.notifyCompilingStructureTransition(m_jit.graph().m_plan, m_jit.codeBlock(), node); >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (working copy) >@@ -3767,32 +3767,6 @@ void SpeculativeJIT::compile(Node* node) > break; > } > >- case StructureTransitionWatchpoint: { >- // There is a fascinating question here of what to do about array profiling. >- // We *could* try to tell the OSR exit about where the base of the access is. >- // The DFG will have kept it alive, though it may not be in a register, and >- // we shouldn't really load it since that could be a waste. For now though, >- // we'll just rely on the fact that when a watchpoint fires then that's >- // quite a hint already. >- >- m_jit.addWeakReference(node->structure()); >- >-#if !ASSERT_DISABLED >- SpeculateCellOperand op1(this, node->child1()); >- JITCompiler::Jump isOK = m_jit.branchStructurePtr( >- JITCompiler::Equal, >- JITCompiler::Address(op1.gpr(), JSCell::structureIDOffset()), >- node->structure()); >- m_jit.breakpoint(); >- isOK.link(&m_jit); >-#else >- speculateCell(node->child1()); >-#endif >- >- noResult(node); >- break; >- } >- > case PhantomPutStructure: { > ASSERT(isKnownCell(node->child1().node())); > m_jit.jitCode()->common.notifyCompilingStructureTransition(m_jit.graph().m_plan, m_jit.codeBlock(), node); >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (working copy) >@@ -3115,7 +3115,7 @@ void SpeculativeJIT::speculateStringObje > Structure* stringObjectStructure = > m_jit.globalObjectFor(m_currentNode->origin.semantic)->stringObjectStructure(); > >- if (!m_state.forNode(edge).m_currentKnownStructure.isSubsetOf(StructureSet(stringObjectStructure))) { >+ if (!m_state.forNode(edge).m_structure.isSubsetOf(StructureSet(stringObjectStructure))) { > speculationCheck( > NotStringObject, JSValueRegs(), 0, > m_jit.branchStructurePtr( >Index: Source/JavaScriptCore/dfg/DFGStructureAbstractValue.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGStructureAbstractValue.cpp (revision 0) >+++ Source/JavaScriptCore/dfg/DFGStructureAbstractValue.cpp (working copy) >@@ -0,0 +1,374 @@ >+/* >+ * 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 "DFGStructureAbstractValue.h" >+ >+#if ENABLE(DFG_JIT) >+ >+#include "DFGGraph.h" >+ >+namespace JSC { namespace DFG { >+ >+StructureAbstractValue::StructureAbstractValue(const StructureAbstractValue& other) >+ : m_set(other.m_set) >+{ >+ setClobbered(other.isClobbered()); >+} >+ >+void StructureAbstractValue::makeTop() >+{ >+ m_set.deleteStructureListIfNecessary(); >+ m_set.m_pointer = topValue; >+} >+ >+void StructureAbstractValue::assertIsWatched(Graph& graph) const >+{ >+ if (isTop()) >+ return; >+ >+ for (unsigned i = size(); i--;) { >+ Structure* structure = at(i); >+ if (structure->transitionWatchpointSetHasBeenInvalidated()) >+ continue; >+ graph.watchpoints().assertIsWatched(structure->transitionWatchpointSet()); >+ } >+} >+ >+void StructureAbstractValue::clobber() >+{ >+ // The premise of this approach to clobbering is that anytime we introduce >+ // a watchable structure into an abstract value, we watchpoint it. You can assert >+ // that this holds by calling assertIsWatched(). >+ >+ if (isTop()) >+ return; >+ >+ setClobbered(true); >+ >+ if (m_set.isThin()) { >+ if (!m_set.singleStructure()) >+ return; >+ if (m_set.singleStructure()->transitionWatchpointSetHasBeenInvalidated()) >+ makeTopWhenThin(); >+ return; >+ } >+ >+ StructureSet::OutOfLineList* list = m_set.structureList(); >+ for (unsigned i = list->m_length; i--;) { >+ if (list->list()[i]->transitionWatchpointSetHasBeenInvalidated()) { >+ makeTop(); >+ return; >+ } >+ } >+} >+ >+void StructureAbstractValue::observeTransition(Structure* from, Structure* to) >+{ >+ if (isTop()) >+ return; >+ >+ if (!m_set.contains(from)) >+ return; >+ >+ if (from->transitionWatchpointSetIsStillValid()) { >+ setClobbered(true); >+ return; >+ } >+ >+ if (!m_set.add(to)) >+ return; >+ >+ if (m_set.size() > polymorphismLimit) >+ makeTop(); >+} >+ >+bool StructureAbstractValue::add(Structure* structure) >+{ >+ if (isTop()) >+ return false; >+ >+ if (!m_set.add(structure)) >+ return false; >+ >+ if (m_set.size() > polymorphismLimit) >+ makeTop(); >+ >+ return true; >+} >+ >+bool StructureAbstractValue::merge(const StructureSet& other) >+{ >+ if (isTop()) >+ return false; >+ >+ return mergeNotTop(other); >+} >+ >+bool StructureAbstractValue::merge(const StructureAbstractValue& other) >+{ >+ if (isTop()) >+ return false; >+ >+ if (other.isTop()) { >+ makeTop(); >+ return true; >+ } >+ >+ // It isn't immediately obvious that the code below is doing the right thing, so let's go >+ // through it. >+ // >+ // This not clobbered, other not clobbered: Clearly, we don't want to make anything clobbered >+ // since we just have two sets and we are merging them. mergeNotTop() can handle this just >+ // fine. >+ // >+ // This clobbered, other clobbered: Clobbered means that we have a set of things, plus we >+ // temporarily have the set of all things but the latter will go away once we hit the next >+ // invalidation point. This allows us to merge two clobbered sets the natural way. For now >+ // the set will still be TOP (and so we keep the clobbered bit set), but we know that after >+ // invalidation, we will have the union of the this and other. >+ // >+ // This clobbered, other not clobbered: It's safe to merge in other for both before and after >+ // invalidation, so long as we leave the clobbered bit set. Before invalidation this has no >+ // effect since the set will still appear to have all things in it. The way to think about >+ // what invalidation would do is imagine if we had a set A that was clobbered and a set B >+ // that wasn't and we considered the following two cases. Note that we expect A to be the >+ // same at the end in both cases: >+ // >+ // A.merge(B) InvalidationPoint >+ // InvalidationPoint A.merge(B) >+ // >+ // The fact that we expect A to be the same in both cases means that we want to merge other >+ // into this but keep the clobbered bit. >+ // >+ // This not clobbered, other clobbered: This is just the converse of the previous case. We >+ // want to merge other into this and set the clobbered bit. >+ >+ bool changed = false; >+ >+ if (!isClobbered() && other.isClobbered()) { >+ setClobbered(true); >+ changed = true; >+ } >+ >+ changed |= mergeNotTop(other.m_set); >+ >+ return changed; >+} >+ >+bool StructureAbstractValue::mergeNotTop(const StructureSet& other) >+{ >+ if (!m_set.merge(other)) >+ return false; >+ >+ if (m_set.size() > polymorphismLimit) >+ makeTop(); >+ >+ return true; >+} >+ >+void StructureAbstractValue::filter(const StructureSet& other) >+{ >+ if (isTop()) { >+ m_set = other; >+ return; >+ } >+ >+ if (isClobbered()) { >+ // We have two choices here: >+ // >+ // Do nothing: It's legal to keep our set intact, which would essentially mean that for >+ // now, our set would behave like TOP but after the next invalidation point it wold be >+ // a finite set again. This may be a good choice if 'other' is much bigger than our >+ // m_set. >+ // >+ // Replace m_set with other and clear the clobber bit: This is also legal, and means that >+ // we're no longer clobbered. This is usually better because it immediately gives us a >+ // smaller set. >+ // >+ // This scenario should come up rarely. We usually don't do anything to an abstract value >+ // after it is clobbered. But we apply some heuristics. >+ >+ if (other.size() > m_set.size() + clobberedSupremacyThreshold) >+ return; // Keep the clobbered set. >+ >+ m_set = other; >+ setClobbered(false); >+ return; >+ } >+ >+ m_set.filter(other); >+} >+ >+void StructureAbstractValue::filter(const StructureAbstractValue& other) >+{ >+ if (other.isTop()) >+ return; >+ >+ if (other.isClobbered()) { >+ if (isTop()) >+ return; >+ >+ if (!isClobbered()) { >+ // See justification in filter(const StructureSet&), above. An unclobbered set is >+ // almost always better. >+ if (m_set.size() > other.m_set.size() + clobberedSupremacyThreshold) >+ *this = other; // Keep the clobbered set. >+ return; >+ } >+ >+ m_set.filter(other.m_set); >+ return; >+ } >+ >+ filter(other.m_set); >+} >+ >+namespace { >+ >+class ConformsToType { >+public: >+ ConformsToType(SpeculatedType type) >+ : m_type(type) >+ { >+ } >+ >+ bool operator()(Structure* structure) >+ { >+ return !!(speculationFromStructure(structure) & m_type); >+ } >+private: >+ SpeculatedType m_type; >+}; >+ >+} // anonymous namespace >+ >+void StructureAbstractValue::filter(SpeculatedType type) >+{ >+ if (!(type & SpecCell)) { >+ clear(); >+ return; >+ } >+ >+ if (isTop()) >+ return; >+ >+ ConformsToType conformsToType(type); >+ m_set.genericFilter(conformsToType); >+} >+ >+bool StructureAbstractValue::contains(Structure* structure) const >+{ >+ if (isTop() || isClobbered()) >+ return true; >+ >+ return m_set.contains(structure); >+} >+ >+bool StructureAbstractValue::isSubsetOf(const StructureSet& other) const >+{ >+ if (isTop() || isClobbered()) >+ return false; >+ >+ return m_set.isSubsetOf(other); >+} >+ >+bool StructureAbstractValue::isSubsetOf(const StructureAbstractValue& other) const >+{ >+ if (isTop()) >+ return false; >+ >+ if (other.isTop()) >+ return true; >+ >+ if (isClobbered() == other.isClobbered()) >+ return m_set.isSubsetOf(other.m_set); >+ >+ // Here it gets tricky. If in doubt, return false! >+ >+ if (isClobbered()) >+ return false; // A clobbered set is never a subset of an unclobbered set. >+ >+ // An unclobbered set is currently a subset of a clobbered set, but it may not be so after >+ // invalidation. >+ return m_set.isSubsetOf(other.m_set); >+} >+ >+bool StructureAbstractValue::isSupersetOf(const StructureSet& other) const >+{ >+ if (isTop() || isClobbered()) >+ return true; >+ >+ return m_set.isSupersetOf(other); >+} >+ >+bool StructureAbstractValue::overlaps(const StructureSet& other) const >+{ >+ if (isTop() || isClobbered()) >+ return true; >+ >+ return m_set.overlaps(other); >+} >+ >+bool StructureAbstractValue::overlaps(const StructureAbstractValue& other) const >+{ >+ if (other.isTop() || other.isClobbered()) >+ return true; >+ >+ return overlaps(other.m_set); >+} >+ >+bool StructureAbstractValue::operator==(const StructureAbstractValue& other) const >+{ >+ if (m_set.m_pointer == other.m_set.m_pointer) >+ return true; >+ >+ if (isTop() || other.isTop()) >+ return false; // They can't be both top because then their pointer values would have been identical, above. >+ >+ return m_set == other.m_set >+ && isClobbered() == other.isClobbered(); >+} >+ >+void StructureAbstractValue::dumpInContext(PrintStream& out, DumpContext* context) const >+{ >+ if (isClobbered()) >+ out.print("Clobbered:"); >+ >+ if (isTop()) >+ out.print("TOP"); >+ else >+ out.print(inContext(m_set, context)); >+} >+ >+void StructureAbstractValue::dump(PrintStream& out) const >+{ >+ dumpInContext(out, 0); >+} >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >+ >Index: Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGStructureAbstractValue.h (working copy) >@@ -1,5 +1,5 @@ > /* >- * Copyright (C) 2011, 2012, 2013 Apple Inc. All rights reserved. >+ * Copyright (C) 2011, 2012, 2013, 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 >@@ -37,300 +37,131 @@ namespace JSC { namespace DFG { > > class StructureAbstractValue { > public: >- StructureAbstractValue() >- : m_structure(0) >- { >- } >- >+ StructureAbstractValue() { } > StructureAbstractValue(Structure* structure) >- : m_structure(structure) >+ : m_set(structure) > { > } >- >- StructureAbstractValue(const StructureSet& set) >+ StructureAbstractValue(const StructureSet& other) >+ : m_set(other) > { >- switch (set.size()) { >- case 0: >- m_structure = 0; >- break; >- >- case 1: >- m_structure = set[0]; >- break; >- >- default: >- m_structure = topValue(); >- break; >- } > } >+ StructureAbstractValue(const StructureAbstractValue& other); >+ >+ StructureAbstractValue& operator=(const StructureAbstractValue& other); > > void clear() > { >- m_structure = 0; >+ m_set.clear(); >+ setClobbered(false); > } > >- void makeTop() >- { >- m_structure = topValue(); >- } >+ void makeTop(); > >- static StructureAbstractValue top() >- { >- StructureAbstractValue value; >- value.makeTop(); >- return value; >- } >+#if ASSERT_DISABLED >+ void assertIsWatched(Graph&) const { } >+#else >+ void assertIsWatched(Graph&) const; >+#endif > >- void add(Structure* structure) >- { >- ASSERT(!contains(structure) && !isTop()); >- if (m_structure) >- makeTop(); >- else >- m_structure = structure; >- } >+ void clobber(); >+ void observeInvalidationPoint() { setClobbered(false); } > >- bool addAll(const StructureSet& other) >- { >- if (isTop() || !other.size()) >- return false; >- if (other.size() > 1) { >- makeTop(); >- return true; >- } >- if (!m_structure) { >- m_structure = other[0]; >- return true; >- } >- if (m_structure == other[0]) >- return false; >- makeTop(); >- return true; >- } >+ void observeTransition(Structure* from, Structure* to); > >- bool addAll(const StructureAbstractValue& other) >- { >- if (!other.m_structure) >- return false; >- if (isTop()) >- return false; >- if (other.isTop()) { >- makeTop(); >- return true; >- } >- if (m_structure) { >- if (m_structure == other.m_structure) >- return false; >- makeTop(); >- return true; >- } >- m_structure = other.m_structure; >- return true; >- } >- >- bool contains(Structure* structure) const >+ static StructureAbstractValue top() > { >- if (isTop()) >- return true; >- if (m_structure == structure) >- return true; >- return false; >- } >+ StructureAbstractValue result; >+ result.m_set.m_pointer = topValue; >+ return result; >+ } >+ >+ bool isClear() const { return m_set.isEmpty(); } >+ bool isTop() const { return m_set.m_pointer == topValue; } >+ >+ // A clobbered abstract value means that the set currently contains the m_set set of >+ // structures plus TOP, except that TOP will go away at the next invalidation point. Note >+ // that intuitively you can sometimes think of this as "the set of structures in m_set plus >+ // the set of structures transition-reachable from m_set" - but this isn't really correct, >+ // since if we add an unwatchable structure after clobbering, the two definitions are not >+ // equivalent. If we do this, the new unwatchable structure will be added to m_set. >+ // Invalidation points do not try to "clip" the set of transition-reachable structures from >+ // m_set by looking at reachability as this would mean that the new set is TOP. Instead they >+ // literally assume that the set is just m_set rather than TOP. >+ bool isClobbered() const { return m_set.reservedFlag(); } >+ >+ bool add(Structure* structure); >+ >+ bool merge(const StructureSet& other); >+ bool merge(const StructureAbstractValue& other); >+ void filter(const StructureSet& other); >+ void filter(const StructureAbstractValue& other); >+ void filter(SpeculatedType type); > >- bool isSubsetOf(const StructureSet& other) const >- { >- if (isTop()) >- return false; >- if (!m_structure) >- return true; >- return other.contains(m_structure); >- } >+ bool contains(Structure* structure) const; > >- bool doesNotContainAnyOtherThan(Structure* structure) const >- { >- if (isTop()) >- return false; >- if (!m_structure) >- return true; >- return m_structure == structure; >- } >- >- bool isSupersetOf(const StructureSet& other) const >- { >- if (isTop()) >- return true; >- if (!other.size()) >- return true; >- if (other.size() > 1) >- return false; >- return m_structure == other[0]; >- } >- >- bool isSubsetOf(const StructureAbstractValue& other) const >- { >- if (other.isTop()) >- return true; >- if (isTop()) >- return false; >- if (m_structure) { >- if (other.m_structure) >- return m_structure == other.m_structure; >- return false; >- } >- return true; >- } >+ bool isSubsetOf(const StructureSet& other) const; >+ bool isSubsetOf(const StructureAbstractValue& other) const; > >+ bool isSupersetOf(const StructureSet& other) const; > bool isSupersetOf(const StructureAbstractValue& other) const > { > return other.isSubsetOf(*this); > } > >- void filter(const StructureSet& other) >- { >- if (!m_structure) >- return; >- >- if (isTop()) { >- switch (other.size()) { >- case 0: >- m_structure = 0; >- return; >- >- case 1: >- m_structure = other[0]; >- return; >- >- default: >- return; >- } >- } >- >- if (other.contains(m_structure)) >- return; >- >- m_structure = 0; >- } >+ bool overlaps(const StructureSet& other) const; >+ bool overlaps(const StructureAbstractValue& other) const; > >- void filter(const StructureAbstractValue& other) >- { >- if (isTop()) { >- m_structure = other.m_structure; >- return; >- } >- if (m_structure == other.m_structure) >- return; >- if (other.isTop()) >- return; >- m_structure = 0; >- } >- >- void filter(SpeculatedType other) >- { >- if (!(other & SpecCell)) { >- clear(); >- return; >- } >- >- if (isClearOrTop()) >- return; >- >- if (!(speculationFromStructure(m_structure) & other)) >- m_structure = 0; >- } >- >- bool isClear() const >- { >- return !m_structure; >- } >- >- bool isTop() const { return m_structure == topValue(); } >- >- bool isClearOrTop() const { return m_structure <= topValue(); } >- bool isNeitherClearNorTop() const { return !isClearOrTop(); } >+ bool operator==(const StructureAbstractValue& other) const; > > size_t size() const > { > ASSERT(!isTop()); >- return !!m_structure; >+ return m_set.size(); > } > > Structure* at(size_t i) const > { > ASSERT(!isTop()); >- ASSERT(m_structure); >- ASSERT_UNUSED(i, !i); >- return m_structure; >+ return m_set.at(i); > } > >- Structure* operator[](size_t i) const >- { >- return at(i); >- } >+ Structure* operator[](size_t i) const { return at(i); } > >- Structure* last() const >+ // FIXME: Eliminate all uses of this method. There shouldn't be any >+ // special-casing for the one-structure case. >+ // https://bugs.webkit.org/show_bug.cgi?id=133229 >+ Structure* onlyStructure() const > { >+ if (isTop() || size() != 1) >+ return nullptr; > return at(0); > } > >- SpeculatedType speculationFromStructures() const >- { >- if (isTop()) >- return SpecCell; >- if (isClear()) >- return SpecNone; >- return speculationFromStructure(m_structure); >- } >- >- bool isValidOffset(PropertyOffset offset) >- { >- if (isTop()) >- return false; >- if (isClear()) >- return true; >- return m_structure->isValidOffset(offset); >- } >+ void dumpInContext(PrintStream&, DumpContext*) const; >+ void dump(PrintStream&) const; > >- bool hasSingleton() const >- { >- return isNeitherClearNorTop(); >- } >+private: >+ static const uintptr_t clobberedFlag = StructureSet::reservedFlag; >+ static const uintptr_t topValue = StructureSet::reservedValue; >+ static const unsigned polymorphismLimit = 10; >+ static const unsigned clobberedSupremacyThreshold = 2; > >- Structure* singleton() const >+ void makeTopWhenThin() > { >- ASSERT(isNeitherClearNorTop()); >- return m_structure; >+ ASSERT(m_set.isThin()); >+ m_set.m_pointer = topValue; > } > >- bool operator==(const StructureAbstractValue& other) const >- { >- return m_structure == other.m_structure; >- } >+ bool mergeNotTop(const StructureSet& other); > >- void dumpInContext(PrintStream& out, DumpContext* context) const >+ void setClobbered(bool clobbered) > { >- if (isTop()) { >- out.print("TOP"); >- return; >- } >- >- out.print("["); >- if (m_structure) >- out.print(inContext(*m_structure, context)); >- out.print("]"); >- } >- >- void dump(PrintStream& out) const >- { >- dumpInContext(out, 0); >+ ASSERT(!isTop()); >+ m_set.setReservedFlag(clobbered); > } >- >-private: >- static Structure* topValue() { return reinterpret_cast<Structure*>(1); } >- >- // NB. This must have a trivial destructor. > >- // This can only remember one structure at a time. >- Structure* m_structure; >+ StructureSet m_set; > }; > > } } // namespace JSC::DFG >Index: Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGTypeCheckHoistingPhase.cpp (working copy) >@@ -215,8 +215,7 @@ private: > for (unsigned indexInBlock = 0; indexInBlock < block->size(); ++indexInBlock) { > Node* node = block->at(indexInBlock); > switch (node->op()) { >- case CheckStructure: >- case StructureTransitionWatchpoint: { >+ case CheckStructure: { > Node* child = node->child1().node(); > if (child->op() != GetLocal) > break; >@@ -285,13 +284,6 @@ private: > noticeStructureCheck(variable, subNode->structureSet()); > break; > } >- case StructureTransitionWatchpoint: { >- if (subNode->child1() != source) >- break; >- >- noticeStructureCheck(variable, subNode->structure()); >- break; >- } > default: > break; > } >@@ -331,7 +323,6 @@ private: > } > > case CheckStructure: >- case StructureTransitionWatchpoint: > case GetByOffset: > case PutByOffset: > case PutStructure: >@@ -386,13 +377,6 @@ private: > noticeStructureCheckAccountingForArrayMode(variable, subNode->structureSet()); > break; > } >- case StructureTransitionWatchpoint: { >- if (subNode->child1() != source) >- break; >- >- noticeStructureCheckAccountingForArrayMode(variable, subNode->structure()); >- break; >- } > case CheckArray: { > if (subNode->child1() != source) > break; >Index: Source/JavaScriptCore/dfg/DFGWatchableStructureWatchingPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGWatchableStructureWatchingPhase.cpp (revision 0) >+++ Source/JavaScriptCore/dfg/DFGWatchableStructureWatchingPhase.cpp (working copy) >@@ -0,0 +1,180 @@ >+/* >+ * 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 "DFGWatchableStructureWatchingPhase.h" >+ >+#if ENABLE(DFG_JIT) >+ >+#include "DFGBasicBlockInlines.h" >+#include "DFGGraph.h" >+#include "DFGPhase.h" >+#include "JSCInlines.h" >+ >+namespace JSC { namespace DFG { >+ >+class WatchableStructureWatchingPhase : public Phase { >+public: >+ WatchableStructureWatchingPhase(Graph& graph) >+ : Phase(graph, "watchable structure watching") >+ { >+ } >+ >+ bool run() >+ { >+ // These are pretty dumb, but needed to placate subsequent assertions. We con't actually >+ // have to watch these because there is no way to transition away from it, but they are >+ // watchable and so we will assert if they aren't watched. >+ tryWatch(m_graph.m_vm.stringStructure.get()); >+ tryWatch(m_graph.m_vm.getterSetterStructure.get()); >+ >+ for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) { >+ BasicBlock* block = m_graph.block(blockIndex); >+ if (!block) >+ continue; >+ >+ for (unsigned nodeIndex = 0; nodeIndex < block->size(); ++nodeIndex) { >+ Node* node = block->at(nodeIndex); >+ >+ switch (node->op()) { >+ case JSConstant: >+ case WeakJSConstant: >+ tryWatch(m_graph.valueOfJSConstant(node)); >+ break; >+ >+ case CheckFunction: >+ tryWatch(node->function()); >+ break; >+ >+ case CheckExecutable: >+ tryWatch(node->executable()); >+ break; >+ >+ case CheckStructure: >+ tryWatch(node->structureSet()); >+ break; >+ >+ case NewObject: >+ case ArrayifyToStructure: >+ case NewStringObject: >+ tryWatch(node->structure()); >+ break; >+ >+ case PutStructure: >+ case PhantomPutStructure: >+ case AllocatePropertyStorage: >+ case ReallocatePropertyStorage: >+ tryWatch(node->structureTransitionData().previousStructure); >+ tryWatch(node->structureTransitionData().newStructure); >+ break; >+ >+ case MultiGetByOffset: >+ for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;) { >+ GetByIdVariant& variant = node->multiGetByOffsetData().variants[i]; >+ tryWatch(variant.specificValue()); >+ tryWatch(variant.structureSet()); >+ // Don't need to watch anything in the structure chain because that would >+ // have been decomposed into CheckStructure's. Don't need to watch the >+ // callLinkStatus because we wouldn't use MultiGetByOffset if any of the >+ // variants did that. >+ ASSERT(!variant.callLinkStatus()); >+ } >+ break; >+ >+ case MultiPutByOffset: >+ for (unsigned i = node->multiPutByOffsetData().variants.size(); i--;) { >+ PutByIdVariant& variant = node->multiPutByOffsetData().variants[i]; >+ tryWatch(variant.oldStructure()); >+ if (variant.kind() == PutByIdVariant::Transition) >+ tryWatch(variant.newStructure()); >+ } >+ break; >+ >+ case NewArray: >+ case NewArrayBuffer: >+ tryWatch(m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())); >+ break; >+ >+ case NewTypedArray: >+ tryWatch(m_graph.globalObjectFor(node->origin.semantic)->typedArrayStructure(node->typedArrayType())); >+ break; >+ >+ case ToString: >+ tryWatch(m_graph.globalObjectFor(node->origin.semantic)->stringObjectStructure()); >+ break; >+ >+ case CreateActivation: >+ tryWatch(m_graph.globalObjectFor(node->origin.semantic)->activationStructure()); >+ break; >+ >+ case NewRegexp: >+ tryWatch(m_graph.globalObjectFor(node->origin.semantic)->regExpStructure()); >+ break; >+ >+ default: >+ break; >+ } >+ } >+ } >+ >+ return true; >+ } >+ >+private: >+ void tryWatch(JSValue value) >+ { >+ if (value.isCell()) >+ tryWatch(value.asCell()); >+ } >+ >+ void tryWatch(JSCell* cell) >+ { >+ if (cell) >+ tryWatch(cell->structure()); >+ } >+ >+ void tryWatch(const StructureSet& set) >+ { >+ for (unsigned i = set.size(); i--;) >+ tryWatch(set[i]); >+ } >+ >+ void tryWatch(Structure* structure) >+ { >+ if (structure->transitionWatchpointSetIsStillValid()) >+ m_graph.watchpoints().addLazily(structure->transitionWatchpointSet()); >+ } >+}; >+ >+bool performWatchableStructureWatching(Graph& graph) >+{ >+ SamplingRegion samplingRegion("DFG Watchable Structure Watching"); >+ return runPhase<WatchableStructureWatchingPhase>(graph); >+} >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >+ >Index: Source/JavaScriptCore/dfg/DFGWatchableStructureWatchingPhase.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGWatchableStructureWatchingPhase.h (revision 0) >+++ Source/JavaScriptCore/dfg/DFGWatchableStructureWatchingPhase.h (working copy) >@@ -0,0 +1,53 @@ >+/* >+ * 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 DFGWatchableStructureWatchingPhase_h >+#define DFGWatchableStructureWatchingPhase_h >+ >+#if ENABLE(DFG_JIT) >+ >+namespace JSC { namespace DFG { >+ >+class Graph; >+ >+// Set watchpoints on any structures that we know of that are currently watchable. It's >+// somewhat counterintuitive, but this ends up being the cleanest and most effective way >+// of reducing structure checks on terminal structures: >+// >+// - We used to only set watchpoints on watchable structures if we knew that this would >+// remove a structure check. Experiments show that switching from that, to blindly >+// setting watchpoints on all watchable structures, was not a regression. >+// >+// - It makes abstract interpretation a whole lot easier. We just assume that watchable >+// structures are unclobberable without having to do any other logic. >+ >+bool performWatchableStructureWatching(Graph&); >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >+ >+#endif // DFGWatchableStructureWatchingPhase_h >+ >Index: Source/JavaScriptCore/dfg/DFGWatchpointCollectionPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGWatchpointCollectionPhase.cpp (revision 169200) >+++ Source/JavaScriptCore/dfg/DFGWatchpointCollectionPhase.cpp (working copy) >@@ -119,13 +119,6 @@ private: > addLazily(jsCast<JSFunction*>(m_node->function())->allocationProfileWatchpointSet()); > break; > >- case StructureTransitionWatchpoint: >- m_graph.watchpoints().addLazily( >- m_node->origin.semantic, >- m_node->child1()->op() == WeakJSConstant ? BadWeakConstantCacheWatchpoint : BadCacheWatchpoint, >- m_node->structure()->transitionWatchpointSet()); >- break; >- > case VariableWatchpoint: > addLazily(m_node->variableWatchpointSet()); > break; >Index: Source/JavaScriptCore/ftl/FTLCapabilities.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLCapabilities.cpp (revision 169200) >+++ Source/JavaScriptCore/ftl/FTLCapabilities.cpp (working copy) >@@ -63,7 +63,6 @@ inline CapabilityLevel canCompile(Node* > case BitLShift: > case BitURShift: > case CheckStructure: >- case StructureTransitionWatchpoint: > case ArrayifyToStructure: > case PutStructure: > case PhantomPutStructure: >Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (revision 169200) >+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (working copy) >@@ -399,9 +399,6 @@ private: > case CheckStructure: > compileCheckStructure(); > break; >- case StructureTransitionWatchpoint: >- compileStructureTransitionWatchpoint(); >- break; > case CheckFunction: > compileCheckFunction(); > break; >@@ -1609,12 +1606,6 @@ private: > m_out.appendTo(continuation, lastNext); > } > >- void compileStructureTransitionWatchpoint() >- { >- addWeakReference(m_node->structure()); >- speculateCell(m_node->child1()); >- } >- > void compileCheckFunction() > { > LValue cell = lowCell(m_node->child1()); >@@ -3207,7 +3198,7 @@ private: > } > > m_out.appendTo(exit, continuation); >- terminate(BadCache); >+ speculate(BadCache, noValue(), nullptr, m_out.booleanTrue); > m_out.unreachable(); > > m_out.appendTo(continuation, lastNext); >@@ -3279,7 +3270,7 @@ private: > } > > m_out.appendTo(exit, continuation); >- terminate(BadCache); >+ speculate(BadCache, noValue(), nullptr, m_out.booleanTrue); > m_out.unreachable(); > > m_out.appendTo(continuation, lastNext); >@@ -4718,7 +4709,8 @@ private: > > void terminate(ExitKind kind) > { >- speculate(kind, noValue(), 0, m_out.booleanTrue); >+ speculate(kind, noValue(), nullptr, m_out.booleanTrue); >+ m_state.setIsValid(false); > } > > void typeCheck( >@@ -5463,7 +5455,7 @@ private: > Structure* stringObjectStructure = > m_graph.globalObjectFor(m_node->origin.semantic)->stringObjectStructure(); > >- if (m_state.forNode(edge).m_currentKnownStructure.isSubsetOf(StructureSet(stringObjectStructure))) >+ if (m_state.forNode(edge).m_structure.isSubsetOf(StructureSet(stringObjectStructure))) > return; > > speculate( >Index: Source/JavaScriptCore/tests/stress/arrayify-to-structure-contradiction.js >=================================================================== >--- Source/JavaScriptCore/tests/stress/arrayify-to-structure-contradiction.js (revision 0) >+++ Source/JavaScriptCore/tests/stress/arrayify-to-structure-contradiction.js (working copy) >@@ -0,0 +1,16 @@ >+function foo(array, v, p) { >+ array[0] = 10; >+ if (p) >+ v = "hello"; >+ array[0] = v; >+} >+ >+noInline(foo); >+ >+for (var i = 0; i < 100000; ++i) { >+ var array = [42]; >+ foo(array, 43, false); >+ if (array[0] != 43) >+ throw "Error: bad result: " + array; >+} >+ >Index: LayoutTests/ChangeLog >=================================================================== >--- LayoutTests/ChangeLog (revision 169467) >+++ LayoutTests/ChangeLog (working copy) >@@ -1,3 +1,23 @@ >+2014-05-29 Filip Pizlo <fpizlo@apple.com> >+ >+ AI should be able track structure sets larger than 1 >+ https://bugs.webkit.org/show_bug.cgi?id=128073 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * js/regress/get-by-id-bimorphic-check-structure-elimination-expected.txt: Added. >+ * js/regress/get-by-id-bimorphic-check-structure-elimination-simple-expected.txt: Added. >+ * js/regress/get-by-id-bimorphic-check-structure-elimination-simple.html: Added. >+ * js/regress/get-by-id-bimorphic-check-structure-elimination.html: Added. >+ * js/regress/get-by-id-check-structure-elimination-expected.txt: Added. >+ * js/regress/get-by-id-check-structure-elimination.html: Added. >+ * js/regress/get-by-id-quadmorphic-check-structure-elimination-simple-expected.txt: Added. >+ * js/regress/get-by-id-quadmorphic-check-structure-elimination-simple.html: Added. >+ * js/regress/script-tests/get-by-id-bimorphic-check-structure-elimination-simple.js: Added. >+ * js/regress/script-tests/get-by-id-bimorphic-check-structure-elimination.js: Added. >+ * js/regress/script-tests/get-by-id-check-structure-elimination.js: Added. >+ * js/regress/script-tests/get-by-id-quadmorphic-check-structure-elimination-simple.js: Added. >+ > 2014-05-21 Filip Pizlo <fpizlo@apple.com> > > [ftlopt] DFG::clobberize should be blind to the effects of GC >Index: LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination-expected.txt >=================================================================== >--- LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination-expected.txt (revision 0) >+++ LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination-expected.txt (working copy) >@@ -0,0 +1,10 @@ >+JSRegress/get-by-id-bimorphic-check-structure-elimination >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS no exception thrown >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination-simple-expected.txt >=================================================================== >--- LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination-simple-expected.txt (revision 0) >+++ LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination-simple-expected.txt (working copy) >@@ -0,0 +1,10 @@ >+JSRegress/get-by-id-bimorphic-check-structure-elimination-simple >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS no exception thrown >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination-simple.html >=================================================================== >--- LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination-simple.html (revision 0) >+++ LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination-simple.html (working copy) >@@ -0,0 +1,12 @@ >+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> >+<html> >+<head> >+<script src="../../resources/js-test-pre.js"></script> >+</head> >+<body> >+<script src="../../resources/regress-pre.js"></script> >+<script src="script-tests/get-by-id-bimorphic-check-structure-elimination-simple.js"></script> >+<script src="../../resources/regress-post.js"></script> >+<script src="../../resources/js-test-post.js"></script> >+</body> >+</html> >Index: LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination.html >=================================================================== >--- LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination.html (revision 0) >+++ LayoutTests/js/regress/get-by-id-bimorphic-check-structure-elimination.html (working copy) >@@ -0,0 +1,12 @@ >+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> >+<html> >+<head> >+<script src="../../resources/js-test-pre.js"></script> >+</head> >+<body> >+<script src="../../resources/regress-pre.js"></script> >+<script src="script-tests/get-by-id-bimorphic-check-structure-elimination.js"></script> >+<script src="../../resources/regress-post.js"></script> >+<script src="../../resources/js-test-post.js"></script> >+</body> >+</html> >Index: LayoutTests/js/regress/get-by-id-check-structure-elimination-expected.txt >=================================================================== >--- LayoutTests/js/regress/get-by-id-check-structure-elimination-expected.txt (revision 0) >+++ LayoutTests/js/regress/get-by-id-check-structure-elimination-expected.txt (working copy) >@@ -0,0 +1,10 @@ >+JSRegress/get-by-id-check-structure-elimination >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS no exception thrown >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/js/regress/get-by-id-check-structure-elimination.html >=================================================================== >--- LayoutTests/js/regress/get-by-id-check-structure-elimination.html (revision 0) >+++ LayoutTests/js/regress/get-by-id-check-structure-elimination.html (working copy) >@@ -0,0 +1,12 @@ >+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> >+<html> >+<head> >+<script src="../../resources/js-test-pre.js"></script> >+</head> >+<body> >+<script src="../../resources/regress-pre.js"></script> >+<script src="script-tests/get-by-id-check-structure-elimination.js"></script> >+<script src="../../resources/regress-post.js"></script> >+<script src="../../resources/js-test-post.js"></script> >+</body> >+</html> >Index: LayoutTests/js/regress/get-by-id-quadmorphic-check-structure-elimination-simple-expected.txt >=================================================================== >--- LayoutTests/js/regress/get-by-id-quadmorphic-check-structure-elimination-simple-expected.txt (revision 0) >+++ LayoutTests/js/regress/get-by-id-quadmorphic-check-structure-elimination-simple-expected.txt (working copy) >@@ -0,0 +1,10 @@ >+JSRegress/get-by-id-quadmorphic-check-structure-elimination-simple >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+PASS no exception thrown >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >Index: LayoutTests/js/regress/get-by-id-quadmorphic-check-structure-elimination-simple.html >=================================================================== >--- LayoutTests/js/regress/get-by-id-quadmorphic-check-structure-elimination-simple.html (revision 0) >+++ LayoutTests/js/regress/get-by-id-quadmorphic-check-structure-elimination-simple.html (working copy) >@@ -0,0 +1,12 @@ >+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> >+<html> >+<head> >+<script src="../../resources/js-test-pre.js"></script> >+</head> >+<body> >+<script src="../../resources/regress-pre.js"></script> >+<script src="script-tests/get-by-id-quadmorphic-check-structure-elimination-simple.js"></script> >+<script src="../../resources/regress-post.js"></script> >+<script src="../../resources/js-test-post.js"></script> >+</body> >+</html> >Index: LayoutTests/js/regress/script-tests/get-by-id-bimorphic-check-structure-elimination-simple.js >=================================================================== >--- LayoutTests/js/regress/script-tests/get-by-id-bimorphic-check-structure-elimination-simple.js (revision 0) >+++ LayoutTests/js/regress/script-tests/get-by-id-bimorphic-check-structure-elimination-simple.js (working copy) >@@ -0,0 +1,13 @@ >+(function() { >+ var o = {a:1}; >+ var p = {a:2, l:13}; >+ var result = 0; >+ for (var i = 0; i < 1000000; ++i) { >+ result ^= o.a; >+ var tmp = o; >+ o = p; >+ p = tmp; >+ } >+ if (result != 0) >+ throw "Error: bad result: " + result; >+})(); >Index: LayoutTests/js/regress/script-tests/get-by-id-bimorphic-check-structure-elimination.js >=================================================================== >--- LayoutTests/js/regress/script-tests/get-by-id-bimorphic-check-structure-elimination.js (revision 0) >+++ LayoutTests/js/regress/script-tests/get-by-id-bimorphic-check-structure-elimination.js (working copy) >@@ -0,0 +1,13 @@ >+(function() { >+ var o = {a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10, k:11}; >+ var p = {a:2, b:3, c:4, d:5, e:6, f:7, g:8, h:9, i:10, j:11, k:12, l:13}; >+ var result = 0; >+ for (var i = 0; i < 1000000; ++i) { >+ result += o.a ^ o.b ^ o.c ^ o.d ^ o.e ^ o.f ^ o.g ^ o.h ^ o.i ^ o.j ^ o.k; >+ var tmp = o; >+ o = p; >+ p = tmp; >+ } >+ if (result != 6500000) >+ throw "Error: bad result: " + result; >+})(); >Index: LayoutTests/js/regress/script-tests/get-by-id-check-structure-elimination.js >=================================================================== >--- LayoutTests/js/regress/script-tests/get-by-id-check-structure-elimination.js (revision 0) >+++ LayoutTests/js/regress/script-tests/get-by-id-check-structure-elimination.js (working copy) >@@ -0,0 +1,8 @@ >+(function() { >+ var o = {a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9, j:10, k:11}; >+ var result = 0; >+ for (var i = 0; i < 1000000; ++i) >+ result += o.a ^ o.b | o.c ^ o.d & o.e ^ o.f | o.g ^ o.h & o.i ^ o.j | o.k; >+ if (result != 15000000) >+ throw "Error: bad result: " + result; >+})(); >Index: LayoutTests/js/regress/script-tests/get-by-id-quadmorphic-check-structure-elimination-simple.js >=================================================================== >--- LayoutTests/js/regress/script-tests/get-by-id-quadmorphic-check-structure-elimination-simple.js (revision 0) >+++ LayoutTests/js/regress/script-tests/get-by-id-quadmorphic-check-structure-elimination-simple.js (working copy) >@@ -0,0 +1,17 @@ >+(function() { >+ var o = {a:1}; >+ var p = {a:2, l:13}; >+ var q = {a:3, b:3}; >+ var r = {a:4, c:5}; >+ var result = 0; >+ for (var i = 0; i < 1000000; ++i) { >+ result ^= o.a; >+ var tmp = o; >+ o = p; >+ p = q; >+ q = r; >+ r = tmp; >+ } >+ if (result != 0) >+ throw "Error: bad result: " + result; >+})();
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 128073
:
231915
|
231935
|
231983
|
232228
|
232232
|
232264
|
232270
|
232281
|
232304
|
232349
|
232350
|
232413
|
232437
|
232449
|
232460