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

<h3>Log Message</h3>
<pre>Unreviewed, revert unintended change in <a href="http://trac.webkit.org/projects/webkit/changeset/171051">r171051</a>.

* dfg/DFGCSEPhase.cpp:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#branchesftloptSourceJavaScriptCoreChangeLog">branches/ftlopt/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#branchesftloptSourceJavaScriptCoredfgDFGCSEPhasecpp">branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="branchesftloptSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: branches/ftlopt/Source/JavaScriptCore/ChangeLog (171051 => 171052)</h4>
<pre class="diff"><span>
<span class="info">--- branches/ftlopt/Source/JavaScriptCore/ChangeLog        2014-07-13 17:18:00 UTC (rev 171051)
+++ branches/ftlopt/Source/JavaScriptCore/ChangeLog        2014-07-13 17:26:35 UTC (rev 171052)
</span><span class="lines">@@ -1,3 +1,9 @@
</span><ins>+2014-07-13  Filip Pizlo  &lt;fpizlo@apple.com&gt;
+
+        Unreviewed, revert unintended change in r171051.
+
+        * dfg/DFGCSEPhase.cpp:
+
</ins><span class="cx"> 2014-07-08  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [ftlopt] Move Flush(SetLocal) store elimination to StrengthReductionPhase
</span></span></pre></div>
<a id="branchesftloptSourceJavaScriptCoredfgDFGCSEPhasecpp"></a>
<div class="modfile"><h4>Modified: branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp (171051 => 171052)</h4>
<pre class="diff"><span>
<span class="info">--- branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp        2014-07-13 17:18:00 UTC (rev 171051)
+++ branches/ftlopt/Source/JavaScriptCore/dfg/DFGCSEPhase.cpp        2014-07-13 17:26:35 UTC (rev 171052)
</span><span class="lines">@@ -29,7 +29,6 @@
</span><span class="cx"> #if ENABLE(DFG_JIT)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;DFGAbstractHeap.h&quot;
</span><del>-#include &quot;DFGClobberSet.h&quot;
</del><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">@@ -40,479 +39,984 @@
</span><span class="cx"> 
</span><span class="cx"> namespace JSC { namespace DFG {
</span><span class="cx"> 
</span><del>-// 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 {
</del><ins>+class CSEPhase : public Phase {
</ins><span class="cx"> public:
</span><del>-    ClobberFilter(AbstractHeap heap)
-        : m_heap(heap)
</del><ins>+    CSEPhase(Graph&amp; graph)
+        : Phase(graph, &quot;common subexpression elimination&quot;)
</ins><span class="cx">     {
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    bool operator()(const ImpureMap::KeyValuePairType&amp; pair)
</del><ins>+    bool run()
</ins><span class="cx">     {
</span><del>-        return m_heap.overlaps(pair.key.heap());
</del><ins>+        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;
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx"> private:
</span><del>-    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)
</del><ins>+    
+    unsigned endIndexForPureCSE()
</ins><span class="cx">     {
</span><ins>+        unsigned result = m_lastSeen[m_currentNode-&gt;op()];
+        if (result == UINT_MAX)
+            result = 0;
+        else
+            result++;
+        ASSERT(result &lt;= m_indexInBlock);
+        return result;
</ins><span class="cx">     }
</span><del>-    
-    bool run()
</del><ins>+
+    Node* pureCSE(Node* node)
</ins><span class="cx">     {
</span><del>-        ASSERT(m_graph.m_fixpointState == FixpointNotConverged);
-        ASSERT(m_graph.m_form == ThreadedCPS || m_graph.m_form == LoadStore);
</del><ins>+        Edge child1 = node-&gt;child1().sanitized();
+        Edge child2 = node-&gt;child2().sanitized();
+        Edge child3 = node-&gt;child3().sanitized();
</ins><span class="cx">         
</span><del>-        bool changed = false;
-        
-        m_graph.clearReplacements();
-        
-        for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
-            BasicBlock* block = m_graph.block(blockIndex);
-            if (!block)
</del><ins>+        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())
</ins><span class="cx">                 continue;
</span><span class="cx">             
</span><del>-            if (block-&gt;size() &lt;= SmallMaps::capacity)
-                changed |= m_smallBlock.run(block);
-            else
-                changed |= m_largeBlock.run(block);
</del><ins>+            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;
</ins><span class="cx">         }
</span><del>-        
-        return changed;
</del><ins>+        return 0;
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-private:
-    class SmallMaps {
-    public:
-        static const unsigned capacity = 20;
-    
-        SmallMaps()
-            : m_pureLength(0)
-            , m_impureLength(0)
-        {
</del><ins>+    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;
</ins><span class="cx">         }
</span><ins>+        return 0;
+    }
</ins><span class="cx">     
</span><del>-        void clear()
-        {
-            m_pureLength = 0;
-            m_impureLength = 0;
</del><ins>+    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;
</ins><span class="cx">         }
</span><ins>+        return 0;
+    }
</ins><span class="cx">     
</span><del>-        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];
</del><ins>+    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;
</ins><span class="cx">             }
</span><span class="cx">         }
</span><ins>+        return 0;
+    }
</ins><span class="cx">     
</span><del>-        Node* addPure(PureValue value, Node* node)
-        {
-            for (unsigned i = m_pureLength; i--;) {
-                if (m_pureMap[i].key == value)
-                    return m_pureMap[i].value;
</del><ins>+    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;
</ins><span class="cx">             }
</span><del>-        
-            ASSERT(m_pureLength &lt; capacity);
-            m_pureMap[m_pureLength++] = WTF::KeyValuePair&lt;PureValue, Node*&gt;(value, node);
-            return nullptr;
</del><span class="cx">         }
</span><ins>+        return 0;
+    }
</ins><span class="cx">     
</span><del>-        Node* addImpure(HeapLocation location, Node* node)
-        {
-            for (unsigned i = m_impureLength; i--;) {
-                if (m_impureMap[i].key == location)
-                    return m_impureMap[i].value;
</del><ins>+    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;
</ins><span class="cx">             }
</span><del>-        
-            ASSERT(m_impureLength &lt; capacity);
-            m_impureMap[m_impureLength++] = WTF::KeyValuePair&lt;HeapLocation, Node*&gt;(location, node);
-            return nullptr;
</del><ins>+            if (m_graph.clobbersWorld(node))
+                break;
</ins><span class="cx">         }
</span><ins>+        return 0;
+    }
</ins><span class="cx">     
</span><del>-    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()
-        {
</del><ins>+    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;
</ins><span class="cx">         }
</span><ins>+        return 0;
+    }
</ins><span class="cx">     
</span><del>-        void clear()
-        {
-            m_pureMap.clear();
-            m_impureMap.clear();
</del><ins>+    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;
</ins><span class="cx">         }
</span><ins>+        return false;
+    }
</ins><span class="cx">     
</span><del>-        void write(AbstractHeap heap)
-        {
-            clobber(m_impureMap, heap);
</del><ins>+    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;
+            }
</ins><span class="cx">         }
</span><del>-    
-        Node* addPure(PureValue value, Node* node)
-        {
-            auto result = m_pureMap.add(value, node);
-            if (result.isNewEntry)
-                return nullptr;
-            return result.iterator-&gt;value;
</del><ins>+        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;
</ins><span class="cx">         }
</span><ins>+        return false;
+    }
</ins><span class="cx">     
</span><del>-        Node* addImpure(HeapLocation location, Node* node)
-        {
-            auto result = m_impureMap.add(location, node);
-            if (result.isNewEntry)
-                return nullptr;
-            return result.iterator-&gt;value;
</del><ins>+    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;
</ins><span class="cx">         }
</span><ins>+        return false;
+    }
</ins><span class="cx"> 
</span><del>-    private:
-        HashMap&lt;PureValue, Node*&gt; m_pureMap;
-        HashMap&lt;HeapLocation, Node*&gt; m_impureMap;
-    };
</del><ins>+    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;
</ins><span class="cx"> 
</span><del>-    template&lt;typename Maps&gt;
-    class BlockCSE {
-    public:
-        BlockCSE(Graph&amp; graph)
-            : m_graph(graph)
-        {
</del><ins>+            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;
+            }
</ins><span class="cx">         }
</span><ins>+        return false;
+    }
</ins><span class="cx">     
</span><del>-        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);
</del><ins>+    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;
</ins><span class="cx">             }
</span><del>-        
-            return m_changed;
</del><span class="cx">         }
</span><ins>+        return false;
+    }
</ins><span class="cx">     
</span><del>-        void read(AbstractHeap) { }
-    
-        void write(AbstractHeap heap)
-        {
-            m_maps.write(heap);
</del><ins>+    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;
+            }
</ins><span class="cx">         }
</span><ins>+        return 0;
+    }
</ins><span class="cx">     
</span><del>-        void def(PureValue value)
-        {
-            Node* match = m_maps.addPure(value, m_node);
-            if (!match)
-                return;
</del><ins>+    Node* getGetterSetterByOffsetLoadElimination(unsigned identifierNumber, Node* base)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock-&gt;at(i);
+            if (node == base)
+                break;
</ins><span class="cx"> 
</span><del>-            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();
</del><ins>+            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;
</ins><span class="cx">             }
</span><del>-        
-            m_node-&gt;replaceWith(match);
-            m_changed = true;
</del><span class="cx">         }
</span><ins>+        return 0;
+    }
</ins><span class="cx">     
</span><del>-    private:
-        Graph&amp; m_graph;
-        
-        bool m_changed;
-        Node* m_node;
-    
-        Maps m_maps;
-    };
</del><ins>+    Node* getPropertyStorageLoadElimination(Node* child1)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock-&gt;at(i);
+            if (node == child1) 
+                break;
</ins><span class="cx"> 
</span><del>-    BlockCSE&lt;SmallMaps&gt; m_smallBlock;
-    BlockCSE&lt;LargeMaps&gt; m_largeBlock;
-};
</del><ins>+            switch (node-&gt;op()) {
+            case GetButterfly:
+                if (node-&gt;child1() == child1)
+                    return node;
+                break;
</ins><span class="cx"> 
</span><del>-class GCSEPhase : public Phase {
-public:
-    GCSEPhase(Graph&amp; graph)
-        : Phase(graph, &quot;global common subexpression elimination&quot;)
-    {
</del><ins>+            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;
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-    bool run()
</del><ins>+    bool checkArrayElimination(Node* child1, ArrayMode arrayMode)
</ins><span class="cx">     {
</span><del>-        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;);
</del><ins>+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock-&gt;at(i);
+            if (node == child1) 
+                break;
</ins><span class="cx"> 
</span><del>-            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;);
</del><ins>+            switch (node-&gt;op()) {
+            case CheckArray:
+                if (node-&gt;child1() == child1 &amp;&amp; node-&gt;arrayMode() == arrayMode)
+                    return true;
+                break;
</ins><span class="cx">                 
</span><del>-                m_graph.performSubstitution(m_node);
</del><ins>+            case Arrayify:
+            case ArrayifyToStructure:
+                // We could check if the arrayification could affect our array.
+                // But that seems like it would take Effort.
+                return false;
</ins><span class="cx">                 
</span><del>-                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);
</del><ins>+            default:
+                if (m_graph.clobbersWorld(node))
+                    return false;
+                break;
</ins><span class="cx">             }
</span><del>-            
-            m_impureData-&gt;didVisit = true;
</del><span class="cx">         }
</span><del>-        
-        return m_changed;
</del><ins>+        return false;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void read(AbstractHeap) { }
</del><ins>+    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;
+    }
</ins><span class="cx">     
</span><del>-    void write(AbstractHeap heap)
</del><ins>+    Node* getInternalFieldLoadElimination(NodeType op, Node* child1)
</ins><span class="cx">     {
</span><del>-        clobber(m_impureData-&gt;availableAtTail, heap);
-        m_writesSoFar.add(heap);
</del><ins>+        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;
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-    void def(PureValue value)
</del><ins>+    Node* getMyScopeLoadElimination()
</ins><span class="cx">     {
</span><del>-        auto result = m_pureValues.add(value, Vector&lt;Node*&gt;());
-        if (result.isNewEntry) {
-            result.iterator-&gt;value.append(m_node);
-            return;
</del><ins>+        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;
+            }
</ins><span class="cx">         }
</span><ins>+        return 0;
+    }
+    
+    Node* getLocalLoadElimination(VirtualRegister local, Node*&amp; relevantLocalOp, bool careAboutClobbering)
+    {
+        relevantLocalOp = 0;
</ins><span class="cx">         
</span><del>-        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;
</del><ins>+        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;
</ins><span class="cx">             }
</span><span class="cx">         }
</span><del>-        
-        result.iterator-&gt;value.append(m_node);
</del><ins>+        return 0;
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-    Node* findReplacement(HeapLocation location)
</del><ins>+    bool invalidationPointElimination()
</ins><span class="cx">     {
</span><del>-        Node* match = m_impureData-&gt;availableAtTail.get(location);
-        if (match) {
-            if (verbose)
-                dataLog(&quot;      Found local match: &quot;, match, &quot;\n&quot;);
-            return match;
</del><ins>+        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;
</ins><span class="cx">         }
</span><ins>+        return false;
+    }
+    
+    bool setReplacement(Node* replacement)
+    {
+        if (!replacement)
+            return false;
</ins><span class="cx">         
</span><del>-        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;
-        }
</del><ins>+        m_currentNode-&gt;convertToPhantom();
</ins><span class="cx">         
</span><del>-        // 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.
</del><ins>+        // At this point we will eliminate all references to this node.
+        m_currentNode-&gt;replacement = replacement;
</ins><span class="cx">         
</span><del>-        Vector&lt;BasicBlock*, 8&gt; worklist;
-        Vector&lt;BasicBlock*, 8&gt; seenList;
-        BitVector seen;
</del><ins>+        m_changed = true;
</ins><span class="cx">         
</span><del>-        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);
-            }
-        }
</del><ins>+        return true;
+    }
+    
+    void eliminate()
+    {
+        ASSERT(m_currentNode-&gt;mustGenerate());
+        m_currentNode-&gt;convertToPhantom();
</ins><span class="cx">         
</span><del>-        while (!worklist.isEmpty()) {
-            BasicBlock* block = worklist.takeLast();
-            seenList.append(block);
</del><ins>+        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;
</ins><span class="cx">             
</span><del>-            if (verbose)
-                dataLog(&quot;      Searching in block &quot;, *block, &quot;\n&quot;);
-            ImpureBlockData&amp; data = m_impureDataMap[block-&gt;index];
</del><ins>+        // 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;
</ins><span class="cx">             
</span><del>-            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;
-                }
</del><ins>+        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());
</ins><span class="cx">             }
</span><ins>+            setReplacement(candidate);
+            break;
+        }
</ins><span class="cx">             
</span><del>-            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;
</del><ins>+        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);
</ins><span class="cx">             }
</span><ins>+            break;
+        }
</ins><span class="cx">             
</span><del>-            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);
-                }
</del><ins>+        // 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);
</ins><span class="cx">             }
</span><ins>+            break;
</ins><span class="cx">         }
</span><ins>+            
+        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;
+        }
</ins><span class="cx"> 
</span><del>-        // 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);
</del><ins>+        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;
+        }
</ins><span class="cx">         
</span><del>-        return match;
</del><ins>+        m_lastSeen[node-&gt;op()] = m_indexInBlock;
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-    void def(HeapLocation location, Node* value)
</del><ins>+    void performBlockCSE(BasicBlock* block)
</ins><span class="cx">     {
</span><del>-        if (verbose)
-            dataLog(&quot;    Got heap location def: &quot;, location, &quot; -&gt; &quot;, value, &quot;\n&quot;);
</del><ins>+        if (!block)
+            return;
+        if (!block-&gt;isReachable)
+            return;
</ins><span class="cx">         
</span><del>-        Node* match = findReplacement(location);
</del><ins>+        m_currentBlock = block;
+        for (unsigned i = 0; i &lt; LastNodeType; ++i)
+            m_lastSeen[i] = UINT_MAX;
</ins><span class="cx">         
</span><del>-        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;
</del><ins>+        for (m_indexInBlock = 0; m_indexInBlock &lt; block-&gt;size(); ++m_indexInBlock) {
+            m_currentNode = block-&gt;at(m_indexInBlock);
+            performNodeCSE(m_currentNode);
</ins><span class="cx">         }
</span><del>-        
-        m_node-&gt;replaceWith(match);
-        m_changed = true;
</del><span class="cx">     }
</span><span class="cx">     
</span><del>-    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;
</del><ins>+    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.
</ins><span class="cx"> };
</span><span class="cx"> 
</span><del>-} // anonymous namespace
-
-bool performLCSE(Graph&amp; graph)
</del><ins>+bool performCSE(Graph&amp; graph)
</ins><span class="cx"> {
</span><del>-    SamplingRegion samplingRegion(&quot;DFG LCSE Phase&quot;);
-    return runPhase&lt;LCSEPhase&gt;(graph);
</del><ins>+    SamplingRegion samplingRegion(&quot;DFG CSE Phase&quot;);
+    return runPhase&lt;CSEPhase&gt;(graph);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool performGCSE(Graph&amp; graph)
-{
-    SamplingRegion samplingRegion(&quot;DFG GCSE Phase&quot;);
-    return runPhase&lt;GCSEPhase&gt;(graph);
-}
-
</del><span class="cx"> } } // namespace JSC::DFG
</span><span class="cx"> 
</span><span class="cx"> #endif // ENABLE(DFG_JIT)
</span><ins>+
+
</ins></span></pre>
</div>
</div>

</body>
</html>