]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - dfg/DFGCSEPhase.cpp
JavaScriptCore-7600.1.4.9.tar.gz
[apple/javascriptcore.git] / dfg / DFGCSEPhase.cpp
index 0eb29fcafa8ede4d94aa7aac32d0b62f9bb9a94c..d4e1023d048c99998e342cb6540cd51da61e89a2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011, 2012, 2013 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 "JSCellInlines.h"
+#include "JSCInlines.h"
+#include <array>
 #include <wtf/FastBitVector.h>
 
 namespace JSC { namespace DFG {
@@ -47,34 +51,68 @@ public:
     
     bool run()
     {
-        ASSERT((cseMode == NormalCSE) == (m_graph.m_fixpointState == FixpointNotConverged));
         ASSERT(m_graph.m_fixpointState != BeforeFixpoint);
         
         m_changed = false;
         
-        for (unsigned block = 0; block < m_graph.m_blocks.size(); ++block)
-            performBlockCSE(m_graph.m_blocks[block].get());
+        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:
     
-    Node* canonicalize(Node* node)
-    {
-        if (!node)
-            return 0;
-        
-        if (node->op() == ValueToInt32)
-            node = node->child1().node();
-        
-        return node;
-    }
-    Node* canonicalize(Edge edge)
-    {
-        return canonicalize(edge.node());
-    }
-    
     unsigned endIndexForPureCSE()
     {
         unsigned result = m_lastSeen[m_currentNode->op()];
@@ -83,17 +121,14 @@ private:
         else
             result++;
         ASSERT(result <= m_indexInBlock);
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLogF("  limit %u: ", result);
-#endif
         return result;
     }
 
     Node* pureCSE(Node* node)
     {
-        Node* child1 = canonicalize(node->child1());
-        Node* child2 = canonicalize(node->child2());
-        Node* child3 = canonicalize(node->child3());
+        Edge child1 = node->child1().sanitized();
+        Edge child2 = node->child2().sanitized();
+        Edge child3 = node->child3().sanitized();
         
         for (unsigned i = endIndexForPureCSE(); i--;) {
             Node* otherNode = m_currentBlock->at(i);
@@ -103,22 +138,19 @@ private:
             if (node->op() != otherNode->op())
                 continue;
             
-            if (node->arithNodeFlags() != otherNode->arithNodeFlags())
-                continue;
-            
-            Node* otherChild = canonicalize(otherNode->child1());
+            Edge otherChild = otherNode->child1().sanitized();
             if (!otherChild)
                 return otherNode;
             if (otherChild != child1)
                 continue;
             
-            otherChild = canonicalize(otherNode->child2());
+            otherChild = otherNode->child2().sanitized();
             if (!otherChild)
                 return otherNode;
             if (otherChild != child2)
                 continue;
             
-            otherChild = canonicalize(otherNode->child3());
+            otherChild = otherNode->child3().sanitized();
             if (!otherChild)
                 return otherNode;
             if (otherChild != child3)
@@ -129,33 +161,29 @@ private:
         return 0;
     }
     
-    Node* int32ToDoubleCSE(Node* node)
+    Node* constantCSE(Node* node)
     {
-        for (unsigned i = m_indexInBlock; i--;) {
+        for (unsigned i = endIndexForPureCSE(); i--;) {
             Node* otherNode = m_currentBlock->at(i);
-            if (otherNode == node->child1())
-                return 0;
-            switch (otherNode->op()) {
-            case Int32ToDouble:
-            case ForwardInt32ToDouble:
-                if (otherNode->child1() == node->child1())
-                    return otherNode;
-                break;
-            default:
-                break;
-            }
+            if (otherNode->op() != node->op())
+                continue;
+            
+            if (otherNode->constantNumber() != node->constantNumber())
+                continue;
+            
+            return otherNode;
         }
         return 0;
     }
     
-    Node* constantCSE(Node* node)
+    Node* weakConstantCSE(Node* node)
     {
         for (unsigned i = endIndexForPureCSE(); i--;) {
             Node* otherNode = m_currentBlock->at(i);
-            if (otherNode->op() != JSConstant)
+            if (otherNode->op() != WeakJSConstant)
                 continue;
             
-            if (otherNode->constantNumber() != node->constantNumber())
+            if (otherNode->weakConstant() != node->weakConstant())
                 continue;
             
             return otherNode;
@@ -163,14 +191,14 @@ private:
         return 0;
     }
     
-    Node* weakConstantCSE(Node* node)
+    Node* constantStoragePointerCSE(Node* node)
     {
         for (unsigned i = endIndexForPureCSE(); i--;) {
             Node* otherNode = m_currentBlock->at(i);
-            if (otherNode->op() != WeakJSConstant)
+            if (otherNode->op() != ConstantStoragePointer)
                 continue;
             
-            if (otherNode->weakConstant() != node->weakConstant())
+            if (otherNode->storagePointer() != node->storagePointer())
                 continue;
             
             return otherNode;
@@ -178,17 +206,13 @@ private:
         return 0;
     }
     
-    Node* getCalleeLoadElimination(InlineCallFrame* inlineCallFrame)
+    Node* getCalleeLoadElimination()
     {
         for (unsigned i = m_indexInBlock; i--;) {
             Node* node = m_currentBlock->at(i);
-            if (node->codeOrigin.inlineCallFrame != inlineCallFrame)
-                continue;
             switch (node->op()) {
             case GetCallee:
                 return node;
-            case SetCallee:
-                return node->child1().node();
             default:
                 break;
             }
@@ -206,6 +230,7 @@ private:
                     return node;
                 break;
                 
+            case PutByValDirect:
             case PutByVal:
                 if (!m_graph.byValIsPure(node))
                     return 0;
@@ -243,17 +268,17 @@ private:
         return 0;
     }
     
-    Node* scopedVarLoadElimination(Node* registers, unsigned varNumber)
+    Node* scopedVarLoadElimination(Node* registers, int varNumber)
     {
         for (unsigned i = m_indexInBlock; i--;) {
             Node* node = m_currentBlock->at(i);
             switch (node->op()) {
-            case GetScopedVar: {
+            case GetClosureVar: {
                 if (node->child1() == registers && node->varNumber() == varNumber)
                     return node;
                 break;
             } 
-            case PutScopedVar: {
+            case PutClosureVar: {
                 if (node->varNumber() != varNumber)
                     break;
                 if (node->child2() == registers)
@@ -276,22 +301,12 @@ private:
         return 0;
     }
     
-    bool globalVarWatchpointElimination(WriteBarrier<Unknown>* registerPointer)
+    bool varInjectionWatchpointElimination()
     {
         for (unsigned i = m_indexInBlock; i--;) {
             Node* node = m_currentBlock->at(i);
-            switch (node->op()) {
-            case GlobalVarWatchpoint:
-                if (node->registerPointer() == registerPointer)
-                    return true;
-                break;
-            case PutGlobalVar:
-                if (node->registerPointer() == registerPointer)
-                    return false;
-                break;
-            default:
-                break;
-            }
+            if (node->op() == VarInjectionWatchpoint)
+                return true;
             if (m_graph.clobbersWorld(node))
                 break;
         }
@@ -304,7 +319,6 @@ private:
             Node* node = m_currentBlock->at(i);
             switch (node->op()) {
             case PutGlobalVar:
-            case PutGlobalVarCheck:
                 if (node->registerPointer() == registerPointer)
                     return node;
                 break;
@@ -323,12 +337,12 @@ private:
         return 0;
     }
     
-    Node* scopedVarStoreElimination(Node* scope, Node* registers, unsigned varNumber)
+    Node* scopedVarStoreElimination(Node* scope, Node* registers, int varNumber)
     {
         for (unsigned i = m_indexInBlock; i--;) {
             Node* node = m_currentBlock->at(i);
             switch (node->op()) {
-            case PutScopedVar: {
+            case PutClosureVar: {
                 if (node->varNumber() != varNumber)
                     break;
                 if (node->child1() == scope && node->child2() == registers)
@@ -336,14 +350,15 @@ private:
                 return 0;
             }
                 
-            case GetScopedVar: {
+            case GetClosureVar: {
                 // Let's be conservative.
                 if (node->varNumber() == varNumber)
                     return 0;
                 break;
             }
                 
-            case GetLocal: {
+            case GetLocal:
+            case SetLocal: {
                 VariableAccessData* variableAccessData = node->variableAccessData();
                 if (variableAccessData->isCaptured()
                     && variableAccessData->local() == static_cast<VirtualRegister>(varNumber))
@@ -360,25 +375,34 @@ private:
         return 0;
     }
     
-    Node* getByValLoadElimination(Node* child1, Node* child2)
+    Node* getByValLoadElimination(Node* child1, Node* child2, ArrayMode arrayMode)
     {
         for (unsigned i = m_indexInBlock; i--;) {
             Node* node = m_currentBlock->at(i);
-            if (node == child1 || node == canonicalize(child2)
+            if (node == child1 || node == child2
                 break;
 
             switch (node->op()) {
             case GetByVal:
                 if (!m_graph.byValIsPure(node))
                     return 0;
-                if (node->child1() == child1 && canonicalize(node->child2()) == canonicalize(child2))
+                if (node->child1() == child1
+                    && node->child2() == child2
+                    && node->arrayMode().type() == arrayMode.type())
                     return node;
                 break;
+                    
+            case PutByValDirect:
             case PutByVal:
             case PutByValAlias: {
                 if (!m_graph.byValIsPure(node))
                     return 0;
-                if (m_graph.varArgChild(node, 0) == child1 && canonicalize(m_graph.varArgChild(node, 1)) == canonicalize(child2))
+                // 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
@@ -387,13 +411,6 @@ private:
                 // typed arrays!  An Int32Array can alias a Float64Array for example, and so on.
                 return 0;
             }
-            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;
             default:
                 if (m_graph.clobbersWorld(node))
                     return 0;
@@ -438,14 +455,12 @@ private:
 
             switch (node->op()) {
             case CheckStructure:
-            case ForwardCheckStructure:
                 if (node->child1() == child1
                     && structureSet.isSupersetOf(node->structureSet()))
                     return true;
                 break;
                 
             case StructureTransitionWatchpoint:
-            case ForwardStructureTransitionWatchpoint:
                 if (node->child1() == child1
                     && structureSet.contains(node->structure()))
                     return true;
@@ -463,6 +478,12 @@ 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 (m_graph.byValIsPure(node)) {
@@ -497,7 +518,6 @@ private:
 
             switch (node->op()) {
             case CheckStructure:
-            case ForwardCheckStructure:
                 if (node->child1() == child1
                     && node->structureSet().containsOnly(structure))
                     return true;
@@ -510,7 +530,13 @@ private:
             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 (m_graph.byValIsPure(node)) {
@@ -522,7 +548,6 @@ private:
                 return false;
                 
             case StructureTransitionWatchpoint:
-            case ForwardStructureTransitionWatchpoint:
                 if (node->structure() == structure && node->child1() == child1)
                     return true;
                 break;
@@ -550,7 +575,6 @@ private:
                 break;
             switch (node->op()) {
             case CheckStructure:
-            case ForwardCheckStructure:
                 return 0;
                 
             case PhantomPutStructure:
@@ -584,6 +608,14 @@ private:
             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:
@@ -596,36 +628,49 @@ private:
             }
             if (m_graph.clobbersWorld(node) || node->canExit())
                 return 0;
+            if (edgesUseStructure(m_graph, node))
+                return 0;
         }
         return 0;
     }
     
-    Node* getByOffsetLoadElimination(unsigned identifierNumber, Node* child1)
+    Node* getByOffsetLoadElimination(unsigned identifierNumber, Node* base)
     {
         for (unsigned i = m_indexInBlock; i--;) {
             Node* node = m_currentBlock->at(i);
-            if (node == child1)
+            if (node == base)
                 break;
 
             switch (node->op()) {
             case GetByOffset:
-                if (node->child1() == child1
+                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:
                 if (m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber == identifierNumber) {
-                    if (node->child1() == child1) // Must be same property storage.
+                    if (node->child2() == base) // Must be same property storage.
                         return node->child3().node();
                     return 0;
                 }
                 break;
                 
-            case PutStructure:
-                // Changing the structure cannot change the outcome of a property get.
+            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 (m_graph.byValIsPure(node)) {
@@ -666,6 +711,12 @@ private:
                 }
                 break;
                 
+            case MultiPutByOffset:
+                if (node->multiPutByOffsetData().identifierNumber == identifierNumber)
+                    return 0;
+                break;
+                
+            case PutByValDirect:
             case PutByVal:
             case PutByValAlias:
             case GetByVal:
@@ -711,13 +762,8 @@ private:
                 // storage, so we conservatively assume that it may change the storage
                 // pointer of any object, including ours.
                 return 0;
-                
-            case PutByOffset:
-            case PutStructure:
-                // Changing the structure or putting to the storage cannot
-                // change the property storage pointer.
-                break;
-                
+                    
+            case PutByValDirect:
             case PutByVal:
             case PutByValAlias:
                 if (m_graph.byValIsPure(node)) {
@@ -734,6 +780,11 @@ private:
                 // 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;
@@ -751,12 +802,6 @@ private:
                 break;
 
             switch (node->op()) {
-            case PutByOffset:
-            case PutStructure:
-                // Changing the structure or putting to the storage cannot
-                // change the property storage pointer.
-                break;
-                
             case CheckArray:
                 if (node->child1() == child1 && node->arrayMode() == arrayMode)
                     return true;
@@ -791,12 +836,29 @@ private:
                 break;
             }
 
-            case PutByOffset:
-            case PutStructure:
-                // Changing the structure or putting to the storage cannot
-                // change the property storage pointer.
+            default:
+                if (m_graph.clobbersWorld(node))
+                    return 0;
                 break;
-                
+            }
+        }
+        return 0;
+    }
+    
+    Node* getTypedArrayByteOffsetLoadElimination(Node* child1)
+    {
+        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;
@@ -806,20 +868,16 @@ private:
         return 0;
     }
     
-    Node* getMyScopeLoadElimination(InlineCallFrame* inlineCallFrame)
+    Node* getMyScopeLoadElimination()
     {
         for (unsigned i = m_indexInBlock; i--;) {
             Node* node = m_currentBlock->at(i);
-            if (node->codeOrigin.inlineCallFrame != inlineCallFrame)
-                continue;
             switch (node->op()) {
             case CreateActivation:
                 // This may cause us to return a different scope.
                 return 0;
             case GetMyScope:
                 return node;
-            case SetMyScope:
-                return node->child1().node();
             default:
                 break;
             }
@@ -855,7 +913,8 @@ private:
                 }
                 break;
                 
-            case PutScopedVar:
+            case GetClosureVar:
+            case PutClosureVar:
                 if (static_cast<VirtualRegister>(node->varNumber()) == local)
                     return 0;
                 break;
@@ -869,71 +928,59 @@ private:
         return 0;
     }
     
-    struct SetLocalStoreEliminationResult {
-        SetLocalStoreEliminationResult()
-            : mayBeAccessed(false)
-            , mayExit(false)
-            , mayClobberWorld(false)
-        {
-        }
-        
-        bool mayBeAccessed;
-        bool mayExit;
-        bool mayClobberWorld;
-    };
-    SetLocalStoreEliminationResult setLocalStoreElimination(
-        VirtualRegister local, Node* expectedNode)
+    bool uncapturedSetLocalStoreElimination(VirtualRegister local, Node* expectedNode)
     {
-        SetLocalStoreEliminationResult result;
         for (unsigned i = m_indexInBlock; i--;) {
             Node* node = m_currentBlock->at(i);
             switch (node->op()) {
             case GetLocal:
             case Flush:
                 if (node->local() == local)
-                    result.mayBeAccessed = true;
+                    return false;
                 break;
                 
             case GetLocalUnlinked:
                 if (node->unlinkedLocal() == local)
-                    result.mayBeAccessed = true;
+                    return false;
                 break;
                 
             case SetLocal: {
                 if (node->local() != local)
                     break;
                 if (node != expectedNode)
-                    result.mayBeAccessed = true;
-                return result;
+                    return false;
+                return true;
             }
                 
-            case GetScopedVar:
+            case GetClosureVar:
+            case PutClosureVar:
                 if (static_cast<VirtualRegister>(node->varNumber()) == local)
-                    result.mayBeAccessed = true;
+                    return false;
                 break;
                 
             case GetMyScope:
             case SkipTopScope:
-                if (m_graph.uncheckedActivationRegisterFor(node->codeOrigin) == local)
-                    result.mayBeAccessed = true;
+                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->codeOrigin) == local)
-                    result.mayBeAccessed = true;
+                if (m_graph.uncheckedArgumentsRegisterFor(node->origin.semantic) == local)
+                    return false;
                 break;
                 
             case GetMyArgumentByVal:
             case GetMyArgumentByValSafe:
-                result.mayBeAccessed = true;
-                break;
+                return false;
                 
             case GetByVal:
                 // If this is accessing arguments then it's potentially accessing locals.
                 if (node->arrayMode().type() == Array::Arguments)
-                    result.mayBeAccessed = true;
+                    return false;
                 break;
                 
             case CreateArguments:
@@ -943,19 +990,76 @@ private:
                 // 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.
-                result.mayBeAccessed = true;
-                break;
+                return false;
                 
             default:
                 break;
             }
-            result.mayExit |= node->canExit();
-            result.mayClobberWorld |= m_graph.clobbersWorld(node);
+            if (m_graph.clobbersWorld(node))
+                return false;
         }
         RELEASE_ASSERT_NOT_REACHED();
-        // Be safe in release mode.
-        result.mayBeAccessed = true;
-        return result;
+        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)
@@ -970,9 +1074,6 @@ private:
             if (edge->flags() & NodeRelevantToOSR)
                 continue;
             
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-            dataLog("   Eliminating edge @", m_currentNode->index(), " -> @", edge->index());
-#endif
             node->children.removeEdge(i--);
             m_changed = true;
         }
@@ -983,15 +1084,20 @@ private:
         if (!replacement)
             return false;
         
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLogF("   Replacing @%u -> @%u", m_currentNode->index(), replacement->index());
-#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();
+        }
         
         m_currentNode->convertToPhantom();
         eliminateIrrelevantPhantomChildren(m_currentNode);
         
         // At this point we will eliminate all references to this node.
-        m_currentNode->replacement = replacement;
+        m_currentNode->misc.replacement = replacement;
         
         m_changed = true;
         
@@ -1000,10 +1106,6 @@ private:
     
     void eliminate()
     {
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLogF("   Eliminating @%u", m_currentNode->index());
-#endif
-        
         ASSERT(m_currentNode->mustGenerate());
         m_currentNode->convertToPhantom();
         eliminateIrrelevantPhantomChildren(m_currentNode);
@@ -1016,7 +1118,7 @@ private:
         if (!node)
             return;
         ASSERT(node->mustGenerate());
-        node->setOpAndDefaultNonExitFlags(phantomType);
+        node->setOpAndDefaultFlags(phantomType);
         if (phantomType == Phantom)
             eliminateIrrelevantPhantomChildren(node);
         
@@ -1028,22 +1130,6 @@ private:
         if (cseMode == NormalCSE)
             m_graph.performSubstitution(node);
         
-        if (node->op() == SetLocal)
-            node->child1()->mergeFlags(NodeRelevantToOSR);
-        
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLogF("   %s @%u: ", Graph::opName(node->op()), node->index());
-#endif
-        
-        // NOTE: there are some nodes that we deliberately don't CSE even though we
-        // probably could, like MakeRope 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 MakeRope is trivially CSE-able. It's not trivially doable for
-        // ToPrimitive, but we could change that with some speculations if we really
-        // needed to.
-        
         switch (node->op()) {
         
         case Identity:
@@ -1059,17 +1145,13 @@ private:
         case BitRShift:
         case BitLShift:
         case BitURShift:
-        case ArithAdd:
-        case ArithSub:
-        case ArithNegate:
-        case ArithMul:
-        case ArithIMul:
-        case ArithMod:
-        case ArithDiv:
         case ArithAbs:
         case ArithMin:
         case ArithMax:
         case ArithSqrt:
+        case ArithFRound:
+        case ArithSin:
+        case ArithCos:
         case StringCharAt:
         case StringCharCodeAt:
         case IsUndefined:
@@ -1078,31 +1160,50 @@ private:
         case IsString:
         case IsObject:
         case IsFunction:
-        case DoubleAsInt32:
         case LogicalNot:
         case SkipTopScope:
         case SkipScope:
-        case GetScopeRegisters:
+        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 Int32ToDouble:
-        case ForwardInt32ToDouble:
+        case ArithAdd:
+        case ArithSub:
+        case ArithNegate:
+        case ArithMul:
+        case ArithDiv:
+        case ArithMod:
+        case UInt32ToNumber:
+        case DoubleAsInt32: {
             if (cseMode == StoreElimination)
                 break;
-            setReplacement(int32ToDoubleCSE(node));
+            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(node->codeOrigin.inlineCallFrame));
+            setReplacement(getCalleeLoadElimination());
             break;
 
         case GetLocal: {
@@ -1142,8 +1243,14 @@ private:
         }
             
         case Flush: {
+            ASSERT(m_graph.m_form != SSA);
             VariableAccessData* variableAccessData = node->variableAccessData();
-            VirtualRegister local = variableAccessData->local();
+            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;
@@ -1153,40 +1260,29 @@ private:
             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.
-                if (variableAccessData->isCaptured()) {
-                    // Captured SetLocals never speculate and hence never exit.
-                } else {
-                    if (variableAccessData->shouldUseDoubleFormat())
-                        break;
-                    SpeculatedType prediction = variableAccessData->argumentAwarePrediction();
-                    if (isInt32Speculation(prediction))
-                        break;
-                    if (isArraySpeculation(prediction))
-                        break;
-                    if (isBooleanSpeculation(prediction))
-                        break;
-                }
+                FlushFormat format = variableAccessData->flushFormat();
+                ASSERT(format != DeadFlush);
+                if (format != FlushedJSValue)
+                    break;
             } else {
                 if (replacement->canExit())
                     break;
             }
-            SetLocalStoreEliminationResult result =
-                setLocalStoreElimination(local, replacement);
-            if (result.mayBeAccessed || result.mayClobberWorld)
+            if (!setLocalStoreElimination(variableAccessData, replacement))
                 break;
             ASSERT(replacement->op() == SetLocal);
-            // FIXME: Investigate using mayExit as a further optimization.
             node->convertToPhantom();
             Node* dataNode = replacement->child1().node();
             ASSERT(dataNode->hasResult());
-            m_graph.clearAndDerefChild1(node);
-            node->children.child1() = Edge(dataNode);
+            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,
@@ -1201,6 +1297,12 @@ private:
             setReplacement(weakConstantCSE(node));
             break;
             
+        case ConstantStoragePointer:
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(constantStoragePointerCSE(node));
+            break;
+            
         case GetArrayLength:
             if (cseMode == StoreElimination)
                 break;
@@ -1210,12 +1312,11 @@ private:
         case GetMyScope:
             if (cseMode == StoreElimination)
                 break;
-            setReplacement(getMyScopeLoadElimination(node->codeOrigin.inlineCallFrame));
+            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:
@@ -1239,28 +1340,27 @@ private:
             setReplacement(globalVarLoadElimination(node->registerPointer()));
             break;
 
-        case GetScopedVar: {
+        case GetClosureVar: {
             if (cseMode == StoreElimination)
                 break;
             setReplacement(scopedVarLoadElimination(node->child1().node(), node->varNumber()));
             break;
         }
 
-        case GlobalVarWatchpoint:
+        case VarInjectionWatchpoint:
             if (cseMode == StoreElimination)
                 break;
-            if (globalVarWatchpointElimination(node->registerPointer()))
+            if (varInjectionWatchpointElimination())
                 eliminate();
             break;
             
         case PutGlobalVar:
-        case PutGlobalVarCheck:
             if (cseMode == NormalCSE)
                 break;
             eliminate(globalVarStoreElimination(node->registerPointer()));
             break;
             
-        case PutScopedVar: {
+        case PutClosureVar: {
             if (cseMode == NormalCSE)
                 break;
             eliminate(scopedVarStoreElimination(node->child1().node(), node->child2().node(), node->varNumber()));
@@ -1271,16 +1371,17 @@ private:
             if (cseMode == StoreElimination)
                 break;
             if (m_graph.byValIsPure(node))
-                setReplacement(getByValLoadElimination(node->child1().node(), node->child2().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* replacement = getByValLoadElimination(child1.node(), child2.node(), node->arrayMode());
                 if (!replacement)
                     break;
                 node->setOp(PutByValAlias);
@@ -1289,7 +1390,6 @@ private:
         }
             
         case CheckStructure:
-        case ForwardCheckStructure:
             if (cseMode == StoreElimination)
                 break;
             if (checkStructureElimination(node->structureSet(), node->child1().node()))
@@ -1297,7 +1397,6 @@ private:
             break;
             
         case StructureTransitionWatchpoint:
-        case ForwardStructureTransitionWatchpoint:
             if (cseMode == StoreElimination)
                 break;
             if (structureTransitionWatchpointElimination(node->structure(), node->child1().node()))
@@ -1337,6 +1436,13 @@ private:
             setReplacement(getIndexedPropertyStorageLoadElimination(node->child1().node(), node->arrayMode()));
             break;
         }
+            
+        case GetTypedArrayByteOffset: {
+            if (cseMode == StoreElimination)
+                break;
+            setReplacement(getTypedArrayByteOffsetLoadElimination(node->child1().node()));
+            break;
+        }
 
         case GetButterfly:
             if (cseMode == StoreElimination)
@@ -1347,7 +1453,13 @@ private:
         case GetByOffset:
             if (cseMode == StoreElimination)
                 break;
-            setReplacement(getByOffsetLoadElimination(m_graph.m_storageAccessData[node->storageAccessDataIndex()].identifierNumber, node->child1().node()));
+            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:
@@ -1356,9 +1468,16 @@ private:
             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;
             
@@ -1368,9 +1487,6 @@ private:
         }
         
         m_lastSeen[node->op()] = m_indexInBlock;
-#if DFG_ENABLE(DEBUG_PROPAGATION_VERBOSE)
-        dataLogF("\n");
-#endif
     }
     
     void performBlockCSE(BasicBlock* block)
@@ -1384,32 +1500,6 @@ private:
         for (unsigned i = 0; i < LastNodeType; ++i)
             m_lastSeen[i] = UINT_MAX;
         
-        // All Phis need to already be marked as relevant to OSR, and have their
-        // replacements cleared, so we don't get confused while doing substitutions on
-        // GetLocal's.
-        for (unsigned i = 0; i < block->phis.size(); ++i) {
-            ASSERT(block->phis[i]->flags() & NodeRelevantToOSR);
-            block->phis[i]->replacement = 0;
-        }
-        
-        // Make all of my SetLocal and GetLocal nodes relevant to OSR, and do some other
-        // necessary bookkeeping.
-        for (unsigned i = 0; i < block->size(); ++i) {
-            Node* node = block->at(i);
-            
-            node->replacement = 0;
-            
-            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 (m_indexInBlock = 0; m_indexInBlock < block->size(); ++m_indexInBlock) {
             m_currentNode = block->at(m_indexInBlock);
             performNodeCSE(m_currentNode);
@@ -1418,27 +1508,27 @@ private:
         if (!ASSERT_DISABLED && cseMode == StoreElimination) {
             // Nobody should have replacements set.
             for (unsigned i = 0; i < block->size(); ++i)
-                ASSERT(!block->at(i)->replacement);
+                ASSERT(!block->at(i)->misc.replacement);
         }
     }
     
     BasicBlock* m_currentBlock;
     Node* m_currentNode;
     unsigned m_indexInBlock;
-    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.
 };
 
 bool performCSE(Graph& graph)
 {
     SamplingRegion samplingRegion("DFG CSE Phase");
-    return runPhase<CSEPhase<NormalCSE> >(graph);
+    return runPhase<CSEPhase<NormalCSE>>(graph);
 }
 
 bool performStoreElimination(Graph& graph)
 {
     SamplingRegion samplingRegion("DFG Store Elimination Phase");
-    return runPhase<CSEPhase<StoreElimination> >(graph);
+    return runPhase<CSEPhase<StoreElimination>>(graph);
 }
 
 } } // namespace JSC::DFG