WebKit Bugzilla
New
Browse
Search+
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
[patch]
the patch
blah.patch (text/plain), 214.04 KB, created by
Filip Pizlo
on 2013-07-18 16:29:14 PDT
(
hide
)
Description:
the patch
Filename:
MIME Type:
Creator:
Filip Pizlo
Created:
2013-07-18 16:29:14 PDT
Size:
214.04 KB
patch
obsolete
>Index: Source/JavaScriptCore/ChangeLog >=================================================================== >--- Source/JavaScriptCore/ChangeLog (revision 152871) >+++ Source/JavaScriptCore/ChangeLog (working copy) >@@ -1,3 +1,150 @@ >+2013-07-18 Filip Pizlo <fpizlo@apple.com> >+ >+ fourthTier: Decouple the way that CFA stores its state from the way it does abstract interpretation >+ https://bugs.webkit.org/show_bug.cgi?id=118835 >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ This separates AbstractState into two things: >+ >+ - InPlaceAbstractState, which can tell you the abstract state of anything you >+ might care about, and uses the old AbstractState's algorithms and data >+ structures for doing so. >+ >+ - AbstractInterpreter<AbstractStateType>, which can execute a DFG::Node* with >+ respect to an AbstractStateType. Currently we always use >+ AbstractStateType = InPlaceAbstractState. But we could drop in an other >+ class that supports basic primitives like forNode() and variables(). >+ >+ This is important because: >+ >+ - We want to hoist things out of loops. >+ >+ - We don't know what things rely on what type checks. >+ >+ - We only want to hoist type checks out of loops if they aren't clobbered. >+ >+ - We may want to still hoist things that depended on those type checks, if it's >+ safe to do those things based on the CFA state at the tail of the loop >+ pre-header. >+ >+ - We don't want things to rely on their type checks by way of a token, because >+ that's just weird. >+ >+ So, we want to be able to have a special form of the CFA that can >+ incrementally update a basic block's state-at-tail, and we want to be able to >+ do this for multiple blocks simultaneously. This requires *not* storing the >+ per-node state in the nodes themselves, but instead using the at-tail HashMap >+ directly. >+ >+ Hence we need to have a way of making the abstract interpreter (i.e. >+ AbstractState::execute) polymorphic with respect to state representation. Put >+ another way, we need to separate the way that abstract state is represented >+ from the way DFG IR is abstractly interpreted. >+ >+ * JavaScriptCore.xcodeproj/project.pbxproj: >+ * dfg/DFGAbstractInterpreter.h: Added. >+ (DFG): >+ (AbstractInterpreter): >+ (JSC::DFG::AbstractInterpreter::forNode): >+ (JSC::DFG::AbstractInterpreter::variables): >+ (JSC::DFG::AbstractInterpreter::needsTypeCheck): >+ (JSC::DFG::AbstractInterpreter::filterEdgeByUse): >+ (JSC::DFG::AbstractInterpreter::filter): >+ (JSC::DFG::AbstractInterpreter::filterArrayModes): >+ (JSC::DFG::AbstractInterpreter::filterByValue): >+ (JSC::DFG::AbstractInterpreter::trySetConstant): >+ (JSC::DFG::AbstractInterpreter::filterByType): >+ * dfg/DFGAbstractInterpreterInlines.h: Added. >+ (DFG): >+ (JSC::DFG::::AbstractInterpreter): >+ (JSC::DFG::::~AbstractInterpreter): >+ (JSC::DFG::::booleanResult): >+ (JSC::DFG::::startExecuting): >+ (JSC::DFG::::executeEdges): >+ (JSC::DFG::::verifyEdge): >+ (JSC::DFG::::verifyEdges): >+ (JSC::DFG::::executeEffects): >+ (JSC::DFG::::execute): >+ (JSC::DFG::::clobberWorld): >+ (JSC::DFG::::clobberCapturedVars): >+ (JSC::DFG::::clobberStructures): >+ (JSC::DFG::::dump): >+ (JSC::DFG::::filter): >+ (JSC::DFG::::filterArrayModes): >+ (JSC::DFG::::filterByValue): >+ * dfg/DFGAbstractState.cpp: Removed. >+ * dfg/DFGAbstractState.h: Removed. >+ * dfg/DFGArgumentsSimplificationPhase.cpp: >+ * dfg/DFGCFAPhase.cpp: >+ (JSC::DFG::CFAPhase::CFAPhase): >+ (JSC::DFG::CFAPhase::performBlockCFA): >+ (CFAPhase): >+ * dfg/DFGCFGSimplificationPhase.cpp: >+ * dfg/DFGConstantFoldingPhase.cpp: >+ (JSC::DFG::ConstantFoldingPhase::ConstantFoldingPhase): >+ (JSC::DFG::ConstantFoldingPhase::foldConstants): >+ (ConstantFoldingPhase): >+ * dfg/DFGInPlaceAbstractState.cpp: Added. >+ (DFG): >+ (JSC::DFG::InPlaceAbstractState::InPlaceAbstractState): >+ (JSC::DFG::InPlaceAbstractState::~InPlaceAbstractState): >+ (JSC::DFG::InPlaceAbstractState::beginBasicBlock): >+ (JSC::DFG::setLiveValues): >+ (JSC::DFG::InPlaceAbstractState::initialize): >+ (JSC::DFG::InPlaceAbstractState::endBasicBlock): >+ (JSC::DFG::InPlaceAbstractState::reset): >+ (JSC::DFG::InPlaceAbstractState::mergeStateAtTail): >+ (JSC::DFG::InPlaceAbstractState::merge): >+ (JSC::DFG::InPlaceAbstractState::mergeToSuccessors): >+ (JSC::DFG::InPlaceAbstractState::mergeVariableBetweenBlocks): >+ * dfg/DFGInPlaceAbstractState.h: Added. >+ (DFG): >+ (InPlaceAbstractState): >+ (JSC::DFG::InPlaceAbstractState::forNode): >+ (JSC::DFG::InPlaceAbstractState::variables): >+ (JSC::DFG::InPlaceAbstractState::block): >+ (JSC::DFG::InPlaceAbstractState::didClobber): >+ (JSC::DFG::InPlaceAbstractState::isValid): >+ (JSC::DFG::InPlaceAbstractState::setDidClobber): >+ (JSC::DFG::InPlaceAbstractState::setIsValid): >+ (JSC::DFG::InPlaceAbstractState::setBranchDirection): >+ (JSC::DFG::InPlaceAbstractState::setFoundConstants): >+ (JSC::DFG::InPlaceAbstractState::haveStructures): >+ (JSC::DFG::InPlaceAbstractState::setHaveStructures): >+ * dfg/DFGMergeMode.h: Added. >+ (DFG): >+ * dfg/DFGSpeculativeJIT.cpp: >+ (JSC::DFG::SpeculativeJIT::SpeculativeJIT): >+ (JSC::DFG::SpeculativeJIT::backwardTypeCheck): >+ (JSC::DFG::SpeculativeJIT::compileCurrentBlock): >+ (JSC::DFG::SpeculativeJIT::compileToStringOnCell): >+ (JSC::DFG::SpeculativeJIT::speculateStringIdentAndLoadStorage): >+ (JSC::DFG::SpeculativeJIT::speculateStringObject): >+ (JSC::DFG::SpeculativeJIT::speculateStringOrStringObject): >+ * dfg/DFGSpeculativeJIT.h: >+ (JSC::DFG::SpeculativeJIT::needsTypeCheck): >+ (SpeculativeJIT): >+ * dfg/DFGSpeculativeJIT32_64.cpp: >+ (JSC::DFG::SpeculativeJIT::fillSpeculateIntInternal): >+ (JSC::DFG::SpeculativeJIT::fillSpeculateDouble): >+ (JSC::DFG::SpeculativeJIT::fillSpeculateCell): >+ (JSC::DFG::SpeculativeJIT::fillSpeculateBoolean): >+ * dfg/DFGSpeculativeJIT64.cpp: >+ (JSC::DFG::SpeculativeJIT::fillSpeculateIntInternal): >+ (JSC::DFG::SpeculativeJIT::fillSpeculateDouble): >+ (JSC::DFG::SpeculativeJIT::fillSpeculateCell): >+ (JSC::DFG::SpeculativeJIT::fillSpeculateBoolean): >+ * ftl/FTLLowerDFGToLLVM.cpp: >+ (FTL): >+ (JSC::FTL::LowerDFGToLLVM::LowerDFGToLLVM): >+ (JSC::FTL::LowerDFGToLLVM::compileNode): >+ (JSC::FTL::LowerDFGToLLVM::appendTypeCheck): >+ (JSC::FTL::LowerDFGToLLVM::speculate): >+ (JSC::FTL::LowerDFGToLLVM::speculateNumber): >+ (JSC::FTL::LowerDFGToLLVM::speculateRealNumber): >+ (LowerDFGToLLVM): >+ > 2013-07-18 Filip Pizlo <fpizlo@apple.com> > > fourthTier: CFA should consider live-at-head for clobbering and dumping >Index: Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj >=================================================================== >--- Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (revision 152828) >+++ Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (working copy) >@@ -198,8 +198,6 @@ > 0F620174143FCD330068B77C /* DFGVariableAccessData.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F620172143FCD2F0068B77C /* DFGVariableAccessData.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F620176143FCD3B0068B77C /* DFGBasicBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F620170143FCD2F0068B77C /* DFGBasicBlock.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F620177143FCD3F0068B77C /* DFGAbstractValue.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F62016F143FCD2F0068B77C /* DFGAbstractValue.h */; settings = {ATTRIBUTES = (Private, ); }; }; >- 0F620178143FCD440068B77C /* DFGAbstractState.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F62016E143FCD2F0068B77C /* DFGAbstractState.h */; settings = {ATTRIBUTES = (Private, ); }; }; >- 0F620179143FCD480068B77C /* DFGAbstractState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F62016D143FCD2F0068B77C /* DFGAbstractState.cpp */; }; > 0F63943F15C75F19006A597C /* DFGTypeCheckHoistingPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F63943D15C75F14006A597C /* DFGTypeCheckHoistingPhase.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0F63945415D07055006A597C /* ArrayProfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F63945115D07051006A597C /* ArrayProfile.cpp */; }; > 0F63945515D07057006A597C /* ArrayProfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F63945215D07051006A597C /* ArrayProfile.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -368,6 +366,11 @@ > 0FE5057B178F5B5300B35F8C /* DFGOSRAvailabilityAnalysisPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE5056E178F5B5300B35F8C /* DFGOSRAvailabilityAnalysisPhase.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FE5057C178F5B5300B35F8C /* DFGSSAConversionPhase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FE5056F178F5B5300B35F8C /* DFGSSAConversionPhase.cpp */; }; > 0FE5057D178F5B5300B35F8C /* DFGSSAConversionPhase.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE50570178F5B5300B35F8C /* DFGSSAConversionPhase.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0FE5058A1798ABF500B35F8C /* DFGAbstractInterpreter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE505861798ABF500B35F8C /* DFGAbstractInterpreter.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0FE5058B1798ABF500B35F8C /* DFGAbstractInterpreterInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE505871798ABF500B35F8C /* DFGAbstractInterpreterInlines.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0FE5058C1798ABF500B35F8C /* DFGInPlaceAbstractState.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FE505881798ABF500B35F8C /* DFGInPlaceAbstractState.cpp */; }; >+ 0FE5058D1798ABF500B35F8C /* DFGInPlaceAbstractState.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE505891798ABF500B35F8C /* DFGInPlaceAbstractState.h */; settings = {ATTRIBUTES = (Private, ); }; }; >+ 0FE5058F1798AE7100B35F8C /* DFGMergeMode.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE5058E1798AE6F00B35F8C /* DFGMergeMode.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FE8534B1723CDA500B618F5 /* DFGDesiredWatchpoints.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FE853491723CDA500B618F5 /* DFGDesiredWatchpoints.cpp */; }; > 0FE8534C1723CDA500B618F5 /* DFGDesiredWatchpoints.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE8534A1723CDA500B618F5 /* DFGDesiredWatchpoints.h */; settings = {ATTRIBUTES = (Private, ); }; }; > 0FEA0A08170513DB00BB722C /* FTLAbbreviations.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEA09FD170513DB00BB722C /* FTLAbbreviations.h */; settings = {ATTRIBUTES = (Private, ); }; }; >@@ -1226,8 +1229,6 @@ > 0F5EF91B16878F78003E5C25 /* JITThunks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JITThunks.cpp; sourceTree = "<group>"; }; > 0F5EF91C16878F78003E5C25 /* JITThunks.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JITThunks.h; sourceTree = "<group>"; }; > 0F5F08CE146C762F000472A9 /* UnconditionalFinalizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UnconditionalFinalizer.h; sourceTree = "<group>"; }; >- 0F62016D143FCD2F0068B77C /* DFGAbstractState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGAbstractState.cpp; path = dfg/DFGAbstractState.cpp; sourceTree = "<group>"; }; >- 0F62016E143FCD2F0068B77C /* DFGAbstractState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGAbstractState.h; path = dfg/DFGAbstractState.h; sourceTree = "<group>"; }; > 0F62016F143FCD2F0068B77C /* DFGAbstractValue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGAbstractValue.h; path = dfg/DFGAbstractValue.h; sourceTree = "<group>"; }; > 0F620170143FCD2F0068B77C /* DFGBasicBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGBasicBlock.h; path = dfg/DFGBasicBlock.h; sourceTree = "<group>"; }; > 0F620172143FCD2F0068B77C /* DFGVariableAccessData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGVariableAccessData.h; path = dfg/DFGVariableAccessData.h; sourceTree = "<group>"; }; >@@ -1414,6 +1415,11 @@ > 0FE5056E178F5B5300B35F8C /* DFGOSRAvailabilityAnalysisPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGOSRAvailabilityAnalysisPhase.h; path = dfg/DFGOSRAvailabilityAnalysisPhase.h; sourceTree = "<group>"; }; > 0FE5056F178F5B5300B35F8C /* DFGSSAConversionPhase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGSSAConversionPhase.cpp; path = dfg/DFGSSAConversionPhase.cpp; sourceTree = "<group>"; }; > 0FE50570178F5B5300B35F8C /* DFGSSAConversionPhase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGSSAConversionPhase.h; path = dfg/DFGSSAConversionPhase.h; sourceTree = "<group>"; }; >+ 0FE505861798ABF500B35F8C /* DFGAbstractInterpreter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGAbstractInterpreter.h; path = dfg/DFGAbstractInterpreter.h; sourceTree = "<group>"; }; >+ 0FE505871798ABF500B35F8C /* DFGAbstractInterpreterInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGAbstractInterpreterInlines.h; path = dfg/DFGAbstractInterpreterInlines.h; sourceTree = "<group>"; }; >+ 0FE505881798ABF500B35F8C /* DFGInPlaceAbstractState.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGInPlaceAbstractState.cpp; path = dfg/DFGInPlaceAbstractState.cpp; sourceTree = "<group>"; }; >+ 0FE505891798ABF500B35F8C /* DFGInPlaceAbstractState.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGInPlaceAbstractState.h; path = dfg/DFGInPlaceAbstractState.h; sourceTree = "<group>"; }; >+ 0FE5058E1798AE6F00B35F8C /* DFGMergeMode.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = DFGMergeMode.h; path = dfg/DFGMergeMode.h; sourceTree = "<group>"; }; > 0FE853491723CDA500B618F5 /* DFGDesiredWatchpoints.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGDesiredWatchpoints.cpp; path = dfg/DFGDesiredWatchpoints.cpp; sourceTree = "<group>"; }; > 0FE8534A1723CDA500B618F5 /* DFGDesiredWatchpoints.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGDesiredWatchpoints.h; path = dfg/DFGDesiredWatchpoints.h; sourceTree = "<group>"; }; > 0FEA09FD170513DB00BB722C /* FTLAbbreviations.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLAbbreviations.h; path = ftl/FTLAbbreviations.h; sourceTree = "<group>"; }; >@@ -2975,8 +2981,9 @@ > 86EC9DB31328DF44002B2AD7 /* dfg */ = { > isa = PBXGroup; > children = ( >- 0F62016D143FCD2F0068B77C /* DFGAbstractState.cpp */, >- 0F62016E143FCD2F0068B77C /* DFGAbstractState.h */, >+ 0FE5058E1798AE6F00B35F8C /* DFGMergeMode.h */, >+ 0FE505861798ABF500B35F8C /* DFGAbstractInterpreter.h */, >+ 0FE505871798ABF500B35F8C /* DFGAbstractInterpreterInlines.h */, > 0F55C19317276E4600CEABFD /* DFGAbstractValue.cpp */, > 0F62016F143FCD2F0068B77C /* DFGAbstractValue.h */, > 0F66E16814DF3F1300B7B2E4 /* DFGAdjacencyList.h */, >@@ -3055,6 +3062,8 @@ > 86AE6C4C136A11E400963012 /* DFGGPRInfo.h */, > 86EC9DB71328DF82002B2AD7 /* DFGGraph.cpp */, > 86EC9DB81328DF82002B2AD7 /* DFGGraph.h */, >+ 0FE505881798ABF500B35F8C /* DFGInPlaceAbstractState.cpp */, >+ 0FE505891798ABF500B35F8C /* DFGInPlaceAbstractState.h */, > 0F2BDC1F151E803800CD8910 /* DFGInsertionSet.h */, > 0FEA0A2F170D40BF00BB722C /* DFGJITCode.cpp */, > 0FEA0A30170D40BF00BB722C /* DFGJITCode.h */, >@@ -3356,6 +3365,7 @@ > BC18C3E70E16F5CD00B34460 /* ArrayPrototype.h in Headers */, > FE67DB9517519B4A006A814F /* StackIterator.h in Headers */, > FE526BD517663A8400B120DC /* StackIteratorPrivate.h in Headers */, >+ 0FE5058F1798AE7100B35F8C /* DFGMergeMode.h in Headers */, > FEF6835F174343CC00A32E25 /* JITStubsARMv7.h in Headers */, > FE24596217601D580074FCE0 /* CallFrameInlines.h in Headers */, > FEF68360174343CC00A32E25 /* JITStubsX86_64.h in Headers */, >@@ -3419,6 +3429,7 @@ > 0F426A4B1460CD6E00131F8F /* DataFormat.h in Headers */, > BCD2034A0E17135E002C7E82 /* DateConstructor.h in Headers */, > 41359CF30FDD89AD00206180 /* DateConversion.h in Headers */, >+ 0FE5058B1798ABF500B35F8C /* DFGAbstractInterpreterInlines.h in Headers */, > BC1166020E1997B4008066DD /* DateInstance.h in Headers */, > 14A1563210966365006FA260 /* DateInstanceCache.h in Headers */, > 0FEA0A12170513DB00BB722C /* FTLState.h in Headers */, >@@ -3427,7 +3438,6 @@ > BC18C3FA0E16F5CD00B34460 /* Debugger.h in Headers */, > BC3135640F302FA3003DFD3A /* DebuggerActivation.h in Headers */, > BC18C3FB0E16F5CD00B34460 /* DebuggerCallFrame.h in Headers */, >- 0F620178143FCD440068B77C /* DFGAbstractState.h in Headers */, > 0F620177143FCD3F0068B77C /* DFGAbstractValue.h in Headers */, > 0F66E16B14DF3F1600B7B2E4 /* DFGAdjacencyList.h in Headers */, > 0F1E3A461534CBAF000F9456 /* DFGArgumentPosition.h in Headers */, >@@ -3571,6 +3581,7 @@ > A76F54A313B28AAB00EF2BCE /* JITWriteBarrier.h in Headers */, > BC18C4160E16F5CD00B34460 /* JSActivation.h in Headers */, > 0FDB2CEA174896C7007B3C1B /* ConcurrentJITLock.h in Headers */, >+ 0FE5058A1798ABF500B35F8C /* DFGAbstractInterpreter.h in Headers */, > 840480131021A1D9008E7F01 /* JSAPIValueWrapper.h in Headers */, > 0F235BE417178E1C00690C7F /* FTLValueFormat.h in Headers */, > BC18C4170E16F5CD00B34460 /* JSArray.h in Headers */, >@@ -3833,6 +3844,7 @@ > 0F426A481460CBB300131F8F /* ValueRecovery.h in Headers */, > 0F426A491460CBB700131F8F /* VirtualRegister.h in Headers */, > FE4A332015BD2E07006F54F3 /* VMInspector.h in Headers */, >+ 0FE5058D1798ABF500B35F8C /* DFGInPlaceAbstractState.h in Headers */, > 0FDB2CD01742D7DA007B3C1B /* FTLJITFinalizer.h in Headers */, > 0FC815151405119B00CFA603 /* VTableSpectrum.h in Headers */, > 0F919D2615853CE3004A4E7D /* Watchpoint.h in Headers */, >@@ -4271,7 +4283,6 @@ > 14280823107EC02C0013E7B2 /* Debugger.cpp in Sources */, > BC3135650F302FA3003DFD3A /* DebuggerActivation.cpp in Sources */, > 149559EE0DDCDDF700648087 /* DebuggerCallFrame.cpp in Sources */, >- 0F620179143FCD480068B77C /* DFGAbstractState.cpp in Sources */, > 0F16015D156198C900C2587C /* DFGArgumentsSimplificationPhase.cpp in Sources */, > 0F63948415E48118006A597C /* DFGArrayMode.cpp in Sources */, > 0F235BD517178E1C00690C7F /* FTLExitArgumentForOperand.cpp in Sources */, >@@ -4438,6 +4449,7 @@ > 0F766D3415AE2538008F363E /* JumpReplacementWatchpoint.cpp in Sources */, > BCFD8C920EEB2EE700283848 /* JumpTable.cpp in Sources */, > 0FB5467914F5C46B002C2989 /* LazyOperandValueProfile.cpp in Sources */, >+ 0FE5058C1798ABF500B35F8C /* DFGInPlaceAbstractState.cpp in Sources */, > 95AB83420DA4322500BC83F3 /* LegacyProfiler.cpp in Sources */, > 0FE5057C178F5B5300B35F8C /* DFGSSAConversionPhase.cpp in Sources */, > 148F21B0107EC5410042EC2C /* Lexer.cpp in Sources */, >Index: Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h (revision 0) >+++ Source/JavaScriptCore/dfg/DFGAbstractInterpreter.h (working copy) >@@ -0,0 +1,204 @@ >+/* >+ * Copyright (C) 2013 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 DFGAbstractInterpreter_h >+#define DFGAbstractInterpreter_h >+ >+#include <wtf/Platform.h> >+ >+#if ENABLE(DFG_JIT) >+ >+#include "DFGAbstractValue.h" >+#include "DFGBranchDirection.h" >+#include "DFGGraph.h" >+#include "DFGNode.h" >+ >+namespace JSC { namespace DFG { >+ >+template<typename AbstractStateType> >+class AbstractInterpreter { >+public: >+ AbstractInterpreter(Graph&, AbstractStateType& state); >+ ~AbstractInterpreter(); >+ >+ AbstractValue& forNode(Node* node) >+ { >+ return m_state.forNode(node); >+ } >+ >+ AbstractValue& forNode(Edge edge) >+ { >+ return forNode(edge.node()); >+ } >+ >+ Operands<AbstractValue>& variables() >+ { >+ return m_state.variables(); >+ } >+ >+ bool needsTypeCheck(Node* node, SpeculatedType typesPassedThrough) >+ { >+ return forNode(node).m_type & ~typesPassedThrough; >+ } >+ >+ bool needsTypeCheck(Edge edge, SpeculatedType typesPassedThrough) >+ { >+ return needsTypeCheck(edge.node(), typesPassedThrough); >+ } >+ >+ bool needsTypeCheck(Edge edge) >+ { >+ return needsTypeCheck(edge, typeFilterFor(edge.useKind())); >+ } >+ >+ // Abstractly executes the given node. The new abstract state is stored into an >+ // abstract stack stored in *this. Loads of local variables (that span >+ // basic blocks) interrogate the basic block's notion of the state at the head. >+ // Stores to local variables are handled in endBasicBlock(). This returns true >+ // if execution should continue past this node. Notably, it will return true >+ // for block terminals, so long as those terminals are not Return or variants >+ // of Throw. >+ // >+ // This is guaranteed to be equivalent to doing: >+ // >+ // if (state.startExecuting(index)) { >+ // state.executeEdges(index); >+ // result = state.executeEffects(index); >+ // } else >+ // result = true; >+ bool execute(unsigned indexInBlock); >+ >+ // Indicate the start of execution of the node. It resets any state in the node, >+ // that is progressively built up by executeEdges() and executeEffects(). In >+ // particular, this resets canExit(), so if you want to "know" between calls of >+ // startExecuting() and executeEdges()/Effects() whether the last run of the >+ // analysis concluded that the node can exit, you should probably set that >+ // information aside prior to calling startExecuting(). >+ bool startExecuting(Node*); >+ bool startExecuting(unsigned indexInBlock); >+ >+ // Abstractly execute the edges of the given node. This runs filterEdgeByUse() >+ // on all edges of the node. You can skip this step, if you have already used >+ // filterEdgeByUse() (or some equivalent) on each edge. >+ void executeEdges(Node*); >+ void executeEdges(unsigned indexInBlock); >+ >+ ALWAYS_INLINE void filterEdgeByUse(Node* node, Edge& edge) >+ { >+ ASSERT(mayHaveTypeCheck(edge.useKind()) || !needsTypeCheck(edge)); >+ filterByType(node, edge, typeFilterFor(edge.useKind())); >+ } >+ >+ // Abstractly execute the effects of the given node. This changes the abstract >+ // state assuming that edges have already been filtered. >+ bool executeEffects(unsigned indexInBlock); >+ bool executeEffects(unsigned indexInBlock, Node*); >+ >+ void dump(PrintStream& out); >+ >+ template<typename T> >+ FiltrationResult filter(T node, const StructureSet& set) >+ { >+ return filter(forNode(node), set); >+ } >+ >+ template<typename T> >+ FiltrationResult filterArrayModes(T node, ArrayModes arrayModes) >+ { >+ return filterArrayModes(forNode(node), arrayModes); >+ } >+ >+ template<typename T> >+ FiltrationResult filter(T node, SpeculatedType type) >+ { >+ return filter(forNode(node), type); >+ } >+ >+ template<typename T> >+ FiltrationResult filterByValue(T node, JSValue value) >+ { >+ return filterByValue(forNode(node), value); >+ } >+ >+ FiltrationResult filter(AbstractValue&, const StructureSet&); >+ FiltrationResult filterArrayModes(AbstractValue&, ArrayModes); >+ FiltrationResult filter(AbstractValue&, SpeculatedType); >+ FiltrationResult filterByValue(AbstractValue&, JSValue); >+ >+private: >+ void clobberWorld(const CodeOrigin&, unsigned indexInBlock); >+ void clobberCapturedVars(const CodeOrigin&); >+ void clobberStructures(unsigned indexInBlock); >+ >+ enum BooleanResult { >+ UnknownBooleanResult, >+ DefinitelyFalse, >+ DefinitelyTrue >+ }; >+ BooleanResult booleanResult(Node*, AbstractValue&); >+ >+ bool trySetConstant(Node* node, JSValue value) >+ { >+ // Make sure we don't constant fold something that will produce values that contravene >+ // predictions. If that happens then we know that the code will OSR exit, forcing >+ // recompilation. But if we tried to constant fold then we'll have a very degenerate >+ // IR: namely we'll have a JSConstant that contravenes its own prediction. There's a >+ // lot of subtle code that assumes that >+ // speculationFromValue(jsConstant) == jsConstant.prediction(). "Hardening" that code >+ // is probably less sane than just pulling back on constant folding. >+ SpeculatedType oldType = node->prediction(); >+ if (mergeSpeculations(speculationFromValue(value), oldType) != oldType) >+ return false; >+ >+ forNode(node).set(m_graph, value); >+ return true; >+ } >+ >+ ALWAYS_INLINE void filterByType(Node* node, Edge& edge, SpeculatedType type) >+ { >+ AbstractValue& value = forNode(edge); >+ if (value.m_type & ~type) { >+ node->setCanExit(true); >+ edge.setProofStatus(NeedsCheck); >+ } else >+ edge.setProofStatus(IsProved); >+ >+ filter(value, type); >+ } >+ >+ void verifyEdge(Node*, Edge); >+ void verifyEdges(Node*); >+ >+ CodeBlock* m_codeBlock; >+ Graph& m_graph; >+ AbstractStateType& m_state; >+}; >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >+ >+#endif // DFGAbstractInterpreter_h >+ >Index: Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (revision 0) >+++ Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h (working copy) >@@ -0,0 +1,1629 @@ >+/* >+ * Copyright (C) 2013 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 DFGAbstractInterpreterInlines_h >+#define DFGAbstractInterpreterInlines_h >+ >+#include <wtf/Platform.h> >+ >+#if ENABLE(DFG_JIT) >+ >+#include "DFGAbstractInterpreter.h" >+#include "GetByIdStatus.h" >+#include "Operations.h" >+#include "PutByIdStatus.h" >+#include "StringObject.h" >+ >+namespace JSC { namespace DFG { >+ >+template<typename AbstractStateType> >+AbstractInterpreter<AbstractStateType>::AbstractInterpreter(Graph& graph, AbstractStateType& state) >+ : m_codeBlock(graph.m_codeBlock) >+ , m_graph(graph) >+ , m_state(state) >+{ >+} >+ >+template<typename AbstractStateType> >+AbstractInterpreter<AbstractStateType>::~AbstractInterpreter() >+{ >+} >+ >+template<typename AbstractStateType> >+typename AbstractInterpreter<AbstractStateType>::BooleanResult >+AbstractInterpreter<AbstractStateType>::booleanResult( >+ Node* node, AbstractValue& value) >+{ >+ JSValue childConst = value.value(); >+ if (childConst) { >+ if (childConst.toBoolean(m_codeBlock->globalObjectFor(node->codeOrigin)->globalExec())) >+ return DefinitelyTrue; >+ return DefinitelyFalse; >+ } >+ >+ // 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->codeOrigin)) >+ && structure->typeInfo().type() != StringType) >+ return DefinitelyTrue; >+ } >+ >+ return UnknownBooleanResult; >+} >+ >+template<typename AbstractStateType> >+bool AbstractInterpreter<AbstractStateType>::startExecuting(Node* node) >+{ >+ ASSERT(m_state.block()); >+ ASSERT(m_state.isValid()); >+ >+ m_state.setDidClobber(false); >+ >+ node->setCanExit(false); >+ >+ return node->shouldGenerate(); >+} >+ >+template<typename AbstractStateType> >+bool AbstractInterpreter<AbstractStateType>::startExecuting(unsigned indexInBlock) >+{ >+ return startExecuting(m_state.block()->at(indexInBlock)); >+} >+ >+template<typename AbstractStateType> >+void AbstractInterpreter<AbstractStateType>::executeEdges(Node* node) >+{ >+ DFG_NODE_DO_TO_CHILDREN(m_graph, node, filterEdgeByUse); >+} >+ >+template<typename AbstractStateType> >+void AbstractInterpreter<AbstractStateType>::executeEdges(unsigned indexInBlock) >+{ >+ executeEdges(m_state.block()->at(indexInBlock)); >+} >+ >+template<typename AbstractStateType> >+void AbstractInterpreter<AbstractStateType>::verifyEdge(Node*, Edge edge) >+{ >+ RELEASE_ASSERT(!(forNode(edge).m_type & ~typeFilterFor(edge.useKind()))); >+} >+ >+template<typename AbstractStateType> >+void AbstractInterpreter<AbstractStateType>::verifyEdges(Node* node) >+{ >+ DFG_NODE_DO_TO_CHILDREN(m_graph, node, verifyEdge); >+} >+ >+template<typename AbstractStateType> >+bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned indexInBlock, Node* node) >+{ >+ if (!ASSERT_DISABLED) >+ verifyEdges(node); >+ >+ switch (node->op()) { >+ case JSConstant: >+ case WeakJSConstant: >+ case PhantomArguments: { >+ forNode(node).set(m_graph, m_graph.valueOfJSConstant(node)); >+ break; >+ } >+ >+ case Identity: { >+ forNode(node) = forNode(node->child1()); >+ break; >+ } >+ >+ case GetArgument: { >+ ASSERT(m_graph.m_form == SSA); >+ VariableAccessData* variable = node->variableAccessData(); >+ AbstractValue& value = m_state.variables().operand(variable->local()); >+ ASSERT(value.isTop()); >+ FiltrationResult result = >+ value.filter(typeFilterFor(useKindFor(variable->flushFormat()))); >+ ASSERT_UNUSED(result, result == FiltrationOK); >+ forNode(node) = value; >+ break; >+ } >+ >+ case GetLocal: { >+ VariableAccessData* variableAccessData = node->variableAccessData(); >+ if (variableAccessData->prediction() == SpecNone) { >+ m_state.setIsValid(false); >+ break; >+ } >+ AbstractValue value = m_state.variables().operand(variableAccessData->local()); >+ if (!variableAccessData->isCaptured()) { >+ if (value.isClear()) >+ node->setCanExit(true); >+ } >+ if (value.value()) >+ m_state.setFoundConstants(true); >+ forNode(node) = value; >+ break; >+ } >+ >+ case GetLocalUnlinked: { >+ AbstractValue value = m_state.variables().operand(node->unlinkedLocal()); >+ if (value.value()) >+ m_state.setFoundConstants(true); >+ forNode(node) = value; >+ break; >+ } >+ >+ case SetLocal: { >+ m_state.variables().operand(node->local()) = forNode(node->child1()); >+ break; >+ } >+ >+ case MovHint: >+ case MovHintAndCheck: { >+ // Don't need to do anything. A MovHint is effectively a promise that the SetLocal >+ // was dead. >+ break; >+ } >+ >+ case ZombieHint: { >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ >+ case SetArgument: >+ // Assert that the state of arguments has been set. >+ ASSERT(!m_state.block()->valuesAtHead.operand(node->local()).isClear()); >+ break; >+ >+ case BitAnd: >+ case BitOr: >+ case BitXor: >+ case BitRShift: >+ case BitLShift: >+ case BitURShift: { >+ JSValue left = forNode(node->child1()).value(); >+ JSValue right = forNode(node->child2()).value(); >+ if (left && right && left.isInt32() && right.isInt32()) { >+ int32_t a = left.asInt32(); >+ int32_t b = right.asInt32(); >+ bool constantWasSet; >+ switch (node->op()) { >+ case BitAnd: >+ constantWasSet = trySetConstant(node, JSValue(a & b)); >+ break; >+ case BitOr: >+ constantWasSet = trySetConstant(node, JSValue(a | b)); >+ break; >+ case BitXor: >+ constantWasSet = trySetConstant(node, JSValue(a ^ b)); >+ break; >+ case BitRShift: >+ constantWasSet = trySetConstant(node, JSValue(a >> static_cast<uint32_t>(b))); >+ break; >+ case BitLShift: >+ constantWasSet = trySetConstant(node, JSValue(a << static_cast<uint32_t>(b))); >+ break; >+ case BitURShift: >+ constantWasSet = trySetConstant(node, JSValue(static_cast<uint32_t>(a) >> static_cast<uint32_t>(b))); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ constantWasSet = false; >+ } >+ if (constantWasSet) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } >+ forNode(node).setType(SpecInt32); >+ break; >+ } >+ >+ case UInt32ToNumber: { >+ JSValue child = forNode(node->child1()).value(); >+ if (child && child.isNumber()) { >+ ASSERT(child.isInt32()); >+ if (trySetConstant(node, JSValue(child.asUInt32()))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } >+ if (!node->canSpeculateInteger()) >+ forNode(node).setType(SpecDouble); >+ else { >+ forNode(node).setType(SpecInt32); >+ node->setCanExit(true); >+ } >+ break; >+ } >+ >+ case DoubleAsInt32: { >+ JSValue child = forNode(node->child1()).value(); >+ if (child && child.isNumber()) { >+ double asDouble = child.asNumber(); >+ int32_t asInt = JSC::toInt32(asDouble); >+ if (bitwise_cast<int64_t>(static_cast<double>(asInt)) == bitwise_cast<int64_t>(asDouble) >+ && trySetConstant(node, JSValue(asInt))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } >+ node->setCanExit(true); >+ forNode(node).setType(SpecInt32); >+ break; >+ } >+ >+ case ValueToInt32: { >+ JSValue child = forNode(node->child1()).value(); >+ if (child && child.isNumber()) { >+ bool constantWasSet; >+ if (child.isInt32()) >+ constantWasSet = trySetConstant(node, child); >+ else >+ constantWasSet = trySetConstant(node, JSValue(JSC::toInt32(child.asDouble()))); >+ if (constantWasSet) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } >+ >+ forNode(node).setType(SpecInt32); >+ break; >+ } >+ >+ case Int32ToDouble: >+ case ForwardInt32ToDouble: { >+ JSValue child = forNode(node->child1()).value(); >+ if (child && child.isNumber() >+ && trySetConstant(node, JSValue(JSValue::EncodeAsDouble, child.asNumber()))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ if (isInt32Speculation(forNode(node->child1()).m_type)) >+ forNode(node).setType(SpecDoubleReal); >+ else >+ forNode(node).setType(SpecDouble); >+ break; >+ } >+ >+ case ValueAdd: >+ case ArithAdd: { >+ JSValue left = forNode(node->child1()).value(); >+ JSValue right = forNode(node->child2()).value(); >+ if (left && right && left.isNumber() && right.isNumber() >+ && trySetConstant(node, JSValue(left.asNumber() + right.asNumber()))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ switch (node->binaryUseKind()) { >+ case Int32Use: >+ forNode(node).setType(SpecInt32); >+ if (!nodeCanTruncateInteger(node->arithNodeFlags())) >+ node->setCanExit(true); >+ break; >+ case NumberUse: >+ if (isRealNumberSpeculation(forNode(node->child1()).m_type) >+ && isRealNumberSpeculation(forNode(node->child2()).m_type)) >+ forNode(node).setType(SpecDoubleReal); >+ else >+ forNode(node).setType(SpecDouble); >+ break; >+ default: >+ RELEASE_ASSERT(node->op() == ValueAdd); >+ clobberWorld(node->codeOrigin, indexInBlock); >+ forNode(node).setType(SpecString | SpecInt32 | SpecNumber); >+ break; >+ } >+ break; >+ } >+ >+ case MakeRope: { >+ forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); >+ break; >+ } >+ >+ case ArithSub: { >+ JSValue left = forNode(node->child1()).value(); >+ JSValue right = forNode(node->child2()).value(); >+ if (left && right && left.isNumber() && right.isNumber() >+ && trySetConstant(node, JSValue(left.asNumber() - right.asNumber()))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ switch (node->binaryUseKind()) { >+ case Int32Use: >+ forNode(node).setType(SpecInt32); >+ if (!nodeCanTruncateInteger(node->arithNodeFlags())) >+ node->setCanExit(true); >+ break; >+ case NumberUse: >+ forNode(node).setType(SpecDouble); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ break; >+ } >+ >+ case ArithNegate: { >+ JSValue child = forNode(node->child1()).value(); >+ if (child && child.isNumber() >+ && trySetConstant(node, JSValue(-child.asNumber()))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ switch (node->child1().useKind()) { >+ case Int32Use: >+ forNode(node).setType(SpecInt32); >+ if (!nodeCanTruncateInteger(node->arithNodeFlags())) >+ node->setCanExit(true); >+ break; >+ case NumberUse: >+ forNode(node).setType(SpecDouble); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ break; >+ } >+ >+ case ArithMul: { >+ JSValue left = forNode(node->child1()).value(); >+ JSValue right = forNode(node->child2()).value(); >+ if (left && right && left.isNumber() && right.isNumber() >+ && trySetConstant(node, JSValue(left.asNumber() * right.asNumber()))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ switch (node->binaryUseKind()) { >+ case Int32Use: >+ forNode(node).setType(SpecInt32); >+ if (!nodeCanTruncateInteger(node->arithNodeFlags()) >+ || !nodeCanIgnoreNegativeZero(node->arithNodeFlags())) >+ node->setCanExit(true); >+ break; >+ case NumberUse: >+ if (isRealNumberSpeculation(forNode(node->child1()).m_type) >+ || isRealNumberSpeculation(forNode(node->child2()).m_type)) >+ forNode(node).setType(SpecDoubleReal); >+ else >+ forNode(node).setType(SpecDouble); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ break; >+ } >+ >+ case ArithDiv: >+ case ArithMin: >+ case ArithMax: >+ case ArithMod: { >+ JSValue left = forNode(node->child1()).value(); >+ JSValue right = forNode(node->child2()).value(); >+ if (node->op() == ArithMod && right && right.isNumber() && right.asNumber() == 1 >+ && trySetConstant(node, JSValue(0))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ if (left && right && left.isNumber() && right.isNumber()) { >+ double a = left.asNumber(); >+ double b = right.asNumber(); >+ bool constantWasSet; >+ switch (node->op()) { >+ case ArithDiv: >+ constantWasSet = trySetConstant(node, JSValue(a / b)); >+ break; >+ case ArithMin: >+ constantWasSet = trySetConstant(node, JSValue(a < b ? a : (b <= a ? b : a + b))); >+ break; >+ case ArithMax: >+ constantWasSet = trySetConstant(node, JSValue(a > b ? a : (b >= a ? b : a + b))); >+ break; >+ case ArithMod: >+ constantWasSet = trySetConstant(node, JSValue(fmod(a, b))); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ constantWasSet = false; >+ break; >+ } >+ if (constantWasSet) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } >+ switch (node->binaryUseKind()) { >+ case Int32Use: >+ forNode(node).setType(SpecInt32); >+ node->setCanExit(true); >+ break; >+ case NumberUse: >+ forNode(node).setType(SpecDouble); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ break; >+ } >+ >+ case ArithAbs: { >+ JSValue child = forNode(node->child1()).value(); >+ if (child && child.isNumber() >+ && trySetConstant(node, JSValue(fabs(child.asNumber())))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ switch (node->child1().useKind()) { >+ case Int32Use: >+ forNode(node).setType(SpecInt32); >+ node->setCanExit(true); >+ break; >+ case NumberUse: >+ forNode(node).setType(SpecDouble); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ break; >+ } >+ >+ case ArithSqrt: { >+ JSValue child = forNode(node->child1()).value(); >+ if (child && child.isNumber() >+ && trySetConstant(node, JSValue(sqrt(child.asNumber())))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ forNode(node).setType(SpecDouble); >+ break; >+ } >+ >+ case LogicalNot: { >+ bool didSetConstant = false; >+ switch (booleanResult(node, forNode(node->child1()))) { >+ case DefinitelyTrue: >+ didSetConstant = trySetConstant(node, jsBoolean(false)); >+ break; >+ case DefinitelyFalse: >+ didSetConstant = trySetConstant(node, jsBoolean(true)); >+ break; >+ default: >+ break; >+ } >+ if (didSetConstant) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ switch (node->child1().useKind()) { >+ case BooleanUse: >+ case Int32Use: >+ case NumberUse: >+ case UntypedUse: >+ break; >+ case ObjectOrOtherUse: >+ node->setCanExit(true); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ forNode(node).setType(SpecBoolean); >+ break; >+ } >+ >+ case IsUndefined: >+ case IsBoolean: >+ case IsNumber: >+ case IsString: >+ case IsObject: >+ case IsFunction: { >+ node->setCanExit( >+ node->op() == IsUndefined >+ && m_graph.masqueradesAsUndefinedWatchpointIsStillValid(node->codeOrigin)); >+ JSValue child = forNode(node->child1()).value(); >+ if (child) { >+ bool constantWasSet; >+ switch (node->op()) { >+ case IsUndefined: >+ constantWasSet = trySetConstant(node, jsBoolean( >+ child.isCell() >+ ? child.asCell()->structure()->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->codeOrigin)) >+ : child.isUndefined())); >+ break; >+ case IsBoolean: >+ constantWasSet = trySetConstant(node, jsBoolean(child.isBoolean())); >+ break; >+ case IsNumber: >+ constantWasSet = trySetConstant(node, jsBoolean(child.isNumber())); >+ break; >+ case IsString: >+ constantWasSet = trySetConstant(node, jsBoolean(isJSString(child))); >+ break; >+ case IsObject: >+ if (child.isNull() || !child.isObject()) { >+ constantWasSet = trySetConstant(node, jsBoolean(child.isNull())); >+ break; >+ } >+ default: >+ constantWasSet = false; >+ break; >+ } >+ if (constantWasSet) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } >+ >+ forNode(node).setType(SpecBoolean); >+ break; >+ } >+ >+ case TypeOf: { >+ VM* vm = m_codeBlock->vm(); >+ JSValue child = forNode(node->child1()).value(); >+ AbstractValue& abstractChild = forNode(node->child1()); >+ if (child) { >+ JSValue typeString = jsTypeStringForValue(*vm, m_codeBlock->globalObjectFor(node->codeOrigin), child); >+ if (trySetConstant(node, typeString)) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } else if (isNumberSpeculation(abstractChild.m_type)) { >+ if (trySetConstant(node, vm->smallStrings.numberString())) { >+ filter(node->child1(), SpecNumber); >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } else if (isStringSpeculation(abstractChild.m_type)) { >+ if (trySetConstant(node, vm->smallStrings.stringString())) { >+ filter(node->child1(), SpecString); >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } else if (isFinalObjectSpeculation(abstractChild.m_type) || isArraySpeculation(abstractChild.m_type) || isArgumentsSpeculation(abstractChild.m_type)) { >+ if (trySetConstant(node, vm->smallStrings.objectString())) { >+ filter(node->child1(), SpecFinalObject | SpecArray | SpecArguments); >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } else if (isFunctionSpeculation(abstractChild.m_type)) { >+ if (trySetConstant(node, vm->smallStrings.functionString())) { >+ filter(node->child1(), SpecFunction); >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } else if (isBooleanSpeculation(abstractChild.m_type)) { >+ if (trySetConstant(node, vm->smallStrings.booleanString())) { >+ filter(node->child1(), SpecBoolean); >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } >+ >+ switch (node->child1().useKind()) { >+ case StringUse: >+ case CellUse: >+ node->setCanExit(true); >+ break; >+ case UntypedUse: >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); >+ break; >+ } >+ >+ case CompareLess: >+ case CompareLessEq: >+ case CompareGreater: >+ case CompareGreaterEq: >+ case CompareEq: >+ case CompareEqConstant: { >+ bool constantWasSet = false; >+ >+ JSValue leftConst = forNode(node->child1()).value(); >+ JSValue rightConst = forNode(node->child2()).value(); >+ if (leftConst && rightConst) { >+ if (leftConst.isNumber() && rightConst.isNumber()) { >+ double a = leftConst.asNumber(); >+ double b = rightConst.asNumber(); >+ switch (node->op()) { >+ case CompareLess: >+ constantWasSet = trySetConstant(node, jsBoolean(a < b)); >+ break; >+ case CompareLessEq: >+ constantWasSet = trySetConstant(node, jsBoolean(a <= b)); >+ break; >+ case CompareGreater: >+ constantWasSet = trySetConstant(node, jsBoolean(a > b)); >+ break; >+ case CompareGreaterEq: >+ constantWasSet = trySetConstant(node, jsBoolean(a >= b)); >+ break; >+ case CompareEq: >+ constantWasSet = trySetConstant(node, jsBoolean(a == b)); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ constantWasSet = false; >+ break; >+ } >+ } >+ >+ if (!constantWasSet && node->op() == CompareEq >+ && leftConst.isString() && rightConst.isString()) { >+ const StringImpl* a = asString(leftConst)->tryGetValueImpl(); >+ const StringImpl* b = asString(rightConst)->tryGetValueImpl(); >+ if (a && b) >+ constantWasSet = trySetConstant(node, jsBoolean(WTF::equal(a, b))); >+ } >+ } >+ >+ if (!constantWasSet && (node->op() == CompareEqConstant || node->op() == CompareEq)) { >+ SpeculatedType leftType = forNode(node->child1()).m_type; >+ SpeculatedType rightType = forNode(node->child2()).m_type; >+ if ((isInt32Speculation(leftType) && isOtherSpeculation(rightType)) >+ || (isOtherSpeculation(leftType) && isInt32Speculation(rightType))) >+ constantWasSet = trySetConstant(node, jsBoolean(false)); >+ } >+ >+ if (constantWasSet) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ >+ forNode(node).setType(SpecBoolean); >+ >+ // This is overly conservative. But the only thing this prevents is store elimination, >+ // and how likely is it, really, that you'll have redundant stores across a comparison >+ // operation? Comparison operations are typically at the end of basic blocks, so >+ // unless we have global store elimination (super unlikely given how unprofitable that >+ // optimization is to begin with), you aren't going to be wanting to store eliminate >+ // across an equality op. >+ node->setCanExit(true); >+ break; >+ } >+ >+ case CompareStrictEq: >+ case CompareStrictEqConstant: { >+ Node* leftNode = node->child1().node(); >+ Node* rightNode = node->child2().node(); >+ JSValue left = forNode(leftNode).value(); >+ JSValue right = forNode(rightNode).value(); >+ if (left && right) { >+ if (left.isNumber() && right.isNumber() >+ && trySetConstant(node, jsBoolean(left.asNumber() == right.asNumber()))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ if (left.isString() && right.isString()) { >+ const StringImpl* a = asString(left)->tryGetValueImpl(); >+ const StringImpl* b = asString(right)->tryGetValueImpl(); >+ if (a && b && trySetConstant(node, jsBoolean(WTF::equal(a, b)))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } >+ } >+ forNode(node).setType(SpecBoolean); >+ node->setCanExit(true); // This is overly conservative. >+ break; >+ } >+ >+ case StringCharCodeAt: >+ node->setCanExit(true); >+ forNode(node).setType(SpecInt32); >+ break; >+ >+ case StringCharAt: >+ node->setCanExit(true); >+ forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); >+ break; >+ >+ case GetByVal: { >+ node->setCanExit(true); >+ switch (node->arrayMode().type()) { >+ case Array::SelectUsingPredictions: >+ case Array::Unprofiled: >+ case Array::Undecided: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ case Array::ForceExit: >+ m_state.setIsValid(false); >+ break; >+ case Array::Generic: >+ clobberWorld(node->codeOrigin, indexInBlock); >+ forNode(node).makeTop(); >+ break; >+ case Array::String: >+ if (node->arrayMode().isOutOfBounds()) { >+ // If the watchpoint was still valid we could totally set this to be >+ // SpecString | SpecOther. Except that we'd have to be careful. If we >+ // tested the watchpoint state here then it could change by the time >+ // we got to the backend. So to do this right, we'd have to get the >+ // fixup phase to check the watchpoint state and then bake into the >+ // GetByVal operation the fact that we're using a watchpoint, using >+ // something like Array::SaneChain (except not quite, because that >+ // implies an in-bounds access). None of this feels like it's worth it, >+ // so we're going with TOP for now. >+ forNode(node).makeTop(); >+ } else >+ forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); >+ break; >+ case Array::Arguments: >+ forNode(node).makeTop(); >+ break; >+ case Array::Int32: >+ if (node->arrayMode().isOutOfBounds()) { >+ clobberWorld(node->codeOrigin, indexInBlock); >+ forNode(node).makeTop(); >+ } else >+ forNode(node).setType(SpecInt32); >+ break; >+ case Array::Double: >+ if (node->arrayMode().isOutOfBounds()) { >+ clobberWorld(node->codeOrigin, indexInBlock); >+ forNode(node).makeTop(); >+ } else if (node->arrayMode().isSaneChain()) >+ forNode(node).setType(SpecDouble); >+ else >+ forNode(node).setType(SpecDoubleReal); >+ break; >+ case Array::Contiguous: >+ case Array::ArrayStorage: >+ case Array::SlowPutArrayStorage: >+ if (node->arrayMode().isOutOfBounds()) >+ clobberWorld(node->codeOrigin, indexInBlock); >+ forNode(node).makeTop(); >+ break; >+ case Array::Int8Array: >+ forNode(node).setType(SpecInt32); >+ break; >+ case Array::Int16Array: >+ forNode(node).setType(SpecInt32); >+ break; >+ case Array::Int32Array: >+ forNode(node).setType(SpecInt32); >+ break; >+ case Array::Uint8Array: >+ forNode(node).setType(SpecInt32); >+ break; >+ case Array::Uint8ClampedArray: >+ forNode(node).setType(SpecInt32); >+ break; >+ case Array::Uint16Array: >+ forNode(node).setType(SpecInt32); >+ break; >+ case Array::Uint32Array: >+ if (node->shouldSpeculateInteger()) >+ forNode(node).setType(SpecInt32); >+ else >+ forNode(node).setType(SpecDouble); >+ break; >+ case Array::Float32Array: >+ forNode(node).setType(SpecDouble); >+ break; >+ case Array::Float64Array: >+ forNode(node).setType(SpecDouble); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ break; >+ } >+ >+ case PutByVal: >+ case PutByValAlias: { >+ node->setCanExit(true); >+ switch (node->arrayMode().modeForPut().type()) { >+ case Array::ForceExit: >+ m_state.setIsValid(false); >+ break; >+ case Array::Generic: >+ clobberWorld(node->codeOrigin, indexInBlock); >+ break; >+ case Array::Int32: >+ if (node->arrayMode().isOutOfBounds()) >+ clobberWorld(node->codeOrigin, indexInBlock); >+ break; >+ case Array::Double: >+ if (node->arrayMode().isOutOfBounds()) >+ clobberWorld(node->codeOrigin, indexInBlock); >+ break; >+ case Array::Contiguous: >+ case Array::ArrayStorage: >+ if (node->arrayMode().isOutOfBounds()) >+ clobberWorld(node->codeOrigin, indexInBlock); >+ break; >+ case Array::SlowPutArrayStorage: >+ if (node->arrayMode().mayStoreToHole()) >+ clobberWorld(node->codeOrigin, indexInBlock); >+ break; >+ default: >+ break; >+ } >+ break; >+ } >+ >+ case ArrayPush: >+ node->setCanExit(true); >+ clobberWorld(node->codeOrigin, indexInBlock); >+ forNode(node).setType(SpecNumber); >+ break; >+ >+ case ArrayPop: >+ node->setCanExit(true); >+ clobberWorld(node->codeOrigin, indexInBlock); >+ forNode(node).makeTop(); >+ break; >+ >+ case RegExpExec: >+ case RegExpTest: >+ forNode(node).makeTop(); >+ break; >+ >+ case Jump: >+ break; >+ >+ case Branch: { >+ Node* child = node->child1().node(); >+ BooleanResult result = booleanResult(node, forNode(child)); >+ if (result == DefinitelyTrue) { >+ m_state.setBranchDirection(TakeTrue); >+ break; >+ } >+ if (result == DefinitelyFalse) { >+ m_state.setBranchDirection(TakeFalse); >+ break; >+ } >+ // FIXME: The above handles the trivial cases of sparse conditional >+ // constant propagation, but we can do better: >+ // We can specialize the source variable's value on each direction of >+ // the branch. >+ node->setCanExit(true); // This is overly conservative. >+ m_state.setBranchDirection(TakeBoth); >+ break; >+ } >+ >+ case Switch: { >+ // Nothing to do for now. >+ // FIXME: Do sparse conditional things. >+ break; >+ } >+ >+ case Return: >+ m_state.setIsValid(false); >+ break; >+ >+ case Throw: >+ case ThrowReferenceError: >+ m_state.setIsValid(false); >+ node->setCanExit(true); >+ break; >+ >+ case ToPrimitive: { >+ JSValue childConst = forNode(node->child1()).value(); >+ if (childConst && childConst.isNumber() && trySetConstant(node, childConst)) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ >+ ASSERT(node->child1().useKind() == UntypedUse); >+ >+ AbstractValue& source = forNode(node->child1()); >+ AbstractValue& destination = forNode(node); >+ >+ // NB. The more canonical way of writing this would have been: >+ // >+ // destination = source; >+ // if (destination.m_type & !(SpecNumber | SpecString | SpecBoolean)) { >+ // destination.filter(SpecNumber | SpecString | SpecBoolean); >+ // AbstractValue string; >+ // string.set(vm->stringStructure); >+ // destination.merge(string); >+ // } >+ // >+ // The reason why this would, in most other cases, have been better is that >+ // then destination would preserve any non-SpeculatedType knowledge of source. >+ // As it stands, the code below forgets any non-SpeculatedType knowledge that >+ // source would have had. Fortunately, though, for things like strings and >+ // numbers and booleans, we don't care about the non-SpeculatedType knowedge: >+ // the structure won't tell us anything we don't already know, and neither >+ // will ArrayModes. And if the source was a meaningful constant then we >+ // would have handled that above. Unfortunately, this does mean that >+ // ToPrimitive will currently forget string constants. But that's not a big >+ // deal since we don't do any optimization on those currently. >+ >+ clobberWorld(node->codeOrigin, indexInBlock); >+ >+ SpeculatedType type = source.m_type; >+ if (type & ~(SpecNumber | SpecString | SpecBoolean)) { >+ type &= (SpecNumber | SpecString | SpecBoolean); >+ type |= SpecString; >+ } >+ destination.setType(type); >+ if (destination.isClear()) >+ m_state.setIsValid(false); >+ break; >+ } >+ >+ case ToString: { >+ switch (node->child1().useKind()) { >+ case StringObjectUse: >+ // This also filters that the StringObject has the primordial StringObject >+ // structure. >+ filter( >+ node->child1(), >+ m_graph.globalObjectFor(node->codeOrigin)->stringObjectStructure()); >+ node->setCanExit(true); // We could be more precise but it's likely not worth it. >+ break; >+ case StringOrStringObjectUse: >+ node->setCanExit(true); // We could be more precise but it's likely not worth it. >+ break; >+ case CellUse: >+ case UntypedUse: >+ clobberWorld(node->codeOrigin, indexInBlock); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); >+ break; >+ } >+ >+ case NewStringObject: { >+ ASSERT(node->structure()->classInfo() == &StringObject::s_info); >+ forNode(node).set(m_graph, node->structure()); >+ break; >+ } >+ >+ case NewArray: >+ node->setCanExit(true); >+ forNode(node).set( >+ m_graph, >+ m_graph.globalObjectFor(node->codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())); >+ m_state.setHaveStructures(true); >+ break; >+ >+ case NewArrayBuffer: >+ node->setCanExit(true); >+ forNode(node).set( >+ m_graph, >+ m_graph.globalObjectFor(node->codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())); >+ m_state.setHaveStructures(true); >+ break; >+ >+ case NewArrayWithSize: >+ node->setCanExit(true); >+ forNode(node).setType(SpecArray); >+ m_state.setHaveStructures(true); >+ break; >+ >+ case NewRegexp: >+ forNode(node).set(m_graph, m_graph.globalObjectFor(node->codeOrigin)->regExpStructure()); >+ m_state.setHaveStructures(true); >+ break; >+ >+ case ToThis: { >+ AbstractValue& source = forNode(node->child1()); >+ AbstractValue& destination = forNode(node); >+ >+ destination = source; >+ destination.merge(SpecObjectOther); >+ break; >+ } >+ >+ case CreateThis: { >+ forNode(node).setType(SpecFinalObject); >+ break; >+ } >+ >+ case AllocationProfileWatchpoint: >+ node->setCanExit(true); >+ break; >+ >+ case NewObject: >+ forNode(node).set(m_graph, node->structure()); >+ m_state.setHaveStructures(true); >+ break; >+ >+ case CreateActivation: >+ forNode(node).set( >+ m_graph, m_codeBlock->globalObjectFor(node->codeOrigin)->activationStructure()); >+ m_state.setHaveStructures(true); >+ break; >+ >+ case CreateArguments: >+ forNode(node).set( >+ m_graph, m_codeBlock->globalObjectFor(node->codeOrigin)->argumentsStructure()); >+ m_state.setHaveStructures(true); >+ break; >+ >+ case TearOffActivation: >+ case TearOffArguments: >+ // Does nothing that is user-visible. >+ break; >+ >+ case CheckArgumentsNotCreated: >+ if (isEmptySpeculation( >+ m_state.variables().operand( >+ m_graph.argumentsRegisterFor(node->codeOrigin)).m_type)) >+ m_state.setFoundConstants(true); >+ else >+ node->setCanExit(true); >+ break; >+ >+ case GetMyArgumentsLength: >+ // We know that this executable does not escape its arguments, so we can optimize >+ // the arguments a bit. Note that this is not sufficient to force constant folding >+ // of GetMyArgumentsLength, because GetMyArgumentsLength is a clobbering operation. >+ // We perform further optimizations on this later on. >+ if (node->codeOrigin.inlineCallFrame) { >+ forNode(node).set( >+ m_graph, jsNumber(node->codeOrigin.inlineCallFrame->arguments.size() - 1)); >+ } else >+ forNode(node).setType(SpecInt32); >+ node->setCanExit( >+ !isEmptySpeculation( >+ m_state.variables().operand( >+ m_graph.argumentsRegisterFor(node->codeOrigin)).m_type)); >+ break; >+ >+ case GetMyArgumentsLengthSafe: >+ // This potentially clobbers all structures if the arguments object had a getter >+ // installed on the length property. >+ clobberWorld(node->codeOrigin, indexInBlock); >+ // We currently make no guarantee about what this returns because it does not >+ // speculate that the length property is actually a length. >+ forNode(node).makeTop(); >+ break; >+ >+ case GetMyArgumentByVal: >+ node->setCanExit(true); >+ // We know that this executable does not escape its arguments, so we can optimize >+ // the arguments a bit. Note that this ends up being further optimized by the >+ // ArgumentsSimplificationPhase. >+ forNode(node).makeTop(); >+ break; >+ >+ case GetMyArgumentByValSafe: >+ node->setCanExit(true); >+ // This potentially clobbers all structures if the property we're accessing has >+ // a getter. We don't speculate against this. >+ clobberWorld(node->codeOrigin, indexInBlock); >+ // And the result is unknown. >+ forNode(node).makeTop(); >+ break; >+ >+ case NewFunction: >+ case NewFunctionExpression: >+ case NewFunctionNoCheck: >+ forNode(node).set( >+ m_graph, m_codeBlock->globalObjectFor(node->codeOrigin)->functionStructure()); >+ break; >+ >+ case GetCallee: >+ forNode(node).setType(SpecFunction); >+ break; >+ >+ case SetCallee: >+ case SetMyScope: >+ break; >+ >+ case GetScope: // FIXME: We could get rid of these if we know that the JSFunction is a constant. https://bugs.webkit.org/show_bug.cgi?id=106202 >+ case GetMyScope: >+ case SkipTopScope: >+ forNode(node).setType(SpecCellOther); >+ break; >+ >+ case SkipScope: { >+ JSValue child = forNode(node->child1()).value(); >+ if (child && trySetConstant(node, JSValue(jsCast<JSScope*>(child.asCell())->next()))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ forNode(node).setType(SpecCellOther); >+ break; >+ } >+ >+ case GetClosureRegisters: >+ forNode(node).clear(); // The result is not a JS value. >+ break; >+ >+ case GetClosureVar: >+ forNode(node).makeTop(); >+ break; >+ >+ case PutClosureVar: >+ clobberCapturedVars(node->codeOrigin); >+ break; >+ >+ case GetById: >+ case GetByIdFlush: >+ node->setCanExit(true); >+ if (!node->prediction()) { >+ m_state.setIsValid(false); >+ break; >+ } >+ if (isCellSpeculation(node->child1()->prediction())) { >+ if (Structure* structure = forNode(node->child1()).bestProvenStructure()) { >+ GetByIdStatus status = GetByIdStatus::computeFor( >+ m_graph.m_vm, structure, >+ m_graph.identifiers()[node->identifierNumber()]); >+ if (status.isSimple()) { >+ // Assert things that we can't handle and that the computeFor() method >+ // above won't be able to return. >+ ASSERT(status.structureSet().size() == 1); >+ ASSERT(!status.chain()); >+ >+ if (status.specificValue()) >+ forNode(node).set(m_graph, status.specificValue()); >+ else >+ forNode(node).makeTop(); >+ filter(node->child1(), status.structureSet()); >+ >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } >+ } >+ clobberWorld(node->codeOrigin, indexInBlock); >+ forNode(node).makeTop(); >+ break; >+ >+ case GetArrayLength: >+ node->setCanExit(true); // Lies, but it's true for the common case of JSArray, so it's good enough. >+ forNode(node).setType(SpecInt32); >+ break; >+ >+ case CheckExecutable: { >+ // FIXME: We could track executables in AbstractValue, which would allow us to get rid of these checks >+ // more thoroughly. https://bugs.webkit.org/show_bug.cgi?id=106200 >+ // FIXME: We could eliminate these entirely if we know the exact value that flows into this. >+ // https://bugs.webkit.org/show_bug.cgi?id=106201 >+ node->setCanExit(true); >+ break; >+ } >+ >+ case CheckStructure: >+ case ForwardCheckStructure: { >+ // FIXME: We should be able to propagate the structure sets of constants (i.e. prototypes). >+ AbstractValue& value = forNode(node->child1()); >+ ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this. >+ >+ StructureSet& set = node->structureSet(); >+ >+ if (value.m_currentKnownStructure.isSubsetOf(set)) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ >+ node->setCanExit(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); >+ m_state.setHaveStructures(true); >+ break; >+ } >+ >+ case StructureTransitionWatchpoint: >+ case ForwardStructureTransitionWatchpoint: { >+ AbstractValue& value = forNode(node->child1()); >+ >+ // It's only valid to issue a structure transition watchpoint if we already >+ // know that the watchpoint covers a superset of the structures known to >+ // belong to the set of future structures that this value may have. >+ // Currently, we only issue singleton watchpoints (that check one structure) >+ // and our futurePossibleStructure set can only contain zero, one, or an >+ // infinity of structures. >+ ASSERT( >+ value.m_futurePossibleStructure.isSubsetOf(StructureSet(node->structure())) >+ || m_graph.watchpoints().shouldAssumeMixedState(node->structure()->transitionWatchpointSet())); >+ >+ filter(value, node->structure()); >+ m_state.setHaveStructures(true); >+ node->setCanExit(true); >+ break; >+ } >+ >+ case PutStructure: >+ case PhantomPutStructure: >+ if (!forNode(node->child1()).m_currentKnownStructure.isClear()) { >+ clobberStructures(indexInBlock); >+ forNode(node->child1()).set(m_graph, node->structureTransitionData().newStructure); >+ m_state.setHaveStructures(true); >+ } >+ break; >+ case GetButterfly: >+ case AllocatePropertyStorage: >+ case ReallocatePropertyStorage: >+ forNode(node).clear(); // The result is not a JS value. >+ break; >+ case ForwardCheckArray: >+ case CheckArray: { >+ if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ node->setCanExit(true); // Lies, but this is followed by operations (like GetByVal) that always exit, so there is no point in us trying to be clever here. >+ switch (node->arrayMode().type()) { >+ case Array::String: >+ filter(node->child1(), SpecString); >+ break; >+ case Array::Int32: >+ case Array::Double: >+ case Array::Contiguous: >+ case Array::ArrayStorage: >+ case Array::SlowPutArrayStorage: >+ break; >+ case Array::Arguments: >+ filter(node->child1(), SpecArguments); >+ break; >+ case Array::Int8Array: >+ filter(node->child1(), SpecInt8Array); >+ break; >+ case Array::Int16Array: >+ filter(node->child1(), SpecInt16Array); >+ break; >+ case Array::Int32Array: >+ filter(node->child1(), SpecInt32Array); >+ break; >+ case Array::Uint8Array: >+ filter(node->child1(), SpecUint8Array); >+ break; >+ case Array::Uint8ClampedArray: >+ filter(node->child1(), SpecUint8ClampedArray); >+ break; >+ case Array::Uint16Array: >+ filter(node->child1(), SpecUint16Array); >+ break; >+ case Array::Uint32Array: >+ filter(node->child1(), SpecUint32Array); >+ break; >+ case Array::Float32Array: >+ filter(node->child1(), SpecFloat32Array); >+ break; >+ case Array::Float64Array: >+ filter(node->child1(), SpecFloat64Array); >+ break; >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering()); >+ m_state.setHaveStructures(true); >+ break; >+ } >+ case Arrayify: { >+ if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) { >+ m_state.setFoundConstants(true); >+ break; >+ } >+ ASSERT(node->arrayMode().conversion() == Array::Convert >+ || node->arrayMode().conversion() == Array::RageConvert); >+ node->setCanExit(true); >+ clobberStructures(indexInBlock); >+ filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering()); >+ m_state.setHaveStructures(true); >+ break; >+ } >+ case ArrayifyToStructure: { >+ AbstractValue& value = forNode(node->child1()); >+ StructureSet set = node->structure(); >+ if (value.m_futurePossibleStructure.isSubsetOf(set) >+ || value.m_currentKnownStructure.isSubsetOf(set)) >+ m_state.setFoundConstants(true); >+ node->setCanExit(true); >+ clobberStructures(indexInBlock); >+ filter(value, set); >+ m_state.setHaveStructures(true); >+ break; >+ } >+ case GetIndexedPropertyStorage: { >+ forNode(node).clear(); >+ break; >+ } >+ case GetByOffset: { >+ forNode(node).makeTop(); >+ break; >+ } >+ >+ case PutByOffset: { >+ break; >+ } >+ >+ case CheckFunction: { >+ JSValue value = forNode(node->child1()).value(); >+ if (value == node->function()) { >+ m_state.setFoundConstants(true); >+ ASSERT(value); >+ break; >+ } >+ >+ node->setCanExit(true); // Lies! We can do better. >+ filterByValue(node->child1(), node->function()); >+ break; >+ } >+ >+ case PutById: >+ case PutByIdDirect: >+ node->setCanExit(true); >+ if (Structure* structure = forNode(node->child1()).bestProvenStructure()) { >+ PutByIdStatus status = PutByIdStatus::computeFor( >+ m_graph.m_vm, >+ m_graph.globalObjectFor(node->codeOrigin), >+ structure, >+ m_graph.identifiers()[node->identifierNumber()], >+ node->op() == PutByIdDirect); >+ if (status.isSimpleReplace()) { >+ filter(node->child1(), structure); >+ m_state.setFoundConstants(true); >+ break; >+ } >+ if (status.isSimpleTransition()) { >+ clobberStructures(indexInBlock); >+ forNode(node->child1()).set(m_graph, status.newStructure()); >+ m_state.setHaveStructures(true); >+ m_state.setFoundConstants(true); >+ break; >+ } >+ } >+ clobberWorld(node->codeOrigin, indexInBlock); >+ break; >+ >+ case In: >+ // FIXME: We can determine when the property definitely exists based on abstract >+ // value information. >+ clobberWorld(node->codeOrigin, indexInBlock); >+ forNode(node).setType(SpecBoolean); >+ break; >+ >+ case GetGlobalVar: >+ forNode(node).makeTop(); >+ break; >+ >+ case GlobalVarWatchpoint: >+ case VarInjectionWatchpoint: >+ node->setCanExit(true); >+ break; >+ >+ case PutGlobalVar: >+ break; >+ >+ case CheckHasInstance: >+ node->setCanExit(true); >+ // Sadly, we don't propagate the fact that we've done CheckHasInstance >+ break; >+ >+ case InstanceOf: >+ node->setCanExit(true); >+ // Again, sadly, we don't propagate the fact that we've done InstanceOf >+ forNode(node).setType(SpecBoolean); >+ break; >+ >+ case Phi: >+ RELEASE_ASSERT(m_graph.m_form == SSA); >+ // The state of this node would have already been decided. >+ break; >+ >+ case Upsilon: { >+ AbstractValue& value = forNode(node->child1()); >+ forNode(node) = value; >+ forNode(node->phi()) = value; >+ break; >+ } >+ >+ case Flush: >+ case PhantomLocal: >+ case Breakpoint: >+ break; >+ >+ case Call: >+ case Construct: >+ node->setCanExit(true); >+ clobberWorld(node->codeOrigin, indexInBlock); >+ forNode(node).makeTop(); >+ break; >+ >+ case ForceOSRExit: >+ case ForwardForceOSRExit: >+ node->setCanExit(true); >+ m_state.setIsValid(false); >+ break; >+ >+ case CheckWatchdogTimer: >+ node->setCanExit(true); >+ break; >+ >+ case Phantom: >+ case InlineStart: >+ case CountExecution: >+ break; >+ >+ case LastNodeType: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ >+ return m_state.isValid(); >+} >+ >+template<typename AbstractStateType> >+bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned indexInBlock) >+{ >+ return executeEffects(indexInBlock, m_state.block()->at(indexInBlock)); >+} >+ >+template<typename AbstractStateType> >+bool AbstractInterpreter<AbstractStateType>::execute(unsigned indexInBlock) >+{ >+ Node* node = m_state.block()->at(indexInBlock); >+ if (!startExecuting(node)) >+ return true; >+ >+ executeEdges(node); >+ return executeEffects(indexInBlock, node); >+} >+ >+template<typename AbstractStateType> >+void AbstractInterpreter<AbstractStateType>::clobberWorld( >+ const CodeOrigin& codeOrigin, unsigned indexInBlock) >+{ >+ clobberCapturedVars(codeOrigin); >+ clobberStructures(indexInBlock); >+} >+ >+template<typename AbstractStateType> >+void AbstractInterpreter<AbstractStateType>::clobberCapturedVars(const CodeOrigin& codeOrigin) >+{ >+ if (codeOrigin.inlineCallFrame) { >+ const BitVector& capturedVars = codeOrigin.inlineCallFrame->capturedVars; >+ for (size_t i = capturedVars.size(); i--;) { >+ if (!capturedVars.quickGet(i)) >+ continue; >+ m_state.variables().local(i).makeTop(); >+ } >+ } else { >+ for (size_t i = m_codeBlock->m_numVars; i--;) { >+ if (m_codeBlock->isCaptured(i)) >+ m_state.variables().local(i).makeTop(); >+ } >+ } >+ >+ for (size_t i = m_state.variables().numberOfArguments(); i--;) { >+ if (m_codeBlock->isCaptured(argumentToOperand(i))) >+ m_state.variables().argument(i).makeTop(); >+ } >+} >+ >+template<typename AbstractStateType> >+void AbstractInterpreter<AbstractStateType>::clobberStructures(unsigned indexInBlock) >+{ >+ if (!m_state.haveStructures()) >+ return; >+ for (size_t i = indexInBlock + 1; i--;) >+ forNode(m_state.block()->at(i)).clobberStructures(); >+ 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(); >+ } >+ for (size_t i = m_state.variables().numberOfArguments(); i--;) >+ m_state.variables().argument(i).clobberStructures(); >+ for (size_t i = m_state.variables().numberOfLocals(); i--;) >+ m_state.variables().local(i).clobberStructures(); >+ m_state.setHaveStructures(true); >+ m_state.setDidClobber(true); >+} >+ >+template<typename AbstractStateType> >+void AbstractInterpreter<AbstractStateType>::dump(PrintStream& out) >+{ >+ CommaPrinter comma(" "); >+ 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) { >+ Node* node = *iter; >+ AbstractValue& value = forNode(node); >+ if (value.isClear()) >+ continue; >+ out.print(comma, node, ":", value); >+ } >+ } >+ for (size_t i = 0; i < m_state.block()->size(); ++i) { >+ Node* node = m_state.block()->at(i); >+ AbstractValue& value = forNode(node); >+ if (value.isClear()) >+ continue; >+ out.print(comma, node, ":", value); >+ } >+} >+ >+template<typename AbstractStateType> >+FiltrationResult AbstractInterpreter<AbstractStateType>::filter( >+ AbstractValue& value, const StructureSet& set) >+{ >+ if (value.filter(m_graph, set) == FiltrationOK) >+ return FiltrationOK; >+ m_state.setIsValid(false); >+ return Contradiction; >+} >+ >+template<typename AbstractStateType> >+FiltrationResult AbstractInterpreter<AbstractStateType>::filterArrayModes( >+ AbstractValue& value, ArrayModes arrayModes) >+{ >+ if (value.filterArrayModes(arrayModes) == FiltrationOK) >+ return FiltrationOK; >+ m_state.setIsValid(false); >+ return Contradiction; >+} >+ >+template<typename AbstractStateType> >+FiltrationResult AbstractInterpreter<AbstractStateType>::filter( >+ AbstractValue& value, SpeculatedType type) >+{ >+ if (value.filter(type) == FiltrationOK) >+ return FiltrationOK; >+ m_state.setIsValid(false); >+ return Contradiction; >+} >+ >+template<typename AbstractStateType> >+FiltrationResult AbstractInterpreter<AbstractStateType>::filterByValue( >+ AbstractValue& abstractValue, JSValue concreteValue) >+{ >+ if (abstractValue.filterByValue(concreteValue) == FiltrationOK) >+ return FiltrationOK; >+ m_state.setIsValid(false); >+ return Contradiction; >+} >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >+ >+#endif // DFGAbstractInterpreterInlines_h >+ >Index: Source/JavaScriptCore/dfg/DFGAbstractState.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractState.cpp (revision 152828) >+++ Source/JavaScriptCore/dfg/DFGAbstractState.cpp (working copy) >@@ -1,2013 +0,0 @@ >-/* >- * Copyright (C) 2011, 2012, 2013 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 "DFGAbstractState.h" >- >-#if ENABLE(DFG_JIT) >- >-#include "CodeBlock.h" >-#include "DFGBasicBlock.h" >-#include "GetByIdStatus.h" >-#include "Operations.h" >-#include "PutByIdStatus.h" >-#include "StringObject.h" >- >-namespace JSC { namespace DFG { >- >-AbstractState::AbstractState(Graph& graph) >- : m_codeBlock(graph.m_codeBlock) >- , m_graph(graph) >- , m_variables(m_codeBlock->numParameters(), graph.m_localVars) >- , m_block(0) >- , m_currentNode(0) >-{ >-} >- >-AbstractState::~AbstractState() { } >- >-void AbstractState::beginBasicBlock(BasicBlock* basicBlock) >-{ >- ASSERT(!m_block); >- >- ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->valuesAtHead.numberOfLocals()); >- ASSERT(basicBlock->variablesAtTail.numberOfLocals() == basicBlock->valuesAtTail.numberOfLocals()); >- ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->variablesAtTail.numberOfLocals()); >- >- for (size_t i = 0; i < basicBlock->size(); i++) >- forNode(basicBlock->at(i)).clear(); >- >- m_variables = basicBlock->valuesAtHead; >- m_haveStructures = false; >- for (size_t i = 0; i < m_variables.numberOfArguments(); ++i) { >- if (m_variables.argument(i).hasClobberableState()) { >- m_haveStructures = true; >- break; >- } >- } >- for (size_t i = 0; i < m_variables.numberOfLocals(); ++i) { >- if (m_variables.local(i).hasClobberableState()) { >- m_haveStructures = true; >- break; >- } >- } >- >- if (m_graph.m_form == SSA) { >- HashMap<Node*, AbstractValue>::iterator iter = basicBlock->ssa->valuesAtHead.begin(); >- HashMap<Node*, AbstractValue>::iterator end = basicBlock->ssa->valuesAtHead.end(); >- for (; iter != end; ++iter) { >- forNode(iter->key) = iter->value; >- if (iter->value.hasClobberableState()) >- m_haveStructures = true; >- } >- } >- >- basicBlock->cfaShouldRevisit = false; >- basicBlock->cfaHasVisited = true; >- m_block = basicBlock; >- m_isValid = true; >- m_foundConstants = false; >- m_branchDirection = InvalidBranchDirection; >- m_currentNode = 0; >-} >- >-static void setLiveValues(HashMap<Node*, AbstractValue>& values, HashSet<Node*>& live) >-{ >- values.clear(); >- >- HashSet<Node*>::iterator iter = live.begin(); >- HashSet<Node*>::iterator end = live.end(); >- for (; iter != end; ++iter) >- values.add(*iter, AbstractValue()); >-} >- >-void AbstractState::initialize() >-{ >- BasicBlock* root = m_graph.block(0); >- root->cfaShouldRevisit = true; >- root->cfaHasVisited = false; >- root->cfaFoundConstants = false; >- for (size_t i = 0; i < root->valuesAtHead.numberOfArguments(); ++i) { >- if (m_graph.m_form == SSA) { >- root->valuesAtHead.argument(i).makeTop(); >- continue; >- } >- >- Node* node = root->variablesAtHead.argument(i); >- ASSERT(node->op() == SetArgument); >- if (!node->variableAccessData()->shouldUnboxIfPossible()) { >- root->valuesAtHead.argument(i).makeTop(); >- continue; >- } >- >- SpeculatedType prediction = >- node->variableAccessData()->argumentAwarePrediction(); >- if (isInt32Speculation(prediction)) >- root->valuesAtHead.argument(i).setType(SpecInt32); >- else if (isBooleanSpeculation(prediction)) >- root->valuesAtHead.argument(i).setType(SpecBoolean); >- else if (isCellSpeculation(prediction)) >- root->valuesAtHead.argument(i).setType(SpecCell); >- else >- root->valuesAtHead.argument(i).makeTop(); >- >- root->valuesAtTail.argument(i).clear(); >- } >- for (size_t i = 0; i < root->valuesAtHead.numberOfLocals(); ++i) { >- Node* node = root->variablesAtHead.local(i); >- if (node && node->variableAccessData()->isCaptured()) >- root->valuesAtHead.local(i).makeTop(); >- else >- root->valuesAtHead.local(i).clear(); >- root->valuesAtTail.local(i).clear(); >- } >- for (BlockIndex blockIndex = 1 ; blockIndex < m_graph.numBlocks(); ++blockIndex) { >- BasicBlock* block = m_graph.block(blockIndex); >- if (!block) >- continue; >- ASSERT(block->isReachable); >- block->cfaShouldRevisit = false; >- block->cfaHasVisited = false; >- block->cfaFoundConstants = false; >- for (size_t i = 0; i < block->valuesAtHead.numberOfArguments(); ++i) { >- block->valuesAtHead.argument(i).clear(); >- block->valuesAtTail.argument(i).clear(); >- } >- for (size_t i = 0; i < block->valuesAtHead.numberOfLocals(); ++i) { >- block->valuesAtHead.local(i).clear(); >- block->valuesAtTail.local(i).clear(); >- } >- if (!block->isOSRTarget) >- continue; >- if (block->bytecodeBegin != m_graph.m_plan.osrEntryBytecodeIndex) >- continue; >- for (size_t i = 0; i < m_graph.m_plan.mustHandleValues.size(); ++i) { >- AbstractValue value; >- value.setMostSpecific(m_graph, m_graph.m_plan.mustHandleValues[i]); >- int operand = m_graph.m_plan.mustHandleValues.operandForIndex(i); >- block->valuesAtHead.operand(operand).merge(value); >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLogF(" Initializing Block #%u, operand r%d, to ", blockIndex, operand); >- block->valuesAtHead.operand(operand).dump(WTF::dataFile()); >- dataLogF("\n"); >-#endif >- } >- block->cfaShouldRevisit = true; >- } >- if (m_graph.m_form == SSA) { >- for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) { >- BasicBlock* block = m_graph.block(blockIndex); >- if (!block) >- continue; >- setLiveValues(block->ssa->valuesAtHead, block->ssa->liveAtHead); >- setLiveValues(block->ssa->valuesAtTail, block->ssa->liveAtTail); >- } >- } >-} >- >-bool AbstractState::endBasicBlock(MergeMode mergeMode) >-{ >- ASSERT(m_block); >- >- BasicBlock* block = m_block; // Save the block for successor merging. >- >- block->cfaFoundConstants = m_foundConstants; >- block->cfaDidFinish = m_isValid; >- block->cfaBranchDirection = m_branchDirection; >- >- if (!m_isValid) { >- reset(); >- return false; >- } >- >- bool changed = false; >- >- if (mergeMode != DontMerge || !ASSERT_DISABLED) { >- switch (m_graph.m_form) { >- case ThreadedCPS: { >- for (size_t argument = 0; argument < block->variablesAtTail.numberOfArguments(); ++argument) { >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLogF(" Merging state for argument %zu.\n", argument); >-#endif >- AbstractValue& destination = block->valuesAtTail.argument(argument); >- changed |= mergeStateAtTail(destination, m_variables.argument(argument), block->variablesAtTail.argument(argument)); >- } >- >- for (size_t local = 0; local < block->variablesAtTail.numberOfLocals(); ++local) { >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLogF(" Merging state for local %zu.\n", local); >-#endif >- AbstractValue& destination = block->valuesAtTail.local(local); >- changed |= mergeStateAtTail(destination, m_variables.local(local), block->variablesAtTail.local(local)); >- } >- break; >- } >- >- case SSA: { >- for (size_t i = 0; i < block->valuesAtTail.size(); ++i) >- changed |= block->valuesAtTail[i].merge(m_variables[i]); >- >- HashSet<Node*>::iterator iter = block->ssa->liveAtTail.begin(); >- HashSet<Node*>::iterator end = block->ssa->liveAtTail.end(); >- for (; iter != end; ++iter) { >- Node* node = *iter; >- changed |= block->ssa->valuesAtTail.find(node)->value.merge(forNode(node)); >- } >- break; >- } >- >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- } >- } >- >- ASSERT(mergeMode != DontMerge || !changed); >- >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLogF(" Branch direction = %s\n", branchDirectionToString(m_branchDirection)); >-#endif >- >- reset(); >- >- if (mergeMode != MergeToSuccessors) >- return changed; >- >- return mergeToSuccessors(block); >-} >- >-void AbstractState::reset() >-{ >- m_block = 0; >- m_isValid = false; >- m_branchDirection = InvalidBranchDirection; >- m_currentNode = 0; >-} >- >-AbstractState::BooleanResult AbstractState::booleanResult(Node* node, AbstractValue& value) >-{ >- JSValue childConst = value.value(); >- if (childConst) { >- if (childConst.toBoolean(m_codeBlock->globalObjectFor(node->codeOrigin)->globalExec())) >- return DefinitelyTrue; >- return DefinitelyFalse; >- } >- >- // 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->codeOrigin)) >- && structure->typeInfo().type() != StringType) >- return DefinitelyTrue; >- } >- >- return UnknownBooleanResult; >-} >- >-bool AbstractState::startExecuting(Node* node) >-{ >- ASSERT(m_block); >- ASSERT(m_isValid); >- >- m_didClobber = false; >- >- node->setCanExit(false); >- >- if (!node->shouldGenerate()) { >- m_currentNode = 0; >- return false; >- } >- >- m_currentNode = node; >- return true; >-} >- >-bool AbstractState::startExecuting(unsigned indexInBlock) >-{ >- return startExecuting(m_block->at(indexInBlock)); >-} >- >-void AbstractState::executeEdges(Node* node) >-{ >- DFG_NODE_DO_TO_CHILDREN(m_graph, node, filterEdgeByUse); >-} >- >-void AbstractState::executeEdges(unsigned indexInBlock) >-{ >- executeEdges(m_block->at(indexInBlock)); >-} >- >-void AbstractState::verifyEdge(Node*, Edge edge) >-{ >- RELEASE_ASSERT(!(forNode(edge).m_type & ~typeFilterFor(edge.useKind()))); >-} >- >-void AbstractState::verifyEdges(Node* node) >-{ >- DFG_NODE_DO_TO_CHILDREN(m_graph, node, verifyEdge); >-} >- >-bool AbstractState::executeEffects(unsigned indexInBlock, Node* node) >-{ >- if (!ASSERT_DISABLED) >- verifyEdges(node); >- >- switch (node->op()) { >- case JSConstant: >- case WeakJSConstant: >- case PhantomArguments: { >- forNode(node).set(m_graph, m_graph.valueOfJSConstant(node)); >- break; >- } >- >- case Identity: { >- forNode(node) = forNode(node->child1()); >- break; >- } >- >- case GetArgument: { >- ASSERT(m_graph.m_form == SSA); >- VariableAccessData* variable = node->variableAccessData(); >- AbstractValue& value = m_variables.operand(variable->local()); >- ASSERT(value.isTop()); >- FiltrationResult result = >- value.filter(typeFilterFor(useKindFor(variable->flushFormat()))); >- ASSERT_UNUSED(result, result == FiltrationOK); >- forNode(node) = value; >- break; >- } >- >- case GetLocal: { >- VariableAccessData* variableAccessData = node->variableAccessData(); >- if (variableAccessData->prediction() == SpecNone) { >- m_isValid = false; >- break; >- } >- AbstractValue value = m_variables.operand(variableAccessData->local()); >- if (!variableAccessData->isCaptured()) { >- if (value.isClear()) >- node->setCanExit(true); >- } >- if (value.value()) >- m_foundConstants = true; >- forNode(node) = value; >- break; >- } >- >- case GetLocalUnlinked: { >- AbstractValue value = m_variables.operand(node->unlinkedLocal()); >- if (value.value()) >- m_foundConstants = true; >- forNode(node) = value; >- break; >- } >- >- case SetLocal: { >- m_variables.operand(node->local()) = forNode(node->child1()); >- break; >- } >- >- case MovHint: >- case MovHintAndCheck: { >- // Don't need to do anything. A MovHint is effectively a promise that the SetLocal >- // was dead. >- break; >- } >- >- case ZombieHint: { >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- >- case SetArgument: >- // Assert that the state of arguments has been set. >- ASSERT(!m_block->valuesAtHead.operand(node->local()).isClear()); >- break; >- >- case BitAnd: >- case BitOr: >- case BitXor: >- case BitRShift: >- case BitLShift: >- case BitURShift: { >- JSValue left = forNode(node->child1()).value(); >- JSValue right = forNode(node->child2()).value(); >- if (left && right && left.isInt32() && right.isInt32()) { >- int32_t a = left.asInt32(); >- int32_t b = right.asInt32(); >- bool constantWasSet; >- switch (node->op()) { >- case BitAnd: >- constantWasSet = trySetConstant(node, JSValue(a & b)); >- break; >- case BitOr: >- constantWasSet = trySetConstant(node, JSValue(a | b)); >- break; >- case BitXor: >- constantWasSet = trySetConstant(node, JSValue(a ^ b)); >- break; >- case BitRShift: >- constantWasSet = trySetConstant(node, JSValue(a >> static_cast<uint32_t>(b))); >- break; >- case BitLShift: >- constantWasSet = trySetConstant(node, JSValue(a << static_cast<uint32_t>(b))); >- break; >- case BitURShift: >- constantWasSet = trySetConstant(node, JSValue(static_cast<uint32_t>(a) >> static_cast<uint32_t>(b))); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- constantWasSet = false; >- } >- if (constantWasSet) { >- m_foundConstants = true; >- break; >- } >- } >- forNode(node).setType(SpecInt32); >- break; >- } >- >- case UInt32ToNumber: { >- JSValue child = forNode(node->child1()).value(); >- if (child && child.isNumber()) { >- ASSERT(child.isInt32()); >- if (trySetConstant(node, JSValue(child.asUInt32()))) { >- m_foundConstants = true; >- break; >- } >- } >- if (!node->canSpeculateInteger()) >- forNode(node).setType(SpecDouble); >- else { >- forNode(node).setType(SpecInt32); >- node->setCanExit(true); >- } >- break; >- } >- >- case DoubleAsInt32: { >- JSValue child = forNode(node->child1()).value(); >- if (child && child.isNumber()) { >- double asDouble = child.asNumber(); >- int32_t asInt = JSC::toInt32(asDouble); >- if (bitwise_cast<int64_t>(static_cast<double>(asInt)) == bitwise_cast<int64_t>(asDouble) >- && trySetConstant(node, JSValue(asInt))) { >- m_foundConstants = true; >- break; >- } >- } >- node->setCanExit(true); >- forNode(node).setType(SpecInt32); >- break; >- } >- >- case ValueToInt32: { >- JSValue child = forNode(node->child1()).value(); >- if (child && child.isNumber()) { >- bool constantWasSet; >- if (child.isInt32()) >- constantWasSet = trySetConstant(node, child); >- else >- constantWasSet = trySetConstant(node, JSValue(JSC::toInt32(child.asDouble()))); >- if (constantWasSet) { >- m_foundConstants = true; >- break; >- } >- } >- >- forNode(node).setType(SpecInt32); >- break; >- } >- >- case Int32ToDouble: >- case ForwardInt32ToDouble: { >- JSValue child = forNode(node->child1()).value(); >- if (child && child.isNumber() >- && trySetConstant(node, JSValue(JSValue::EncodeAsDouble, child.asNumber()))) { >- m_foundConstants = true; >- break; >- } >- if (isInt32Speculation(forNode(node->child1()).m_type)) >- forNode(node).setType(SpecDoubleReal); >- else >- forNode(node).setType(SpecDouble); >- break; >- } >- >- case ValueAdd: >- case ArithAdd: { >- JSValue left = forNode(node->child1()).value(); >- JSValue right = forNode(node->child2()).value(); >- if (left && right && left.isNumber() && right.isNumber() >- && trySetConstant(node, JSValue(left.asNumber() + right.asNumber()))) { >- m_foundConstants = true; >- break; >- } >- switch (node->binaryUseKind()) { >- case Int32Use: >- forNode(node).setType(SpecInt32); >- if (!nodeCanTruncateInteger(node->arithNodeFlags())) >- node->setCanExit(true); >- break; >- case NumberUse: >- if (isRealNumberSpeculation(forNode(node->child1()).m_type) >- && isRealNumberSpeculation(forNode(node->child2()).m_type)) >- forNode(node).setType(SpecDoubleReal); >- else >- forNode(node).setType(SpecDouble); >- break; >- default: >- RELEASE_ASSERT(node->op() == ValueAdd); >- clobberWorld(node->codeOrigin, indexInBlock); >- forNode(node).setType(SpecString | SpecInt32 | SpecNumber); >- break; >- } >- break; >- } >- >- case MakeRope: { >- forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); >- break; >- } >- >- case ArithSub: { >- JSValue left = forNode(node->child1()).value(); >- JSValue right = forNode(node->child2()).value(); >- if (left && right && left.isNumber() && right.isNumber() >- && trySetConstant(node, JSValue(left.asNumber() - right.asNumber()))) { >- m_foundConstants = true; >- break; >- } >- switch (node->binaryUseKind()) { >- case Int32Use: >- forNode(node).setType(SpecInt32); >- if (!nodeCanTruncateInteger(node->arithNodeFlags())) >- node->setCanExit(true); >- break; >- case NumberUse: >- forNode(node).setType(SpecDouble); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- break; >- } >- >- case ArithNegate: { >- JSValue child = forNode(node->child1()).value(); >- if (child && child.isNumber() >- && trySetConstant(node, JSValue(-child.asNumber()))) { >- m_foundConstants = true; >- break; >- } >- switch (node->child1().useKind()) { >- case Int32Use: >- forNode(node).setType(SpecInt32); >- if (!nodeCanTruncateInteger(node->arithNodeFlags())) >- node->setCanExit(true); >- break; >- case NumberUse: >- forNode(node).setType(SpecDouble); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- break; >- } >- >- case ArithMul: { >- JSValue left = forNode(node->child1()).value(); >- JSValue right = forNode(node->child2()).value(); >- if (left && right && left.isNumber() && right.isNumber() >- && trySetConstant(node, JSValue(left.asNumber() * right.asNumber()))) { >- m_foundConstants = true; >- break; >- } >- switch (node->binaryUseKind()) { >- case Int32Use: >- forNode(node).setType(SpecInt32); >- if (!nodeCanTruncateInteger(node->arithNodeFlags()) >- || !nodeCanIgnoreNegativeZero(node->arithNodeFlags())) >- node->setCanExit(true); >- break; >- case NumberUse: >- if (isRealNumberSpeculation(forNode(node->child1()).m_type) >- || isRealNumberSpeculation(forNode(node->child2()).m_type)) >- forNode(node).setType(SpecDoubleReal); >- else >- forNode(node).setType(SpecDouble); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- break; >- } >- >- case ArithDiv: >- case ArithMin: >- case ArithMax: >- case ArithMod: { >- JSValue left = forNode(node->child1()).value(); >- JSValue right = forNode(node->child2()).value(); >- if (node->op() == ArithMod && right && right.isNumber() && right.asNumber() == 1 >- && trySetConstant(node, JSValue(0))) { >- m_foundConstants = true; >- break; >- } >- if (left && right && left.isNumber() && right.isNumber()) { >- double a = left.asNumber(); >- double b = right.asNumber(); >- bool constantWasSet; >- switch (node->op()) { >- case ArithDiv: >- constantWasSet = trySetConstant(node, JSValue(a / b)); >- break; >- case ArithMin: >- constantWasSet = trySetConstant(node, JSValue(a < b ? a : (b <= a ? b : a + b))); >- break; >- case ArithMax: >- constantWasSet = trySetConstant(node, JSValue(a > b ? a : (b >= a ? b : a + b))); >- break; >- case ArithMod: >- constantWasSet = trySetConstant(node, JSValue(fmod(a, b))); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- constantWasSet = false; >- break; >- } >- if (constantWasSet) { >- m_foundConstants = true; >- break; >- } >- } >- switch (node->binaryUseKind()) { >- case Int32Use: >- forNode(node).setType(SpecInt32); >- node->setCanExit(true); >- break; >- case NumberUse: >- forNode(node).setType(SpecDouble); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- break; >- } >- >- case ArithAbs: { >- JSValue child = forNode(node->child1()).value(); >- if (child && child.isNumber() >- && trySetConstant(node, JSValue(fabs(child.asNumber())))) { >- m_foundConstants = true; >- break; >- } >- switch (node->child1().useKind()) { >- case Int32Use: >- forNode(node).setType(SpecInt32); >- node->setCanExit(true); >- break; >- case NumberUse: >- forNode(node).setType(SpecDouble); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- break; >- } >- >- case ArithSqrt: { >- JSValue child = forNode(node->child1()).value(); >- if (child && child.isNumber() >- && trySetConstant(node, JSValue(sqrt(child.asNumber())))) { >- m_foundConstants = true; >- break; >- } >- forNode(node).setType(SpecDouble); >- break; >- } >- >- case LogicalNot: { >- bool didSetConstant = false; >- switch (booleanResult(node, forNode(node->child1()))) { >- case DefinitelyTrue: >- didSetConstant = trySetConstant(node, jsBoolean(false)); >- break; >- case DefinitelyFalse: >- didSetConstant = trySetConstant(node, jsBoolean(true)); >- break; >- default: >- break; >- } >- if (didSetConstant) { >- m_foundConstants = true; >- break; >- } >- switch (node->child1().useKind()) { >- case BooleanUse: >- case Int32Use: >- case NumberUse: >- case UntypedUse: >- break; >- case ObjectOrOtherUse: >- node->setCanExit(true); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- forNode(node).setType(SpecBoolean); >- break; >- } >- >- case IsUndefined: >- case IsBoolean: >- case IsNumber: >- case IsString: >- case IsObject: >- case IsFunction: { >- node->setCanExit( >- node->op() == IsUndefined >- && m_graph.masqueradesAsUndefinedWatchpointIsStillValid(node->codeOrigin)); >- JSValue child = forNode(node->child1()).value(); >- if (child) { >- bool constantWasSet; >- switch (node->op()) { >- case IsUndefined: >- constantWasSet = trySetConstant(node, jsBoolean( >- child.isCell() >- ? child.asCell()->structure()->masqueradesAsUndefined(m_codeBlock->globalObjectFor(node->codeOrigin)) >- : child.isUndefined())); >- break; >- case IsBoolean: >- constantWasSet = trySetConstant(node, jsBoolean(child.isBoolean())); >- break; >- case IsNumber: >- constantWasSet = trySetConstant(node, jsBoolean(child.isNumber())); >- break; >- case IsString: >- constantWasSet = trySetConstant(node, jsBoolean(isJSString(child))); >- break; >- case IsObject: >- if (child.isNull() || !child.isObject()) { >- constantWasSet = trySetConstant(node, jsBoolean(child.isNull())); >- break; >- } >- default: >- constantWasSet = false; >- break; >- } >- if (constantWasSet) { >- m_foundConstants = true; >- break; >- } >- } >- >- forNode(node).setType(SpecBoolean); >- break; >- } >- >- case TypeOf: { >- VM* vm = m_codeBlock->vm(); >- JSValue child = forNode(node->child1()).value(); >- AbstractValue& abstractChild = forNode(node->child1()); >- if (child) { >- JSValue typeString = jsTypeStringForValue(*vm, m_codeBlock->globalObjectFor(node->codeOrigin), child); >- if (trySetConstant(node, typeString)) { >- m_foundConstants = true; >- break; >- } >- } else if (isNumberSpeculation(abstractChild.m_type)) { >- if (trySetConstant(node, vm->smallStrings.numberString())) { >- filter(node->child1(), SpecNumber); >- m_foundConstants = true; >- break; >- } >- } else if (isStringSpeculation(abstractChild.m_type)) { >- if (trySetConstant(node, vm->smallStrings.stringString())) { >- filter(node->child1(), SpecString); >- m_foundConstants = true; >- break; >- } >- } else if (isFinalObjectSpeculation(abstractChild.m_type) || isArraySpeculation(abstractChild.m_type) || isArgumentsSpeculation(abstractChild.m_type)) { >- if (trySetConstant(node, vm->smallStrings.objectString())) { >- filter(node->child1(), SpecFinalObject | SpecArray | SpecArguments); >- m_foundConstants = true; >- break; >- } >- } else if (isFunctionSpeculation(abstractChild.m_type)) { >- if (trySetConstant(node, vm->smallStrings.functionString())) { >- filter(node->child1(), SpecFunction); >- m_foundConstants = true; >- break; >- } >- } else if (isBooleanSpeculation(abstractChild.m_type)) { >- if (trySetConstant(node, vm->smallStrings.booleanString())) { >- filter(node->child1(), SpecBoolean); >- m_foundConstants = true; >- break; >- } >- } >- >- switch (node->child1().useKind()) { >- case StringUse: >- case CellUse: >- node->setCanExit(true); >- break; >- case UntypedUse: >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); >- break; >- } >- >- case CompareLess: >- case CompareLessEq: >- case CompareGreater: >- case CompareGreaterEq: >- case CompareEq: >- case CompareEqConstant: { >- bool constantWasSet = false; >- >- JSValue leftConst = forNode(node->child1()).value(); >- JSValue rightConst = forNode(node->child2()).value(); >- if (leftConst && rightConst) { >- if (leftConst.isNumber() && rightConst.isNumber()) { >- double a = leftConst.asNumber(); >- double b = rightConst.asNumber(); >- switch (node->op()) { >- case CompareLess: >- constantWasSet = trySetConstant(node, jsBoolean(a < b)); >- break; >- case CompareLessEq: >- constantWasSet = trySetConstant(node, jsBoolean(a <= b)); >- break; >- case CompareGreater: >- constantWasSet = trySetConstant(node, jsBoolean(a > b)); >- break; >- case CompareGreaterEq: >- constantWasSet = trySetConstant(node, jsBoolean(a >= b)); >- break; >- case CompareEq: >- constantWasSet = trySetConstant(node, jsBoolean(a == b)); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- constantWasSet = false; >- break; >- } >- } >- >- if (!constantWasSet && node->op() == CompareEq >- && leftConst.isString() && rightConst.isString()) { >- const StringImpl* a = asString(leftConst)->tryGetValueImpl(); >- const StringImpl* b = asString(rightConst)->tryGetValueImpl(); >- if (a && b) >- constantWasSet = trySetConstant(node, jsBoolean(WTF::equal(a, b))); >- } >- } >- >- if (!constantWasSet && (node->op() == CompareEqConstant || node->op() == CompareEq)) { >- SpeculatedType leftType = forNode(node->child1()).m_type; >- SpeculatedType rightType = forNode(node->child2()).m_type; >- if ((isInt32Speculation(leftType) && isOtherSpeculation(rightType)) >- || (isOtherSpeculation(leftType) && isInt32Speculation(rightType))) >- constantWasSet = trySetConstant(node, jsBoolean(false)); >- } >- >- if (constantWasSet) { >- m_foundConstants = true; >- break; >- } >- >- forNode(node).setType(SpecBoolean); >- >- // This is overly conservative. But the only thing this prevents is store elimination, >- // and how likely is it, really, that you'll have redundant stores across a comparison >- // operation? Comparison operations are typically at the end of basic blocks, so >- // unless we have global store elimination (super unlikely given how unprofitable that >- // optimization is to begin with), you aren't going to be wanting to store eliminate >- // across an equality op. >- node->setCanExit(true); >- break; >- } >- >- case CompareStrictEq: >- case CompareStrictEqConstant: { >- Node* leftNode = node->child1().node(); >- Node* rightNode = node->child2().node(); >- JSValue left = forNode(leftNode).value(); >- JSValue right = forNode(rightNode).value(); >- if (left && right) { >- if (left.isNumber() && right.isNumber() >- && trySetConstant(node, jsBoolean(left.asNumber() == right.asNumber()))) { >- m_foundConstants = true; >- break; >- } >- if (left.isString() && right.isString()) { >- const StringImpl* a = asString(left)->tryGetValueImpl(); >- const StringImpl* b = asString(right)->tryGetValueImpl(); >- if (a && b && trySetConstant(node, jsBoolean(WTF::equal(a, b)))) { >- m_foundConstants = true; >- break; >- } >- } >- } >- forNode(node).setType(SpecBoolean); >- node->setCanExit(true); // This is overly conservative. >- break; >- } >- >- case StringCharCodeAt: >- node->setCanExit(true); >- forNode(node).setType(SpecInt32); >- break; >- >- case StringCharAt: >- node->setCanExit(true); >- forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); >- break; >- >- case GetByVal: { >- node->setCanExit(true); >- switch (node->arrayMode().type()) { >- case Array::SelectUsingPredictions: >- case Array::Unprofiled: >- case Array::Undecided: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- case Array::ForceExit: >- m_isValid = false; >- break; >- case Array::Generic: >- clobberWorld(node->codeOrigin, indexInBlock); >- forNode(node).makeTop(); >- break; >- case Array::String: >- if (node->arrayMode().isOutOfBounds()) { >- // If the watchpoint was still valid we could totally set this to be >- // SpecString | SpecOther. Except that we'd have to be careful. If we >- // tested the watchpoint state here then it could change by the time >- // we got to the backend. So to do this right, we'd have to get the >- // fixup phase to check the watchpoint state and then bake into the >- // GetByVal operation the fact that we're using a watchpoint, using >- // something like Array::SaneChain (except not quite, because that >- // implies an in-bounds access). None of this feels like it's worth it, >- // so we're going with TOP for now. >- forNode(node).makeTop(); >- } else >- forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); >- break; >- case Array::Arguments: >- forNode(node).makeTop(); >- break; >- case Array::Int32: >- if (node->arrayMode().isOutOfBounds()) { >- clobberWorld(node->codeOrigin, indexInBlock); >- forNode(node).makeTop(); >- } else >- forNode(node).setType(SpecInt32); >- break; >- case Array::Double: >- if (node->arrayMode().isOutOfBounds()) { >- clobberWorld(node->codeOrigin, indexInBlock); >- forNode(node).makeTop(); >- } else if (node->arrayMode().isSaneChain()) >- forNode(node).setType(SpecDouble); >- else >- forNode(node).setType(SpecDoubleReal); >- break; >- case Array::Contiguous: >- case Array::ArrayStorage: >- case Array::SlowPutArrayStorage: >- if (node->arrayMode().isOutOfBounds()) >- clobberWorld(node->codeOrigin, indexInBlock); >- forNode(node).makeTop(); >- break; >- case Array::Int8Array: >- forNode(node).setType(SpecInt32); >- break; >- case Array::Int16Array: >- forNode(node).setType(SpecInt32); >- break; >- case Array::Int32Array: >- forNode(node).setType(SpecInt32); >- break; >- case Array::Uint8Array: >- forNode(node).setType(SpecInt32); >- break; >- case Array::Uint8ClampedArray: >- forNode(node).setType(SpecInt32); >- break; >- case Array::Uint16Array: >- forNode(node).setType(SpecInt32); >- break; >- case Array::Uint32Array: >- if (node->shouldSpeculateInteger()) >- forNode(node).setType(SpecInt32); >- else >- forNode(node).setType(SpecDouble); >- break; >- case Array::Float32Array: >- forNode(node).setType(SpecDouble); >- break; >- case Array::Float64Array: >- forNode(node).setType(SpecDouble); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- break; >- } >- >- case PutByVal: >- case PutByValAlias: { >- node->setCanExit(true); >- switch (node->arrayMode().modeForPut().type()) { >- case Array::ForceExit: >- m_isValid = false; >- break; >- case Array::Generic: >- clobberWorld(node->codeOrigin, indexInBlock); >- break; >- case Array::Int32: >- if (node->arrayMode().isOutOfBounds()) >- clobberWorld(node->codeOrigin, indexInBlock); >- break; >- case Array::Double: >- if (node->arrayMode().isOutOfBounds()) >- clobberWorld(node->codeOrigin, indexInBlock); >- break; >- case Array::Contiguous: >- case Array::ArrayStorage: >- if (node->arrayMode().isOutOfBounds()) >- clobberWorld(node->codeOrigin, indexInBlock); >- break; >- case Array::SlowPutArrayStorage: >- if (node->arrayMode().mayStoreToHole()) >- clobberWorld(node->codeOrigin, indexInBlock); >- break; >- default: >- break; >- } >- break; >- } >- >- case ArrayPush: >- node->setCanExit(true); >- clobberWorld(node->codeOrigin, indexInBlock); >- forNode(node).setType(SpecNumber); >- break; >- >- case ArrayPop: >- node->setCanExit(true); >- clobberWorld(node->codeOrigin, indexInBlock); >- forNode(node).makeTop(); >- break; >- >- case RegExpExec: >- case RegExpTest: >- forNode(node).makeTop(); >- break; >- >- case Jump: >- break; >- >- case Branch: { >- Node* child = node->child1().node(); >- BooleanResult result = booleanResult(node, forNode(child)); >- if (result == DefinitelyTrue) { >- m_branchDirection = TakeTrue; >- break; >- } >- if (result == DefinitelyFalse) { >- m_branchDirection = TakeFalse; >- break; >- } >- // FIXME: The above handles the trivial cases of sparse conditional >- // constant propagation, but we can do better: >- // We can specialize the source variable's value on each direction of >- // the branch. >- node->setCanExit(true); // This is overly conservative. >- m_branchDirection = TakeBoth; >- break; >- } >- >- case Switch: { >- // Nothing to do for now. >- // FIXME: Do sparse conditional things. >- break; >- } >- >- case Return: >- m_isValid = false; >- break; >- >- case Throw: >- case ThrowReferenceError: >- m_isValid = false; >- node->setCanExit(true); >- break; >- >- case ToPrimitive: { >- JSValue childConst = forNode(node->child1()).value(); >- if (childConst && childConst.isNumber() && trySetConstant(node, childConst)) { >- m_foundConstants = true; >- break; >- } >- >- ASSERT(node->child1().useKind() == UntypedUse); >- >- AbstractValue& source = forNode(node->child1()); >- AbstractValue& destination = forNode(node); >- >- // NB. The more canonical way of writing this would have been: >- // >- // destination = source; >- // if (destination.m_type & !(SpecNumber | SpecString | SpecBoolean)) { >- // destination.filter(SpecNumber | SpecString | SpecBoolean); >- // AbstractValue string; >- // string.set(vm->stringStructure); >- // destination.merge(string); >- // } >- // >- // The reason why this would, in most other cases, have been better is that >- // then destination would preserve any non-SpeculatedType knowledge of source. >- // As it stands, the code below forgets any non-SpeculatedType knowledge that >- // source would have had. Fortunately, though, for things like strings and >- // numbers and booleans, we don't care about the non-SpeculatedType knowedge: >- // the structure won't tell us anything we don't already know, and neither >- // will ArrayModes. And if the source was a meaningful constant then we >- // would have handled that above. Unfortunately, this does mean that >- // ToPrimitive will currently forget string constants. But that's not a big >- // deal since we don't do any optimization on those currently. >- >- clobberWorld(node->codeOrigin, indexInBlock); >- >- SpeculatedType type = source.m_type; >- if (type & ~(SpecNumber | SpecString | SpecBoolean)) { >- type &= (SpecNumber | SpecString | SpecBoolean); >- type |= SpecString; >- } >- destination.setType(type); >- if (destination.isClear()) >- m_isValid = false; >- break; >- } >- >- case ToString: { >- switch (node->child1().useKind()) { >- case StringObjectUse: >- // This also filters that the StringObject has the primordial StringObject >- // structure. >- filter( >- node->child1(), >- m_graph.globalObjectFor(node->codeOrigin)->stringObjectStructure()); >- node->setCanExit(true); // We could be more precise but it's likely not worth it. >- break; >- case StringOrStringObjectUse: >- node->setCanExit(true); // We could be more precise but it's likely not worth it. >- break; >- case CellUse: >- case UntypedUse: >- clobberWorld(node->codeOrigin, indexInBlock); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- forNode(node).set(m_graph, m_graph.m_vm.stringStructure.get()); >- break; >- } >- >- case NewStringObject: { >- ASSERT(node->structure()->classInfo() == &StringObject::s_info); >- forNode(node).set(m_graph, node->structure()); >- break; >- } >- >- case NewArray: >- node->setCanExit(true); >- forNode(node).set( >- m_graph, >- m_graph.globalObjectFor(node->codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())); >- m_haveStructures = true; >- break; >- >- case NewArrayBuffer: >- node->setCanExit(true); >- forNode(node).set( >- m_graph, >- m_graph.globalObjectFor(node->codeOrigin)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())); >- m_haveStructures = true; >- break; >- >- case NewArrayWithSize: >- node->setCanExit(true); >- forNode(node).setType(SpecArray); >- m_haveStructures = true; >- break; >- >- case NewRegexp: >- forNode(node).set(m_graph, m_graph.globalObjectFor(node->codeOrigin)->regExpStructure()); >- m_haveStructures = true; >- break; >- >- case ToThis: { >- AbstractValue& source = forNode(node->child1()); >- AbstractValue& destination = forNode(node); >- >- destination = source; >- destination.merge(SpecObjectOther); >- break; >- } >- >- case CreateThis: { >- forNode(node).setType(SpecFinalObject); >- break; >- } >- >- case AllocationProfileWatchpoint: >- node->setCanExit(true); >- break; >- >- case NewObject: >- forNode(node).set(m_graph, node->structure()); >- m_haveStructures = true; >- break; >- >- case CreateActivation: >- forNode(node).set( >- m_graph, m_codeBlock->globalObjectFor(node->codeOrigin)->activationStructure()); >- m_haveStructures = true; >- break; >- >- case CreateArguments: >- forNode(node).set( >- m_graph, m_codeBlock->globalObjectFor(node->codeOrigin)->argumentsStructure()); >- m_haveStructures = true; >- break; >- >- case TearOffActivation: >- case TearOffArguments: >- // Does nothing that is user-visible. >- break; >- >- case CheckArgumentsNotCreated: >- if (isEmptySpeculation( >- m_variables.operand( >- m_graph.argumentsRegisterFor(node->codeOrigin)).m_type)) >- m_foundConstants = true; >- else >- node->setCanExit(true); >- break; >- >- case GetMyArgumentsLength: >- // We know that this executable does not escape its arguments, so we can optimize >- // the arguments a bit. Note that this is not sufficient to force constant folding >- // of GetMyArgumentsLength, because GetMyArgumentsLength is a clobbering operation. >- // We perform further optimizations on this later on. >- if (node->codeOrigin.inlineCallFrame) { >- forNode(node).set( >- m_graph, jsNumber(node->codeOrigin.inlineCallFrame->arguments.size() - 1)); >- } else >- forNode(node).setType(SpecInt32); >- node->setCanExit( >- !isEmptySpeculation( >- m_variables.operand( >- m_graph.argumentsRegisterFor(node->codeOrigin)).m_type)); >- break; >- >- case GetMyArgumentsLengthSafe: >- // This potentially clobbers all structures if the arguments object had a getter >- // installed on the length property. >- clobberWorld(node->codeOrigin, indexInBlock); >- // We currently make no guarantee about what this returns because it does not >- // speculate that the length property is actually a length. >- forNode(node).makeTop(); >- break; >- >- case GetMyArgumentByVal: >- node->setCanExit(true); >- // We know that this executable does not escape its arguments, so we can optimize >- // the arguments a bit. Note that this ends up being further optimized by the >- // ArgumentsSimplificationPhase. >- forNode(node).makeTop(); >- break; >- >- case GetMyArgumentByValSafe: >- node->setCanExit(true); >- // This potentially clobbers all structures if the property we're accessing has >- // a getter. We don't speculate against this. >- clobberWorld(node->codeOrigin, indexInBlock); >- // And the result is unknown. >- forNode(node).makeTop(); >- break; >- >- case NewFunction: >- case NewFunctionExpression: >- case NewFunctionNoCheck: >- forNode(node).set( >- m_graph, m_codeBlock->globalObjectFor(node->codeOrigin)->functionStructure()); >- break; >- >- case GetCallee: >- forNode(node).setType(SpecFunction); >- break; >- >- case SetCallee: >- case SetMyScope: >- break; >- >- case GetScope: // FIXME: We could get rid of these if we know that the JSFunction is a constant. https://bugs.webkit.org/show_bug.cgi?id=106202 >- case GetMyScope: >- case SkipTopScope: >- forNode(node).setType(SpecCellOther); >- break; >- >- case SkipScope: { >- JSValue child = forNode(node->child1()).value(); >- if (child && trySetConstant(node, JSValue(jsCast<JSScope*>(child.asCell())->next()))) { >- m_foundConstants = true; >- break; >- } >- forNode(node).setType(SpecCellOther); >- break; >- } >- >- case GetClosureRegisters: >- forNode(node).clear(); // The result is not a JS value. >- break; >- >- case GetClosureVar: >- forNode(node).makeTop(); >- break; >- >- case PutClosureVar: >- clobberCapturedVars(node->codeOrigin); >- break; >- >- case GetById: >- case GetByIdFlush: >- node->setCanExit(true); >- if (!node->prediction()) { >- m_isValid = false; >- break; >- } >- if (isCellSpeculation(node->child1()->prediction())) { >- if (Structure* structure = forNode(node->child1()).bestProvenStructure()) { >- GetByIdStatus status = GetByIdStatus::computeFor( >- m_graph.m_vm, structure, >- m_graph.identifiers()[node->identifierNumber()]); >- if (status.isSimple()) { >- // Assert things that we can't handle and that the computeFor() method >- // above won't be able to return. >- ASSERT(status.structureSet().size() == 1); >- ASSERT(!status.chain()); >- >- if (status.specificValue()) >- forNode(node).set(m_graph, status.specificValue()); >- else >- forNode(node).makeTop(); >- filter(node->child1(), status.structureSet()); >- >- m_foundConstants = true; >- break; >- } >- } >- } >- clobberWorld(node->codeOrigin, indexInBlock); >- forNode(node).makeTop(); >- break; >- >- case GetArrayLength: >- node->setCanExit(true); // Lies, but it's true for the common case of JSArray, so it's good enough. >- forNode(node).setType(SpecInt32); >- break; >- >- case CheckExecutable: { >- // FIXME: We could track executables in AbstractValue, which would allow us to get rid of these checks >- // more thoroughly. https://bugs.webkit.org/show_bug.cgi?id=106200 >- // FIXME: We could eliminate these entirely if we know the exact value that flows into this. >- // https://bugs.webkit.org/show_bug.cgi?id=106201 >- node->setCanExit(true); >- break; >- } >- >- case CheckStructure: >- case ForwardCheckStructure: { >- // FIXME: We should be able to propagate the structure sets of constants (i.e. prototypes). >- AbstractValue& value = forNode(node->child1()); >- ASSERT(!(value.m_type & ~SpecCell)); // Edge filtering should have already ensured this. >- >- StructureSet& set = node->structureSet(); >- >- if (value.m_currentKnownStructure.isSubsetOf(set)) { >- m_foundConstants = true; >- break; >- } >- >- node->setCanExit(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_foundConstants = true; >- filter(value, value.m_futurePossibleStructure.singleton()); >- break; >- } >- >- filter(value, set); >- m_haveStructures = true; >- break; >- } >- >- case StructureTransitionWatchpoint: >- case ForwardStructureTransitionWatchpoint: { >- AbstractValue& value = forNode(node->child1()); >- >- // It's only valid to issue a structure transition watchpoint if we already >- // know that the watchpoint covers a superset of the structures known to >- // belong to the set of future structures that this value may have. >- // Currently, we only issue singleton watchpoints (that check one structure) >- // and our futurePossibleStructure set can only contain zero, one, or an >- // infinity of structures. >- ASSERT( >- value.m_futurePossibleStructure.isSubsetOf(StructureSet(node->structure())) >- || m_graph.watchpoints().shouldAssumeMixedState(node->structure()->transitionWatchpointSet())); >- >- filter(value, node->structure()); >- m_haveStructures = true; >- node->setCanExit(true); >- break; >- } >- >- case PutStructure: >- case PhantomPutStructure: >- if (!forNode(node->child1()).m_currentKnownStructure.isClear()) { >- clobberStructures(indexInBlock); >- forNode(node->child1()).set(m_graph, node->structureTransitionData().newStructure); >- m_haveStructures = true; >- } >- break; >- case GetButterfly: >- case AllocatePropertyStorage: >- case ReallocatePropertyStorage: >- forNode(node).clear(); // The result is not a JS value. >- break; >- case ForwardCheckArray: >- case CheckArray: { >- if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) { >- m_foundConstants = true; >- break; >- } >- node->setCanExit(true); // Lies, but this is followed by operations (like GetByVal) that always exit, so there is no point in us trying to be clever here. >- switch (node->arrayMode().type()) { >- case Array::String: >- filter(node->child1(), SpecString); >- break; >- case Array::Int32: >- case Array::Double: >- case Array::Contiguous: >- case Array::ArrayStorage: >- case Array::SlowPutArrayStorage: >- break; >- case Array::Arguments: >- filter(node->child1(), SpecArguments); >- break; >- case Array::Int8Array: >- filter(node->child1(), SpecInt8Array); >- break; >- case Array::Int16Array: >- filter(node->child1(), SpecInt16Array); >- break; >- case Array::Int32Array: >- filter(node->child1(), SpecInt32Array); >- break; >- case Array::Uint8Array: >- filter(node->child1(), SpecUint8Array); >- break; >- case Array::Uint8ClampedArray: >- filter(node->child1(), SpecUint8ClampedArray); >- break; >- case Array::Uint16Array: >- filter(node->child1(), SpecUint16Array); >- break; >- case Array::Uint32Array: >- filter(node->child1(), SpecUint32Array); >- break; >- case Array::Float32Array: >- filter(node->child1(), SpecFloat32Array); >- break; >- case Array::Float64Array: >- filter(node->child1(), SpecFloat64Array); >- break; >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering()); >- m_haveStructures = true; >- break; >- } >- case Arrayify: { >- if (node->arrayMode().alreadyChecked(m_graph, node, forNode(node->child1()))) { >- m_foundConstants = true; >- break; >- } >- ASSERT(node->arrayMode().conversion() == Array::Convert >- || node->arrayMode().conversion() == Array::RageConvert); >- node->setCanExit(true); >- clobberStructures(indexInBlock); >- filterArrayModes(node->child1(), node->arrayMode().arrayModesThatPassFiltering()); >- m_haveStructures = true; >- break; >- } >- case ArrayifyToStructure: { >- AbstractValue& value = forNode(node->child1()); >- StructureSet set = node->structure(); >- if (value.m_futurePossibleStructure.isSubsetOf(set) >- || value.m_currentKnownStructure.isSubsetOf(set)) >- m_foundConstants = true; >- node->setCanExit(true); >- clobberStructures(indexInBlock); >- filter(value, set); >- m_haveStructures = true; >- break; >- } >- case GetIndexedPropertyStorage: { >- forNode(node).clear(); >- break; >- } >- case GetByOffset: { >- forNode(node).makeTop(); >- break; >- } >- >- case PutByOffset: { >- break; >- } >- >- case CheckFunction: { >- JSValue value = forNode(node->child1()).value(); >- if (value == node->function()) { >- m_foundConstants = true; >- ASSERT(value); >- break; >- } >- >- node->setCanExit(true); // Lies! We can do better. >- filterByValue(node->child1(), node->function()); >- break; >- } >- >- case PutById: >- case PutByIdDirect: >- node->setCanExit(true); >- if (Structure* structure = forNode(node->child1()).bestProvenStructure()) { >- PutByIdStatus status = PutByIdStatus::computeFor( >- m_graph.m_vm, >- m_graph.globalObjectFor(node->codeOrigin), >- structure, >- m_graph.identifiers()[node->identifierNumber()], >- node->op() == PutByIdDirect); >- if (status.isSimpleReplace()) { >- filter(node->child1(), structure); >- m_foundConstants = true; >- break; >- } >- if (status.isSimpleTransition()) { >- clobberStructures(indexInBlock); >- forNode(node->child1()).set(m_graph, status.newStructure()); >- m_haveStructures = true; >- m_foundConstants = true; >- break; >- } >- } >- clobberWorld(node->codeOrigin, indexInBlock); >- break; >- >- case In: >- // FIXME: We can determine when the property definitely exists based on abstract >- // value information. >- clobberWorld(node->codeOrigin, indexInBlock); >- forNode(node).setType(SpecBoolean); >- break; >- >- case GetGlobalVar: >- forNode(node).makeTop(); >- break; >- >- case GlobalVarWatchpoint: >- case VarInjectionWatchpoint: >- node->setCanExit(true); >- break; >- >- case PutGlobalVar: >- break; >- >- case CheckHasInstance: >- node->setCanExit(true); >- // Sadly, we don't propagate the fact that we've done CheckHasInstance >- break; >- >- case InstanceOf: >- node->setCanExit(true); >- // Again, sadly, we don't propagate the fact that we've done InstanceOf >- forNode(node).setType(SpecBoolean); >- break; >- >- case Phi: >- RELEASE_ASSERT(m_graph.m_form == SSA); >- // The state of this node would have already been decided. >- break; >- >- case Upsilon: { >- AbstractValue& value = forNode(node->child1()); >- forNode(node) = value; >- forNode(node->phi()) = value; >- break; >- } >- >- case Flush: >- case PhantomLocal: >- case Breakpoint: >- break; >- >- case Call: >- case Construct: >- node->setCanExit(true); >- clobberWorld(node->codeOrigin, indexInBlock); >- forNode(node).makeTop(); >- break; >- >- case ForceOSRExit: >- case ForwardForceOSRExit: >- node->setCanExit(true); >- m_isValid = false; >- break; >- >- case CheckWatchdogTimer: >- node->setCanExit(true); >- break; >- >- case Phantom: >- case InlineStart: >- case CountExecution: >- break; >- >- case LastNodeType: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- >- return m_isValid; >-} >- >-bool AbstractState::executeEffects(unsigned indexInBlock) >-{ >- return executeEffects(indexInBlock, m_block->at(indexInBlock)); >-} >- >-bool AbstractState::execute(unsigned indexInBlock) >-{ >- Node* node = m_block->at(indexInBlock); >- if (!startExecuting(node)) >- return true; >- >- executeEdges(node); >- return executeEffects(indexInBlock, node); >-} >- >-inline void AbstractState::clobberWorld(const CodeOrigin& codeOrigin, unsigned indexInBlock) >-{ >- clobberCapturedVars(codeOrigin); >- clobberStructures(indexInBlock); >-} >- >-inline void AbstractState::clobberCapturedVars(const CodeOrigin& codeOrigin) >-{ >- if (codeOrigin.inlineCallFrame) { >- const BitVector& capturedVars = codeOrigin.inlineCallFrame->capturedVars; >- for (size_t i = capturedVars.size(); i--;) { >- if (!capturedVars.quickGet(i)) >- continue; >- m_variables.local(i).makeTop(); >- } >- } else { >- for (size_t i = m_codeBlock->m_numVars; i--;) { >- if (m_codeBlock->isCaptured(i)) >- m_variables.local(i).makeTop(); >- } >- } >- >- for (size_t i = m_variables.numberOfArguments(); i--;) { >- if (m_codeBlock->isCaptured(argumentToOperand(i))) >- m_variables.argument(i).makeTop(); >- } >-} >- >-inline void AbstractState::clobberStructures(unsigned indexInBlock) >-{ >- if (!m_haveStructures) >- return; >- for (size_t i = indexInBlock + 1; i--;) >- forNode(m_block->at(i)).clobberStructures(); >- for (size_t i = m_variables.numberOfArguments(); i--;) >- m_variables.argument(i).clobberStructures(); >- for (size_t i = m_variables.numberOfLocals(); i--;) >- m_variables.local(i).clobberStructures(); >- m_haveStructures = false; >- m_didClobber = true; >-} >- >-bool AbstractState::mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, Node* node) >-{ >- if (!node) >- return false; >- >- AbstractValue source; >- >- if (node->variableAccessData()->isCaptured()) { >- // If it's captured then we know that whatever value was stored into the variable last is the >- // one we care about. This is true even if the variable at tail is dead, which might happen if >- // the last thing we did to the variable was a GetLocal and then ended up now using the >- // GetLocal's result. >- >- source = inVariable; >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLogF(" Transfering "); >- source.dump(WTF::dataFile()); >- dataLogF(" from last access due to captured variable.\n"); >-#endif >- } else { >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLogF(" It's live, node @%u.\n", node->index()); >-#endif >- >- switch (node->op()) { >- case Phi: >- case SetArgument: >- case PhantomLocal: >- case Flush: >- // The block transfers the value from head to tail. >- source = inVariable; >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLogF(" Transfering "); >- source.dump(WTF::dataFile()); >- dataLogF(" from head to tail.\n"); >-#endif >- break; >- >- case GetLocal: >- // The block refines the value with additional speculations. >- source = forNode(node); >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLogF(" Refining to "); >- source.dump(WTF::dataFile()); >- dataLogF("\n"); >-#endif >- break; >- >- case SetLocal: >- // The block sets the variable, and potentially refines it, both >- // before and after setting it. >- if (node->variableAccessData()->shouldUseDoubleFormat()) { >- // FIXME: This unnecessarily loses precision. >- source.setType(SpecDouble); >- } else >- source = forNode(node->child1()); >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLogF(" Setting to "); >- source.dump(WTF::dataFile()); >- dataLogF("\n"); >-#endif >- break; >- >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- } >- >- if (destination == source) { >- // Abstract execution did not change the output value of the variable, for this >- // basic block, on this iteration. >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLogF(" Not changed!\n"); >-#endif >- return false; >- } >- >- // Abstract execution reached a new conclusion about the speculations reached about >- // this variable after execution of this basic block. Update the state, and return >- // true to indicate that the fixpoint must go on! >- destination = source; >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLogF(" Changed!\n"); >-#endif >- return true; >-} >- >-bool AbstractState::merge(BasicBlock* from, BasicBlock* to) >-{ >- ASSERT(from->variablesAtTail.numberOfArguments() == to->variablesAtHead.numberOfArguments()); >- ASSERT(from->variablesAtTail.numberOfLocals() == to->variablesAtHead.numberOfLocals()); >- >- bool changed = false; >- >- switch (m_graph.m_form) { >- case ThreadedCPS: { >- for (size_t argument = 0; argument < from->variablesAtTail.numberOfArguments(); ++argument) { >- AbstractValue& destination = to->valuesAtHead.argument(argument); >- changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.argument(argument), to->variablesAtHead.argument(argument), from->variablesAtTail.argument(argument)); >- } >- >- for (size_t local = 0; local < from->variablesAtTail.numberOfLocals(); ++local) { >- AbstractValue& destination = to->valuesAtHead.local(local); >- changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.local(local), to->variablesAtHead.local(local), from->variablesAtTail.local(local)); >- } >- break; >- } >- >- case SSA: { >- for (size_t i = from->valuesAtTail.size(); i--;) >- changed |= to->valuesAtHead[i].merge(from->valuesAtTail[i]); >- >- HashSet<Node*>::iterator iter = to->ssa->liveAtHead.begin(); >- HashSet<Node*>::iterator end = to->ssa->liveAtHead.end(); >- for (; iter != end; ++iter) { >- Node* node = *iter; >- changed |= to->ssa->valuesAtHead.find(node)->value.merge( >- from->ssa->valuesAtTail.find(node)->value); >- } >- break; >- } >- >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- break; >- } >- >- if (!to->cfaHasVisited) >- changed = true; >- >- to->cfaShouldRevisit |= changed; >- >- return changed; >-} >- >-inline bool AbstractState::mergeToSuccessors(BasicBlock* basicBlock) >-{ >- Node* terminal = basicBlock->last(); >- >- ASSERT(terminal->isTerminal()); >- >- switch (terminal->op()) { >- case Jump: { >- ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection); >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLog(" Merging to block ", *terminal->takenBlock(), ".\n"); >-#endif >- return merge(basicBlock, terminal->takenBlock()); >- } >- >- case Branch: { >- ASSERT(basicBlock->cfaBranchDirection != InvalidBranchDirection); >- bool changed = false; >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLog(" Merging to block ", *terminal->takenBlock(), ".\n"); >-#endif >- if (basicBlock->cfaBranchDirection != TakeFalse) >- changed |= merge(basicBlock, terminal->takenBlock()); >-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >- dataLog(" Merging to block ", *terminal->notTakenBlock(), ".\n"); >-#endif >- if (basicBlock->cfaBranchDirection != TakeTrue) >- changed |= merge(basicBlock, terminal->notTakenBlock()); >- return changed; >- } >- >- case Switch: { >- // FIXME: It would be cool to be sparse conditional for Switch's. Currently >- // we're not. However I somehow doubt that this will ever be a big deal. >- ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection); >- SwitchData* data = terminal->switchData(); >- bool changed = merge(basicBlock, data->fallThrough); >- for (unsigned i = data->cases.size(); i--;) >- changed |= merge(basicBlock, data->cases[i].target); >- return changed; >- } >- >- case Return: >- case Throw: >- case ThrowReferenceError: >- ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection); >- return false; >- >- default: >- RELEASE_ASSERT_NOT_REACHED(); >- return false; >- } >-} >- >-inline bool AbstractState::mergeVariableBetweenBlocks(AbstractValue& destination, AbstractValue& source, Node* destinationNode, Node* sourceNode) >-{ >- if (!destinationNode) >- return false; >- >- ASSERT_UNUSED(sourceNode, sourceNode); >- >- // FIXME: We could do some sparse conditional propagation here! >- >- return destination.merge(source); >-} >- >-void AbstractState::dump(PrintStream& out) >-{ >- bool first = true; >- for (size_t i = 0; i < m_block->size(); ++i) { >- Node* node = m_block->at(i); >- AbstractValue& value = forNode(node); >- if (value.isClear()) >- continue; >- if (first) >- first = false; >- else >- out.printf(" "); >- out.printf("@%lu:", static_cast<unsigned long>(node->index())); >- value.dump(out); >- } >-} >- >-FiltrationResult AbstractState::filter(AbstractValue& value, const StructureSet& set) >-{ >- if (value.filter(m_graph, set) == FiltrationOK) >- return FiltrationOK; >- m_isValid = false; >- return Contradiction; >-} >- >-FiltrationResult AbstractState::filterArrayModes(AbstractValue& value, ArrayModes arrayModes) >-{ >- if (value.filterArrayModes(arrayModes) == FiltrationOK) >- return FiltrationOK; >- m_isValid = false; >- return Contradiction; >-} >- >-FiltrationResult AbstractState::filter(AbstractValue& value, SpeculatedType type) >-{ >- if (value.filter(type) == FiltrationOK) >- return FiltrationOK; >- m_isValid = false; >- return Contradiction; >-} >- >-FiltrationResult AbstractState::filterByValue( >- AbstractValue& abstractValue, JSValue concreteValue) >-{ >- if (abstractValue.filterByValue(concreteValue) == FiltrationOK) >- return FiltrationOK; >- m_isValid = false; >- return Contradiction; >-} >- >-} } // namespace JSC::DFG >- >-#endif // ENABLE(DFG_JIT) >- >Index: Source/JavaScriptCore/dfg/DFGAbstractState.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGAbstractState.h (revision 152828) >+++ Source/JavaScriptCore/dfg/DFGAbstractState.h (working copy) >@@ -1,332 +0,0 @@ >-/* >- * Copyright (C) 2011, 2012, 2013 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 DFGAbstractState_h >-#define DFGAbstractState_h >- >-#include <wtf/Platform.h> >- >-#if ENABLE(DFG_JIT) >- >-#include "DFGAbstractValue.h" >-#include "DFGBranchDirection.h" >-#include "DFGGraph.h" >-#include "DFGNode.h" >-#include <wtf/Vector.h> >- >-namespace JSC { >- >-class CodeBlock; >- >-namespace DFG { >- >-struct BasicBlock; >- >-// This implements the notion of an abstract state for flow-sensitive intraprocedural >-// control flow analysis (CFA), with a focus on the elimination of redundant type checks. >-// It also implements most of the mechanisms of abstract interpretation that such an >-// analysis would use. This class should be used in two idioms: >-// >-// 1) Performing the CFA. In this case, AbstractState should be run over all basic >-// blocks repeatedly until convergence is reached. Convergence is defined by >-// endBasicBlock(AbstractState::MergeToSuccessors) returning false for all blocks. >-// >-// 2) Rematerializing the results of a previously executed CFA. In this case, >-// AbstractState should be run over whatever basic block you're interested in up >-// to the point of the node at which you'd like to interrogate the known type >-// of all other nodes. At this point it's safe to discard the AbstractState entirely, >-// call reset(), or to run it to the end of the basic block and call >-// endBasicBlock(AbstractState::DontMerge). The latter option is safest because >-// it performs some useful integrity checks. >-// >-// After the CFA is run, the inter-block state is saved at the heads and tails of all >-// basic blocks. This allows the intra-block state to be rematerialized by just >-// executing the CFA for that block. If you need to know inter-block state only, then >-// you only need to examine the BasicBlock::m_valuesAtHead or m_valuesAtTail fields. >-// >-// Running this analysis involves the following, modulo the inter-block state >-// merging and convergence fixpoint: >-// >-// AbstractState state(codeBlock, graph); >-// state.beginBasicBlock(basicBlock); >-// bool endReached = true; >-// for (unsigned i = 0; i < basicBlock->size(); ++i) { >-// if (!state.execute(i)) >-// break; >-// } >-// bool result = state.endBasicBlock(<either Merge or DontMerge>); >- >-class AbstractState { >-public: >- enum MergeMode { >- // Don't merge the state in AbstractState with basic blocks. >- DontMerge, >- >- // Merge the state in AbstractState with the tail of the basic >- // block being analyzed. >- MergeToTail, >- >- // Merge the state in AbstractState with the tail of the basic >- // block, and with the heads of successor blocks. >- MergeToSuccessors >- }; >- >- AbstractState(Graph&); >- >- ~AbstractState(); >- >- AbstractValue& forNode(Node* node) >- { >- return node->value; >- } >- >- AbstractValue& forNode(Edge edge) >- { >- return forNode(edge.node()); >- } >- >- Operands<AbstractValue>& variables() >- { >- return m_variables; >- } >- >- bool needsTypeCheck(Node* node, SpeculatedType typesPassedThrough) >- { >- return forNode(node).m_type & ~typesPassedThrough; >- } >- >- bool needsTypeCheck(Edge edge, SpeculatedType typesPassedThrough) >- { >- return needsTypeCheck(edge.node(), typesPassedThrough); >- } >- >- bool needsTypeCheck(Edge edge) >- { >- return needsTypeCheck(edge, typeFilterFor(edge.useKind())); >- } >- >- // Call this before beginning CFA to initialize the abstract values of >- // arguments, and to indicate which blocks should be listed for CFA >- // execution. >- void initialize(); >- >- // Start abstractly executing the given basic block. Initializes the >- // notion of abstract state to what we believe it to be at the head >- // of the basic block, according to the basic block's data structures. >- // This method also sets cfaShouldRevisit to false. >- void beginBasicBlock(BasicBlock*); >- >- // Finish abstractly executing a basic block. If MergeToTail or >- // MergeToSuccessors is passed, then this merges everything we have >- // learned about how the state changes during this block's execution into >- // the block's data structures. There are three return modes, depending >- // on the value of mergeMode: >- // >- // DontMerge: >- // Always returns false. >- // >- // MergeToTail: >- // Returns true if the state of the block at the tail was changed. >- // This means that you must call mergeToSuccessors(), and if that >- // returns true, then you must revisit (at least) the successor >- // blocks. False will always be returned if the block is terminal >- // (i.e. ends in Throw or Return, or has a ForceOSRExit inside it). >- // >- // MergeToSuccessors: >- // Returns true if the state of the block at the tail was changed, >- // and, if the state at the heads of successors was changed. >- // A true return means that you must revisit (at least) the successor >- // blocks. This also sets cfaShouldRevisit to true for basic blocks >- // that must be visited next. >- bool endBasicBlock(MergeMode); >- >- // Reset the AbstractState. This throws away any results, and at this point >- // you can safely call beginBasicBlock() on any basic block. >- void reset(); >- >- // Abstractly executes the given node. The new abstract state is stored into an >- // abstract stack stored in *this. Loads of local variables (that span >- // basic blocks) interrogate the basic block's notion of the state at the head. >- // Stores to local variables are handled in endBasicBlock(). This returns true >- // if execution should continue past this node. Notably, it will return true >- // for block terminals, so long as those terminals are not Return or variants >- // of Throw. >- // >- // This is guaranteed to be equivalent to doing: >- // >- // if (state.startExecuting(index)) { >- // state.executeEdges(index); >- // result = state.executeEffects(index); >- // } else >- // result = true; >- bool execute(unsigned indexInBlock); >- >- // Indicate the start of execution of the node. It resets any state in the node, >- // that is progressively built up by executeEdges() and executeEffects(). In >- // particular, this resets canExit(), so if you want to "know" between calls of >- // startExecuting() and executeEdges()/Effects() whether the last run of the >- // analysis concluded that the node can exit, you should probably set that >- // information aside prior to calling startExecuting(). >- bool startExecuting(Node*); >- bool startExecuting(unsigned indexInBlock); >- >- // Abstractly execute the edges of the given node. This runs filterEdgeByUse() >- // on all edges of the node. You can skip this step, if you have already used >- // filterEdgeByUse() (or some equivalent) on each edge. >- void executeEdges(Node*); >- void executeEdges(unsigned indexInBlock); >- >- ALWAYS_INLINE void filterEdgeByUse(Node* node, Edge& edge) >- { >- ASSERT(mayHaveTypeCheck(edge.useKind()) || !needsTypeCheck(edge)); >- filterByType(node, edge, typeFilterFor(edge.useKind())); >- } >- >- // Abstractly execute the effects of the given node. This changes the abstract >- // state assuming that edges have already been filtered. >- bool executeEffects(unsigned indexInBlock); >- bool executeEffects(unsigned indexInBlock, Node*); >- >- // Did the last executed node clobber the world? >- bool didClobber() const { return m_didClobber; } >- >- // Is the execution state still valid? This will be false if execute() has >- // returned false previously. >- bool isValid() const { return m_isValid; } >- >- // Merge the abstract state stored at the first block's tail into the second >- // block's head. Returns true if the second block's state changed. If so, >- // that block must be abstractly interpreted again. This also sets >- // to->cfaShouldRevisit to true, if it returns true, or if to has not been >- // visited yet. >- bool merge(BasicBlock* from, BasicBlock* to); >- >- // Merge the abstract state stored at the block's tail into all of its >- // successors. Returns true if any of the successors' states changed. Note >- // that this is automatically called in endBasicBlock() if MergeMode is >- // MergeToSuccessors. >- bool mergeToSuccessors(BasicBlock*); >- >- void dump(PrintStream& out); >- >- template<typename T> >- FiltrationResult filter(T node, const StructureSet& set) >- { >- return filter(forNode(node), set); >- } >- >- template<typename T> >- FiltrationResult filterArrayModes(T node, ArrayModes arrayModes) >- { >- return filterArrayModes(forNode(node), arrayModes); >- } >- >- template<typename T> >- FiltrationResult filter(T node, SpeculatedType type) >- { >- return filter(forNode(node), type); >- } >- >- template<typename T> >- FiltrationResult filterByValue(T node, JSValue value) >- { >- return filterByValue(forNode(node), value); >- } >- >- FiltrationResult filter(AbstractValue&, const StructureSet&); >- FiltrationResult filterArrayModes(AbstractValue&, ArrayModes); >- FiltrationResult filter(AbstractValue&, SpeculatedType); >- FiltrationResult filterByValue(AbstractValue&, JSValue); >- >-private: >- void clobberWorld(const CodeOrigin&, unsigned indexInBlock); >- void clobberCapturedVars(const CodeOrigin&); >- void clobberStructures(unsigned indexInBlock); >- >- bool mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, Node*); >- >- static bool mergeVariableBetweenBlocks(AbstractValue& destination, AbstractValue& source, Node* destinationNode, Node* sourceNode); >- >- enum BooleanResult { >- UnknownBooleanResult, >- DefinitelyFalse, >- DefinitelyTrue >- }; >- BooleanResult booleanResult(Node*, AbstractValue&); >- >- bool trySetConstant(Node* node, JSValue value) >- { >- // Make sure we don't constant fold something that will produce values that contravene >- // predictions. If that happens then we know that the code will OSR exit, forcing >- // recompilation. But if we tried to constant fold then we'll have a very degenerate >- // IR: namely we'll have a JSConstant that contravenes its own prediction. There's a >- // lot of subtle code that assumes that >- // speculationFromValue(jsConstant) == jsConstant.prediction(). "Hardening" that code >- // is probably less sane than just pulling back on constant folding. >- SpeculatedType oldType = node->prediction(); >- if (mergeSpeculations(speculationFromValue(value), oldType) != oldType) >- return false; >- >- forNode(node).set(m_graph, value); >- return true; >- } >- >- ALWAYS_INLINE void filterByType(Node* node, Edge& edge, SpeculatedType type) >- { >- AbstractValue& value = forNode(edge); >- if (value.m_type & ~type) { >- node->setCanExit(true); >- edge.setProofStatus(NeedsCheck); >- } else >- edge.setProofStatus(IsProved); >- >- filter(value, type); >- } >- >- void verifyEdge(Node*, Edge); >- void verifyEdges(Node*); >- >- CodeBlock* m_codeBlock; >- Graph& m_graph; >- >- Operands<AbstractValue> m_variables; >- BasicBlock* m_block; >- Node* m_currentNode; >- >- bool m_haveStructures; >- bool m_foundConstants; >- >- bool m_isValid; >- bool m_didClobber; >- >- BranchDirection m_branchDirection; // This is only set for blocks that end in Branch and that execute to completion (i.e. m_isValid == true). >-}; >- >-} } // namespace JSC::DFG >- >-#endif // ENABLE(DFG_JIT) >- >-#endif // DFGAbstractState_h >- >Index: Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp (revision 152828) >+++ Source/JavaScriptCore/dfg/DFGArgumentsSimplificationPhase.cpp (working copy) >@@ -28,7 +28,6 @@ > > #if ENABLE(DFG_JIT) > >-#include "DFGAbstractState.h" > #include "DFGBasicBlock.h" > #include "DFGGraph.h" > #include "DFGInsertionSet.h" >Index: Source/JavaScriptCore/dfg/DFGCFAPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGCFAPhase.cpp (revision 152828) >+++ Source/JavaScriptCore/dfg/DFGCFAPhase.cpp (working copy) >@@ -28,8 +28,9 @@ > > #if ENABLE(DFG_JIT) > >-#include "DFGAbstractState.h" >+#include "DFGAbstractInterpreterInlines.h" > #include "DFGGraph.h" >+#include "DFGInPlaceAbstractState.h" > #include "DFGPhase.h" > #include "Operations.h" > >@@ -46,6 +47,7 @@ public: > CFAPhase(Graph& graph) > : Phase(graph, "control flow analysis") > , m_state(graph) >+ , m_interpreter(graph, m_state) > { > } > >@@ -97,10 +99,10 @@ private: > if (verbose) { > Node* node = block->at(i); > dataLogF(" %s @%u: ", Graph::opName(node->op()), node->index()); >- m_state.dump(WTF::dataFile()); >+ m_interpreter.dump(WTF::dataFile()); > dataLogF("\n"); > } >- if (!m_state.execute(i)) { >+ if (!m_interpreter.execute(i)) { > if (verbose) > dataLogF(" Expect OSR exit.\n"); > break; >@@ -108,10 +110,10 @@ private: > } > if (verbose) { > dataLogF(" tail regs: "); >- m_state.dump(WTF::dataFile()); >+ m_interpreter.dump(WTF::dataFile()); > dataLogF("\n"); > } >- m_changed |= m_state.endBasicBlock(AbstractState::MergeToSuccessors); >+ m_changed |= m_state.endBasicBlock(MergeToSuccessors); > > if (verbose) { > dataLogF(" tail vars: "); >@@ -131,7 +133,8 @@ private: > } > > private: >- AbstractState m_state; >+ InPlaceAbstractState m_state; >+ AbstractInterpreter<InPlaceAbstractState> m_interpreter; > > bool m_changed; > unsigned m_count; >Index: Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp (revision 152828) >+++ Source/JavaScriptCore/dfg/DFGCFGSimplificationPhase.cpp (working copy) >@@ -28,7 +28,6 @@ > > #if ENABLE(DFG_JIT) > >-#include "DFGAbstractState.h" > #include "DFGBasicBlockInlines.h" > #include "DFGGraph.h" > #include "DFGInsertionSet.h" >Index: Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp (revision 152828) >+++ Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp (working copy) >@@ -28,9 +28,10 @@ > > #if ENABLE(DFG_JIT) > >-#include "DFGAbstractState.h" >+#include "DFGAbstractInterpreterInlines.h" > #include "DFGBasicBlock.h" > #include "DFGGraph.h" >+#include "DFGInPlaceAbstractState.h" > #include "DFGInsertionSet.h" > #include "DFGPhase.h" > #include "GetByIdStatus.h" >@@ -44,6 +45,7 @@ public: > ConstantFoldingPhase(Graph& graph) > : Phase(graph, "constant folding") > , m_state(graph) >+ , m_interpreter(graph, m_state) > , m_insertionSet(graph) > { > } >@@ -100,7 +102,7 @@ private: > else > set = node->structureSet(); > if (value.m_currentKnownStructure.isSubsetOf(set)) { >- m_state.execute(indexInBlock); // Catch the fact that we may filter on cell. >+ m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell. > node->convertToPhantom(); > eliminated = true; > break; >@@ -109,7 +111,7 @@ private: > if (structureValue.isSubsetOf(set) > && structureValue.hasSingleton()) { > Structure* structure = structureValue.singleton(); >- m_state.execute(indexInBlock); // Catch the fact that we may filter on cell. >+ m_interpreter.execute(indexInBlock); // Catch the fact that we may filter on cell. > AdjacencyList children = node->children; > children.removeEdge(0); > if (!!children.child1()) { >@@ -177,7 +179,7 @@ private: > // Now before we do anything else, push the CFA forward over the GetById > // and make sure we signal to the loop that it should continue and not > // do any eliminations. >- m_state.execute(indexInBlock); >+ m_interpreter.execute(indexInBlock); > eliminated = true; > > if (needsWatchpoint) { >@@ -240,7 +242,7 @@ private: > // Now before we do anything else, push the CFA forward over the PutById > // and make sure we signal to the loop that it should continue and not > // do any eliminations. >- m_state.execute(indexInBlock); >+ m_interpreter.execute(indexInBlock); > eliminated = true; > > if (needsWatchpoint) { >@@ -329,7 +331,7 @@ private: > continue; > } > >- m_state.execute(indexInBlock); >+ m_interpreter.execute(indexInBlock); > if (!node->shouldGenerate() || m_state.didClobber() || node->hasConstant()) > continue; > JSValue value = m_state.forNode(node).value(); >@@ -417,7 +419,8 @@ private: > OpInfo(m_graph.addStructureSet(cell->structure())), Edge(weakConstant, CellUse)); > } > >- AbstractState m_state; >+ InPlaceAbstractState m_state; >+ AbstractInterpreter<InPlaceAbstractState> m_interpreter; > InsertionSet m_insertionSet; > }; > >Index: Source/JavaScriptCore/dfg/DFGInPlaceAbstractState.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGInPlaceAbstractState.cpp (revision 0) >+++ Source/JavaScriptCore/dfg/DFGInPlaceAbstractState.cpp (working copy) >@@ -0,0 +1,469 @@ >+/* >+ * Copyright (C) 2013 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 "DFGInPlaceAbstractState.h" >+ >+#if ENABLE(DFG_JIT) >+ >+#include "CodeBlock.h" >+#include "DFGBasicBlock.h" >+#include "GetByIdStatus.h" >+#include "Operations.h" >+#include "PutByIdStatus.h" >+#include "StringObject.h" >+ >+namespace JSC { namespace DFG { >+ >+InPlaceAbstractState::InPlaceAbstractState(Graph& graph) >+ : m_graph(graph) >+ , m_variables(m_graph.m_codeBlock->numParameters(), graph.m_localVars) >+ , m_block(0) >+{ >+} >+ >+InPlaceAbstractState::~InPlaceAbstractState() { } >+ >+void InPlaceAbstractState::beginBasicBlock(BasicBlock* basicBlock) >+{ >+ ASSERT(!m_block); >+ >+ ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->valuesAtHead.numberOfLocals()); >+ ASSERT(basicBlock->variablesAtTail.numberOfLocals() == basicBlock->valuesAtTail.numberOfLocals()); >+ ASSERT(basicBlock->variablesAtHead.numberOfLocals() == basicBlock->variablesAtTail.numberOfLocals()); >+ >+ for (size_t i = 0; i < basicBlock->size(); i++) >+ forNode(basicBlock->at(i)).clear(); >+ >+ m_variables = basicBlock->valuesAtHead; >+ m_haveStructures = false; >+ for (size_t i = 0; i < m_variables.numberOfArguments(); ++i) { >+ if (m_variables.argument(i).hasClobberableState()) { >+ m_haveStructures = true; >+ break; >+ } >+ } >+ for (size_t i = 0; i < m_variables.numberOfLocals(); ++i) { >+ if (m_variables.local(i).hasClobberableState()) { >+ m_haveStructures = true; >+ break; >+ } >+ } >+ >+ if (m_graph.m_form == SSA) { >+ HashMap<Node*, AbstractValue>::iterator iter = basicBlock->ssa->valuesAtHead.begin(); >+ HashMap<Node*, AbstractValue>::iterator end = basicBlock->ssa->valuesAtHead.end(); >+ for (; iter != end; ++iter) { >+ forNode(iter->key) = iter->value; >+ if (iter->value.hasClobberableState()) >+ m_haveStructures = true; >+ } >+ } >+ >+ basicBlock->cfaShouldRevisit = false; >+ basicBlock->cfaHasVisited = true; >+ m_block = basicBlock; >+ m_isValid = true; >+ m_foundConstants = false; >+ m_branchDirection = InvalidBranchDirection; >+} >+ >+static void setLiveValues(HashMap<Node*, AbstractValue>& values, HashSet<Node*>& live) >+{ >+ values.clear(); >+ >+ HashSet<Node*>::iterator iter = live.begin(); >+ HashSet<Node*>::iterator end = live.end(); >+ for (; iter != end; ++iter) >+ values.add(*iter, AbstractValue()); >+} >+ >+void InPlaceAbstractState::initialize() >+{ >+ BasicBlock* root = m_graph.block(0); >+ root->cfaShouldRevisit = true; >+ root->cfaHasVisited = false; >+ root->cfaFoundConstants = false; >+ for (size_t i = 0; i < root->valuesAtHead.numberOfArguments(); ++i) { >+ if (m_graph.m_form == SSA) { >+ root->valuesAtHead.argument(i).makeTop(); >+ continue; >+ } >+ >+ Node* node = root->variablesAtHead.argument(i); >+ ASSERT(node->op() == SetArgument); >+ if (!node->variableAccessData()->shouldUnboxIfPossible()) { >+ root->valuesAtHead.argument(i).makeTop(); >+ continue; >+ } >+ >+ SpeculatedType prediction = >+ node->variableAccessData()->argumentAwarePrediction(); >+ if (isInt32Speculation(prediction)) >+ root->valuesAtHead.argument(i).setType(SpecInt32); >+ else if (isBooleanSpeculation(prediction)) >+ root->valuesAtHead.argument(i).setType(SpecBoolean); >+ else if (isCellSpeculation(prediction)) >+ root->valuesAtHead.argument(i).setType(SpecCell); >+ else >+ root->valuesAtHead.argument(i).makeTop(); >+ >+ root->valuesAtTail.argument(i).clear(); >+ } >+ for (size_t i = 0; i < root->valuesAtHead.numberOfLocals(); ++i) { >+ Node* node = root->variablesAtHead.local(i); >+ if (node && node->variableAccessData()->isCaptured()) >+ root->valuesAtHead.local(i).makeTop(); >+ else >+ root->valuesAtHead.local(i).clear(); >+ root->valuesAtTail.local(i).clear(); >+ } >+ for (BlockIndex blockIndex = 1 ; blockIndex < m_graph.numBlocks(); ++blockIndex) { >+ BasicBlock* block = m_graph.block(blockIndex); >+ if (!block) >+ continue; >+ ASSERT(block->isReachable); >+ block->cfaShouldRevisit = false; >+ block->cfaHasVisited = false; >+ block->cfaFoundConstants = false; >+ for (size_t i = 0; i < block->valuesAtHead.numberOfArguments(); ++i) { >+ block->valuesAtHead.argument(i).clear(); >+ block->valuesAtTail.argument(i).clear(); >+ } >+ for (size_t i = 0; i < block->valuesAtHead.numberOfLocals(); ++i) { >+ block->valuesAtHead.local(i).clear(); >+ block->valuesAtTail.local(i).clear(); >+ } >+ if (!block->isOSRTarget) >+ continue; >+ if (block->bytecodeBegin != m_graph.m_plan.osrEntryBytecodeIndex) >+ continue; >+ for (size_t i = 0; i < m_graph.m_plan.mustHandleValues.size(); ++i) { >+ AbstractValue value; >+ value.setMostSpecific(m_graph, m_graph.m_plan.mustHandleValues[i]); >+ int operand = m_graph.m_plan.mustHandleValues.operandForIndex(i); >+ block->valuesAtHead.operand(operand).merge(value); >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLogF(" Initializing Block #%u, operand r%d, to ", blockIndex, operand); >+ block->valuesAtHead.operand(operand).dump(WTF::dataFile()); >+ dataLogF("\n"); >+#endif >+ } >+ block->cfaShouldRevisit = true; >+ } >+ if (m_graph.m_form == SSA) { >+ for (BlockIndex blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex) { >+ BasicBlock* block = m_graph.block(blockIndex); >+ if (!block) >+ continue; >+ setLiveValues(block->ssa->valuesAtHead, block->ssa->liveAtHead); >+ setLiveValues(block->ssa->valuesAtTail, block->ssa->liveAtTail); >+ } >+ } >+} >+ >+bool InPlaceAbstractState::endBasicBlock(MergeMode mergeMode) >+{ >+ ASSERT(m_block); >+ >+ BasicBlock* block = m_block; // Save the block for successor merging. >+ >+ block->cfaFoundConstants = m_foundConstants; >+ block->cfaDidFinish = m_isValid; >+ block->cfaBranchDirection = m_branchDirection; >+ >+ if (!m_isValid) { >+ reset(); >+ return false; >+ } >+ >+ bool changed = false; >+ >+ if (mergeMode != DontMerge || !ASSERT_DISABLED) { >+ switch (m_graph.m_form) { >+ case ThreadedCPS: { >+ for (size_t argument = 0; argument < block->variablesAtTail.numberOfArguments(); ++argument) { >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLogF(" Merging state for argument %zu.\n", argument); >+#endif >+ AbstractValue& destination = block->valuesAtTail.argument(argument); >+ changed |= mergeStateAtTail(destination, m_variables.argument(argument), block->variablesAtTail.argument(argument)); >+ } >+ >+ for (size_t local = 0; local < block->variablesAtTail.numberOfLocals(); ++local) { >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLogF(" Merging state for local %zu.\n", local); >+#endif >+ AbstractValue& destination = block->valuesAtTail.local(local); >+ changed |= mergeStateAtTail(destination, m_variables.local(local), block->variablesAtTail.local(local)); >+ } >+ break; >+ } >+ >+ case SSA: { >+ for (size_t i = 0; i < block->valuesAtTail.size(); ++i) >+ changed |= block->valuesAtTail[i].merge(m_variables[i]); >+ >+ HashSet<Node*>::iterator iter = block->ssa->liveAtTail.begin(); >+ HashSet<Node*>::iterator end = block->ssa->liveAtTail.end(); >+ for (; iter != end; ++iter) { >+ Node* node = *iter; >+ changed |= block->ssa->valuesAtTail.find(node)->value.merge(forNode(node)); >+ } >+ break; >+ } >+ >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ } >+ } >+ >+ ASSERT(mergeMode != DontMerge || !changed); >+ >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLogF(" Branch direction = %s\n", branchDirectionToString(m_branchDirection)); >+#endif >+ >+ reset(); >+ >+ if (mergeMode != MergeToSuccessors) >+ return changed; >+ >+ return mergeToSuccessors(block); >+} >+ >+void InPlaceAbstractState::reset() >+{ >+ m_block = 0; >+ m_isValid = false; >+ m_branchDirection = InvalidBranchDirection; >+} >+ >+bool InPlaceAbstractState::mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, Node* node) >+{ >+ if (!node) >+ return false; >+ >+ AbstractValue source; >+ >+ if (node->variableAccessData()->isCaptured()) { >+ // If it's captured then we know that whatever value was stored into the variable last is the >+ // one we care about. This is true even if the variable at tail is dead, which might happen if >+ // the last thing we did to the variable was a GetLocal and then ended up now using the >+ // GetLocal's result. >+ >+ source = inVariable; >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLogF(" Transfering "); >+ source.dump(WTF::dataFile()); >+ dataLogF(" from last access due to captured variable.\n"); >+#endif >+ } else { >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLogF(" It's live, node @%u.\n", node->index()); >+#endif >+ >+ switch (node->op()) { >+ case Phi: >+ case SetArgument: >+ case PhantomLocal: >+ case Flush: >+ // The block transfers the value from head to tail. >+ source = inVariable; >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLogF(" Transfering "); >+ source.dump(WTF::dataFile()); >+ dataLogF(" from head to tail.\n"); >+#endif >+ break; >+ >+ case GetLocal: >+ // The block refines the value with additional speculations. >+ source = forNode(node); >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLogF(" Refining to "); >+ source.dump(WTF::dataFile()); >+ dataLogF("\n"); >+#endif >+ break; >+ >+ case SetLocal: >+ // The block sets the variable, and potentially refines it, both >+ // before and after setting it. >+ if (node->variableAccessData()->shouldUseDoubleFormat()) { >+ // FIXME: This unnecessarily loses precision. >+ source.setType(SpecDouble); >+ } else >+ source = forNode(node->child1()); >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLogF(" Setting to "); >+ source.dump(WTF::dataFile()); >+ dataLogF("\n"); >+#endif >+ break; >+ >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ } >+ >+ if (destination == source) { >+ // Abstract execution did not change the output value of the variable, for this >+ // basic block, on this iteration. >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLogF(" Not changed!\n"); >+#endif >+ return false; >+ } >+ >+ // Abstract execution reached a new conclusion about the speculations reached about >+ // this variable after execution of this basic block. Update the state, and return >+ // true to indicate that the fixpoint must go on! >+ destination = source; >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLogF(" Changed!\n"); >+#endif >+ return true; >+} >+ >+bool InPlaceAbstractState::merge(BasicBlock* from, BasicBlock* to) >+{ >+ ASSERT(from->variablesAtTail.numberOfArguments() == to->variablesAtHead.numberOfArguments()); >+ ASSERT(from->variablesAtTail.numberOfLocals() == to->variablesAtHead.numberOfLocals()); >+ >+ bool changed = false; >+ >+ switch (m_graph.m_form) { >+ case ThreadedCPS: { >+ for (size_t argument = 0; argument < from->variablesAtTail.numberOfArguments(); ++argument) { >+ AbstractValue& destination = to->valuesAtHead.argument(argument); >+ changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.argument(argument), to->variablesAtHead.argument(argument), from->variablesAtTail.argument(argument)); >+ } >+ >+ for (size_t local = 0; local < from->variablesAtTail.numberOfLocals(); ++local) { >+ AbstractValue& destination = to->valuesAtHead.local(local); >+ changed |= mergeVariableBetweenBlocks(destination, from->valuesAtTail.local(local), to->variablesAtHead.local(local), from->variablesAtTail.local(local)); >+ } >+ break; >+ } >+ >+ case SSA: { >+ for (size_t i = from->valuesAtTail.size(); i--;) >+ changed |= to->valuesAtHead[i].merge(from->valuesAtTail[i]); >+ >+ HashSet<Node*>::iterator iter = to->ssa->liveAtHead.begin(); >+ HashSet<Node*>::iterator end = to->ssa->liveAtHead.end(); >+ for (; iter != end; ++iter) { >+ Node* node = *iter; >+ changed |= to->ssa->valuesAtHead.find(node)->value.merge( >+ from->ssa->valuesAtTail.find(node)->value); >+ } >+ break; >+ } >+ >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ break; >+ } >+ >+ if (!to->cfaHasVisited) >+ changed = true; >+ >+ to->cfaShouldRevisit |= changed; >+ >+ return changed; >+} >+ >+inline bool InPlaceAbstractState::mergeToSuccessors(BasicBlock* basicBlock) >+{ >+ Node* terminal = basicBlock->last(); >+ >+ ASSERT(terminal->isTerminal()); >+ >+ switch (terminal->op()) { >+ case Jump: { >+ ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection); >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLog(" Merging to block ", *terminal->takenBlock(), ".\n"); >+#endif >+ return merge(basicBlock, terminal->takenBlock()); >+ } >+ >+ case Branch: { >+ ASSERT(basicBlock->cfaBranchDirection != InvalidBranchDirection); >+ bool changed = false; >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLog(" Merging to block ", *terminal->takenBlock(), ".\n"); >+#endif >+ if (basicBlock->cfaBranchDirection != TakeFalse) >+ changed |= merge(basicBlock, terminal->takenBlock()); >+#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE) >+ dataLog(" Merging to block ", *terminal->notTakenBlock(), ".\n"); >+#endif >+ if (basicBlock->cfaBranchDirection != TakeTrue) >+ changed |= merge(basicBlock, terminal->notTakenBlock()); >+ return changed; >+ } >+ >+ case Switch: { >+ // FIXME: It would be cool to be sparse conditional for Switch's. Currently >+ // we're not. However I somehow doubt that this will ever be a big deal. >+ ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection); >+ SwitchData* data = terminal->switchData(); >+ bool changed = merge(basicBlock, data->fallThrough); >+ for (unsigned i = data->cases.size(); i--;) >+ changed |= merge(basicBlock, data->cases[i].target); >+ return changed; >+ } >+ >+ case Return: >+ case Throw: >+ case ThrowReferenceError: >+ ASSERT(basicBlock->cfaBranchDirection == InvalidBranchDirection); >+ return false; >+ >+ default: >+ RELEASE_ASSERT_NOT_REACHED(); >+ return false; >+ } >+} >+ >+inline bool InPlaceAbstractState::mergeVariableBetweenBlocks(AbstractValue& destination, AbstractValue& source, Node* destinationNode, Node* sourceNode) >+{ >+ if (!destinationNode) >+ return false; >+ >+ ASSERT_UNUSED(sourceNode, sourceNode); >+ >+ // FIXME: We could do some sparse conditional propagation here! >+ >+ return destination.merge(source); >+} >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >+ >Index: Source/JavaScriptCore/dfg/DFGInPlaceAbstractState.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGInPlaceAbstractState.h (revision 0) >+++ Source/JavaScriptCore/dfg/DFGInPlaceAbstractState.h (working copy) >@@ -0,0 +1,155 @@ >+/* >+ * Copyright (C) 2013 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 DFGInPlaceAbstractState_h >+#define DFGInPlaceAbstractState_h >+ >+#include <wtf/Platform.h> >+ >+#if ENABLE(DFG_JIT) >+ >+#include "DFGAbstractValue.h" >+#include "DFGBranchDirection.h" >+#include "DFGGraph.h" >+#include "DFGMergeMode.h" >+#include "DFGNode.h" >+ >+namespace JSC { namespace DFG { >+ >+class InPlaceAbstractState { >+public: >+ InPlaceAbstractState(Graph& graph); >+ >+ ~InPlaceAbstractState(); >+ >+ AbstractValue& forNode(Node* node) >+ { >+ return node->value; >+ } >+ >+ AbstractValue& forNode(Edge edge) >+ { >+ return forNode(edge.node()); >+ } >+ >+ Operands<AbstractValue>& variables() >+ { >+ return m_variables; >+ } >+ >+ // Call this before beginning CFA to initialize the abstract values of >+ // arguments, and to indicate which blocks should be listed for CFA >+ // execution. >+ void initialize(); >+ >+ // Start abstractly executing the given basic block. Initializes the >+ // notion of abstract state to what we believe it to be at the head >+ // of the basic block, according to the basic block's data structures. >+ // This method also sets cfaShouldRevisit to false. >+ void beginBasicBlock(BasicBlock*); >+ >+ BasicBlock* block() const { return m_block; } >+ >+ // Finish abstractly executing a basic block. If MergeToTail or >+ // MergeToSuccessors is passed, then this merges everything we have >+ // learned about how the state changes during this block's execution into >+ // the block's data structures. There are three return modes, depending >+ // on the value of mergeMode: >+ // >+ // DontMerge: >+ // Always returns false. >+ // >+ // MergeToTail: >+ // Returns true if the state of the block at the tail was changed. >+ // This means that you must call mergeToSuccessors(), and if that >+ // returns true, then you must revisit (at least) the successor >+ // blocks. False will always be returned if the block is terminal >+ // (i.e. ends in Throw or Return, or has a ForceOSRExit inside it). >+ // >+ // MergeToSuccessors: >+ // Returns true if the state of the block at the tail was changed, >+ // and, if the state at the heads of successors was changed. >+ // A true return means that you must revisit (at least) the successor >+ // blocks. This also sets cfaShouldRevisit to true for basic blocks >+ // that must be visited next. >+ bool endBasicBlock(MergeMode); >+ >+ // Reset the AbstractState. This throws away any results, and at this point >+ // you can safely call beginBasicBlock() on any basic block. >+ void reset(); >+ >+ // Did the last executed node clobber the world? >+ bool didClobber() const { return m_didClobber; } >+ >+ // Is the execution state still valid? This will be false if execute() has >+ // returned false previously. >+ bool isValid() const { return m_isValid; } >+ >+ // Merge the abstract state stored at the first block's tail into the second >+ // block's head. Returns true if the second block's state changed. If so, >+ // that block must be abstractly interpreted again. This also sets >+ // to->cfaShouldRevisit to true, if it returns true, or if to has not been >+ // visited yet. >+ bool merge(BasicBlock* from, BasicBlock* to); >+ >+ // Merge the abstract state stored at the block's tail into all of its >+ // successors. Returns true if any of the successors' states changed. Note >+ // that this is automatically called in endBasicBlock() if MergeMode is >+ // MergeToSuccessors. >+ bool mergeToSuccessors(BasicBlock*); >+ >+ // Methods intended to be called from AbstractInterpreter. >+ void setDidClobber(bool didClobber) { m_didClobber = didClobber; } >+ void setIsValid(bool isValid) { m_isValid = isValid; } >+ void setBranchDirection(BranchDirection branchDirection) { m_branchDirection = branchDirection; } >+ void setFoundConstants(bool foundConstants) { m_foundConstants = foundConstants; } >+ bool haveStructures() const { return m_haveStructures; } // It's always safe to return true. >+ void setHaveStructures(bool haveStructures) { m_haveStructures = haveStructures; } >+ >+private: >+ bool mergeStateAtTail(AbstractValue& destination, AbstractValue& inVariable, Node*); >+ >+ static bool mergeVariableBetweenBlocks(AbstractValue& destination, AbstractValue& source, Node* destinationNode, Node* sourceNode); >+ >+ Graph& m_graph; >+ >+ Operands<AbstractValue> m_variables; >+ BasicBlock* m_block; >+ >+ bool m_haveStructures; >+ bool m_foundConstants; >+ >+ bool m_isValid; >+ bool m_didClobber; >+ >+ BranchDirection m_branchDirection; // This is only set for blocks that end in Branch and that execute to completion (i.e. m_isValid == true). >+}; >+ >+} } // namespace JSC::DFG >+ >+#endif // ENABLE(DFG_JIT) >+ >+#endif // DFGInPlaceAbstractState_h >+ >Index: Source/JavaScriptCore/dfg/DFGMergeMode.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGMergeMode.h (revision 0) >+++ Source/JavaScriptCore/dfg/DFGMergeMode.h (working copy) >@@ -0,0 +1,47 @@ >+/* >+ * Copyright (C) 2013 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 DFGMergeMode_h >+#define DFGMergeMode_h >+ >+namespace JSC { namespace DFG { >+ >+enum MergeMode { >+ // Don't merge the state in AbstractState with basic blocks. >+ DontMerge, >+ >+ // Merge the state in AbstractState with the tail of the basic >+ // block being analyzed. >+ MergeToTail, >+ >+ // Merge the state in AbstractState with the tail of the basic >+ // block, and with the heads of successor blocks. >+ MergeToSuccessors >+}; >+ >+} } // namespace JSC::DFG >+ >+#endif // DFGMergeMode_h >+ >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (revision 152828) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp (working copy) >@@ -862,7 +862,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntI > AbstractValue& value = m_state.forNode(edge); > SpeculatedType type = value.m_type; > ASSERT(edge.useKind() != KnownInt32Use || !(value.m_type & ~SpecInt32)); >- m_state.filter(value, SpecInt32); >+ m_interpreter.filter(value, SpecInt32); > VirtualRegister virtualRegister = edge->virtualRegister(); > GenerationInfo& info = m_generationInfo[virtualRegister]; > >@@ -963,7 +963,7 @@ FPRReg SpeculativeJIT::fillSpeculateDoub > AbstractValue& value = m_state.forNode(edge); > SpeculatedType type = value.m_type; > ASSERT(edge.useKind() != KnownNumberUse || !(value.m_type & ~SpecNumber)); >- m_state.filter(value, SpecNumber); >+ m_interpreter.filter(value, SpecNumber); > VirtualRegister virtualRegister = edge->virtualRegister(); > GenerationInfo& info = m_generationInfo[virtualRegister]; > >@@ -1100,7 +1100,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell > AbstractValue& value = m_state.forNode(edge); > SpeculatedType type = value.m_type; > ASSERT((edge.useKind() != KnownCellUse && edge.useKind() != KnownStringUse) || !(value.m_type & ~SpecCell)); >- m_state.filter(value, SpecCell); >+ m_interpreter.filter(value, SpecCell); > VirtualRegister virtualRegister = edge->virtualRegister(); > GenerationInfo& info = m_generationInfo[virtualRegister]; > >@@ -1177,7 +1177,7 @@ GPRReg SpeculativeJIT::fillSpeculateBool > #endif > AbstractValue& value = m_state.forNode(edge); > SpeculatedType type = value.m_type; >- m_state.filter(value, SpecBoolean); >+ m_interpreter.filter(value, SpecBoolean); > VirtualRegister virtualRegister = edge->virtualRegister(); > GenerationInfo& info = m_generationInfo[virtualRegister]; > >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (revision 152828) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp (working copy) >@@ -822,7 +822,7 @@ GPRReg SpeculativeJIT::fillSpeculateIntI > AbstractValue& value = m_state.forNode(edge); > SpeculatedType type = value.m_type; > ASSERT(edge.useKind() != KnownInt32Use || !(value.m_type & ~SpecInt32)); >- m_state.filter(value, SpecInt32); >+ m_interpreter.filter(value, SpecInt32); > VirtualRegister virtualRegister = edge->virtualRegister(); > GenerationInfo& info = m_generationInfo[virtualRegister]; > >@@ -972,7 +972,7 @@ FPRReg SpeculativeJIT::fillSpeculateDoub > AbstractValue& value = m_state.forNode(edge); > SpeculatedType type = value.m_type; > ASSERT(edge.useKind() != KnownNumberUse || !(value.m_type & ~SpecNumber)); >- m_state.filter(value, SpecNumber); >+ m_interpreter.filter(value, SpecNumber); > VirtualRegister virtualRegister = edge->virtualRegister(); > GenerationInfo& info = m_generationInfo[virtualRegister]; > >@@ -1128,7 +1128,7 @@ GPRReg SpeculativeJIT::fillSpeculateCell > AbstractValue& value = m_state.forNode(edge); > SpeculatedType type = value.m_type; > ASSERT((edge.useKind() != KnownCellUse && edge.useKind() != KnownStringUse) || !(value.m_type & ~SpecCell)); >- m_state.filter(value, SpecCell); >+ m_interpreter.filter(value, SpecCell); > VirtualRegister virtualRegister = edge->virtualRegister(); > GenerationInfo& info = m_generationInfo[virtualRegister]; > >@@ -1205,7 +1205,7 @@ GPRReg SpeculativeJIT::fillSpeculateBool > #endif > AbstractValue& value = m_state.forNode(edge); > SpeculatedType type = value.m_type; >- m_state.filter(value, SpecBoolean); >+ m_interpreter.filter(value, SpecBoolean); > VirtualRegister virtualRegister = edge->virtualRegister(); > GenerationInfo& info = m_generationInfo[virtualRegister]; > >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (revision 152828) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp (working copy) >@@ -29,6 +29,7 @@ > #if ENABLE(DFG_JIT) > > #include "Arguments.h" >+#include "DFGAbstractInterpreterInlines.h" > #include "DFGArrayifySlowPathGenerator.h" > #include "DFGBinarySwitch.h" > #include "DFGCallArrayAllocatorSlowPathGenerator.h" >@@ -48,6 +49,7 @@ SpeculativeJIT::SpeculativeJIT(JITCompil > , m_variables(jit.graph().m_localVars) > , m_lastSetOperand(std::numeric_limits<int>::max()) > , m_state(m_jit.graph()) >+ , m_interpreter(m_jit.graph(), m_state) > , m_stream(&jit.jitCode()->variableEventStream) > , m_minifiedGraph(&jit.jitCode()->minifiedDFG) > , m_isCheckingArgumentTypes(false) >@@ -262,7 +264,7 @@ void SpeculativeJIT::terminateSpeculativ > void SpeculativeJIT::backwardTypeCheck(JSValueSource source, Edge edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail) > { > ASSERT(needsTypeCheck(edge, typesPassedThrough)); >- m_state.filter(edge, typesPassedThrough); >+ m_interpreter.filter(edge, typesPassedThrough); > backwardSpeculationCheck(BadType, source, edge.node(), jumpToFail); > } > >@@ -1703,7 +1705,7 @@ void SpeculativeJIT::compileCurrentBlock > } > > m_canExit = m_currentNode->canExit(); >- bool shouldExecuteEffects = m_state.startExecuting(m_currentNode); >+ bool shouldExecuteEffects = m_interpreter.startExecuting(m_currentNode); > m_jit.setForNode(m_currentNode); > m_codeOriginForOSR = m_currentNode->codeOrigin; > if (!m_currentNode->shouldGenerate()) { >@@ -1800,7 +1802,7 @@ void SpeculativeJIT::compileCurrentBlock > > // Make sure that the abstract state is rematerialized for the next node. > if (shouldExecuteEffects) >- m_state.executeEffects(m_indexInBlock); >+ m_interpreter.executeEffects(m_indexInBlock); > > if (m_currentNode->shouldGenerate()) > checkConsistency(); >@@ -4273,7 +4275,7 @@ void SpeculativeJIT::compileToStringOnCe > > if (!m_state.forNode(node->child1()).m_currentKnownStructure.isSubsetOf(StructureSet(m_jit.globalObjectFor(node->codeOrigin)->stringObjectStructure()))) { > speculateStringObject(op1GPR); >- m_state.filter(node->child1(), SpecStringObject); >+ m_interpreter.filter(node->child1(), SpecStringObject); > } > m_jit.loadPtr(JITCompiler::Address(op1GPR, JSWrapperObject::internalValueCellOffset()), resultGPR); > cellResult(resultGPR, node); >@@ -4297,7 +4299,7 @@ void SpeculativeJIT::compileToStringOnCe > m_jit.move(op1GPR, resultGPR); > done.link(&m_jit); > >- m_state.filter(node->child1(), SpecString | SpecStringObject); >+ m_interpreter.filter(node->child1(), SpecString | SpecStringObject); > > cellResult(resultGPR, node); > break; >@@ -4513,7 +4515,7 @@ void SpeculativeJIT::speculateStringIden > MacroAssembler::Address(storage, StringImpl::flagsOffset()), > MacroAssembler::TrustedImm32(StringImpl::flagIsIdentifier()))); > >- m_state.filter(edge, SpecStringIdent); >+ m_interpreter.filter(edge, SpecStringIdent); > } > > void SpeculativeJIT::speculateStringIdent(Edge edge, GPRReg string) >@@ -4561,7 +4563,7 @@ void SpeculativeJIT::speculateStringObje > return; > > speculateStringObject(gpr); >- m_state.filter(edge, SpecStringObject); >+ m_interpreter.filter(edge, SpecStringObject); > } > > void SpeculativeJIT::speculateStringOrStringObject(Edge edge) >@@ -4586,7 +4588,7 @@ void SpeculativeJIT::speculateStringOrSt > > isString.link(&m_jit); > >- m_state.filter(edge, SpecString | SpecStringObject); >+ m_interpreter.filter(edge, SpecString | SpecStringObject); > } > > void SpeculativeJIT::speculateNotCell(Edge edge) >Index: Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h >=================================================================== >--- Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (revision 152828) >+++ Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h (working copy) >@@ -30,8 +30,9 @@ > > #if ENABLE(DFG_JIT) > >-#include "DFGAbstractState.h" >+#include "DFGAbstractInterpreter.h" > #include "DFGGenerationInfo.h" >+#include "DFGInPlaceAbstractState.h" > #include "DFGJITCompiler.h" > #include "DFGOSRExit.h" > #include "DFGOSRExitJumpPlaceholder.h" >@@ -2062,7 +2063,7 @@ public: > void terminateSpeculativeExecution(ExitKind, JSValueRegs, Edge); > > // Helpers for performing type checks on an edge stored in the given registers. >- bool needsTypeCheck(Edge edge, SpeculatedType typesPassedThrough) { return m_state.needsTypeCheck(edge, typesPassedThrough); } >+ bool needsTypeCheck(Edge edge, SpeculatedType typesPassedThrough) { return m_interpreter.needsTypeCheck(edge, typesPassedThrough); } > void backwardTypeCheck(JSValueSource, Edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail); > void typeCheck(JSValueSource, Edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail); > void forwardTypeCheck(JSValueSource, Edge, SpeculatedType typesPassedThrough, MacroAssembler::Jump jumpToFail, const ValueRecovery&); >@@ -2174,7 +2175,8 @@ public: > int m_lastSetOperand; > CodeOrigin m_codeOriginForOSR; > >- AbstractState m_state; >+ InPlaceAbstractState m_state; >+ AbstractInterpreter<InPlaceAbstractState> m_interpreter; > > VariableEventStream* m_stream; > MinifiedGraph* m_minifiedGraph; >Index: Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp >=================================================================== >--- Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (revision 152828) >+++ Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp (working copy) >@@ -29,7 +29,8 @@ > #if ENABLE(FTL_JIT) > > #include "CodeBlockWithJITType.h" >-#include "DFGAbstractState.h" >+#include "DFGAbstractInterpreterInlines.h" >+#include "DFGInPlaceAbstractState.h" > #include "FTLAbstractHeapRepository.h" > #include "FTLExitThunkGenerator.h" > #include "FTLFormattedValue.h" >@@ -53,7 +54,7 @@ static int compileCounter; > FormattedValue _ftc_lowValue = (lowValue); \ > Edge _ftc_highValue = (highValue); \ > SpeculatedType _ftc_typesPassedThrough = (typesPassedThrough); \ >- if (!m_state.needsTypeCheck(_ftc_highValue, _ftc_typesPassedThrough)) \ >+ if (!m_interpreter.needsTypeCheck(_ftc_highValue, _ftc_typesPassedThrough)) \ > break; \ > typeCheck(_ftc_lowValue, _ftc_highValue, _ftc_typesPassedThrough, (failCondition)); \ > } while (false) >@@ -69,6 +70,7 @@ public: > , m_lastSetOperand(std::numeric_limits<int>::max()) > , m_exitThunkGenerator(state) > , m_state(state.graph) >+ , m_interpreter(state.graph, m_state) > { > } > >@@ -219,7 +221,7 @@ private: > if (verboseCompilationEnabled()) > dataLog("Lowering ", m_node, "\n"); > >- bool shouldExecuteEffects = m_state.startExecuting(m_node); >+ bool shouldExecuteEffects = m_interpreter.startExecuting(m_node); > > m_direction = (m_node->flags() & NodeExitsForward) ? ForwardSpeculation : BackwardSpeculation; > >@@ -408,7 +410,7 @@ private: > m_live.add(m_node); > > if (shouldExecuteEffects) >- m_state.executeEffects(nodeIndex); >+ m_interpreter.executeEffects(nodeIndex); > > return true; > } >@@ -1929,11 +1931,11 @@ private: > FormattedValue lowValue, Edge highValue, SpeculatedType typesPassedThrough, > LValue failCondition, SpeculationDirection direction, FormattedValue recovery) > { >- if (!m_state.needsTypeCheck(highValue, typesPassedThrough)) >+ if (!m_interpreter.needsTypeCheck(highValue, typesPassedThrough)) > return; > ASSERT(mayHaveTypeCheck(highValue.useKind())); > appendOSRExit(BadType, lowValue, highValue.node(), failCondition, direction, recovery); >- m_state.filter(highValue, typesPassedThrough); >+ m_interpreter.filter(highValue, typesPassedThrough); > } > > LValue lowInt32(Edge edge, OperandSpeculationMode mode = AutomaticOperandSpeculation) >@@ -2178,7 +2180,7 @@ private: > break; > case KnownInt32Use: > case KnownNumberUse: >- ASSERT(!m_state.needsTypeCheck(edge)); >+ ASSERT(!m_interpreter.needsTypeCheck(edge)); > break; > case Int32Use: > speculateInt32(edge); >@@ -2187,7 +2189,7 @@ private: > speculateCell(edge); > break; > case KnownCellUse: >- ASSERT(!m_state.needsTypeCheck(edge)); >+ ASSERT(!m_interpreter.needsTypeCheck(edge)); > break; > case ObjectUse: > speculateObject(edge); >@@ -2287,7 +2289,7 @@ private: > void speculateNumber(Edge edge) > { > // Do an early return here because lowDouble() can create a lot of control flow. >- if (!m_state.needsTypeCheck(edge)) >+ if (!m_interpreter.needsTypeCheck(edge)) > return; > > lowDouble(edge); >@@ -2296,7 +2298,7 @@ private: > void speculateRealNumber(Edge edge) > { > // Do an early return here because lowDouble() can create a lot of control flow. >- if (!m_state.needsTypeCheck(edge)) >+ if (!m_interpreter.needsTypeCheck(edge)) > return; > > LValue value = lowDouble(edge); >@@ -2862,7 +2864,8 @@ private: > int m_lastSetOperand; > ExitThunkGenerator m_exitThunkGenerator; > >- AbstractState m_state; >+ InPlaceAbstractState m_state; >+ AbstractInterpreter<InPlaceAbstractState> m_interpreter; > BasicBlock* m_highBlock; > BasicBlock* m_nextHighBlock; > LBasicBlock m_nextLowBlock;
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
Flags:
oliver
:
review+
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 118835
:
206952
| 207031