<!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>[171051] branches/ftlopt/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/171051">171051</a></dd>
<dt>Author</dt> <dd>fpizlo@apple.com</dd>
<dt>Date</dt> <dd>2014-07-13 10:18:00 -0700 (Sun, 13 Jul 2014)</dd>
</dl>

<h3>Log Message</h3>
<pre>Merge trunk <a href="http://trac.webkit.org/projects/webkit/changeset/171049">r171049</a>.

    2014-07-13  Filip Pizlo  &lt;fpizlo@apple.com&gt;
    
    HashMap should have removeIf()
    https://bugs.webkit.org/show_bug.cgi?id=134870
    
    Reviewed by Sam Weinig.
            
    Expose a new HashMap method, called removeIf(), which allows you to do an efficient
    pass over the map and remove a bunch of things at once. This is used by DFG GCSE as
    part of https://bugs.webkit.org/show_bug.cgi?id=134677.
    
    * wtf/HashMap.h:
    (WTF::X&gt;::removeIf):
    * wtf/HashTable.h:
    (WTF::KeyTraits&gt;::removeIf):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#branchesftloptSourceJavaScriptCoredfgDFGCSEPhasecpp">branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp</a></li>
<li><a href="#branchesftloptSourceWTFChangeLog">branches/ftlopt/Source/WTF/ChangeLog</a></li>
<li><a href="#branchesftloptSourceWTFwtfHashMaph">branches/ftlopt/Source/WTF/wtf/HashMap.h</a></li>
<li><a href="#branchesftloptSourceWTFwtfHashTableh">branches/ftlopt/Source/WTF/wtf/HashTable.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="branchesftloptSourceJavaScriptCoredfgDFGCSEPhasecpp"></a>
<div class="modfile"><h4>Modified: branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp (171050 => 171051)</h4>
<pre class="diff"><span>
<span class="info">--- branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp        2014-07-13 17:09:13 UTC (rev 171050)
+++ branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp        2014-07-13 17:18:00 UTC (rev 171051)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> #if ENABLE(DFG_JIT)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;DFGAbstractHeap.h&quot;
</span><ins>+#include &quot;DFGClobberSet.h&quot;
</ins><span class="cx"> #include &quot;DFGClobberize.h&quot;
</span><span class="cx"> #include &quot;DFGEdgeUsesStructure.h&quot;
</span><span class="cx"> #include &quot;DFGGraph.h&quot;
</span><span class="lines">@@ -39,984 +40,479 @@
</span><span class="cx"> 
</span><span class="cx"> namespace JSC { namespace DFG {
</span><span class="cx"> 
</span><del>-class CSEPhase : public Phase {
</del><ins>+// This file contains two CSE implementations: local (LCSE) and global (GCSE). LCSE typically runs
+// when we're in DFG mode, i.e. we want to compile quickly. LCSE contains a lot of optimizations
+// for compile time. GCSE, on the other hand, is fairly straight-forward. It will find more
+// optimization opportunities by virtue of being global.
+
+namespace {
+
+const bool verbose = false;
+
+class ClobberFilter {
</ins><span class="cx"> public:
</span><del>-    CSEPhase(Graph&amp; graph)
-        : Phase(graph, &quot;common subexpression elimination&quot;)
</del><ins>+    ClobberFilter(AbstractHeap heap)
+        : m_heap(heap)
</ins><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    bool run()
</del><ins>+    bool operator()(const ImpureMap::KeyValuePairType&amp; pair)
</ins><span class="cx">     {
</span><del>-        ASSERT(m_graph.m_fixpointState != BeforeFixpoint);
-        
-        m_changed = false;
-        
-        m_graph.clearReplacements();
-        
-        if (m_graph.m_form == SSA) {
-            Vector&lt;BasicBlock*&gt; depthFirst;
-            m_graph.getBlocksInDepthFirstOrder(depthFirst);
-            for (unsigned i = 0; i &lt; depthFirst.size(); ++i)
-                performBlockCSE(depthFirst[i]);
-        } else {
-            for (unsigned blockIndex = 0; blockIndex &lt; m_graph.numBlocks(); ++blockIndex)
-                performBlockCSE(m_graph.block(blockIndex));
-        }
-        
-        return m_changed;
</del><ins>+        return m_heap.overlaps(pair.key.heap());
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx"> private:
</span><del>-    
-    unsigned endIndexForPureCSE()
</del><ins>+    AbstractHeap m_heap;
+};
+
+inline void clobber(ImpureMap&amp; map, AbstractHeap heap)
+{
+    ClobberFilter filter(heap);
+    map.removeIf(filter);
+}
+
+class LCSEPhase : public Phase {
+public:
+    LCSEPhase(Graph&amp; graph)
+        : Phase(graph, &quot;local common subexpression elimination&quot;)
+        , m_smallBlock(graph)
+        , m_largeBlock(graph)
</ins><span class="cx">     {
</span><del>-        unsigned result = m_lastSeen[m_currentNode-&gt;op()];
-        if (result == UINT_MAX)
-            result = 0;
-        else
-            result++;
-        ASSERT(result &lt;= m_indexInBlock);
-        return result;
</del><span class="cx">     }
</span><del>-
-    Node* pureCSE(Node* node)
</del><ins>+    
+    bool run()
</ins><span class="cx">     {
</span><del>-        Edge child1 = node-&gt;child1().sanitized();
-        Edge child2 = node-&gt;child2().sanitized();
-        Edge child3 = node-&gt;child3().sanitized();
</del><ins>+        ASSERT(m_graph.m_fixpointState == FixpointNotConverged);
+        ASSERT(m_graph.m_form == ThreadedCPS || m_graph.m_form == LoadStore);
</ins><span class="cx">         
</span><del>-        for (unsigned i = endIndexForPureCSE(); i--;) {
-            Node* otherNode = m_currentBlock-&gt;at(i);
-            if (otherNode == child1 || otherNode == child2 || otherNode == child3)
-                break;
-
-            if (node-&gt;op() != otherNode-&gt;op())
</del><ins>+        bool changed = false;
+        
+        m_graph.clearReplacements();
+        
+        for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
+            BasicBlock* block = m_graph.block(blockIndex);
+            if (!block)
</ins><span class="cx">                 continue;
</span><span class="cx">             
</span><del>-            Edge otherChild = otherNode-&gt;child1().sanitized();
-            if (!otherChild)
-                return otherNode;
-            if (otherChild != child1)
-                continue;
-            
-            otherChild = otherNode-&gt;child2().sanitized();
-            if (!otherChild)
-                return otherNode;
-            if (otherChild != child2)
-                continue;
-            
-            otherChild = otherNode-&gt;child3().sanitized();
-            if (!otherChild)
-                return otherNode;
-            if (otherChild != child3)
-                continue;
-            
-            return otherNode;
</del><ins>+            if (block-&gt;size() &lt;= SmallMaps::capacity)
+                changed |= m_smallBlock.run(block);
+            else
+                changed |= m_largeBlock.run(block);
</ins><span class="cx">         }
</span><del>-        return 0;
</del><ins>+        
+        return changed;
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-    Node* constantCSE(Node* node)
-    {
-        for (unsigned i = endIndexForPureCSE(); i--;) {
-            Node* otherNode = m_currentBlock-&gt;at(i);
-            if (otherNode-&gt;op() != node-&gt;op())
-                continue;
-            
-            if (otherNode-&gt;constant() != node-&gt;constant())
-                continue;
-            
-            return otherNode;
</del><ins>+private:
+    class SmallMaps {
+    public:
+        static const unsigned capacity = 20;
+    
+        SmallMaps()
+            : m_pureLength(0)
+            , m_impureLength(0)
+        {
</ins><span class="cx">         }
</span><del>-        return 0;
-    }
</del><span class="cx">     
</span><del>-    Node* constantStoragePointerCSE(Node* node)
-    {
-        for (unsigned i = endIndexForPureCSE(); i--;) {
-            Node* otherNode = m_currentBlock-&gt;at(i);
-            if (otherNode-&gt;op() != ConstantStoragePointer)
-                continue;
-            
-            if (otherNode-&gt;storagePointer() != node-&gt;storagePointer())
-                continue;
-            
-            return otherNode;
</del><ins>+        void clear()
+        {
+            m_pureLength = 0;
+            m_impureLength = 0;
</ins><span class="cx">         }
</span><del>-        return 0;
-    }
</del><span class="cx">     
</span><del>-    Node* getCalleeLoadElimination()
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            switch (node-&gt;op()) {
-            case GetCallee:
-                return node;
-            default:
-                break;
</del><ins>+        void write(AbstractHeap heap)
+        {
+            for (unsigned i = 0; i &lt; m_impureLength; ++i) {
+                if (heap.overlaps(m_impureMap[i].key.heap()))
+                    m_impureMap[i--] = m_impureMap[--m_impureLength];
</ins><span class="cx">             }
</span><span class="cx">         }
</span><del>-        return 0;
-    }
</del><span class="cx">     
</span><del>-    Node* getArrayLengthElimination(Node* array)
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            switch (node-&gt;op()) {
-            case GetArrayLength:
-                if (node-&gt;child1() == array)
-                    return node;
-                break;
-                
-            case PutByValDirect:
-            case PutByVal:
-                if (!m_graph.byValIsPure(node))
-                    return 0;
-                if (node-&gt;arrayMode().mayStoreToHole())
-                    return 0;
-                break;
-                
-            default:
-                if (m_graph.clobbersWorld(node))
-                    return 0;
</del><ins>+        Node* addPure(PureValue value, Node* node)
+        {
+            for (unsigned i = m_pureLength; i--;) {
+                if (m_pureMap[i].key == value)
+                    return m_pureMap[i].value;
</ins><span class="cx">             }
</span><ins>+        
+            ASSERT(m_pureLength &lt; capacity);
+            m_pureMap[m_pureLength++] = WTF::KeyValuePair&lt;PureValue, Node*&gt;(value, node);
+            return nullptr;
</ins><span class="cx">         }
</span><del>-        return 0;
-    }
</del><span class="cx">     
</span><del>-    Node* globalVarLoadElimination(WriteBarrier&lt;Unknown&gt;* registerPointer)
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            switch (node-&gt;op()) {
-            case GetGlobalVar:
-                if (node-&gt;registerPointer() == registerPointer)
-                    return node;
-                break;
-            case PutGlobalVar:
-                if (node-&gt;registerPointer() == registerPointer)
-                    return node-&gt;child1().node();
-                break;
-            default:
-                break;
</del><ins>+        Node* addImpure(HeapLocation location, Node* node)
+        {
+            for (unsigned i = m_impureLength; i--;) {
+                if (m_impureMap[i].key == location)
+                    return m_impureMap[i].value;
</ins><span class="cx">             }
</span><del>-            if (m_graph.clobbersWorld(node))
-                break;
</del><ins>+        
+            ASSERT(m_impureLength &lt; capacity);
+            m_impureMap[m_impureLength++] = WTF::KeyValuePair&lt;HeapLocation, Node*&gt;(location, node);
+            return nullptr;
</ins><span class="cx">         }
</span><del>-        return 0;
-    }
</del><span class="cx">     
</span><del>-    Node* scopedVarLoadElimination(Node* registers, int varNumber)
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            switch (node-&gt;op()) {
-            case GetClosureVar: {
-                if (node-&gt;child1() == registers &amp;&amp; node-&gt;varNumber() == varNumber)
-                    return node;
-                break;
-            } 
-            case PutClosureVar: {
-                if (node-&gt;varNumber() != varNumber)
-                    break;
-                if (node-&gt;child2() == registers)
-                    return node-&gt;child3().node();
-                return 0;
-            }
-            case SetLocal: {
-                VariableAccessData* variableAccessData = node-&gt;variableAccessData();
-                if (variableAccessData-&gt;isCaptured()
-                    &amp;&amp; variableAccessData-&gt;local() == static_cast&lt;VirtualRegister&gt;(varNumber))
-                    return 0;
-                break;
-            }
-            default:
-                break;
-            }
-            if (m_graph.clobbersWorld(node))
-                break;
</del><ins>+    private:
+        WTF::KeyValuePair&lt;PureValue, Node*&gt; m_pureMap[capacity];
+        WTF::KeyValuePair&lt;HeapLocation, Node*&gt; m_impureMap[capacity];
+        unsigned m_pureLength;
+        unsigned m_impureLength;
+    };
+
+    class LargeMaps {
+    public:
+        LargeMaps()
+        {
</ins><span class="cx">         }
</span><del>-        return 0;
-    }
</del><span class="cx">     
</span><del>-    bool varInjectionWatchpointElimination()
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node-&gt;op() == VarInjectionWatchpoint)
-                return true;
-            if (m_graph.clobbersWorld(node))
-                break;
</del><ins>+        void clear()
+        {
+            m_pureMap.clear();
+            m_impureMap.clear();
</ins><span class="cx">         }
</span><del>-        return false;
-    }
</del><span class="cx">     
</span><del>-    Node* getByValLoadElimination(Node* child1, Node* child2, ArrayMode arrayMode)
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node == child1 || node == child2) 
-                break;
-
-            switch (node-&gt;op()) {
-            case GetByVal:
-                if (!m_graph.byValIsPure(node))
-                    return 0;
-                if (node-&gt;child1() == child1
-                    &amp;&amp; node-&gt;child2() == child2
-                    &amp;&amp; node-&gt;arrayMode().type() == arrayMode.type())
-                    return node;
-                break;
-                    
-            case PutByValDirect:
-            case PutByVal:
-            case PutByValAlias: {
-                if (!m_graph.byValIsPure(node))
-                    return 0;
-                // Typed arrays 
-                if (arrayMode.typedArrayType() != NotTypedArray)
-                    return 0;
-                if (m_graph.varArgChild(node, 0) == child1
-                    &amp;&amp; m_graph.varArgChild(node, 1) == child2
-                    &amp;&amp; node-&gt;arrayMode().type() == arrayMode.type())
-                    return m_graph.varArgChild(node, 2).node();
-                // We must assume that the PutByVal will clobber the location we're getting from.
-                // FIXME: We can do better; if we know that the PutByVal is accessing an array of a
-                // different type than the GetByVal, then we know that they won't clobber each other.
-                // ... except of course for typed arrays, where all typed arrays clobber all other
-                // typed arrays!  An Int32Array can alias a Float64Array for example, and so on.
-                return 0;
-            }
-            default:
-                if (m_graph.clobbersWorld(node))
-                    return 0;
-                break;
-            }
</del><ins>+        void write(AbstractHeap heap)
+        {
+            clobber(m_impureMap, heap);
</ins><span class="cx">         }
</span><del>-        return 0;
-    }
-
-    bool checkFunctionElimination(FrozenValue* function, Node* child1)
-    {
-        for (unsigned i = endIndexForPureCSE(); i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node == child1) 
-                break;
-
-            if (node-&gt;op() == CheckFunction &amp;&amp; node-&gt;child1() == child1 &amp;&amp; node-&gt;function() == function)
-                return true;
</del><ins>+    
+        Node* addPure(PureValue value, Node* node)
+        {
+            auto result = m_pureMap.add(value, node);
+            if (result.isNewEntry)
+                return nullptr;
+            return result.iterator-&gt;value;
</ins><span class="cx">         }
</span><del>-        return false;
-    }
</del><span class="cx">     
</span><del>-    bool checkExecutableElimination(ExecutableBase* executable, Node* child1)
-    {
-        for (unsigned i = endIndexForPureCSE(); i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node == child1)
-                break;
-
-            if (node-&gt;op() == CheckExecutable &amp;&amp; node-&gt;child1() == child1 &amp;&amp; node-&gt;executable() == executable)
-                return true;
</del><ins>+        Node* addImpure(HeapLocation location, Node* node)
+        {
+            auto result = m_impureMap.add(location, node);
+            if (result.isNewEntry)
+                return nullptr;
+            return result.iterator-&gt;value;
</ins><span class="cx">         }
</span><del>-        return false;
-    }
</del><span class="cx"> 
</span><del>-    bool checkStructureElimination(const StructureSet&amp; structureSet, Node* child1)
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node == child1) 
-                break;
</del><ins>+    private:
+        HashMap&lt;PureValue, Node*&gt; m_pureMap;
+        HashMap&lt;HeapLocation, Node*&gt; m_impureMap;
+    };
</ins><span class="cx"> 
</span><del>-            switch (node-&gt;op()) {
-            case CheckStructure:
-                if (node-&gt;child1() == child1
-                    &amp;&amp; structureSet.isSupersetOf(node-&gt;structureSet()))
-                    return true;
-                break;
-                
-            case PutStructure:
-                if (node-&gt;child1() == child1
-                    &amp;&amp; structureSet.contains(node-&gt;transition()-&gt;next))
-                    return true;
-                if (structureSet.contains(node-&gt;transition()-&gt;previous))
-                    return false;
-                break;
-                
-            case PutByOffset:
-                // Setting a property cannot change the structure.
-                break;
-                
-            case MultiPutByOffset:
-                if (node-&gt;multiPutByOffsetData().writesStructures())
-                    return false;
-                break;
-                
-            case PutByValDirect:
-            case PutByVal:
-            case PutByValAlias:
-                if (m_graph.byValIsPure(node)) {
-                    // If PutByVal speculates that it's accessing an array with an
-                    // integer index, then it's impossible for it to cause a structure
-                    // change.
-                    break;
-                }
-                return false;
-                
-            case Arrayify:
-            case ArrayifyToStructure:
-                // We could check if the arrayification could affect our structures.
-                // But that seems like it would take Effort.
-                return false;
-                
-            default:
-                if (m_graph.clobbersWorld(node))
-                    return false;
-                break;
-            }
</del><ins>+    template&lt;typename Maps&gt;
+    class BlockCSE {
+    public:
+        BlockCSE(Graph&amp; graph)
+            : m_graph(graph)
+        {
</ins><span class="cx">         }
</span><del>-        return false;
-    }
</del><span class="cx">     
</span><del>-    bool structureTransitionWatchpointElimination(Structure* structure, Node* child1)
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node == child1) 
-                break;
-
-            switch (node-&gt;op()) {
-            case CheckStructure:
-                if (node-&gt;child1() == child1
-                    &amp;&amp; node-&gt;structureSet().isSubsetOf(StructureSet(structure)))
-                    return true;
-                break;
-                
-            case PutStructure:
-                ASSERT(node-&gt;transition()-&gt;previous != structure);
-                break;
-                
-            case PutByOffset:
-                // Setting a property cannot change the structure.
-                break;
-                    
-            case MultiPutByOffset:
-                if (node-&gt;multiPutByOffsetData().writesStructures())
-                    return false;
-                break;
-                
-            case PutByValDirect:
-            case PutByVal:
-            case PutByValAlias:
-                if (m_graph.byValIsPure(node)) {
-                    // If PutByVal speculates that it's accessing an array with an
-                    // integer index, then it's impossible for it to cause a structure
-                    // change.
-                    break;
-                }
-                return false;
-                
-            case Arrayify:
-            case ArrayifyToStructure:
-                // We could check if the arrayification could affect our structures.
-                // But that seems like it would take Effort.
-                return false;
-                
-            default:
-                if (m_graph.clobbersWorld(node))
-                    return false;
-                break;
</del><ins>+        bool run(BasicBlock* block)
+        {
+            m_maps.clear();
+            m_changed = false;
+        
+            for (unsigned nodeIndex = 0; nodeIndex &lt; block-&gt;size(); ++nodeIndex) {
+                m_node = block-&gt;at(nodeIndex);
+                m_graph.performSubstitution(m_node);
+            
+                if (m_node-&gt;op() == Identity) {
+                    m_node-&gt;replaceWith(m_node-&gt;child1().node());
+                    m_changed = true;
+                } else
+                    clobberize(m_graph, m_node, *this);
</ins><span class="cx">             }
</span><ins>+        
+            return m_changed;
</ins><span class="cx">         }
</span><del>-        return false;
-    }
</del><span class="cx">     
</span><del>-    Node* getByOffsetLoadElimination(unsigned identifierNumber, Node* base)
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node == base)
-                break;
-
-            switch (node-&gt;op()) {
-            case GetByOffset:
-                if (node-&gt;child2() == base
-                    &amp;&amp; m_graph.m_storageAccessData[node-&gt;storageAccessDataIndex()].identifierNumber == identifierNumber)
-                    return node;
-                break;
-                
-            case MultiGetByOffset:
-                if (node-&gt;child1() == base
-                    &amp;&amp; node-&gt;multiGetByOffsetData().identifierNumber == identifierNumber)
-                    return node;
-                break;
-                
-            case PutByOffset:
-                if (m_graph.m_storageAccessData[node-&gt;storageAccessDataIndex()].identifierNumber == identifierNumber) {
-                    if (node-&gt;child2() == base) // Must be same property storage.
-                        return node-&gt;child3().node();
-                    return 0;
-                }
-                break;
-                
-            case MultiPutByOffset:
-                if (node-&gt;multiPutByOffsetData().identifierNumber == identifierNumber) {
-                    if (node-&gt;child1() == base)
-                        return node-&gt;child2().node();
-                    return 0;
-                }
-                break;
-                    
-            case PutByValDirect:
-            case PutByVal:
-            case PutByValAlias:
-                if (m_graph.byValIsPure(node)) {
-                    // If PutByVal speculates that it's accessing an array with an
-                    // integer index, then it's impossible for it to cause a structure
-                    // change.
-                    break;
-                }
-                return 0;
-                
-            default:
-                if (m_graph.clobbersWorld(node))
-                    return 0;
-                break;
-            }
</del><ins>+        void read(AbstractHeap) { }
+    
+        void write(AbstractHeap heap)
+        {
+            m_maps.write(heap);
</ins><span class="cx">         }
</span><del>-        return 0;
-    }
</del><span class="cx">     
</span><del>-    Node* getGetterSetterByOffsetLoadElimination(unsigned identifierNumber, Node* base)
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node == base)
-                break;
</del><ins>+        void def(PureValue value)
+        {
+            Node* match = m_maps.addPure(value, m_node);
+            if (!match)
+                return;
</ins><span class="cx"> 
</span><del>-            switch (node-&gt;op()) {
-            case GetGetterSetterByOffset:
-                if (node-&gt;child2() == base
-                    &amp;&amp; m_graph.m_storageAccessData[node-&gt;storageAccessDataIndex()].identifierNumber == identifierNumber)
-                    return node;
-                break;
-                    
-            case PutByValDirect:
-            case PutByVal:
-            case PutByValAlias:
-                if (m_graph.byValIsPure(node)) {
-                    // If PutByVal speculates that it's accessing an array with an
-                    // integer index, then it's impossible for it to cause a structure
-                    // change.
-                    break;
-                }
-                return 0;
-                
-            default:
-                if (m_graph.clobbersWorld(node))
-                    return 0;
-                break;
</del><ins>+            m_node-&gt;replaceWith(match);
+            m_changed = true;
+        }
+    
+        void def(HeapLocation location, Node* value)
+        {
+            Node* match = m_maps.addImpure(location, value);
+            if (!match)
+                return;
+        
+            if (m_node-&gt;op() == GetLocal) {
+                // For uncaptured locals, usually the CPS rethreading phase does this. But it's OK
+                // for us to mess with locals - regardless of their capturedness - so long as:
+                // 
+                // - We dethread the graph. Any changes we make may invalidate the assumptions of
+                //   our CPS form, particularly if this GetLocal is linked to the variablesAtTail.
+                //
+                // - We don't introduce a Phantom for the child of the GetLocal. This wouldn't be
+                //   totally wrong but it would pessimize the code. Just because there is a
+                //   GetLocal doesn't mean that the child was live. Simply rerouting the all uses
+                //   of this GetLocal will preserve the live-at-exit information just fine.
+                //
+                // We accomplish the latter by just clearing the child; then the Phantom that we
+                // introduce won't have children and so it will eventually just be deleted.
+            
+                m_node-&gt;child1() = Edge();
+                m_graph.dethread();
</ins><span class="cx">             }
</span><ins>+        
+            m_node-&gt;replaceWith(match);
+            m_changed = true;
</ins><span class="cx">         }
</span><del>-        return 0;
-    }
</del><span class="cx">     
</span><del>-    Node* getPropertyStorageLoadElimination(Node* child1)
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node == child1) 
-                break;
</del><ins>+    private:
+        Graph&amp; m_graph;
+        
+        bool m_changed;
+        Node* m_node;
+    
+        Maps m_maps;
+    };
</ins><span class="cx"> 
</span><del>-            switch (node-&gt;op()) {
-            case GetButterfly:
-                if (node-&gt;child1() == child1)
-                    return node;
-                break;
</del><ins>+    BlockCSE&lt;SmallMaps&gt; m_smallBlock;
+    BlockCSE&lt;LargeMaps&gt; m_largeBlock;
+};
</ins><span class="cx"> 
</span><del>-            case AllocatePropertyStorage:
-            case ReallocatePropertyStorage:
-                // If we can cheaply prove this is a change to our object's storage, we
-                // can optimize and use its result.
-                if (node-&gt;child1() == child1)
-                    return node;
-                // Otherwise, we currently can't prove that this doesn't change our object's
-                // storage, so we conservatively assume that it may change the storage
-                // pointer of any object, including ours.
-                return 0;
-                    
-            case PutByValDirect:
-            case PutByVal:
-            case PutByValAlias:
-                if (m_graph.byValIsPure(node)) {
-                    // If PutByVal speculates that it's accessing an array with an
-                    // integer index, then it's impossible for it to cause a structure
-                    // change.
-                    break;
-                }
-                return 0;
-                
-            case Arrayify:
-            case ArrayifyToStructure:
-                // We could check if the arrayification could affect our butterfly.
-                // But that seems like it would take Effort.
-                return 0;
-                
-            case MultiPutByOffset:
-                if (node-&gt;multiPutByOffsetData().reallocatesStorage())
-                    return 0;
-                break;
-                
-            default:
-                if (m_graph.clobbersWorld(node))
-                    return 0;
-                break;
-            }
-        }
-        return 0;
</del><ins>+class GCSEPhase : public Phase {
+public:
+    GCSEPhase(Graph&amp; graph)
+        : Phase(graph, &quot;global common subexpression elimination&quot;)
+    {
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-    bool checkArrayElimination(Node* child1, ArrayMode arrayMode)
</del><ins>+    bool run()
</ins><span class="cx">     {
</span><del>-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node == child1) 
-                break;
</del><ins>+        ASSERT(m_graph.m_fixpointState == FixpointNotConverged);
+        ASSERT(m_graph.m_form == SSA);
+        
+        m_graph.clearReplacements();
+        m_graph.initializeNodeOwners();
+        m_graph.m_dominators.computeIfNecessary(m_graph);
+        
+        Vector&lt;BasicBlock*&gt; preOrder;
+        m_graph.getBlocksInPreOrder(preOrder);
+        
+        m_impureDataMap.resize(m_graph.numBlocks());
+        
+        // First figure out what gets clobbered by blocks. Node that this uses the preOrder list
+        // for convenience only.
+        for (unsigned i = preOrder.size(); i--;) {
+            m_block = preOrder[i];
+            m_impureData = &amp;m_impureDataMap[m_block-&gt;index];
+            for (unsigned nodeIndex = m_block-&gt;size(); nodeIndex--;)
+                addWrites(m_graph, m_block-&gt;at(nodeIndex), m_impureData-&gt;writes);
+        }
+        
+        // Based on my experience doing this before, what follows might have to be made iterative.
+        // But it feels like, right now, this works as a single pass because we only turn a node
+        // into a value or heap location after we have performed substitutions on that node's
+        // children.
+        
+        for (unsigned i = 0; i &lt; preOrder.size(); ++i) {
+            m_block = preOrder[i];
+            m_impureData = &amp;m_impureDataMap[m_block-&gt;index];
+            m_writesSoFar.clear();
+            
+            if (verbose)
+                dataLog(&quot;Processing block &quot;, *m_block, &quot;:\n&quot;);
</ins><span class="cx"> 
</span><del>-            switch (node-&gt;op()) {
-            case CheckArray:
-                if (node-&gt;child1() == child1 &amp;&amp; node-&gt;arrayMode() == arrayMode)
-                    return true;
-                break;
</del><ins>+            for (unsigned nodeIndex = 0; nodeIndex &lt; m_block-&gt;size(); ++nodeIndex) {
+                m_node = m_block-&gt;at(nodeIndex);
+                if (verbose)
+                    dataLog(&quot;  Looking at node &quot;, m_node, &quot;:\n&quot;);
</ins><span class="cx">                 
</span><del>-            case Arrayify:
-            case ArrayifyToStructure:
-                // We could check if the arrayification could affect our array.
-                // But that seems like it would take Effort.
-                return false;
</del><ins>+                m_graph.performSubstitution(m_node);
</ins><span class="cx">                 
</span><del>-            default:
-                if (m_graph.clobbersWorld(node))
-                    return false;
-                break;
</del><ins>+                if (m_node-&gt;op() == Identity) {
+                    m_node-&gt;replaceWith(m_node-&gt;child1().node());
+                    m_changed = true;
+                } else
+                    clobberize(m_graph, m_node, *this);
</ins><span class="cx">             }
</span><ins>+            
+            m_impureData-&gt;didVisit = true;
</ins><span class="cx">         }
</span><del>-        return false;
</del><ins>+        
+        return m_changed;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    Node* getIndexedPropertyStorageLoadElimination(Node* child1, ArrayMode arrayMode)
-    {
-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node == child1) 
-                break;
-
-            switch (node-&gt;op()) {
-            case GetIndexedPropertyStorage: {
-                if (node-&gt;child1() == child1 &amp;&amp; node-&gt;arrayMode() == arrayMode)
-                    return node;
-                break;
-            }
-
-            default:
-                if (m_graph.clobbersWorld(node))
-                    return 0;
-                break;
-            }
-        }
-        return 0;
-    }
</del><ins>+    void read(AbstractHeap) { }
</ins><span class="cx">     
</span><del>-    Node* getInternalFieldLoadElimination(NodeType op, Node* child1)
</del><ins>+    void write(AbstractHeap heap)
</ins><span class="cx">     {
</span><del>-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node == child1) 
-                break;
-
-            if (node-&gt;op() == op &amp;&amp; node-&gt;child1() == child1)
-                return node;
-
-            if (m_graph.clobbersWorld(node))
-                return 0;
-        }
-        return 0;
</del><ins>+        clobber(m_impureData-&gt;availableAtTail, heap);
+        m_writesSoFar.add(heap);
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-    Node* getMyScopeLoadElimination()
</del><ins>+    void def(PureValue value)
</ins><span class="cx">     {
</span><del>-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            switch (node-&gt;op()) {
-            case CreateActivation:
-                // This may cause us to return a different scope.
-                return 0;
-            case GetMyScope:
-                return node;
-            default:
-                break;
-            }
</del><ins>+        auto result = m_pureValues.add(value, Vector&lt;Node*&gt;());
+        if (result.isNewEntry) {
+            result.iterator-&gt;value.append(m_node);
+            return;
</ins><span class="cx">         }
</span><del>-        return 0;
-    }
-    
-    Node* getLocalLoadElimination(VirtualRegister local, Node*&amp; relevantLocalOp, bool careAboutClobbering)
-    {
-        relevantLocalOp = 0;
</del><span class="cx">         
</span><del>-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            switch (node-&gt;op()) {
-            case GetLocal:
-                if (node-&gt;local() == local) {
-                    relevantLocalOp = node;
-                    return node;
-                }
-                break;
-                
-            case GetLocalUnlinked:
-                if (node-&gt;unlinkedLocal() == local) {
-                    relevantLocalOp = node;
-                    return node;
-                }
-                break;
-                
-            case SetLocal:
-                if (node-&gt;local() == local) {
-                    relevantLocalOp = node;
-                    return node-&gt;child1().node();
-                }
-                break;
-                
-            case GetClosureVar:
-            case PutClosureVar:
-                if (static_cast&lt;VirtualRegister&gt;(node-&gt;varNumber()) == local)
-                    return 0;
-                break;
-                
-            default:
-                if (careAboutClobbering &amp;&amp; m_graph.clobbersWorld(node))
-                    return 0;
-                break;
</del><ins>+        for (unsigned i = result.iterator-&gt;value.size(); i--;) {
+            Node* candidate = result.iterator-&gt;value[i];
+            if (m_graph.m_dominators.dominates(candidate-&gt;owner, m_block)) {
+                m_node-&gt;replaceWith(candidate);
+                m_changed = true;
+                return;
</ins><span class="cx">             }
</span><span class="cx">         }
</span><del>-        return 0;
</del><ins>+        
+        result.iterator-&gt;value.append(m_node);
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-    bool invalidationPointElimination()
</del><ins>+    Node* findReplacement(HeapLocation location)
</ins><span class="cx">     {
</span><del>-        for (unsigned i = m_indexInBlock; i--;) {
-            Node* node = m_currentBlock-&gt;at(i);
-            if (node-&gt;op() == InvalidationPoint)
-                return true;
-            if (writesOverlap(m_graph, node, Watchpoint_fire))
-                break;
</del><ins>+        Node* match = m_impureData-&gt;availableAtTail.get(location);
+        if (match) {
+            if (verbose)
+                dataLog(&quot;      Found local match: &quot;, match, &quot;\n&quot;);
+            return match;
</ins><span class="cx">         }
</span><del>-        return false;
-    }
-    
-    bool setReplacement(Node* replacement)
-    {
-        if (!replacement)
-            return false;
</del><span class="cx">         
</span><del>-        m_currentNode-&gt;convertToPhantom();
</del><ins>+        if (m_writesSoFar.overlaps(location.heap())) {
+            if (verbose)
+                dataLog(&quot;      Not looking globally because of local clobber: &quot;, m_writesSoFar, &quot;\n&quot;);
+            return nullptr;
+        }
</ins><span class="cx">         
</span><del>-        // At this point we will eliminate all references to this node.
-        m_currentNode-&gt;replacement = replacement;
</del><ins>+        // This perfoms a backward search to find some possible def() that matches our heap
+        // location. As soon as it finds a possible def(), it assumes that this one must be the
+        // match and completes the search by halting at this block. As soon as it finds a write()
+        // that overlaps the location's heap, it stops, assuming that there is no possible match.
</ins><span class="cx">         
</span><del>-        m_changed = true;
</del><ins>+        Vector&lt;BasicBlock*, 8&gt; worklist;
+        Vector&lt;BasicBlock*, 8&gt; seenList;
+        BitVector seen;
</ins><span class="cx">         
</span><del>-        return true;
-    }
-    
-    void eliminate()
-    {
-        ASSERT(m_currentNode-&gt;mustGenerate());
-        m_currentNode-&gt;convertToPhantom();
</del><ins>+        for (unsigned i = m_block-&gt;predecessors.size(); i--;) {
+            BasicBlock* predecessor = m_block-&gt;predecessors[i];
+            if (!seen.get(predecessor-&gt;index)) {
+                worklist.append(predecessor);
+                seen.set(predecessor-&gt;index);
+            }
+        }
</ins><span class="cx">         
</span><del>-        m_changed = true;
-    }
-    
-    void eliminate(Node* node, NodeType phantomType = Phantom)
-    {
-        if (!node)
-            return;
-        ASSERT(node-&gt;mustGenerate());
-        node-&gt;setOpAndDefaultFlags(phantomType);
-        
-        m_changed = true;
-    }
-    
-    void performNodeCSE(Node* node)
-    {
-        m_graph.performSubstitution(node);
-        
-        switch (node-&gt;op()) {
-        
-        case Identity:
-            setReplacement(node-&gt;child1().node());
-            break;
</del><ins>+        while (!worklist.isEmpty()) {
+            BasicBlock* block = worklist.takeLast();
+            seenList.append(block);
</ins><span class="cx">             
</span><del>-        // Handle the pure nodes. These nodes never have any side-effects.
-        case BitAnd:
-        case BitOr:
-        case BitXor:
-        case BitRShift:
-        case BitLShift:
-        case BitURShift:
-        case ArithAbs:
-        case ArithMin:
-        case ArithMax:
-        case ArithSqrt:
-        case ArithFRound:
-        case ArithSin:
-        case ArithCos:
-        case StringCharAt:
-        case StringCharCodeAt:
-        case IsUndefined:
-        case IsBoolean:
-        case IsNumber:
-        case IsString:
-        case IsObject:
-        case IsFunction:
-        case LogicalNot:
-        case SkipTopScope:
-        case SkipScope:
-        case GetClosureRegisters:
-        case GetScope:
-        case TypeOf:
-        case CompareEqConstant:
-        case ValueToInt32:
-        case MakeRope:
-        case DoubleRep:
-        case ValueRep:
-        case Int52Rep:
-            setReplacement(pureCSE(node));
-            break;
</del><ins>+            if (verbose)
+                dataLog(&quot;      Searching in block &quot;, *block, &quot;\n&quot;);
+            ImpureBlockData&amp; data = m_impureDataMap[block-&gt;index];
</ins><span class="cx">             
</span><del>-        case ArithAdd:
-        case ArithSub:
-        case ArithNegate:
-        case ArithMul:
-        case ArithDiv:
-        case ArithMod:
-        case UInt32ToNumber:
-        case DoubleAsInt32: {
-            Node* candidate = pureCSE(node);
-            if (!candidate)
-                break;
-            if (!subsumes(candidate-&gt;arithMode(), node-&gt;arithMode())) {
-                if (!subsumes(node-&gt;arithMode(), candidate-&gt;arithMode()))
-                    break;
-                candidate-&gt;setArithMode(node-&gt;arithMode());
</del><ins>+            if (m_graph.m_dominators.dominates(block, m_block) &amp;&amp; block != m_block) {
+                if (verbose)
+                    dataLog(&quot;        It strictly dominates.\n&quot;);
+                DFG_ASSERT(m_graph, m_node, data.didVisit);
+                DFG_ASSERT(m_graph, m_node, !match);
+                match = data.availableAtTail.get(location);
+                if (verbose)
+                    dataLog(&quot;        Availability: &quot;, match, &quot;\n&quot;);
+                if (match) {
+                    // Don't examine the predecessors of a match. At this point we just want to
+                    // establish that other blocks on the path from here to there don't clobber
+                    // the location we're interested in.
+                    continue;
+                }
</ins><span class="cx">             }
</span><del>-            setReplacement(candidate);
-            break;
-        }
</del><span class="cx">             
</span><del>-        case GetCallee:
-            setReplacement(getCalleeLoadElimination());
-            break;
-
-        case GetLocal: {
-            VariableAccessData* variableAccessData = node-&gt;variableAccessData();
-            if (!variableAccessData-&gt;isCaptured())
-                break;
-            Node* relevantLocalOp;
-            Node* possibleReplacement = getLocalLoadElimination(variableAccessData-&gt;local(), relevantLocalOp, variableAccessData-&gt;isCaptured());
-            if (!relevantLocalOp)
-                break;
-            if (relevantLocalOp-&gt;op() != GetLocalUnlinked
-                &amp;&amp; relevantLocalOp-&gt;variableAccessData() != variableAccessData)
-                break;
-            Node* phi = node-&gt;child1().node();
-            if (!setReplacement(possibleReplacement))
-                break;
-            
-            m_graph.dethread();
-            
-            // If we replace a GetLocal with a GetLocalUnlinked, then turn the GetLocalUnlinked
-            // into a GetLocal.
-            if (relevantLocalOp-&gt;op() == GetLocalUnlinked)
-                relevantLocalOp-&gt;convertToGetLocal(variableAccessData, phi);
-
-            m_changed = true;
-            break;
-        }
-            
-        case GetLocalUnlinked: {
-            Node* relevantLocalOpIgnored;
-            setReplacement(getLocalLoadElimination(node-&gt;unlinkedLocal(), relevantLocalOpIgnored, true));
-            break;
-        }
-            
-        case JSConstant:
-        case DoubleConstant:
-        case Int52Constant:
-            // This is strange, but necessary. Some phases will convert nodes to constants,
-            // which may result in duplicated constants. We use CSE to clean this up.
-            setReplacement(constantCSE(node));
-            break;
-            
-        case ConstantStoragePointer:
-            setReplacement(constantStoragePointerCSE(node));
-            break;
-            
-        case GetArrayLength:
-            setReplacement(getArrayLengthElimination(node-&gt;child1().node()));
-            break;
-
-        case GetMyScope:
-            setReplacement(getMyScopeLoadElimination());
-            break;
-            
-        // Handle nodes that are conditionally pure: these are pure, and can
-        // be CSE'd, so long as the prediction is the one we want.
-        case CompareLess:
-        case CompareLessEq:
-        case CompareGreater:
-        case CompareGreaterEq:
-        case CompareEq: {
-            if (m_graph.isPredictedNumerical(node)) {
-                Node* replacement = pureCSE(node);
-                if (replacement &amp;&amp; m_graph.isPredictedNumerical(replacement))
-                    setReplacement(replacement);
</del><ins>+            if (verbose)
+                dataLog(&quot;        Dealing with write set &quot;, data.writes, &quot;\n&quot;);
+            if (data.writes.overlaps(location.heap())) {
+                if (verbose)
+                    dataLog(&quot;        Clobbered.\n&quot;);
+                return nullptr;
</ins><span class="cx">             }
</span><del>-            break;
-        }
</del><span class="cx">             
</span><del>-        // Finally handle heap accesses. These are not quite pure, but we can still
-        // optimize them provided that some subtle conditions are met.
-        case GetGlobalVar:
-            setReplacement(globalVarLoadElimination(node-&gt;registerPointer()));
-            break;
-
-        case GetClosureVar: {
-            setReplacement(scopedVarLoadElimination(node-&gt;child1().node(), node-&gt;varNumber()));
-            break;
-        }
-
-        case VarInjectionWatchpoint:
-            if (varInjectionWatchpointElimination())
-                eliminate();
-            break;
-            
-        case GetByVal:
-            if (m_graph.byValIsPure(node))
-                setReplacement(getByValLoadElimination(node-&gt;child1().node(), node-&gt;child2().node(), node-&gt;arrayMode()));
-            break;
-                
-        case PutByValDirect:
-        case PutByVal: {
-            Edge child1 = m_graph.varArgChild(node, 0);
-            Edge child2 = m_graph.varArgChild(node, 1);
-            if (node-&gt;arrayMode().canCSEStorage()) {
-                Node* replacement = getByValLoadElimination(child1.node(), child2.node(), node-&gt;arrayMode());
-                if (!replacement)
-                    break;
-                node-&gt;setOp(PutByValAlias);
</del><ins>+            for (unsigned i = block-&gt;predecessors.size(); i--;) {
+                BasicBlock* predecessor = block-&gt;predecessors[i];
+                if (!seen.get(predecessor-&gt;index)) {
+                    worklist.append(predecessor);
+                    seen.set(predecessor-&gt;index);
+                }
</ins><span class="cx">             }
</span><del>-            break;
</del><span class="cx">         }
</span><del>-            
-        case CheckStructure:
-            if (checkStructureElimination(node-&gt;structureSet(), node-&gt;child1().node()))
-                eliminate();
-            break;
-            
-        case CheckFunction:
-            if (checkFunctionElimination(node-&gt;function(), node-&gt;child1().node()))
-                eliminate();
-            break;
-                
-        case CheckExecutable:
-            if (checkExecutableElimination(node-&gt;executable(), node-&gt;child1().node()))
-                eliminate();
-            break;
-                
-        case CheckArray:
-            if (checkArrayElimination(node-&gt;child1().node(), node-&gt;arrayMode()))
-                eliminate();
-            break;
-            
-        case GetIndexedPropertyStorage: {
-            setReplacement(getIndexedPropertyStorageLoadElimination(node-&gt;child1().node(), node-&gt;arrayMode()));
-            break;
-        }
-            
-        case GetTypedArrayByteOffset:
-        case GetGetter:
-        case GetSetter: {
-            setReplacement(getInternalFieldLoadElimination(node-&gt;op(), node-&gt;child1().node()));
-            break;
-        }
</del><span class="cx"> 
</span><del>-        case GetButterfly:
-            setReplacement(getPropertyStorageLoadElimination(node-&gt;child1().node()));
-            break;
-
-        case GetByOffset:
-            setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node-&gt;storageAccessDataIndex()].identifierNumber, node-&gt;child2().node()));
-            break;
-            
-        case GetGetterSetterByOffset:
-            setReplacement(getGetterSetterByOffsetLoadElimination(m_graph.m_storageAccessData[node-&gt;storageAccessDataIndex()].identifierNumber, node-&gt;child2().node()));
-            break;
-            
-        case MultiGetByOffset:
-            setReplacement(getByOffsetLoadElimination(node-&gt;multiGetByOffsetData().identifierNumber, node-&gt;child1().node()));
-            break;
-            
-        case InvalidationPoint:
-            if (invalidationPointElimination())
-                eliminate();
-            break;
-            
-        default:
-            // do nothing.
-            break;
-        }
</del><ins>+        // Cache the results for next time. We cache them both for this block and for all of our
+        // predecessors, since even though we've already visited our predecessors, our predecessors
+        // probably have successors other than us.
+        for (BasicBlock* block : seenList)
+            m_impureDataMap[block-&gt;index].availableAtTail.add(location, match);
+        m_impureData-&gt;availableAtTail.add(location, match);
</ins><span class="cx">         
</span><del>-        m_lastSeen[node-&gt;op()] = m_indexInBlock;
</del><ins>+        return match;
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-    void performBlockCSE(BasicBlock* block)
</del><ins>+    void def(HeapLocation location, Node* value)
</ins><span class="cx">     {
</span><del>-        if (!block)
-            return;
-        if (!block-&gt;isReachable)
-            return;
</del><ins>+        if (verbose)
+            dataLog(&quot;    Got heap location def: &quot;, location, &quot; -&gt; &quot;, value, &quot;\n&quot;);
</ins><span class="cx">         
</span><del>-        m_currentBlock = block;
-        for (unsigned i = 0; i &lt; LastNodeType; ++i)
-            m_lastSeen[i] = UINT_MAX;
</del><ins>+        Node* match = findReplacement(location);
</ins><span class="cx">         
</span><del>-        for (m_indexInBlock = 0; m_indexInBlock &lt; block-&gt;size(); ++m_indexInBlock) {
-            m_currentNode = block-&gt;at(m_indexInBlock);
-            performNodeCSE(m_currentNode);
</del><ins>+        if (verbose)
+            dataLog(&quot;      Got match: &quot;, match, &quot;\n&quot;);
+        
+        if (!match) {
+            auto result = m_impureData-&gt;availableAtTail.add(location, value);
+            ASSERT_UNUSED(result, result.isNewEntry);
+            return;
</ins><span class="cx">         }
</span><ins>+        
+        m_node-&gt;replaceWith(match);
+        m_changed = true;
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-    BasicBlock* m_currentBlock;
-    Node* m_currentNode;
-    unsigned m_indexInBlock;
-    std::array&lt;unsigned, LastNodeType&gt; m_lastSeen;
-    bool m_changed; // Only tracks changes that have a substantive effect on other optimizations.
</del><ins>+    struct ImpureBlockData {
+        ImpureBlockData()
+            : didVisit(false)
+        {
+        }
+        
+        ClobberSet writes;
+        ImpureMap availableAtTail;
+        bool didVisit;
+    };
+    
+    PureMultiMap m_pureValues;
+    Vector&lt;ImpureBlockData&gt; m_impureDataMap;
+    
+    BasicBlock* m_block;
+    Node* m_node;
+    ImpureBlockData* m_impureData;
+    ClobberSet m_writesSoFar;
+    
+    bool m_changed;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><del>-bool performCSE(Graph&amp; graph)
</del><ins>+} // anonymous namespace
+
+bool performLCSE(Graph&amp; graph)
</ins><span class="cx"> {
</span><del>-    SamplingRegion samplingRegion(&quot;DFG CSE Phase&quot;);
-    return runPhase&lt;CSEPhase&gt;(graph);
</del><ins>+    SamplingRegion samplingRegion(&quot;DFG LCSE Phase&quot;);
+    return runPhase&lt;LCSEPhase&gt;(graph);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool performGCSE(Graph&amp; graph)
+{
+    SamplingRegion samplingRegion(&quot;DFG GCSE Phase&quot;);
+    return runPhase&lt;GCSEPhase&gt;(graph);
+}
+
</ins><span class="cx"> } } // namespace JSC::DFG
</span><span class="cx"> 
</span><span class="cx"> #endif // ENABLE(DFG_JIT)
</span><del>-
-
</del></span></pre></div>
<a id="branchesftloptSourceWTFChangeLog"></a>
<div class="modfile"><h4>Modified: branches/ftlopt/Source/WTF/ChangeLog (171050 => 171051)</h4>
<pre class="diff"><span>
<span class="info">--- branches/ftlopt/Source/WTF/ChangeLog        2014-07-13 17:09:13 UTC (rev 171050)
+++ branches/ftlopt/Source/WTF/ChangeLog        2014-07-13 17:18:00 UTC (rev 171051)
</span><span class="lines">@@ -1,3 +1,23 @@
</span><ins>+2014-07-13  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        Merge trunk r171049.
+
+    2014-07-13  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+    
+            HashMap should have removeIf()
+            https://bugs.webkit.org/show_bug.cgi?id=134870
+    
+            Reviewed by Sam Weinig.
+            
+            Expose a new HashMap method, called removeIf(), which allows you to do an efficient
+            pass over the map and remove a bunch of things at once. This is used by DFG GCSE as
+            part of https://bugs.webkit.org/show_bug.cgi?id=134677.
+    
+            * wtf/HashMap.h:
+            (WTF::X&gt;::removeIf):
+            * wtf/HashTable.h:
+            (WTF::KeyTraits&gt;::removeIf):
+    
</ins><span class="cx"> 2014-06-01  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [ftlopt] AI should be able track structure sets larger than 1
</span></span></pre></div>
<a id="branchesftloptSourceWTFwtfHashMaph"></a>
<div class="modfile"><h4>Modified: branches/ftlopt/Source/WTF/wtf/HashMap.h (171050 => 171051)</h4>
<pre class="diff"><span>
<span class="info">--- branches/ftlopt/Source/WTF/wtf/HashMap.h        2014-07-13 17:09:13 UTC (rev 171050)
+++ branches/ftlopt/Source/WTF/wtf/HashMap.h        2014-07-13 17:18:00 UTC (rev 171051)
</span><span class="lines">@@ -120,6 +120,8 @@
</span><span class="cx"> 
</span><span class="cx">     bool remove(const KeyType&amp;);
</span><span class="cx">     bool remove(iterator);
</span><ins>+    template&lt;typename Functor&gt;
+    void removeIf(const Functor&amp; functor);
</ins><span class="cx">     void clear();
</span><span class="cx"> 
</span><span class="cx">     MappedType take(const KeyType&amp;); // efficient combination of get with remove
</span><span class="lines">@@ -354,6 +356,13 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> template&lt;typename T, typename U, typename V, typename W, typename X&gt;
</span><ins>+template&lt;typename Functor&gt;
+inline void HashMap&lt;T, U, V, W, X&gt;::removeIf(const Functor&amp; functor)
+{
+    m_impl.removeIf(functor);
+}
+
+template&lt;typename T, typename U, typename V, typename W, typename X&gt;
</ins><span class="cx"> inline bool HashMap&lt;T, U, V, W, X&gt;::remove(const KeyType&amp; key)
</span><span class="cx"> {
</span><span class="cx">     return remove(find(key));
</span></span></pre></div>
<a id="branchesftloptSourceWTFwtfHashTableh"></a>
<div class="modfile"><h4>Modified: branches/ftlopt/Source/WTF/wtf/HashTable.h (171050 => 171051)</h4>
<pre class="diff"><span>
<span class="info">--- branches/ftlopt/Source/WTF/wtf/HashTable.h        2014-07-13 17:09:13 UTC (rev 171050)
+++ branches/ftlopt/Source/WTF/wtf/HashTable.h        2014-07-13 17:18:00 UTC (rev 171051)
</span><span class="lines">@@ -393,6 +393,8 @@
</span><span class="cx">         void remove(iterator);
</span><span class="cx">         void removeWithoutEntryConsistencyCheck(iterator);
</span><span class="cx">         void removeWithoutEntryConsistencyCheck(const_iterator);
</span><ins>+        template&lt;typename Functor&gt;
+        void removeIf(const Functor&amp;);
</ins><span class="cx">         void clear();
</span><span class="cx"> 
</span><span class="cx">         static bool isEmptyBucket(const ValueType&amp; value) { return isHashTraitsEmptyValue&lt;KeyTraits&gt;(Extractor::extract(value)); }
</span><span class="lines">@@ -1031,6 +1033,28 @@
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     template&lt;typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits&gt;
</span><ins>+    template&lt;typename Functor&gt;
+    inline void HashTable&lt;Key, Value, Extractor, HashFunctions, Traits, KeyTraits&gt;::removeIf(const Functor&amp; functor)
+    {
+        for (unsigned i = m_tableSize; i--;) {
+            if (isEmptyOrDeletedBucket(m_table[i]))
+                continue;
+            
+            if (!functor(m_table[i]))
+                continue;
+            
+            deleteBucket(m_table[i]);
+            ++m_deletedCount;
+            --m_keyCount;
+        }
+        
+        if (shouldShrink())
+            shrink();
+        
+        internalCheckTableConsistency();
+    }
+
+    template&lt;typename Key, typename Value, typename Extractor, typename HashFunctions, typename Traits, typename KeyTraits&gt;
</ins><span class="cx">     auto HashTable&lt;Key, Value, Extractor, HashFunctions, Traits, KeyTraits&gt;::allocateTable(int size) -&gt; ValueType*
</span><span class="cx">     {
</span><span class="cx">         // would use a template member function with explicit specializations here, but
</span></span></pre>
</div>
</div>

</body>
</html>