blob: 439741d8e65ed215b5c8fd614212276ab4c96667 [file] [log] [blame]
/*
* Copyright (C) 2011-2018 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 "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/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 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(0)
, 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(VirtualRegister local, FlushFormat format)
: local(local)
, format(format)
{
}
VirtualRegister local;
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 };
};
enum class BucketOwnerType : uint32_t {
Map,
Set
};
// === Node ===
//
// Node represents a single operation in the data flow graph.
struct Node {
WTF_MAKE_FAST_ALLOCATED;
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 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 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 == MaterializeNewObject);
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 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 == MaterializeCreateActivation);
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 = 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 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(unsigned identifierNumber)
{
ASSERT(m_op == InByVal);
setOpAndDefaultFlags(InById);
children.setChild2(Edge());
m_opInfo = identifierNumber;
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());
}
template<typename T>
T castConstant(VM& vm)
{
T result = dynamicCastConstant<T>(vm);
RELEASE_ASSERT(result);
return result;
}
bool hasLazyJSValue()
{
return op() == LazyJSConstant;
}
LazyJSValue lazyJSValue()
{
ASSERT(hasLazyJSValue());
return *m_opInfo.as<LazyJSValue*>();
}
String tryGetString(Graph&);
JSValue initializationValueForActivation() const
{
ASSERT(op() == CreateActivation);
return m_opInfo2.as<FrozenValue*>()->value();
}
bool hasArgumentsChild()
{
switch (op()) {
case GetMyArgumentByVal:
case GetMyArgumentByValOutOfBounds:
case LoadVarargs:
case ForwardVarargs:
case CallVarargs:
case CallForwardVarargs:
case ConstructVarargs:
case ConstructForwardVarargs:
case TailCallVarargs:
case TailCallForwardVarargs:
case TailCallVarargsInlinedCaller:
case TailCallForwardVarargsInlinedCaller:
return true;
default:
return false;
}
}
Edge& argumentsChild()
{
switch (op()) {
case GetMyArgumentByVal:
case GetMyArgumentByValOutOfBounds:
case LoadVarargs:
case ForwardVarargs:
return child1();
case CallVarargs:
case CallForwardVarargs:
case ConstructVarargs:
case ConstructForwardVarargs:
case TailCallVarargs:
case TailCallForwardVarargs:
case TailCallVarargsInlinedCaller:
case TailCallForwardVarargsInlinedCaller:
return child3();
default:
RELEASE_ASSERT_NOT_REACHED();
return child1();
}
}
bool containsMovHint()
{
switch (op()) {
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 0;
return result->find();
}
VariableAccessData* variableAccessData()
{
return m_opInfo.as<VariableAccessData*>()->find();
}
VirtualRegister local()
{
return variableAccessData()->local();
}
VirtualRegister machineLocal()
{
return variableAccessData()->machineLocal();
}
bool hasUnlinkedLocal()
{
switch (op()) {
case ExtractOSREntryLocal:
case MovHint:
case ZombieHint:
case KillStack:
return true;
default:
return false;
}
}
VirtualRegister unlinkedLocal()
{
ASSERT(hasUnlinkedLocal());
return VirtualRegister(m_opInfo.as<int32_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 hasIdentifier()
{
switch (op()) {
case TryGetById:
case GetById:
case GetByIdFlush:
case GetByIdWithThis:
case GetByIdDirect:
case GetByIdDirectFlush:
case PutById:
case PutByIdFlush:
case PutByIdDirect:
case PutByIdWithThis:
case PutGetterById:
case PutSetterById:
case PutGetterSetterById:
case DeleteById:
case InById:
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 mayHaveBigIntResult()
{
return m_flags & NodeMayHaveBigIntResult;
}
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>();
}
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>());
}
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;
}
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;
}
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 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 CheckBadCell:
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*>();
}
Intrinsic intrinsic()
{
RELEASE_ASSERT(op() == CPUIntrinsic);
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 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 ToObject:
case ValueBitAnd:
case ValueBitOr:
case ValueBitXor:
case ValueBitNot:
case ValueBitLShift:
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:
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 CheckCell:
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() == CheckStringIdent;
}
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 NewObject:
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 hasMatchStructureData()
{
return op() == MatchStructure;
}
MatchStructureData& matchStructureData()
{
ASSERT(hasMatchStructureData());
return *m_opInfo.as<MatchStructureData*>();
}
bool hasObjectMaterializationData()
{
switch (op()) {
case MaterializeNewObject:
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 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 CheckArray:
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 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());
}
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 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();
}
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);
}
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 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() == CheckSubClass;
}
const ClassInfo* classInfo()
{
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*>();
}
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 hasGetByIdStatus()
{
return op() == FilterGetByIdStatus;
}
GetByIdStatus* getByIdStatus()
{
ASSERT(hasGetByIdStatus());
return m_opInfo.as<GetByIdStatus*>();
}
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*>();
}
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& 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=(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 = 0)
{
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 = 0)
{
using V = typename T::ValueType;
T sortedList = nodeValuePairList;
std::sort(sortedList.begin(), sortedList.end(), [](const V& a, const V& b) {
return NodeComparator()(a.node, b.node);
});
StringPrintStream out;
CommaPrinter comma;
for (const auto& pair : sortedList)
out.print(comma, pair.node, "=>", inContext(pair.value, context));
return out.toCString();
}
} } // namespace JSC::DFG
namespace WTF {
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