Unreviewed, rolling out r215476.
[WebKit-https.git] / Source / JavaScriptCore / dfg / DFGNode.h
index 1aa1516..0d98caf 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011-2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2011-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -23,8 +23,7 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#ifndef DFGNode_h
-#define DFGNode_h
+#pragma once
 
 #if ENABLE(DFG_JIT)
 
@@ -42,6 +41,9 @@
 #include "DFGNodeOrigin.h"
 #include "DFGNodeType.h"
 #include "DFGObjectMaterializationData.h"
+#include "DFGOpInfo.h"
+#include "DFGRegisteredStructure.h"
+#include "DFGRegisteredStructureSet.h"
 #include "DFGTransition.h"
 #include "DFGUseKind.h"
 #include "DFGVariableAccessData.h"
 #include "StructureSet.h"
 #include "TypeLocation.h"
 #include "ValueProfile.h"
+#include <type_traits>
 #include <wtf/ListDump.h>
+#include <wtf/LoggingHashSet.h>
 
-namespace JSC { namespace DFG {
+namespace JSC {
+
+namespace DOMJIT {
+class GetterSetter;
+class Patchpoint;
+class CallDOMGetterPatchpoint;
+class Signature;
+}
+
+namespace Profiler {
+class ExecutionCounter;
+}
+
+namespace DFG {
 
 class Graph;
 class PromotedLocationDescriptor;
@@ -219,24 +236,19 @@ struct StackAccessData {
     FlushedAt flushedAt() { return FlushedAt(format, machineLocal); }
 };
 
-// This type used in passing an immediate argument to Node constructor;
-// distinguishes an immediate value (typically an index into a CodeBlock data structure - 
-// a constant index, argument, or identifier) from a Node*.
-struct OpInfo {
-    OpInfo() : m_value(0) { }
-    explicit OpInfo(int32_t value) : m_value(static_cast<uintptr_t>(value)) { }
-    explicit OpInfo(uint32_t value) : m_value(static_cast<uintptr_t>(value)) { }
-#if OS(DARWIN) || USE(JSVALUE64)
-    explicit OpInfo(size_t value) : m_value(static_cast<uintptr_t>(value)) { }
-#endif
-    explicit OpInfo(void* value) : m_value(reinterpret_cast<uintptr_t>(value)) { }
-    uintptr_t m_value;
+struct CallDOMGetterData {
+    DOMJIT::GetterSetter* domJIT { nullptr };
+    DOMJIT::CallDOMGetterPatchpoint* patchpoint { nullptr };
+    unsigned identifierNumber { 0 };
 };
 
 // === Node ===
 //
 // Node represents a single operation in the data flow graph.
 struct Node {
+public:
+    static const char HashSetTemplateInstantiationString[];
+    
     enum VarArgTag { VarArg };
     
     Node() { }
@@ -260,8 +272,6 @@ struct Node {
         , m_virtualRegister(VirtualRegister())
         , m_refCount(1)
         , m_prediction(SpecNone)
-        , m_opInfo(0)
-        , m_opInfo2(0)
         , owner(nullptr)
     {
         m_misc.replacement = nullptr;
@@ -276,8 +286,6 @@ struct Node {
         , m_virtualRegister(VirtualRegister())
         , m_refCount(1)
         , m_prediction(SpecNone)
-        , m_opInfo(0)
-        , m_opInfo2(0)
         , owner(nullptr)
     {
         m_misc.replacement = nullptr;
@@ -294,7 +302,6 @@ struct Node {
         , m_refCount(1)
         , m_prediction(SpecNone)
         , m_opInfo(imm.m_value)
-        , m_opInfo2(0)
         , owner(nullptr)
     {
         m_misc.replacement = nullptr;
@@ -310,7 +317,6 @@ struct Node {
         , m_refCount(1)
         , m_prediction(SpecNone)
         , m_opInfo(imm.m_value)
-        , m_opInfo2(0)
         , owner(nullptr)
     {
         m_misc.replacement = nullptr;
@@ -353,9 +359,8 @@ struct Node {
     
     NodeType op() const { return static_cast<NodeType>(m_op); }
     NodeFlags flags() const { return m_flags; }
-    
-    // This is not a fast method.
-    unsigned index() const;
+
+    unsigned index() const { return m_index; }
     
     void setOp(NodeType op)
     {
@@ -410,10 +415,10 @@ struct Node {
 
     void remove();
 
-    void convertToCheckStructure(StructureSet* set)
+    void convertToCheckStructure(RegisteredStructureSet* set)
     {
         setOpAndDefaultFlags(CheckStructure);
-        m_opInfo = bitwise_cast<uintptr_t>(set); 
+        m_opInfo = set;
     }
 
     void convertToCheckStructureImmediate(Node* structure)
@@ -478,7 +483,7 @@ struct Node {
             return FrozenValue::emptySingleton();
         }
         
-        return bitwise_cast<FrozenValue*>(m_opInfo);
+        return m_opInfo.as<FrozenValue*>();
     }
     
     // Don't call this directly - use Graph::convertToConstant() instead!
@@ -491,15 +496,17 @@ struct Node {
         else
             m_op = JSConstant;
         m_flags &= ~NodeMustGenerate;
-        m_opInfo = bitwise_cast<uintptr_t>(value);
+        m_opInfo = value;
         children.reset();
     }
+
+    void convertToLazyJSConstant(Graph&, LazyJSValue);
     
     void convertToConstantStoragePointer(void* pointer)
     {
         ASSERT(op() == GetIndexedPropertyStorage);
         m_op = ConstantStoragePointer;
-        m_opInfo = bitwise_cast<uintptr_t>(pointer);
+        m_opInfo = pointer;
         children.reset();
     }
     
@@ -516,26 +523,25 @@ struct Node {
     {
         m_op = PutStack;
         m_flags |= NodeMustGenerate;
-        m_opInfo = bitwise_cast<uintptr_t>(data);
-        m_opInfo2 = 0;
+        m_opInfo = data;
+        m_opInfo2 = OpInfoWrapper();
     }
     
     void convertToGetStack(StackAccessData* data)
     {
         m_op = GetStack;
         m_flags &= ~NodeMustGenerate;
-        m_opInfo = bitwise_cast<uintptr_t>(data);
-        m_opInfo2 = 0;
+        m_opInfo = data;
+        m_opInfo2 = OpInfoWrapper();
         children.reset();
     }
     
-    void convertToGetByOffset(StorageAccessData& data, Edge storage)
+    void convertToGetByOffset(StorageAccessData& data, Edge storage, Edge base)
     {
         ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == MultiGetByOffset);
-        m_opInfo = bitwise_cast<uintptr_t>(&data);
-        children.setChild2(children.child1());
-        children.child2().setUseKind(KnownCellUse);
+        m_opInfo = &data;
         children.setChild1(storage);
+        children.setChild2(base);
         m_op = GetByOffset;
         m_flags &= ~NodeMustGenerate;
     }
@@ -543,18 +549,18 @@ struct Node {
     void convertToMultiGetByOffset(MultiGetByOffsetData* data)
     {
         ASSERT(m_op == GetById || m_op == GetByIdFlush);
-        m_opInfo = bitwise_cast<intptr_t>(data);
+        m_opInfo = data;
         child1().setUseKind(CellUse);
         m_op = MultiGetByOffset;
         ASSERT(m_flags & NodeMustGenerate);
     }
     
-    void convertToPutByOffset(StorageAccessData& data, Edge storage)
+    void convertToPutByOffset(StorageAccessData& data, Edge storage, Edge base)
     {
         ASSERT(m_op == PutById || m_op == PutByIdDirect || m_op == PutByIdFlush || m_op == MultiPutByOffset);
-        m_opInfo = bitwise_cast<uintptr_t>(&data);
+        m_opInfo = &data;
         children.setChild3(children.child2());
-        children.setChild2(children.child1());
+        children.setChild2(base);
         children.setChild1(storage);
         m_op = PutByOffset;
     }
@@ -562,7 +568,7 @@ struct Node {
     void convertToMultiPutByOffset(MultiPutByOffsetData* data)
     {
         ASSERT(m_op == PutById || m_op == PutByIdDirect || m_op == PutByIdFlush);
-        m_opInfo = bitwise_cast<intptr_t>(data);
+        m_opInfo = data;
         m_op = MultiPutByOffset;
     }
     
@@ -578,18 +584,38 @@ struct Node {
         m_op = PhantomNewObject;
         m_flags &= ~NodeHasVarArgs;
         m_flags |= NodeMustGenerate;
-        m_opInfo = 0;
-        m_opInfo2 = 0;
+        m_opInfo = OpInfoWrapper();
+        m_opInfo2 = OpInfoWrapper();
         children = AdjacencyList();
     }
 
     void convertToPhantomNewFunction()
     {
-        ASSERT(m_op == NewFunction || m_op == NewArrowFunction);
+        ASSERT(m_op == NewFunction || m_op == NewGeneratorFunction || m_op == NewAsyncFunction);
         m_op = PhantomNewFunction;
         m_flags |= NodeMustGenerate;
-        m_opInfo = 0;
-        m_opInfo2 = 0;
+        m_opInfo = OpInfoWrapper();
+        m_opInfo2 = OpInfoWrapper();
+        children = AdjacencyList();
+    }
+
+    void convertToPhantomNewGeneratorFunction()
+    {
+        ASSERT(m_op == NewGeneratorFunction);
+        m_op = PhantomNewGeneratorFunction;
+        m_flags |= NodeMustGenerate;
+        m_opInfo = OpInfoWrapper();
+        m_opInfo2 = OpInfoWrapper();
+        children = AdjacencyList();
+    }
+
+    void convertToPhantomNewAsyncFunction()
+    {
+        ASSERT(m_op == NewAsyncFunction);
+        m_op = PhantomNewAsyncFunction;
+        m_flags |= NodeMustGenerate;
+        m_opInfo = OpInfoWrapper();
+        m_opInfo2 = OpInfoWrapper();
         children = AdjacencyList();
     }
 
@@ -599,8 +625,8 @@ struct Node {
         m_op = PhantomCreateActivation;
         m_flags &= ~NodeHasVarArgs;
         m_flags |= NodeMustGenerate;
-        m_opInfo = 0;
-        m_opInfo2 = 0;
+        m_opInfo = OpInfoWrapper();
+        m_opInfo2 = OpInfoWrapper();
         children = AdjacencyList();
     }
 
@@ -623,8 +649,8 @@ struct Node {
     {
         ASSERT(m_op == GetLocalUnlinked);
         m_op = GetLocal;
-        m_opInfo = bitwise_cast<uintptr_t>(variable);
-        m_opInfo2 = 0;
+        m_opInfo = variable;
+        m_opInfo2 = OpInfoWrapper();
         children.setChild1(Edge(phi));
     }
     
@@ -634,13 +660,16 @@ struct Node {
         m_op = ToString;
     }
 
-    void convertToArithSqrt()
+    void convertToArithNegate()
     {
-        ASSERT(m_op == ArithPow);
-        child2() = Edge();
-        m_op = ArithSqrt;
+        ASSERT(m_op == ArithAbs && child1().useKind() == Int32Use);
+        m_op = ArithNegate;
     }
     
+    void convertToDirectCall(FrozenValue*);
+
+    void convertToCallDOM(Graph&);
+    
     JSValue asJSValue()
     {
         return constant()->value();
@@ -676,14 +705,14 @@ struct Node {
         return asJSValue().asNumber();
     }
      
-    bool isMachineIntConstant()
+    bool isAnyIntConstant()
     {
-        return isConstant() && constant()->value().isMachineInt();
+        return isConstant() && constant()->value().isAnyInt();
     }
      
-    int64_t asMachineInt()
+    int64_t asAnyInt()
     {
-        return asJSValue().asMachineInt();
+        return asJSValue().asAnyInt();
     }
      
     bool isBooleanConstant()
@@ -712,27 +741,84 @@ struct Node {
     }
      
     template<typename T>
-    T dynamicCastConstant()
+    T dynamicCastConstant(VM& vm)
     {
         if (!isCellConstant())
             return nullptr;
-        return jsDynamicCast<T>(asCell());
+        return jsDynamicCast<T>(vm, asCell());
     }
     
     template<typename T>
-    T castConstant()
+    T castConstant(VM& vm)
     {
-        T result = dynamicCastConstant<T>();
+        T result = dynamicCastConstant<T>(vm);
         RELEASE_ASSERT(result);
         return result;
     }
 
+    bool hasLazyJSValue()
+    {
+        return op() == LazyJSConstant;
+    }
+
+    LazyJSValue lazyJSValue()
+    {
+        ASSERT(hasLazyJSValue());
+        return *m_opInfo.as<LazyJSValue*>();
+    }
+
+    String tryGetString(Graph&);
+
     JSValue initializationValueForActivation() const
     {
         ASSERT(op() == CreateActivation);
-        return bitwise_cast<FrozenValue*>(m_opInfo2)->value();
+        return m_opInfo2.as<FrozenValue*>()->value();
     }
-     
+
+    bool hasArgumentsChild()
+    {
+        switch (op()) {
+        case GetMyArgumentByVal:
+        case GetMyArgumentByValOutOfBounds:
+        case LoadVarargs:
+        case ForwardVarargs:
+        case CallVarargs:
+        case CallForwardVarargs:
+        case ConstructVarargs:
+        case ConstructForwardVarargs:
+        case TailCallVarargs:
+        case TailCallForwardVarargs:
+        case TailCallVarargsInlinedCaller:
+        case TailCallForwardVarargsInlinedCaller:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    Edge& argumentsChild()
+    {
+        switch (op()) {
+        case GetMyArgumentByVal:
+        case GetMyArgumentByValOutOfBounds:
+        case LoadVarargs:
+        case ForwardVarargs:
+            return child1();
+        case CallVarargs:
+        case CallForwardVarargs:
+        case ConstructVarargs:
+        case ConstructForwardVarargs:
+        case TailCallVarargs:
+        case TailCallForwardVarargs:
+        case TailCallVarargsInlinedCaller:
+        case TailCallForwardVarargsInlinedCaller:
+            return child3();
+        default:
+            RELEASE_ASSERT_NOT_REACHED();
+            return child1();
+        }
+    }
+
     bool containsMovHint()
     {
         switch (op()) {
@@ -745,7 +831,7 @@ struct Node {
     }
     
     bool hasVariableAccessData(Graph&);
-    bool hasLocal(Graph& graph)
+    bool accessesStack(Graph& graph)
     {
         return hasVariableAccessData(graph);
     }
@@ -754,7 +840,7 @@ struct Node {
     // access data doesn't have one because it hasn't been initialized yet.
     VariableAccessData* tryGetVariableAccessData()
     {
-        VariableAccessData* result = reinterpret_cast<VariableAccessData*>(m_opInfo);
+        VariableAccessData* result = m_opInfo.as<VariableAccessData*>();
         if (!result)
             return 0;
         return result->find();
@@ -762,7 +848,7 @@ struct Node {
     
     VariableAccessData* variableAccessData()
     {
-        return reinterpret_cast<VariableAccessData*>(m_opInfo)->find();
+        return m_opInfo.as<VariableAccessData*>()->find();
     }
     
     VirtualRegister local()
@@ -792,7 +878,7 @@ struct Node {
     VirtualRegister unlinkedLocal()
     {
         ASSERT(hasUnlinkedLocal());
-        return static_cast<VirtualRegister>(m_opInfo);
+        return VirtualRegister(m_opInfo.as<int32_t>());
     }
     
     bool hasUnlinkedMachineLocal()
@@ -809,7 +895,7 @@ struct Node {
     VirtualRegister unlinkedMachineLocal()
     {
         ASSERT(hasUnlinkedMachineLocal());
-        return VirtualRegister(m_opInfo2);
+        return VirtualRegister(m_opInfo2.as<int32_t>());
     }
     
     bool hasStackAccessData()
@@ -826,7 +912,7 @@ struct Node {
     StackAccessData* stackAccessData()
     {
         ASSERT(hasStackAccessData());
-        return bitwise_cast<StackAccessData*>(m_opInfo);
+        return m_opInfo.as<StackAccessData*>();
     }
     
     bool hasPhi()
@@ -837,25 +923,32 @@ struct Node {
     Node* phi()
     {
         ASSERT(hasPhi());
-        return bitwise_cast<Node*>(m_opInfo);
+        return m_opInfo.as<Node*>();
     }
 
     bool isStoreBarrier()
     {
-        return op() == StoreBarrier;
+        return op() == StoreBarrier || op() == FencedStoreBarrier;
     }
 
     bool hasIdentifier()
     {
         switch (op()) {
+        case TryGetById:
         case GetById:
         case GetByIdFlush:
+        case GetByIdWithThis:
         case PutById:
         case PutByIdFlush:
         case PutByIdDirect:
+        case PutByIdWithThis:
         case PutGetterById:
         case PutSetterById:
         case PutGetterSetterById:
+        case DeleteById:
+        case GetDynamicVar:
+        case PutDynamicVar:
+        case ResolveScope:
             return true;
         default:
             return false;
@@ -865,7 +958,24 @@ struct Node {
     unsigned identifierNumber()
     {
         ASSERT(hasIdentifier());
-        return m_opInfo;
+        return m_opInfo.as<unsigned>();
+    }
+
+    bool hasGetPutInfo()
+    {
+        switch (op()) {
+        case GetDynamicVar:
+        case PutDynamicVar:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    unsigned getPutInfo()
+    {
+        ASSERT(hasGetPutInfo());
+        return m_opInfo2.as<unsigned>();
     }
 
     bool hasAccessorAttributes()
@@ -889,10 +999,10 @@ struct Node {
         case PutGetterById:
         case PutSetterById:
         case PutGetterSetterById:
-            return m_opInfo2;
+            return m_opInfo2.as<int32_t>();
         case PutGetterByVal:
         case PutSetterByVal:
-            return m_opInfo;
+            return m_opInfo.as<int32_t>();
         default:
             RELEASE_ASSERT_NOT_REACHED();
             return 0;
@@ -912,11 +1022,26 @@ struct Node {
     NodeFlags arithNodeFlags()
     {
         NodeFlags result = m_flags & NodeArithFlagsMask;
-        if (op() == ArithMul || op() == ArithDiv || op() == ArithMod || op() == ArithNegate || op() == ArithPow || op() == ArithRound || op() == DoubleAsInt32)
+        if (op() == ArithMul || op() == ArithDiv || op() == ArithMod || op() == ArithNegate || op() == ArithPow || op() == ArithRound || op() == ArithFloor || op() == ArithCeil || op() == ArithTrunc || op() == DoubleAsInt32)
             return result;
         return result & ~NodeBytecodeNeedsNegZero;
     }
+
+    bool mayHaveNonIntResult()
+    {
+        return m_flags & NodeMayHaveNonIntResult;
+    }
+    
+    bool mayHaveDoubleResult()
+    {
+        return m_flags & NodeMayHaveDoubleResult;
+    }
     
+    bool mayHaveNonNumberResult()
+    {
+        return m_flags & NodeMayHaveNonNumberResult;
+    }
+
     bool hasConstantBuffer()
     {
         return op() == NewArrayBuffer;
@@ -925,7 +1050,7 @@ struct Node {
     NewArrayBufferData* newArrayBufferData()
     {
         ASSERT(hasConstantBuffer());
-        return reinterpret_cast<NewArrayBufferData*>(m_opInfo);
+        return m_opInfo.as<NewArrayBufferData*>();
     }
     
     unsigned startConstant()
@@ -949,13 +1074,27 @@ struct Node {
             return false;
         }
     }
-    
+
+    BitVector* bitVector()
+    {
+        ASSERT(op() == NewArrayWithSpread || op() == PhantomNewArrayWithSpread);
+        return m_opInfo.as<BitVector*>();
+    }
+
+    // Return the indexing type that an array allocation *wants* to use. It may end up using a different
+    // type if we're having a bad time. You can determine the actual indexing type by asking the global
+    // object:
+    //
+    //     m_graph.globalObjectFor(node->origin.semantic)->arrayStructureForIndexingTypeDuringAllocation(node->indexingType())
+    //
+    // This will give you a Structure*, and that will have some indexing type that may be different from
+    // the this one.
     IndexingType indexingType()
     {
         ASSERT(hasIndexingType());
         if (op() == NewArrayBuffer)
             return newArrayBufferData()->indexingType;
-        return m_opInfo;
+        return static_cast<IndexingType>(m_opInfo.as<uint32_t>());
     }
     
     bool hasTypedArrayType()
@@ -971,7 +1110,7 @@ struct Node {
     TypedArrayType typedArrayType()
     {
         ASSERT(hasTypedArrayType());
-        TypedArrayType result = static_cast<TypedArrayType>(m_opInfo);
+        TypedArrayType result = static_cast<TypedArrayType>(m_opInfo.as<uint32_t>());
         ASSERT(isTypedView(result));
         return result;
     }
@@ -984,7 +1123,7 @@ struct Node {
     unsigned inlineCapacity()
     {
         ASSERT(hasInlineCapacity());
-        return m_opInfo;
+        return m_opInfo.as<unsigned>();
     }
 
     void setIndexingType(IndexingType indexingType)
@@ -993,17 +1132,6 @@ struct Node {
         m_opInfo = indexingType;
     }
     
-    bool hasRegexpIndex()
-    {
-        return op() == NewRegexp;
-    }
-    
-    unsigned regexpIndex()
-    {
-        ASSERT(hasRegexpIndex());
-        return m_opInfo;
-    }
-    
     bool hasScopeOffset()
     {
         return op() == GetClosureVar || op() == PutClosureVar;
@@ -1012,7 +1140,7 @@ struct Node {
     ScopeOffset scopeOffset()
     {
         ASSERT(hasScopeOffset());
-        return ScopeOffset(m_opInfo);
+        return ScopeOffset(m_opInfo.as<uint32_t>());
     }
     
     bool hasDirectArgumentsOffset()
@@ -1023,7 +1151,7 @@ struct Node {
     DirectArgumentsOffset capturedArgumentsOffset()
     {
         ASSERT(hasDirectArgumentsOffset());
-        return DirectArgumentsOffset(m_opInfo);
+        return DirectArgumentsOffset(m_opInfo.as<uint32_t>());
     }
     
     bool hasRegisterPointer()
@@ -1033,7 +1161,7 @@ struct Node {
     
     WriteBarrier<Unknown>* variablePointer()
     {
-        return bitwise_cast<WriteBarrier<Unknown>*>(m_opInfo);
+        return m_opInfo.as<WriteBarrier<Unknown>*>();
     }
     
     bool hasCallVarargsData()
@@ -1056,7 +1184,7 @@ struct Node {
     CallVarargsData* callVarargsData()
     {
         ASSERT(hasCallVarargsData());
-        return bitwise_cast<CallVarargsData*>(m_opInfo);
+        return m_opInfo.as<CallVarargsData*>();
     }
     
     bool hasLoadVarargsData()
@@ -1067,17 +1195,33 @@ struct Node {
     LoadVarargsData* loadVarargsData()
     {
         ASSERT(hasLoadVarargsData());
-        return bitwise_cast<LoadVarargsData*>(m_opInfo);
+        return m_opInfo.as<LoadVarargsData*>();
     }
-    
-    bool hasResult()
+
+    bool hasQueriedType()
     {
-        return !!result();
+        return op() == IsCellWithType;
+    }
+
+    JSType queriedType()
+    {
+        static_assert(std::is_same<uint8_t, std::underlying_type<JSType>::type>::value, "Ensure that uint8_t is the underlying type for JSType.");
+        return static_cast<JSType>(m_opInfo.as<uint32_t>());
     }
 
-    bool hasInt32Result()
+    bool hasSpeculatedTypeForQuery()
     {
-        return result() == NodeResultInt32;
+        return op() == IsCellWithType;
+    }
+
+    SpeculatedType speculatedTypeForQuery()
+    {
+        return speculationFromJSType(queriedType());
+    }
+    
+    bool hasResult()
+    {
+        return !!result();
     }
     
     bool hasInt52Result()
@@ -1143,6 +1287,7 @@ struct Node {
         case Switch:
         case Return:
         case TailCall:
+        case DirectTailCall:
         case TailCallVarargs:
         case TailCallForwardVarargs:
         case Unreachable:
@@ -1163,25 +1308,25 @@ struct Node {
     unsigned targetBytecodeOffsetDuringParsing()
     {
         ASSERT(isJump());
-        return m_opInfo;
+        return m_opInfo.as<unsigned>();
     }
 
     BasicBlock*& targetBlock()
     {
         ASSERT(isJump());
-        return *bitwise_cast<BasicBlock**>(&m_opInfo);
+        return *bitwise_cast<BasicBlock**>(&m_opInfo.u.pointer);
     }
     
     BranchData* branchData()
     {
         ASSERT(isBranch());
-        return bitwise_cast<BranchData*>(m_opInfo);
+        return m_opInfo.as<BranchData*>();
     }
     
     SwitchData* switchData()
     {
         ASSERT(isSwitch());
-        return bitwise_cast<SwitchData*>(m_opInfo);
+        return m_opInfo.as<SwitchData*>();
     }
     
     unsigned numSuccessors()
@@ -1301,15 +1446,26 @@ struct Node {
     bool hasHeapPrediction()
     {
         switch (op()) {
+        case ArithAbs:
         case ArithRound:
+        case ArithFloor:
+        case ArithCeil:
+        case ArithTrunc:
         case GetDirectPname:
         case GetById:
         case GetByIdFlush:
+        case GetByIdWithThis:
+        case TryGetById:
         case GetByVal:
+        case GetByValWithThis:
         case Call:
+        case DirectCall:
         case TailCallInlinedCaller:
+        case DirectTailCallInlinedCaller:
         case Construct:
+        case DirectConstruct:
         case CallVarargs:
+        case CallEval:
         case TailCallVarargsInlinedCaller:
         case ConstructVarargs:
         case CallForwardVarargs:
@@ -1318,12 +1474,29 @@ struct Node {
         case MultiGetByOffset:
         case GetClosureVar:
         case GetFromArguments:
+        case GetArgument:
         case ArrayPop:
         case ArrayPush:
         case RegExpExec:
         case RegExpTest:
         case GetGlobalVar:
         case GetGlobalLexicalVariable:
+        case StringReplace:
+        case StringReplaceRegExp:
+        case ToNumber:
+        case LoadFromJSMapBucket:
+        case CallDOMGetter:
+        case CallDOM:
+        case ParseInt:
+        case AtomicsAdd:
+        case AtomicsAnd:
+        case AtomicsCompareExchange:
+        case AtomicsExchange:
+        case AtomicsLoad:
+        case AtomicsOr:
+        case AtomicsStore:
+        case AtomicsSub:
+        case AtomicsXor:
             return true;
         default:
             return false;
@@ -1333,7 +1506,7 @@ struct Node {
     SpeculatedType getHeapPrediction()
     {
         ASSERT(hasHeapPrediction());
-        return static_cast<SpeculatedType>(m_opInfo2);
+        return m_opInfo2.as<SpeculatedType>();
     }
 
     void setHeapPrediction(SpeculatedType prediction)
@@ -1346,10 +1519,18 @@ struct Node {
     {
         switch (op()) {
         case CheckCell:
+        case OverridesHasInstance:
         case NewFunction:
-        case NewArrowFunction:
+        case NewGeneratorFunction:
+        case NewAsyncFunction:
         case CreateActivation:
         case MaterializeCreateActivation:
+        case NewRegexp:
+        case CompareEqPtr:
+        case DirectCall:
+        case DirectTailCall:
+        case DirectConstruct:
+        case DirectTailCallInlinedCaller:
             return true;
         default:
             return false;
@@ -1359,7 +1540,7 @@ struct Node {
     FrozenValue* cellOperand()
     {
         ASSERT(hasCellOperand());
-        return reinterpret_cast<FrozenValue*>(m_opInfo);
+        return m_opInfo.as<FrozenValue*>();
     }
     
     template<typename T>
@@ -1371,7 +1552,7 @@ struct Node {
     void setCellOperand(FrozenValue* value)
     {
         ASSERT(hasCellOperand());
-        m_opInfo = bitwise_cast<uintptr_t>(value);
+        m_opInfo = value;
     }
     
     bool hasWatchpointSet()
@@ -1382,7 +1563,7 @@ struct Node {
     WatchpointSet* watchpointSet()
     {
         ASSERT(hasWatchpointSet());
-        return reinterpret_cast<WatchpointSet*>(m_opInfo);
+        return m_opInfo.as<WatchpointSet*>();
     }
     
     bool hasStoragePointer()
@@ -1393,18 +1574,29 @@ struct Node {
     void* storagePointer()
     {
         ASSERT(hasStoragePointer());
-        return reinterpret_cast<void*>(m_opInfo);
+        return m_opInfo.as<void*>();
     }
 
     bool hasUidOperand()
     {
-        return op() == CheckIdent;
+        return op() == CheckStringIdent;
     }
 
     UniquedStringImpl* uidOperand()
     {
         ASSERT(hasUidOperand());
-        return reinterpret_cast<UniquedStringImpl*>(m_opInfo);
+        return m_opInfo.as<UniquedStringImpl*>();
+    }
+
+    bool hasTypeInfoOperand()
+    {
+        return op() == CheckTypeInfoFlags;
+    }
+
+    unsigned typeInfoOperand()
+    {
+        ASSERT(hasTypeInfoOperand() && m_opInfo.as<uint32_t>() <= static_cast<uint32_t>(UCHAR_MAX));
+        return m_opInfo.as<uint32_t>();
     }
 
     bool hasTransition()
@@ -1422,7 +1614,7 @@ struct Node {
     Transition* transition()
     {
         ASSERT(hasTransition());
-        return reinterpret_cast<Transition*>(m_opInfo);
+        return m_opInfo.as<Transition*>();
     }
     
     bool hasStructureSet()
@@ -1437,10 +1629,10 @@ struct Node {
         }
     }
     
-    StructureSet& structureSet()
+    const RegisteredStructureSet& structureSet()
     {
         ASSERT(hasStructureSet());
-        return *reinterpret_cast<StructureSet*>(m_opInfo);
+        return *m_opInfo.as<RegisteredStructureSet*>();
     }
     
     bool hasStructure()
@@ -1455,10 +1647,10 @@ struct Node {
         }
     }
     
-    Structure* structure()
+    RegisteredStructure structure()
     {
         ASSERT(hasStructure());
-        return reinterpret_cast<Structure*>(m_opInfo);
+        return m_opInfo.asRegisteredStructure();
     }
     
     bool hasStorageAccessData()
@@ -1476,7 +1668,7 @@ struct Node {
     StorageAccessData& storageAccessData()
     {
         ASSERT(hasStorageAccessData());
-        return *bitwise_cast<StorageAccessData*>(m_opInfo);
+        return *m_opInfo.as<StorageAccessData*>();
     }
     
     bool hasMultiGetByOffsetData()
@@ -1487,7 +1679,7 @@ struct Node {
     MultiGetByOffsetData& multiGetByOffsetData()
     {
         ASSERT(hasMultiGetByOffsetData());
-        return *reinterpret_cast<MultiGetByOffsetData*>(m_opInfo);
+        return *m_opInfo.as<MultiGetByOffsetData*>();
     }
     
     bool hasMultiPutByOffsetData()
@@ -1498,7 +1690,7 @@ struct Node {
     MultiPutByOffsetData& multiPutByOffsetData()
     {
         ASSERT(hasMultiPutByOffsetData());
-        return *reinterpret_cast<MultiPutByOffsetData*>(m_opInfo);
+        return *m_opInfo.as<MultiPutByOffsetData*>();
     }
     
     bool hasObjectMaterializationData()
@@ -1516,7 +1708,7 @@ struct Node {
     ObjectMaterializationData& objectMaterializationData()
     {
         ASSERT(hasObjectMaterializationData());
-        return *reinterpret_cast<ObjectMaterializationData*>(m_opInfo2);
+        return *m_opInfo2.as<ObjectMaterializationData*>();
     }
 
     bool isObjectAllocation()
@@ -1564,8 +1756,9 @@ struct Node {
     bool isFunctionAllocation()
     {
         switch (op()) {
-        case NewArrowFunction:
         case NewFunction:
+        case NewGeneratorFunction:
+        case NewAsyncFunction:
             return true;
         default:
             return false;
@@ -1576,6 +1769,8 @@ struct Node {
     {
         switch (op()) {
         case PhantomNewFunction:
+        case PhantomNewGeneratorFunction:
+        case PhantomNewAsyncFunction:
             return true;
         default:
             return false;
@@ -1587,8 +1782,13 @@ struct Node {
         switch (op()) {
         case PhantomNewObject:
         case PhantomDirectArguments:
+        case PhantomCreateRest:
+        case PhantomSpread:
+        case PhantomNewArrayWithSpread:
         case PhantomClonedArguments:
         case PhantomNewFunction:
+        case PhantomNewGeneratorFunction:
+        case PhantomNewAsyncFunction:
         case PhantomCreateActivation:
             return true;
         default:
@@ -1601,6 +1801,7 @@ struct Node {
         switch (op()) {
         case GetIndexedPropertyStorage:
         case GetArrayLength:
+        case In:
         case PutByValDirect:
         case PutByVal:
         case PutByValAlias:
@@ -1613,6 +1814,15 @@ struct Node {
         case ArrayPush:
         case ArrayPop:
         case HasIndexedProperty:
+        case AtomicsAdd:
+        case AtomicsAnd:
+        case AtomicsCompareExchange:
+        case AtomicsExchange:
+        case AtomicsLoad:
+        case AtomicsOr:
+        case AtomicsStore:
+        case AtomicsSub:
+        case AtomicsXor:
             return true;
         default:
             return false;
@@ -1623,8 +1833,8 @@ struct Node {
     {
         ASSERT(hasArrayMode());
         if (op() == ArrayifyToStructure)
-            return ArrayMode::fromWord(m_opInfo2);
-        return ArrayMode::fromWord(m_opInfo);
+            return ArrayMode::fromWord(m_opInfo2.as<uint32_t>());
+        return ArrayMode::fromWord(m_opInfo.as<uint32_t>());
     }
     
     bool setArrayMode(ArrayMode arrayMode)
@@ -1639,6 +1849,7 @@ struct Node {
     bool hasArithMode()
     {
         switch (op()) {
+        case ArithAbs:
         case ArithAdd:
         case ArithSub:
         case ArithNegate:
@@ -1656,7 +1867,7 @@ struct Node {
     Arith::Mode arithMode()
     {
         ASSERT(hasArithMode());
-        return static_cast<Arith::Mode>(m_opInfo);
+        return static_cast<Arith::Mode>(m_opInfo.as<uint32_t>());
     }
     
     void setArithMode(Arith::Mode mode)
@@ -1666,19 +1877,19 @@ struct Node {
 
     bool hasArithRoundingMode()
     {
-        return op() == ArithRound;
+        return op() == ArithRound || op() == ArithFloor || op() == ArithCeil || op() == ArithTrunc;
     }
 
     Arith::RoundingMode arithRoundingMode()
     {
         ASSERT(hasArithRoundingMode());
-        return static_cast<Arith::RoundingMode>(m_opInfo);
+        return static_cast<Arith::RoundingMode>(m_opInfo.as<uint32_t>());
     }
 
     void setArithRoundingMode(Arith::RoundingMode mode)
     {
         ASSERT(hasArithRoundingMode());
-        m_opInfo = static_cast<uintptr_t>(mode);
+        m_opInfo = static_cast<uint32_t>(mode);
     }
     
     bool hasVirtualRegister()
@@ -1707,7 +1918,7 @@ struct Node {
     
     Profiler::ExecutionCounter* executionCounter()
     {
-        return bitwise_cast<Profiler::ExecutionCounter*>(m_opInfo);
+        return m_opInfo.as<Profiler::ExecutionCounter*>();
     }
 
     bool shouldGenerate()
@@ -1715,9 +1926,11 @@ struct Node {
         return m_refCount;
     }
     
+    // Return true if the execution of this Node does not affect our ability to OSR to the FTL.
+    // FIXME: Isn't this just like checking if the node has effects?
     bool isSemanticallySkippable()
     {
-        return op() == CountExecution;
+        return op() == CountExecution || op() == InvalidationPoint;
     }
 
     unsigned refCount()
@@ -1819,6 +2032,11 @@ struct Node {
     {
         return isInt32Speculation(prediction());
     }
+
+    bool shouldSpeculateNotInt32()
+    {
+        return isNotInt32Speculation(prediction());
+    }
     
     bool sawBooleans()
     {
@@ -1845,9 +2063,9 @@ struct Node {
         return isInt32OrBooleanSpeculationExpectingDefined(prediction());
     }
     
-    bool shouldSpeculateMachineInt()
+    bool shouldSpeculateAnyInt()
     {
-        return isMachineIntSpeculation(prediction());
+        return isAnyIntSpeculation(prediction());
     }
     
     bool shouldSpeculateDouble()
@@ -1879,6 +2097,11 @@ struct Node {
     {
         return isBooleanSpeculation(prediction());
     }
+
+    bool shouldSpeculateNotBoolean()
+    {
+        return isNotBooleanSpeculation(prediction());
+    }
     
     bool shouldSpeculateOther()
     {
@@ -1904,6 +2127,16 @@ struct Node {
     {
         return isStringSpeculation(prediction());
     }
+
+    bool shouldSpeculateNotString()
+    {
+        return isNotStringSpeculation(prediction());
+    }
+    bool shouldSpeculateStringOrOther()
+    {
+        return isStringOrOtherSpeculation(prediction());
+    }
  
     bool shouldSpeculateStringObject()
     {
@@ -1915,6 +2148,11 @@ struct Node {
         return isStringOrStringObjectSpeculation(prediction());
     }
 
+    bool shouldSpeculateRegExpObject()
+    {
+        return isRegExpObjectSpeculation(prediction());
+    }
+    
     bool shouldSpeculateSymbol()
     {
         return isSymbolSpeculation(prediction());
@@ -1934,6 +2172,16 @@ struct Node {
     {
         return isArraySpeculation(prediction());
     }
+
+    bool shouldSpeculateProxyObject()
+    {
+        return isProxyObjectSpeculation(prediction());
+    }
+
+    bool shouldSpeculateDerivedArray()
+    {
+        return isDerivedArraySpeculation(prediction());
+    }
     
     bool shouldSpeculateDirectArguments()
     {
@@ -2010,6 +2258,11 @@ struct Node {
         return isCellSpeculation(prediction());
     }
     
+    bool shouldSpeculateCellOrOther()
+    {
+        return isCellOrOtherSpeculation(prediction());
+    }
+    
     bool shouldSpeculateNotCell()
     {
         return isNotCellSpeculation(prediction());
@@ -2025,6 +2278,16 @@ struct Node {
         return op1->shouldSpeculateUntypedForArithmetic() || op2->shouldSpeculateUntypedForArithmetic();
     }
     
+    bool shouldSpeculateUntypedForBitOps()
+    {
+        return isUntypedSpeculationForBitOps(prediction());
+    }
+    
+    static bool shouldSpeculateUntypedForBitOps(Node* op1, Node* op2)
+    {
+        return op1->shouldSpeculateUntypedForBitOps() || op2->shouldSpeculateUntypedForBitOps();
+    }
+    
     static bool shouldSpeculateBoolean(Node* op1, Node* op2)
     {
         return op1->shouldSpeculateBoolean() && op2->shouldSpeculateBoolean();
@@ -2053,9 +2316,9 @@ struct Node {
             && op2->shouldSpeculateInt32OrBooleanExpectingDefined();
     }
     
-    static bool shouldSpeculateMachineInt(Node* op1, Node* op2)
+    static bool shouldSpeculateAnyInt(Node* op1, Node* op2)
     {
-        return op1->shouldSpeculateMachineInt() && op2->shouldSpeculateMachineInt();
+        return op1->shouldSpeculateAnyInt() && op2->shouldSpeculateAnyInt();
     }
     
     static bool shouldSpeculateNumber(Node* op1, Node* op2)
@@ -2125,7 +2388,7 @@ struct Node {
     TypeLocation* typeLocation()
     {
         ASSERT(hasTypeLocation());
-        return reinterpret_cast<TypeLocation*>(m_opInfo);
+        return m_opInfo.as<TypeLocation*>();
     }
 
     bool hasBasicBlockLocation()
@@ -2136,9 +2399,70 @@ struct Node {
     BasicBlockLocation* basicBlockLocation()
     {
         ASSERT(hasBasicBlockLocation());
-        return reinterpret_cast<BasicBlockLocation*>(m_opInfo);
+        return m_opInfo.as<BasicBlockLocation*>();
     }
-    
+
+    bool hasCheckDOMPatchpoint() const
+    {
+        return op() == CheckDOM;
+    }
+
+    DOMJIT::Patchpoint* checkDOMPatchpoint()
+    {
+        ASSERT(hasCheckDOMPatchpoint());
+        return m_opInfo.as<DOMJIT::Patchpoint*>();
+    }
+
+    bool hasCallDOMGetterData() const
+    {
+        return op() == CallDOMGetter;
+    }
+
+    CallDOMGetterData* callDOMGetterData()
+    {
+        ASSERT(hasCallDOMGetterData());
+        return m_opInfo.as<CallDOMGetterData*>();
+    }
+
+    bool hasClassInfo() const
+    {
+        return op() == CheckDOM;
+    }
+
+    const ClassInfo* classInfo()
+    {
+        return m_opInfo2.as<const ClassInfo*>();
+    }
+
+    bool hasSignature() const
+    {
+        // Note that this does not include TailCall node types intentionally.
+        // CallDOM node types are always converted from Call.
+        return op() == Call || op() == CallDOM;
+    }
+
+    const DOMJIT::Signature* signature()
+    {
+        return m_opInfo.as<const DOMJIT::Signature*>();
+    }
+
+    bool hasInternalMethodType() const
+    {
+        return op() == HasIndexedProperty;
+    }
+
+    PropertySlot::InternalMethodType internalMethodType() const
+    {
+        ASSERT(hasInternalMethodType());
+        return static_cast<PropertySlot::InternalMethodType>(m_opInfo2.as<uint32_t>());
+    }
+
+    void setInternalMethodType(PropertySlot::InternalMethodType type)
+    {
+        ASSERT(hasInternalMethodType());
+        m_opInfo2 = static_cast<uint32_t>(type);
+    }
+
     Node* replacement() const
     {
         return m_misc.replacement;
@@ -2161,8 +2485,19 @@ struct Node {
 
     unsigned numberOfArgumentsToSkip()
     {
-        ASSERT(op() == CopyRest || op() == GetRestLength);
-        return static_cast<unsigned>(m_opInfo);
+        ASSERT(op() == CreateRest || op() == PhantomCreateRest || op() == GetRestLength || op() == GetMyArgumentByVal || op() == GetMyArgumentByValOutOfBounds);
+        return m_opInfo.as<unsigned>();
+    }
+
+    bool hasArgumentIndex()
+    {
+        return op() == GetArgument;
+    }
+
+    unsigned argumentIndex()
+    {
+        ASSERT(hasArgumentIndex());
+        return m_opInfo.as<unsigned>();
     }
 
     void dumpChildren(PrintStream& out)
@@ -2186,23 +2521,117 @@ struct Node {
     AdjacencyList children;
 
 private:
+    friend class Graph;
+
+    unsigned m_index { std::numeric_limits<unsigned>::max() };
     unsigned m_op : 10; // real type is NodeType
-    unsigned m_flags : 22;
+    unsigned m_flags : 20;
     // The virtual register number (spill location) associated with this .
     VirtualRegister m_virtualRegister;
     // The number of uses of the result of this operation (+1 for 'must generate' nodes, which have side-effects).
     unsigned m_refCount;
     // The prediction ascribed to this node after propagation.
-    SpeculatedType m_prediction;
-    // Immediate values, accesses type-checked via accessors above. The first one is
-    // big enough to store a pointer.
-    uintptr_t m_opInfo;
-    uintptr_t m_opInfo2;
+    SpeculatedType m_prediction { SpecNone };
+    // Immediate values, accesses type-checked via accessors above.
+    struct OpInfoWrapper {
+        OpInfoWrapper()
+        {
+            u.int64 = 0;
+        }
+        OpInfoWrapper(uint32_t intValue)
+        {
+            u.int64 = 0;
+            u.int32 = intValue;
+        }
+        OpInfoWrapper(uint64_t intValue)
+        {
+            u.int64 = intValue;
+        }
+        OpInfoWrapper(void* pointer)
+        {
+            u.int64 = 0;
+            u.pointer = pointer;
+        }
+        OpInfoWrapper(const void* constPointer)
+        {
+            u.int64 = 0;
+            u.constPointer = constPointer;
+        }
+        OpInfoWrapper(RegisteredStructure structure)
+        {
+            u.int64 = 0;
+            u.pointer = bitwise_cast<void*>(structure);
+        }
+        OpInfoWrapper& operator=(uint32_t int32)
+        {
+            u.int64 = 0;
+            u.int32 = int32;
+            return *this;
+        }
+        OpInfoWrapper& operator=(int32_t int32)
+        {
+            u.int64 = 0;
+            u.int32 = int32;
+            return *this;
+        }
+        OpInfoWrapper& operator=(uint64_t int64)
+        {
+            u.int64 = int64;
+            return *this;
+        }
+        OpInfoWrapper& operator=(void* pointer)
+        {
+            u.int64 = 0;
+            u.pointer = pointer;
+            return *this;
+        }
+        OpInfoWrapper& operator=(const void* constPointer)
+        {
+            u.int64 = 0;
+            u.constPointer = constPointer;
+            return *this;
+        }
+        OpInfoWrapper& operator=(RegisteredStructure structure)
+        {
+            u.int64 = 0;
+            u.pointer = bitwise_cast<void*>(structure);
+            return *this;
+        }
+        template <typename T>
+        ALWAYS_INLINE auto as() const -> typename std::enable_if<std::is_pointer<T>::value && !std::is_const<typename std::remove_pointer<T>::type>::value, T>::type
+        {
+            return static_cast<T>(u.pointer);
+        }
+        template <typename T>
+        ALWAYS_INLINE auto as() const -> typename std::enable_if<std::is_pointer<T>::value && std::is_const<typename std::remove_pointer<T>::type>::value, T>::type
+        {
+            return static_cast<T>(u.constPointer);
+        }
+        template <typename T>
+        ALWAYS_INLINE auto as() const -> typename std::enable_if<std::is_integral<T>::value && sizeof(T) == 4, T>::type
+        {
+            return u.int32;
+        }
+        template <typename T>
+        ALWAYS_INLINE auto as() const -> typename std::enable_if<std::is_integral<T>::value && sizeof(T) == 8, T>::type
+        {
+            return u.int64;
+        }
+        ALWAYS_INLINE RegisteredStructure asRegisteredStructure() const
+        {
+            return bitwise_cast<RegisteredStructure>(u.pointer);
+        }
+
+        union {
+            uint32_t int32;
+            uint64_t int64;
+            void* pointer;
+            const void* constPointer;
+        } u;
+    };
+    OpInfoWrapper m_opInfo;
+    OpInfoWrapper m_opInfo2;
 
-public:
-    // Fields used by various analyses.
-    AbstractValue value;
-    
     // Miscellaneous data that is usually meaningless, but can hold some analysis results
     // if you ask right. For example, if you do Graph::initializeNodeOwners(), Node::owner
     // will tell you which basic block a node belongs to. You cannot rely on this persisting
@@ -2222,15 +2651,22 @@ public:
     BasicBlock* owner;
 };
 
-inline bool nodeComparator(Node* a, Node* b)
-{
-    return a->index() < b->index();
-}
+// Uncomment this to log NodeSet operations.
+// typedef LoggingHashSet<Node::HashSetTemplateInstantiationString, Node*> NodeSet;
+typedef HashSet<Node*> NodeSet;
+
+struct NodeComparator {
+    template<typename NodePtrType>
+    bool operator()(NodePtrType a, NodePtrType b) const
+    {
+        return a->index() < b->index();
+    }
+};
 
 template<typename T>
 CString nodeListDump(const T& nodeList)
 {
-    return sortedListDump(nodeList, nodeComparator);
+    return sortedListDump(nodeList, NodeComparator());
 }
 
 template<typename T>
@@ -2241,7 +2677,7 @@ CString nodeMapDump(const T& nodeMap, DumpContext* context = 0)
         typename T::const_iterator iter = nodeMap.begin();
         iter != nodeMap.end(); ++iter)
         keys.append(iter->key);
-    std::sort(keys.begin(), keys.end(), nodeComparator);
+    std::sort(keys.begin(), keys.end(), NodeComparator());
     StringPrintStream out;
     CommaPrinter comma;
     for(unsigned i = 0; i < keys.size(); ++i)
@@ -2249,6 +2685,22 @@ CString nodeMapDump(const T& nodeMap, DumpContext* context = 0)
     return out.toCString();
 }
 
+template<typename T>
+CString nodeValuePairListDump(const T& nodeValuePairList, DumpContext* context = 0)
+{
+    using V = typename T::ValueType;
+    T sortedList = nodeValuePairList;
+    std::sort(sortedList.begin(), sortedList.end(), [](const V& a, const V& b) {
+        return NodeComparator()(a.node, b.node);
+    });
+
+    StringPrintStream out;
+    CommaPrinter comma;
+    for (const auto& pair : sortedList)
+        out.print(comma, pair.node, "=>", inContext(pair.value, context));
+    return out.toCString();
+}
+
 } } // namespace JSC::DFG
 
 namespace WTF {
@@ -2258,9 +2710,16 @@ void printInternal(PrintStream&, JSC::DFG::Node*);
 
 inline JSC::DFG::Node* inContext(JSC::DFG::Node* node, JSC::DumpContext*) { return node; }
 
+template<>
+struct LoggingHashKeyTraits<JSC::DFG::Node*> {
+    static void print(PrintStream& out, JSC::DFG::Node* key)
+    {
+        out.print("bitwise_cast<::JSC::DFG::Node*>(", RawPointer(key), "lu)");
+    }
+};
+
 } // namespace WTF
 
 using WTF::inContext;
 
 #endif
-#endif