]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - dfg/DFGCSEPhase.cpp
JavaScriptCore-7600.1.4.15.12.tar.gz
[apple/javascriptcore.git] / dfg / DFGCSEPhase.cpp
index 020b1cfd2b32fff2aa77125e5275fa4fa6854bd6..d4e1023d048c99998e342cb6540cd51da61e89a2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 Apple Inc. All rights reserved.
+ * Copyright (C) 2011, 2012, 2013, 2014 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 #if ENABLE(DFG_JIT)
 
+#include "DFGAbstractHeap.h"
+#include "DFGClobberize.h"
+#include "DFGEdgeUsesStructure.h"
 #include "DFGGraph.h"
 #include "DFGPhase.h"
+#include "JSCInlines.h"
+#include <array>
+#include <wtf/FastBitVector.h>
 
 namespace JSC { namespace DFG {
 
+enum CSEMode { NormalCSE, StoreElimination };
+
+template<CSEMode cseMode>
 class CSEPhase : public Phase {
 public:
     CSEPhase(Graph& graph)
-        : Phase(graph, "common subexpression elimination")
-    {
-        // Replacements are used to implement local common subexpression elimination.
-        m_replacements.resize(m_graph.size());
-        
-        for (unsigned i = 0; i < m_graph.size(); ++i)
-            m_replacements[i] = NoNode;
-    }
-    
-    void run()
+        : Phase(graph, cseMode == NormalCSE ? "common subexpression elimination" : "store elimination")
     {
-        for (unsigned block = 0; block < m_graph.m_blocks.size(); ++block)
-            performBlockCSE(*m_graph.m_blocks[block]);
     }
     
-private:
-    
-    NodeIndex canonicalize(NodeIndex nodeIndex)
+    bool run()
     {
-        if (nodeIndex == NoNode)
-            return NoNode;
+        ASSERT(m_graph.m_fixpointState != BeforeFixpoint);
         
-        if (m_graph[nodeIndex].op() == ValueToInt32)
-            nodeIndex = m_graph[nodeIndex].child1().index();
+        m_changed = false;
         
-        return nodeIndex;
-    }
-    NodeIndex canonicalize(Edge nodeUse)
-    {
-        return canonicalize(nodeUse.indexUnchecked());
+        m_graph.clearReplacements();
+        
+        for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
+            BasicBlock* block = m_graph.block(blockIndex);
+            if (!block)
+                continue;
+            
+            // All Phis need to already be marked as relevant to OSR.
+            if (!ASSERT_DISABLED) {
+                for (unsigned i = 0; i < block->phis.size(); ++i)
+                    ASSERT(block->phis[i]->flags() & NodeRelevantToOSR);
+            }
+            
+            for (unsigned i = block->size(); i--;) {
+                Node* node = block->at(i);
+                
+                switch (node->op()) {
+                case SetLocal:
+                case GetLocal: // FIXME: The GetLocal case is only necessary until we do https://bugs.webkit.org/show_bug.cgi?id=106707.
+                    node->mergeFlags(NodeRelevantToOSR);
+                    break;
+                default:
+                    node->clearFlags(NodeRelevantToOSR);
+                    break;
+                }
+            }
+        }
+        
+        for (BlockIndex blockIndex = m_graph.numBlocks(); blockIndex--;) {
+            BasicBlock* block = m_graph.block(blockIndex);
+            if (!block)
+                continue;
+            
+            for (unsigned i = block->size(); i--;) {
+                Node* node = block->at(i);
+                if (!node->containsMovHint())
+                    continue;
+                
+                ASSERT(node->op() != ZombieHint);
+                node->child1()->mergeFlags(NodeRelevantToOSR);
+            }
+        }
+        
+        if (m_graph.m_form == SSA) {
+            Vector<BasicBlock*> depthFirst;
+            m_graph.getBlocksInDepthFirstOrder(depthFirst);
+            for (unsigned i = 0; i < depthFirst.size(); ++i)
+                performBlockCSE(depthFirst[i]);
+        } else {
+            for (unsigned blockIndex = 0; blockIndex < m_graph.numBlocks(); ++blockIndex)
+                performBlockCSE(m_graph.block(blockIndex));
+        }
+        
+        return m_changed;
     }
     
+private:
+    
     unsigned endIndexForPureCSE()
     {
-        unsigned result = m_lastSeen[m_graph[m_compileIndex].op()];
+        unsigned result = m_lastSeen[m_currentNode->op()];
         if (result == UINT_MAX)
             result = 0;
         else
             result++;
         ASSERT(result <= m_indexInBlock);
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLog("  limit %u: ", result);
-#endif
         return result;
     }
-    
-    NodeIndex pureCSE(Node& node)
+
+    Node* pureCSE(Node* node)
     {
-        NodeIndex child1 = canonicalize(node.child1());
-        NodeIndex child2 = canonicalize(node.child2());
-        NodeIndex child3 = canonicalize(node.child3());
+        Edge child1 = node->child1().sanitized();
+        Edge child2 = node->child2().sanitized();
+        Edge child3 = node->child3().sanitized();
         
         for (unsigned i = endIndexForPureCSE(); i--;) {
-            NodeIndex index = m_currentBlock->at(i);
-            if (index == child1 || index == child2 || index == child3)
+            Node* otherNode = m_currentBlock->at(i);
+            if (otherNode == child1 || otherNode == child2 || otherNode == child3)
                 break;
 
-            Node& otherNode = m_graph[index];
-            if (node.op() != otherNode.op())
-                continue;
-            
-            if (node.arithNodeFlags() != otherNode.arithNodeFlags())
+            if (node->op() != otherNode->op())
                 continue;
             
-            NodeIndex otherChild = canonicalize(otherNode.child1());
-            if (otherChild == NoNode)
-                return index;
+            Edge otherChild = otherNode->child1().sanitized();
+            if (!otherChild)
+                return otherNode;
             if (otherChild != child1)
                 continue;
             
-            otherChild = canonicalize(otherNode.child2());
-            if (otherChild == NoNode)
-                return index;
+            otherChild = otherNode->child2().sanitized();
+            if (!otherChild)
+                return otherNode;
             if (otherChild != child2)
                 continue;
             
-            otherChild = canonicalize(otherNode.child3());
-            if (otherChild == NoNode)
-                return index;
+            otherChild = otherNode->child3().sanitized();
+            if (!otherChild)
+                return otherNode;
             if (otherChild != child3)
                 continue;
             
-            return index;
+            return otherNode;
         }
-        return NoNode;
+        return 0;
     }
     
-    bool isPredictedNumerical(Node& node)
+    Node* constantCSE(Node* node)
     {
-        PredictedType left = m_graph[node.child1()].prediction();
-        PredictedType right = m_graph[node.child2()].prediction();
-        return isNumberPrediction(left) && isNumberPrediction(right);
+        for (unsigned i = endIndexForPureCSE(); i--;) {
+            Node* otherNode = m_currentBlock->at(i);
+            if (otherNode->op() != node->op())
+                continue;
+            
+            if (otherNode->constantNumber() != node->constantNumber())
+                continue;
+            
+            return otherNode;
+        }
+        return 0;
     }
     
-    bool logicalNotIsPure(Node& node)
+    Node* weakConstantCSE(Node* node)
     {
-        PredictedType prediction = m_graph[node.child1()].prediction();
-        return isBooleanPrediction(prediction) || !prediction;
+        for (unsigned i = endIndexForPureCSE(); i--;) {
+            Node* otherNode = m_currentBlock->at(i);
+            if (otherNode->op() != WeakJSConstant)
+                continue;
+            
+            if (otherNode->weakConstant() != node->weakConstant())
+                continue;
+            
+            return otherNode;
+        }
+        return 0;
     }
     
-    bool byValIsPure(Node& node)
+    Node* constantStoragePointerCSE(Node* node)
     {
-        return m_graph[node.child2()].shouldSpeculateInteger()
-            && ((node.op() == PutByVal || node.op() == PutByValAlias)
-                ? isActionableMutableArrayPrediction(m_graph[node.child1()].prediction())
-                : isActionableArrayPrediction(m_graph[node.child1()].prediction()));
+        for (unsigned i = endIndexForPureCSE(); i--;) {
+            Node* otherNode = m_currentBlock->at(i);
+            if (otherNode->op() != ConstantStoragePointer)
+                continue;
+            
+            if (otherNode->storagePointer() != node->storagePointer())
+                continue;
+            
+            return otherNode;
+        }
+        return 0;
     }
     
-    bool clobbersWorld(NodeIndex nodeIndex)
+    Node* getCalleeLoadElimination()
     {
-        Node& node = m_graph[nodeIndex];
-        if (node.flags() & NodeClobbersWorld)
-            return true;
-        if (!(node.flags() & NodeMightClobber))
-            return false;
-        switch (node.op()) {
-        case ValueAdd:
-        case CompareLess:
-        case CompareLessEq:
-        case CompareGreater:
-        case CompareGreaterEq:
-        case CompareEq:
-            return !isPredictedNumerical(node);
-        case LogicalNot:
-            return !logicalNotIsPure(node);
-        case GetByVal:
-            return !byValIsPure(node);
-        default:
-            ASSERT_NOT_REACHED();
-            return true; // If by some oddity we hit this case in release build it's safer to have CSE assume the worst.
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            switch (node->op()) {
+            case GetCallee:
+                return node;
+            default:
+                break;
+            }
         }
+        return 0;
     }
     
-    NodeIndex impureCSE(Node& node)
+    Node* getArrayLengthElimination(Node* array)
     {
-        NodeIndex child1 = canonicalize(node.child1());
-        NodeIndex child2 = canonicalize(node.child2());
-        NodeIndex child3 = canonicalize(node.child3());
-        
         for (unsigned i = m_indexInBlock; i--;) {
-            NodeIndex index = m_currentBlock->at(i);
-            if (index == child1 || index == child2 || index == child3)
-                break;
-
-            Node& otherNode = m_graph[index];
-            if (node.op() == otherNode.op()
-                && node.arithNodeFlags() == otherNode.arithNodeFlags()) {
-                NodeIndex otherChild = canonicalize(otherNode.child1());
-                if (otherChild == NoNode)
-                    return index;
-                if (otherChild == child1) {
-                    otherChild = canonicalize(otherNode.child2());
-                    if (otherChild == NoNode)
-                        return index;
-                    if (otherChild == child2) {
-                        otherChild = canonicalize(otherNode.child3());
-                        if (otherChild == NoNode)
-                            return index;
-                        if (otherChild == child3)
-                            return index;
-                    }
-                }
-            }
-            if (clobbersWorld(index))
+            Node* node = m_currentBlock->at(i);
+            switch (node->op()) {
+            case GetArrayLength:
+                if (node->child1() == array)
+                    return node;
+                break;
+                
+            case PutByValDirect:
+            case PutByVal:
+                if (!m_graph.byValIsPure(node))
+                    return 0;
+                if (node->arrayMode().mayStoreToHole())
+                    return 0;
                 break;
+                
+            default:
+                if (m_graph.clobbersWorld(node))
+                    return 0;
+            }
         }
-        return NoNode;
+        return 0;
     }
     
-    NodeIndex globalVarLoadElimination(unsigned varNumber, JSGlobalObject* globalObject)
+    Node* globalVarLoadElimination(WriteBarrier<Unknown>* registerPointer)
     {
         for (unsigned i = m_indexInBlock; i--;) {
-            NodeIndex index = m_currentBlock->at(i);
-            Node& node = m_graph[index];
-            switch (node.op()) {
+            Node* node = m_currentBlock->at(i);
+            switch (node->op()) {
             case GetGlobalVar:
-                if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject)
-                    return index;
+                if (node->registerPointer() == registerPointer)
+                    return node;
+                break;
+            case PutGlobalVar:
+                if (node->registerPointer() == registerPointer)
+                    return node->child1().node();
+                break;
+            default:
+                break;
+            }
+            if (m_graph.clobbersWorld(node))
+                break;
+        }
+        return 0;
+    }
+    
+    Node* scopedVarLoadElimination(Node* registers, int varNumber)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            switch (node->op()) {
+            case GetClosureVar: {
+                if (node->child1() == registers && node->varNumber() == varNumber)
+                    return node;
+                break;
+            } 
+            case PutClosureVar: {
+                if (node->varNumber() != varNumber)
+                    break;
+                if (node->child2() == registers)
+                    return node->child3().node();
+                return 0;
+            }
+            case SetLocal: {
+                VariableAccessData* variableAccessData = node->variableAccessData();
+                if (variableAccessData->isCaptured()
+                    && variableAccessData->local() == static_cast<VirtualRegister>(varNumber))
+                    return 0;
+                break;
+            }
+            default:
+                break;
+            }
+            if (m_graph.clobbersWorld(node))
+                break;
+        }
+        return 0;
+    }
+    
+    bool varInjectionWatchpointElimination()
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            if (node->op() == VarInjectionWatchpoint)
+                return true;
+            if (m_graph.clobbersWorld(node))
                 break;
+        }
+        return false;
+    }
+    
+    Node* globalVarStoreElimination(WriteBarrier<Unknown>* registerPointer)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            switch (node->op()) {
             case PutGlobalVar:
-                if (node.varNumber() == varNumber && codeBlock()->globalObjectFor(node.codeOrigin) == globalObject)
-                    return node.child1().index();
+                if (node->registerPointer() == registerPointer)
+                    return node;
                 break;
+                
+            case GetGlobalVar:
+                if (node->registerPointer() == registerPointer)
+                    return 0;
+                break;
+                
             default:
                 break;
             }
-            if (clobbersWorld(index))
+            if (m_graph.clobbersWorld(node) || node->canExit())
+                return 0;
+        }
+        return 0;
+    }
+    
+    Node* scopedVarStoreElimination(Node* scope, Node* registers, int varNumber)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            switch (node->op()) {
+            case PutClosureVar: {
+                if (node->varNumber() != varNumber)
+                    break;
+                if (node->child1() == scope && node->child2() == registers)
+                    return node;
+                return 0;
+            }
+                
+            case GetClosureVar: {
+                // Let's be conservative.
+                if (node->varNumber() == varNumber)
+                    return 0;
                 break;
+            }
+                
+            case GetLocal:
+            case SetLocal: {
+                VariableAccessData* variableAccessData = node->variableAccessData();
+                if (variableAccessData->isCaptured()
+                    && variableAccessData->local() == static_cast<VirtualRegister>(varNumber))
+                    return 0;
+                break;
+            }
+
+            default:
+                break;
+            }
+            if (m_graph.clobbersWorld(node) || node->canExit())
+                return 0;
         }
-        return NoNode;
+        return 0;
     }
     
-    NodeIndex getByValLoadElimination(NodeIndex child1, NodeIndex child2)
+    Node* getByValLoadElimination(Node* child1, Node* child2, ArrayMode arrayMode)
     {
         for (unsigned i = m_indexInBlock; i--;) {
-            NodeIndex index = m_currentBlock->at(i);
-            if (index == child1 || index == canonicalize(child2)
+            Node* node = m_currentBlock->at(i);
+            if (node == child1 || node == child2
                 break;
 
-            Node& node = m_graph[index];
-            switch (node.op()) {
+            switch (node->op()) {
             case GetByVal:
-                if (!byValIsPure(node))
-                    return NoNode;
-                if (node.child1() == child1 && canonicalize(node.child2()) == canonicalize(child2))
-                    return index;
+                if (!m_graph.byValIsPure(node))
+                    return 0;
+                if (node->child1() == child1
+                    && node->child2() == child2
+                    && node->arrayMode().type() == arrayMode.type())
+                    return node;
                 break;
+                    
+            case PutByValDirect:
             case PutByVal:
-            case PutByValAlias:
-                if (!byValIsPure(node))
-                    return NoNode;
-                if (node.child1() == child1 && canonicalize(node.child2()) == canonicalize(child2))
-                    return node.child3().index();
+            case PutByValAlias: {
+                if (!m_graph.byValIsPure(node))
+                    return 0;
+                // Typed arrays 
+                if (arrayMode.typedArrayType() != NotTypedArray)
+                    return 0;
+                if (m_graph.varArgChild(node, 0) == child1
+                    && m_graph.varArgChild(node, 1) == child2
+                    && node->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.
-                return NoNode;
-            case PutStructure:
-            case PutByOffset:
-                // GetByVal currently always speculates that it's accessing an
-                // array with an integer index, which means that it's impossible
-                // for a structure change or a put to property storage to affect
-                // the GetByVal.
-                break;
-            case ArrayPush:
-                // A push cannot affect previously existing elements in the array.
-                break;
+                // ... 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 (clobbersWorld(index))
-                    return NoNode;
+                if (m_graph.clobbersWorld(node))
+                    return 0;
                 break;
             }
         }
-        return NoNode;
+        return 0;
     }
 
-    bool checkFunctionElimination(JSFunction* function, NodeIndex child1)
+    bool checkFunctionElimination(JSCell* function, Node* child1)
+    {
+        for (unsigned i = endIndexForPureCSE(); i--;) {
+            Node* node = m_currentBlock->at(i);
+            if (node == child1) 
+                break;
+
+            if (node->op() == CheckFunction && node->child1() == child1 && node->function() == function)
+                return true;
+        }
+        return false;
+    }
+    
+    bool checkExecutableElimination(ExecutableBase* executable, Node* child1)
     {
         for (unsigned i = endIndexForPureCSE(); i--;) {
-            NodeIndex index = m_currentBlock->at(i);
-            if (index == child1) 
+            Node* node = m_currentBlock->at(i);
+            if (node == child1)
                 break;
 
-            Node& node = m_graph[index];
-            if (node.op() == CheckFunction && node.child1() == child1 && node.function() == function)
+            if (node->op() == CheckExecutable && node->child1() == child1 && node->executable() == executable)
                 return true;
         }
         return false;
     }
 
-    bool checkStructureLoadElimination(const StructureSet& structureSet, NodeIndex child1)
+    bool checkStructureElimination(const StructureSet& structureSet, Node* child1)
     {
         for (unsigned i = m_indexInBlock; i--;) {
-            NodeIndex index = m_currentBlock->at(i);
-            if (index == child1) 
+            Node* node = m_currentBlock->at(i);
+            if (node == child1) 
                 break;
 
-            Node& node = m_graph[index];
-            switch (node.op()) {
+            switch (node->op()) {
             case CheckStructure:
-                if (node.child1() == child1
-                    && structureSet.isSupersetOf(node.structureSet()))
+                if (node->child1() == child1
+                    && structureSet.isSupersetOf(node->structureSet()))
+                    return true;
+                break;
+                
+            case StructureTransitionWatchpoint:
+                if (node->child1() == child1
+                    && structureSet.contains(node->structure()))
                     return true;
                 break;
                 
             case PutStructure:
-                if (node.child1() == child1
-                    && structureSet.contains(node.structureTransitionData().newStructure))
+                if (node->child1() == child1
+                    && structureSet.contains(node->structureTransitionData().newStructure))
                     return true;
-                if (structureSet.contains(node.structureTransitionData().previousStructure))
+                if (structureSet.contains(node->structureTransitionData().previousStructure))
                     return false;
                 break;
                 
@@ -313,9 +478,15 @@ private:
                 // Setting a property cannot change the structure.
                 break;
                 
+            case MultiPutByOffset:
+                if (node->multiPutByOffsetData().writesStructures())
+                    return false;
+                break;
+                
+            case PutByValDirect:
             case PutByVal:
             case PutByValAlias:
-                if (byValIsPure(node)) {
+                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.
@@ -323,8 +494,14 @@ private:
                 }
                 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 (clobbersWorld(index))
+                if (m_graph.clobbersWorld(node))
                     return false;
                 break;
             }
@@ -332,230 +509,635 @@ private:
         return false;
     }
     
-    NodeIndex getByOffsetLoadElimination(unsigned identifierNumber, NodeIndex child1)
+    bool structureTransitionWatchpointElimination(Structure* structure, Node* child1)
     {
         for (unsigned i = m_indexInBlock; i--;) {
-            NodeIndex index = m_currentBlock->at(i);
-            if (index == child1) 
+            Node* node = m_currentBlock->at(i);
+            if (node == child1) 
                 break;
 
-            Node& node = m_graph[index];
-            switch (node.op()) {
-            case GetByOffset:
-                if (node.child1() == child1
-                    && m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber == identifierNumber)
-                    return index;
+            switch (node->op()) {
+            case CheckStructure:
+                if (node->child1() == child1
+                    && node->structureSet().containsOnly(structure))
+                    return true;
                 break;
                 
-            case PutByOffset:
-                if (m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber == identifierNumber) {
-                    if (node.child2() == child1)
-                        return node.child3().index();
-                    return NoNode;
-                }
+            case PutStructure:
+                ASSERT(node->structureTransitionData().previousStructure != structure);
                 break;
                 
-            case PutStructure:
-                // Changing the structure cannot change the outcome of a property get.
+            case PutByOffset:
+                // Setting a property cannot change the structure.
+                break;
+                    
+            case MultiPutByOffset:
+                if (node->multiPutByOffsetData().writesStructures())
+                    return false;
                 break;
                 
+            case PutByValDirect:
             case PutByVal:
             case PutByValAlias:
-                if (byValIsPure(node)) {
+                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 NoNode;
+                return false;
+                
+            case StructureTransitionWatchpoint:
+                if (node->structure() == structure && node->child1() == child1)
+                    return true;
+                break;
+                
+            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;
+            }
+        }
+        return false;
+    }
+    
+    Node* putStructureStoreElimination(Node* child1)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            if (node == child1)
+                break;
+            switch (node->op()) {
+            case CheckStructure:
+                return 0;
+                
+            case PhantomPutStructure:
+                if (node->child1() == child1) // No need to retrace our steps.
+                    return 0;
+                break;
+                
+            case PutStructure:
+                if (node->child1() == child1)
+                    return node;
+                break;
+                
+            // PutStructure needs to execute if we GC. Hence this needs to
+            // be careful with respect to nodes that GC.
+            case CreateArguments:
+            case TearOffArguments:
+            case NewFunctionNoCheck:
+            case NewFunction:
+            case NewFunctionExpression:
+            case CreateActivation:
+            case TearOffActivation:
+            case ToPrimitive:
+            case NewRegexp:
+            case NewArrayBuffer:
+            case NewArray:
+            case NewObject:
+            case CreateThis:
+            case AllocatePropertyStorage:
+            case ReallocatePropertyStorage:
+            case TypeOf:
+            case ToString:
+            case NewStringObject:
+            case MakeRope:
+            case NewTypedArray:
+            case MultiPutByOffset:
+                return 0;
+                
+            // This either exits, causes a GC (lazy string allocation), or clobbers
+            // the world. The chances of it not doing any of those things are so
+            // slim that we might as well not even try to reason about it.
+            case GetByVal:
+                return 0;
+                
+            case GetIndexedPropertyStorage:
+                if (node->arrayMode().getIndexedPropertyStorageMayTriggerGC())
+                    return 0;
+                break;
                 
             default:
-                if (clobbersWorld(index))
-                    return NoNode;
                 break;
             }
+            if (m_graph.clobbersWorld(node) || node->canExit())
+                return 0;
+            if (edgesUseStructure(m_graph, node))
+                return 0;
         }
-        return NoNode;
+        return 0;
     }
     
-    NodeIndex getPropertyStorageLoadElimination(NodeIndex child1)
+    Node* getByOffsetLoadElimination(unsigned identifierNumber, Node* base)
     {
         for (unsigned i = m_indexInBlock; i--;) {
-            NodeIndex index = m_currentBlock->at(i);
-            if (index == child1) 
+            Node* node = m_currentBlock->at(i);
+            if (node == base)
                 break;
 
-            Node& node = m_graph[index];
-            switch (node.op()) {
-            case GetPropertyStorage:
-                if (node.child1() == child1)
-                    return index;
+            switch (node->op()) {
+            case GetByOffset:
+                if (node->child2() == base
+                    && m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber == identifierNumber)
+                    return node;
+                break;
+                
+            case MultiGetByOffset:
+                if (node->child1() == base
+                    && node->multiGetByOffsetData().identifierNumber == identifierNumber)
+                    return node;
                 break;
                 
             case PutByOffset:
-            case PutStructure:
-                // Changing the structure or putting to the storage cannot
-                // change the property storage pointer.
+                if (m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber == identifierNumber) {
+                    if (node->child2() == base) // Must be same property storage.
+                        return node->child3().node();
+                    return 0;
+                }
                 break;
                 
+            case MultiPutByOffset:
+                if (node->multiPutByOffsetData().identifierNumber == identifierNumber) {
+                    if (node->child1() == base)
+                        return node->child2().node();
+                    return 0;
+                }
+                break;
+                    
+            case PutByValDirect:
             case PutByVal:
             case PutByValAlias:
-                if (byValIsPure(node)) {
+                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 NoNode;
+                return 0;
                 
             default:
-                if (clobbersWorld(index))
-                    return NoNode;
+                if (m_graph.clobbersWorld(node))
+                    return 0;
                 break;
             }
         }
-        return NoNode;
+        return 0;
     }
-
-    NodeIndex getIndexedPropertyStorageLoadElimination(NodeIndex child1, bool hasIntegerIndexPrediction)
+    
+    Node* putByOffsetStoreElimination(unsigned identifierNumber, Node* child1)
     {
         for (unsigned i = m_indexInBlock; i--;) {
-            NodeIndex index = m_currentBlock->at(i);
-            if (index == child1) 
+            Node* node = m_currentBlock->at(i);
+            if (node == child1)
                 break;
 
-            Node& node = m_graph[index];
-            switch (node.op()) {
-            case GetIndexedPropertyStorage: {
-                PredictedType basePrediction = m_graph[node.child2()].prediction();
-                bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction);
-                if (node.child1() == child1 && hasIntegerIndexPrediction == nodeHasIntegerIndexPrediction)
-                    return index;
+            switch (node->op()) {
+            case GetByOffset:
+                if (m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber == identifierNumber)
+                    return 0;
                 break;
-            }
-
+                
             case PutByOffset:
-            case PutStructure:
-                // Changing the structure or putting to the storage cannot
-                // change the property storage pointer.
+                if (m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber == identifierNumber) {
+                    if (node->child1() == child1) // Must be same property storage.
+                        return node;
+                    return 0;
+                }
                 break;
                 
-            case PutByValAlias:
-                // PutByValAlias can't change the indexed storage pointer
+            case MultiPutByOffset:
+                if (node->multiPutByOffsetData().identifierNumber == identifierNumber)
+                    return 0;
                 break;
                 
+            case PutByValDirect:
             case PutByVal:
-                if (isFixedIndexedStorageObjectPrediction(m_graph[node.child1()].prediction()) && byValIsPure(node))
+            case PutByValAlias:
+            case GetByVal:
+                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 NoNode;
-
+                }
+                return 0;
+                
             default:
-                if (clobbersWorld(index))
-                    return NoNode;
+                if (m_graph.clobbersWorld(node))
+                    return 0;
                 break;
             }
+            if (node->canExit())
+                return 0;
         }
-        return NoNode;
+        return 0;
     }
     
-    NodeIndex getScopeChainLoadElimination(unsigned depth)
+    Node* getPropertyStorageLoadElimination(Node* child1)
     {
-        for (unsigned i = endIndexForPureCSE(); i--;) {
-            NodeIndex index = m_currentBlock->at(i);
-            Node& node = m_graph[index];
-            if (node.op() == GetScopeChain
-                && node.scopeChainDepth() == depth)
-                return index;
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            if (node == child1) 
+                break;
+
+            switch (node->op()) {
+            case GetButterfly:
+                if (node->child1() == child1)
+                    return node;
+                break;
+
+            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->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->multiPutByOffsetData().reallocatesStorage())
+                    return 0;
+                break;
+                
+            default:
+                if (m_graph.clobbersWorld(node))
+                    return 0;
+                break;
+            }
         }
-        return NoNode;
+        return 0;
     }
     
-    void performSubstitution(Edge& child, bool addRef = true)
+    bool checkArrayElimination(Node* child1, ArrayMode arrayMode)
     {
-        // Check if this operand is actually unused.
-        if (!child)
-            return;
-        
-        // Check if there is any replacement.
-        NodeIndex replacement = m_replacements[child.index()];
-        if (replacement == NoNode)
-            return;
-        
-        child.setIndex(replacement);
-        
-        // There is definitely a replacement. Assert that the replacement does not
-        // have a replacement.
-        ASSERT(m_replacements[child.index()] == NoNode);
-        
-        if (addRef)
-            m_graph[child].ref();
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            if (node == child1) 
+                break;
+
+            switch (node->op()) {
+            case CheckArray:
+                if (node->child1() == child1 && node->arrayMode() == arrayMode)
+                    return true;
+                break;
+                
+            case Arrayify:
+            case ArrayifyToStructure:
+                // We could check if the arrayification could affect our array.
+                // But that seems like it would take Effort.
+                return false;
+                
+            default:
+                if (m_graph.clobbersWorld(node))
+                    return false;
+                break;
+            }
+        }
+        return false;
+    }
+
+    Node* getIndexedPropertyStorageLoadElimination(Node* child1, ArrayMode arrayMode)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            if (node == child1) 
+                break;
+
+            switch (node->op()) {
+            case GetIndexedPropertyStorage: {
+                if (node->child1() == child1 && node->arrayMode() == arrayMode)
+                    return node;
+                break;
+            }
+
+            default:
+                if (m_graph.clobbersWorld(node))
+                    return 0;
+                break;
+            }
+        }
+        return 0;
     }
     
-    void setReplacement(NodeIndex replacement)
+    Node* getTypedArrayByteOffsetLoadElimination(Node* child1)
     {
-        if (replacement == NoNode)
-            return;
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            if (node == child1) 
+                break;
+
+            switch (node->op()) {
+            case GetTypedArrayByteOffset: {
+                if (node->child1() == child1)
+                    return node;
+                break;
+            }
+
+            default:
+                if (m_graph.clobbersWorld(node))
+                    return 0;
+                break;
+            }
+        }
+        return 0;
+    }
+    
+    Node* getMyScopeLoadElimination()
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            switch (node->op()) {
+            case CreateActivation:
+                // This may cause us to return a different scope.
+                return 0;
+            case GetMyScope:
+                return node;
+            default:
+                break;
+            }
+        }
+        return 0;
+    }
+    
+    Node* getLocalLoadElimination(VirtualRegister local, Node*& relevantLocalOp, bool careAboutClobbering)
+    {
+        relevantLocalOp = 0;
         
-        // Be safe. Don't try to perform replacements if the predictions don't
-        // agree.
-        if (m_graph[m_compileIndex].prediction() != m_graph[replacement].prediction())
-            return;
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            switch (node->op()) {
+            case GetLocal:
+                if (node->local() == local) {
+                    relevantLocalOp = node;
+                    return node;
+                }
+                break;
+                
+            case GetLocalUnlinked:
+                if (node->unlinkedLocal() == local) {
+                    relevantLocalOp = node;
+                    return node;
+                }
+                break;
+                
+            case SetLocal:
+                if (node->local() == local) {
+                    relevantLocalOp = node;
+                    return node->child1().node();
+                }
+                break;
+                
+            case GetClosureVar:
+            case PutClosureVar:
+                if (static_cast<VirtualRegister>(node->varNumber()) == local)
+                    return 0;
+                break;
+                
+            default:
+                if (careAboutClobbering && m_graph.clobbersWorld(node))
+                    return 0;
+                break;
+            }
+        }
+        return 0;
+    }
+    
+    bool uncapturedSetLocalStoreElimination(VirtualRegister local, Node* expectedNode)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            switch (node->op()) {
+            case GetLocal:
+            case Flush:
+                if (node->local() == local)
+                    return false;
+                break;
+                
+            case GetLocalUnlinked:
+                if (node->unlinkedLocal() == local)
+                    return false;
+                break;
+                
+            case SetLocal: {
+                if (node->local() != local)
+                    break;
+                if (node != expectedNode)
+                    return false;
+                return true;
+            }
+                
+            case GetClosureVar:
+            case PutClosureVar:
+                if (static_cast<VirtualRegister>(node->varNumber()) == local)
+                    return false;
+                break;
+                
+            case GetMyScope:
+            case SkipTopScope:
+                if (node->origin.semantic.inlineCallFrame)
+                    break;
+                if (m_graph.uncheckedActivationRegister() == local)
+                    return false;
+                break;
+                
+            case CheckArgumentsNotCreated:
+            case GetMyArgumentsLength:
+            case GetMyArgumentsLengthSafe:
+                if (m_graph.uncheckedArgumentsRegisterFor(node->origin.semantic) == local)
+                    return false;
+                break;
+                
+            case GetMyArgumentByVal:
+            case GetMyArgumentByValSafe:
+                return false;
+                
+            case GetByVal:
+                // If this is accessing arguments then it's potentially accessing locals.
+                if (node->arrayMode().type() == Array::Arguments)
+                    return false;
+                break;
+                
+            case CreateArguments:
+            case TearOffActivation:
+            case TearOffArguments:
+                // If an activation is being torn off then it means that captured variables
+                // are live. We could be clever here and check if the local qualifies as an
+                // argument register. But that seems like it would buy us very little since
+                // any kind of tear offs are rare to begin with.
+                return false;
+                
+            default:
+                break;
+            }
+            if (m_graph.clobbersWorld(node))
+                return false;
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+        return false;
+    }
+
+    bool capturedSetLocalStoreElimination(VirtualRegister local, Node* expectedNode)
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            switch (node->op()) {
+            case GetLocal:
+            case Flush:
+                if (node->local() == local)
+                    return false;
+                break;
+                
+            case GetLocalUnlinked:
+                if (node->unlinkedLocal() == local)
+                    return false;
+                break;
+                
+            case SetLocal: {
+                if (node->local() != local)
+                    break;
+                if (node != expectedNode)
+                    return false;
+                return true;
+            }
+                
+            case Phantom:
+            case Check:
+            case HardPhantom:
+            case MovHint:
+            case JSConstant:
+            case DoubleConstant:
+            case Int52Constant:
+                break;
+                
+            default:
+                return false;
+            }
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+        return false;
+    }
+    
+    bool setLocalStoreElimination(VariableAccessData* variableAccessData, Node* expectedNode)
+    {
+        if (variableAccessData->isCaptured())
+            return capturedSetLocalStoreElimination(variableAccessData->local(), expectedNode);
+        return uncapturedSetLocalStoreElimination(variableAccessData->local(), expectedNode);
+    }
+    
+    bool invalidationPointElimination()
+    {
+        for (unsigned i = m_indexInBlock; i--;) {
+            Node* node = m_currentBlock->at(i);
+            if (node->op() == InvalidationPoint)
+                return true;
+            if (writesOverlap(m_graph, node, Watchpoint_fire))
+                break;
+        }
+        return false;
+    }
+    
+    void eliminateIrrelevantPhantomChildren(Node* node)
+    {
+        ASSERT(node->op() == Phantom);
+        for (unsigned i = 0; i < AdjacencyList::Size; ++i) {
+            Edge edge = node->children.child(i);
+            if (!edge)
+                continue;
+            if (edge.useKind() != UntypedUse)
+                continue; // Keep the type check.
+            if (edge->flags() & NodeRelevantToOSR)
+                continue;
+            
+            node->children.removeEdge(i--);
+            m_changed = true;
+        }
+    }
+    
+    bool setReplacement(Node* replacement)
+    {
+        if (!replacement)
+            return false;
         
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLog("   Replacing @%u -> @%u", m_compileIndex, replacement);
-#endif
+        if (!ASSERT_DISABLED
+            && canonicalResultRepresentation(m_currentNode->result()) != canonicalResultRepresentation(replacement->result())) {
+            startCrashing();
+            dataLog("CSE attempting to replace a node with a mismatched result: ", m_currentNode, " with ", replacement, "\n");
+            dataLog("\n");
+            m_graph.dump();
+            RELEASE_ASSERT_NOT_REACHED();
+        }
         
-        Node& node = m_graph[m_compileIndex];
-        node.setOpAndDefaultFlags(Phantom);
-        node.setRefCount(1);
+        m_currentNode->convertToPhantom();
+        eliminateIrrelevantPhantomChildren(m_currentNode);
         
         // At this point we will eliminate all references to this node.
-        m_replacements[m_compileIndex] = replacement;
+        m_currentNode->misc.replacement = replacement;
+        
+        m_changed = true;
+        
+        return true;
     }
     
     void eliminate()
     {
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLog("   Eliminating @%u", m_compileIndex);
-#endif
+        ASSERT(m_currentNode->mustGenerate());
+        m_currentNode->convertToPhantom();
+        eliminateIrrelevantPhantomChildren(m_currentNode);
         
-        Node& node = m_graph[m_compileIndex];
-        ASSERT(node.refCount() == 1);
-        ASSERT(node.mustGenerate());
-        node.setOpAndDefaultFlags(Phantom);
+        m_changed = true;
     }
     
-    void performNodeCSE(Node& node)
+    void eliminate(Node* node, NodeType phantomType = Phantom)
     {
-        bool shouldGenerate = node.shouldGenerate();
-
-        if (node.flags() & NodeHasVarArgs) {
-            for (unsigned childIdx = node.firstChild(); childIdx < node.firstChild() + node.numChildren(); childIdx++)
-                performSubstitution(m_graph.m_varArgChildren[childIdx], shouldGenerate);
-        } else {
-            performSubstitution(node.children.child1(), shouldGenerate);
-            performSubstitution(node.children.child2(), shouldGenerate);
-            performSubstitution(node.children.child3(), shouldGenerate);
-        }
-        
-        if (!shouldGenerate)
+        if (!node)
             return;
+        ASSERT(node->mustGenerate());
+        node->setOpAndDefaultFlags(phantomType);
+        if (phantomType == Phantom)
+            eliminateIrrelevantPhantomChildren(node);
         
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLog("   %s @%u: ", Graph::opName(m_graph[m_compileIndex].op()), m_compileIndex);
-#endif
-        
-        // NOTE: there are some nodes that we deliberately don't CSE even though we
-        // probably could, like StrCat and ToPrimitive. That's because there is no
-        // evidence that doing CSE on these nodes would result in a performance
-        // progression. Hence considering these nodes in CSE would just mean that this
-        // code does more work with no win. Of course, we may want to reconsider this,
-        // since StrCat is trivially CSE-able. It's not trivially doable for
-        // ToPrimitive, but we could change that with some speculations if we really
-        // needed to.
+        m_changed = true;
+    }
+    
+    void performNodeCSE(Node* node)
+    {
+        if (cseMode == NormalCSE)
+            m_graph.performSubstitution(node);
         
-        switch (node.op()) {
+        switch (node->op()) {
         
+        case Identity:
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(node->child1().node());
+            break;
+            
         // Handle the pure nodes. These nodes never have any side-effects.
         case BitAnd:
         case BitOr:
@@ -563,69 +1145,189 @@ private:
         case BitRShift:
         case BitLShift:
         case BitURShift:
-        case ArithAdd:
-        case ArithSub:
-        case ArithNegate:
-        case ArithMul:
-        case ArithMod:
-        case ArithDiv:
         case ArithAbs:
         case ArithMin:
         case ArithMax:
         case ArithSqrt:
-        case GetInt8ArrayLength:
-        case GetInt16ArrayLength:
-        case GetInt32ArrayLength:
-        case GetUint8ArrayLength:
-        case GetUint8ClampedArrayLength:
-        case GetUint16ArrayLength:
-        case GetUint32ArrayLength:
-        case GetFloat32ArrayLength:
-        case GetFloat64ArrayLength:
-        case GetCallee:
-        case GetStringLength:
+        case ArithFRound:
+        case ArithSin:
+        case ArithCos:
         case StringCharAt:
         case StringCharCodeAt:
-        case Int32ToDouble:
         case IsUndefined:
         case IsBoolean:
         case IsNumber:
         case IsString:
         case IsObject:
         case IsFunction:
-        case DoubleAsInt32:
+        case LogicalNot:
+        case SkipTopScope:
+        case SkipScope:
+        case GetClosureRegisters:
+        case GetScope:
+        case TypeOf:
+        case CompareEqConstant:
+        case ValueToInt32:
+        case MakeRope:
+        case DoubleRep:
+        case ValueRep:
+        case Int52Rep:
+        case BooleanToNumber:
+            if (cseMode == StoreElimination)
+                break;
             setReplacement(pureCSE(node));
             break;
             
-        case GetArrayLength:
-            setReplacement(impureCSE(node));
+        case ArithAdd:
+        case ArithSub:
+        case ArithNegate:
+        case ArithMul:
+        case ArithDiv:
+        case ArithMod:
+        case UInt32ToNumber:
+        case DoubleAsInt32: {
+            if (cseMode == StoreElimination)
+                break;
+            Node* candidate = pureCSE(node);
+            if (!candidate)
+                break;
+            if (!subsumes(candidate->arithMode(), node->arithMode())) {
+                if (!subsumes(node->arithMode(), candidate->arithMode()))
+                    break;
+                candidate->setArithMode(node->arithMode());
+            }
+            setReplacement(candidate);
+            break;
+        }
+            
+        case GetCallee:
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(getCalleeLoadElimination());
+            break;
+
+        case GetLocal: {
+            if (cseMode == StoreElimination)
+                break;
+            VariableAccessData* variableAccessData = node->variableAccessData();
+            if (!variableAccessData->isCaptured())
+                break;
+            Node* relevantLocalOp;
+            Node* possibleReplacement = getLocalLoadElimination(variableAccessData->local(), relevantLocalOp, variableAccessData->isCaptured());
+            if (!relevantLocalOp)
+                break;
+            if (relevantLocalOp->op() != GetLocalUnlinked
+                && relevantLocalOp->variableAccessData() != variableAccessData)
+                break;
+            Node* phi = node->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->op() == GetLocalUnlinked)
+                relevantLocalOp->convertToGetLocal(variableAccessData, phi);
+
+            m_changed = true;
+            break;
+        }
+            
+        case GetLocalUnlinked: {
+            if (cseMode == StoreElimination)
+                break;
+            Node* relevantLocalOpIgnored;
+            setReplacement(getLocalLoadElimination(node->unlinkedLocal(), relevantLocalOpIgnored, true));
+            break;
+        }
+            
+        case Flush: {
+            ASSERT(m_graph.m_form != SSA);
+            VariableAccessData* variableAccessData = node->variableAccessData();
+            if (!node->child1()) {
+                // FIXME: It's silly that we punt on flush-eliminating here. We don't really
+                // need child1 to figure out what's going on.
+                // https://bugs.webkit.org/show_bug.cgi?id=130521
+                break;
+            }
+            Node* replacement = node->child1().node();
+            if (replacement->op() != SetLocal)
+                break;
+            ASSERT(replacement->variableAccessData() == variableAccessData);
+            // FIXME: We should be able to remove SetLocals that can exit; we just need
+            // to replace them with appropriate type checks.
+            if (cseMode == NormalCSE) {
+                // Need to be conservative at this time; if the SetLocal has any chance of performing
+                // any speculations then we cannot do anything.
+                FlushFormat format = variableAccessData->flushFormat();
+                ASSERT(format != DeadFlush);
+                if (format != FlushedJSValue)
+                    break;
+            } else {
+                if (replacement->canExit())
+                    break;
+            }
+            if (!setLocalStoreElimination(variableAccessData, replacement))
+                break;
+            ASSERT(replacement->op() == SetLocal);
+            node->convertToPhantom();
+            Node* dataNode = replacement->child1().node();
+            ASSERT(dataNode->hasResult());
+            node->child1() = dataNode->defaultEdge();
+            m_graph.dethread();
+            m_changed = true;
+            break;
+        }
+            
+        case JSConstant:
+        case DoubleConstant:
+        case Int52Constant:
+            if (cseMode == StoreElimination)
+                break;
+            // 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 WeakJSConstant:
+            if (cseMode == StoreElimination)
+                break;
+            // FIXME: have CSE for weak constants against strong constants and vice-versa.
+            setReplacement(weakConstantCSE(node));
+            break;
+            
+        case ConstantStoragePointer:
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(constantStoragePointerCSE(node));
             break;
             
-        case GetScopeChain:
-            setReplacement(getScopeChainLoadElimination(node.scopeChainDepth()));
+        case GetArrayLength:
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(getArrayLengthElimination(node->child1().node()));
             break;
 
+        case GetMyScope:
+            if (cseMode == StoreElimination)
+                break;
+            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 ValueAdd:
         case CompareLess:
         case CompareLessEq:
         case CompareGreater:
         case CompareGreaterEq:
         case CompareEq: {
-            if (isPredictedNumerical(node)) {
-                NodeIndex replacementIndex = pureCSE(node);
-                if (replacementIndex != NoNode && isPredictedNumerical(m_graph[replacementIndex]))
-                    setReplacement(replacementIndex);
-            }
-            break;
-        }
-            
-        case LogicalNot: {
-            if (logicalNotIsPure(node)) {
-                NodeIndex replacementIndex = pureCSE(node);
-                if (replacementIndex != NoNode && logicalNotIsPure(m_graph[replacementIndex]))
-                    setReplacement(replacementIndex);
+            if (cseMode == StoreElimination)
+                break;
+            if (m_graph.isPredictedNumerical(node)) {
+                Node* replacement = pureCSE(node);
+                if (replacement && m_graph.isPredictedNumerical(replacement))
+                    setReplacement(replacement);
             }
             break;
         }
@@ -633,42 +1335,150 @@ private:
         // 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.varNumber(), codeBlock()->globalObjectFor(node.codeOrigin)));
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(globalVarLoadElimination(node->registerPointer()));
+            break;
+
+        case GetClosureVar: {
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(scopedVarLoadElimination(node->child1().node(), node->varNumber()));
+            break;
+        }
+
+        case VarInjectionWatchpoint:
+            if (cseMode == StoreElimination)
+                break;
+            if (varInjectionWatchpointElimination())
+                eliminate();
             break;
             
-        case GetByVal:
-            if (byValIsPure(node))
-                setReplacement(getByValLoadElimination(node.child1().index(), node.child2().index()));
+        case PutGlobalVar:
+            if (cseMode == NormalCSE)
+                break;
+            eliminate(globalVarStoreElimination(node->registerPointer()));
             break;
             
-        case PutByVal:
-            if (byValIsPure(node) && getByValLoadElimination(node.child1().index(), node.child2().index()) != NoNode)
-                node.setOp(PutByValAlias);
+        case PutClosureVar: {
+            if (cseMode == NormalCSE)
+                break;
+            eliminate(scopedVarStoreElimination(node->child1().node(), node->child2().node(), node->varNumber()));
+            break;
+        }
+
+        case GetByVal:
+            if (cseMode == StoreElimination)
+                break;
+            if (m_graph.byValIsPure(node))
+                setReplacement(getByValLoadElimination(node->child1().node(), node->child2().node(), node->arrayMode()));
             break;
+                
+        case PutByValDirect:
+        case PutByVal: {
+            if (cseMode == StoreElimination)
+                break;
+            Edge child1 = m_graph.varArgChild(node, 0);
+            Edge child2 = m_graph.varArgChild(node, 1);
+            if (node->arrayMode().canCSEStorage()) {
+                Node* replacement = getByValLoadElimination(child1.node(), child2.node(), node->arrayMode());
+                if (!replacement)
+                    break;
+                node->setOp(PutByValAlias);
+            }
+            break;
+        }
             
         case CheckStructure:
-            if (checkStructureLoadElimination(node.structureSet(), node.child1().index()))
+            if (cseMode == StoreElimination)
+                break;
+            if (checkStructureElimination(node->structureSet(), node->child1().node()))
                 eliminate();
             break;
+            
+        case StructureTransitionWatchpoint:
+            if (cseMode == StoreElimination)
+                break;
+            if (structureTransitionWatchpointElimination(node->structure(), node->child1().node()))
+                eliminate();
+            break;
+            
+        case PutStructure:
+            if (cseMode == NormalCSE)
+                break;
+            eliminate(putStructureStoreElimination(node->child1().node()), PhantomPutStructure);
+            break;
 
         case CheckFunction:
-            if (checkFunctionElimination(node.function(), node.child1().index()))
+            if (cseMode == StoreElimination)
+                break;
+            if (checkFunctionElimination(node->function(), node->child1().node()))
                 eliminate();
             break;
                 
+        case CheckExecutable:
+            if (cseMode == StoreElimination)
+                break;
+            if (checkExecutableElimination(node->executable(), node->child1().node()))
+                eliminate();
+            break;
+                
+        case CheckArray:
+            if (cseMode == StoreElimination)
+                break;
+            if (checkArrayElimination(node->child1().node(), node->arrayMode()))
+                eliminate();
+            break;
+            
         case GetIndexedPropertyStorage: {
-            PredictedType basePrediction = m_graph[node.child2()].prediction();
-            bool nodeHasIntegerIndexPrediction = !(!(basePrediction & PredictInt32) && basePrediction);
-            setReplacement(getIndexedPropertyStorageLoadElimination(node.child1().index(), nodeHasIntegerIndexPrediction));
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(getIndexedPropertyStorageLoadElimination(node->child1().node(), node->arrayMode()));
+            break;
+        }
+            
+        case GetTypedArrayByteOffset: {
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(getTypedArrayByteOffsetLoadElimination(node->child1().node()));
             break;
         }
 
-        case GetPropertyStorage:
-            setReplacement(getPropertyStorageLoadElimination(node.child1().index()));
+        case GetButterfly:
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(getPropertyStorageLoadElimination(node->child1().node()));
             break;
 
         case GetByOffset:
-            setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node.storageAccessDataIndex()].identifierNumber, node.child1().index()));
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber, node->child2().node()));
+            break;
+            
+        case MultiGetByOffset:
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(getByOffsetLoadElimination(node->multiGetByOffsetData().identifierNumber, node->child1().node()));
+            break;
+            
+        case PutByOffset:
+            if (cseMode == NormalCSE)
+                break;
+            eliminate(putByOffsetStoreElimination(m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber, node->child1().node()));
+            break;
+            
+        case InvalidationPoint:
+            if (invalidationPointElimination())
+                eliminate();
+            break;
+            
+        case Phantom:
+            // FIXME: we ought to remove Phantom's that have no children.
+            // NB. It would be incorrect to do this for HardPhantom. In fact, the whole point
+            // of HardPhantom is that we *don't* do this for HardPhantoms, since they signify
+            // a more strict kind of liveness than the Phantom bytecode liveness.
+            eliminateIrrelevantPhantomChildren(node);
             break;
             
         default:
@@ -676,34 +1486,49 @@ private:
             break;
         }
         
-        m_lastSeen[node.op()] = m_indexInBlock;
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLog("\n");
-#endif
+        m_lastSeen[node->op()] = m_indexInBlock;
     }
     
-    void performBlockCSE(BasicBlock& block)
+    void performBlockCSE(BasicBlock* block)
     {
-        m_currentBlock = &block;
+        if (!block)
+            return;
+        if (!block->isReachable)
+            return;
+        
+        m_currentBlock = block;
         for (unsigned i = 0; i < LastNodeType; ++i)
             m_lastSeen[i] = UINT_MAX;
-
-        for (m_indexInBlock = 0; m_indexInBlock < block.size(); ++m_indexInBlock) {
-            m_compileIndex = block[m_indexInBlock];
-            performNodeCSE(m_graph[m_compileIndex]);
+        
+        for (m_indexInBlock = 0; m_indexInBlock < block->size(); ++m_indexInBlock) {
+            m_currentNode = block->at(m_indexInBlock);
+            performNodeCSE(m_currentNode);
+        }
+        
+        if (!ASSERT_DISABLED && cseMode == StoreElimination) {
+            // Nobody should have replacements set.
+            for (unsigned i = 0; i < block->size(); ++i)
+                ASSERT(!block->at(i)->misc.replacement);
         }
     }
     
     BasicBlock* m_currentBlock;
-    NodeIndex m_compileIndex;
+    Node* m_currentNode;
     unsigned m_indexInBlock;
-    Vector<NodeIndex, 16> m_replacements;
-    FixedArray<unsigned, LastNodeType> m_lastSeen;
+    std::array<unsigned, LastNodeType> m_lastSeen;
+    bool m_changed; // Only tracks changes that have a substantive effect on other optimizations.
 };
 
-void performCSE(Graph& graph)
+bool performCSE(Graph& graph)
+{
+    SamplingRegion samplingRegion("DFG CSE Phase");
+    return runPhase<CSEPhase<NormalCSE>>(graph);
+}
+
+bool performStoreElimination(Graph& graph)
 {
-    runPhase<CSEPhase>(graph);
+    SamplingRegion samplingRegion("DFG Store Elimination Phase");
+    return runPhase<CSEPhase<StoreElimination>>(graph);
 }
 
 } } // namespace JSC::DFG