Title: [192851] trunk/Source
Revision
192851
Author
benja...@webkit.org
Date
2015-11-30 18:26:57 -0800 (Mon, 30 Nov 2015)

Log Message

[JSC] Speed up Air Liveness Analysis on Tmps
https://bugs.webkit.org/show_bug.cgi?id=151556

Patch by Benjamin Poulain <bpoul...@apple.com> on 2015-11-30
Reviewed by Filip Pizlo.

Source/_javascript_Core:

Liveness Analysis scales poorly on large graphs like the ones
generated by testComplex().
This patch introduces a faster of Liveness using the continuous indices
of values instead of the values themselves.

There are two main areas of improvements:
1) Reduce the cost of doing a LocalCalc over a BasicBlock.
2) Reduce how many LocalCalc are needed to converge to a solution.

Most of the costs of LocalCalc are from HashSet manipulations.
The HashSet operations are O(1) but the constant is large enough
to be a problem.

I used a similar trick as the Register Allocator to remove hashing
and collision handling: the absolute value of the Tmp is used as an index
into a flat array.

I used Briggs's Sparse Set implementation for the local live information
at each instruction. It has great properties for doing the local calculation:
-No memory reallocation.
-O(1) add() and remove() with a small constant.
-Strict O(n) iteration.
-O(1) clear().

The values Live-At-Head are now stored into a Vector. The Sparse Set
is used to maintain the Tmp uniqueness.

When forwarding new liveness at head to the predecessor, I start by removing
everything that was already in live-at-head. We can assume that any value
in that list has already been added to the predecessors.
This leaves us with a small-ish number of Tmps to add to live-at-head
and to the predecessors.

The speed up convergence, I used the same trick as DFG's liveness: keep
a set of dirty blocks to process. In practice, all the blocks without
back-edges converge quickly, and we only propagate liveness as needed.

This patch reduces the time taken by "testComplex(64, 384)" by another 5%.

The remaining things to do for Liveness are:
-Skip the first block for the fix point (it is often large and doing a local
 calc on it is useless).
-Find a better Data Structure for live-at-tail (updating the HashSet takes
 > 50% of the total convergence time).

* _javascript_Core.xcodeproj/project.pbxproj:
* b3/air/AirIteratedRegisterCoalescing.cpp:
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::build):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::getAlias):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::getAliasWhenSpilling):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::allocatedReg):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::tmpArraySize):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::initializeDegrees):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::addEdges):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::addEdge):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::makeWorkList):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::simplify):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::forEachAdjacent):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::hasBeenSimplified):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::decrementDegree):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::forEachNodeMoves):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::isMoveRelated):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::enableMovesOnValue):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::precoloredCoalescingHeuristic):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::conservativeHeuristic):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::addWorkList):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::combine):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::freezeMoves):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::selectSpill):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::assignColors):
(JSC::B3::Air::IteratedRegisterCoalescingAllocator::dumpInterferenceGraphInDot):
(JSC::B3::Air::iteratedRegisterCoalescingOnType):
(JSC::B3::Air::iteratedRegisterCoalescing):
(JSC::B3::Air::AbsoluteTmpHelper<Arg::GP>::absoluteIndex): Deleted.
(JSC::B3::Air::AbsoluteTmpHelper<Arg::GP>::tmpFromAbsoluteIndex): Deleted.
(JSC::B3::Air::AbsoluteTmpHelper<Arg::FP>::absoluteIndex): Deleted.
(JSC::B3::Air::AbsoluteTmpHelper<Arg::FP>::tmpFromAbsoluteIndex): Deleted.
* b3/air/AirReportUsedRegisters.cpp:
(JSC::B3::Air::reportUsedRegisters):
* b3/air/AirTmpInlines.h:
(JSC::B3::Air::AbsoluteTmpMapper<Arg::GP>::absoluteIndex):
(JSC::B3::Air::AbsoluteTmpMapper<Arg::GP>::tmpFromAbsoluteIndex):
(JSC::B3::Air::AbsoluteTmpMapper<Arg::FP>::absoluteIndex):
(JSC::B3::Air::AbsoluteTmpMapper<Arg::FP>::tmpFromAbsoluteIndex):
* b3/air/AirLiveness.h: Added.

Source/WTF:

* WTF.xcodeproj/project.pbxproj:
* wtf/IndexSparseSet.h: Added.
(WTF::IndexSparseSet<OverflowHandler>::IndexSparseSet):
(WTF::IndexSparseSet<OverflowHandler>::add):
(WTF::IndexSparseSet<OverflowHandler>::remove):
(WTF::IndexSparseSet<OverflowHandler>::clear):
(WTF::IndexSparseSet<OverflowHandler>::size):
(WTF::IndexSparseSet<OverflowHandler>::isEmpty):
(WTF::IndexSparseSet<OverflowHandler>::contains):

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (192850 => 192851)


--- trunk/Source/_javascript_Core/ChangeLog	2015-12-01 02:15:47 UTC (rev 192850)
+++ trunk/Source/_javascript_Core/ChangeLog	2015-12-01 02:26:57 UTC (rev 192851)
@@ -1,3 +1,96 @@
+2015-11-30  Benjamin Poulain  <bpoul...@apple.com>
+
+        [JSC] Speed up Air Liveness Analysis on Tmps
+        https://bugs.webkit.org/show_bug.cgi?id=151556
+
+        Reviewed by Filip Pizlo.
+
+        Liveness Analysis scales poorly on large graphs like the ones
+        generated by testComplex().
+        This patch introduces a faster of Liveness using the continuous indices
+        of values instead of the values themselves.
+
+        There are two main areas of improvements:
+        1) Reduce the cost of doing a LocalCalc over a BasicBlock.
+        2) Reduce how many LocalCalc are needed to converge to a solution.
+
+        Most of the costs of LocalCalc are from HashSet manipulations.
+        The HashSet operations are O(1) but the constant is large enough
+        to be a problem.
+
+        I used a similar trick as the Register Allocator to remove hashing
+        and collision handling: the absolute value of the Tmp is used as an index
+        into a flat array.
+
+        I used Briggs's Sparse Set implementation for the local live information
+        at each instruction. It has great properties for doing the local calculation:
+        -No memory reallocation.
+        -O(1) add() and remove() with a small constant.
+        -Strict O(n) iteration.
+        -O(1) clear().
+
+        The values Live-At-Head are now stored into a Vector. The Sparse Set
+        is used to maintain the Tmp uniqueness.
+
+        When forwarding new liveness at head to the predecessor, I start by removing
+        everything that was already in live-at-head. We can assume that any value
+        in that list has already been added to the predecessors.
+        This leaves us with a small-ish number of Tmps to add to live-at-head
+        and to the predecessors.
+
+        The speed up convergence, I used the same trick as DFG's liveness: keep
+        a set of dirty blocks to process. In practice, all the blocks without
+        back-edges converge quickly, and we only propagate liveness as needed.
+
+        This patch reduces the time taken by "testComplex(64, 384)" by another 5%.
+
+        The remaining things to do for Liveness are:
+        -Skip the first block for the fix point (it is often large and doing a local
+         calc on it is useless).
+        -Find a better Data Structure for live-at-tail (updating the HashSet takes
+         > 50% of the total convergence time).
+
+        * _javascript_Core.xcodeproj/project.pbxproj:
+        * b3/air/AirIteratedRegisterCoalescing.cpp:
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::build):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::getAlias):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::getAliasWhenSpilling):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::allocatedReg):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::tmpArraySize):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::initializeDegrees):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::addEdges):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::addEdge):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::makeWorkList):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::simplify):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::forEachAdjacent):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::hasBeenSimplified):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::decrementDegree):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::forEachNodeMoves):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::isMoveRelated):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::enableMovesOnValue):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::precoloredCoalescingHeuristic):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::conservativeHeuristic):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::addWorkList):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::combine):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::freezeMoves):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::selectSpill):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::assignColors):
+        (JSC::B3::Air::IteratedRegisterCoalescingAllocator::dumpInterferenceGraphInDot):
+        (JSC::B3::Air::iteratedRegisterCoalescingOnType):
+        (JSC::B3::Air::iteratedRegisterCoalescing):
+        (JSC::B3::Air::AbsoluteTmpHelper<Arg::GP>::absoluteIndex): Deleted.
+        (JSC::B3::Air::AbsoluteTmpHelper<Arg::GP>::tmpFromAbsoluteIndex): Deleted.
+        (JSC::B3::Air::AbsoluteTmpHelper<Arg::FP>::absoluteIndex): Deleted.
+        (JSC::B3::Air::AbsoluteTmpHelper<Arg::FP>::tmpFromAbsoluteIndex): Deleted.
+        * b3/air/AirReportUsedRegisters.cpp:
+        (JSC::B3::Air::reportUsedRegisters):
+        * b3/air/AirTmpInlines.h:
+        (JSC::B3::Air::AbsoluteTmpMapper<Arg::GP>::absoluteIndex):
+        (JSC::B3::Air::AbsoluteTmpMapper<Arg::GP>::tmpFromAbsoluteIndex):
+        (JSC::B3::Air::AbsoluteTmpMapper<Arg::FP>::absoluteIndex):
+        (JSC::B3::Air::AbsoluteTmpMapper<Arg::FP>::tmpFromAbsoluteIndex):
+        * b3/air/AirLiveness.h: Added.
+
 2015-11-30  Saam barati  <sbar...@apple.com>
 
         FTL OSR Exits that are exception handlers should not have two different entrances. Instead, we should have two discrete OSR exits that do different things.

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (192850 => 192851)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2015-12-01 02:15:47 UTC (rev 192850)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2015-12-01 02:26:57 UTC (rev 192851)
@@ -814,7 +814,6 @@
 		0FEC857F1BDACDC70080FF74 /* AirInst.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FEC855A1BDACDC70080FF74 /* AirInst.cpp */; };
 		0FEC85801BDACDC70080FF74 /* AirInst.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC855B1BDACDC70080FF74 /* AirInst.h */; };
 		0FEC85811BDACDC70080FF74 /* AirInstInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC855C1BDACDC70080FF74 /* AirInstInlines.h */; };
-		0FEC85821BDACDC70080FF74 /* AirLiveness.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC855D1BDACDC70080FF74 /* AirLiveness.h */; };
 		0FEC85831BDACDC70080FF74 /* AirPhaseScope.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FEC855E1BDACDC70080FF74 /* AirPhaseScope.cpp */; };
 		0FEC85841BDACDC70080FF74 /* AirPhaseScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FEC855F1BDACDC70080FF74 /* AirPhaseScope.h */; };
 		0FEC85851BDACDC70080FF74 /* AirRegisterPriority.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FEC85601BDACDC70080FF74 /* AirRegisterPriority.cpp */; };
@@ -1088,6 +1087,7 @@
 		2600B5A7152BAAA70091EE5F /* JSStringJoiner.h in Headers */ = {isa = PBXBuildFile; fileRef = 2600B5A5152BAAA70091EE5F /* JSStringJoiner.h */; };
 		26718BA41BE99F780052017B /* AirIteratedRegisterCoalescing.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26718BA21BE99F780052017B /* AirIteratedRegisterCoalescing.cpp */; };
 		26718BA51BE99F780052017B /* AirIteratedRegisterCoalescing.h in Headers */ = {isa = PBXBuildFile; fileRef = 26718BA31BE99F780052017B /* AirIteratedRegisterCoalescing.h */; };
+		2684D4381C00161C0081D663 /* AirLiveness.h in Headers */ = {isa = PBXBuildFile; fileRef = 2684D4371C00161C0081D663 /* AirLiveness.h */; };
 		269D636E1BFBE5D100101B1D /* FTLB3Output.h in Headers */ = {isa = PBXBuildFile; fileRef = 269D636D1BFBE5D000101B1D /* FTLB3Output.h */; };
 		26BB57601BFC4328005F12EB /* FTLB3Output.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26BB575F1BFC4328005F12EB /* FTLB3Output.cpp */; };
 		2A05ABD51961DF2400341750 /* JSPropertyNameEnumerator.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A05ABD31961DF2400341750 /* JSPropertyNameEnumerator.cpp */; };
@@ -2895,7 +2895,6 @@
 		0FEC855A1BDACDC70080FF74 /* AirInst.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirInst.cpp; path = b3/air/AirInst.cpp; sourceTree = "<group>"; };
 		0FEC855B1BDACDC70080FF74 /* AirInst.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirInst.h; path = b3/air/AirInst.h; sourceTree = "<group>"; };
 		0FEC855C1BDACDC70080FF74 /* AirInstInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirInstInlines.h; path = b3/air/AirInstInlines.h; sourceTree = "<group>"; };
-		0FEC855D1BDACDC70080FF74 /* AirLiveness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirLiveness.h; path = b3/air/AirLiveness.h; sourceTree = "<group>"; };
 		0FEC855E1BDACDC70080FF74 /* AirPhaseScope.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirPhaseScope.cpp; path = b3/air/AirPhaseScope.cpp; sourceTree = "<group>"; };
 		0FEC855F1BDACDC70080FF74 /* AirPhaseScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirPhaseScope.h; path = b3/air/AirPhaseScope.h; sourceTree = "<group>"; };
 		0FEC85601BDACDC70080FF74 /* AirRegisterPriority.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirRegisterPriority.cpp; path = b3/air/AirRegisterPriority.cpp; sourceTree = "<group>"; };
@@ -3124,6 +3123,7 @@
 		264091FA1BE2FD4100684DB2 /* AirOpcode.opcodes */ = {isa = PBXFileReference; lastKnownFileType = text; name = AirOpcode.opcodes; path = b3/air/AirOpcode.opcodes; sourceTree = "<group>"; };
 		26718BA21BE99F780052017B /* AirIteratedRegisterCoalescing.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirIteratedRegisterCoalescing.cpp; path = b3/air/AirIteratedRegisterCoalescing.cpp; sourceTree = "<group>"; };
 		26718BA31BE99F780052017B /* AirIteratedRegisterCoalescing.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirIteratedRegisterCoalescing.h; path = b3/air/AirIteratedRegisterCoalescing.h; sourceTree = "<group>"; };
+		2684D4371C00161C0081D663 /* AirLiveness.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AirLiveness.h; path = b3/air/AirLiveness.h; sourceTree = "<group>"; };
 		269D636D1BFBE5D000101B1D /* FTLB3Output.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = FTLB3Output.h; path = ftl/FTLB3Output.h; sourceTree = "<group>"; };
 		26BB575F1BFC4328005F12EB /* FTLB3Output.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FTLB3Output.cpp; path = ftl/FTLB3Output.cpp; sourceTree = "<group>"; };
 		2A05ABD31961DF2400341750 /* JSPropertyNameEnumerator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSPropertyNameEnumerator.cpp; sourceTree = "<group>"; };
@@ -4631,7 +4631,7 @@
 				0FEC855C1BDACDC70080FF74 /* AirInstInlines.h */,
 				26718BA21BE99F780052017B /* AirIteratedRegisterCoalescing.cpp */,
 				26718BA31BE99F780052017B /* AirIteratedRegisterCoalescing.h */,
-				0FEC855D1BDACDC70080FF74 /* AirLiveness.h */,
+				2684D4371C00161C0081D663 /* AirLiveness.h */,
 				264091FA1BE2FD4100684DB2 /* AirOpcode.opcodes */,
 				0FB3878C1BFBC44D00E3AB1E /* AirOptimizeBlockOrder.cpp */,
 				0FB3878D1BFBC44D00E3AB1E /* AirOptimizeBlockOrder.h */,
@@ -6674,7 +6674,6 @@
 				0FEC857E1BDACDC70080FF74 /* AirInsertionSet.h in Headers */,
 				0FEC85801BDACDC70080FF74 /* AirInst.h in Headers */,
 				0FEC85811BDACDC70080FF74 /* AirInstInlines.h in Headers */,
-				0FEC85821BDACDC70080FF74 /* AirLiveness.h in Headers */,
 				0FEC85841BDACDC70080FF74 /* AirPhaseScope.h in Headers */,
 				0FEC85861BDACDC70080FF74 /* AirRegisterPriority.h in Headers */,
 				0F45703D1BE45F0A0062A629 /* AirReportUsedRegisters.h in Headers */,
@@ -7697,6 +7696,7 @@
 				0F572D4F16879FDD00E57FBD /* ThunkGenerator.h in Headers */,
 				A7386556118697B400540279 /* ThunkGenerators.h in Headers */,
 				141448CD13A1783700F5BA1A /* TinyBloomFilter.h in Headers */,
+				2684D4381C00161C0081D663 /* AirLiveness.h in Headers */,
 				0F55989817C86C5800A1E543 /* ToNativeFromValue.h in Headers */,
 				0F2D4DE919832DAC007D4B19 /* ToThisStatus.h in Headers */,
 				5D53726F0E1C54880021E549 /* Tracing.h in Headers */,

Modified: trunk/Source/_javascript_Core/b3/B3IndexMap.h (192850 => 192851)


--- trunk/Source/_javascript_Core/b3/B3IndexMap.h	2015-12-01 02:15:47 UTC (rev 192850)
+++ trunk/Source/_javascript_Core/b3/B3IndexMap.h	2015-12-01 02:26:57 UTC (rev 192851)
@@ -58,6 +58,11 @@
     {
         return m_vector[key->index()];
     }
+
+    void clear()
+    {
+        m_vector.clear();
+    }
     
 private:
     Vector<Value> m_vector;

Modified: trunk/Source/_javascript_Core/b3/air/AirAllocateStack.cpp (192850 => 192851)


--- trunk/Source/_javascript_Core/b3/air/AirAllocateStack.cpp	2015-12-01 02:15:47 UTC (rev 192850)
+++ trunk/Source/_javascript_Core/b3/air/AirAllocateStack.cpp	2015-12-01 02:26:57 UTC (rev 192851)
@@ -136,16 +136,16 @@
     }
 
     // Now we handle the anonymous slots.
-    Liveness<StackSlot*> liveness(code);
+    StackSlotLiveness liveness(code);
     IndexMap<StackSlot, HashSet<StackSlot*>> interference(code.stackSlots().size());
     Vector<StackSlot*> slots;
 
     for (BasicBlock* block : code) {
-        Liveness<StackSlot*>::LocalCalc localCalc(liveness, block);
+        StackSlotLiveness::LocalCalc localCalc(liveness, block);
 
         auto interfere = [&] (Inst& inst) {
             if (verbose)
-                dataLog("Interfering: ", pointerListDump(localCalc.live()), "\n");
+                dataLog("Interfering: ", WTF::pointerListDump(localCalc.live()), "\n");
 
             inst.forEachArg(
                 [&] (Arg& arg, Arg::Role role, Arg::Type) {

Modified: trunk/Source/_javascript_Core/b3/air/AirIteratedRegisterCoalescing.cpp (192850 => 192851)


--- trunk/Source/_javascript_Core/b3/air/AirIteratedRegisterCoalescing.cpp	2015-12-01 02:15:47 UTC (rev 192850)
+++ trunk/Source/_javascript_Core/b3/air/AirIteratedRegisterCoalescing.cpp	2015-12-01 02:26:57 UTC (rev 192851)
@@ -34,6 +34,7 @@
 #include "AirLiveness.h"
 #include "AirPhaseScope.h"
 #include "AirRegisterPriority.h"
+#include "AirTmpInlines.h"
 #include <wtf/ListDump.h>
 #include <wtf/ListHashSet.h>
 
@@ -77,75 +78,100 @@
     }
 };
 
-
-// The speed of the alocator depends directly on how fast we can query information associated with a Tmp
-// and/or its ownership to a set.
-//
-// HashSet/HashMap operations are overly expensive for that.
-//
-// Instead of a Hash structure, Tmp are indexed directly by value in Arrays. The internal integer is used as the index
-// to reference them quickly. In some sets, we do not care about the colored regs, we still allocate the memory for them
-// and just do not use it.
 template<Arg::Type type>
-struct AbsoluteTmpHelper;
-
-template<>
-struct AbsoluteTmpHelper<Arg::GP> {
-    static unsigned absoluteIndex(const Tmp& tmp)
+class IteratedRegisterCoalescingAllocator {
+public:
+    IteratedRegisterCoalescingAllocator(Code& code, const HashSet<Tmp>& unspillableTmp)
+        : m_unspillableTmp(unspillableTmp)
+        , m_numberOfRegisters(regsInPriorityOrder(type).size())
     {
-        ASSERT(tmp.isGP());
-        ASSERT(static_cast<int>(tmp.internalValue()) > 0);
-        return tmp.internalValue();
+        initializeDegrees(code);
+
+        unsigned tmpArraySize = this->tmpArraySize(code);
+        m_adjacencyList.resize(tmpArraySize);
+        m_moveList.resize(tmpArraySize);
+        m_coalescedTmps.resize(tmpArraySize);
+        m_isOnSelectStack.ensureSize(tmpArraySize);
+
+        build(code);
+        allocate();
     }
 
-    static unsigned absoluteIndex(unsigned tmpIndex)
+    Tmp getAlias(Tmp tmp) const
     {
-        return absoluteIndex(Tmp::gpTmpForIndex(tmpIndex));
+        Tmp alias = tmp;
+        while (Tmp nextAlias = m_coalescedTmps[AbsoluteTmpMapper<type>::absoluteIndex(alias)])
+            alias = nextAlias;
+        return alias;
     }
 
-    static Tmp tmpFromAbsoluteIndex(unsigned tmpIndex)
+    Tmp getAliasWhenSpilling(Tmp tmp) const
     {
-        return Tmp::tmpForInternalValue(tmpIndex);
+        ASSERT_WITH_MESSAGE(!m_spilledTmp.isEmpty(), "This function is only valid for coalescing during spilling.");
+
+        if (m_coalescedTmpsAtSpill.isEmpty())
+            return tmp;
+
+        Tmp alias = tmp;
+        while (Tmp nextAlias = m_coalescedTmpsAtSpill[AbsoluteTmpMapper<type>::absoluteIndex(alias)])
+            alias = nextAlias;
+
+        ASSERT_WITH_MESSAGE(!m_spilledTmp.contains(tmp) || alias == tmp, "The aliases at spill should always be colorable. Something went horribly wrong.");
+
+        return alias;
     }
-};
 
-template<>
-struct AbsoluteTmpHelper<Arg::FP> {
-    static unsigned absoluteIndex(const Tmp& tmp)
+    const HashSet<Tmp>& spilledTmp() const { return m_spilledTmp; }
+    Reg allocatedReg(Tmp tmp) const
     {
-        ASSERT(tmp.isFP());
-        ASSERT(static_cast<int>(tmp.internalValue()) < 0);
-        return -tmp.internalValue();
+        ASSERT(!tmp.isReg());
+        ASSERT(m_coloredTmp.size());
+        ASSERT(tmp.isGP() == (type == Arg::GP));
+
+        Reg reg = m_coloredTmp[AbsoluteTmpMapper<type>::absoluteIndex(tmp)];
+        if (!reg) {
+            // We only care about Tmps that interfere. A Tmp that never interfere with anything
+            // can take any register.
+            reg = regsInPriorityOrder(type).first();
+        }
+        return reg;
     }
 
-    static unsigned absoluteIndex(unsigned tmpIndex)
+private:
+    static unsigned tmpArraySize(Code& code)
     {
-        return absoluteIndex(Tmp::fpTmpForIndex(tmpIndex));
+        unsigned numTmps = code.numTmps(type);
+        return AbsoluteTmpMapper<type>::absoluteIndex(numTmps);
     }
 
-    static Tmp tmpFromAbsoluteIndex(unsigned tmpIndex)
+    void initializeDegrees(Code& code)
     {
-        return Tmp::tmpForInternalValue(-tmpIndex);
+        unsigned tmpArraySize = this->tmpArraySize(code);
+        m_degrees.resize(tmpArraySize);
+
+        // All precolored registers have  an "infinite" degree.
+        unsigned firstNonRegIndex = AbsoluteTmpMapper<type>::absoluteIndex(0);
+        for (unsigned i = 0; i < firstNonRegIndex; ++i)
+            m_degrees[i] = std::numeric_limits<unsigned>::max();
+
+        bzero(m_degrees.data() + firstNonRegIndex, (tmpArraySize - firstNonRegIndex) * sizeof(unsigned));
     }
-};
 
-template<Arg::Type type>
-class IteratedRegisterCoalescingAllocator {
-public:
-    IteratedRegisterCoalescingAllocator(Code& code, const HashSet<Tmp>& unspillableTmp)
-        : m_unspillableTmp(unspillableTmp)
-        , m_numberOfRegisters(regsInPriorityOrder(type).size())
+    void build(Code& code)
     {
-        initializeDegrees(code);
-
-        unsigned tmpArraySize = this->tmpArraySize(code);
-        m_adjacencyList.resize(tmpArraySize);
-        m_moveList.resize(tmpArraySize);
-        m_coalescedTmps.resize(tmpArraySize);
-        m_isOnSelectStack.ensureSize(tmpArraySize);
+        TmpLiveness<type> liveness(code);
+        for (BasicBlock* block : code) {
+            typename TmpLiveness<type>::LocalCalc localCalc(liveness, block);
+            for (unsigned instIndex = block->size(); instIndex--;) {
+                Inst& inst = block->at(instIndex);
+                Inst* nextInst = instIndex + 1 < block->size() ? &block->at(instIndex + 1) : nullptr;
+                build(inst, nextInst, localCalc);
+                localCalc.execute(instIndex);
+            }
+        }
     }
 
-    void build(Inst& inst, Inst* nextInst, const Liveness<Tmp>::LocalCalc& localCalc)
+    void build(Inst& inst, Inst* nextInst, const typename TmpLiveness<type>::LocalCalc& localCalc)
     {
         inst.forEachTmpWithExtraClobberedRegs(
             nextInst,
@@ -191,12 +217,12 @@
             m_activeMoves.ensureSize(nextMoveIndex + 1);
 
             for (const Arg& arg : inst.args) {
-                auto& list = m_moveList[AbsoluteTmpHelper<type>::absoluteIndex(arg.tmp())];
+                auto& list = m_moveList[AbsoluteTmpMapper<type>::absoluteIndex(arg.tmp())];
                 list.add(nextMoveIndex);
             }
 
             for (const Tmp& liveTmp : localCalc.live()) {
-                if (liveTmp != useTmp && liveTmp.isGP() == (type == Arg::GP))
+                if (liveTmp != useTmp)
                     addEdge(defTmp, liveTmp);
             }
 
@@ -204,8 +230,10 @@
             if (nextInst && nextInst->hasSpecial()) {
                 nextInst->extraEarlyClobberedRegs().forEach(
                     [&] (Reg reg) {
-                        for (const Tmp& liveTmp : localCalc.live()) {
-                            addEdge(Tmp(reg), liveTmp);
+                        if (reg.isGPR() == (type == Arg::GP)) {
+                            for (const Tmp& liveTmp : localCalc.live()) {
+                                addEdge(Tmp(reg), liveTmp);
+                            }
                         }
                     });
             }
@@ -213,105 +241,8 @@
             addEdges(inst, nextInst, localCalc.live());
     }
 
-    void allocate()
+    void addEdges(Inst& inst, Inst* nextInst, typename TmpLiveness<type>::LocalCalc::Iterable liveTmp)
     {
-        ASSERT_WITH_MESSAGE(m_activeMoves.size() >= m_coalescingCandidates.size(), "The activeMove set should be big enough for the quick operations of BitVector.");
-
-        makeWorkList();
-
-        if (debug) {
-            dataLog("Interference: ", listDump(m_interferenceEdges), "\n");
-            dumpInterferenceGraphInDot(WTF::dataFile());
-            dataLog("Initial work list\n");
-            dumpWorkLists(WTF::dataFile());
-        }
-
-        do {
-            if (traceDebug) {
-                dataLog("Before Graph simplification iteration\n");
-                dumpWorkLists(WTF::dataFile());
-            }
-
-            if (!m_simplifyWorklist.isEmpty())
-                simplify();
-            else if (!m_worklistMoves.isEmpty())
-                coalesce();
-            else if (!m_freezeWorklist.isEmpty())
-                freeze();
-            else if (!m_spillWorklist.isEmpty())
-                selectSpill();
-
-            if (traceDebug) {
-                dataLog("After Graph simplification iteration\n");
-                dumpWorkLists(WTF::dataFile());
-            }
-        } while (!m_simplifyWorklist.isEmpty() || !m_worklistMoves.isEmpty() || !m_freezeWorklist.isEmpty() || !m_spillWorklist.isEmpty());
-
-        assignColors();
-    }
-
-    Tmp getAlias(Tmp tmp) const
-    {
-        Tmp alias = tmp;
-        while (Tmp nextAlias = m_coalescedTmps[AbsoluteTmpHelper<type>::absoluteIndex(alias)])
-            alias = nextAlias;
-        return alias;
-    }
-
-    Tmp getAliasWhenSpilling(Tmp tmp) const
-    {
-        ASSERT_WITH_MESSAGE(!m_spilledTmp.isEmpty(), "This function is only valid for coalescing during spilling.");
-
-        if (m_coalescedTmpsAtSpill.isEmpty())
-            return tmp;
-
-        Tmp alias = tmp;
-        while (Tmp nextAlias = m_coalescedTmpsAtSpill[AbsoluteTmpHelper<type>::absoluteIndex(alias)])
-            alias = nextAlias;
-
-        ASSERT_WITH_MESSAGE(!m_spilledTmp.contains(tmp) || alias == tmp, "The aliases at spill should always be colorable. Something went horribly wrong.");
-
-        return alias;
-    }
-
-    const HashSet<Tmp>& spilledTmp() const { return m_spilledTmp; }
-    Reg allocatedReg(Tmp tmp) const
-    {
-        ASSERT(!tmp.isReg());
-        ASSERT(m_coloredTmp.size());
-        ASSERT(tmp.isGP() == (type == Arg::GP));
-
-        Reg reg = m_coloredTmp[AbsoluteTmpHelper<type>::absoluteIndex(tmp)];
-        if (!reg) {
-            // We only care about Tmps that interfere. A Tmp that never interfere with anything
-            // can take any register.
-            reg = regsInPriorityOrder(type).first();
-        }
-        return reg;
-    }
-
-private:
-    static unsigned tmpArraySize(Code& code)
-    {
-        unsigned numTmps = code.numTmps(type);
-        return AbsoluteTmpHelper<type>::absoluteIndex(numTmps);
-    }
-
-    void initializeDegrees(Code& code)
-    {
-        unsigned tmpArraySize = this->tmpArraySize(code);
-        m_degrees.resize(tmpArraySize);
-
-        // All precolored registers have  an "infinite" degree.
-        unsigned firstNonRegIndex = AbsoluteTmpHelper<type>::absoluteIndex(0);
-        for (unsigned i = 0; i < firstNonRegIndex; ++i)
-            m_degrees[i] = std::numeric_limits<unsigned>::max();
-
-        bzero(m_degrees.data() + firstNonRegIndex, (tmpArraySize - firstNonRegIndex) * sizeof(unsigned));
-    }
-
-    void addEdges(Inst& inst, Inst* nextInst, const HashSet<Tmp>& liveTmp)
-    {
         // All the Def()s interfere with everthing live.
         inst.forEachTmpWithExtraClobberedRegs(
             nextInst,
@@ -333,32 +264,32 @@
 
         if (m_interferenceEdges.add(InterferenceEdge(a, b)).isNewEntry) {
             if (!a.isReg()) {
-                ASSERT(!m_adjacencyList[AbsoluteTmpHelper<type>::absoluteIndex(a)].contains(b));
-                m_adjacencyList[AbsoluteTmpHelper<type>::absoluteIndex(a)].append(b);
-                m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(a)]++;
+                ASSERT(!m_adjacencyList[AbsoluteTmpMapper<type>::absoluteIndex(a)].contains(b));
+                m_adjacencyList[AbsoluteTmpMapper<type>::absoluteIndex(a)].append(b);
+                m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(a)]++;
             }
 
             if (!b.isReg()) {
-                ASSERT(!m_adjacencyList[AbsoluteTmpHelper<type>::absoluteIndex(b)].contains(a));
-                m_adjacencyList[AbsoluteTmpHelper<type>::absoluteIndex(b)].append(a);
-                m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(b)]++;
+                ASSERT(!m_adjacencyList[AbsoluteTmpMapper<type>::absoluteIndex(b)].contains(a));
+                m_adjacencyList[AbsoluteTmpMapper<type>::absoluteIndex(b)].append(a);
+                m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(b)]++;
             }
         }
     }
 
     void makeWorkList()
     {
-        unsigned firstNonRegIndex = AbsoluteTmpHelper<type>::absoluteIndex(0);
+        unsigned firstNonRegIndex = AbsoluteTmpMapper<type>::absoluteIndex(0);
         for (unsigned i = firstNonRegIndex; i < m_degrees.size(); ++i) {
             unsigned degree = m_degrees[i];
             if (!degree)
                 continue;
 
-            Tmp tmp = AbsoluteTmpHelper<type>::tmpFromAbsoluteIndex(i);
+            Tmp tmp = AbsoluteTmpMapper<type>::tmpFromAbsoluteIndex(i);
 
             if (degree >= m_numberOfRegisters)
                 m_spillWorklist.add(tmp);
-            else if (!m_moveList[AbsoluteTmpHelper<type>::absoluteIndex(tmp)].isEmpty())
+            else if (!m_moveList[AbsoluteTmpMapper<type>::absoluteIndex(tmp)].isEmpty())
                 m_freezeWorklist.add(tmp);
             else
                 m_simplifyWorklist.append(tmp);
@@ -370,9 +301,9 @@
         Tmp last = m_simplifyWorklist.takeLast();
 
         ASSERT(!m_selectStack.contains(last));
-        ASSERT(!m_isOnSelectStack.get(AbsoluteTmpHelper<type>::absoluteIndex(last)));
+        ASSERT(!m_isOnSelectStack.get(AbsoluteTmpMapper<type>::absoluteIndex(last)));
         m_selectStack.append(last);
-        m_isOnSelectStack.quickSet(AbsoluteTmpHelper<type>::absoluteIndex(last));
+        m_isOnSelectStack.quickSet(AbsoluteTmpMapper<type>::absoluteIndex(last));
 
         forEachAdjacent(last, [this](Tmp adjacentTmp) {
             decrementDegree(adjacentTmp);
@@ -382,7 +313,7 @@
     template<typename Function>
     void forEachAdjacent(Tmp tmp, Function function)
     {
-        for (Tmp adjacentTmp : m_adjacencyList[AbsoluteTmpHelper<type>::absoluteIndex(tmp)]) {
+        for (Tmp adjacentTmp : m_adjacencyList[AbsoluteTmpMapper<type>::absoluteIndex(tmp)]) {
             if (!hasBeenSimplified(adjacentTmp))
                 function(adjacentTmp);
         }
@@ -390,14 +321,14 @@
 
     bool hasBeenSimplified(Tmp tmp)
     {
-        return m_isOnSelectStack.quickGet(AbsoluteTmpHelper<type>::absoluteIndex(tmp)) || !!m_coalescedTmps[AbsoluteTmpHelper<type>::absoluteIndex(tmp)];
+        return m_isOnSelectStack.quickGet(AbsoluteTmpMapper<type>::absoluteIndex(tmp)) || !!m_coalescedTmps[AbsoluteTmpMapper<type>::absoluteIndex(tmp)];
     }
 
     void decrementDegree(Tmp tmp)
     {
-        ASSERT(m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(tmp)]);
+        ASSERT(m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(tmp)]);
 
-        unsigned oldDegree = m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(tmp)]--;
+        unsigned oldDegree = m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(tmp)]--;
         if (oldDegree == m_numberOfRegisters) {
             enableMovesOnValueAndAdjacents(tmp);
             m_spillWorklist.remove(tmp);
@@ -411,7 +342,7 @@
     template<typename Function>
     void forEachNodeMoves(Tmp tmp, Function function)
     {
-        for (unsigned moveIndex : m_moveList[AbsoluteTmpHelper<type>::absoluteIndex(tmp)]) {
+        for (unsigned moveIndex : m_moveList[AbsoluteTmpMapper<type>::absoluteIndex(tmp)]) {
             if (m_activeMoves.quickGet(moveIndex) || m_worklistMoves.contains(moveIndex))
                 function(moveIndex);
         }
@@ -419,7 +350,7 @@
 
     bool isMoveRelated(Tmp tmp)
     {
-        for (unsigned moveIndex : m_moveList[AbsoluteTmpHelper<type>::absoluteIndex(tmp)]) {
+        for (unsigned moveIndex : m_moveList[AbsoluteTmpMapper<type>::absoluteIndex(tmp)]) {
             if (m_activeMoves.quickGet(moveIndex) || m_worklistMoves.contains(moveIndex))
                 return true;
         }
@@ -428,7 +359,7 @@
 
     void enableMovesOnValue(Tmp tmp)
     {
-        for (unsigned moveIndex : m_moveList[AbsoluteTmpHelper<type>::absoluteIndex(tmp)]) {
+        for (unsigned moveIndex : m_moveList[AbsoluteTmpMapper<type>::absoluteIndex(tmp)]) {
             if (m_activeMoves.quickClear(moveIndex))
                 m_worklistMoves.returnMove(moveIndex);
         }
@@ -500,11 +431,11 @@
         // If any adjacent of the non-colored node is not an adjacent of the colored node AND has a degree >= K
         // there is a risk that this node needs to have the same color as our precolored node. If we coalesce such
         // move, we may create an uncolorable graph.
-        const auto& adjacentsOfV = m_adjacencyList[AbsoluteTmpHelper<type>::absoluteIndex(v)];
+        const auto& adjacentsOfV = m_adjacencyList[AbsoluteTmpMapper<type>::absoluteIndex(v)];
         for (Tmp adjacentTmp : adjacentsOfV) {
             if (!adjacentTmp.isReg()
                 && !hasBeenSimplified(adjacentTmp)
-                && m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(adjacentTmp)] >= m_numberOfRegisters
+                && m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(adjacentTmp)] >= m_numberOfRegisters
                 && !m_interferenceEdges.contains(InterferenceEdge(u, adjacentTmp)))
                 return false;
         }
@@ -522,8 +453,8 @@
         ASSERT(!u.isReg());
         ASSERT(!v.isReg());
 
-        const auto& adjacentsOfU = m_adjacencyList[AbsoluteTmpHelper<type>::absoluteIndex(u)];
-        const auto& adjacentsOfV = m_adjacencyList[AbsoluteTmpHelper<type>::absoluteIndex(v)];
+        const auto& adjacentsOfU = m_adjacencyList[AbsoluteTmpMapper<type>::absoluteIndex(u)];
+        const auto& adjacentsOfV = m_adjacencyList[AbsoluteTmpMapper<type>::absoluteIndex(v)];
 
         if (adjacentsOfU.size() + adjacentsOfV.size() < m_numberOfRegisters) {
             // Shortcut: if the total number of adjacents is less than the number of register, the condition is always met.
@@ -535,7 +466,7 @@
         for (Tmp adjacentTmp : adjacentsOfU) {
             ASSERT(adjacentTmp != v);
             ASSERT(adjacentTmp != u);
-            if (!hasBeenSimplified(adjacentTmp) && m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(adjacentTmp)] >= m_numberOfRegisters) {
+            if (!hasBeenSimplified(adjacentTmp) && m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(adjacentTmp)] >= m_numberOfRegisters) {
                 auto addResult = highOrderAdjacents.add(adjacentTmp);
                 if (addResult.isNewEntry && highOrderAdjacents.size() >= m_numberOfRegisters)
                     return false;
@@ -544,7 +475,7 @@
         for (Tmp adjacentTmp : adjacentsOfV) {
             ASSERT(adjacentTmp != u);
             ASSERT(adjacentTmp != v);
-            if (!hasBeenSimplified(adjacentTmp) && m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(adjacentTmp)] >= m_numberOfRegisters) {
+            if (!hasBeenSimplified(adjacentTmp) && m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(adjacentTmp)] >= m_numberOfRegisters) {
                 auto addResult = highOrderAdjacents.add(adjacentTmp);
                 if (addResult.isNewEntry && highOrderAdjacents.size() >= m_numberOfRegisters)
                     return false;
@@ -557,7 +488,7 @@
 
     void addWorkList(Tmp tmp)
     {
-        if (!tmp.isReg() && m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(tmp)] < m_numberOfRegisters && !isMoveRelated(tmp)) {
+        if (!tmp.isReg() && m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(tmp)] < m_numberOfRegisters && !isMoveRelated(tmp)) {
             m_freezeWorklist.remove(tmp);
             m_simplifyWorklist.append(tmp);
         }
@@ -568,18 +499,18 @@
         if (!m_freezeWorklist.remove(v))
             m_spillWorklist.remove(v);
 
-        ASSERT(!m_coalescedTmps[AbsoluteTmpHelper<type>::absoluteIndex(v)]);
-        m_coalescedTmps[AbsoluteTmpHelper<type>::absoluteIndex(v)] = u;
+        ASSERT(!m_coalescedTmps[AbsoluteTmpMapper<type>::absoluteIndex(v)]);
+        m_coalescedTmps[AbsoluteTmpMapper<type>::absoluteIndex(v)] = u;
 
-        auto& vMoves = m_moveList[AbsoluteTmpHelper<type>::absoluteIndex(v)];
-        m_moveList[AbsoluteTmpHelper<type>::absoluteIndex(u)].add(vMoves.begin(), vMoves.end());
+        auto& vMoves = m_moveList[AbsoluteTmpMapper<type>::absoluteIndex(v)];
+        m_moveList[AbsoluteTmpMapper<type>::absoluteIndex(u)].add(vMoves.begin(), vMoves.end());
 
         forEachAdjacent(v, [this, u] (Tmp adjacentTmp) {
             addEdge(adjacentTmp, u);
             decrementDegree(adjacentTmp);
         });
 
-        if (m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(u)] >= m_numberOfRegisters && m_freezeWorklist.remove(u))
+        if (m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(u)] >= m_numberOfRegisters && m_freezeWorklist.remove(u))
             m_spillWorklist.add(u);
     }
 
@@ -600,7 +531,7 @@
             const MoveOperands& moveOperands = m_coalescingCandidates[moveIndex];
             Tmp originalOtherTmp = moveOperands.src != tmp ? moveOperands.src : moveOperands.dst;
             Tmp otherTmp = getAlias(originalOtherTmp);
-            if (m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(otherTmp)] < m_numberOfRegisters && !isMoveRelated(otherTmp)) {
+            if (m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(otherTmp)] < m_numberOfRegisters && !isMoveRelated(otherTmp)) {
                 if (m_freezeWorklist.remove(otherTmp))
                     m_simplifyWorklist.append(otherTmp);
             }
@@ -625,11 +556,11 @@
         RELEASE_ASSERT_WITH_MESSAGE(iterator != m_spillWorklist.end(), "It is not possible to color the Air graph with the number of available registers.");
 
         auto victimIterator = iterator;
-        unsigned maxDegree = m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(*iterator)];
+        unsigned maxDegree = m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(*iterator)];
 
         ++iterator;
         for (;iterator != m_spillWorklist.end(); ++iterator) {
-            unsigned tmpDegree = m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(*iterator)];
+            unsigned tmpDegree = m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(*iterator)];
             if (tmpDegree > maxDegree) {
                 if (m_unspillableTmp.contains(*iterator))
                     continue;
@@ -645,6 +576,43 @@
         freezeMoves(victimTmp);
     }
 
+    void allocate()
+    {
+        ASSERT_WITH_MESSAGE(m_activeMoves.size() >= m_coalescingCandidates.size(), "The activeMove set should be big enough for the quick operations of BitVector.");
+
+        makeWorkList();
+
+        if (debug) {
+            dataLog("Interference: ", listDump(m_interferenceEdges), "\n");
+            dumpInterferenceGraphInDot(WTF::dataFile());
+            dataLog("Initial work list\n");
+            dumpWorkLists(WTF::dataFile());
+        }
+
+        do {
+            if (traceDebug) {
+                dataLog("Before Graph simplification iteration\n");
+                dumpWorkLists(WTF::dataFile());
+            }
+
+            if (!m_simplifyWorklist.isEmpty())
+                simplify();
+            else if (!m_worklistMoves.isEmpty())
+                coalesce();
+            else if (!m_freezeWorklist.isEmpty())
+                freeze();
+            else if (!m_spillWorklist.isEmpty())
+                selectSpill();
+
+            if (traceDebug) {
+                dataLog("After Graph simplification iteration\n");
+                dumpWorkLists(WTF::dataFile());
+            }
+        } while (!m_simplifyWorklist.isEmpty() || !m_worklistMoves.isEmpty() || !m_freezeWorklist.isEmpty() || !m_spillWorklist.isEmpty());
+
+        assignColors();
+    }
+
     void assignColors()
     {
         ASSERT(m_simplifyWorklist.isEmpty());
@@ -668,17 +636,17 @@
         while (!m_selectStack.isEmpty()) {
             Tmp tmp = m_selectStack.takeLast();
             ASSERT(!tmp.isReg());
-            ASSERT(!m_coloredTmp[AbsoluteTmpHelper<type>::absoluteIndex(tmp)]);
+            ASSERT(!m_coloredTmp[AbsoluteTmpMapper<type>::absoluteIndex(tmp)]);
 
             RegisterSet coloredRegisters;
-            for (Tmp adjacentTmp : m_adjacencyList[AbsoluteTmpHelper<type>::absoluteIndex(tmp)]) {
+            for (Tmp adjacentTmp : m_adjacencyList[AbsoluteTmpMapper<type>::absoluteIndex(tmp)]) {
                 Tmp aliasTmp = getAlias(adjacentTmp);
                 if (aliasTmp.isReg()) {
                     coloredRegisters.set(aliasTmp.reg());
                     continue;
                 }
 
-                Reg reg = m_coloredTmp[AbsoluteTmpHelper<type>::absoluteIndex(aliasTmp)];
+                Reg reg = m_coloredTmp[AbsoluteTmpMapper<type>::absoluteIndex(aliasTmp)];
                 if (reg)
                     coloredRegisters.set(reg);
             }
@@ -686,7 +654,7 @@
             bool colorAssigned = false;
             for (Reg reg : registersInPriorityOrder) {
                 if (!coloredRegisters.get(reg)) {
-                    m_coloredTmp[AbsoluteTmpHelper<type>::absoluteIndex(tmp)] = reg;
+                    m_coloredTmp[AbsoluteTmpMapper<type>::absoluteIndex(tmp)] = reg;
                     colorAssigned = true;
                     break;
                 }
@@ -718,7 +686,7 @@
         }
 
         for (const auto& tmp : tmpsWithInterferences)
-            out.print("    ", tmp.internalValue(), " [label=\"", tmp, " (", m_degrees[AbsoluteTmpHelper<type>::absoluteIndex(tmp)], ")\"];\n");
+            out.print("    ", tmp.internalValue(), " [label=\"", tmp, " (", m_degrees[AbsoluteTmpMapper<type>::absoluteIndex(tmp)], ")\"];\n");
 
         for (const auto& edge : m_interferenceEdges)
             out.print("    ", edge.first().internalValue(), " -- ", edge.second().internalValue(), ";\n");
@@ -1039,23 +1007,12 @@
 }
 
 template<Arg::Type type>
-static void iteratedRegisterCoalescingOnType(Code& code, HashSet<Tmp>& unspillableTmps, unsigned& numIterations)
+static void iteratedRegisterCoalescingOnType(Code& code, unsigned& numIterations)
 {
+    HashSet<Tmp> unspillableTmps;
     while (true) {
         numIterations++;
         IteratedRegisterCoalescingAllocator<type> allocator(code, unspillableTmps);
-        Liveness<Tmp> liveness(code);
-        for (BasicBlock* block : code) {
-            Liveness<Tmp>::LocalCalc localCalc(liveness, block);
-            for (unsigned instIndex = block->size(); instIndex--;) {
-                Inst& inst = block->at(instIndex);
-                Inst* nextInst = instIndex + 1 < block->size() ? &block->at(instIndex + 1) : nullptr;
-                allocator.build(inst, nextInst, localCalc);
-                localCalc.execute(instIndex);
-            }
-        }
-
-        allocator.allocate();
         if (allocator.spilledTmp().isEmpty()) {
             assignRegisterToTmpInProgram(code, allocator);
             return;
@@ -1068,56 +1025,11 @@
 {
     PhaseScope phaseScope(code, "iteratedRegisterCoalescing");
 
-    bool gpIsColored = false;
-    bool fpIsColored = false;
     unsigned numIterations = 0;
 
-    HashSet<Tmp> unspillableGPs;
-    HashSet<Tmp> unspillableFPs;
+    iteratedRegisterCoalescingOnType<Arg::GP>(code, numIterations);
+    iteratedRegisterCoalescingOnType<Arg::FP>(code, numIterations);
 
-    // First we run both allocator together as long as they both spill.
-    while (!gpIsColored && !fpIsColored) {
-        numIterations++;
-        IteratedRegisterCoalescingAllocator<Arg::GP> gpAllocator(code, unspillableGPs);
-        IteratedRegisterCoalescingAllocator<Arg::FP> fpAllocator(code, unspillableFPs);
-
-        // Liveness Analysis can be prohibitively expensive. It is shared
-        // between the two allocators to avoid doing it twice.
-        Liveness<Tmp> liveness(code);
-        for (BasicBlock* block : code) {
-            Liveness<Tmp>::LocalCalc localCalc(liveness, block);
-            for (unsigned instIndex = block->size(); instIndex--;) {
-                Inst& inst = block->at(instIndex);
-                Inst* nextInst = instIndex + 1 < block->size() ? &block->at(instIndex + 1) : nullptr;
-
-                gpAllocator.build(inst, nextInst, localCalc);
-                fpAllocator.build(inst, nextInst, localCalc);
-
-                localCalc.execute(instIndex);
-            }
-        }
-
-        gpAllocator.allocate();
-        fpAllocator.allocate();
-
-        if (gpAllocator.spilledTmp().isEmpty()) {
-            assignRegisterToTmpInProgram(code, gpAllocator);
-            gpIsColored = true;
-        } else
-            addSpillAndFillToProgram<Arg::GP>(code, gpAllocator, unspillableGPs);
-
-        if (fpAllocator.spilledTmp().isEmpty()) {
-            assignRegisterToTmpInProgram(code, fpAllocator);
-            fpIsColored = true;
-        } else
-            addSpillAndFillToProgram<Arg::FP>(code, fpAllocator, unspillableFPs);
-    };
-
-    if (!gpIsColored)
-        iteratedRegisterCoalescingOnType<Arg::GP>(code, unspillableGPs, numIterations);
-    if (!fpIsColored)
-        iteratedRegisterCoalescingOnType<Arg::FP>(code, unspillableFPs, numIterations);
-
     if (reportStats)
         dataLog("Num iterations = ", numIterations, "\n");
 }

Modified: trunk/Source/_javascript_Core/b3/air/AirLiveness.h (192850 => 192851)


--- trunk/Source/_javascript_Core/b3/air/AirLiveness.h	2015-12-01 02:15:47 UTC (rev 192850)
+++ trunk/Source/_javascript_Core/b3/air/AirLiveness.h	2015-12-01 02:26:57 UTC (rev 192851)
@@ -20,7 +20,7 @@
  * 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. 
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #ifndef AirLiveness_h
@@ -29,130 +29,236 @@
 #if ENABLE(B3_JIT)
 
 #include "AirBasicBlock.h"
+#include "AirTmpInlines.h"
 #include "B3IndexMap.h"
 #include "B3IndexSet.h"
+#include <wtf/IndexSparseSet.h>
 
 namespace JSC { namespace B3 { namespace Air {
 
-// You can compute liveness over Tmp's or over Arg's. If you compute over Arg's, you get both
-// stack liveness and tmp liveness.
-template<typename Thing>
-class Liveness {
-public:
-    template<typename T>
-    static bool isAlive(const T& thing) { return thing.isAlive(); }
+template<Arg::Type adapterType>
+struct TmpLivenessAdapter {
+    typedef Tmp Thing;
+    typedef HashSet<unsigned> IndexSet;
 
-    static bool isAlive(StackSlot* slot) { return slot->kind() == StackSlotKind::Anonymous; }
-    
-    Liveness(Code& code)
+    TmpLivenessAdapter(Code&) { }
+
+    static unsigned maxIndex(Code& code)
     {
-        m_liveAtHead.resize(code.size());
-        m_liveAtTail.resize(code.size());
+        unsigned numTmps = code.numTmps(adapterType);
+        return AbsoluteTmpMapper<adapterType>::absoluteIndex(numTmps);
+    }
+    static bool acceptsType(Arg::Type type) { return type == adapterType; }
+    static unsigned valueToIndex(Tmp tmp) { return AbsoluteTmpMapper<adapterType>::absoluteIndex(tmp); }
+    static Tmp indexToValue(unsigned index) { return AbsoluteTmpMapper<adapterType>::tmpFromAbsoluteIndex(index); }
+};
 
+struct StackSlotLivenessAdapter {
+    typedef StackSlot* Thing;
+    typedef HashSet<unsigned, DefaultHash<unsigned>::Hash, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> IndexSet;
+
+    StackSlotLivenessAdapter(Code& code)
+        : m_code(code)
+    {
+    }
+
+    static unsigned maxIndex(Code& code)
+    {
+        return code.stackSlots().size() - 1;
+    }
+    static bool acceptsType(Arg::Type) { return true; }
+    static unsigned valueToIndex(StackSlot* stackSlot) { return stackSlot->index(); }
+    StackSlot* indexToValue(unsigned index) { return m_code.stackSlots()[index]; }
+
+private:
+    Code& m_code;
+};
+
+template<typename Adapter>
+class AbstractLiveness : private Adapter {
+    struct Workset;
+public:
+    AbstractLiveness(Code& code)
+        : Adapter(code)
+        , m_workset(Adapter::maxIndex(code))
+        , m_liveAtHead(code.size())
+        , m_liveAtTail(code.size())
+    {
         // The liveAtTail of each block automatically contains the LateUse's of the terminal.
         for (BasicBlock* block : code) {
-            HashSet<Thing>& live = m_liveAtTail[block];
-            block->last().forEach<Thing>(
-                [&] (Thing& thing, Arg::Role role, Arg::Type) {
-                    if (Arg::isLateUse(role))
-                        live.add(thing);
+            typename Adapter::IndexSet& liveAtTail = m_liveAtTail[block];
+
+            block->last().forEach<typename Adapter::Thing>(
+                [&] (typename Adapter::Thing& thing, Arg::Role role, Arg::Type type) {
+                    if (Arg::isLateUse(role) && Adapter::acceptsType(type))
+                        liveAtTail.add(Adapter::valueToIndex(thing));
                 });
         }
 
-        IndexSet<BasicBlock> seen;
+        // Blocks with new live values at tail.
+        BitVector dirtyBlocks;
+        for (size_t blockIndex = 0; blockIndex < code.size(); ++blockIndex)
+            dirtyBlocks.set(blockIndex);
 
-        bool changed = true;
-        while (changed) {
+        bool changed;
+        do {
             changed = false;
 
             for (size_t blockIndex = code.size(); blockIndex--;) {
                 BasicBlock* block = code.at(blockIndex);
                 if (!block)
                     continue;
+
+                if (!dirtyBlocks.quickClear(blockIndex))
+                    continue;
+
                 LocalCalc localCalc(*this, block);
                 for (size_t instIndex = block->size(); instIndex--;)
                     localCalc.execute(instIndex);
-                bool firstTime = seen.add(block);
-                if (!firstTime && localCalc.live() == m_liveAtHead[block])
+
+                Vector<unsigned>& liveAtHead = m_liveAtHead[block];
+
+                // We only care about Tmps that were discovered in this iteration. It is impossible
+                // to remove a live value from the head.
+                // We remove all the values we already knew about so that we only have to deal with
+                // what is new in LiveAtHead.
+                if (m_workset.size() == liveAtHead.size())
+                    m_workset.clear();
+                else {
+                    for (unsigned liveIndexAtHead : liveAtHead)
+                        m_workset.remove(liveIndexAtHead);
+                }
+
+                if (m_workset.isEmpty())
                     continue;
-                changed = true;
+
+                liveAtHead.reserveCapacity(liveAtHead.size() + m_workset.size());
+                for (unsigned newValue : m_workset)
+                    liveAtHead.uncheckedAppend(newValue);
+
                 for (BasicBlock* predecessor : block->predecessors()) {
-                    m_liveAtTail[predecessor].add(
-                        localCalc.live().begin(), localCalc.live().end());
+                    typename Adapter::IndexSet& liveAtTail = m_liveAtTail[predecessor];
+                    for (unsigned newValue : m_workset) {
+                        if (liveAtTail.add(newValue)) {
+                            if (dirtyBlocks.quickSet(predecessor->index()))
+                                changed = true;
+                        }
+                    }
                 }
-                m_liveAtHead[block] = localCalc.takeLive();
             }
-        }
-    }
+        } while (changed);
 
-    const HashSet<Thing>& liveAtHead(BasicBlock* block) const
-    {
-        return m_liveAtHead[block];
+        m_liveAtHead.clear();
     }
 
-    const HashSet<Thing>& liveAtTail(BasicBlock* block) const
-    {
-        return m_liveAtTail[block];
-    }
-
     // This calculator has to be run in reverse.
     class LocalCalc {
     public:
-        LocalCalc(Liveness& liveness, BasicBlock* block)
-            : m_live(liveness.liveAtTail(block))
+        LocalCalc(AbstractLiveness& liveness, BasicBlock* block)
+            : m_liveness(liveness)
             , m_block(block)
         {
+            auto& workset = liveness.m_workset;
+            workset.clear();
+            typename Adapter::IndexSet& liveAtTail = liveness.m_liveAtTail[block];
+            for (unsigned index : liveAtTail)
+                workset.add(index);
         }
 
-        const HashSet<Thing>& live() const { return m_live; }
-        HashSet<Thing>&& takeLive() { return WTF::move(m_live); }
+        struct Iterator {
+            Iterator(Adapter& adapter, IndexSparseSet<UnsafeVectorOverflow>::const_iterator sparceSetIterator)
+                : m_adapter(adapter)
+                , m_sparceSetIterator(sparceSetIterator)
+            {
+            }
 
+            Iterator& operator++()
+            {
+                ++m_sparceSetIterator;
+                return *this;
+            }
+
+            typename Adapter::Thing operator*() const
+            {
+                return m_adapter.indexToValue(*m_sparceSetIterator);
+            }
+
+            bool operator==(const Iterator& other) { return m_sparceSetIterator == other.m_sparceSetIterator; }
+            bool operator!=(const Iterator& other) { return m_sparceSetIterator != other.m_sparceSetIterator; }
+
+        private:
+            Adapter& m_adapter;
+            IndexSparseSet<UnsafeVectorOverflow>::const_iterator m_sparceSetIterator;
+        };
+
+        struct Iterable {
+            Iterable(AbstractLiveness& liveness)
+                : m_liveness(liveness)
+            {
+            }
+
+            Iterator begin() const { return Iterator(m_liveness, m_liveness.m_workset.begin()); }
+            Iterator end() const { return Iterator(m_liveness, m_liveness.m_workset.end()); }
+
+        private:
+            AbstractLiveness& m_liveness;
+        };
+
+        Iterable live() const
+        {
+            return Iterable(m_liveness);
+        }
+
         void execute(unsigned instIndex)
         {
             Inst& inst = m_block->at(instIndex);
-            
+            auto& workset = m_liveness.m_workset;
             // First handle def's.
-            inst.forEach<Thing>(
-                [this] (Thing& arg, Arg::Role role, Arg::Type) {
-                    if (!isAlive(arg))
-                        return;
-                    if (Arg::isDef(role))
-                        m_live.remove(arg);
+            inst.forEach<typename Adapter::Thing>(
+                [&] (typename Adapter::Thing& thing, Arg::Role role, Arg::Type type) {
+                    if (Arg::isDef(role) && Adapter::acceptsType(type))
+                        workset.remove(Adapter::valueToIndex(thing));
                 });
 
             // Then handle use's.
-            inst.forEach<Thing>(
-                [this] (Thing& arg, Arg::Role role, Arg::Type) {
-                    if (!isAlive(arg))
-                        return;
-                    if (Arg::isEarlyUse(role))
-                        m_live.add(arg);
+            inst.forEach<typename Adapter::Thing>(
+                [&] (typename Adapter::Thing& thing, Arg::Role role, Arg::Type type) {
+                    if (Arg::isEarlyUse(role) && Adapter::acceptsType(type))
+                        workset.add(Adapter::valueToIndex(thing));
                 });
 
             // And finally, handle the late use's of the previous instruction.
             if (instIndex) {
                 Inst& prevInst = m_block->at(instIndex - 1);
-
-                prevInst.forEach<Thing>(
-                    [this] (Thing& arg, Arg::Role role, Arg::Type) {
-                        if (!Arg::isLateUse(role))
-                            return;
-                        if (isAlive(arg))
-                            m_live.add(arg);
+                prevInst.forEach<typename Adapter::Thing>(
+                    [&] (typename Adapter::Thing& thing, Arg::Role role, Arg::Type type) {
+                        if (Arg::isLateUse(role) && Adapter::acceptsType(type))
+                            workset.add(Adapter::valueToIndex(thing));
                     });
             }
         }
 
     private:
-        HashSet<Thing> m_live;
+        AbstractLiveness& m_liveness;
         BasicBlock* m_block;
     };
 
 private:
-    IndexMap<BasicBlock, HashSet<Thing>> m_liveAtHead;
-    IndexMap<BasicBlock, HashSet<Thing>> m_liveAtTail;
+    friend class LocalCalc;
+    friend struct LocalCalc::Iterable;
+
+    IndexSparseSet<UnsafeVectorOverflow> m_workset;
+    IndexMap<BasicBlock, Vector<unsigned>> m_liveAtHead;
+    IndexMap<BasicBlock, typename Adapter::IndexSet> m_liveAtTail;
 };
 
+template<Arg::Type type>
+using TmpLiveness = AbstractLiveness<TmpLivenessAdapter<type>>;
+
+typedef AbstractLiveness<TmpLivenessAdapter<Arg::GP>> GPLiveness;
+typedef AbstractLiveness<TmpLivenessAdapter<Arg::FP>> FPLiveness;
+typedef AbstractLiveness<StackSlotLivenessAdapter> StackSlotLiveness;
+
 } } } // namespace JSC::B3::Air
 
 #endif // ENABLE(B3_JIT)

Modified: trunk/Source/_javascript_Core/b3/air/AirReportUsedRegisters.cpp (192850 => 192851)


--- trunk/Source/_javascript_Core/b3/air/AirReportUsedRegisters.cpp	2015-12-01 02:15:47 UTC (rev 192850)
+++ trunk/Source/_javascript_Core/b3/air/AirReportUsedRegisters.cpp	2015-12-01 02:26:57 UTC (rev 192851)
@@ -41,22 +41,29 @@
 
     // FIXME: We should tell liveness to only track Regs.
     // https://bugs.webkit.org/show_bug.cgi?id=150751
-    Liveness<Tmp> liveness(code);
+    GPLiveness gpLiveness(code);
+    FPLiveness fpLiveness(code);
 
     for (BasicBlock* block : code) {
-        Liveness<Tmp>::LocalCalc localCalc(liveness, block);
+        GPLiveness::LocalCalc gpLocalCalc(gpLiveness, block);
+        FPLiveness::LocalCalc fpLocalCalc(fpLiveness, block);
 
         for (unsigned instIndex = block->size(); instIndex--;) {
             Inst& inst = block->at(instIndex);
             if (inst.hasSpecial()) {
                 RegisterSet registerSet;
-                for (Tmp tmp : localCalc.live()) {
-                    if (tmp.isReg())
-                        registerSet.set(tmp.reg());
+                for (Tmp tmp : gpLocalCalc.live()) {
+                    ASSERT(tmp.isGP());
+                    registerSet.set(tmp.reg());
                 }
+                for (Tmp tmp : fpLocalCalc.live()) {
+                    ASSERT(tmp.isFP());
+                    registerSet.set(tmp.reg());
+                }
                 inst.reportUsedRegisters(registerSet);
             }
-            localCalc.execute(instIndex);
+            gpLocalCalc.execute(instIndex);
+            fpLocalCalc.execute(instIndex);
         }
     }
 }

Modified: trunk/Source/_javascript_Core/b3/air/AirSpillEverything.cpp (192850 => 192851)


--- trunk/Source/_javascript_Core/b3/air/AirSpillEverything.cpp	2015-12-01 02:15:47 UTC (rev 192850)
+++ trunk/Source/_javascript_Core/b3/air/AirSpillEverything.cpp	2015-12-01 02:26:57 UTC (rev 192851)
@@ -44,17 +44,24 @@
 
     // We want to know the set of registers used at every point in every basic block.
     IndexMap<BasicBlock, Vector<RegisterSet>> usedRegisters(code.size());
-    Liveness<Tmp> liveness(code);
+    GPLiveness gpLiveness(code);
+    FPLiveness fpLiveness(code);
     for (BasicBlock* block : code) {
-        Liveness<Tmp>::LocalCalc localCalc(liveness, block);
+        GPLiveness::LocalCalc gpLocalCalc(gpLiveness, block);
+        FPLiveness::LocalCalc fpLocalCalc(fpLiveness, block);
+
         usedRegisters[block].resize(block->size() + 1);
 
         auto setUsedRegisters = [&] (unsigned index, Inst& inst) {
             RegisterSet& registerSet = usedRegisters[block][index];
-            for (Tmp tmp : localCalc.live()) {
+            for (Tmp tmp : gpLocalCalc.live()) {
                 if (tmp.isReg())
                     registerSet.set(tmp.reg());
             }
+            for (Tmp tmp : fpLocalCalc.live()) {
+                if (tmp.isReg())
+                    registerSet.set(tmp.reg());
+            }
 
             // Gotta account for dead assignments to registers. These may happen because the input
             // code is suboptimal.
@@ -69,7 +76,8 @@
         for (unsigned instIndex = block->size(); instIndex--;) {
             Inst& inst = block->at(instIndex);
             setUsedRegisters(instIndex + 1, inst);
-            localCalc.execute(instIndex);
+            gpLocalCalc.execute(instIndex);
+            fpLocalCalc.execute(instIndex);
         }
 
         Inst nop;

Modified: trunk/Source/_javascript_Core/b3/air/AirTmpInlines.h (192850 => 192851)


--- trunk/Source/_javascript_Core/b3/air/AirTmpInlines.h	2015-12-01 02:15:47 UTC (rev 192850)
+++ trunk/Source/_javascript_Core/b3/air/AirTmpInlines.h	2015-12-01 02:26:57 UTC (rev 192851)
@@ -38,6 +38,51 @@
     *this = arg.tmp();
 }
 
+// When a Hash structure is too slow or when Sets contains most values, you can
+// use direct array addressing with Tmps.
+template<Arg::Type type>
+struct AbsoluteTmpMapper;
+
+template<>
+struct AbsoluteTmpMapper<Arg::GP> {
+    static unsigned absoluteIndex(const Tmp& tmp)
+    {
+        ASSERT(tmp.isGP());
+        ASSERT(static_cast<int>(tmp.internalValue()) > 0);
+        return tmp.internalValue();
+    }
+
+    static unsigned absoluteIndex(unsigned tmpIndex)
+    {
+        return absoluteIndex(Tmp::gpTmpForIndex(tmpIndex));
+    }
+
+    static Tmp tmpFromAbsoluteIndex(unsigned tmpIndex)
+    {
+        return Tmp::tmpForInternalValue(tmpIndex);
+    }
+};
+
+template<>
+struct AbsoluteTmpMapper<Arg::FP> {
+    static unsigned absoluteIndex(const Tmp& tmp)
+    {
+        ASSERT(tmp.isFP());
+        ASSERT(static_cast<int>(tmp.internalValue()) < 0);
+        return -tmp.internalValue();
+    }
+
+    static unsigned absoluteIndex(unsigned tmpIndex)
+    {
+        return absoluteIndex(Tmp::fpTmpForIndex(tmpIndex));
+    }
+
+    static Tmp tmpFromAbsoluteIndex(unsigned tmpIndex)
+    {
+        return Tmp::tmpForInternalValue(-tmpIndex);
+    }
+};
+
 } } } // namespace JSC::B3::Air
 
 #endif // ENABLE(B3_JIT)

Modified: trunk/Source/WTF/ChangeLog (192850 => 192851)


--- trunk/Source/WTF/ChangeLog	2015-12-01 02:15:47 UTC (rev 192850)
+++ trunk/Source/WTF/ChangeLog	2015-12-01 02:26:57 UTC (rev 192851)
@@ -1,3 +1,20 @@
+2015-11-30  Benjamin Poulain  <bpoul...@apple.com>
+
+        [JSC] Speed up Air Liveness Analysis on Tmps
+        https://bugs.webkit.org/show_bug.cgi?id=151556
+
+        Reviewed by Filip Pizlo.
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/IndexSparseSet.h: Added.
+        (WTF::IndexSparseSet<OverflowHandler>::IndexSparseSet):
+        (WTF::IndexSparseSet<OverflowHandler>::add):
+        (WTF::IndexSparseSet<OverflowHandler>::remove):
+        (WTF::IndexSparseSet<OverflowHandler>::clear):
+        (WTF::IndexSparseSet<OverflowHandler>::size):
+        (WTF::IndexSparseSet<OverflowHandler>::isEmpty):
+        (WTF::IndexSparseSet<OverflowHandler>::contains):
+
 2015-11-30  Tim Horton  <timothy_hor...@apple.com>
 
         Get rid of the !USE(ASYNC_NSTEXTINPUTCLIENT) codepath

Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (192850 => 192851)


--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2015-12-01 02:15:47 UTC (rev 192850)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2015-12-01 02:26:57 UTC (rev 192851)
@@ -86,6 +86,7 @@
 		1FA47C8B152502DA00568D1B /* WebCoreThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 1FA47C89152502DA00568D1B /* WebCoreThread.h */; };
 		26147B0A15DDCCDC00DDB907 /* IntegerToStringConversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 26147B0815DDCCDC00DDB907 /* IntegerToStringConversion.h */; };
 		26299B6E17A9E5B800ADEBE5 /* Ref.h in Headers */ = {isa = PBXBuildFile; fileRef = 26299B6D17A9E5B800ADEBE5 /* Ref.h */; };
+		2684D4361C000D400081D663 /* IndexSparseSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 2684D4351C000D400081D663 /* IndexSparseSet.h */; };
 		2C05385415BC819000F21B96 /* GregorianDateTime.h in Headers */ = {isa = PBXBuildFile; fileRef = 2C05385315BC819000F21B96 /* GregorianDateTime.h */; };
 		2CCD892A15C0390200285083 /* GregorianDateTime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2CCD892915C0390200285083 /* GregorianDateTime.cpp */; };
 		2CDED0EF18115C38004DBA70 /* RunLoopCF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2CDED0EE18115C38004DBA70 /* RunLoopCF.cpp */; };
@@ -386,6 +387,7 @@
 		1FA47C89152502DA00568D1B /* WebCoreThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebCoreThread.h; sourceTree = "<group>"; };
 		26147B0815DDCCDC00DDB907 /* IntegerToStringConversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntegerToStringConversion.h; sourceTree = "<group>"; };
 		26299B6D17A9E5B800ADEBE5 /* Ref.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Ref.h; sourceTree = "<group>"; };
+		2684D4351C000D400081D663 /* IndexSparseSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IndexSparseSet.h; sourceTree = "<group>"; };
 		2C05385315BC819000F21B96 /* GregorianDateTime.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GregorianDateTime.h; sourceTree = "<group>"; };
 		2CCD892915C0390200285083 /* GregorianDateTime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GregorianDateTime.cpp; sourceTree = "<group>"; };
 		2CDED0EE18115C38004DBA70 /* RunLoopCF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RunLoopCF.cpp; sourceTree = "<group>"; };
@@ -801,6 +803,7 @@
 				A8A472B9151A825A004123FF /* HashTable.h */,
 				A8A472BA151A825A004123FF /* HashTraits.h */,
 				A8A472BB151A825A004123FF /* HexNumber.h */,
+				2684D4351C000D400081D663 /* IndexSparseSet.h */,
 				A8A472BC151A825A004123FF /* InlineASM.h */,
 				A70DA0821799F04D00529A9B /* Insertion.h */,
 				7CDD7FF7186D291E007433CD /* IteratorAdaptors.h */,
@@ -1229,6 +1232,7 @@
 				A748745317A0BDAE00FA04CB /* SixCharacterHash.h in Headers */,
 				A8A47426151A825B004123FF /* Spectrum.h in Headers */,
 				A8A47428151A825B004123FF /* StackBounds.h in Headers */,
+				2684D4361C000D400081D663 /* IndexSparseSet.h in Headers */,
 				FEDACD3E1630F83F00C69634 /* StackStats.h in Headers */,
 				A8A47429151A825B004123FF /* StaticConstructors.h in Headers */,
 				A8A4742A151A825B004123FF /* StdLibExtras.h in Headers */,

Added: trunk/Source/WTF/wtf/IndexSparseSet.h (0 => 192851)


--- trunk/Source/WTF/wtf/IndexSparseSet.h	                        (rev 0)
+++ trunk/Source/WTF/wtf/IndexSparseSet.h	2015-12-01 02:26:57 UTC (rev 192851)
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef IndexSparseSet_h
+#define IndexSparseSet_h
+
+#include <wtf/Vector.h>
+
+namespace WTF {
+
+// IndexSparseSet is an efficient set of integers that can only be valued
+// between zero and size() - 1.
+//
+// The implementation is using Briggs Sparse Set representation. We allocate
+// memory from 0 to size() - 1 to do mapping in O(1), but we never initialize
+// that memory. When adding/removing values to the set, they are added in a list
+// and the corresponding bucket is initialized to the position in the list.
+//
+// The assumption here is that we only need a sparse subset of number live at any
+// time.
+
+template<typename OverflowHandler = CrashOnOverflow>
+class IndexSparseSet {
+    typedef Vector<unsigned, 0, OverflowHandler> ValueList;
+public:
+    explicit IndexSparseSet(unsigned maxValue);
+
+    void add(unsigned);
+    void remove(unsigned);
+    void clear();
+
+    unsigned size() const;
+    bool isEmpty() const;
+    bool contains(unsigned) const;
+
+    typedef typename ValueList::const_iterator const_iterator;
+    const_iterator begin() const;
+    const_iterator end() const;
+
+private:
+    Vector<unsigned, 0, OverflowHandler, 1> m_map;
+    ValueList m_values;
+};
+
+template<typename OverflowHandler>
+inline IndexSparseSet<OverflowHandler>::IndexSparseSet(unsigned maxValue)
+{
+    m_map.resize(maxValue + 1);
+}
+
+template<typename OverflowHandler>
+inline void IndexSparseSet<OverflowHandler>::add(unsigned value)
+{
+    if (contains(value))
+        return;
+
+    unsigned newPosition = m_values.size();
+    m_values.append(value);
+    m_map[value] = newPosition;
+}
+
+template<typename OverflowHandler>
+inline void IndexSparseSet<OverflowHandler>::remove(unsigned value)
+{
+    unsigned position = m_map[value];
+    if (position >= m_values.size())
+        return;
+
+    if (m_values[position] == value) {
+        unsigned lastValue = m_values.last();
+        m_values[position] = lastValue;
+        m_map[lastValue] = position;
+        m_values.removeLast();
+    }
+}
+
+template<typename OverflowHandler>
+void IndexSparseSet<OverflowHandler>::clear()
+{
+    m_values.resize(0);
+}
+
+template<typename OverflowHandler>
+unsigned IndexSparseSet<OverflowHandler>::size() const
+{
+    return m_values.size();
+}
+
+template<typename OverflowHandler>
+bool IndexSparseSet<OverflowHandler>::isEmpty() const
+{
+    return !size();
+}
+
+template<typename OverflowHandler>
+bool IndexSparseSet<OverflowHandler>::contains(unsigned value) const
+{
+    unsigned position = m_map[value];
+    if (position >= m_values.size())
+        return false;
+
+    return m_values[position] == value;
+}
+
+template<typename OverflowHandler>
+auto IndexSparseSet<OverflowHandler>::begin() const -> const_iterator
+{
+    return m_values.begin();
+}
+
+template<typename OverflowHandler>
+auto IndexSparseSet<OverflowHandler>::end() const -> const_iterator
+{
+    return m_values.end();
+}
+
+} // namespace WTF
+
+using WTF::IndexSparseSet;
+
+#endif // IndexSparseSet_h
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to