Title: [219898] trunk/Source
Revision
219898
Author
fpi...@apple.com
Date
2017-07-25 18:58:36 -0700 (Tue, 25 Jul 2017)

Log Message

B3 should do LICM
https://bugs.webkit.org/show_bug.cgi?id=174750

Reviewed by Keith Miller and Saam Barati.
        
Source/_javascript_Core:

Added a LICM phase to B3. This phase is called hoistLoopInvariantValues, to conform to the B3 naming
convention for phases (it has to be an imperative). The phase uses NaturalLoops and BackwardsDominators,
so this adds those analyses to B3. BackwardsDominators was already available in templatized form. This
change templatizes DFG::NaturalLoops so that we can just use it.
        
The LICM phase itself is really simple. We are decently precise with our handling of everything except
the relationship between control dependence and side exits.
        
Also added a bunch of tests.
        
This isn't super important. It's perf-neutral on JS benchmarks. FTL already does LICM on DFG SSA IR, and
probably all current WebAssembly content has had LICM done to it. That being said, this is a cheap phase
so it doesn't hurt to have it.
        
I wrote it because I thought I needed it for bug 174727. It turns out that there's a better way to
handle the problem I had, so I ended up not needed it - but by then I had already written it. I think
it's good to have it because LICM is one of those core compiler phases; every compiler has it
eventually.

* CMakeLists.txt:
* _javascript_Core.xcodeproj/project.pbxproj:
* b3/B3BackwardsCFG.h: Added.
(JSC::B3::BackwardsCFG::BackwardsCFG):
* b3/B3BackwardsDominators.h: Added.
(JSC::B3::BackwardsDominators::BackwardsDominators):
* b3/B3BasicBlock.cpp:
(JSC::B3::BasicBlock::appendNonTerminal):
* b3/B3Effects.h:
* b3/B3EnsureLoopPreHeaders.cpp: Added.
(JSC::B3::ensureLoopPreHeaders):
* b3/B3EnsureLoopPreHeaders.h: Added.
* b3/B3Generate.cpp:
(JSC::B3::generateToAir):
* b3/B3HoistLoopInvariantValues.cpp: Added.
(JSC::B3::hoistLoopInvariantValues):
* b3/B3HoistLoopInvariantValues.h: Added.
* b3/B3NaturalLoops.h: Added.
(JSC::B3::NaturalLoops::NaturalLoops):
* b3/B3Procedure.cpp:
(JSC::B3::Procedure::invalidateCFG):
(JSC::B3::Procedure::naturalLoops):
(JSC::B3::Procedure::backwardsCFG):
(JSC::B3::Procedure::backwardsDominators):
* b3/B3Procedure.h:
* b3/testb3.cpp:
(JSC::B3::generateLoop):
(JSC::B3::makeArrayForLoops):
(JSC::B3::generateLoopNotBackwardsDominant):
(JSC::B3::oneFunction):
(JSC::B3::noOpFunction):
(JSC::B3::testLICMPure):
(JSC::B3::testLICMPureSideExits):
(JSC::B3::testLICMPureWritesPinned):
(JSC::B3::testLICMPureWrites):
(JSC::B3::testLICMReadsLocalState):
(JSC::B3::testLICMReadsPinned):
(JSC::B3::testLICMReads):
(JSC::B3::testLICMPureNotBackwardsDominant):
(JSC::B3::testLICMPureFoiledByChild):
(JSC::B3::testLICMPureNotBackwardsDominantFoiledByChild):
(JSC::B3::testLICMExitsSideways):
(JSC::B3::testLICMWritesLocalState):
(JSC::B3::testLICMWrites):
(JSC::B3::testLICMFence):
(JSC::B3::testLICMWritesPinned):
(JSC::B3::testLICMControlDependent):
(JSC::B3::testLICMControlDependentNotBackwardsDominant):
(JSC::B3::testLICMControlDependentSideExits):
(JSC::B3::testLICMReadsPinnedWritesPinned):
(JSC::B3::testLICMReadsWritesDifferentHeaps):
(JSC::B3::testLICMReadsWritesOverlappingHeaps):
(JSC::B3::testLICMDefaultCall):
(JSC::B3::run):
* dfg/DFGBasicBlock.h:
* dfg/DFGCFG.h:
* dfg/DFGNaturalLoops.cpp: Removed.
* dfg/DFGNaturalLoops.h:
(JSC::DFG::NaturalLoops::NaturalLoops):
(JSC::DFG::NaturalLoop::NaturalLoop): Deleted.
(JSC::DFG::NaturalLoop::header): Deleted.
(JSC::DFG::NaturalLoop::size): Deleted.
(JSC::DFG::NaturalLoop::at): Deleted.
(JSC::DFG::NaturalLoop::operator[]): Deleted.
(JSC::DFG::NaturalLoop::contains): Deleted.
(JSC::DFG::NaturalLoop::index): Deleted.
(JSC::DFG::NaturalLoop::isOuterMostLoop): Deleted.
(JSC::DFG::NaturalLoop::addBlock): Deleted.
(JSC::DFG::NaturalLoops::numLoops): Deleted.
(JSC::DFG::NaturalLoops::loop): Deleted.
(JSC::DFG::NaturalLoops::headerOf): Deleted.
(JSC::DFG::NaturalLoops::innerMostLoopOf): Deleted.
(JSC::DFG::NaturalLoops::innerMostOuterLoop): Deleted.
(JSC::DFG::NaturalLoops::belongsTo): Deleted.
(JSC::DFG::NaturalLoops::loopDepth): Deleted.

Source/WTF:

Moved DFG::NaturalLoops to WTF. The new templatized NaturalLoops<> uses the same Graph abstraction as
Dominators<>. This allows us to add a B3::NaturalLoops for free.
        
Also made small tweaks to RangeSet, which the LICM uses.

* WTF.xcodeproj/project.pbxproj:
* wtf/CMakeLists.txt:
* wtf/Dominators.h:
* wtf/NaturalLoops.h: Added.
(WTF::NaturalLoop::NaturalLoop):
(WTF::NaturalLoop::graph):
(WTF::NaturalLoop::header):
(WTF::NaturalLoop::size):
(WTF::NaturalLoop::at):
(WTF::NaturalLoop::operator[]):
(WTF::NaturalLoop::contains):
(WTF::NaturalLoop::index):
(WTF::NaturalLoop::isOuterMostLoop):
(WTF::NaturalLoop::dump):
(WTF::NaturalLoop::addBlock):
(WTF::NaturalLoops::NaturalLoops):
(WTF::NaturalLoops::graph):
(WTF::NaturalLoops::numLoops):
(WTF::NaturalLoops::loop):
(WTF::NaturalLoops::headerOf):
(WTF::NaturalLoops::innerMostLoopOf):
(WTF::NaturalLoops::innerMostOuterLoop):
(WTF::NaturalLoops::belongsTo):
(WTF::NaturalLoops::loopDepth):
(WTF::NaturalLoops::loopsOf):
(WTF::NaturalLoops::dump):
* wtf/RangeSet.h:
(WTF::RangeSet::begin):
(WTF::RangeSet::end):
(WTF::RangeSet::addAll):

Modified Paths

Added Paths

Removed Paths

Diff

Modified: trunk/Source/_javascript_Core/CMakeLists.txt (219897 => 219898)


--- trunk/Source/_javascript_Core/CMakeLists.txt	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/CMakeLists.txt	2017-07-26 01:58:36 UTC (rev 219898)
@@ -143,11 +143,13 @@
     b3/B3DuplicateTails.cpp
     b3/B3Effects.cpp
     b3/B3EliminateCommonSubexpressions.cpp
+    b3/B3EnsureLoopPreHeaders.cpp
     b3/B3FenceValue.cpp
     b3/B3FixSSA.cpp
     b3/B3FoldPathConstants.cpp
     b3/B3FrequencyClass.cpp
     b3/B3Generate.cpp
+    b3/B3HoistLoopInvariantValues.cpp
     b3/B3InferSwitches.cpp
     b3/B3InsertionSet.cpp
     b3/B3Kind.cpp
@@ -364,7 +366,6 @@
     dfg/DFGMinifiedNode.cpp
     dfg/DFGMovHintRemovalPhase.cpp
     dfg/DFGMultiGetByOffsetData.cpp
-    dfg/DFGNaturalLoops.cpp
     dfg/DFGNode.cpp
     dfg/DFGNodeAbstractValuePair.cpp
     dfg/DFGNodeFlags.cpp

Modified: trunk/Source/_javascript_Core/ChangeLog (219897 => 219898)


--- trunk/Source/_javascript_Core/ChangeLog	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/ChangeLog	2017-07-26 01:58:36 UTC (rev 219898)
@@ -1,3 +1,105 @@
+2017-07-23  Filip Pizlo  <fpi...@apple.com>
+
+        B3 should do LICM
+        https://bugs.webkit.org/show_bug.cgi?id=174750
+
+        Reviewed by Keith Miller and Saam Barati.
+        
+        Added a LICM phase to B3. This phase is called hoistLoopInvariantValues, to conform to the B3 naming
+        convention for phases (it has to be an imperative). The phase uses NaturalLoops and BackwardsDominators,
+        so this adds those analyses to B3. BackwardsDominators was already available in templatized form. This
+        change templatizes DFG::NaturalLoops so that we can just use it.
+        
+        The LICM phase itself is really simple. We are decently precise with our handling of everything except
+        the relationship between control dependence and side exits.
+        
+        Also added a bunch of tests.
+        
+        This isn't super important. It's perf-neutral on JS benchmarks. FTL already does LICM on DFG SSA IR, and
+        probably all current WebAssembly content has had LICM done to it. That being said, this is a cheap phase
+        so it doesn't hurt to have it.
+        
+        I wrote it because I thought I needed it for bug 174727. It turns out that there's a better way to
+        handle the problem I had, so I ended up not needed it - but by then I had already written it. I think
+        it's good to have it because LICM is one of those core compiler phases; every compiler has it
+        eventually.
+
+        * CMakeLists.txt:
+        * _javascript_Core.xcodeproj/project.pbxproj:
+        * b3/B3BackwardsCFG.h: Added.
+        (JSC::B3::BackwardsCFG::BackwardsCFG):
+        * b3/B3BackwardsDominators.h: Added.
+        (JSC::B3::BackwardsDominators::BackwardsDominators):
+        * b3/B3BasicBlock.cpp:
+        (JSC::B3::BasicBlock::appendNonTerminal):
+        * b3/B3Effects.h:
+        * b3/B3EnsureLoopPreHeaders.cpp: Added.
+        (JSC::B3::ensureLoopPreHeaders):
+        * b3/B3EnsureLoopPreHeaders.h: Added.
+        * b3/B3Generate.cpp:
+        (JSC::B3::generateToAir):
+        * b3/B3HoistLoopInvariantValues.cpp: Added.
+        (JSC::B3::hoistLoopInvariantValues):
+        * b3/B3HoistLoopInvariantValues.h: Added.
+        * b3/B3NaturalLoops.h: Added.
+        (JSC::B3::NaturalLoops::NaturalLoops):
+        * b3/B3Procedure.cpp:
+        (JSC::B3::Procedure::invalidateCFG):
+        (JSC::B3::Procedure::naturalLoops):
+        (JSC::B3::Procedure::backwardsCFG):
+        (JSC::B3::Procedure::backwardsDominators):
+        * b3/B3Procedure.h:
+        * b3/testb3.cpp:
+        (JSC::B3::generateLoop):
+        (JSC::B3::makeArrayForLoops):
+        (JSC::B3::generateLoopNotBackwardsDominant):
+        (JSC::B3::oneFunction):
+        (JSC::B3::noOpFunction):
+        (JSC::B3::testLICMPure):
+        (JSC::B3::testLICMPureSideExits):
+        (JSC::B3::testLICMPureWritesPinned):
+        (JSC::B3::testLICMPureWrites):
+        (JSC::B3::testLICMReadsLocalState):
+        (JSC::B3::testLICMReadsPinned):
+        (JSC::B3::testLICMReads):
+        (JSC::B3::testLICMPureNotBackwardsDominant):
+        (JSC::B3::testLICMPureFoiledByChild):
+        (JSC::B3::testLICMPureNotBackwardsDominantFoiledByChild):
+        (JSC::B3::testLICMExitsSideways):
+        (JSC::B3::testLICMWritesLocalState):
+        (JSC::B3::testLICMWrites):
+        (JSC::B3::testLICMFence):
+        (JSC::B3::testLICMWritesPinned):
+        (JSC::B3::testLICMControlDependent):
+        (JSC::B3::testLICMControlDependentNotBackwardsDominant):
+        (JSC::B3::testLICMControlDependentSideExits):
+        (JSC::B3::testLICMReadsPinnedWritesPinned):
+        (JSC::B3::testLICMReadsWritesDifferentHeaps):
+        (JSC::B3::testLICMReadsWritesOverlappingHeaps):
+        (JSC::B3::testLICMDefaultCall):
+        (JSC::B3::run):
+        * dfg/DFGBasicBlock.h:
+        * dfg/DFGCFG.h:
+        * dfg/DFGNaturalLoops.cpp: Removed.
+        * dfg/DFGNaturalLoops.h:
+        (JSC::DFG::NaturalLoops::NaturalLoops):
+        (JSC::DFG::NaturalLoop::NaturalLoop): Deleted.
+        (JSC::DFG::NaturalLoop::header): Deleted.
+        (JSC::DFG::NaturalLoop::size): Deleted.
+        (JSC::DFG::NaturalLoop::at): Deleted.
+        (JSC::DFG::NaturalLoop::operator[]): Deleted.
+        (JSC::DFG::NaturalLoop::contains): Deleted.
+        (JSC::DFG::NaturalLoop::index): Deleted.
+        (JSC::DFG::NaturalLoop::isOuterMostLoop): Deleted.
+        (JSC::DFG::NaturalLoop::addBlock): Deleted.
+        (JSC::DFG::NaturalLoops::numLoops): Deleted.
+        (JSC::DFG::NaturalLoops::loop): Deleted.
+        (JSC::DFG::NaturalLoops::headerOf): Deleted.
+        (JSC::DFG::NaturalLoops::innerMostLoopOf): Deleted.
+        (JSC::DFG::NaturalLoops::innerMostOuterLoop): Deleted.
+        (JSC::DFG::NaturalLoops::belongsTo): Deleted.
+        (JSC::DFG::NaturalLoops::loopDepth): Deleted.
+
 2017-07-24  Filip Pizlo  <fpi...@apple.com>
 
         GC should be fine with trading blocks between destructor and non-destructor blocks

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (219897 => 219898)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2017-07-26 01:58:36 UTC (rev 219898)
@@ -464,6 +464,13 @@
 		0F5A6284188C98D40072C9DF /* FTLValueRange.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5A6282188C98D40072C9DF /* FTLValueRange.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		0F5AE2C41DF4F2800066EFE1 /* VMInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = FE90BB3A1B7CF64E006B3F03 /* VMInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		0F5B4A331C84F0D600F1B17E /* SlowPathReturnType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5B4A321C84F0D600F1B17E /* SlowPathReturnType.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		0F5BF1631F2317120029D91D /* B3HoistLoopInvariantValues.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F5BF1611F2317120029D91D /* B3HoistLoopInvariantValues.cpp */; };
+		0F5BF1641F2317120029D91D /* B3HoistLoopInvariantValues.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5BF1621F2317120029D91D /* B3HoistLoopInvariantValues.h */; };
+		0F5BF1671F23A0980029D91D /* B3BackwardsCFG.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5BF1661F23A0980029D91D /* B3BackwardsCFG.h */; };
+		0F5BF1691F23A0AA0029D91D /* B3NaturalLoops.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5BF1681F23A0AA0029D91D /* B3NaturalLoops.h */; };
+		0F5BF16B1F23A0C10029D91D /* B3BackwardsDominators.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5BF16A1F23A0C10029D91D /* B3BackwardsDominators.h */; };
+		0F5BF1701F23A5A10029D91D /* B3EnsureLoopPreHeaders.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F5BF16E1F23A5A10029D91D /* B3EnsureLoopPreHeaders.cpp */; };
+		0F5BF1711F23A5A10029D91D /* B3EnsureLoopPreHeaders.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5BF16F1F23A5A10029D91D /* B3EnsureLoopPreHeaders.h */; };
 		0F5CF9811E96F17F00C18692 /* AirTmpMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5CF9801E96F17D00C18692 /* AirTmpMap.h */; };
 		0F5CF9841E9D537700C18692 /* AirLowerStackArgs.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F5CF9831E9D537500C18692 /* AirLowerStackArgs.h */; };
 		0F5CF9851E9D537A00C18692 /* AirLowerStackArgs.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F5CF9821E9D537500C18692 /* AirLowerStackArgs.cpp */; };
@@ -1950,7 +1957,6 @@
 		A730B6121250068F009D25B1 /* StrictEvalActivation.h in Headers */ = {isa = PBXBuildFile; fileRef = A730B6101250068F009D25B1 /* StrictEvalActivation.h */; };
 		A730B6131250068F009D25B1 /* StrictEvalActivation.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A730B6111250068F009D25B1 /* StrictEvalActivation.cpp */; };
 		A731B25A130093880040A7FA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 51F0EB6105C86C6B00E6DF1B /* Foundation.framework */; };
-		A737810D1799EA2E00817533 /* DFGNaturalLoops.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A737810A1799EA2E00817533 /* DFGNaturalLoops.cpp */; };
 		A737810E1799EA2E00817533 /* DFGNaturalLoops.h in Headers */ = {isa = PBXBuildFile; fileRef = A737810B1799EA2E00817533 /* DFGNaturalLoops.h */; };
 		A7386554118697B400540279 /* SpecializedThunkJIT.h in Headers */ = {isa = PBXBuildFile; fileRef = A7386551118697B400540279 /* SpecializedThunkJIT.h */; };
 		A7386555118697B400540279 /* ThunkGenerators.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A7386552118697B400540279 /* ThunkGenerators.cpp */; };
@@ -3040,6 +3046,13 @@
 		0F5A6281188C98D40072C9DF /* FTLValueRange.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLValueRange.cpp; path = ftl/FTLValueRange.cpp; sourceTree = "<group>"; };
 		0F5A6282188C98D40072C9DF /* FTLValueRange.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLValueRange.h; path = ftl/FTLValueRange.h; sourceTree = "<group>"; };
 		0F5B4A321C84F0D600F1B17E /* SlowPathReturnType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SlowPathReturnType.h; sourceTree = "<group>"; };
+		0F5BF1611F2317120029D91D /* B3HoistLoopInvariantValues.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = B3HoistLoopInvariantValues.cpp; path = b3/B3HoistLoopInvariantValues.cpp; sourceTree = "<group>"; };
+		0F5BF1621F2317120029D91D /* B3HoistLoopInvariantValues.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = B3HoistLoopInvariantValues.h; path = b3/B3HoistLoopInvariantValues.h; sourceTree = "<group>"; };
+		0F5BF1661F23A0980029D91D /* B3BackwardsCFG.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = B3BackwardsCFG.h; path = b3/B3BackwardsCFG.h; sourceTree = "<group>"; };
+		0F5BF1681F23A0AA0029D91D /* B3NaturalLoops.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = B3NaturalLoops.h; path = b3/B3NaturalLoops.h; sourceTree = "<group>"; };
+		0F5BF16A1F23A0C10029D91D /* B3BackwardsDominators.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = B3BackwardsDominators.h; path = b3/B3BackwardsDominators.h; sourceTree = "<group>"; };
+		0F5BF16E1F23A5A10029D91D /* B3EnsureLoopPreHeaders.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = B3EnsureLoopPreHeaders.cpp; path = b3/B3EnsureLoopPreHeaders.cpp; sourceTree = "<group>"; };
+		0F5BF16F1F23A5A10029D91D /* B3EnsureLoopPreHeaders.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = B3EnsureLoopPreHeaders.h; path = b3/B3EnsureLoopPreHeaders.h; sourceTree = "<group>"; };
 		0F5CF9801E96F17D00C18692 /* AirTmpMap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirTmpMap.h; path = b3/air/AirTmpMap.h; sourceTree = "<group>"; };
 		0F5CF9821E9D537500C18692 /* AirLowerStackArgs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirLowerStackArgs.cpp; path = b3/air/AirLowerStackArgs.cpp; sourceTree = "<group>"; };
 		0F5CF9831E9D537500C18692 /* AirLowerStackArgs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirLowerStackArgs.h; path = b3/air/AirLowerStackArgs.h; sourceTree = "<group>"; };
@@ -4576,7 +4589,6 @@
 		A7299DA417D12858005F5FF9 /* SetConstructor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SetConstructor.h; sourceTree = "<group>"; };
 		A730B6101250068F009D25B1 /* StrictEvalActivation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StrictEvalActivation.h; sourceTree = "<group>"; };
 		A730B6111250068F009D25B1 /* StrictEvalActivation.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StrictEvalActivation.cpp; sourceTree = "<group>"; };
-		A737810A1799EA2E00817533 /* DFGNaturalLoops.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGNaturalLoops.cpp; path = dfg/DFGNaturalLoops.cpp; sourceTree = "<group>"; };
 		A737810B1799EA2E00817533 /* DFGNaturalLoops.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGNaturalLoops.h; path = dfg/DFGNaturalLoops.h; sourceTree = "<group>"; };
 		A7386551118697B400540279 /* SpecializedThunkJIT.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SpecializedThunkJIT.h; sourceTree = "<group>"; };
 		A7386552118697B400540279 /* ThunkGenerators.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ThunkGenerators.cpp; sourceTree = "<group>"; };
@@ -5481,6 +5493,8 @@
 				0FEC84B51BDACDAC0080FF74 /* B3ArgumentRegValue.h */,
 				0F2C63B31E6343E800C13839 /* B3AtomicValue.cpp */,
 				0F2C63B41E6343E800C13839 /* B3AtomicValue.h */,
+				0F5BF1661F23A0980029D91D /* B3BackwardsCFG.h */,
+				0F5BF16A1F23A0C10029D91D /* B3BackwardsDominators.h */,
 				0F2C63AB1E60AE3C00C13839 /* B3Bank.cpp */,
 				0F2C63AC1E60AE3C00C13839 /* B3Bank.h */,
 				0FEC84B61BDACDAC0080FF74 /* B3BasicBlock.cpp */,
@@ -5532,6 +5546,8 @@
 				0FEC85BE1BE167A00080FF74 /* B3Effects.h */,
 				0F725CA31C503DED00AD943A /* B3EliminateCommonSubexpressions.cpp */,
 				0F725CA41C503DED00AD943A /* B3EliminateCommonSubexpressions.h */,
+				0F5BF16E1F23A5A10029D91D /* B3EnsureLoopPreHeaders.cpp */,
+				0F5BF16F1F23A5A10029D91D /* B3EnsureLoopPreHeaders.h */,
 				0F6971E81D92F42100BA02A5 /* B3FenceValue.cpp */,
 				0F6971E91D92F42100BA02A5 /* B3FenceValue.h */,
 				0F6B8AE01C4EFE1700969052 /* B3FixSSA.cpp */,
@@ -5546,6 +5562,8 @@
 				0F2C63B51E6343E800C13839 /* B3GenericBlockInsertionSet.h */,
 				0FEC84D01BDACDAC0080FF74 /* B3GenericFrequentedBlock.h */,
 				0FEC85C01BE167A00080FF74 /* B3HeapRange.h */,
+				0F5BF1611F2317120029D91D /* B3HoistLoopInvariantValues.cpp */,
+				0F5BF1621F2317120029D91D /* B3HoistLoopInvariantValues.h */,
 				DC69B99A1D15F90F002E3C00 /* B3InferSwitches.cpp */,
 				DC69B99B1D15F90F002E3C00 /* B3InferSwitches.h */,
 				0FEC85B41BE1462F0080FF74 /* B3InsertionSet.cpp */,
@@ -5569,6 +5587,7 @@
 				0F338E031BF0276C0013C88F /* B3MoveConstants.cpp */,
 				0F338E041BF0276C0013C88F /* B3MoveConstants.h */,
 				0F2C63C11E664A5A00C13839 /* B3NativeTraits.h */,
+				0F5BF1681F23A0AA0029D91D /* B3NaturalLoops.h */,
 				0F338E051BF0276C0013C88F /* B3OpaqueByproduct.h */,
 				0F338E061BF0276C0013C88F /* B3OpaqueByproducts.cpp */,
 				0F338E071BF0276C0013C88F /* B3OpaqueByproducts.h */,
@@ -7393,7 +7412,6 @@
 				0F8F14321ADF090100ED792C /* DFGMovHintRemovalPhase.h */,
 				0FF2CD591B61A4F8004955A8 /* DFGMultiGetByOffsetData.cpp */,
 				0FF2CD5A1B61A4F8004955A8 /* DFGMultiGetByOffsetData.h */,
-				A737810A1799EA2E00817533 /* DFGNaturalLoops.cpp */,
 				A737810B1799EA2E00817533 /* DFGNaturalLoops.h */,
 				0FB4B51E16B62772003F696B /* DFGNode.cpp */,
 				86ECA3E9132DEF1C002B2AD7 /* DFGNode.h */,
@@ -8515,6 +8533,7 @@
 				0FBB73BB1DEF8645002C009E /* DeleteAllCodeEffort.h in Headers */,
 				0F96303C1D4192CD005609D9 /* DestructionMode.h in Headers */,
 				A77A423E17A0BBFD00A8DB81 /* DFGAbstractHeap.h in Headers */,
+				0F5BF1691F23A0AA0029D91D /* B3NaturalLoops.h in Headers */,
 				A704D90317A0BAA8006BA554 /* DFGAbstractInterpreter.h in Headers */,
 				A704D90417A0BAA8006BA554 /* DFGAbstractInterpreterInlines.h in Headers */,
 				0F620177143FCD3F0068B77C /* DFGAbstractValue.h in Headers */,
@@ -8699,6 +8718,7 @@
 				0F1FB3971E1AF7E300A9BE50 /* DFGWorklistInlines.h in Headers */,
 				0FE050181AA9091100D33B33 /* DirectArguments.h in Headers */,
 				0FE050161AA9091100D33B33 /* DirectArgumentsOffset.h in Headers */,
+				0F5BF1711F23A5A10029D91D /* B3EnsureLoopPreHeaders.h in Headers */,
 				969A07980ED1D3AE00F1F681 /* DirectEvalCodeCache.h in Headers */,
 				14386A751DD69895008652C4 /* DirectEvalExecutable.h in Headers */,
 				0F37308F1C0CD68500052BFA /* DisallowMacroScratchRegisterUsage.h in Headers */,
@@ -8883,10 +8903,12 @@
 				A5FD0076189B038C00633231 /* IdentifiersFactory.h in Headers */,
 				C25F8BCE157544A900245B71 /* IncrementalSweeper.h in Headers */,
 				0FB7F39915ED8E4600F167B2 /* IndexingHeader.h in Headers */,
+				0F5BF16B1F23A0C10029D91D /* B3BackwardsDominators.h in Headers */,
 				0FB7F39A15ED8E4600F167B2 /* IndexingHeaderInlines.h in Headers */,
 				0FB7F39B15ED8E4600F167B2 /* IndexingType.h in Headers */,
 				14386A791DD6989C008652C4 /* IndirectEvalExecutable.h in Headers */,
 				0F0A75231B94BFA900110660 /* InferredType.h in Headers */,
+				0F5BF1671F23A0980029D91D /* B3BackwardsCFG.h in Headers */,
 				0FFC92121B94D4DF0071DD66 /* InferredTypeTable.h in Headers */,
 				0FF8BDEB1AD4CF7100DFE884 /* InferredValue.h in Headers */,
 				BC18C4100E16F5CD00B34460 /* InitializeThreading.h in Headers */,
@@ -9190,6 +9212,7 @@
 				86C36EEA0EE1289D00B3DF59 /* MacroAssembler.h in Headers */,
 				86D3B2C610156BDE002865E7 /* MacroAssemblerARM.h in Headers */,
 				A1A009C01831A22D00CF8711 /* MacroAssemblerARM64.h in Headers */,
+				0F5BF1641F2317120029D91D /* B3HoistLoopInvariantValues.h in Headers */,
 				86ADD1460FDDEA980006EEC2 /* MacroAssemblerARMv7.h in Headers */,
 				863B23E00FC6118900703AA4 /* MacroAssemblerCodeRef.h in Headers */,
 				E32AB2441DCD75F400D7533A /* MacroAssemblerHelpers.h in Headers */,
@@ -10406,7 +10429,6 @@
 				0F2BDC4D1522818600CD8910 /* DFGMinifiedNode.cpp in Sources */,
 				0F8F14351ADF090100ED792C /* DFGMovHintRemovalPhase.cpp in Sources */,
 				0FF2CD5B1B61A4F8004955A8 /* DFGMultiGetByOffsetData.cpp in Sources */,
-				A737810D1799EA2E00817533 /* DFGNaturalLoops.cpp in Sources */,
 				0FF0F19C16B72A03005DF95B /* DFGNode.cpp in Sources */,
 				0F20178A1DCB942600EA5950 /* DFGNodeAbstractValuePair.cpp in Sources */,
 				0FA581BA150E952C00B9A2D9 /* DFGNodeFlags.cpp in Sources */,
@@ -10768,6 +10790,7 @@
 				AD2FCBE81DB58DAD00B3E736 /* JSWebAssemblyRuntimeError.cpp in Sources */,
 				AD2FCBEA1DB58DAD00B3E736 /* JSWebAssemblyTable.cpp in Sources */,
 				1442566115EDE98D0066A49B /* JSWithScope.cpp in Sources */,
+				0F5BF1631F2317120029D91D /* B3HoistLoopInvariantValues.cpp in Sources */,
 				86E3C618167BABEE006D760A /* JSWrapperMap.mm in Sources */,
 				14280870107EC1340013E7B2 /* JSWrapperObject.cpp in Sources */,
 				BCFD8C920EEB2EE700283848 /* JumpTable.cpp in Sources */,
@@ -10834,6 +10857,7 @@
 				0FD3E40B1B618B6600C80E1E /* ObjectPropertyConditionSet.cpp in Sources */,
 				14469DE6107EC7E700650446 /* ObjectPrototype.cpp in Sources */,
 				E124A8F80E555775003091F1 /* OpaqueJSString.cpp in Sources */,
+				0F5BF1701F23A5A10029D91D /* B3EnsureLoopPreHeaders.cpp in Sources */,
 				969A079A0ED1D3AE00F1F681 /* Opcode.cpp in Sources */,
 				14280850107EC0D70013E7B2 /* Operations.cpp in Sources */,
 				0FE228EE1436AB2C00196C48 /* Options.cpp in Sources */,

Added: trunk/Source/_javascript_Core/b3/B3BackwardsCFG.h (0 => 219898)


--- trunk/Source/_javascript_Core/b3/B3BackwardsCFG.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/b3/B3BackwardsCFG.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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. 
+ */
+
+#pragma once
+
+#if ENABLE(B3_JIT)
+
+#include "B3CFG.h"
+#include <wtf/BackwardsGraph.h>
+
+namespace JSC { namespace B3 {
+
+class BackwardsCFG : public BackwardsGraph<CFG> {
+    WTF_MAKE_NONCOPYABLE(BackwardsCFG);
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    BackwardsCFG(Procedure& proc)
+        : BackwardsGraph<CFG>(proc.cfg())
+    {
+    }
+};
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+

Added: trunk/Source/_javascript_Core/b3/B3BackwardsDominators.h (0 => 219898)


--- trunk/Source/_javascript_Core/b3/B3BackwardsDominators.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/b3/B3BackwardsDominators.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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. 
+ */
+
+#pragma once
+
+#if ENABLE(B3_JIT)
+
+#include "B3BackwardsCFG.h"
+#include <wtf/Dominators.h>
+
+namespace JSC { namespace B3 {
+
+class BackwardsDominators : public WTF::Dominators<BackwardsCFG> {
+    WTF_MAKE_NONCOPYABLE(BackwardsDominators);
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    BackwardsDominators(Procedure& proc)
+        : WTF::Dominators<BackwardsCFG>(proc.backwardsCFG())
+    {
+    }
+};
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+

Modified: trunk/Source/_javascript_Core/b3/B3BasicBlock.cpp (219897 => 219898)


--- trunk/Source/_javascript_Core/b3/B3BasicBlock.cpp	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/b3/B3BasicBlock.cpp	2017-07-26 01:58:36 UTC (rev 219898)
@@ -57,7 +57,7 @@
 void BasicBlock::appendNonTerminal(Value* value)
 {
     m_values.append(m_values.last());
-    m_values[m_values.size() - 1] = value;
+    m_values[m_values.size() - 2] = value;
     value->owner = this;
 }
 

Modified: trunk/Source/_javascript_Core/b3/B3Effects.h (219897 => 219898)


--- trunk/Source/_javascript_Core/b3/B3Effects.h	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/b3/B3Effects.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -75,6 +75,9 @@
     // Memory fences cannot be reordered around each other regardless of their effects. This is flagged
     // if the operation is a memory fence.
     bool fence { false };
+    
+    // WARNING: The B3::hoistLoopInvariantValues() phase thinks that it understands this exhaustively. If you
+    // add any new kinds of things that can be read or written, you should check that phase.
 
     HeapRange writes;
     HeapRange reads;

Added: trunk/Source/_javascript_Core/b3/B3EnsureLoopPreHeaders.cpp (0 => 219898)


--- trunk/Source/_javascript_Core/b3/B3EnsureLoopPreHeaders.cpp	                        (rev 0)
+++ trunk/Source/_javascript_Core/b3/B3EnsureLoopPreHeaders.cpp	2017-07-26 01:58:36 UTC (rev 219898)
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 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 "B3EnsureLoopPreHeaders.h"
+
+#if ENABLE(B3_JIT)
+
+#include "B3BasicBlockInlines.h"
+#include "B3BlockInsertionSet.h"
+#include "B3NaturalLoops.h"
+#include "B3ProcedureInlines.h"
+#include "B3ValueInlines.h"
+
+namespace JSC { namespace B3 {
+
+bool ensureLoopPreHeaders(Procedure& proc)
+{
+    NaturalLoops& loops = proc.naturalLoops();
+    
+    BlockInsertionSet insertionSet(proc);
+    
+    for (unsigned loopIndex = loops.numLoops(); loopIndex--;) {
+        const NaturalLoop& loop = loops.loop(loopIndex);
+        
+        Vector<BasicBlock*, 4> outOfBodyPredecessors;
+        double totalFrequency = 0;
+        for (BasicBlock* predecessor : loop.header()->predecessors()) {
+            if (loops.belongsTo(predecessor, loop))
+                continue;
+            
+            outOfBodyPredecessors.append(predecessor);
+            totalFrequency += predecessor->frequency();
+        }
+        
+        if (outOfBodyPredecessors.size() <= 1)
+            continue;
+        
+        BasicBlock* preHeader = insertionSet.insertBefore(loop.header(), totalFrequency);
+        preHeader->appendNew<Value>(proc, Jump, loop.header()->at(0)->origin());
+        preHeader->setSuccessors(FrequentedBlock(loop.header()));
+        
+        for (BasicBlock* predecessor : outOfBodyPredecessors) {
+            predecessor->replaceSuccessor(loop.header(), preHeader);
+            preHeader->addPredecessor(predecessor);
+            loop.header()->removePredecessor(predecessor);
+        }
+        
+        loop.header()->addPredecessor(preHeader);
+    }
+    
+    if (insertionSet.execute()) {
+        proc.invalidateCFG();
+        return true;
+    }
+    
+    return false;
+}
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+

Added: trunk/Source/_javascript_Core/b3/B3EnsureLoopPreHeaders.h (0 => 219898)


--- trunk/Source/_javascript_Core/b3/B3EnsureLoopPreHeaders.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/b3/B3EnsureLoopPreHeaders.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 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. 
+ */
+
+#pragma once
+
+#if ENABLE(B3_JIT)
+
+namespace JSC { namespace B3 {
+
+class Procedure;
+
+// Creates a pre-header for any loop that don't already have one. A loop has a pre-header if its header has
+// exactly one predecessor that isn't in the loop body. If a loop header has more than one out-of-body
+// predecessor, then this creates a new block and rewires those predecessors to it.
+
+bool ensureLoopPreHeaders(Procedure&);
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)

Modified: trunk/Source/_javascript_Core/b3/B3Generate.cpp (219897 => 219898)


--- trunk/Source/_javascript_Core/b3/B3Generate.cpp	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/b3/B3Generate.cpp	2017-07-26 01:58:36 UTC (rev 219898)
@@ -36,6 +36,7 @@
 #include "B3EliminateCommonSubexpressions.h"
 #include "B3FixSSA.h"
 #include "B3FoldPathConstants.h"
+#include "B3HoistLoopInvariantValues.h"
 #include "B3InferSwitches.h"
 #include "B3LegalizeMemoryOffsets.h"
 #include "B3LowerMacros.h"
@@ -83,6 +84,7 @@
     if (procedure.optLevel() >= 2) {
         reduceDoubleToFloat(procedure);
         reduceStrength(procedure);
+        hoistLoopInvariantValues(procedure);
         eliminateCommonSubexpressions(procedure);
         inferSwitches(procedure);
         duplicateTails(procedure);

Added: trunk/Source/_javascript_Core/b3/B3HoistLoopInvariantValues.cpp (0 => 219898)


--- trunk/Source/_javascript_Core/b3/B3HoistLoopInvariantValues.cpp	                        (rev 0)
+++ trunk/Source/_javascript_Core/b3/B3HoistLoopInvariantValues.cpp	2017-07-26 01:58:36 UTC (rev 219898)
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2017 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 "B3HoistLoopInvariantValues.h"
+
+#if ENABLE(B3_JIT)
+
+#include "B3BackwardsDominators.h"
+#include "B3BasicBlockInlines.h"
+#include "B3Dominators.h"
+#include "B3EnsureLoopPreHeaders.h"
+#include "B3NaturalLoops.h"
+#include "B3PhaseScope.h"
+#include "B3ProcedureInlines.h"
+#include "B3ValueInlines.h"
+#include <wtf/RangeSet.h>
+
+namespace JSC { namespace B3 {
+
+bool hoistLoopInvariantValues(Procedure& proc)
+{
+    PhaseScope phaseScope(proc, "hoistLoopInvariantValues");
+    
+    ensureLoopPreHeaders(proc);
+    
+    NaturalLoops& loops = proc.naturalLoops();
+    if (!loops.numLoops())
+        return false;
+
+    proc.resetValueOwners();
+    Dominators& dominators = proc.dominators();
+    BackwardsDominators& backwardsDominators = proc.backwardsDominators();
+    
+    // FIXME: We should have a reusable B3::EffectsSet data structure.
+    // https://bugs.webkit.org/show_bug.cgi?id=174762
+    struct LoopData {
+        RangeSet<HeapRange> writes;
+        bool writesLocalState { false };
+        bool writesPinned { false };
+        bool sideExits { false };
+        BasicBlock* preHeader { nullptr };
+    };
+    
+    IndexMap<NaturalLoop, LoopData> data(loops.numLoops());
+    
+    for (unsigned loopIndex = loops.numLoops(); loopIndex--;) {
+        const NaturalLoop& loop = loops.loop(loopIndex);
+        for (BasicBlock* predecessor : loop.header()->predecessors()) {
+            if (loops.belongsTo(predecessor, loop))
+                continue;
+            RELEASE_ASSERT(!data[loop].preHeader);
+            data[loop].preHeader = predecessor;
+        }
+    }
+    
+    for (BasicBlock* block : proc) {
+        const NaturalLoop* loop = loops.innerMostLoopOf(block);
+        if (!loop)
+            continue;
+        for (Value* value : *block) {
+            Effects effects = value->effects();
+            data[*loop].writes.add(effects.writes);
+            data[*loop].writesLocalState |= effects.writesLocalState;
+            data[*loop].writesPinned |= effects.writesPinned;
+            data[*loop].sideExits |= effects.exitsSideways;
+        }
+    }
+    
+    for (unsigned loopIndex = loops.numLoops(); loopIndex--;) {
+        const NaturalLoop& loop = loops.loop(loopIndex);
+        for (const NaturalLoop* current = loops.innerMostOuterLoop(loop); current; current = loops.innerMostOuterLoop(*current)) {
+            data[*current].writes.addAll(data[loop].writes);
+            data[*current].writesLocalState |= data[loop].writesLocalState;
+            data[*current].writesPinned |= data[loop].writesPinned;
+            data[*current].sideExits |= data[loop].sideExits;
+        }
+    }
+    
+    bool changed = false;
+    
+    // Pre-order ensures that we visit our dominators before we visit ourselves. Otherwise we'd miss some
+    // hoisting opportunities in complex CFGs.
+    for (BasicBlock* block : proc.blocksInPreOrder()) {
+        Vector<const NaturalLoop*> blockLoops = loops.loopsOf(block);
+        if (blockLoops.isEmpty())
+            continue;
+        
+        for (Value*& value : *block) {
+            Effects effects = value->effects();
+            
+            // We never hoist write effects or control constructs.
+            if (effects.mustExecute())
+                continue;
+
+            // Try outermost loop first.
+            for (unsigned i = blockLoops.size(); i--;) {
+                const NaturalLoop& loop = *blockLoops[i];
+                
+                bool ok = true;
+                for (Value* child : value->children()) {
+                    if (!dominators.dominates(child->owner, data[loop].preHeader)) {
+                        ok = false;
+                        break;
+                    }
+                }
+                if (!ok)
+                    continue;
+                
+                if (effects.controlDependent) {
+                    if (!backwardsDominators.dominates(block, data[loop].preHeader))
+                        continue;
+                    
+                    // FIXME: This is super conservative. In reality, we just need to make sure that there
+                    // aren't any side exits between here and the pre-header according to backwards search.
+                    // https://bugs.webkit.org/show_bug.cgi?id=174763
+                    if (data[loop].sideExits)
+                        continue;
+                }
+                
+                if (effects.readsPinned && data[loop].writesPinned)
+                    continue;
+                
+                if (effects.readsLocalState && data[loop].writesLocalState)
+                    continue;
+                
+                if (data[loop].writes.overlaps(effects.reads))
+                    continue;
+                
+                data[loop].preHeader->appendNonTerminal(value);
+                value = proc.add<Value>(Nop, Void, value->origin());
+                changed = true;
+            }
+        }
+    }
+    
+    return changed;
+}
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+

Added: trunk/Source/_javascript_Core/b3/B3HoistLoopInvariantValues.h (0 => 219898)


--- trunk/Source/_javascript_Core/b3/B3HoistLoopInvariantValues.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/b3/B3HoistLoopInvariantValues.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2017 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. 
+ */
+
+#pragma once
+
+#if ENABLE(B3_JIT)
+
+namespace JSC { namespace B3 {
+
+class Procedure;
+
+// This does loop-invariant code motion (LICM). It can hoist pure values as well as reads of state that isn't
+// modified inside the loop.
+
+bool hoistLoopInvariantValues(Procedure&);
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)

Added: trunk/Source/_javascript_Core/b3/B3NaturalLoops.h (0 => 219898)


--- trunk/Source/_javascript_Core/b3/B3NaturalLoops.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/b3/B3NaturalLoops.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 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. 
+ */
+
+#pragma once
+
+#if ENABLE(B3_JIT)
+
+#include "B3Dominators.h"
+#include <wtf/NaturalLoops.h>
+
+namespace JSC { namespace B3 {
+
+typedef WTF::NaturalLoop<CFG> NaturalLoop;
+
+class NaturalLoops : public WTF::NaturalLoops<CFG> {
+    WTF_MAKE_NONCOPYABLE(NaturalLoops);
+    WTF_MAKE_FAST_ALLOCATED;
+public:
+    NaturalLoops(Procedure& proc)
+        : WTF::NaturalLoops<CFG>(proc.cfg(), proc.dominators())
+    {
+    }
+};
+
+} } // namespace JSC::B3
+
+#endif // ENABLE(B3_JIT)
+

Modified: trunk/Source/_javascript_Core/b3/B3Procedure.cpp (219897 => 219898)


--- trunk/Source/_javascript_Core/b3/B3Procedure.cpp	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/b3/B3Procedure.cpp	2017-07-26 01:58:36 UTC (rev 219898)
@@ -29,6 +29,8 @@
 #if ENABLE(B3_JIT)
 
 #include "AirCode.h"
+#include "B3BackwardsCFG.h"
+#include "B3BackwardsDominators.h"
 #include "B3BasicBlockInlines.h"
 #include "B3BasicBlockUtils.h"
 #include "B3BlockWorklist.h"
@@ -35,6 +37,7 @@
 #include "B3CFG.h"
 #include "B3DataSection.h"
 #include "B3Dominators.h"
+#include "B3NaturalLoops.h"
 #include "B3OpaqueByproducts.h"
 #include "B3PhiChildren.h"
 #include "B3StackSlot.h"
@@ -200,6 +203,9 @@
 void Procedure::invalidateCFG()
 {
     m_dominators = nullptr;
+    m_naturalLoops = nullptr;
+    m_backwardsCFG = nullptr;
+    m_backwardsDominators = nullptr;
 }
 
 void Procedure::dump(PrintStream& out) const
@@ -291,6 +297,27 @@
     return *m_dominators;
 }
 
+NaturalLoops& Procedure::naturalLoops()
+{
+    if (!m_naturalLoops)
+        m_naturalLoops = std::make_unique<NaturalLoops>(*this);
+    return *m_naturalLoops;
+}
+
+BackwardsCFG& Procedure::backwardsCFG()
+{
+    if (!m_backwardsCFG)
+        m_backwardsCFG = std::make_unique<BackwardsCFG>(*this);
+    return *m_backwardsCFG;
+}
+
+BackwardsDominators& Procedure::backwardsDominators()
+{
+    if (!m_backwardsDominators)
+        m_backwardsDominators = std::make_unique<BackwardsDominators>(*this);
+    return *m_backwardsDominators;
+}
+
 void Procedure::addFastConstant(const ValueKey& constant)
 {
     RELEASE_ASSERT(constant.isConstant());

Modified: trunk/Source/_javascript_Core/b3/B3Procedure.h (219897 => 219898)


--- trunk/Source/_javascript_Core/b3/B3Procedure.h	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/b3/B3Procedure.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -48,10 +48,13 @@
 
 namespace JSC { namespace B3 {
 
+class BackwardsCFG;
+class BackwardsDominators;
 class BasicBlock;
 class BlockInsertionSet;
 class CFG;
 class Dominators;
+class NaturalLoops;
 class StackSlot;
 class Value;
 class Variable;
@@ -175,6 +178,9 @@
     CFG& cfg() const { return *m_cfg; }
 
     Dominators& dominators();
+    NaturalLoops& naturalLoops();
+    BackwardsCFG& backwardsCFG();
+    BackwardsDominators& backwardsDominators();
 
     void addFastConstant(const ValueKey&);
     bool isFastConstant(const ValueKey&);
@@ -271,6 +277,9 @@
     SparseCollection<Value> m_values;
     std::unique_ptr<CFG> m_cfg;
     std::unique_ptr<Dominators> m_dominators;
+    std::unique_ptr<NaturalLoops> m_naturalLoops;
+    std::unique_ptr<BackwardsCFG> m_backwardsCFG;
+    std::unique_ptr<BackwardsDominators> m_backwardsDominators;
     HashSet<ValueKey> m_fastConstants;
     unsigned m_numEntrypoints { 1 };
     const char* m_lastPhaseName;

Modified: trunk/Source/_javascript_Core/b3/testb3.cpp (219897 => 219898)


--- trunk/Source/_javascript_Core/b3/testb3.cpp	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/b3/testb3.cpp	2017-07-26 01:58:36 UTC (rev 219898)
@@ -14592,6 +14592,559 @@
     CHECK(found);
 }
 
+template<typename Func>
+void generateLoop(Procedure& proc, const Func& func)
+{
+    BasicBlock* root = proc.addBlock();
+    BasicBlock* loop = proc.addBlock();
+    BasicBlock* end = proc.addBlock();
+
+    UpsilonValue* initialIndex = root->appendNew<UpsilonValue>(
+        proc, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
+    root->appendNew<Value>(proc, Jump, Origin());
+    root->setSuccessors(loop);
+    
+    Value* index = loop->appendNew<Value>(proc, Phi, Int32, Origin());
+    initialIndex->setPhi(index);
+    
+    Value* _one_ = func(loop, index);
+    
+    Value* nextIndex = loop->appendNew<Value>(proc, Add, Origin(), index, one);
+    UpsilonValue* loopIndex = loop->appendNew<UpsilonValue>(proc, Origin(), nextIndex);
+    loopIndex->setPhi(index);
+    loop->appendNew<Value>(
+        proc, Branch, Origin(),
+        loop->appendNew<Value>(
+            proc, LessThan, Origin(), nextIndex,
+            loop->appendNew<Const32Value>(proc, Origin(), 100)));
+    loop->setSuccessors(loop, end);
+    
+    end->appendNew<Value>(proc, Return, Origin());
+}
+
+std::array<int, 100> makeArrayForLoops()
+{
+    std::array<int, 100> result;
+    for (unsigned i = 0; i < result.size(); ++i)
+        result[i] = i & 1;
+    return result;
+}
+
+template<typename Func>
+void generateLoopNotBackwardsDominant(Procedure& proc, std::array<int, 100>& array, const Func& func)
+{
+    BasicBlock* root = proc.addBlock();
+    BasicBlock* loopHeader = proc.addBlock();
+    BasicBlock* loopCall = proc.addBlock();
+    BasicBlock* loopFooter = proc.addBlock();
+    BasicBlock* end = proc.addBlock();
+
+    UpsilonValue* initialIndex = root->appendNew<UpsilonValue>(
+        proc, Origin(), root->appendNew<Const32Value>(proc, Origin(), 0));
+    // If you look carefully, you'll notice that this is an extremely sneaky use of Upsilon that demonstrates
+    // the extent to which our SSA is different from normal-person SSA.
+    UpsilonValue* defaultOne = root->appendNew<UpsilonValue>(
+        proc, Origin(), root->appendNew<Const32Value>(proc, Origin(), 1));
+    root->appendNew<Value>(proc, Jump, Origin());
+    root->setSuccessors(loopHeader);
+    
+    Value* index = loopHeader->appendNew<Value>(proc, Phi, Int32, Origin());
+    initialIndex->setPhi(index);
+    
+    // if (array[index])
+    loopHeader->appendNew<Value>(
+        proc, Branch, Origin(),
+        loopHeader->appendNew<MemoryValue>(
+            proc, Load, Int32, Origin(),
+            loopHeader->appendNew<Value>(
+                proc, Add, Origin(),
+                loopHeader->appendNew<ConstPtrValue>(proc, Origin(), &array),
+                loopHeader->appendNew<Value>(
+                    proc, Mul, Origin(),
+                    loopHeader->appendNew<Value>(proc, ZExt32, Origin(), index),
+                    loopHeader->appendNew<ConstPtrValue>(proc, Origin(), sizeof(int))))));
+    loopHeader->setSuccessors(loopCall, loopFooter);
+    
+    Value* functionCall = func(loopCall, index);
+    UpsilonValue* _oneFromFunction_ = loopCall->appendNew<UpsilonValue>(proc, Origin(), functionCall);
+    loopCall->appendNew<Value>(proc, Jump, Origin());
+    loopCall->setSuccessors(loopFooter);
+    
+    Value* _one_ = loopFooter->appendNew<Value>(proc, Phi, Int32, Origin());
+    defaultOne->setPhi(one);
+    oneFromFunction->setPhi(one);
+    Value* nextIndex = loopFooter->appendNew<Value>(proc, Add, Origin(), index, one);
+    UpsilonValue* loopIndex = loopFooter->appendNew<UpsilonValue>(proc, Origin(), nextIndex);
+    loopIndex->setPhi(index);
+    loopFooter->appendNew<Value>(
+        proc, Branch, Origin(),
+        loopFooter->appendNew<Value>(
+            proc, LessThan, Origin(), nextIndex,
+            loopFooter->appendNew<Const32Value>(proc, Origin(), 100)));
+    loopFooter->setSuccessors(loopHeader, end);
+    
+    end->appendNew<Value>(proc, Return, Origin());
+}
+
+static int oneFunction(int* callCount)
+{
+    (*callCount)++;
+    return 1;
+}
+
+static void noOpFunction()
+{
+}
+
+void testLICMPure()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), Effects::none(),
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 1u);
+}
+
+void testLICMPureSideExits()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.exitsSideways = true;
+            loop->appendNew<CCallValue>(
+                proc, Void, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(noOpFunction)));
+
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), Effects::none(),
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 1u);
+}
+
+void testLICMPureWritesPinned()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.writesPinned = true;
+            loop->appendNew<CCallValue>(
+                proc, Void, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(noOpFunction)));
+
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), Effects::none(),
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 1u);
+}
+
+void testLICMPureWrites()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.writes = HeapRange(63479);
+            loop->appendNew<CCallValue>(
+                proc, Void, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(noOpFunction)));
+
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), Effects::none(),
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 1u);
+}
+
+void testLICMReadsLocalState()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.readsLocalState = true;
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 100u); // We'll fail to hoist because the loop has Upsilons.
+}
+
+void testLICMReadsPinned()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.readsPinned = true;
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 1u);
+}
+
+void testLICMReads()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.reads = HeapRange::top();
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 1u);
+}
+
+void testLICMPureNotBackwardsDominant()
+{
+    Procedure proc;
+    auto array = makeArrayForLoops();
+    generateLoopNotBackwardsDominant(
+        proc, array,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), Effects::none(),
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 1u);
+}
+
+void testLICMPureFoiledByChild()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value* index) -> Value* {
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), Effects::none(),
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
+                index);
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 100u);
+}
+
+void testLICMPureNotBackwardsDominantFoiledByChild()
+{
+    Procedure proc;
+    auto array = makeArrayForLoops();
+    generateLoopNotBackwardsDominant(
+        proc, array,
+        [&] (BasicBlock* loop, Value* index) -> Value* {
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), Effects::none(),
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0),
+                index);
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 50u);
+}
+
+void testLICMExitsSideways()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.exitsSideways = true;
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 100u);
+}
+
+void testLICMWritesLocalState()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.writesLocalState = true;
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 100u);
+}
+
+void testLICMWrites()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.writes = HeapRange(666);
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 100u);
+}
+
+void testLICMFence()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.fence = true;
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 100u);
+}
+
+void testLICMWritesPinned()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.writesPinned = true;
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 100u);
+}
+
+void testLICMControlDependent()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.controlDependent = true;
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 1u);
+}
+
+void testLICMControlDependentNotBackwardsDominant()
+{
+    Procedure proc;
+    auto array = makeArrayForLoops();
+    generateLoopNotBackwardsDominant(
+        proc, array,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.controlDependent = true;
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 50u);
+}
+
+void testLICMControlDependentSideExits()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.exitsSideways = true;
+            loop->appendNew<CCallValue>(
+                proc, Void, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(noOpFunction)));
+            
+            effects = Effects::none();
+            effects.controlDependent = true;
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 100u);
+}
+
+void testLICMReadsPinnedWritesPinned()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.writesPinned = true;
+            loop->appendNew<CCallValue>(
+                proc, Void, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(noOpFunction)));
+            
+            effects = Effects::none();
+            effects.readsPinned = true;
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 100u);
+}
+
+void testLICMReadsWritesDifferentHeaps()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.writes = HeapRange(6436);
+            loop->appendNew<CCallValue>(
+                proc, Void, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(noOpFunction)));
+            
+            effects = Effects::none();
+            effects.reads = HeapRange(4886);
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 1u);
+}
+
+void testLICMReadsWritesOverlappingHeaps()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            Effects effects = Effects::none();
+            effects.writes = HeapRange(6436, 74458);
+            loop->appendNew<CCallValue>(
+                proc, Void, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(noOpFunction)));
+            
+            effects = Effects::none();
+            effects.reads = HeapRange(48864, 78239);
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(), effects,
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 100u);
+}
+
+void testLICMDefaultCall()
+{
+    Procedure proc;
+    generateLoop(
+        proc,
+        [&] (BasicBlock* loop, Value*) -> Value* {
+            return loop->appendNew<CCallValue>(
+                proc, Int32, Origin(),
+                loop->appendNew<ConstPtrValue>(proc, Origin(), bitwise_cast<void*>(oneFunction)),
+                loop->appendNew<ArgumentRegValue>(proc, Origin(), GPRInfo::argumentGPR0));
+        });
+    
+    unsigned callCount = 0;
+    compileAndRun<void>(proc, &callCount);
+    CHECK_EQ(callCount, 100u);
+}
+
 template<typename T>
 void testAtomicWeakCAS()
 {
@@ -17128,7 +17681,29 @@
     RUN(testLoadBaseIndexShift2());
     RUN(testLoadBaseIndexShift32());
     RUN(testOptimizeMaterialization());
-    
+    RUN(testLICMPure());
+    RUN(testLICMPureSideExits());
+    RUN(testLICMPureWritesPinned());
+    RUN(testLICMPureWrites());
+    RUN(testLICMReadsLocalState());
+    RUN(testLICMReadsPinned());
+    RUN(testLICMReads());
+    RUN(testLICMPureNotBackwardsDominant());
+    RUN(testLICMPureFoiledByChild());
+    RUN(testLICMPureNotBackwardsDominantFoiledByChild());
+    RUN(testLICMExitsSideways());
+    RUN(testLICMWritesLocalState());
+    RUN(testLICMWrites());
+    RUN(testLICMWritesPinned());
+    RUN(testLICMFence());
+    RUN(testLICMControlDependent());
+    RUN(testLICMControlDependentNotBackwardsDominant());
+    RUN(testLICMControlDependentSideExits());
+    RUN(testLICMReadsPinnedWritesPinned());
+    RUN(testLICMReadsWritesDifferentHeaps());
+    RUN(testLICMReadsWritesOverlappingHeaps());
+    RUN(testLICMDefaultCall());
+
     RUN(testAtomicWeakCAS<int8_t>());
     RUN(testAtomicWeakCAS<int16_t>());
     RUN(testAtomicWeakCAS<int32_t>());
@@ -17181,7 +17756,7 @@
     RUN(testFloatEqualOrUnorderedFolding());
     RUN(testFloatEqualOrUnorderedFoldingNaN());
     RUN(testFloatEqualOrUnorderedDontFold());
-
+    
     RUN(testShuffleDoesntTrashCalleeSaves());
 
     if (isX86()) {

Modified: trunk/Source/_javascript_Core/dfg/DFGBasicBlock.h (219897 => 219898)


--- trunk/Source/_javascript_Core/dfg/DFGBasicBlock.h	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/dfg/DFGBasicBlock.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -229,9 +229,7 @@
     float executionCount;
     
     // These fields are reserved for NaturalLoops.
-    static const unsigned numberOfInnerMostLoopIndices = 2;
-    unsigned innerMostLoopIndices[numberOfInnerMostLoopIndices];
-
+    
     struct SSAData {
         WTF_MAKE_FAST_ALLOCATED;
     public:

Modified: trunk/Source/_javascript_Core/dfg/DFGCFG.h (219897 => 219898)


--- trunk/Source/_javascript_Core/dfg/DFGCFG.h	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/dfg/DFGCFG.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -59,7 +59,7 @@
     unsigned index(Node node) const { return node->index; }
     Node node(unsigned index) const { return m_graph.block(index); }
     unsigned numNodes() const { return m_graph.numBlocks(); }
-
+    
     PointerDump<BasicBlock> dump(Node node) const { return pointerDump(node); }
 
     void dump(PrintStream& out) const

Deleted: trunk/Source/_javascript_Core/dfg/DFGNaturalLoops.cpp (219897 => 219898)


--- trunk/Source/_javascript_Core/dfg/DFGNaturalLoops.cpp	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/dfg/DFGNaturalLoops.cpp	2017-07-26 01:58:36 UTC (rev 219898)
@@ -1,219 +0,0 @@
-/*
- * 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 "DFGNaturalLoops.h"
-
-#if ENABLE(DFG_JIT)
-
-#include "DFGDominators.h"
-#include "DFGGraph.h"
-#include "JSCInlines.h"
-#include <wtf/CommaPrinter.h>
-
-namespace JSC { namespace DFG {
-
-void NaturalLoop::dump(PrintStream& out) const
-{
-    out.print("[Header: ", *header(), ", Body:");
-    for (unsigned i = 0; i < m_body.size(); ++i)
-        out.print(" ", *m_body[i]);
-    out.print("]");
-}
-
-NaturalLoops::NaturalLoops(Graph& graph)
-{
-    ASSERT(graph.m_dominators);
-
-    // Implement the classic dominator-based natural loop finder. The first
-    // step is to find all control flow edges A -> B where B dominates A.
-    // Then B is a loop header and A is a backward branching block. We will
-    // then accumulate, for each loop header, multiple backward branching
-    // blocks. Then we backwards graph search from the backward branching
-    // blocks to their loop headers, which gives us all of the blocks in the
-    // loop body.
-    
-    static const bool verbose = false;
-    
-    if (verbose) {
-        dataLog("Dominators:\n");
-        graph.m_dominators->dump(WTF::dataFile());
-    }
-    
-    m_loops.shrink(0);
-    
-    for (BlockIndex blockIndex = graph.numBlocks(); blockIndex--;) {
-        BasicBlock* block = graph.block(blockIndex);
-        if (!block)
-            continue;
-        
-        for (unsigned i = block->numSuccessors(); i--;) {
-            BasicBlock* successor = block->successor(i);
-            if (!graph.m_dominators->dominates(successor, block))
-                continue;
-            bool found = false;
-            for (unsigned j = m_loops.size(); j--;) {
-                if (m_loops[j].header() == successor) {
-                    m_loops[j].addBlock(block);
-                    found = true;
-                    break;
-                }
-            }
-            if (found)
-                continue;
-            NaturalLoop loop(successor, m_loops.size());
-            loop.addBlock(block);
-            m_loops.append(loop);
-        }
-    }
-    
-    if (verbose)
-        dataLog("After bootstrap: ", *this, "\n");
-    
-    FastBitVector seenBlocks;
-    Vector<BasicBlock*, 4> blockWorklist;
-    seenBlocks.resize(graph.numBlocks());
-    
-    for (unsigned i = m_loops.size(); i--;) {
-        NaturalLoop& loop = m_loops[i];
-        
-        seenBlocks.clearAll();
-        ASSERT(blockWorklist.isEmpty());
-        
-        if (verbose)
-            dataLog("Dealing with loop ", loop, "\n");
-        
-        for (unsigned j = loop.size(); j--;) {
-            seenBlocks[loop[j]->index] = true;
-            blockWorklist.append(loop[j]);
-        }
-        
-        while (!blockWorklist.isEmpty()) {
-            BasicBlock* block = blockWorklist.takeLast();
-            
-            if (verbose)
-                dataLog("    Dealing with ", *block, "\n");
-            
-            if (block == loop.header())
-                continue;
-            
-            for (unsigned j = block->predecessors.size(); j--;) {
-                BasicBlock* predecessor = block->predecessors[j];
-                if (seenBlocks[predecessor->index])
-                    continue;
-                
-                loop.addBlock(predecessor);
-                blockWorklist.append(predecessor);
-                seenBlocks[predecessor->index] = true;
-            }
-        }
-    }
-
-    // Figure out reverse mapping from blocks to loops.
-    for (BlockIndex blockIndex = graph.numBlocks(); blockIndex--;) {
-        BasicBlock* block = graph.block(blockIndex);
-        if (!block)
-            continue;
-        for (unsigned i = BasicBlock::numberOfInnerMostLoopIndices; i--;)
-            block->innerMostLoopIndices[i] = UINT_MAX;
-    }
-    for (unsigned loopIndex = m_loops.size(); loopIndex--;) {
-        NaturalLoop& loop = m_loops[loopIndex];
-        
-        for (unsigned blockIndexInLoop = loop.size(); blockIndexInLoop--;) {
-            BasicBlock* block = loop[blockIndexInLoop];
-            
-            for (unsigned i = 0; i < BasicBlock::numberOfInnerMostLoopIndices; ++i) {
-                unsigned thisIndex = block->innerMostLoopIndices[i];
-                if (thisIndex == UINT_MAX || loop.size() < m_loops[thisIndex].size()) {
-                    insertIntoBoundedVector(
-                        block->innerMostLoopIndices, BasicBlock::numberOfInnerMostLoopIndices,
-                        loopIndex, i);
-                    break;
-                }
-            }
-        }
-    }
-    
-    // Now each block knows its inner-most loop and its next-to-inner-most loop. Use
-    // this to figure out loop parenting.
-    for (unsigned i = m_loops.size(); i--;) {
-        NaturalLoop& loop = m_loops[i];
-        RELEASE_ASSERT(loop.header()->innerMostLoopIndices[0] == i);
-        
-        loop.m_outerLoopIndex = loop.header()->innerMostLoopIndices[1];
-    }
-    
-    if (validationEnabled()) {
-        // Do some self-verification that we've done some of this correctly.
-        
-        for (BlockIndex blockIndex = graph.numBlocks(); blockIndex--;) {
-            BasicBlock* block = graph.block(blockIndex);
-            if (!block)
-                continue;
-            
-            Vector<const NaturalLoop*> simpleLoopsOf;
-            
-            for (unsigned i = m_loops.size(); i--;) {
-                if (m_loops[i].contains(block))
-                    simpleLoopsOf.append(&m_loops[i]);
-            }
-            
-            Vector<const NaturalLoop*> fancyLoopsOf = loopsOf(block);
-            
-            std::sort(simpleLoopsOf.begin(), simpleLoopsOf.end());
-            std::sort(fancyLoopsOf.begin(), fancyLoopsOf.end());
-            
-            RELEASE_ASSERT(simpleLoopsOf == fancyLoopsOf);
-        }
-    }
-    
-    if (verbose)
-        dataLog("Results: ", *this, "\n");
-}
-
-NaturalLoops::~NaturalLoops() { }
-
-Vector<const NaturalLoop*> NaturalLoops::loopsOf(BasicBlock* block) const
-{
-    Vector<const NaturalLoop*> result;
-    for (const NaturalLoop* loop = innerMostLoopOf(block); loop; loop = innerMostOuterLoop(*loop))
-        result.append(loop);
-    return result;
-}
-
-void NaturalLoops::dump(PrintStream& out) const
-{
-    out.print("NaturalLoops:{");
-    CommaPrinter comma;
-    for (unsigned i = 0; i < m_loops.size(); ++i)
-        out.print(comma, m_loops[i]);
-    out.print("}");
-}
-
-} } // namespace JSC::DFG
-
-#endif // ENABLE(DFG_JIT)
-

Modified: trunk/Source/_javascript_Core/dfg/DFGNaturalLoops.h (219897 => 219898)


--- trunk/Source/_javascript_Core/dfg/DFGNaturalLoops.h	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/_javascript_Core/dfg/DFGNaturalLoops.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,142 +27,23 @@
 
 #if ENABLE(DFG_JIT)
 
-#include "DFGBasicBlock.h"
+#include "DFGDominators.h"
 #include <wtf/FastMalloc.h>
+#include <wtf/NaturalLoops.h>
 #include <wtf/Noncopyable.h>
 
 namespace JSC { namespace DFG {
 
-class NaturalLoops;
+typedef WTF::NaturalLoop<CFG> NaturalLoop;
 
-class NaturalLoop {
-public:
-    NaturalLoop()
-        : m_header(0)
-        , m_outerLoopIndex(UINT_MAX)
-    {
-    }
-    
-    NaturalLoop(BasicBlock* header, unsigned index)
-        : m_header(header)
-        , m_outerLoopIndex(UINT_MAX)
-        , m_index(index)
-    {
-    }
-    
-    BasicBlock* header() const { return m_header; }
-    
-    unsigned size() const { return m_body.size(); }
-    BasicBlock* at(unsigned i) const { return m_body[i]; }
-    BasicBlock* operator[](unsigned i) const { return at(i); }
-
-    // This is the slower, but simpler, way of asking if a block belongs to
-    // a natural loop. It's faster to call NaturalLoops::belongsTo(), which
-    // tries to be O(loop depth) rather than O(loop size). Loop depth is
-    // almost always smaller than loop size. A *lot* smaller.
-    bool contains(BasicBlock* block) const
-    {
-        for (unsigned i = m_body.size(); i--;) {
-            if (m_body[i] == block)
-                return true;
-        }
-        ASSERT(block != header()); // Header should be contained.
-        return false;
-    }
-
-    // The index of this loop in NaturalLoops.
-    unsigned index() const { return m_index; }
-    
-    bool isOuterMostLoop() const { return m_outerLoopIndex == UINT_MAX; }
-    
-    void dump(PrintStream&) const;
-private:
-    friend class NaturalLoops;
-    
-    void addBlock(BasicBlock* block) { m_body.append(block); }
-    
-    BasicBlock* m_header;
-    Vector<BasicBlock*, 4> m_body;
-    unsigned m_outerLoopIndex;
-    unsigned m_index;
-};
-
-class NaturalLoops {
+class NaturalLoops : public WTF::NaturalLoops<CFG> {
     WTF_MAKE_NONCOPYABLE(NaturalLoops);
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    NaturalLoops(Graph&);
-    ~NaturalLoops();
-    
-    unsigned numLoops() const
+    NaturalLoops(Graph& graph)
+        : WTF::NaturalLoops<CFG>(*graph.m_cfg, graph.ensureDominators(), validationEnabled())
     {
-        return m_loops.size();
     }
-    const NaturalLoop& loop(unsigned i) const
-    {
-        return m_loops[i];
-    }
-    
-    // Return either null if the block isn't a loop header, or the
-    // loop it belongs to.
-    const NaturalLoop* headerOf(BasicBlock* block) const
-    {
-        const NaturalLoop* loop = innerMostLoopOf(block);
-        if (!loop)
-            return 0;
-        if (loop->header() == block)
-            return loop;
-        if (!ASSERT_DISABLED) {
-            for (; loop; loop = innerMostOuterLoop(*loop))
-                ASSERT(loop->header() != block);
-        }
-        return 0;
-    }
-    
-    const NaturalLoop* innerMostLoopOf(BasicBlock* block) const
-    {
-        unsigned index = block->innerMostLoopIndices[0];
-        if (index == UINT_MAX)
-            return 0;
-        return &m_loops[index];
-    }
-    
-    const NaturalLoop* innerMostOuterLoop(const NaturalLoop& loop) const
-    {
-        if (loop.m_outerLoopIndex == UINT_MAX)
-            return 0;
-        return &m_loops[loop.m_outerLoopIndex];
-    }
-    
-    bool belongsTo(BasicBlock* block, const NaturalLoop& candidateLoop) const
-    {
-        // It's faster to do this test using the loop itself, if it's small.
-        if (candidateLoop.size() < 4)
-            return candidateLoop.contains(block);
-        
-        for (const NaturalLoop* loop = innerMostLoopOf(block); loop; loop = innerMostOuterLoop(*loop)) {
-            if (loop == &candidateLoop)
-                return true;
-        }
-        return false;
-    }
-    
-    unsigned loopDepth(BasicBlock* block) const
-    {
-        unsigned depth = 0;
-        for (const NaturalLoop* loop = innerMostLoopOf(block); loop; loop = innerMostOuterLoop(*loop))
-            depth++;
-        return depth;
-    }
-    
-    // Return the indices of all loops this belongs to.
-    Vector<const NaturalLoop*> loopsOf(BasicBlock*) const;
-
-    void dump(PrintStream&) const;
-private:
-    // If we ever had a scalability problem in our natural loop finder, we could
-    // use some HashMap's here. But it just feels a heck of a lot less convenient.
-    Vector<NaturalLoop, 4> m_loops;
 };
 
 } } // namespace JSC::DFG

Modified: trunk/Source/WTF/ChangeLog (219897 => 219898)


--- trunk/Source/WTF/ChangeLog	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/WTF/ChangeLog	2017-07-26 01:58:36 UTC (rev 219898)
@@ -1,3 +1,46 @@
+2017-07-23  Filip Pizlo  <fpi...@apple.com>
+
+        B3 should do LICM
+        https://bugs.webkit.org/show_bug.cgi?id=174750
+
+        Reviewed by Keith Miller and Saam Barati.
+        
+        Moved DFG::NaturalLoops to WTF. The new templatized NaturalLoops<> uses the same Graph abstraction as
+        Dominators<>. This allows us to add a B3::NaturalLoops for free.
+        
+        Also made small tweaks to RangeSet, which the LICM uses.
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/CMakeLists.txt:
+        * wtf/Dominators.h:
+        * wtf/NaturalLoops.h: Added.
+        (WTF::NaturalLoop::NaturalLoop):
+        (WTF::NaturalLoop::graph):
+        (WTF::NaturalLoop::header):
+        (WTF::NaturalLoop::size):
+        (WTF::NaturalLoop::at):
+        (WTF::NaturalLoop::operator[]):
+        (WTF::NaturalLoop::contains):
+        (WTF::NaturalLoop::index):
+        (WTF::NaturalLoop::isOuterMostLoop):
+        (WTF::NaturalLoop::dump):
+        (WTF::NaturalLoop::addBlock):
+        (WTF::NaturalLoops::NaturalLoops):
+        (WTF::NaturalLoops::graph):
+        (WTF::NaturalLoops::numLoops):
+        (WTF::NaturalLoops::loop):
+        (WTF::NaturalLoops::headerOf):
+        (WTF::NaturalLoops::innerMostLoopOf):
+        (WTF::NaturalLoops::innerMostOuterLoop):
+        (WTF::NaturalLoops::belongsTo):
+        (WTF::NaturalLoops::loopDepth):
+        (WTF::NaturalLoops::loopsOf):
+        (WTF::NaturalLoops::dump):
+        * wtf/RangeSet.h:
+        (WTF::RangeSet::begin):
+        (WTF::RangeSet::end):
+        (WTF::RangeSet::addAll):
+
 2017-07-23  Michael Catanzaro  <mcatanz...@igalia.com>
 
         Implement FALLTHROUGH attribute for C with GCC

Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (219897 => 219898)


--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2017-07-26 01:58:36 UTC (rev 219898)
@@ -180,6 +180,7 @@
 		0F43D8F01DB5ADDC00108FB6 /* AutomaticThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutomaticThread.h; sourceTree = "<group>"; };
 		0F4570421BE5B58F0062A629 /* Dominators.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dominators.h; sourceTree = "<group>"; };
 		0F4570441BE834410062A629 /* BubbleSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BubbleSort.h; sourceTree = "<group>"; };
+		0F5BF1651F2317830029D91D /* NaturalLoops.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NaturalLoops.h; sourceTree = "<group>"; };
 		0F60F32D1DFCBD1B00416D6C /* LockedPrintStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LockedPrintStream.cpp; sourceTree = "<group>"; };
 		0F60F32E1DFCBD1B00416D6C /* LockedPrintStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LockedPrintStream.h; sourceTree = "<group>"; };
 		0F66B2801DC97BAB004A1D3F /* ClockType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClockType.cpp; sourceTree = "<group>"; };
@@ -860,6 +861,7 @@
 				0F66B2821DC97BAB004A1D3F /* MonotonicTime.cpp */,
 				0F66B2831DC97BAB004A1D3F /* MonotonicTime.h */,
 				FE8225301B2A1E5B00BA68FD /* NakedPtr.h */,
+				0F5BF1651F2317830029D91D /* NaturalLoops.h */,
 				1A3F6BE6174ADA2100B2EEA7 /* NeverDestroyed.h */,
 				0F0D85B317234CB100338210 /* NoLock.h */,
 				A8A472D0151A825B004123FF /* Noncopyable.h */,

Modified: trunk/Source/WTF/wtf/CMakeLists.txt (219897 => 219898)


--- trunk/Source/WTF/wtf/CMakeLists.txt	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/WTF/wtf/CMakeLists.txt	2017-07-26 01:58:36 UTC (rev 219898)
@@ -79,6 +79,7 @@
     MessageQueue.h
     MetaAllocator.h
     MetaAllocatorHandle.h
+    NaturalLoops.h
     MonotonicTime.h
     Noncopyable.h
     NumberOfCores.h

Modified: trunk/Source/WTF/wtf/Dominators.h (219897 => 219898)


--- trunk/Source/WTF/wtf/Dominators.h	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/WTF/wtf/Dominators.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -26,6 +26,7 @@
 #ifndef WTFDominators_h
 #define WTFDominators_h
 
+#include <wtf/CommaPrinter.h>
 #include <wtf/FastBitVector.h>
 #include <wtf/GraphNodeWorklist.h>
 

Added: trunk/Source/WTF/wtf/NaturalLoops.h (0 => 219898)


--- trunk/Source/WTF/wtf/NaturalLoops.h	                        (rev 0)
+++ trunk/Source/WTF/wtf/NaturalLoops.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2013-2017 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. 
+ */
+
+#pragma once
+
+#include <wtf/Dominators.h>
+
+namespace WTF {
+
+template<typename Graph>
+class NaturalLoops;
+
+template<typename Graph>
+class NaturalLoop {
+public:
+    NaturalLoop()
+        : m_graph(nullptr)
+        , m_header(nullptr)
+        , m_outerLoopIndex(UINT_MAX)
+    {
+    }
+    
+    NaturalLoop(Graph& graph, typename Graph::Node header, unsigned index)
+        : m_graph(&graph)
+        , m_header(header)
+        , m_outerLoopIndex(UINT_MAX)
+        , m_index(index)
+    {
+    }
+    
+    Graph* graph() const { return m_graph; }
+    
+    typename Graph::Node header() const { return m_header; }
+    
+    unsigned size() const { return m_body.size(); }
+    typename Graph::Node at(unsigned i) const { return m_body[i]; }
+    typename Graph::Node operator[](unsigned i) const { return at(i); }
+
+    // This is the slower, but simpler, way of asking if a block belongs to
+    // a natural loop. It's faster to call NaturalLoops::belongsTo(), which
+    // tries to be O(loop depth) rather than O(loop size). Loop depth is
+    // almost always smaller than loop size. A *lot* smaller.
+    bool contains(typename Graph::Node block) const
+    {
+        for (unsigned i = m_body.size(); i--;) {
+            if (m_body[i] == block)
+                return true;
+        }
+        ASSERT(block != header()); // Header should be contained.
+        return false;
+    }
+
+    // The index of this loop in NaturalLoops.
+    unsigned index() const { return m_index; }
+    
+    bool isOuterMostLoop() const { return m_outerLoopIndex == UINT_MAX; }
+    
+    void dump(PrintStream& out) const
+    {
+        if (!m_graph) {
+            out.print("<null>");
+            return;
+        }
+        
+        out.print("[Header: ", m_graph->dump(header()), ", Body:");
+        for (unsigned i = 0; i < m_body.size(); ++i)
+            out.print(" ", m_graph->dump(m_body[i]));
+        out.print("]");
+    }
+    
+private:
+    template<typename>
+    friend class NaturalLoops;
+    
+    void addBlock(typename Graph::Node block) { m_body.append(block); }
+
+    Graph* m_graph;
+    typename Graph::Node m_header;
+    Vector<typename Graph::Node, 4> m_body;
+    unsigned m_outerLoopIndex;
+    unsigned m_index;
+};
+
+template<typename Graph>
+class NaturalLoops {
+public:
+    typedef std::array<unsigned, 2> InnerMostLoopIndices;
+
+    NaturalLoops(Graph& graph, Dominators<Graph>& dominators, bool selfCheck = false)
+        : m_graph(graph)
+        , m_innerMostLoopIndices(graph.template newMap<InnerMostLoopIndices>())
+    {
+        // Implement the classic dominator-based natural loop finder. The first
+        // step is to find all control flow edges A -> B where B dominates A.
+        // Then B is a loop header and A is a backward branching block. We will
+        // then accumulate, for each loop header, multiple backward branching
+        // blocks. Then we backwards graph search from the backward branching
+        // blocks to their loop headers, which gives us all of the blocks in the
+        // loop body.
+    
+        static const bool verbose = false;
+    
+        if (verbose) {
+            dataLog("Dominators:\n");
+            dominators.dump(WTF::dataFile());
+        }
+    
+        m_loops.shrink(0);
+    
+        for (unsigned blockIndex = graph.numNodes(); blockIndex--;) {
+            typename Graph::Node block = graph.node(blockIndex);
+            if (!block)
+                continue;
+        
+            for (unsigned i = graph.successors(block).size(); i--;) {
+                typename Graph::Node successor = graph.successors(block)[i];
+                if (!dominators.dominates(successor, block))
+                    continue;
+                bool found = false;
+                for (unsigned j = m_loops.size(); j--;) {
+                    if (m_loops[j].header() == successor) {
+                        m_loops[j].addBlock(block);
+                        found = true;
+                        break;
+                    }
+                }
+                if (found)
+                    continue;
+                NaturalLoop<Graph> loop(graph, successor, m_loops.size());
+                loop.addBlock(block);
+                m_loops.append(loop);
+            }
+        }
+    
+        if (verbose)
+            dataLog("After bootstrap: ", *this, "\n");
+    
+        FastBitVector seenBlocks;
+        Vector<typename Graph::Node, 4> blockWorklist;
+        seenBlocks.resize(graph.numNodes());
+    
+        for (unsigned i = m_loops.size(); i--;) {
+            NaturalLoop<Graph>& loop = m_loops[i];
+        
+            seenBlocks.clearAll();
+            ASSERT(blockWorklist.isEmpty());
+        
+            if (verbose)
+                dataLog("Dealing with loop ", loop, "\n");
+        
+            for (unsigned j = loop.size(); j--;) {
+                seenBlocks[graph.index(loop[j])] = true;
+                blockWorklist.append(loop[j]);
+            }
+        
+            while (!blockWorklist.isEmpty()) {
+                typename Graph::Node block = blockWorklist.takeLast();
+            
+                if (verbose)
+                    dataLog("    Dealing with ", graph.dump(block), "\n");
+            
+                if (block == loop.header())
+                    continue;
+            
+                for (unsigned j = graph.predecessors(block).size(); j--;) {
+                    typename Graph::Node predecessor = graph.predecessors(block)[j];
+                    if (seenBlocks[graph.index(predecessor)])
+                        continue;
+                
+                    loop.addBlock(predecessor);
+                    blockWorklist.append(predecessor);
+                    seenBlocks[graph.index(predecessor)] = true;
+                }
+            }
+        }
+
+        // Figure out reverse mapping from blocks to loops.
+        for (unsigned blockIndex = graph.numNodes(); blockIndex--;) {
+            typename Graph::Node block = graph.node(blockIndex);
+            if (!block)
+                continue;
+            for (unsigned i = std::tuple_size<InnerMostLoopIndices>::value; i--;)
+                m_innerMostLoopIndices[block][i] = UINT_MAX;
+        }
+        for (unsigned loopIndex = m_loops.size(); loopIndex--;) {
+            NaturalLoop<Graph>& loop = m_loops[loopIndex];
+        
+            for (unsigned blockIndexInLoop = loop.size(); blockIndexInLoop--;) {
+                typename Graph::Node block = loop[blockIndexInLoop];
+            
+                for (unsigned i = 0; i < std::tuple_size<InnerMostLoopIndices>::value; ++i) {
+                    unsigned thisIndex = m_innerMostLoopIndices[block][i];
+                    if (thisIndex == UINT_MAX || loop.size() < m_loops[thisIndex].size()) {
+                        insertIntoBoundedVector(
+                            m_innerMostLoopIndices[block], std::tuple_size<InnerMostLoopIndices>::value,
+                            loopIndex, i);
+                        break;
+                    }
+                }
+            }
+        }
+    
+        // Now each block knows its inner-most loop and its next-to-inner-most loop. Use
+        // this to figure out loop parenting.
+        for (unsigned i = m_loops.size(); i--;) {
+            NaturalLoop<Graph>& loop = m_loops[i];
+            RELEASE_ASSERT(m_innerMostLoopIndices[loop.header()][0] == i);
+        
+            loop.m_outerLoopIndex = m_innerMostLoopIndices[loop.header()][1];
+        }
+    
+        if (selfCheck) {
+            // Do some self-verification that we've done some of this correctly.
+        
+            for (unsigned blockIndex = graph.numNodes(); blockIndex--;) {
+                typename Graph::Node block = graph.node(blockIndex);
+                if (!block)
+                    continue;
+            
+                Vector<const NaturalLoop<Graph>*> simpleLoopsOf;
+            
+                for (unsigned i = m_loops.size(); i--;) {
+                    if (m_loops[i].contains(block))
+                        simpleLoopsOf.append(&m_loops[i]);
+                }
+            
+                Vector<const NaturalLoop<Graph>*> fancyLoopsOf = loopsOf(block);
+            
+                std::sort(simpleLoopsOf.begin(), simpleLoopsOf.end());
+                std::sort(fancyLoopsOf.begin(), fancyLoopsOf.end());
+            
+                RELEASE_ASSERT(simpleLoopsOf == fancyLoopsOf);
+            }
+        }
+    
+        if (verbose)
+            dataLog("Results: ", *this, "\n");
+    }
+    
+    Graph& graph() { return m_graph; }
+    
+    unsigned numLoops() const
+    {
+        return m_loops.size();
+    }
+    const NaturalLoop<Graph>& loop(unsigned i) const
+    {
+        return m_loops[i];
+    }
+    
+    // Return either null if the block isn't a loop header, or the
+    // loop it belongs to.
+    const NaturalLoop<Graph>* headerOf(typename Graph::Node block) const
+    {
+        const NaturalLoop<Graph>* loop = innerMostLoopOf(block);
+        if (!loop)
+            return nullptr;
+        if (loop->header() == block)
+            return loop;
+        if (!ASSERT_DISABLED) {
+            for (; loop; loop = innerMostOuterLoop(*loop))
+                ASSERT(loop->header() != block);
+        }
+        return nullptr;
+    }
+    
+    const NaturalLoop<Graph>* innerMostLoopOf(typename Graph::Node block) const
+    {
+        unsigned index = m_innerMostLoopIndices[block][0];
+        if (index == UINT_MAX)
+            return nullptr;
+        return &m_loops[index];
+    }
+    
+    const NaturalLoop<Graph>* innerMostOuterLoop(const NaturalLoop<Graph>& loop) const
+    {
+        if (loop.m_outerLoopIndex == UINT_MAX)
+            return nullptr;
+        return &m_loops[loop.m_outerLoopIndex];
+    }
+    
+    bool belongsTo(typename Graph::Node block, const NaturalLoop<Graph>& candidateLoop) const
+    {
+        // It's faster to do this test using the loop itself, if it's small.
+        if (candidateLoop.size() < 4)
+            return candidateLoop.contains(block);
+        
+        for (const NaturalLoop<Graph>* loop = innerMostLoopOf(block); loop; loop = innerMostOuterLoop(*loop)) {
+            if (loop == &candidateLoop)
+                return true;
+        }
+        return false;
+    }
+    
+    unsigned loopDepth(typename Graph::Node block) const
+    {
+        unsigned depth = 0;
+        for (const NaturalLoop<Graph>* loop = innerMostLoopOf(block); loop; loop = innerMostOuterLoop(*loop))
+            depth++;
+        return depth;
+    }
+    
+    // Return all loops this belongs to. The first entry in the vector is the innermost loop. The last is the
+    // outermost loop.
+    Vector<const NaturalLoop<Graph>*> loopsOf(typename Graph::Node block) const
+    {
+        Vector<const NaturalLoop<Graph>*> result;
+        for (const NaturalLoop<Graph>* loop = innerMostLoopOf(block); loop; loop = innerMostOuterLoop(*loop))
+            result.append(loop);
+        return result;
+    }
+
+    void dump(PrintStream& out) const
+    {
+        out.print("NaturalLoops:{");
+        CommaPrinter comma;
+        for (unsigned i = 0; i < m_loops.size(); ++i)
+            out.print(comma, m_loops[i]);
+        out.print("}");
+    }
+    
+private:
+    Graph& m_graph;
+    
+    // If we ever had a scalability problem in our natural loop finder, we could
+    // use some HashMap's here. But it just feels a heck of a lot less convenient.
+    Vector<NaturalLoop<Graph>, 4> m_loops;
+    
+    typename Graph::template Map<InnerMostLoopIndices> m_innerMostLoopIndices;
+};
+
+} // namespace WTF
+
+using WTF::NaturalLoop;
+using WTF::NaturalLoops;

Modified: trunk/Source/WTF/wtf/RangeSet.h (219897 => 219898)


--- trunk/Source/WTF/wtf/RangeSet.h	2017-07-26 01:19:23 UTC (rev 219897)
+++ trunk/Source/WTF/wtf/RangeSet.h	2017-07-26 01:58:36 UTC (rev 219898)
@@ -56,6 +56,8 @@
     typedef RangeType Range;
     typedef typename Range::Type Type;
 
+    typedef Vector<Range, 8> VectorType;
+    
     RangeSet()
     {
     }
@@ -125,8 +127,23 @@
     {
         out.print("{", listDump(m_ranges), ", isCompact = ", m_isCompact, "}");
     }
+    
+    typename VectorType::const_iterator begin() const
+    {
+        return m_ranges.begin();
+    }
+    
+    typename VectorType::const_iterator end() const
+    {
+        return m_ranges.end();
+    }
+    
+    void addAll(const RangeSet& other)
+    {
+        for (Range range : other)
+            add(range);
+    }
 
-private:
     void compact()
     {
         if (m_isCompact)
@@ -164,6 +181,7 @@
         m_isCompact = true;
     }
     
+private:
     static bool overlapsNonEmpty(const Range& a, const Range& b)
     {
         return nonEmptyRangesOverlap(a.begin(), a.end(), b.begin(), b.end());
@@ -187,7 +205,7 @@
         return UINT_MAX;
     }
     
-    Vector<Range, 8> m_ranges;
+    VectorType m_ranges;
     bool m_isCompact { true };
 };
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to