<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[214917] trunk/Source</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/214917">214917</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2017-04-04 17:25:02 -0700 (Tue, 04 Apr 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>B3::fixSSA() needs a tune-up
https://bugs.webkit.org/show_bug.cgi?id=170485

Reviewed by Saam Barati.
        
Source/JavaScriptCore:

After the various optimizations to liveness, register allocation, and other phases, the
fixSSA() phase now looks like one of the top offenders. This includes a bunch of
changes to make this phase run faster. This is a ~7% wasm -O1 compile time progression.
        
Here's what I did:
        
- We now use IndexSparseSet instead of IndexMap for tracking variable values. This
  makes it cheaper to chew through small blocks while there is a non-trivial number of
  total variables.
        
- We now do a &quot;local SSA conversion&quot; pass before anything else. This eliminates
  obvious Get's. If we were using temporary Variables, it would eliminate many of
  those. That's useful for when we use demoteValues() and duplciateTails(). For wasm
  -O1, we mainly care about the fact that it makes a bunch of Set's dead.
        
- We now do a Set DCE pass after the local SSA but before SSA conversion. This ensures
  that any block-local live intervals of Variables disappear and don't need further
  consideration.
        
- We now cache the reaching defs calculation.
        
- We now perform the reaching defs calculation lazily.

* b3/B3FixSSA.cpp:
(JSC::B3::demoteValues):
(JSC::B3::fixSSA):
* b3/B3SSACalculator.cpp:
(JSC::B3::SSACalculator::reachingDefAtTail):
* b3/B3VariableLiveness.cpp:
(JSC::B3::VariableLiveness::VariableLiveness):
* b3/air/AirLiveness.h:
(JSC::B3::Air::Liveness::Liveness):
* dfg/DFGLivenessAnalysisPhase.cpp:
(JSC::DFG::LivenessAnalysisPhase::LivenessAnalysisPhase): Deleted.
(JSC::DFG::LivenessAnalysisPhase::run): Deleted.
(JSC::DFG::LivenessAnalysisPhase::processBlock): Deleted.

Source/WTF:

This makes IndexSparseSet capable of being used as a map if you instantiate it with
KeyValuePair&lt;unsigned, ValueType&gt;.

* wtf/HashTraits.h:
* wtf/IndexSparseSet.h:
(WTF::DefaultIndexSparseSetTraits::create):
(WTF::DefaultIndexSparseSetTraits::key):
(WTF::OverflowHandler&gt;::IndexSparseSet):
(WTF::OverflowHandler&gt;::add):
(WTF::OverflowHandler&gt;::set):
(WTF::OverflowHandler&gt;::remove):
(WTF::OverflowHandler&gt;::clear):
(WTF::OverflowHandler&gt;::size):
(WTF::OverflowHandler&gt;::isEmpty):
(WTF::OverflowHandler&gt;::contains):
(WTF::OverflowHandler&gt;::sort):
(WTF::IndexSparseSet&lt;OverflowHandler&gt;::IndexSparseSet): Deleted.
(WTF::IndexSparseSet&lt;OverflowHandler&gt;::add): Deleted.
(WTF::IndexSparseSet&lt;OverflowHandler&gt;::remove): Deleted.
(WTF::IndexSparseSet&lt;OverflowHandler&gt;::clear): Deleted.
(WTF::IndexSparseSet&lt;OverflowHandler&gt;::size): Deleted.
(WTF::IndexSparseSet&lt;OverflowHandler&gt;::isEmpty): Deleted.
(WTF::IndexSparseSet&lt;OverflowHandler&gt;::contains): Deleted.
(WTF::IndexSparseSet&lt;OverflowHandler&gt;::sort): Deleted.
* wtf/Liveness.h:
(WTF::Liveness::LocalCalc::Iterable::iterator::iterator):
(WTF::Liveness::workset):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3FixSSAcpp">trunk/Source/JavaScriptCore/b3/B3FixSSA.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3SSACalculatorcpp">trunk/Source/JavaScriptCore/b3/B3SSACalculator.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3B3VariableLivenesscpp">trunk/Source/JavaScriptCore/b3/B3VariableLiveness.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreb3airAirLivenessh">trunk/Source/JavaScriptCore/b3/air/AirLiveness.h</a></li>
<li><a href="#trunkSourceJavaScriptCoredfgDFGLivenessAnalysisPhasecpp">trunk/Source/JavaScriptCore/dfg/DFGLivenessAnalysisPhase.cpp</a></li>
<li><a href="#trunkSourceWTFChangeLog">trunk/Source/WTF/ChangeLog</a></li>
<li><a href="#trunkSourceWTFwtfHashTraitsh">trunk/Source/WTF/wtf/HashTraits.h</a></li>
<li><a href="#trunkSourceWTFwtfIndexSparseSeth">trunk/Source/WTF/wtf/IndexSparseSet.h</a></li>
<li><a href="#trunkSourceWTFwtfLivenessh">trunk/Source/WTF/wtf/Liveness.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (214916 => 214917)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2017-04-05 00:23:46 UTC (rev 214916)
+++ trunk/Source/JavaScriptCore/ChangeLog        2017-04-05 00:25:02 UTC (rev 214917)
</span><span class="lines">@@ -1,3 +1,47 @@
</span><ins>+2017-04-04  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        B3::fixSSA() needs a tune-up
+        https://bugs.webkit.org/show_bug.cgi?id=170485
+
+        Reviewed by Saam Barati.
+        
+        After the various optimizations to liveness, register allocation, and other phases, the
+        fixSSA() phase now looks like one of the top offenders. This includes a bunch of
+        changes to make this phase run faster. This is a ~7% wasm -O1 compile time progression.
+        
+        Here's what I did:
+        
+        - We now use IndexSparseSet instead of IndexMap for tracking variable values. This
+          makes it cheaper to chew through small blocks while there is a non-trivial number of
+          total variables.
+        
+        - We now do a &quot;local SSA conversion&quot; pass before anything else. This eliminates
+          obvious Get's. If we were using temporary Variables, it would eliminate many of
+          those. That's useful for when we use demoteValues() and duplciateTails(). For wasm
+          -O1, we mainly care about the fact that it makes a bunch of Set's dead.
+        
+        - We now do a Set DCE pass after the local SSA but before SSA conversion. This ensures
+          that any block-local live intervals of Variables disappear and don't need further
+          consideration.
+        
+        - We now cache the reaching defs calculation.
+        
+        - We now perform the reaching defs calculation lazily.
+
+        * b3/B3FixSSA.cpp:
+        (JSC::B3::demoteValues):
+        (JSC::B3::fixSSA):
+        * b3/B3SSACalculator.cpp:
+        (JSC::B3::SSACalculator::reachingDefAtTail):
+        * b3/B3VariableLiveness.cpp:
+        (JSC::B3::VariableLiveness::VariableLiveness):
+        * b3/air/AirLiveness.h:
+        (JSC::B3::Air::Liveness::Liveness):
+        * dfg/DFGLivenessAnalysisPhase.cpp:
+        (JSC::DFG::LivenessAnalysisPhase::LivenessAnalysisPhase): Deleted.
+        (JSC::DFG::LivenessAnalysisPhase::run): Deleted.
+        (JSC::DFG::LivenessAnalysisPhase::processBlock): Deleted.
+
</ins><span class="cx"> 2017-04-04  Joseph Pecoraro  &lt;pecoraro@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Remove stale LLVM Header Path includes from JavaScriptCore
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3FixSSAcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3FixSSA.cpp (214916 => 214917)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3FixSSA.cpp        2017-04-05 00:23:46 UTC (rev 214916)
+++ trunk/Source/JavaScriptCore/b3/B3FixSSA.cpp        2017-04-05 00:25:02 UTC (rev 214917)
</span><span class="lines">@@ -42,103 +42,83 @@
</span><span class="cx"> #include &quot;B3VariableValue.h&quot;
</span><span class="cx"> #include &lt;wtf/CommaPrinter.h&gt;
</span><span class="cx"> #include &lt;wtf/IndexSet.h&gt;
</span><ins>+#include &lt;wtf/IndexSparseSet.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace JSC { namespace B3 {
</span><span class="cx"> 
</span><span class="cx"> namespace {
</span><ins>+
</ins><span class="cx"> const bool verbose = false;
</span><del>-} // anonymous namespace
</del><span class="cx"> 
</span><del>-void demoteValues(Procedure&amp; proc, const IndexSet&lt;Value*&gt;&amp; values)
</del><ins>+void killDeadVariables(Procedure&amp; proc)
</ins><span class="cx"> {
</span><del>-    HashMap&lt;Value*, Variable*&gt; map;
-    HashMap&lt;Value*, Variable*&gt; phiMap;
</del><ins>+    IndexSet&lt;Variable*&gt; liveVariables;
+    for (Value* value : proc.values()) {
+        if (value-&gt;opcode() == Get)
+            liveVariables.add(value-&gt;as&lt;VariableValue&gt;()-&gt;variable());
+    }
</ins><span class="cx"> 
</span><del>-    // Create stack slots.
-    for (Value* value : values.values(proc.values())) {
-        map.add(value, proc.addVariable(value-&gt;type()));
-
-        if (value-&gt;opcode() == Phi)
-            phiMap.add(value, proc.addVariable(value-&gt;type()));
</del><ins>+    for (Value* value : proc.values()) {
+        if (value-&gt;opcode() == Set &amp;&amp; !liveVariables.contains(value-&gt;as&lt;VariableValue&gt;()-&gt;variable()))
+            value-&gt;replaceWithNop();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (verbose) {
-        dataLog(&quot;Demoting values as follows:\n&quot;);
-        dataLog(&quot;   map = &quot;);
-        CommaPrinter comma;
-        for (auto&amp; entry : map)
-            dataLog(comma, *entry.key, &quot;=&gt;&quot;, *entry.value);
-        dataLog(&quot;\n&quot;);
-        dataLog(&quot;   phiMap = &quot;);
-        comma = CommaPrinter();
-        for (auto&amp; entry : phiMap)
-            dataLog(comma, *entry.key, &quot;=&gt;&quot;, *entry.value);
-        dataLog(&quot;\n&quot;);
</del><ins>+    for (Variable* variable : proc.variables()) {
+        if (!liveVariables.contains(variable))
+            proc.deleteVariable(variable);
</ins><span class="cx">     }
</span><ins>+}
</ins><span class="cx"> 
</span><del>-    // Change accesses to the values to accesses to the stack slots.
-    InsertionSet insertionSet(proc);
-    for (BasicBlock* block : proc) {
</del><ins>+void fixSSALocally(Procedure&amp; proc)
+{
+    IndexSparseSet&lt;KeyValuePair&lt;unsigned, Value*&gt;&gt; mapping(proc.variables().size());
+    for (BasicBlock* block : proc.blocksInPreOrder()) {
+        mapping.clear();
+
</ins><span class="cx">         for (unsigned valueIndex = 0; valueIndex &lt; block-&gt;size(); ++valueIndex) {
</span><span class="cx">             Value* value = block-&gt;at(valueIndex);
</span><ins>+            value-&gt;performSubstitution();
</ins><span class="cx"> 
</span><del>-            if (value-&gt;opcode() == Phi) {
-                if (Variable* variable = phiMap.get(value)) {
-                    value-&gt;replaceWithIdentity(
-                        insertionSet.insert&lt;VariableValue&gt;(
-                            valueIndex, Get, value-&gt;origin(), variable));
-                }
-            } else {
-                for (Value*&amp; child : value-&gt;children()) {
-                    if (Variable* variable = map.get(child)) {
-                        child = insertionSet.insert&lt;VariableValue&gt;(
-                            valueIndex, Get, value-&gt;origin(), variable);
-                    }
-                }
</del><ins>+            switch (value-&gt;opcode()) {
+            case Get: {
+                VariableValue* variableValue = value-&gt;as&lt;VariableValue&gt;();
+                Variable* variable = variableValue-&gt;variable();
</ins><span class="cx"> 
</span><del>-                if (UpsilonValue* upsilon = value-&gt;as&lt;UpsilonValue&gt;()) {
-                    if (Variable* variable = phiMap.get(upsilon-&gt;phi())) {
-                        insertionSet.insert&lt;VariableValue&gt;(
-                            valueIndex, Set, upsilon-&gt;origin(), variable, upsilon-&gt;child(0));
-                        value-&gt;replaceWithNop();
-                    }
-                }
</del><ins>+                if (KeyValuePair&lt;unsigned, Value*&gt;* replacement = mapping.get(variable-&gt;index()))
+                    value-&gt;replaceWithIdentity(replacement-&gt;value);
+                break;
</ins><span class="cx">             }
</span><ins>+                
+            case Set: {
+                VariableValue* variableValue = value-&gt;as&lt;VariableValue&gt;();
+                Variable* variable = variableValue-&gt;variable();
</ins><span class="cx"> 
</span><del>-            if (Variable* variable = map.get(value)) {
-                insertionSet.insert&lt;VariableValue&gt;(
-                    valueIndex + 1, Set, value-&gt;origin(), variable, value);
</del><ins>+                mapping.set(variable-&gt;index(), value-&gt;child(0));
+                break;
</ins><span class="cx">             }
</span><ins>+
+            default:
+                break;
+            }
</ins><span class="cx">         }
</span><del>-        insertionSet.execute(block);
</del><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool fixSSA(Procedure&amp; proc)
</del><ins>+void fixSSAGlobally(Procedure&amp; proc)
</ins><span class="cx"> {
</span><del>-    PhaseScope phaseScope(proc, &quot;fixSSA&quot;);
-
-    // Just for sanity, remove any unused variables first. It's unlikely that this code has any
-    // bugs having to do with dead variables, but it would be silly to have to fix such a bug if
-    // it did arise.
-    IndexSet&lt;Variable*&gt; liveVariables;
-    for (Value* value : proc.values()) {
-        if (VariableValue* variableValue = value-&gt;as&lt;VariableValue&gt;())
-            liveVariables.add(variableValue-&gt;variable());
</del><ins>+    VariableLiveness liveness(proc);
+    
+    // Kill any dead Set's. Each Set creates work for us, so this is profitable.
+    for (BasicBlock* block : proc) {
+        VariableLiveness::LocalCalc localCalc(liveness, block);
+        for (unsigned valueIndex = block-&gt;size(); valueIndex--;) {
+            Value* value = block-&gt;at(valueIndex);
+            if (value-&gt;opcode() == Set &amp;&amp; !localCalc.isLive(value-&gt;as&lt;VariableValue&gt;()-&gt;variable()))
+                value-&gt;replaceWithNop();
+            localCalc.execute(valueIndex);
+        }
</ins><span class="cx">     }
</span><del>-
-    for (Variable* variable : proc.variables()) {
-        if (!liveVariables.contains(variable))
-            proc.deleteVariable(variable);
-    }
-
-    if (proc.variables().isEmpty())
-        return false;
-
-    // We know that we have variables to optimize, so do that now.
-    breakCriticalEdges(proc);
</del><span class="cx">     
</span><del>-    VariableLiveness liveness(proc);
</del><span class="cx">     VariableLiveness::LiveAtHead liveAtHead = liveness.liveAtHead();
</span><span class="cx">     
</span><span class="cx">     SSACalculator ssa(proc);
</span><span class="lines">@@ -168,39 +148,51 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Decide where Phis are to be inserted. This creates them but does not insert them.
</span><del>-    ssa.computePhis(
-        [&amp;] (SSACalculator::Variable* calcVar, BasicBlock* block) -&gt; Value* {
-            Variable* variable = calcVarToVariable[calcVar-&gt;index()];
-            if (!liveAtHead.isLiveAtHead(block, variable))
-                return nullptr;
-            
-            Value* phi = proc.add&lt;Value&gt;(Phi, variable-&gt;type(), block-&gt;at(0)-&gt;origin());
-            if (verbose) {
-                dataLog(
-                    &quot;Adding Phi for &quot;, pointerDump(variable), &quot; at &quot;, *block, &quot;: &quot;,
-                    deepDump(proc, phi), &quot;\n&quot;);
-            }
-            return phi;
-        });
</del><ins>+    {
+        TimingScope timingScope(&quot;fixSSA: computePhis&quot;);
+        ssa.computePhis(
+            [&amp;] (SSACalculator::Variable* calcVar, BasicBlock* block) -&gt; Value* {
+                Variable* variable = calcVarToVariable[calcVar-&gt;index()];
+                if (!liveAtHead.isLiveAtHead(block, variable))
+                    return nullptr;
+                
+                Value* phi = proc.add&lt;Value&gt;(Phi, variable-&gt;type(), block-&gt;at(0)-&gt;origin());
+                if (verbose) {
+                    dataLog(
+                        &quot;Adding Phi for &quot;, pointerDump(variable), &quot; at &quot;, *block, &quot;: &quot;,
+                        deepDump(proc, phi), &quot;\n&quot;);
+                }
+                return phi;
+            });
+    }
</ins><span class="cx"> 
</span><span class="cx">     // Now perform the conversion.
</span><ins>+    TimingScope timingScope(&quot;fixSSA: convert&quot;);
</ins><span class="cx">     InsertionSet insertionSet(proc);
</span><del>-    IndexMap&lt;Variable*, Value*&gt; mapping(proc.variables().size());
</del><ins>+    IndexSparseSet&lt;KeyValuePair&lt;unsigned, Value*&gt;&gt; mapping(proc.variables().size());
</ins><span class="cx">     for (BasicBlock* block : proc.blocksInPreOrder()) {
</span><span class="cx">         mapping.clear();
</span><del>-
-        for (Variable* variable : liveness.liveAtHead(block)) {
</del><ins>+        
+        auto ensureMapping = [&amp;] (Variable* variable, unsigned valueIndex, Origin origin) -&gt; Value* {
+            KeyValuePair&lt;unsigned, Value*&gt;* found = mapping.get(variable-&gt;index());
+            if (found)
+                return found-&gt;value;
+            
</ins><span class="cx">             SSACalculator::Variable* calcVar = variableToCalcVar[variable];
</span><span class="cx">             SSACalculator::Def* def = ssa.reachingDefAtHead(block, calcVar);
</span><del>-            if (def)
-                mapping[variable] = def-&gt;value();
-        }
</del><ins>+            if (def) {
+                mapping.set(variable-&gt;index(), def-&gt;value());
+                return def-&gt;value();
+            }
+            
+            return insertionSet.insertBottom(valueIndex, origin, variable-&gt;type());
+        };
</ins><span class="cx"> 
</span><span class="cx">         for (SSACalculator::Def* phiDef : ssa.phisForBlock(block)) {
</span><span class="cx">             Variable* variable = calcVarToVariable[phiDef-&gt;variable()-&gt;index()];
</span><span class="cx"> 
</span><span class="cx">             insertionSet.insertValue(0, phiDef-&gt;value());
</span><del>-            mapping[variable] = phiDef-&gt;value();
</del><ins>+            mapping.set(variable-&gt;index(), phiDef-&gt;value());
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         for (unsigned valueIndex = 0; valueIndex &lt; block-&gt;size(); ++valueIndex) {
</span><span class="lines">@@ -212,12 +204,7 @@
</span><span class="cx">                 VariableValue* variableValue = value-&gt;as&lt;VariableValue&gt;();
</span><span class="cx">                 Variable* variable = variableValue-&gt;variable();
</span><span class="cx"> 
</span><del>-                if (Value* replacement = mapping[variable])
-                    value-&gt;replaceWithIdentity(replacement);
-                else {
-                    value-&gt;replaceWithIdentity(
-                        insertionSet.insertBottom(valueIndex, value));
-                }
</del><ins>+                value-&gt;replaceWithIdentity(ensureMapping(variable, valueIndex, value-&gt;origin()));
</ins><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="cx">                 
</span><span class="lines">@@ -225,7 +212,7 @@
</span><span class="cx">                 VariableValue* variableValue = value-&gt;as&lt;VariableValue&gt;();
</span><span class="cx">                 Variable* variable = variableValue-&gt;variable();
</span><span class="cx"> 
</span><del>-                mapping[variable] = value-&gt;child(0);
</del><ins>+                mapping.set(variable-&gt;index(), value-&gt;child(0));
</ins><span class="cx">                 value-&gt;replaceWithNop();
</span><span class="cx">                 break;
</span><span class="cx">             }
</span><span class="lines">@@ -243,7 +230,7 @@
</span><span class="cx">                 SSACalculator::Variable* calcVar = phiDef-&gt;variable();
</span><span class="cx">                 Variable* variable = calcVarToVariable[calcVar-&gt;index()];
</span><span class="cx"> 
</span><del>-                Value* mappedValue = mapping[variable];
</del><ins>+                Value* mappedValue = ensureMapping(variable, upsilonInsertionPoint, upsilonOrigin);
</ins><span class="cx">                 if (verbose) {
</span><span class="cx">                     dataLog(
</span><span class="cx">                         &quot;Mapped value for &quot;, *variable, &quot; with successor Phi &quot;, *phi,
</span><span class="lines">@@ -250,9 +237,6 @@
</span><span class="cx">                         &quot; at end of &quot;, *block, &quot;: &quot;, pointerDump(mappedValue), &quot;\n&quot;);
</span><span class="cx">                 }
</span><span class="cx">                 
</span><del>-                if (!mappedValue)
-                    mappedValue = insertionSet.insertBottom(upsilonInsertionPoint, phi);
-                
</del><span class="cx">                 insertionSet.insert&lt;UpsilonValue&gt;(
</span><span class="cx">                     upsilonInsertionPoint, upsilonOrigin, mappedValue, phi);
</span><span class="cx">             }
</span><span class="lines">@@ -265,7 +249,96 @@
</span><span class="cx">         dataLog(&quot;B3 after SSA conversion:\n&quot;);
</span><span class="cx">         dataLog(proc);
</span><span class="cx">     }
</span><ins>+}
</ins><span class="cx"> 
</span><ins>+} // anonymous namespace
+
+void demoteValues(Procedure&amp; proc, const IndexSet&lt;Value*&gt;&amp; values)
+{
+    HashMap&lt;Value*, Variable*&gt; map;
+    HashMap&lt;Value*, Variable*&gt; phiMap;
+
+    // Create stack slots.
+    for (Value* value : values.values(proc.values())) {
+        map.add(value, proc.addVariable(value-&gt;type()));
+
+        if (value-&gt;opcode() == Phi)
+            phiMap.add(value, proc.addVariable(value-&gt;type()));
+    }
+
+    if (verbose) {
+        dataLog(&quot;Demoting values as follows:\n&quot;);
+        dataLog(&quot;   map = &quot;);
+        CommaPrinter comma;
+        for (auto&amp; entry : map)
+            dataLog(comma, *entry.key, &quot;=&gt;&quot;, *entry.value);
+        dataLog(&quot;\n&quot;);
+        dataLog(&quot;   phiMap = &quot;);
+        comma = CommaPrinter();
+        for (auto&amp; entry : phiMap)
+            dataLog(comma, *entry.key, &quot;=&gt;&quot;, *entry.value);
+        dataLog(&quot;\n&quot;);
+    }
+
+    // Change accesses to the values to accesses to the stack slots.
+    InsertionSet insertionSet(proc);
+    for (BasicBlock* block : proc) {
+        for (unsigned valueIndex = 0; valueIndex &lt; block-&gt;size(); ++valueIndex) {
+            Value* value = block-&gt;at(valueIndex);
+
+            if (value-&gt;opcode() == Phi) {
+                if (Variable* variable = phiMap.get(value)) {
+                    value-&gt;replaceWithIdentity(
+                        insertionSet.insert&lt;VariableValue&gt;(
+                            valueIndex, Get, value-&gt;origin(), variable));
+                }
+            } else {
+                for (Value*&amp; child : value-&gt;children()) {
+                    if (Variable* variable = map.get(child)) {
+                        child = insertionSet.insert&lt;VariableValue&gt;(
+                            valueIndex, Get, value-&gt;origin(), variable);
+                    }
+                }
+
+                if (UpsilonValue* upsilon = value-&gt;as&lt;UpsilonValue&gt;()) {
+                    if (Variable* variable = phiMap.get(upsilon-&gt;phi())) {
+                        insertionSet.insert&lt;VariableValue&gt;(
+                            valueIndex, Set, upsilon-&gt;origin(), variable, upsilon-&gt;child(0));
+                        value-&gt;replaceWithNop();
+                    }
+                }
+            }
+
+            if (Variable* variable = map.get(value)) {
+                insertionSet.insert&lt;VariableValue&gt;(
+                    valueIndex + 1, Set, value-&gt;origin(), variable, value);
+            }
+        }
+        insertionSet.execute(block);
+    }
+}
+
+bool fixSSA(Procedure&amp; proc)
+{
+    PhaseScope phaseScope(proc, &quot;fixSSA&quot;);
+
+    // Lots of variables have trivial local liveness. We can allocate those without any
+    // trouble.
+    fixSSALocally(proc);
+
+    // Just for sanity, remove any unused variables first. It's unlikely that this code has any
+    // bugs having to do with dead variables, but it would be silly to have to fix such a bug if
+    // it did arise.
+    killDeadVariables(proc);
+    
+    if (proc.variables().isEmpty())
+        return false;
+    
+    // We know that we have variables to optimize, so do that now.
+    breakCriticalEdges(proc);
+
+    fixSSAGlobally(proc);
+    
</ins><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3SSACalculatorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3SSACalculator.cpp (214916 => 214917)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3SSACalculator.cpp        2017-04-05 00:23:46 UTC (rev 214916)
+++ trunk/Source/JavaScriptCore/b3/B3SSACalculator.cpp        2017-04-05 00:25:02 UTC (rev 214917)
</span><span class="lines">@@ -98,11 +98,14 @@
</span><span class="cx">     return reachingDefAtTail(m_dominators-&gt;idom(block), variable);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-SSACalculator::Def* SSACalculator::reachingDefAtTail(BasicBlock* block, Variable* variable)
</del><ins>+SSACalculator::Def* SSACalculator::reachingDefAtTail(BasicBlock* startingBlock, Variable* variable)
</ins><span class="cx"> {
</span><del>-    for (; block; block = m_dominators-&gt;idom(block)) {
-        if (Def* def = m_data[block].m_defs.get(variable))
</del><ins>+    for (BasicBlock* block = startingBlock; block; block = m_dominators-&gt;idom(block)) {
+        if (Def* def = m_data[block].m_defs.get(variable)) {
+            for (BasicBlock* otherBlock = startingBlock; otherBlock != block; otherBlock = m_dominators-&gt;idom(otherBlock))
+                m_data[block].m_defs.add(variable, def);
</ins><span class="cx">             return def;
</span><ins>+        }
</ins><span class="cx">     }
</span><span class="cx">     return nullptr;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3B3VariableLivenesscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/B3VariableLiveness.cpp (214916 => 214917)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/B3VariableLiveness.cpp        2017-04-05 00:23:46 UTC (rev 214916)
+++ trunk/Source/JavaScriptCore/b3/B3VariableLiveness.cpp        2017-04-05 00:25:02 UTC (rev 214917)
</span><span class="lines">@@ -28,11 +28,14 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(B3_JIT)
</span><span class="cx"> 
</span><ins>+#include &quot;B3TimingScope.h&quot;
+
</ins><span class="cx"> namespace JSC { namespace B3 {
</span><span class="cx"> 
</span><span class="cx"> VariableLiveness::VariableLiveness(Procedure&amp; proc)
</span><span class="cx">     : WTF::Liveness&lt;VariableLivenessAdapter&gt;(proc.cfg(), proc)
</span><span class="cx"> {
</span><ins>+    TimingScope timingScope(&quot;B3::VariableLiveness&quot;);
</ins><span class="cx">     compute();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreb3airAirLivenessh"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/b3/air/AirLiveness.h (214916 => 214917)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/b3/air/AirLiveness.h        2017-04-05 00:23:46 UTC (rev 214916)
+++ trunk/Source/JavaScriptCore/b3/air/AirLiveness.h        2017-04-05 00:25:02 UTC (rev 214917)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> #if ENABLE(B3_JIT)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;AirLivenessAdapter.h&quot;
</span><ins>+#include &quot;B3TimingScope.h&quot;
</ins><span class="cx"> #include &lt;wtf/Liveness.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace JSC { namespace B3 { namespace Air {
</span><span class="lines">@@ -39,6 +40,7 @@
</span><span class="cx">         : WTF::Liveness&lt;Adapter&gt;(code.cfg(), code)
</span><span class="cx">     {
</span><span class="cx">         SuperSamplerScope samplingScope(false);
</span><ins>+        TimingScope timingScope(&quot;Air::Liveness&quot;);
</ins><span class="cx">         WTF::Liveness&lt;Adapter&gt;::compute();
</span><span class="cx">     }
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoredfgDFGLivenessAnalysisPhasecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/dfg/DFGLivenessAnalysisPhase.cpp (214916 => 214917)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/dfg/DFGLivenessAnalysisPhase.cpp        2017-04-05 00:23:46 UTC (rev 214916)
+++ trunk/Source/JavaScriptCore/dfg/DFGLivenessAnalysisPhase.cpp        2017-04-05 00:25:02 UTC (rev 214917)
</span><span class="lines">@@ -41,6 +41,8 @@
</span><span class="cx"> 
</span><span class="cx"> namespace JSC { namespace DFG {
</span><span class="cx"> 
</span><ins>+namespace {
+
</ins><span class="cx"> // Uncomment this to log hashtable operations.
</span><span class="cx"> // static const char templateString[] = &quot;unsigned, DefaultHash&lt;unsigned&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;unsigned&gt;&quot;;
</span><span class="cx"> // typedef LoggingHashSet&lt;templateString, unsigned, DefaultHash&lt;unsigned&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;unsigned&gt;&gt; LiveSet;
</span><span class="lines">@@ -47,6 +49,8 @@
</span><span class="cx"> 
</span><span class="cx"> typedef HashSet&lt;unsigned, DefaultHash&lt;unsigned&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;unsigned&gt;&gt; LiveSet;
</span><span class="cx"> 
</span><ins>+typedef IndexSparseSet&lt;unsigned, DefaultIndexSparseSetTraits&lt;unsigned&gt;, UnsafeVectorOverflow&gt; Workset;
+
</ins><span class="cx"> class LivenessAnalysisPhase : public Phase {
</span><span class="cx"> public:
</span><span class="cx">     LivenessAnalysisPhase(Graph&amp; graph)
</span><span class="lines">@@ -57,7 +61,7 @@
</span><span class="cx">         , m_liveAtTail(m_graph)
</span><span class="cx">     {
</span><span class="cx">         m_graph.m_indexingCache-&gt;recompute();
</span><del>-        m_workset = std::make_unique&lt;IndexSparseSet&lt;UnsafeVectorOverflow&gt;&gt;(m_graph.m_indexingCache-&gt;numIndices());
</del><ins>+        m_workset = std::make_unique&lt;Workset&gt;(m_graph.m_indexingCache-&gt;numIndices());
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     bool run()
</span><span class="lines">@@ -185,9 +189,11 @@
</span><span class="cx">     BlockMap&lt;LiveSet&gt; m_liveAtTail;
</span><span class="cx"> 
</span><span class="cx">     // Single sparse set allocated once and used by every basic block.
</span><del>-    std::unique_ptr&lt;IndexSparseSet&lt;UnsafeVectorOverflow&gt;&gt; m_workset;
</del><ins>+    std::unique_ptr&lt;Workset&gt; m_workset;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><ins>+} // anonymous namespace
+
</ins><span class="cx"> bool performLivenessAnalysis(Graph&amp; graph)
</span><span class="cx"> {
</span><span class="cx">     graph.packNodeIndices();
</span></span></pre></div>
<a id="trunkSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/ChangeLog (214916 => 214917)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/ChangeLog        2017-04-05 00:23:46 UTC (rev 214916)
+++ trunk/Source/WTF/ChangeLog        2017-04-05 00:25:02 UTC (rev 214917)
</span><span class="lines">@@ -1,5 +1,40 @@
</span><span class="cx"> 2017-04-04  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><ins>+        B3::fixSSA() needs a tune-up
+        https://bugs.webkit.org/show_bug.cgi?id=170485
+
+        Reviewed by Saam Barati.
+        
+        This makes IndexSparseSet capable of being used as a map if you instantiate it with
+        KeyValuePair&lt;unsigned, ValueType&gt;.
+
+        * wtf/HashTraits.h:
+        * wtf/IndexSparseSet.h:
+        (WTF::DefaultIndexSparseSetTraits::create):
+        (WTF::DefaultIndexSparseSetTraits::key):
+        (WTF::OverflowHandler&gt;::IndexSparseSet):
+        (WTF::OverflowHandler&gt;::add):
+        (WTF::OverflowHandler&gt;::set):
+        (WTF::OverflowHandler&gt;::remove):
+        (WTF::OverflowHandler&gt;::clear):
+        (WTF::OverflowHandler&gt;::size):
+        (WTF::OverflowHandler&gt;::isEmpty):
+        (WTF::OverflowHandler&gt;::contains):
+        (WTF::OverflowHandler&gt;::sort):
+        (WTF::IndexSparseSet&lt;OverflowHandler&gt;::IndexSparseSet): Deleted.
+        (WTF::IndexSparseSet&lt;OverflowHandler&gt;::add): Deleted.
+        (WTF::IndexSparseSet&lt;OverflowHandler&gt;::remove): Deleted.
+        (WTF::IndexSparseSet&lt;OverflowHandler&gt;::clear): Deleted.
+        (WTF::IndexSparseSet&lt;OverflowHandler&gt;::size): Deleted.
+        (WTF::IndexSparseSet&lt;OverflowHandler&gt;::isEmpty): Deleted.
+        (WTF::IndexSparseSet&lt;OverflowHandler&gt;::contains): Deleted.
+        (WTF::IndexSparseSet&lt;OverflowHandler&gt;::sort): Deleted.
+        * wtf/Liveness.h:
+        (WTF::Liveness::LocalCalc::Iterable::iterator::iterator):
+        (WTF::Liveness::workset):
+
+2017-04-04  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
</ins><span class="cx">         Don't need to Air::reportUsedRegisters for wasm at -O1
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=170459
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWTFwtfHashTraitsh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/HashTraits.h (214916 => 214917)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/HashTraits.h        2017-04-05 00:23:46 UTC (rev 214916)
+++ trunk/Source/WTF/wtf/HashTraits.h        2017-04-05 00:25:02 UTC (rev 214917)
</span><span class="lines">@@ -371,6 +371,7 @@
</span><span class="cx"> } // namespace WTF
</span><span class="cx"> 
</span><span class="cx"> using WTF::HashTraits;
</span><ins>+using WTF::KeyValuePair;
</ins><span class="cx"> using WTF::PairHashTraits;
</span><span class="cx"> using WTF::NullableHashTraits;
</span><span class="cx"> using WTF::SimpleClassHashTraits;
</span></span></pre></div>
<a id="trunkSourceWTFwtfIndexSparseSeth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/IndexSparseSet.h (214916 => 214917)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/IndexSparseSet.h        2017-04-05 00:23:46 UTC (rev 214916)
+++ trunk/Source/WTF/wtf/IndexSparseSet.h        2017-04-05 00:25:02 UTC (rev 214917)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2015 Apple Inc. All Rights Reserved.
</del><ins>+ * Copyright (C) 2015-2017 Apple Inc. All Rights Reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #ifndef IndexSparseSet_h
</span><span class="cx"> #define IndexSparseSet_h
</span><span class="cx"> 
</span><ins>+#include &lt;wtf/HashTraits.h&gt;
</ins><span class="cx"> #include &lt;wtf/Vector.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace WTF {
</span><span class="lines">@@ -41,13 +42,47 @@
</span><span class="cx"> // The assumption here is that we only need a sparse subset of number live at any
</span><span class="cx"> // time.
</span><span class="cx"> 
</span><del>-template&lt;typename OverflowHandler = CrashOnOverflow&gt;
</del><ins>+template&lt;typename T&gt;
+struct DefaultIndexSparseSetTraits {
+    typedef T EntryType;
+    
+    static T create(unsigned entry)
+    {
+        return entry;
+    }
+    
+    static unsigned key(const T&amp; entry)
+    {
+        return entry;
+    }
+};
+
+template&lt;typename KeyType, typename ValueType&gt;
+struct DefaultIndexSparseSetTraits&lt;KeyValuePair&lt;KeyType, ValueType&gt;&gt; {
+    typedef KeyValuePair&lt;KeyType, ValueType&gt; EntryType;
+
+    template&lt;typename PassedValueType&gt;
+    static EntryType create(unsigned key, PassedValueType&amp;&amp; value)
+    {
+        return EntryType(key, std::forward&lt;PassedValueType&gt;(value));
+    }
+    
+    static unsigned key(const EntryType&amp; entry)
+    {
+        return entry.key;
+    }
+};
+
+template&lt;typename EntryType = unsigned, typename EntryTypeTraits = DefaultIndexSparseSetTraits&lt;EntryType&gt;, typename OverflowHandler = CrashOnOverflow&gt;
</ins><span class="cx"> class IndexSparseSet {
</span><del>-    typedef Vector&lt;unsigned, 0, OverflowHandler&gt; ValueList;
</del><ins>+    typedef Vector&lt;EntryType, 0, OverflowHandler&gt; ValueList;
</ins><span class="cx"> public:
</span><span class="cx">     explicit IndexSparseSet(unsigned size);
</span><span class="cx"> 
</span><del>-    bool add(unsigned);
</del><ins>+    template&lt;typename... Arguments&gt;
+    bool add(unsigned, Arguments&amp;&amp;...);
+    template&lt;typename... Arguments&gt;
+    bool set(unsigned, Arguments&amp;&amp;...);
</ins><span class="cx">     bool remove(unsigned);
</span><span class="cx">     void clear();
</span><span class="cx"> 
</span><span class="lines">@@ -54,6 +89,8 @@
</span><span class="cx">     unsigned size() const;
</span><span class="cx">     bool isEmpty() const;
</span><span class="cx">     bool contains(unsigned) const;
</span><ins>+    const EntryType* get(unsigned) const;
+    EntryType* get(unsigned);
</ins><span class="cx"> 
</span><span class="cx">     typedef typename ValueList::const_iterator const_iterator;
</span><span class="cx">     const_iterator begin() const;
</span><span class="lines">@@ -68,35 +105,51 @@
</span><span class="cx">     ValueList m_values;
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-template&lt;typename OverflowHandler&gt;
-inline IndexSparseSet&lt;OverflowHandler&gt;::IndexSparseSet(unsigned size)
</del><ins>+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+inline IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::IndexSparseSet(unsigned size)
</ins><span class="cx"> {
</span><span class="cx">     m_map.resize(size);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename OverflowHandler&gt;
-inline bool IndexSparseSet&lt;OverflowHandler&gt;::add(unsigned value)
</del><ins>+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+template&lt;typename... Arguments&gt;
+inline bool IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::add(unsigned value, Arguments&amp;&amp;... arguments)
</ins><span class="cx"> {
</span><span class="cx">     if (contains(value))
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="cx">     unsigned newPosition = m_values.size();
</span><del>-    m_values.append(value);
</del><ins>+    m_values.append(EntryTypeTraits::create(value, std::forward&lt;Arguments&gt;(arguments)...));
</ins><span class="cx">     m_map[value] = newPosition;
</span><span class="cx">     return true;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename OverflowHandler&gt;
-inline bool IndexSparseSet&lt;OverflowHandler&gt;::remove(unsigned value)
</del><ins>+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+template&lt;typename... Arguments&gt;
+inline bool IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::set(unsigned value, Arguments&amp;&amp;... arguments)
</ins><span class="cx"> {
</span><ins>+    if (EntryType* entry = get(value)) {
+        *entry = EntryTypeTraits::create(value, std::forward&lt;Arguments&gt;(arguments)...);
+        return false;
+    }
+
+    unsigned newPosition = m_values.size();
+    m_values.append(EntryTypeTraits::create(value, std::forward&lt;Arguments&gt;(arguments)...));
+    m_map[value] = newPosition;
+    return true;
+}
+
+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+inline bool IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::remove(unsigned value)
+{
</ins><span class="cx">     unsigned position = m_map[value];
</span><span class="cx">     if (position &gt;= m_values.size())
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><span class="cx">     if (m_values[position] == value) {
</span><del>-        unsigned lastValue = m_values.last();
-        m_values[position] = lastValue;
-        m_map[lastValue] = position;
</del><ins>+        EntryType lastValue = m_values.last();
+        m_values[position] = WTFMove(lastValue);
+        m_map[EntryTypeTraits::key(lastValue)] = position;
</ins><span class="cx">         m_values.removeLast();
</span><span class="cx">         return true;
</span><span class="cx">     }
</span><span class="lines">@@ -104,48 +157,72 @@
</span><span class="cx">     return false;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename OverflowHandler&gt;
-void IndexSparseSet&lt;OverflowHandler&gt;::clear()
</del><ins>+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+void IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::clear()
</ins><span class="cx"> {
</span><span class="cx">     m_values.resize(0);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename OverflowHandler&gt;
-unsigned IndexSparseSet&lt;OverflowHandler&gt;::size() const
</del><ins>+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+unsigned IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::size() const
</ins><span class="cx"> {
</span><span class="cx">     return m_values.size();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename OverflowHandler&gt;
-bool IndexSparseSet&lt;OverflowHandler&gt;::isEmpty() const
</del><ins>+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+bool IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::isEmpty() const
</ins><span class="cx"> {
</span><span class="cx">     return !size();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename OverflowHandler&gt;
-bool IndexSparseSet&lt;OverflowHandler&gt;::contains(unsigned value) const
</del><ins>+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+bool IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::contains(unsigned value) const
</ins><span class="cx"> {
</span><span class="cx">     unsigned position = m_map[value];
</span><span class="cx">     if (position &gt;= m_values.size())
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    return m_values[position] == value;
</del><ins>+    return EntryTypeTraits::key(m_values[position]) == value;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename OverflowHandler&gt;
-void IndexSparseSet&lt;OverflowHandler&gt;::sort()
</del><ins>+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+auto IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::get(unsigned value) -&gt; EntryType*
</ins><span class="cx"> {
</span><del>-    std::sort(m_values.begin(), m_values.end());
</del><ins>+    unsigned position = m_map[value];
+    if (position &gt;= m_values.size())
+        return nullptr;
+
+    EntryType&amp; entry = m_values[position];
+    if (EntryTypeTraits::key(entry) != value)
+        return nullptr;
+    
+    return &amp;entry;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename OverflowHandler&gt;
-auto IndexSparseSet&lt;OverflowHandler&gt;::begin() const -&gt; const_iterator
</del><ins>+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+auto IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::get(unsigned value) const -&gt; const EntryType*
</ins><span class="cx"> {
</span><ins>+    return const_cast&lt;IndexSparseSet*&gt;(this)-&gt;get(value);
+}
+
+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+void IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::sort()
+{
+    std::sort(
+        m_values.begin(), m_values.end(),
+        [&amp;] (const EntryType&amp; a, const EntryType&amp; b) {
+            return EntryTypeTraits::key(a) &lt; EntryTypeTraits::key(b);
+        });
+}
+
+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+auto IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::begin() const -&gt; const_iterator
+{
</ins><span class="cx">     return m_values.begin();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-template&lt;typename OverflowHandler&gt;
-auto IndexSparseSet&lt;OverflowHandler&gt;::end() const -&gt; const_iterator
</del><ins>+template&lt;typename EntryType, typename EntryTypeTraits, typename OverflowHandler&gt;
+auto IndexSparseSet&lt;EntryType, EntryTypeTraits, OverflowHandler&gt;::end() const -&gt; const_iterator
</ins><span class="cx"> {
</span><span class="cx">     return m_values.end();
</span><span class="cx"> }
</span><span class="lines">@@ -152,6 +229,7 @@
</span><span class="cx"> 
</span><span class="cx"> } // namespace WTF
</span><span class="cx"> 
</span><ins>+using WTF::DefaultIndexSparseSetTraits;
</ins><span class="cx"> using WTF::IndexSparseSet;
</span><span class="cx"> 
</span><span class="cx"> #endif // IndexSparseSet_h
</span></span></pre></div>
<a id="trunkSourceWTFwtfLivenessh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WTF/wtf/Liveness.h (214916 => 214917)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WTF/wtf/Liveness.h        2017-04-05 00:23:46 UTC (rev 214916)
+++ trunk/Source/WTF/wtf/Liveness.h        2017-04-05 00:25:02 UTC (rev 214917)
</span><span class="lines">@@ -40,6 +40,7 @@
</span><span class="cx">     typedef typename Adapter::CFG CFG;
</span><span class="cx">     typedef typename Adapter::Thing Thing;
</span><span class="cx">     typedef Vector&lt;unsigned, 4, UnsafeVectorOverflow&gt; IndexVector;
</span><ins>+    typedef IndexSparseSet&lt;unsigned, DefaultIndexSparseSetTraits&lt;unsigned&gt;, UnsafeVectorOverflow&gt; Workset;
</ins><span class="cx">     
</span><span class="cx">     template&lt;typename... Arguments&gt;
</span><span class="cx">     Liveness(CFG&amp; cfg, Arguments&amp;&amp;... arguments)
</span><span class="lines">@@ -74,7 +75,7 @@
</span><span class="cx"> 
</span><span class="cx">             class iterator {
</span><span class="cx">             public:
</span><del>-                iterator(Adapter&amp; adapter, IndexSparseSet&lt;UnsafeVectorOverflow&gt;::const_iterator sparceSetIterator)
</del><ins>+                iterator(Adapter&amp; adapter, Workset::const_iterator sparceSetIterator)
</ins><span class="cx">                     : m_adapter(adapter)
</span><span class="cx">                     , m_sparceSetIterator(sparceSetIterator)
</span><span class="cx">                 {
</span><span class="lines">@@ -96,7 +97,7 @@
</span><span class="cx"> 
</span><span class="cx">             private:
</span><span class="cx">                 Adapter&amp; m_adapter;
</span><del>-                IndexSparseSet&lt;UnsafeVectorOverflow&gt;::const_iterator m_sparceSetIterator;
</del><ins>+                Workset::const_iterator m_sparceSetIterator;
</ins><span class="cx">             };
</span><span class="cx"> 
</span><span class="cx">             iterator begin() const { return iterator(m_liveness, m_liveness.m_workset.begin()); }
</span><span class="lines">@@ -227,7 +228,7 @@
</span><span class="cx">         return Iterable&lt;IndexVector&gt;(*this, m_liveAtTail[block]);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    IndexSparseSet&lt;UnsafeVectorOverflow&gt;&amp; workset() { return m_workset; }
</del><ins>+    Workset&amp; workset() { return m_workset; }
</ins><span class="cx">     
</span><span class="cx">     class LiveAtHead {
</span><span class="cx">     public:
</span><span class="lines">@@ -359,7 +360,7 @@
</span><span class="cx">     friend class LocalCalc::Iterable;
</span><span class="cx"> 
</span><span class="cx">     CFG&amp; m_cfg;
</span><del>-    IndexSparseSet&lt;UnsafeVectorOverflow&gt; m_workset;
</del><ins>+    Workset m_workset;
</ins><span class="cx">     typename CFG::template Map&lt;IndexVector&gt; m_liveAtHead;
</span><span class="cx">     typename CFG::template Map&lt;IndexVector&gt; m_liveAtTail;
</span><span class="cx"> };
</span></span></pre>
</div>
</div>

</body>
</html>