|  | /* | 
|  | * Copyright (C) 2011-2020 Apple Inc. All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | 
|  | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
|  | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | 
|  | * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR | 
|  | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | 
|  | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | 
|  | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | 
|  | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | 
|  | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
|  | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  |  | 
|  | #pragma once | 
|  |  | 
|  | #if ENABLE(DFG_JIT) | 
|  |  | 
|  | #include "B3SparseCollection.h" | 
|  | #include "BasicBlockLocation.h" | 
|  | #include "CodeBlock.h" | 
|  | #include "DFGAdjacencyList.h" | 
|  | #include "DFGArithMode.h" | 
|  | #include "DFGArrayMode.h" | 
|  | #include "DFGCommon.h" | 
|  | #include "DFGEpoch.h" | 
|  | #include "DFGLazyJSValue.h" | 
|  | #include "DFGMultiGetByOffsetData.h" | 
|  | #include "DFGNodeFlags.h" | 
|  | #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 "DOMJITSignature.h" | 
|  | #include "DeleteByIdVariant.h" | 
|  | #include "GetByIdVariant.h" | 
|  | #include "JSCJSValue.h" | 
|  | #include "Operands.h" | 
|  | #include "PutByIdVariant.h" | 
|  | #include "SpeculatedType.h" | 
|  | #include "TypeLocation.h" | 
|  | #include "ValueProfile.h" | 
|  | #include <type_traits> | 
|  | #include <wtf/FastMalloc.h> | 
|  | #include <wtf/ListDump.h> | 
|  | #include <wtf/LoggingHashSet.h> | 
|  |  | 
|  | namespace JSC { | 
|  |  | 
|  | namespace DOMJIT { | 
|  | class GetterSetter; | 
|  | class CallDOMGetterSnippet; | 
|  | class Signature; | 
|  | } | 
|  |  | 
|  | namespace Profiler { | 
|  | class ExecutionCounter; | 
|  | } | 
|  |  | 
|  | class Snippet; | 
|  |  | 
|  | namespace DFG { | 
|  |  | 
|  | class Graph; | 
|  | class PromotedLocationDescriptor; | 
|  | struct BasicBlock; | 
|  |  | 
|  | struct StorageAccessData { | 
|  | PropertyOffset offset; | 
|  | unsigned identifierNumber; | 
|  | }; | 
|  |  | 
|  | struct MultiPutByOffsetData { | 
|  | unsigned identifierNumber; | 
|  | Vector<PutByIdVariant, 2> variants; | 
|  |  | 
|  | bool writesStructures() const; | 
|  | bool reallocatesStorage() const; | 
|  | }; | 
|  |  | 
|  | struct MultiDeleteByOffsetData { | 
|  | unsigned identifierNumber; | 
|  | Vector<DeleteByIdVariant, 2> variants; | 
|  |  | 
|  | bool writesStructures() const; | 
|  | bool allVariantsStoreEmpty() const; | 
|  | }; | 
|  |  | 
|  | struct MatchStructureVariant { | 
|  | RegisteredStructure structure; | 
|  | bool result; | 
|  | }; | 
|  |  | 
|  | struct MatchStructureData { | 
|  | Vector<MatchStructureVariant, 2> variants; | 
|  | }; | 
|  |  | 
|  | struct NewArrayBufferData { | 
|  | union { | 
|  | struct { | 
|  | unsigned vectorLengthHint; | 
|  | unsigned indexingMode; | 
|  | }; | 
|  | uint64_t asQuadWord; | 
|  | }; | 
|  | }; | 
|  | static_assert(sizeof(IndexingType) <= sizeof(unsigned), ""); | 
|  | static_assert(sizeof(NewArrayBufferData) == sizeof(uint64_t), ""); | 
|  |  | 
|  | struct DataViewData { | 
|  | union { | 
|  | struct { | 
|  | uint8_t byteSize; | 
|  | bool isSigned; | 
|  | bool isFloatingPoint; // Used for the DataViewSet node. | 
|  | TriState isLittleEndian; | 
|  | }; | 
|  | uint64_t asQuadWord; | 
|  | }; | 
|  | }; | 
|  | static_assert(sizeof(DataViewData) == sizeof(uint64_t), ""); | 
|  |  | 
|  | struct BranchTarget { | 
|  | BranchTarget() | 
|  | : block(nullptr) | 
|  | , count(PNaN) | 
|  | { | 
|  | } | 
|  |  | 
|  | explicit BranchTarget(BasicBlock* block) | 
|  | : block(block) | 
|  | , count(PNaN) | 
|  | { | 
|  | } | 
|  |  | 
|  | void setBytecodeIndex(unsigned bytecodeIndex) | 
|  | { | 
|  | block = bitwise_cast<BasicBlock*>(static_cast<uintptr_t>(bytecodeIndex)); | 
|  | } | 
|  | unsigned bytecodeIndex() const { return bitwise_cast<uintptr_t>(block); } | 
|  |  | 
|  | void dump(PrintStream&) const; | 
|  |  | 
|  | BasicBlock* block; | 
|  | float count; | 
|  | }; | 
|  |  | 
|  | struct BranchData { | 
|  | static BranchData withBytecodeIndices( | 
|  | unsigned takenBytecodeIndex, unsigned notTakenBytecodeIndex) | 
|  | { | 
|  | BranchData result; | 
|  | result.taken.block = bitwise_cast<BasicBlock*>(static_cast<uintptr_t>(takenBytecodeIndex)); | 
|  | result.notTaken.block = bitwise_cast<BasicBlock*>(static_cast<uintptr_t>(notTakenBytecodeIndex)); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | unsigned takenBytecodeIndex() const { return taken.bytecodeIndex(); } | 
|  | unsigned notTakenBytecodeIndex() const { return notTaken.bytecodeIndex(); } | 
|  |  | 
|  | BasicBlock*& forCondition(bool condition) | 
|  | { | 
|  | if (condition) | 
|  | return taken.block; | 
|  | return notTaken.block; | 
|  | } | 
|  |  | 
|  | BranchTarget taken; | 
|  | BranchTarget notTaken; | 
|  | }; | 
|  |  | 
|  | // The SwitchData and associated data structures duplicate the information in | 
|  | // JumpTable. The DFG may ultimately end up using the JumpTable, though it may | 
|  | // instead decide to do something different - this is entirely up to the DFG. | 
|  | // These data structures give the DFG a higher-level semantic description of | 
|  | // what is going on, which will allow it to make the right decision. | 
|  | // | 
|  | // Note that there will never be multiple SwitchCases in SwitchData::cases that | 
|  | // have the same SwitchCase::value, since the bytecode's JumpTables never have | 
|  | // duplicates - since the JumpTable maps a value to a target. It's a | 
|  | // one-to-many mapping. So we may have duplicate targets, but never duplicate | 
|  | // values. | 
|  | struct SwitchCase { | 
|  | SwitchCase() | 
|  | { | 
|  | } | 
|  |  | 
|  | SwitchCase(LazyJSValue value, BasicBlock* target) | 
|  | : value(value) | 
|  | , target(target) | 
|  | { | 
|  | } | 
|  |  | 
|  | static SwitchCase withBytecodeIndex(LazyJSValue value, unsigned bytecodeIndex) | 
|  | { | 
|  | SwitchCase result; | 
|  | result.value = value; | 
|  | result.target.setBytecodeIndex(bytecodeIndex); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | LazyJSValue value; | 
|  | BranchTarget target; | 
|  | }; | 
|  |  | 
|  | struct SwitchData { | 
|  | // Initializes most fields to obviously invalid values. Anyone | 
|  | // constructing this should make sure to initialize everything they | 
|  | // care about manually. | 
|  | SwitchData() | 
|  | : switchTableIndex(UINT_MAX) | 
|  | , kind(static_cast<SwitchKind>(-1)) | 
|  | , didUseJumpTable(false) | 
|  | { | 
|  | } | 
|  |  | 
|  | Vector<SwitchCase> cases; | 
|  | BranchTarget fallThrough; | 
|  | size_t switchTableIndex; | 
|  | SwitchKind kind; | 
|  | bool didUseJumpTable; | 
|  | }; | 
|  |  | 
|  | struct EntrySwitchData { | 
|  | Vector<BasicBlock*> cases; | 
|  | }; | 
|  |  | 
|  | struct CallVarargsData { | 
|  | int firstVarArgOffset; | 
|  | }; | 
|  |  | 
|  | struct LoadVarargsData { | 
|  | VirtualRegister start; // Local for the first element. This is the first actual argument, not this. | 
|  | VirtualRegister count; // Local for the count. | 
|  | VirtualRegister machineStart; | 
|  | VirtualRegister machineCount; | 
|  | unsigned offset; // Which array element to start with. Usually this is 0. | 
|  | unsigned mandatoryMinimum; // The number of elements on the stack that must be initialized; if the array is too short then the missing elements must get undefined. Does not include "this". | 
|  | unsigned limit; // Maximum number of elements to load. Includes "this". | 
|  | }; | 
|  |  | 
|  | struct StackAccessData { | 
|  | StackAccessData() | 
|  | : format(DeadFlush) | 
|  | { | 
|  | } | 
|  |  | 
|  | StackAccessData(Operand operand, FlushFormat format) | 
|  | : operand(operand) | 
|  | , format(format) | 
|  | { | 
|  | } | 
|  |  | 
|  | Operand operand; | 
|  | VirtualRegister machineLocal; | 
|  | FlushFormat format; | 
|  |  | 
|  | FlushedAt flushedAt() { return FlushedAt(format, machineLocal); } | 
|  | }; | 
|  |  | 
|  | struct CallDOMGetterData { | 
|  | FunctionPtr<OperationPtrTag> customAccessorGetter; | 
|  | const DOMJIT::GetterSetter* domJIT { nullptr }; | 
|  | DOMJIT::CallDOMGetterSnippet* snippet { nullptr }; | 
|  | unsigned identifierNumber { 0 }; | 
|  | const ClassInfo* requiredClassInfo { nullptr }; | 
|  | }; | 
|  |  | 
|  | enum class BucketOwnerType : uint32_t { | 
|  | Map, | 
|  | Set | 
|  | }; | 
|  |  | 
|  | // === Node === | 
|  | // | 
|  | // Node represents a single operation in the data flow graph. | 
|  | DECLARE_ALLOCATOR_WITH_HEAP_IDENTIFIER(DFGNode); | 
|  | struct Node { | 
|  | WTF_MAKE_STRUCT_FAST_ALLOCATED_WITH_HEAP_IDENTIFIER(DFGNode); | 
|  | public: | 
|  | static const char HashSetTemplateInstantiationString[]; | 
|  |  | 
|  | enum VarArgTag { VarArg }; | 
|  |  | 
|  | Node() { } | 
|  |  | 
|  | Node(NodeType op, NodeOrigin nodeOrigin, const AdjacencyList& children) | 
|  | : origin(nodeOrigin) | 
|  | , children(children) | 
|  | , m_virtualRegister(VirtualRegister()) | 
|  | , m_refCount(1) | 
|  | , m_prediction(SpecNone) | 
|  | , owner(nullptr) | 
|  | { | 
|  | m_misc.replacement = nullptr; | 
|  | setOpAndDefaultFlags(op); | 
|  | } | 
|  |  | 
|  | // Construct a node with up to 3 children, no immediate value. | 
|  | Node(NodeType op, NodeOrigin nodeOrigin, Edge child1 = Edge(), Edge child2 = Edge(), Edge child3 = Edge()) | 
|  | : origin(nodeOrigin) | 
|  | , children(AdjacencyList::Fixed, child1, child2, child3) | 
|  | , m_virtualRegister(VirtualRegister()) | 
|  | , m_refCount(1) | 
|  | , m_prediction(SpecNone) | 
|  | , owner(nullptr) | 
|  | { | 
|  | m_misc.replacement = nullptr; | 
|  | setOpAndDefaultFlags(op); | 
|  | ASSERT(!(m_flags & NodeHasVarArgs)); | 
|  | } | 
|  |  | 
|  | // Construct a node with up to 3 children, no immediate value. | 
|  | Node(NodeFlags result, NodeType op, NodeOrigin nodeOrigin, Edge child1 = Edge(), Edge child2 = Edge(), Edge child3 = Edge()) | 
|  | : origin(nodeOrigin) | 
|  | , children(AdjacencyList::Fixed, child1, child2, child3) | 
|  | , m_virtualRegister(VirtualRegister()) | 
|  | , m_refCount(1) | 
|  | , m_prediction(SpecNone) | 
|  | , owner(nullptr) | 
|  | { | 
|  | m_misc.replacement = nullptr; | 
|  | setOpAndDefaultFlags(op); | 
|  | setResult(result); | 
|  | ASSERT(!(m_flags & NodeHasVarArgs)); | 
|  | } | 
|  |  | 
|  | // Construct a node with up to 3 children and an immediate value. | 
|  | Node(NodeType op, NodeOrigin nodeOrigin, OpInfo imm, Edge child1 = Edge(), Edge child2 = Edge(), Edge child3 = Edge()) | 
|  | : origin(nodeOrigin) | 
|  | , children(AdjacencyList::Fixed, child1, child2, child3) | 
|  | , m_virtualRegister(VirtualRegister()) | 
|  | , m_refCount(1) | 
|  | , m_prediction(SpecNone) | 
|  | , m_opInfo(imm.m_value) | 
|  | , owner(nullptr) | 
|  | { | 
|  | m_misc.replacement = nullptr; | 
|  | setOpAndDefaultFlags(op); | 
|  | ASSERT(!(m_flags & NodeHasVarArgs)); | 
|  | } | 
|  |  | 
|  | // Construct a node with up to 3 children and an immediate value. | 
|  | Node(NodeFlags result, NodeType op, NodeOrigin nodeOrigin, OpInfo imm, Edge child1 = Edge(), Edge child2 = Edge(), Edge child3 = Edge()) | 
|  | : origin(nodeOrigin) | 
|  | , children(AdjacencyList::Fixed, child1, child2, child3) | 
|  | , m_virtualRegister(VirtualRegister()) | 
|  | , m_refCount(1) | 
|  | , m_prediction(SpecNone) | 
|  | , m_opInfo(imm.m_value) | 
|  | , owner(nullptr) | 
|  | { | 
|  | m_misc.replacement = nullptr; | 
|  | setOpAndDefaultFlags(op); | 
|  | setResult(result); | 
|  | ASSERT(!(m_flags & NodeHasVarArgs)); | 
|  | } | 
|  |  | 
|  | // Construct a node with up to 3 children and two immediate values. | 
|  | Node(NodeType op, NodeOrigin nodeOrigin, OpInfo imm1, OpInfo imm2, Edge child1 = Edge(), Edge child2 = Edge(), Edge child3 = Edge()) | 
|  | : origin(nodeOrigin) | 
|  | , children(AdjacencyList::Fixed, child1, child2, child3) | 
|  | , m_virtualRegister(VirtualRegister()) | 
|  | , m_refCount(1) | 
|  | , m_prediction(SpecNone) | 
|  | , m_opInfo(imm1.m_value) | 
|  | , m_opInfo2(imm2.m_value) | 
|  | , owner(nullptr) | 
|  | { | 
|  | m_misc.replacement = nullptr; | 
|  | setOpAndDefaultFlags(op); | 
|  | ASSERT(!(m_flags & NodeHasVarArgs)); | 
|  | } | 
|  |  | 
|  | // Construct a node with a variable number of children and two immediate values. | 
|  | Node(VarArgTag, NodeType op, NodeOrigin nodeOrigin, OpInfo imm1, OpInfo imm2, unsigned firstChild, unsigned numChildren) | 
|  | : origin(nodeOrigin) | 
|  | , children(AdjacencyList::Variable, firstChild, numChildren) | 
|  | , m_virtualRegister(VirtualRegister()) | 
|  | , m_refCount(1) | 
|  | , m_prediction(SpecNone) | 
|  | , m_opInfo(imm1.m_value) | 
|  | , m_opInfo2(imm2.m_value) | 
|  | , owner(nullptr) | 
|  | { | 
|  | m_misc.replacement = nullptr; | 
|  | setOpAndDefaultFlags(op); | 
|  | ASSERT(m_flags & NodeHasVarArgs); | 
|  | } | 
|  |  | 
|  | NodeType op() const { return static_cast<NodeType>(m_op); } | 
|  | NodeFlags flags() const { return m_flags; } | 
|  |  | 
|  | unsigned index() const { return m_index; } | 
|  |  | 
|  | void setOp(NodeType op) | 
|  | { | 
|  | m_op = op; | 
|  | } | 
|  |  | 
|  | void setFlags(NodeFlags flags) | 
|  | { | 
|  | m_flags = flags; | 
|  | } | 
|  |  | 
|  | bool mergeFlags(NodeFlags flags) | 
|  | { | 
|  | NodeFlags newFlags = m_flags | flags; | 
|  | if (newFlags == m_flags) | 
|  | return false; | 
|  | m_flags = newFlags; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool filterFlags(NodeFlags flags) | 
|  | { | 
|  | NodeFlags newFlags = m_flags & flags; | 
|  | if (newFlags == m_flags) | 
|  | return false; | 
|  | m_flags = newFlags; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool clearFlags(NodeFlags flags) | 
|  | { | 
|  | return filterFlags(~flags); | 
|  | } | 
|  |  | 
|  | void setResult(NodeFlags result) | 
|  | { | 
|  | ASSERT(!(result & ~NodeResultMask)); | 
|  | clearFlags(NodeResultMask); | 
|  | mergeFlags(result); | 
|  | } | 
|  |  | 
|  | NodeFlags result() const | 
|  | { | 
|  | return flags() & NodeResultMask; | 
|  | } | 
|  |  | 
|  | void setOpAndDefaultFlags(NodeType op) | 
|  | { | 
|  | m_op = op; | 
|  | m_flags = defaultFlags(op); | 
|  | } | 
|  |  | 
|  | void remove(Graph&); | 
|  | void removeWithoutChecks(); | 
|  |  | 
|  | void convertToCheckStructure(RegisteredStructureSet* set) | 
|  | { | 
|  | setOpAndDefaultFlags(CheckStructure); | 
|  | m_opInfo = set; | 
|  | } | 
|  |  | 
|  | void convertToCheckStructureOrEmpty(RegisteredStructureSet* set) | 
|  | { | 
|  | if (SpecCellCheck & SpecEmpty) | 
|  | setOpAndDefaultFlags(CheckStructureOrEmpty); | 
|  | else | 
|  | setOpAndDefaultFlags(CheckStructure); | 
|  | m_opInfo = set; | 
|  | } | 
|  |  | 
|  | void convertCheckStructureOrEmptyToCheckStructure() | 
|  | { | 
|  | ASSERT(op() == CheckStructureOrEmpty); | 
|  | setOpAndDefaultFlags(CheckStructure); | 
|  | } | 
|  |  | 
|  | void convertToCheckStructureImmediate(Node* structure) | 
|  | { | 
|  | ASSERT(op() == CheckStructure || op() == CheckStructureOrEmpty); | 
|  | m_op = CheckStructureImmediate; | 
|  | children.setChild1(Edge(structure, CellUse)); | 
|  | } | 
|  |  | 
|  | void convertCheckArrayOrEmptyToCheckArray() | 
|  | { | 
|  | ASSERT(op() == CheckArrayOrEmpty); | 
|  | setOpAndDefaultFlags(CheckArray); | 
|  | } | 
|  |  | 
|  | void replaceWith(Graph&, Node* other); | 
|  | void replaceWithWithoutChecks(Node* other); | 
|  |  | 
|  | void convertToIdentity(); | 
|  | void convertToIdentityOn(Node*); | 
|  |  | 
|  | bool mustGenerate() | 
|  | { | 
|  | return m_flags & NodeMustGenerate; | 
|  | } | 
|  |  | 
|  | bool isConstant() | 
|  | { | 
|  | switch (op()) { | 
|  | case JSConstant: | 
|  | case DoubleConstant: | 
|  | case Int52Constant: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool hasConstant() | 
|  | { | 
|  | switch (op()) { | 
|  | case CheckIsConstant: | 
|  | case JSConstant: | 
|  | case DoubleConstant: | 
|  | case Int52Constant: | 
|  | return true; | 
|  |  | 
|  | case PhantomDirectArguments: | 
|  | case PhantomClonedArguments: | 
|  | // These pretend to be the empty value constant for the benefit of the DFG backend, which | 
|  | // otherwise wouldn't take kindly to a node that doesn't compute a value. | 
|  | return true; | 
|  |  | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | FrozenValue* constant() | 
|  | { | 
|  | ASSERT(hasConstant()); | 
|  |  | 
|  | if (op() == PhantomDirectArguments || op() == PhantomClonedArguments) { | 
|  | // These pretend to be the empty value constant for the benefit of the DFG backend, which | 
|  | // otherwise wouldn't take kindly to a node that doesn't compute a value. | 
|  | return FrozenValue::emptySingleton(); | 
|  | } | 
|  |  | 
|  | return m_opInfo.as<FrozenValue*>(); | 
|  | } | 
|  |  | 
|  | // Don't call this directly - use Graph::convertToConstant() instead! | 
|  | void convertToConstant(FrozenValue* value) | 
|  | { | 
|  | if (hasDoubleResult()) | 
|  | m_op = DoubleConstant; | 
|  | else if (hasInt52Result()) | 
|  | m_op = Int52Constant; | 
|  | else | 
|  | m_op = JSConstant; | 
|  | m_flags &= ~(NodeMustGenerate | NodeHasVarArgs); | 
|  | m_opInfo = value; | 
|  | children.reset(); | 
|  | } | 
|  |  | 
|  | void convertToLazyJSConstant(Graph&, LazyJSValue); | 
|  |  | 
|  | void convertToConstantStoragePointer(void* pointer) | 
|  | { | 
|  | ASSERT(op() == GetIndexedPropertyStorage); | 
|  | m_op = ConstantStoragePointer; | 
|  | m_opInfo = pointer; | 
|  | children.reset(); | 
|  | } | 
|  |  | 
|  | void convertToPutStack(StackAccessData* data) | 
|  | { | 
|  | m_op = PutStack; | 
|  | m_flags |= NodeMustGenerate; | 
|  | m_opInfo = data; | 
|  | m_opInfo2 = OpInfoWrapper(); | 
|  | } | 
|  |  | 
|  | void convertToGetStack(StackAccessData* data) | 
|  | { | 
|  | m_op = GetStack; | 
|  | m_flags &= ~NodeMustGenerate; | 
|  | m_opInfo = data; | 
|  | m_opInfo2 = OpInfoWrapper(); | 
|  | children.reset(); | 
|  | } | 
|  |  | 
|  | void convertToGetByOffset(StorageAccessData& data, Edge storage, Edge base) | 
|  | { | 
|  | ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == GetByIdDirect || m_op == GetByIdDirectFlush || m_op == MultiGetByOffset); | 
|  | m_opInfo = &data; | 
|  | children.setChild1(storage); | 
|  | children.setChild2(base); | 
|  | m_op = GetByOffset; | 
|  | m_flags &= ~NodeMustGenerate; | 
|  | } | 
|  |  | 
|  | void convertToMultiGetByOffset(MultiGetByOffsetData* data) | 
|  | { | 
|  | RELEASE_ASSERT(m_op == GetById || m_op == GetByIdFlush || m_op == GetByIdDirect || m_op == GetByIdDirectFlush); | 
|  | m_opInfo = data; | 
|  | child1().setUseKind(CellUse); | 
|  | m_op = MultiGetByOffset; | 
|  | RELEASE_ASSERT(m_flags & NodeMustGenerate); | 
|  | } | 
|  |  | 
|  | void convertToPutByOffset(StorageAccessData& data, Edge storage, Edge base) | 
|  | { | 
|  | ASSERT(m_op == PutById || m_op == PutByIdDirect || m_op == PutByIdFlush || m_op == MultiPutByOffset); | 
|  | m_opInfo = &data; | 
|  | children.setChild3(children.child2()); | 
|  | children.setChild2(base); | 
|  | children.setChild1(storage); | 
|  | m_op = PutByOffset; | 
|  | } | 
|  |  | 
|  | void convertToMultiPutByOffset(MultiPutByOffsetData* data) | 
|  | { | 
|  | ASSERT(m_op == PutById || m_op == PutByIdDirect || m_op == PutByIdFlush); | 
|  | m_opInfo = data; | 
|  | m_op = MultiPutByOffset; | 
|  | } | 
|  |  | 
|  | void convertToPhantomNewObject() | 
|  | { | 
|  | ASSERT(m_op == NewObject); | 
|  | m_op = PhantomNewObject; | 
|  | m_flags &= ~NodeHasVarArgs; | 
|  | m_flags |= NodeMustGenerate; | 
|  | m_opInfo = OpInfoWrapper(); | 
|  | m_opInfo2 = OpInfoWrapper(); | 
|  | children = AdjacencyList(); | 
|  | } | 
|  |  | 
|  | void convertToPhantomNewFunction() | 
|  | { | 
|  | ASSERT(m_op == NewFunction || m_op == NewGeneratorFunction || m_op == NewAsyncFunction || m_op == NewAsyncGeneratorFunction); | 
|  | m_op = PhantomNewFunction; | 
|  | m_flags |= NodeMustGenerate; | 
|  | 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 convertToPhantomNewInternalFieldObject() | 
|  | { | 
|  | ASSERT(m_op == NewInternalFieldObject); | 
|  | m_op = PhantomNewInternalFieldObject; | 
|  | m_flags &= ~NodeHasVarArgs; | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | void convertToPhantomNewAsyncGeneratorFunction() | 
|  | { | 
|  | ASSERT(m_op == NewAsyncGeneratorFunction); | 
|  | m_op = PhantomNewAsyncGeneratorFunction; | 
|  | m_flags |= NodeMustGenerate; | 
|  | m_opInfo = OpInfoWrapper(); | 
|  | m_opInfo2 = OpInfoWrapper(); | 
|  | children = AdjacencyList(); | 
|  | } | 
|  |  | 
|  | void convertToPhantomCreateActivation() | 
|  | { | 
|  | ASSERT(m_op == CreateActivation); | 
|  | m_op = PhantomCreateActivation; | 
|  | m_flags &= ~NodeHasVarArgs; | 
|  | m_flags |= NodeMustGenerate; | 
|  | m_opInfo = OpInfoWrapper(); | 
|  | m_opInfo2 = OpInfoWrapper(); | 
|  | children = AdjacencyList(); | 
|  | } | 
|  |  | 
|  | void convertToPhantomNewRegexp() | 
|  | { | 
|  | ASSERT(m_op == NewRegexp); | 
|  | setOpAndDefaultFlags(PhantomNewRegexp); | 
|  | m_opInfo = OpInfoWrapper(); | 
|  | m_opInfo2 = OpInfoWrapper(); | 
|  | children = AdjacencyList(); | 
|  | } | 
|  |  | 
|  | void convertPhantomToPhantomLocal() | 
|  | { | 
|  | ASSERT(m_op == Phantom && (child1()->op() == Phi || child1()->op() == SetLocal || child1()->op() == SetArgumentDefinitely)); | 
|  | m_op = PhantomLocal; | 
|  | m_opInfo = child1()->m_opInfo; // Copy the variableAccessData. | 
|  | children.setChild1(Edge()); | 
|  | } | 
|  |  | 
|  | void convertFlushToPhantomLocal() | 
|  | { | 
|  | ASSERT(m_op == Flush); | 
|  | m_op = PhantomLocal; | 
|  | children = AdjacencyList(); | 
|  | } | 
|  |  | 
|  | void convertToToString() | 
|  | { | 
|  | ASSERT(m_op == ToPrimitive || m_op == StringValueOf || m_op == ToPropertyKey); | 
|  | m_op = ToString; | 
|  | } | 
|  |  | 
|  | void convertToArithNegate() | 
|  | { | 
|  | ASSERT(m_op == ArithAbs && child1().useKind() == Int32Use); | 
|  | m_op = ArithNegate; | 
|  | } | 
|  |  | 
|  | void convertToCompareEqPtr(FrozenValue* cell, Edge node) | 
|  | { | 
|  | ASSERT(m_op == CompareStrictEq || m_op == SameValue); | 
|  | setOpAndDefaultFlags(CompareEqPtr); | 
|  | children.setChild1(node); | 
|  | children.setChild2(Edge()); | 
|  | m_opInfo = cell; | 
|  | } | 
|  |  | 
|  | void convertToNumberToStringWithValidRadixConstant(int32_t radix) | 
|  | { | 
|  | ASSERT(m_op == NumberToStringWithRadix); | 
|  | ASSERT(2 <= radix && radix <= 36); | 
|  | setOpAndDefaultFlags(NumberToStringWithValidRadixConstant); | 
|  | children.setChild2(Edge()); | 
|  | m_opInfo = radix; | 
|  | } | 
|  |  | 
|  | void convertToGetGlobalThis() | 
|  | { | 
|  | ASSERT(m_op == ToThis); | 
|  | setOpAndDefaultFlags(GetGlobalThis); | 
|  | children.setChild1(Edge()); | 
|  | } | 
|  |  | 
|  | void convertToCallObjectConstructor(FrozenValue* globalObject) | 
|  | { | 
|  | ASSERT(m_op == ToObject); | 
|  | setOpAndDefaultFlags(CallObjectConstructor); | 
|  | m_opInfo = globalObject; | 
|  | } | 
|  |  | 
|  | void convertToNewStringObject(RegisteredStructure structure) | 
|  | { | 
|  | ASSERT(m_op == CallObjectConstructor || m_op == ToObject); | 
|  | setOpAndDefaultFlags(NewStringObject); | 
|  | m_opInfo = structure; | 
|  | m_opInfo2 = OpInfoWrapper(); | 
|  | } | 
|  |  | 
|  | void convertToNewObject(RegisteredStructure structure) | 
|  | { | 
|  | ASSERT(m_op == CallObjectConstructor || m_op == CreateThis || m_op == ObjectCreate); | 
|  | setOpAndDefaultFlags(NewObject); | 
|  | children.reset(); | 
|  | m_opInfo = structure; | 
|  | m_opInfo2 = OpInfoWrapper(); | 
|  | } | 
|  |  | 
|  | void convertToNewInternalFieldObject(RegisteredStructure structure) | 
|  | { | 
|  | ASSERT(m_op == CreatePromise); | 
|  | setOpAndDefaultFlags(NewInternalFieldObject); | 
|  | children.reset(); | 
|  | m_opInfo = structure; | 
|  | m_opInfo2 = OpInfoWrapper(); | 
|  | } | 
|  |  | 
|  | void convertToNewInternalFieldObjectWithInlineFields(NodeType newOp, RegisteredStructure structure) | 
|  | { | 
|  | ASSERT(m_op == CreateAsyncGenerator || m_op == CreateGenerator); | 
|  | setOpAndDefaultFlags(newOp); | 
|  | children.reset(); | 
|  | m_opInfo = structure; | 
|  | m_opInfo2 = OpInfoWrapper(); | 
|  | } | 
|  |  | 
|  | void convertToNewArrayBuffer(FrozenValue* immutableButterfly); | 
|  |  | 
|  | void convertToDirectCall(FrozenValue*); | 
|  |  | 
|  | void convertToCallDOM(Graph&); | 
|  |  | 
|  | void convertToRegExpExecNonGlobalOrStickyWithoutChecks(FrozenValue* regExp); | 
|  | void convertToRegExpMatchFastGlobalWithoutChecks(FrozenValue* regExp); | 
|  |  | 
|  | void convertToSetRegExpObjectLastIndex() | 
|  | { | 
|  | setOp(SetRegExpObjectLastIndex); | 
|  | m_opInfo = false; | 
|  | } | 
|  |  | 
|  | void convertToInById(CacheableIdentifier identifier) | 
|  | { | 
|  | ASSERT(m_op == InByVal); | 
|  | setOpAndDefaultFlags(InById); | 
|  | children.setChild2(Edge()); | 
|  | m_opInfo = identifier; | 
|  | m_opInfo2 = OpInfoWrapper(); | 
|  | } | 
|  |  | 
|  | JSValue asJSValue() | 
|  | { | 
|  | return constant()->value(); | 
|  | } | 
|  |  | 
|  | bool isInt32Constant() | 
|  | { | 
|  | return isConstant() && constant()->value().isInt32(); | 
|  | } | 
|  |  | 
|  | int32_t asInt32() | 
|  | { | 
|  | return asJSValue().asInt32(); | 
|  | } | 
|  |  | 
|  | uint32_t asUInt32() | 
|  | { | 
|  | return asInt32(); | 
|  | } | 
|  |  | 
|  | bool isDoubleConstant() | 
|  | { | 
|  | return isConstant() && constant()->value().isDouble(); | 
|  | } | 
|  |  | 
|  | bool isNumberConstant() | 
|  | { | 
|  | return isConstant() && constant()->value().isNumber(); | 
|  | } | 
|  |  | 
|  | double asNumber() | 
|  | { | 
|  | return asJSValue().asNumber(); | 
|  | } | 
|  |  | 
|  | bool isAnyIntConstant() | 
|  | { | 
|  | return isConstant() && constant()->value().isAnyInt(); | 
|  | } | 
|  |  | 
|  | int64_t asAnyInt() | 
|  | { | 
|  | return asJSValue().asAnyInt(); | 
|  | } | 
|  |  | 
|  | bool isBooleanConstant() | 
|  | { | 
|  | return isConstant() && constant()->value().isBoolean(); | 
|  | } | 
|  |  | 
|  | bool asBoolean() | 
|  | { | 
|  | return constant()->value().asBoolean(); | 
|  | } | 
|  |  | 
|  | bool isUndefinedOrNullConstant() | 
|  | { | 
|  | return isConstant() && constant()->value().isUndefinedOrNull(); | 
|  | } | 
|  |  | 
|  | bool isCellConstant() | 
|  | { | 
|  | return isConstant() && constant()->value() && constant()->value().isCell(); | 
|  | } | 
|  |  | 
|  | JSCell* asCell() | 
|  | { | 
|  | return constant()->value().asCell(); | 
|  | } | 
|  |  | 
|  | template<typename T> | 
|  | T dynamicCastConstant(VM& vm) | 
|  | { | 
|  | if (!isCellConstant()) | 
|  | return nullptr; | 
|  | return jsDynamicCast<T>(vm, asCell()); | 
|  | } | 
|  |  | 
|  | bool hasLazyJSValue() | 
|  | { | 
|  | return op() == LazyJSConstant; | 
|  | } | 
|  |  | 
|  | LazyJSValue lazyJSValue() | 
|  | { | 
|  | ASSERT(hasLazyJSValue()); | 
|  | return *m_opInfo.as<LazyJSValue*>(); | 
|  | } | 
|  |  | 
|  | String tryGetString(Graph&); | 
|  |  | 
|  | JSValue initializationValueForActivation() const | 
|  | { | 
|  | ASSERT(op() == CreateActivation); | 
|  | return m_opInfo2.as<FrozenValue*>()->value(); | 
|  | } | 
|  |  | 
|  | bool hasArgumentsChild() | 
|  | { | 
|  | switch (op()) { | 
|  | case GetMyArgumentByVal: | 
|  | case GetMyArgumentByValOutOfBounds: | 
|  | case VarargsLength: | 
|  | 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 VarargsLength: | 
|  | return child1(); | 
|  | case LoadVarargs: | 
|  | case ForwardVarargs: | 
|  | return child2(); | 
|  | 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()) { | 
|  | case MovHint: | 
|  | case ZombieHint: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool hasVariableAccessData(Graph&); | 
|  | bool accessesStack(Graph& graph) | 
|  | { | 
|  | return hasVariableAccessData(graph); | 
|  | } | 
|  |  | 
|  | // This is useful for debugging code, where a node that should have a variable | 
|  | // access data doesn't have one because it hasn't been initialized yet. | 
|  | VariableAccessData* tryGetVariableAccessData() | 
|  | { | 
|  | VariableAccessData* result = m_opInfo.as<VariableAccessData*>(); | 
|  | if (!result) | 
|  | return nullptr; | 
|  | return result->find(); | 
|  | } | 
|  |  | 
|  | VariableAccessData* variableAccessData() | 
|  | { | 
|  | return m_opInfo.as<VariableAccessData*>()->find(); | 
|  | } | 
|  |  | 
|  | Operand operand() | 
|  | { | 
|  | return variableAccessData()->operand(); | 
|  | } | 
|  |  | 
|  | VirtualRegister machineLocal() | 
|  | { | 
|  | return variableAccessData()->machineLocal(); | 
|  | } | 
|  |  | 
|  | bool hasUnlinkedOperand() | 
|  | { | 
|  | switch (op()) { | 
|  | case ExtractOSREntryLocal: | 
|  | case MovHint: | 
|  | case ZombieHint: | 
|  | case KillStack: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | Operand unlinkedOperand() | 
|  | { | 
|  | ASSERT(hasUnlinkedOperand()); | 
|  | return Operand::fromBits(m_opInfo.as<uint64_t>()); | 
|  | } | 
|  |  | 
|  | bool hasStackAccessData() | 
|  | { | 
|  | switch (op()) { | 
|  | case PutStack: | 
|  | case GetStack: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | StackAccessData* stackAccessData() | 
|  | { | 
|  | ASSERT(hasStackAccessData()); | 
|  | return m_opInfo.as<StackAccessData*>(); | 
|  | } | 
|  |  | 
|  | unsigned argumentCountIncludingThis() | 
|  | { | 
|  | ASSERT(op() == SetArgumentCountIncludingThis); | 
|  | return m_opInfo.as<unsigned>(); | 
|  | } | 
|  |  | 
|  | bool hasPhi() | 
|  | { | 
|  | return op() == Upsilon; | 
|  | } | 
|  |  | 
|  | Node* phi() | 
|  | { | 
|  | ASSERT(hasPhi()); | 
|  | return m_opInfo.as<Node*>(); | 
|  | } | 
|  |  | 
|  | bool isStoreBarrier() | 
|  | { | 
|  | return op() == StoreBarrier || op() == FencedStoreBarrier; | 
|  | } | 
|  |  | 
|  | bool hasCacheableIdentifier() | 
|  | { | 
|  | switch (op()) { | 
|  | case TryGetById: | 
|  | case GetById: | 
|  | case GetByIdFlush: | 
|  | case GetByIdWithThis: | 
|  | case GetByIdDirect: | 
|  | case GetByIdDirectFlush: | 
|  | case DeleteById: | 
|  | case InById: | 
|  | case PutById: | 
|  | case PutByIdFlush: | 
|  | case PutByIdDirect: | 
|  | case PutByIdWithThis: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | CacheableIdentifier cacheableIdentifier() | 
|  | { | 
|  | ASSERT(hasCacheableIdentifier()); | 
|  | return CacheableIdentifier::createFromRawBits(m_opInfo.as<uintptr_t>()); | 
|  | } | 
|  |  | 
|  | bool hasIdentifier() | 
|  | { | 
|  | switch (op()) { | 
|  | case PutGetterById: | 
|  | case PutSetterById: | 
|  | case PutGetterSetterById: | 
|  | case GetDynamicVar: | 
|  | case PutDynamicVar: | 
|  | case ResolveScopeForHoistingFuncDeclInEval: | 
|  | case ResolveScope: | 
|  | case ToObject: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | unsigned identifierNumber() | 
|  | { | 
|  | ASSERT(hasIdentifier()); | 
|  | return m_opInfo.as<unsigned>(); | 
|  | } | 
|  |  | 
|  | bool hasGetPutInfo() | 
|  | { | 
|  | switch (op()) { | 
|  | case GetDynamicVar: | 
|  | case PutDynamicVar: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | unsigned getPutInfo() | 
|  | { | 
|  | ASSERT(hasGetPutInfo()); | 
|  | return static_cast<unsigned>(m_opInfo.as<uint64_t>() >> 32); | 
|  | } | 
|  |  | 
|  | bool hasAccessorAttributes() | 
|  | { | 
|  | switch (op()) { | 
|  | case PutGetterById: | 
|  | case PutSetterById: | 
|  | case PutGetterSetterById: | 
|  | case PutGetterByVal: | 
|  | case PutSetterByVal: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | int32_t accessorAttributes() | 
|  | { | 
|  | ASSERT(hasAccessorAttributes()); | 
|  | switch (op()) { | 
|  | case PutGetterById: | 
|  | case PutSetterById: | 
|  | case PutGetterSetterById: | 
|  | return m_opInfo2.as<int32_t>(); | 
|  | case PutGetterByVal: | 
|  | case PutSetterByVal: | 
|  | return m_opInfo.as<int32_t>(); | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool hasPromotedLocationDescriptor() | 
|  | { | 
|  | return op() == PutHint; | 
|  | } | 
|  |  | 
|  | PromotedLocationDescriptor promotedLocationDescriptor(); | 
|  |  | 
|  | // This corrects the arithmetic node flags, so that irrelevant bits are | 
|  | // ignored. In particular, anything other than ArithMul or ValueMul does not need | 
|  | // to know if it can speculate on negative zero. | 
|  | NodeFlags arithNodeFlags() | 
|  | { | 
|  | NodeFlags result = m_flags & NodeArithFlagsMask; | 
|  | if (op() == ArithMul || op() == ArithDiv || op() == ValueDiv || op() == ArithMod || op() == ArithNegate || op() == ArithPow || op() == ArithRound || op() == ArithFloor || op() == ArithCeil || op() == ArithTrunc || op() == DoubleAsInt32 || op() == ValueNegate || op() == ValueMul || op() == ValueDiv) | 
|  | return result; | 
|  | return result & ~NodeBytecodeNeedsNegZero; | 
|  | } | 
|  |  | 
|  | bool mayHaveNonIntResult() | 
|  | { | 
|  | return m_flags & NodeMayHaveNonIntResult; | 
|  | } | 
|  |  | 
|  | bool mayHaveDoubleResult() | 
|  | { | 
|  | return m_flags & NodeMayHaveDoubleResult; | 
|  | } | 
|  |  | 
|  | bool mayHaveNonNumericResult() | 
|  | { | 
|  | return m_flags & NodeMayHaveNonNumericResult; | 
|  | } | 
|  |  | 
|  | bool mayHaveBigInt32Result() | 
|  | { | 
|  | return m_flags & NodeMayHaveBigInt32Result; | 
|  | } | 
|  |  | 
|  | bool mayHaveHeapBigIntResult() | 
|  | { | 
|  | return m_flags & NodeMayHaveHeapBigIntResult; | 
|  | } | 
|  |  | 
|  | bool mayHaveBigIntResult() | 
|  | { | 
|  | return mayHaveBigInt32Result() || mayHaveHeapBigIntResult(); | 
|  | } | 
|  |  | 
|  | bool hasNewArrayBufferData() | 
|  | { | 
|  | return op() == NewArrayBuffer || op() == PhantomNewArrayBuffer; | 
|  | } | 
|  |  | 
|  | NewArrayBufferData newArrayBufferData() | 
|  | { | 
|  | ASSERT(hasNewArrayBufferData()); | 
|  | return m_opInfo2.asNewArrayBufferData(); | 
|  | } | 
|  |  | 
|  | unsigned hasVectorLengthHint() | 
|  | { | 
|  | switch (op()) { | 
|  | case NewArray: | 
|  | case NewArrayBuffer: | 
|  | case PhantomNewArrayBuffer: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | unsigned vectorLengthHint() | 
|  | { | 
|  | ASSERT(hasVectorLengthHint()); | 
|  | if (op() == NewArray) | 
|  | return m_opInfo2.as<unsigned>(); | 
|  | return newArrayBufferData().vectorLengthHint; | 
|  | } | 
|  |  | 
|  | bool hasIndexingType() | 
|  | { | 
|  | switch (op()) { | 
|  | case NewArray: | 
|  | case NewArrayWithSize: | 
|  | case NewArrayBuffer: | 
|  | case PhantomNewArrayBuffer: | 
|  | return true; | 
|  | default: | 
|  | 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 || op() == PhantomNewArrayBuffer) | 
|  | return static_cast<IndexingType>(newArrayBufferData().indexingMode) & IndexingTypeMask; | 
|  | return static_cast<IndexingType>(m_opInfo.as<uint32_t>()); | 
|  | } | 
|  |  | 
|  | IndexingType indexingMode() | 
|  | { | 
|  | ASSERT(hasIndexingType()); | 
|  | if (op() == NewArrayBuffer || op() == PhantomNewArrayBuffer) | 
|  | return static_cast<IndexingType>(newArrayBufferData().indexingMode); | 
|  | return static_cast<IndexingType>(m_opInfo.as<uint32_t>()); | 
|  | } | 
|  |  | 
|  | bool hasTypedArrayType() | 
|  | { | 
|  | switch (op()) { | 
|  | case NewTypedArray: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | TypedArrayType typedArrayType() | 
|  | { | 
|  | ASSERT(hasTypedArrayType()); | 
|  | TypedArrayType result = static_cast<TypedArrayType>(m_opInfo.as<uint32_t>()); | 
|  | ASSERT(isTypedView(result)); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | bool hasInlineCapacity() | 
|  | { | 
|  | return op() == CreateThis; | 
|  | } | 
|  |  | 
|  | unsigned inlineCapacity() | 
|  | { | 
|  | ASSERT(hasInlineCapacity()); | 
|  | return m_opInfo.as<unsigned>(); | 
|  | } | 
|  |  | 
|  | bool hasIsInternalPromise() | 
|  | { | 
|  | return op() == CreatePromise; | 
|  | } | 
|  |  | 
|  | bool isInternalPromise() | 
|  | { | 
|  | ASSERT(hasIsInternalPromise()); | 
|  | return m_opInfo2.as<bool>(); | 
|  | } | 
|  |  | 
|  | void setIndexingType(IndexingType indexingType) | 
|  | { | 
|  | ASSERT(hasIndexingType()); | 
|  | m_opInfo = indexingType; | 
|  | } | 
|  |  | 
|  | bool hasScopeOffset() | 
|  | { | 
|  | return op() == GetClosureVar || op() == PutClosureVar; | 
|  | } | 
|  |  | 
|  | ScopeOffset scopeOffset() | 
|  | { | 
|  | ASSERT(hasScopeOffset()); | 
|  | return ScopeOffset(m_opInfo.as<uint32_t>()); | 
|  | } | 
|  |  | 
|  | unsigned hasInternalFieldIndex() | 
|  | { | 
|  | return op() == GetInternalField || op() == PutInternalField; | 
|  | } | 
|  |  | 
|  | unsigned internalFieldIndex() | 
|  | { | 
|  | ASSERT(hasInternalFieldIndex()); | 
|  | return m_opInfo.as<uint32_t>(); | 
|  | } | 
|  |  | 
|  | bool hasDirectArgumentsOffset() | 
|  | { | 
|  | return op() == GetFromArguments || op() == PutToArguments; | 
|  | } | 
|  |  | 
|  | DirectArgumentsOffset capturedArgumentsOffset() | 
|  | { | 
|  | ASSERT(hasDirectArgumentsOffset()); | 
|  | return DirectArgumentsOffset(m_opInfo.as<uint32_t>()); | 
|  | } | 
|  |  | 
|  | bool hasRegisterPointer() | 
|  | { | 
|  | return op() == GetGlobalVar || op() == GetGlobalLexicalVariable || op() == PutGlobalVariable; | 
|  | } | 
|  |  | 
|  | WriteBarrier<Unknown>* variablePointer() | 
|  | { | 
|  | return m_opInfo.as<WriteBarrier<Unknown>*>(); | 
|  | } | 
|  |  | 
|  | bool hasCallVarargsData() | 
|  | { | 
|  | switch (op()) { | 
|  | case CallVarargs: | 
|  | case CallForwardVarargs: | 
|  | case TailCallVarargs: | 
|  | case TailCallForwardVarargs: | 
|  | case TailCallVarargsInlinedCaller: | 
|  | case TailCallForwardVarargsInlinedCaller: | 
|  | case ConstructVarargs: | 
|  | case ConstructForwardVarargs: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | CallVarargsData* callVarargsData() | 
|  | { | 
|  | ASSERT(hasCallVarargsData()); | 
|  | return m_opInfo.as<CallVarargsData*>(); | 
|  | } | 
|  |  | 
|  | bool hasLoadVarargsData() | 
|  | { | 
|  | return op() == LoadVarargs || op() == ForwardVarargs || op() == VarargsLength; | 
|  | } | 
|  |  | 
|  | LoadVarargsData* loadVarargsData() | 
|  | { | 
|  | ASSERT(hasLoadVarargsData()); | 
|  | return m_opInfo.as<LoadVarargsData*>(); | 
|  | } | 
|  |  | 
|  | InlineCallFrame* argumentsInlineCallFrame() | 
|  | { | 
|  | ASSERT(op() == GetArgumentCountIncludingThis); | 
|  | return m_opInfo.as<InlineCallFrame*>(); | 
|  | } | 
|  |  | 
|  | bool hasQueriedType() | 
|  | { | 
|  | 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 hasSpeculatedTypeForQuery() | 
|  | { | 
|  | return op() == IsCellWithType; | 
|  | } | 
|  |  | 
|  | Optional<SpeculatedType> speculatedTypeForQuery() | 
|  | { | 
|  | return speculationFromJSType(queriedType()); | 
|  | } | 
|  |  | 
|  | bool hasResult() | 
|  | { | 
|  | return !!result(); | 
|  | } | 
|  |  | 
|  | bool hasInt32Result() | 
|  | { | 
|  | return result() == NodeResultInt32; | 
|  | } | 
|  |  | 
|  | bool hasInt52Result() | 
|  | { | 
|  | return result() == NodeResultInt52; | 
|  | } | 
|  |  | 
|  | bool hasNumberResult() | 
|  | { | 
|  | return result() == NodeResultNumber; | 
|  | } | 
|  |  | 
|  | bool hasNumberOrAnyIntResult() | 
|  | { | 
|  | return hasNumberResult() || hasInt32Result() || hasInt52Result(); | 
|  | } | 
|  |  | 
|  | bool hasNumericResult() | 
|  | { | 
|  | switch (op()) { | 
|  | case ValueSub: | 
|  | case ValueMul: | 
|  | case ValueBitAnd: | 
|  | case ValueBitOr: | 
|  | case ValueBitXor: | 
|  | case ValueBitNot: | 
|  | case ValueBitLShift: | 
|  | case ValueBitRShift: | 
|  | case ValueNegate: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool hasDoubleResult() | 
|  | { | 
|  | return result() == NodeResultDouble; | 
|  | } | 
|  |  | 
|  | bool hasJSResult() | 
|  | { | 
|  | return result() == NodeResultJS; | 
|  | } | 
|  |  | 
|  | bool hasBooleanResult() | 
|  | { | 
|  | return result() == NodeResultBoolean; | 
|  | } | 
|  |  | 
|  | bool hasStorageResult() | 
|  | { | 
|  | return result() == NodeResultStorage; | 
|  | } | 
|  |  | 
|  | UseKind defaultUseKind() | 
|  | { | 
|  | return useKindForResult(result()); | 
|  | } | 
|  |  | 
|  | Edge defaultEdge() | 
|  | { | 
|  | return Edge(this, defaultUseKind()); | 
|  | } | 
|  |  | 
|  | bool isJump() | 
|  | { | 
|  | return op() == Jump; | 
|  | } | 
|  |  | 
|  | bool isBranch() | 
|  | { | 
|  | return op() == Branch; | 
|  | } | 
|  |  | 
|  | bool isSwitch() const | 
|  | { | 
|  | return op() == Switch; | 
|  | } | 
|  |  | 
|  | bool isEntrySwitch() const | 
|  | { | 
|  | return op() == EntrySwitch; | 
|  | } | 
|  |  | 
|  | bool isTerminal() | 
|  | { | 
|  | switch (op()) { | 
|  | case Jump: | 
|  | case Branch: | 
|  | case Switch: | 
|  | case EntrySwitch: | 
|  | case Return: | 
|  | case TailCall: | 
|  | case DirectTailCall: | 
|  | case TailCallVarargs: | 
|  | case TailCallForwardVarargs: | 
|  | case Unreachable: | 
|  | case Throw: | 
|  | case ThrowStaticError: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool isFunctionTerminal() | 
|  | { | 
|  | if (isTerminal() && !numSuccessors()) | 
|  | return true; | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // As is described in DFGNodeType.h's ForceOSRExit, this is a pseudo-terminal. | 
|  | // It means that execution should fall out of DFG at this point, but execution | 
|  | // does continue in the basic block - just in a different compiler. | 
|  | // FIXME: This is used for lightweight reachability decision. But this should | 
|  | // be replaced with AI-based reachability ideally. | 
|  | bool isPseudoTerminal() | 
|  | { | 
|  | switch (op()) { | 
|  | case ForceOSRExit: | 
|  | case CheckBadValue: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | unsigned targetBytecodeOffsetDuringParsing() | 
|  | { | 
|  | ASSERT(isJump()); | 
|  | return m_opInfo.as<unsigned>(); | 
|  | } | 
|  |  | 
|  | BasicBlock*& targetBlock() | 
|  | { | 
|  | ASSERT(isJump()); | 
|  | return *bitwise_cast<BasicBlock**>(&m_opInfo.u.pointer); | 
|  | } | 
|  |  | 
|  | BranchData* branchData() | 
|  | { | 
|  | ASSERT(isBranch()); | 
|  | return m_opInfo.as<BranchData*>(); | 
|  | } | 
|  |  | 
|  | SwitchData* switchData() | 
|  | { | 
|  | ASSERT(isSwitch()); | 
|  | return m_opInfo.as<SwitchData*>(); | 
|  | } | 
|  |  | 
|  | EntrySwitchData* entrySwitchData() | 
|  | { | 
|  | ASSERT(isEntrySwitch()); | 
|  | return m_opInfo.as<EntrySwitchData*>(); | 
|  | } | 
|  |  | 
|  | bool hasIntrinsic() | 
|  | { | 
|  | switch (op()) { | 
|  | case CPUIntrinsic: | 
|  | case DateGetTime: | 
|  | case DateGetInt32OrNaN: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | Intrinsic intrinsic() | 
|  | { | 
|  | ASSERT(hasIntrinsic()); | 
|  | return m_opInfo.as<Intrinsic>(); | 
|  | } | 
|  |  | 
|  | unsigned numSuccessors() | 
|  | { | 
|  | switch (op()) { | 
|  | case Jump: | 
|  | return 1; | 
|  | case Branch: | 
|  | return 2; | 
|  | case Switch: | 
|  | return switchData()->cases.size() + 1; | 
|  | case EntrySwitch: | 
|  | return entrySwitchData()->cases.size(); | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | BasicBlock*& successor(unsigned index) | 
|  | { | 
|  | if (isSwitch()) { | 
|  | if (index < switchData()->cases.size()) | 
|  | return switchData()->cases[index].target.block; | 
|  | RELEASE_ASSERT(index == switchData()->cases.size()); | 
|  | return switchData()->fallThrough.block; | 
|  | } else if (isEntrySwitch()) | 
|  | return entrySwitchData()->cases[index]; | 
|  |  | 
|  | switch (index) { | 
|  | case 0: | 
|  | if (isJump()) | 
|  | return targetBlock(); | 
|  | return branchData()->taken.block; | 
|  | case 1: | 
|  | return branchData()->notTaken.block; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return targetBlock(); | 
|  | } | 
|  | } | 
|  |  | 
|  | class SuccessorsIterable { | 
|  | public: | 
|  | SuccessorsIterable() | 
|  | : m_terminal(nullptr) | 
|  | { | 
|  | } | 
|  |  | 
|  | SuccessorsIterable(Node* terminal) | 
|  | : m_terminal(terminal) | 
|  | { | 
|  | } | 
|  |  | 
|  | class iterator { | 
|  | public: | 
|  | iterator() | 
|  | : m_terminal(nullptr) | 
|  | , m_index(UINT_MAX) | 
|  | { | 
|  | } | 
|  |  | 
|  | iterator(Node* terminal, unsigned index) | 
|  | : m_terminal(terminal) | 
|  | , m_index(index) | 
|  | { | 
|  | } | 
|  |  | 
|  | BasicBlock* operator*() | 
|  | { | 
|  | return m_terminal->successor(m_index); | 
|  | } | 
|  |  | 
|  | iterator& operator++() | 
|  | { | 
|  | m_index++; | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | bool operator==(const iterator& other) const | 
|  | { | 
|  | return m_index == other.m_index; | 
|  | } | 
|  |  | 
|  | bool operator!=(const iterator& other) const | 
|  | { | 
|  | return !(*this == other); | 
|  | } | 
|  | private: | 
|  | Node* m_terminal; | 
|  | unsigned m_index; | 
|  | }; | 
|  |  | 
|  | iterator begin() | 
|  | { | 
|  | return iterator(m_terminal, 0); | 
|  | } | 
|  |  | 
|  | iterator end() | 
|  | { | 
|  | return iterator(m_terminal, m_terminal->numSuccessors()); | 
|  | } | 
|  |  | 
|  | size_t size() const { return m_terminal->numSuccessors(); } | 
|  | BasicBlock* at(size_t index) const { return m_terminal->successor(index); } | 
|  | BasicBlock* operator[](size_t index) const { return at(index); } | 
|  |  | 
|  | private: | 
|  | Node* m_terminal; | 
|  | }; | 
|  |  | 
|  | SuccessorsIterable successors() | 
|  | { | 
|  | return SuccessorsIterable(this); | 
|  | } | 
|  |  | 
|  | BasicBlock*& successorForCondition(bool condition) | 
|  | { | 
|  | return branchData()->forCondition(condition); | 
|  | } | 
|  |  | 
|  | bool hasHeapPrediction() | 
|  | { | 
|  | switch (op()) { | 
|  | case ArithAbs: | 
|  | case ArithRound: | 
|  | case ArithFloor: | 
|  | case ArithCeil: | 
|  | case ArithTrunc: | 
|  | case GetDirectPname: | 
|  | case GetById: | 
|  | case GetByIdFlush: | 
|  | case GetByIdWithThis: | 
|  | case GetByIdDirect: | 
|  | case GetByIdDirectFlush: | 
|  | case GetPrototypeOf: | 
|  | 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: | 
|  | case TailCallForwardVarargsInlinedCaller: | 
|  | case GetByOffset: | 
|  | case MultiGetByOffset: | 
|  | case GetClosureVar: | 
|  | case GetInternalField: | 
|  | case GetFromArguments: | 
|  | case GetArgument: | 
|  | case ArrayPop: | 
|  | case ArrayPush: | 
|  | case RegExpExec: | 
|  | case RegExpExecNonGlobalOrSticky: | 
|  | case RegExpTest: | 
|  | case RegExpMatchFast: | 
|  | case RegExpMatchFastGlobal: | 
|  | case GetGlobalVar: | 
|  | case GetGlobalLexicalVariable: | 
|  | case StringReplace: | 
|  | case StringReplaceRegExp: | 
|  | case ToNumber: | 
|  | case ToNumeric: | 
|  | case ToObject: | 
|  | case CallNumberConstructor: | 
|  | case ValueBitAnd: | 
|  | case ValueBitOr: | 
|  | case ValueBitXor: | 
|  | case ValueBitNot: | 
|  | case ValueBitLShift: | 
|  | case ValueBitRShift: | 
|  | case CallObjectConstructor: | 
|  | case LoadKeyFromMapBucket: | 
|  | case LoadValueFromMapBucket: | 
|  | case CallDOMGetter: | 
|  | case CallDOM: | 
|  | case ParseInt: | 
|  | case AtomicsAdd: | 
|  | case AtomicsAnd: | 
|  | case AtomicsCompareExchange: | 
|  | case AtomicsExchange: | 
|  | case AtomicsLoad: | 
|  | case AtomicsOr: | 
|  | case AtomicsStore: | 
|  | case AtomicsSub: | 
|  | case AtomicsXor: | 
|  | case GetDynamicVar: | 
|  | case ExtractValueFromWeakMapGet: | 
|  | case ToThis: | 
|  | case DataViewGetInt: | 
|  | case DataViewGetFloat: | 
|  | case DateGetInt32OrNaN: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | SpeculatedType getHeapPrediction() | 
|  | { | 
|  | ASSERT(hasHeapPrediction()); | 
|  | return m_opInfo2.as<SpeculatedType>(); | 
|  | } | 
|  |  | 
|  | void setHeapPrediction(SpeculatedType prediction) | 
|  | { | 
|  | ASSERT(hasHeapPrediction()); | 
|  | m_opInfo2 = prediction; | 
|  | } | 
|  |  | 
|  | SpeculatedType getForcedPrediction() | 
|  | { | 
|  | ASSERT(op() == IdentityWithProfile); | 
|  | return m_opInfo.as<SpeculatedType>(); | 
|  | } | 
|  |  | 
|  | uint32_t catchOSREntryIndex() const | 
|  | { | 
|  | ASSERT(op() == ExtractCatchLocal); | 
|  | return m_opInfo.as<uint32_t>(); | 
|  | } | 
|  |  | 
|  | SpeculatedType catchLocalPrediction() | 
|  | { | 
|  | ASSERT(op() == ExtractCatchLocal); | 
|  | return m_opInfo2.as<SpeculatedType>(); | 
|  | } | 
|  |  | 
|  | bool hasCellOperand() | 
|  | { | 
|  | switch (op()) { | 
|  | case CheckIsConstant: | 
|  | return isCell(child1().useKind()); | 
|  | case OverridesHasInstance: | 
|  | case NewFunction: | 
|  | case NewGeneratorFunction: | 
|  | case NewAsyncFunction: | 
|  | case NewAsyncGeneratorFunction: | 
|  | case CreateActivation: | 
|  | case MaterializeCreateActivation: | 
|  | case NewRegexp: | 
|  | case NewArrayBuffer: | 
|  | case PhantomNewArrayBuffer: | 
|  | case CompareEqPtr: | 
|  | case CallObjectConstructor: | 
|  | case DirectCall: | 
|  | case DirectTailCall: | 
|  | case DirectConstruct: | 
|  | case DirectTailCallInlinedCaller: | 
|  | case RegExpExecNonGlobalOrSticky: | 
|  | case RegExpMatchFastGlobal: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | FrozenValue* cellOperand() | 
|  | { | 
|  | ASSERT(hasCellOperand()); | 
|  | return m_opInfo.as<FrozenValue*>(); | 
|  | } | 
|  |  | 
|  | template<typename T> | 
|  | T castOperand() | 
|  | { | 
|  | return cellOperand()->cast<T>(); | 
|  | } | 
|  |  | 
|  | void setCellOperand(FrozenValue* value) | 
|  | { | 
|  | ASSERT(hasCellOperand()); | 
|  | m_opInfo = value; | 
|  | } | 
|  |  | 
|  | bool hasWatchpointSet() | 
|  | { | 
|  | return op() == NotifyWrite; | 
|  | } | 
|  |  | 
|  | WatchpointSet* watchpointSet() | 
|  | { | 
|  | ASSERT(hasWatchpointSet()); | 
|  | return m_opInfo.as<WatchpointSet*>(); | 
|  | } | 
|  |  | 
|  | bool hasStoragePointer() | 
|  | { | 
|  | return op() == ConstantStoragePointer; | 
|  | } | 
|  |  | 
|  | void* storagePointer() | 
|  | { | 
|  | ASSERT(hasStoragePointer()); | 
|  | return m_opInfo.as<void*>(); | 
|  | } | 
|  |  | 
|  | bool hasUidOperand() | 
|  | { | 
|  | return op() == CheckIdent; | 
|  | } | 
|  |  | 
|  | UniquedStringImpl* uidOperand() | 
|  | { | 
|  | ASSERT(hasUidOperand()); | 
|  | 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() | 
|  | { | 
|  | switch (op()) { | 
|  | case PutStructure: | 
|  | case AllocatePropertyStorage: | 
|  | case ReallocatePropertyStorage: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | Transition* transition() | 
|  | { | 
|  | ASSERT(hasTransition()); | 
|  | return m_opInfo.as<Transition*>(); | 
|  | } | 
|  |  | 
|  | bool hasStructureSet() | 
|  | { | 
|  | switch (op()) { | 
|  | case CheckStructure: | 
|  | case CheckStructureOrEmpty: | 
|  | case CheckStructureImmediate: | 
|  | case MaterializeNewObject: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | const RegisteredStructureSet& structureSet() | 
|  | { | 
|  | ASSERT(hasStructureSet()); | 
|  | return *m_opInfo.as<RegisteredStructureSet*>(); | 
|  | } | 
|  |  | 
|  | bool hasStructure() | 
|  | { | 
|  | switch (op()) { | 
|  | case ArrayifyToStructure: | 
|  | case MaterializeNewInternalFieldObject: | 
|  | case NewObject: | 
|  | case NewGenerator: | 
|  | case NewAsyncGenerator: | 
|  | case NewInternalFieldObject: | 
|  | case NewStringObject: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | RegisteredStructure structure() | 
|  | { | 
|  | ASSERT(hasStructure()); | 
|  | return m_opInfo.asRegisteredStructure(); | 
|  | } | 
|  |  | 
|  | bool hasStorageAccessData() | 
|  | { | 
|  | switch (op()) { | 
|  | case GetByOffset: | 
|  | case PutByOffset: | 
|  | case GetGetterSetterByOffset: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | StorageAccessData& storageAccessData() | 
|  | { | 
|  | ASSERT(hasStorageAccessData()); | 
|  | return *m_opInfo.as<StorageAccessData*>(); | 
|  | } | 
|  |  | 
|  | bool hasMultiGetByOffsetData() | 
|  | { | 
|  | return op() == MultiGetByOffset; | 
|  | } | 
|  |  | 
|  | MultiGetByOffsetData& multiGetByOffsetData() | 
|  | { | 
|  | ASSERT(hasMultiGetByOffsetData()); | 
|  | return *m_opInfo.as<MultiGetByOffsetData*>(); | 
|  | } | 
|  |  | 
|  | bool hasMultiPutByOffsetData() | 
|  | { | 
|  | return op() == MultiPutByOffset; | 
|  | } | 
|  |  | 
|  | MultiPutByOffsetData& multiPutByOffsetData() | 
|  | { | 
|  | ASSERT(hasMultiPutByOffsetData()); | 
|  | return *m_opInfo.as<MultiPutByOffsetData*>(); | 
|  | } | 
|  |  | 
|  | bool hasMultiDeleteByOffsetData() | 
|  | { | 
|  | return op() == MultiDeleteByOffset; | 
|  | } | 
|  |  | 
|  | MultiDeleteByOffsetData& multiDeleteByOffsetData() | 
|  | { | 
|  | ASSERT(hasMultiDeleteByOffsetData()); | 
|  | return *m_opInfo.as<MultiDeleteByOffsetData*>(); | 
|  | } | 
|  |  | 
|  | bool hasMatchStructureData() | 
|  | { | 
|  | return op() == MatchStructure; | 
|  | } | 
|  |  | 
|  | MatchStructureData& matchStructureData() | 
|  | { | 
|  | ASSERT(hasMatchStructureData()); | 
|  | return *m_opInfo.as<MatchStructureData*>(); | 
|  | } | 
|  |  | 
|  | bool hasObjectMaterializationData() | 
|  | { | 
|  | switch (op()) { | 
|  | case MaterializeNewObject: | 
|  | case MaterializeNewInternalFieldObject: | 
|  | case MaterializeCreateActivation: | 
|  | return true; | 
|  |  | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | ObjectMaterializationData& objectMaterializationData() | 
|  | { | 
|  | ASSERT(hasObjectMaterializationData()); | 
|  | return *m_opInfo2.as<ObjectMaterializationData*>(); | 
|  | } | 
|  |  | 
|  | bool isObjectAllocation() | 
|  | { | 
|  | switch (op()) { | 
|  | case NewObject: | 
|  | case MaterializeNewObject: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool isPhantomObjectAllocation() | 
|  | { | 
|  | switch (op()) { | 
|  | case PhantomNewObject: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool isActivationAllocation() | 
|  | { | 
|  | switch (op()) { | 
|  | case CreateActivation: | 
|  | case MaterializeCreateActivation: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool isPhantomActivationAllocation() | 
|  | { | 
|  | switch (op()) { | 
|  | case PhantomCreateActivation: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool isFunctionAllocation() | 
|  | { | 
|  | switch (op()) { | 
|  | case NewFunction: | 
|  | case NewGeneratorFunction: | 
|  | case NewAsyncGeneratorFunction: | 
|  | case NewAsyncFunction: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool isPhantomFunctionAllocation() | 
|  | { | 
|  | switch (op()) { | 
|  | case PhantomNewFunction: | 
|  | case PhantomNewGeneratorFunction: | 
|  | case PhantomNewAsyncFunction: | 
|  | case PhantomNewAsyncGeneratorFunction: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool isPhantomAllocation() | 
|  | { | 
|  | switch (op()) { | 
|  | case PhantomNewObject: | 
|  | case PhantomDirectArguments: | 
|  | case PhantomCreateRest: | 
|  | case PhantomSpread: | 
|  | case PhantomNewArrayWithSpread: | 
|  | case PhantomNewArrayBuffer: | 
|  | case PhantomClonedArguments: | 
|  | case PhantomNewFunction: | 
|  | case PhantomNewGeneratorFunction: | 
|  | case PhantomNewAsyncFunction: | 
|  | case PhantomNewAsyncGeneratorFunction: | 
|  | case PhantomNewInternalFieldObject: | 
|  | case PhantomCreateActivation: | 
|  | case PhantomNewRegexp: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | bool hasArrayMode() | 
|  | { | 
|  | switch (op()) { | 
|  | case GetIndexedPropertyStorage: | 
|  | case GetArrayLength: | 
|  | case GetVectorLength: | 
|  | case InByVal: | 
|  | case PutByValDirect: | 
|  | case PutByVal: | 
|  | case PutByValAlias: | 
|  | case GetByVal: | 
|  | case StringCharAt: | 
|  | case StringCharCodeAt: | 
|  | case StringCodePointAt: | 
|  | case CheckArray: | 
|  | case CheckArrayOrEmpty: | 
|  | case Arrayify: | 
|  | case ArrayifyToStructure: | 
|  | case ArrayPush: | 
|  | case ArrayPop: | 
|  | case ArrayIndexOf: | 
|  | 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; | 
|  | } | 
|  | } | 
|  |  | 
|  | ArrayMode arrayMode() | 
|  | { | 
|  | ASSERT(hasArrayMode()); | 
|  | if (op() == ArrayifyToStructure) | 
|  | return ArrayMode::fromWord(m_opInfo2.as<uint32_t>()); | 
|  | return ArrayMode::fromWord(m_opInfo.as<uint32_t>()); | 
|  | } | 
|  |  | 
|  | bool setArrayMode(ArrayMode arrayMode) | 
|  | { | 
|  | ASSERT(hasArrayMode()); | 
|  | if (this->arrayMode() == arrayMode) | 
|  | return false; | 
|  | m_opInfo = arrayMode.asWord(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool hasECMAMode() | 
|  | { | 
|  | switch (op()) { | 
|  | case CallEval: | 
|  | case DeleteById: | 
|  | case DeleteByVal: | 
|  | case PutById: | 
|  | case PutByIdDirect: | 
|  | case PutByIdFlush: | 
|  | case PutByIdWithThis: | 
|  | case PutByVal: | 
|  | case PutByValAlias: | 
|  | case PutByValDirect: | 
|  | case PutByValWithThis: | 
|  | case PutDynamicVar: | 
|  | case ToThis: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | ECMAMode ecmaMode() | 
|  | { | 
|  | ASSERT(hasECMAMode()); | 
|  | switch (op()) { | 
|  | case CallEval: | 
|  | case DeleteByVal: | 
|  | case PutByValWithThis: | 
|  | case ToThis: | 
|  | return ECMAMode::fromByte(m_opInfo.as<uint8_t>()); | 
|  | case DeleteById: | 
|  | case PutById: | 
|  | case PutByIdDirect: | 
|  | case PutByIdFlush: | 
|  | case PutByIdWithThis: | 
|  | case PutByVal: | 
|  | case PutByValAlias: | 
|  | case PutByValDirect: | 
|  | case PutDynamicVar: | 
|  | return ECMAMode::fromByte(m_opInfo2.as<uint8_t>()); | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | return ECMAMode::strict(); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool hasArithMode() | 
|  | { | 
|  | switch (op()) { | 
|  | case ArithAbs: | 
|  | case ArithAdd: | 
|  | case ArithSub: | 
|  | case ArithNegate: | 
|  | case ArithMul: | 
|  | case ArithDiv: | 
|  | case ArithMod: | 
|  | case UInt32ToNumber: | 
|  | case DoubleAsInt32: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | Arith::Mode arithMode() | 
|  | { | 
|  | ASSERT(hasArithMode()); | 
|  | return static_cast<Arith::Mode>(m_opInfo.as<uint32_t>()); | 
|  | } | 
|  |  | 
|  | void setArithMode(Arith::Mode mode) | 
|  | { | 
|  | m_opInfo = mode; | 
|  | } | 
|  |  | 
|  | bool hasArithRoundingMode() | 
|  | { | 
|  | return op() == ArithRound || op() == ArithFloor || op() == ArithCeil || op() == ArithTrunc; | 
|  | } | 
|  |  | 
|  | Arith::RoundingMode arithRoundingMode() | 
|  | { | 
|  | ASSERT(hasArithRoundingMode()); | 
|  | return static_cast<Arith::RoundingMode>(m_opInfo.as<uint32_t>()); | 
|  | } | 
|  |  | 
|  | void setArithRoundingMode(Arith::RoundingMode mode) | 
|  | { | 
|  | ASSERT(hasArithRoundingMode()); | 
|  | m_opInfo = static_cast<uint32_t>(mode); | 
|  | } | 
|  |  | 
|  | bool hasArithUnaryType() | 
|  | { | 
|  | return op() == ArithUnary; | 
|  | } | 
|  |  | 
|  | Arith::UnaryType arithUnaryType() | 
|  | { | 
|  | ASSERT(hasArithUnaryType()); | 
|  | return static_cast<Arith::UnaryType>(m_opInfo.as<uint32_t>()); | 
|  | } | 
|  |  | 
|  | bool hasVirtualRegister() | 
|  | { | 
|  | return m_virtualRegister.isValid(); | 
|  | } | 
|  |  | 
|  | VirtualRegister virtualRegister() | 
|  | { | 
|  | ASSERT(hasResult()); | 
|  | ASSERT(m_virtualRegister.isValid()); | 
|  | return m_virtualRegister; | 
|  | } | 
|  |  | 
|  | void setVirtualRegister(VirtualRegister virtualRegister) | 
|  | { | 
|  | ASSERT(hasResult()); | 
|  | ASSERT(!m_virtualRegister.isValid()); | 
|  | m_virtualRegister = virtualRegister; | 
|  | } | 
|  |  | 
|  | bool hasExecutionCounter() | 
|  | { | 
|  | return op() == CountExecution; | 
|  | } | 
|  |  | 
|  | Profiler::ExecutionCounter* executionCounter() | 
|  | { | 
|  | return m_opInfo.as<Profiler::ExecutionCounter*>(); | 
|  | } | 
|  |  | 
|  | unsigned entrypointIndex() | 
|  | { | 
|  | ASSERT(op() == InitializeEntrypointArguments); | 
|  | return m_opInfo.as<unsigned>(); | 
|  | } | 
|  |  | 
|  | DataViewData dataViewData() | 
|  | { | 
|  | ASSERT(op() == DataViewGetInt || op() == DataViewGetFloat || op() == DataViewSet); | 
|  | return bitwise_cast<DataViewData>(m_opInfo.as<uint64_t>()); | 
|  | } | 
|  |  | 
|  | bool shouldGenerate() | 
|  | { | 
|  | 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 || op() == InvalidationPoint; | 
|  | } | 
|  |  | 
|  | unsigned refCount() | 
|  | { | 
|  | return m_refCount; | 
|  | } | 
|  |  | 
|  | unsigned postfixRef() | 
|  | { | 
|  | return m_refCount++; | 
|  | } | 
|  |  | 
|  | unsigned adjustedRefCount() | 
|  | { | 
|  | return mustGenerate() ? m_refCount - 1 : m_refCount; | 
|  | } | 
|  |  | 
|  | void setRefCount(unsigned refCount) | 
|  | { | 
|  | m_refCount = refCount; | 
|  | } | 
|  |  | 
|  | Edge& child1() | 
|  | { | 
|  | ASSERT(!(m_flags & NodeHasVarArgs)); | 
|  | return children.child1(); | 
|  | } | 
|  |  | 
|  | // This is useful if you want to do a fast check on the first child | 
|  | // before also doing a check on the opcode. Use this with care and | 
|  | // avoid it if possible. | 
|  | Edge child1Unchecked() | 
|  | { | 
|  | return children.child1Unchecked(); | 
|  | } | 
|  |  | 
|  | Edge& child2() | 
|  | { | 
|  | ASSERT(!(m_flags & NodeHasVarArgs)); | 
|  | return children.child2(); | 
|  | } | 
|  |  | 
|  | Edge& child3() | 
|  | { | 
|  | ASSERT(!(m_flags & NodeHasVarArgs)); | 
|  | return children.child3(); | 
|  | } | 
|  |  | 
|  | unsigned firstChild() | 
|  | { | 
|  | ASSERT(m_flags & NodeHasVarArgs); | 
|  | return children.firstChild(); | 
|  | } | 
|  |  | 
|  | unsigned numChildren() | 
|  | { | 
|  | ASSERT(m_flags & NodeHasVarArgs); | 
|  | return children.numChildren(); | 
|  | } | 
|  |  | 
|  | UseKind binaryUseKind() | 
|  | { | 
|  | ASSERT(child1().useKind() == child2().useKind()); | 
|  | return child1().useKind(); | 
|  | } | 
|  |  | 
|  | bool isBinaryUseKind(UseKind left, UseKind right) | 
|  | { | 
|  | return child1().useKind() == left && child2().useKind() == right; | 
|  | } | 
|  |  | 
|  | bool isBinaryUseKind(UseKind useKind) | 
|  | { | 
|  | return isBinaryUseKind(useKind, useKind); | 
|  | } | 
|  |  | 
|  | Edge childFor(UseKind useKind) | 
|  | { | 
|  | if (child1().useKind() == useKind) | 
|  | return child1(); | 
|  | if (child2().useKind() == useKind) | 
|  | return child2(); | 
|  | if (child3().useKind() == useKind) | 
|  | return child3(); | 
|  | return Edge(); | 
|  | } | 
|  |  | 
|  | SpeculatedType prediction() | 
|  | { | 
|  | return m_prediction; | 
|  | } | 
|  |  | 
|  | bool predict(SpeculatedType prediction) | 
|  | { | 
|  | return mergeSpeculation(m_prediction, prediction); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateInt32() | 
|  | { | 
|  | return isInt32Speculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateNotInt32() | 
|  | { | 
|  | return isNotInt32Speculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool sawBooleans() | 
|  | { | 
|  | return !!(prediction() & SpecBoolean); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateInt32OrBoolean() | 
|  | { | 
|  | return isInt32OrBooleanSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateInt32ForArithmetic() | 
|  | { | 
|  | return isInt32SpeculationForArithmetic(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateInt32OrBooleanForArithmetic() | 
|  | { | 
|  | return isInt32OrBooleanSpeculationForArithmetic(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateInt32OrBooleanExpectingDefined() | 
|  | { | 
|  | return isInt32OrBooleanSpeculationExpectingDefined(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateInt52() | 
|  | { | 
|  | // We have to include SpecInt32Only here for two reasons: | 
|  | // 1. We diligently write code that first checks if we should speculate Int32. | 
|  | // For example: | 
|  | // if (shouldSpeculateInt32()) ... | 
|  | // else if (shouldSpeculateInt52()) ... | 
|  | // This means we it's totally valid to speculate Int52 when we're dealing | 
|  | // with a type that's the union of Int32 and Int52. | 
|  | // | 
|  | // It would be a performance mistake to not include Int32 here because we obviously | 
|  | // have variables that are the union of Int32 and Int52 values, and it's better | 
|  | // to speculate Int52 than double in that situation. | 
|  | // | 
|  | // 2. We also write code where we ask if the inputs can be Int52, like if | 
|  | // we know via profiling that an Add overflows, we may not emit an Int32 add. | 
|  | // However, we only emit such an add if both inputs can be Int52, and Int32 | 
|  | // can trivially become Int52. | 
|  | // | 
|  | return enableInt52() && isInt32OrInt52Speculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateDouble() | 
|  | { | 
|  | return isDoubleSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateDoubleReal() | 
|  | { | 
|  | return isDoubleRealSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateNumber() | 
|  | { | 
|  | return isFullNumberSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateNumberOrBoolean() | 
|  | { | 
|  | return isFullNumberOrBooleanSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateNumberOrBooleanExpectingDefined() | 
|  | { | 
|  | return isFullNumberOrBooleanSpeculationExpectingDefined(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateBoolean() | 
|  | { | 
|  | return isBooleanSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateNotBoolean() | 
|  | { | 
|  | return isNotBooleanSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateOther() | 
|  | { | 
|  | return isOtherSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateMisc() | 
|  | { | 
|  | return isMiscSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateStringIdent() | 
|  | { | 
|  | return isStringIdentSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateNotStringVar() | 
|  | { | 
|  | return isNotStringVarSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateString() | 
|  | { | 
|  | return isStringSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateNotString() | 
|  | { | 
|  | return isNotStringSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateStringOrOther() | 
|  | { | 
|  | return isStringOrOtherSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateStringObject() | 
|  | { | 
|  | return isStringObjectSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateStringOrStringObject() | 
|  | { | 
|  | return isStringOrStringObjectSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateRegExpObject() | 
|  | { | 
|  | return isRegExpObjectSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateSymbol() | 
|  | { | 
|  | return isSymbolSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | bool shouldSpeculateBigInt32() | 
|  | { | 
|  | return isBigInt32Speculation(prediction()); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | bool shouldSpeculateHeapBigInt() | 
|  | { | 
|  | return isHeapBigIntSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateBigInt() | 
|  | { | 
|  | return isBigIntSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateFinalObject() | 
|  | { | 
|  | return isFinalObjectSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateFinalObjectOrOther() | 
|  | { | 
|  | return isFinalObjectOrOtherSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateArray() | 
|  | { | 
|  | return isArraySpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateFunction() | 
|  | { | 
|  | return isFunctionSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateProxyObject() | 
|  | { | 
|  | return isProxyObjectSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateDerivedArray() | 
|  | { | 
|  | return isDerivedArraySpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateDirectArguments() | 
|  | { | 
|  | return isDirectArgumentsSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateScopedArguments() | 
|  | { | 
|  | return isScopedArgumentsSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateInt8Array() | 
|  | { | 
|  | return isInt8ArraySpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateInt16Array() | 
|  | { | 
|  | return isInt16ArraySpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateInt32Array() | 
|  | { | 
|  | return isInt32ArraySpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateUint8Array() | 
|  | { | 
|  | return isUint8ArraySpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateUint8ClampedArray() | 
|  | { | 
|  | return isUint8ClampedArraySpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateUint16Array() | 
|  | { | 
|  | return isUint16ArraySpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateUint32Array() | 
|  | { | 
|  | return isUint32ArraySpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateFloat32Array() | 
|  | { | 
|  | return isFloat32ArraySpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateFloat64Array() | 
|  | { | 
|  | return isFloat64ArraySpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateArrayOrOther() | 
|  | { | 
|  | return isArrayOrOtherSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateObject() | 
|  | { | 
|  | return isObjectSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateObjectOrOther() | 
|  | { | 
|  | return isObjectOrOtherSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateCell() | 
|  | { | 
|  | return isCellSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateCellOrOther() | 
|  | { | 
|  | return isCellOrOtherSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateNotCell() | 
|  | { | 
|  | return isNotCellSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateNotCellNorBigInt() | 
|  | { | 
|  | return isNotCellNorBigIntSpeculation(prediction()); | 
|  | } | 
|  |  | 
|  | bool shouldSpeculateUntypedForArithmetic() | 
|  | { | 
|  | return isUntypedSpeculationForArithmetic(prediction()); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateUntypedForArithmetic(Node* op1, Node* op2) | 
|  | { | 
|  | 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(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateInt32(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateInt32() && op2->shouldSpeculateInt32(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateInt32OrBoolean(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateInt32OrBoolean() | 
|  | && op2->shouldSpeculateInt32OrBoolean(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateInt32OrBooleanForArithmetic(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateInt32OrBooleanForArithmetic() | 
|  | && op2->shouldSpeculateInt32OrBooleanForArithmetic(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateInt32OrBooleanExpectingDefined(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateInt32OrBooleanExpectingDefined() | 
|  | && op2->shouldSpeculateInt32OrBooleanExpectingDefined(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateInt52(Node* op1, Node* op2) | 
|  | { | 
|  | return enableInt52() && op1->shouldSpeculateInt52() && op2->shouldSpeculateInt52(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateNumber(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateNumber() && op2->shouldSpeculateNumber(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateNumberOrBoolean(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateNumberOrBoolean() | 
|  | && op2->shouldSpeculateNumberOrBoolean(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateNumberOrBooleanExpectingDefined(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateNumberOrBooleanExpectingDefined() | 
|  | && op2->shouldSpeculateNumberOrBooleanExpectingDefined(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateSymbol(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateSymbol() && op2->shouldSpeculateSymbol(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateBigInt(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateBigInt() && op2->shouldSpeculateBigInt(); | 
|  | } | 
|  |  | 
|  | #if USE(BIGINT32) | 
|  | static bool shouldSpeculateBigInt32(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateBigInt32() && op2->shouldSpeculateBigInt32(); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | static bool shouldSpeculateHeapBigInt(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateHeapBigInt() && op2->shouldSpeculateHeapBigInt(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateFinalObject(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateFinalObject() && op2->shouldSpeculateFinalObject(); | 
|  | } | 
|  |  | 
|  | static bool shouldSpeculateArray(Node* op1, Node* op2) | 
|  | { | 
|  | return op1->shouldSpeculateArray() && op2->shouldSpeculateArray(); | 
|  | } | 
|  |  | 
|  | bool canSpeculateInt32(RareCaseProfilingSource source) | 
|  | { | 
|  | return nodeCanSpeculateInt32(arithNodeFlags(), source); | 
|  | } | 
|  |  | 
|  | bool canSpeculateInt52(RareCaseProfilingSource source) | 
|  | { | 
|  | return nodeCanSpeculateInt52(arithNodeFlags(), source); | 
|  | } | 
|  |  | 
|  | bool canSpeculateBigInt32(RareCaseProfilingSource source) | 
|  | { | 
|  | return nodeCanSpeculateBigInt32(arithNodeFlags(), source); | 
|  | } | 
|  |  | 
|  | RareCaseProfilingSource sourceFor(PredictionPass pass) | 
|  | { | 
|  | if (pass == PrimaryPass || child1()->sawBooleans() || (child2() && child2()->sawBooleans())) | 
|  | return DFGRareCase; | 
|  | return AllRareCases; | 
|  | } | 
|  |  | 
|  | bool canSpeculateInt32(PredictionPass pass) | 
|  | { | 
|  | return canSpeculateInt32(sourceFor(pass)); | 
|  | } | 
|  |  | 
|  | bool canSpeculateInt52(PredictionPass pass) | 
|  | { | 
|  | return canSpeculateInt52(sourceFor(pass)); | 
|  | } | 
|  |  | 
|  | bool canSpeculateBigInt32(PredictionPass pass) | 
|  | { | 
|  | return canSpeculateBigInt32(sourceFor(pass)); | 
|  | } | 
|  |  | 
|  | bool hasTypeLocation() | 
|  | { | 
|  | return op() == ProfileType; | 
|  | } | 
|  |  | 
|  | TypeLocation* typeLocation() | 
|  | { | 
|  | ASSERT(hasTypeLocation()); | 
|  | return m_opInfo.as<TypeLocation*>(); | 
|  | } | 
|  |  | 
|  | bool hasBasicBlockLocation() | 
|  | { | 
|  | return op() == ProfileControlFlow; | 
|  | } | 
|  |  | 
|  | BasicBlockLocation* basicBlockLocation() | 
|  | { | 
|  | ASSERT(hasBasicBlockLocation()); | 
|  | return m_opInfo.as<BasicBlockLocation*>(); | 
|  | } | 
|  |  | 
|  | bool hasCallDOMGetterData() const | 
|  | { | 
|  | return op() == CallDOMGetter; | 
|  | } | 
|  |  | 
|  | CallDOMGetterData* callDOMGetterData() | 
|  | { | 
|  | ASSERT(hasCallDOMGetterData()); | 
|  | return m_opInfo.as<CallDOMGetterData*>(); | 
|  | } | 
|  |  | 
|  | bool hasClassInfo() const | 
|  | { | 
|  | return op() == CheckJSCast || op() == CheckNotJSCast; | 
|  | } | 
|  |  | 
|  | const ClassInfo* classInfo() | 
|  | { | 
|  | ASSERT(hasClassInfo()); | 
|  | return m_opInfo.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*>(); | 
|  | } | 
|  |  | 
|  | const ClassInfo* requiredDOMJITClassInfo() | 
|  | { | 
|  | switch (op()) { | 
|  | case CallDOMGetter: | 
|  | return callDOMGetterData()->requiredClassInfo; | 
|  | case CallDOM: | 
|  | return signature()->classInfo; | 
|  | default: | 
|  | RELEASE_ASSERT_NOT_REACHED(); | 
|  | } | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | void setReplacement(Node* replacement) | 
|  | { | 
|  | m_misc.replacement = replacement; | 
|  | } | 
|  |  | 
|  | Epoch epoch() const | 
|  | { | 
|  | return Epoch::fromUnsigned(m_misc.epoch); | 
|  | } | 
|  |  | 
|  | void setEpoch(Epoch epoch) | 
|  | { | 
|  | m_misc.epoch = epoch.toUnsigned(); | 
|  | } | 
|  |  | 
|  | bool hasNumberOfArgumentsToSkip() | 
|  | { | 
|  | return op() == CreateRest || op() == PhantomCreateRest || op() == GetRestLength || op() == GetMyArgumentByVal || op() == GetMyArgumentByValOutOfBounds; | 
|  | } | 
|  |  | 
|  | unsigned numberOfArgumentsToSkip() | 
|  | { | 
|  | ASSERT(hasNumberOfArgumentsToSkip()); | 
|  | return m_opInfo.as<unsigned>(); | 
|  | } | 
|  |  | 
|  | bool hasArgumentIndex() | 
|  | { | 
|  | return op() == GetArgument; | 
|  | } | 
|  |  | 
|  | unsigned argumentIndex() | 
|  | { | 
|  | ASSERT(hasArgumentIndex()); | 
|  | return m_opInfo.as<unsigned>(); | 
|  | } | 
|  |  | 
|  | bool hasBucketOwnerType() | 
|  | { | 
|  | return op() == GetMapBucketNext || op() == LoadKeyFromMapBucket || op() == LoadValueFromMapBucket; | 
|  | } | 
|  |  | 
|  | BucketOwnerType bucketOwnerType() | 
|  | { | 
|  | ASSERT(hasBucketOwnerType()); | 
|  | return m_opInfo.as<BucketOwnerType>(); | 
|  | } | 
|  |  | 
|  | bool hasValidRadixConstant() | 
|  | { | 
|  | return op() == NumberToStringWithValidRadixConstant; | 
|  | } | 
|  |  | 
|  | int32_t validRadixConstant() | 
|  | { | 
|  | ASSERT(hasValidRadixConstant()); | 
|  | return m_opInfo.as<int32_t>(); | 
|  | } | 
|  |  | 
|  | bool hasIgnoreLastIndexIsWritable() | 
|  | { | 
|  | return op() == SetRegExpObjectLastIndex; | 
|  | } | 
|  |  | 
|  | bool ignoreLastIndexIsWritable() | 
|  | { | 
|  | ASSERT(hasIgnoreLastIndexIsWritable()); | 
|  | return m_opInfo.as<uint32_t>(); | 
|  | } | 
|  |  | 
|  | uint32_t errorType() | 
|  | { | 
|  | ASSERT(op() == ThrowStaticError); | 
|  | return m_opInfo.as<uint32_t>(); | 
|  | } | 
|  |  | 
|  | bool hasCallLinkStatus() | 
|  | { | 
|  | return op() == FilterCallLinkStatus; | 
|  | } | 
|  |  | 
|  | CallLinkStatus* callLinkStatus() | 
|  | { | 
|  | ASSERT(hasCallLinkStatus()); | 
|  | return m_opInfo.as<CallLinkStatus*>(); | 
|  | } | 
|  |  | 
|  | bool hasGetByStatus() | 
|  | { | 
|  | return op() == FilterGetByStatus; | 
|  | } | 
|  |  | 
|  | GetByStatus* getByStatus() | 
|  | { | 
|  | ASSERT(hasGetByStatus()); | 
|  | return m_opInfo.as<GetByStatus*>(); | 
|  | } | 
|  |  | 
|  | bool hasInByIdStatus() | 
|  | { | 
|  | return op() == FilterInByIdStatus; | 
|  | } | 
|  |  | 
|  | InByIdStatus* inByIdStatus() | 
|  | { | 
|  | ASSERT(hasInByIdStatus()); | 
|  | return m_opInfo.as<InByIdStatus*>(); | 
|  | } | 
|  |  | 
|  | bool hasPutByIdStatus() | 
|  | { | 
|  | return op() == FilterPutByIdStatus; | 
|  | } | 
|  |  | 
|  | PutByIdStatus* putByIdStatus() | 
|  | { | 
|  | ASSERT(hasPutByIdStatus()); | 
|  | return m_opInfo.as<PutByIdStatus*>(); | 
|  | } | 
|  |  | 
|  | bool hasDeleteByStatus() | 
|  | { | 
|  | return op() == FilterDeleteByStatus; | 
|  | } | 
|  |  | 
|  | DeleteByStatus* deleteByStatus() | 
|  | { | 
|  | ASSERT(hasDeleteByStatus()); | 
|  | return m_opInfo.as<DeleteByStatus*>(); | 
|  | } | 
|  |  | 
|  | void dumpChildren(PrintStream& out) | 
|  | { | 
|  | if (!child1()) | 
|  | return; | 
|  | out.printf("@%u", child1()->index()); | 
|  | if (!child2()) | 
|  | return; | 
|  | out.printf(", @%u", child2()->index()); | 
|  | if (!child3()) | 
|  | return; | 
|  | out.printf(", @%u", child3()->index()); | 
|  | } | 
|  |  | 
|  | NodeOrigin origin; | 
|  |  | 
|  | // References to up to 3 children, or links to a variable length set of children. | 
|  | AdjacencyList children; | 
|  |  | 
|  | private: | 
|  | friend class B3::SparseCollection<Node>; | 
|  |  | 
|  | unsigned m_index { std::numeric_limits<unsigned>::max() }; | 
|  | unsigned m_op : 10; // real type is NodeType | 
|  | unsigned m_flags : 21; | 
|  | // 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 { 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(CacheableIdentifier identifier) | 
|  | { | 
|  | u.int64 = 0; | 
|  | u.pointer = bitwise_cast<void*>(identifier.rawBits()); | 
|  | } | 
|  | 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; | 
|  | } | 
|  | OpInfoWrapper& operator=(CacheableIdentifier identifier) | 
|  | { | 
|  | u.int64 = 0; | 
|  | u.pointer = bitwise_cast<void*>(identifier.rawBits()); | 
|  | return *this; | 
|  | } | 
|  | OpInfoWrapper& operator=(NewArrayBufferData newArrayBufferData) | 
|  | { | 
|  | u.int64 = bitwise_cast<uint64_t>(newArrayBufferData); | 
|  | 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 || std::is_enum<T>::value) && sizeof(T) <= 4, T>::type | 
|  | { | 
|  | return static_cast<T>(u.int32); | 
|  | } | 
|  | template <typename T> | 
|  | ALWAYS_INLINE auto as() const -> typename std::enable_if<(std::is_integral<T>::value || std::is_enum<T>::value) && sizeof(T) == 8, T>::type | 
|  | { | 
|  | return static_cast<T>(u.int64); | 
|  | } | 
|  | ALWAYS_INLINE RegisteredStructure asRegisteredStructure() const | 
|  | { | 
|  | return bitwise_cast<RegisteredStructure>(u.pointer); | 
|  | } | 
|  | ALWAYS_INLINE NewArrayBufferData asNewArrayBufferData() const | 
|  | { | 
|  | return bitwise_cast<NewArrayBufferData>(u.int64); | 
|  | } | 
|  |  | 
|  | union { | 
|  | uint32_t int32; | 
|  | uint64_t int64; | 
|  | void* pointer; | 
|  | const void* constPointer; | 
|  | } u; | 
|  | }; | 
|  | OpInfoWrapper m_opInfo; | 
|  | OpInfoWrapper m_opInfo2; | 
|  |  | 
|  | // 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 | 
|  | // across transformations unless you do the maintenance work yourself. Other phases use | 
|  | // Node::replacement, but they do so manually: first you do Graph::clearReplacements() | 
|  | // and then you set, and use, replacement's yourself. Same thing for epoch. | 
|  | // | 
|  | // Bottom line: don't use these fields unless you initialize them yourself, or by | 
|  | // calling some appropriate methods that initialize them the way you want. Otherwise, | 
|  | // these fields are meaningless. | 
|  | private: | 
|  | union { | 
|  | Node* replacement; | 
|  | unsigned epoch; | 
|  | } m_misc; | 
|  | public: | 
|  | BasicBlock* owner; | 
|  | }; | 
|  |  | 
|  | // 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()); | 
|  | } | 
|  |  | 
|  | template<typename T> | 
|  | CString nodeMapDump(const T& nodeMap, DumpContext* context = nullptr) | 
|  | { | 
|  | Vector<typename T::KeyType> keys; | 
|  | for ( | 
|  | typename T::const_iterator iter = nodeMap.begin(); | 
|  | iter != nodeMap.end(); ++iter) | 
|  | keys.append(iter->key); | 
|  | std::sort(keys.begin(), keys.end(), NodeComparator()); | 
|  | StringPrintStream out; | 
|  | CommaPrinter comma; | 
|  | for(unsigned i = 0; i < keys.size(); ++i) | 
|  | out.print(comma, keys[i], "=>", inContext(nodeMap.get(keys[i]), context)); | 
|  | return out.toCString(); | 
|  | } | 
|  |  | 
|  | template<typename T> | 
|  | CString nodeValuePairListDump(const T& nodeValuePairList, DumpContext* context = nullptr) | 
|  | { | 
|  | 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 { | 
|  |  | 
|  | void printInternal(PrintStream&, JSC::DFG::SwitchKind); | 
|  | 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 |