DFGNode.h   [plain text]


/*
 * Copyright (C) 2011 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. 
 */

#ifndef DFGNode_h
#define DFGNode_h

#include <wtf/Platform.h>

#if ENABLE(DFG_JIT)

#include "CodeBlock.h"
#include "CodeOrigin.h"
#include "DFGCommon.h"
#include "DFGOperands.h"
#include "DFGVariableAccessData.h"
#include "JSValue.h"
#include "PredictedType.h"
#include "ValueProfile.h"
#include <wtf/BoundsCheckedPointer.h>
#include <wtf/Vector.h>

namespace JSC { namespace DFG {

struct StructureTransitionData {
    Structure* previousStructure;
    Structure* newStructure;
    
    StructureTransitionData() { }
    
    StructureTransitionData(Structure* previousStructure, Structure* newStructure)
        : previousStructure(previousStructure)
        , newStructure(newStructure)
    {
    }
};

typedef unsigned ArithNodeFlags;
#define NodeUseBottom      0x00
#define NodeUsedAsNumber   0x01
#define NodeNeedsNegZero   0x02
#define NodeUsedAsMask     0x03
#define NodeMayOverflow    0x04
#define NodeMayNegZero     0x08
#define NodeBehaviorMask   0x0c

static inline bool nodeUsedAsNumber(ArithNodeFlags flags)
{
    return !!(flags & NodeUsedAsNumber);
}

static inline bool nodeCanTruncateInteger(ArithNodeFlags flags)
{
    return !nodeUsedAsNumber(flags);
}

static inline bool nodeCanIgnoreNegativeZero(ArithNodeFlags flags)
{
    return !(flags & NodeNeedsNegZero);
}

static inline bool nodeMayOverflow(ArithNodeFlags flags)
{
    return !!(flags & NodeMayOverflow);
}

static inline bool nodeCanSpeculateInteger(ArithNodeFlags flags)
{
    if (flags & NodeMayOverflow)
        return !nodeUsedAsNumber(flags);
    
    if (flags & NodeMayNegZero)
        return nodeCanIgnoreNegativeZero(flags);
    
    return true;
}

#ifndef NDEBUG
static inline const char* arithNodeFlagsAsString(ArithNodeFlags flags)
{
    if (!flags)
        return "<empty>";

    static const int size = 64;
    static char description[size];
    BoundsCheckedPointer<char> ptr(description, size);
    
    bool hasPrinted = false;
    
    if (flags & NodeUsedAsNumber) {
        ptr.strcat("UsedAsNum");
        hasPrinted = true;
    }
    
    if (flags & NodeNeedsNegZero) {
        if (hasPrinted)
            ptr.strcat("|");
        ptr.strcat("NeedsNegZero");
        hasPrinted = true;
    }
    
    if (flags & NodeMayOverflow) {
        if (hasPrinted)
            ptr.strcat("|");
        ptr.strcat("MayOverflow");
        hasPrinted = true;
    }
    
    if (flags & NodeMayNegZero) {
        if (hasPrinted)
            ptr.strcat("|");
        ptr.strcat("MayNegZero");
        hasPrinted = true;
    }
    
    *ptr++ = 0;
    
    return description;
}
#endif

// Entries in the NodeType enum (below) are composed of an id, a result type (possibly none)
// and some additional informative flags (must generate, is constant, etc).
#define NodeIdMask           0xFFF
#define NodeResultMask      0xF000
#define NodeMustGenerate   0x10000 // set on nodes that have side effects, and may not trivially be removed by DCE.
#define NodeIsConstant     0x20000
#define NodeIsJump         0x40000
#define NodeIsBranch       0x80000
#define NodeIsTerminal    0x100000
#define NodeHasVarArgs    0x200000
#define NodeClobbersWorld 0x400000
#define NodeMightClobber  0x800000

// These values record the result type of the node (as checked by NodeResultMask, above), 0 for no result.
#define NodeResultJS        0x1000
#define NodeResultNumber    0x2000
#define NodeResultInt32     0x3000
#define NodeResultBoolean   0x4000
#define NodeResultStorage   0x5000

// This macro defines a set of information about all known node types, used to populate NodeId, NodeType below.
#define FOR_EACH_DFG_OP(macro) \
    /* A constant in the CodeBlock's constant pool. */\
    macro(JSConstant, NodeResultJS) \
    \
    /* A constant not in the CodeBlock's constant pool. Uses get patched to jumps that exit the */\
    /* code block. */\
    macro(WeakJSConstant, NodeResultJS) \
    \
    /* Nodes for handling functions (both as call and as construct). */\
    macro(ConvertThis, NodeResultJS) \
    macro(CreateThis, NodeResultJS) /* Note this is not MustGenerate since we're returning it anyway. */ \
    macro(GetCallee, NodeResultJS) \
    \
    /* Nodes for local variable access. */\
    macro(GetLocal, NodeResultJS) \
    macro(SetLocal, 0) \
    macro(Phantom, NodeMustGenerate) \
    macro(Nop, 0) \
    macro(Phi, 0) \
    macro(Flush, NodeMustGenerate) \
    \
    /* Marker for arguments being set. */\
    macro(SetArgument, 0) \
    \
    /* Hint that inlining begins here. No code is generated for this node. It's only */\
    /* used for copying OSR data into inline frame data, to support reification of */\
    /* call frames of inlined functions. */\
    macro(InlineStart, 0) \
    \
    /* Nodes for bitwise operations. */\
    macro(BitAnd, NodeResultInt32) \
    macro(BitOr, NodeResultInt32) \
    macro(BitXor, NodeResultInt32) \
    macro(BitLShift, NodeResultInt32) \
    macro(BitRShift, NodeResultInt32) \
    macro(BitURShift, NodeResultInt32) \
    /* Bitwise operators call ToInt32 on their operands. */\
    macro(ValueToInt32, NodeResultInt32 | NodeMustGenerate) \
    /* Used to box the result of URShift nodes (result has range 0..2^32-1). */\
    macro(UInt32ToNumber, NodeResultNumber) \
    \
    /* Nodes for arithmetic operations. */\
    macro(ArithAdd, NodeResultNumber) \
    macro(ArithSub, NodeResultNumber) \
    macro(ArithMul, NodeResultNumber) \
    macro(ArithDiv, NodeResultNumber) \
    macro(ArithMod, NodeResultNumber) \
    macro(ArithAbs, NodeResultNumber) \
    macro(ArithMin, NodeResultNumber) \
    macro(ArithMax, NodeResultNumber) \
    macro(ArithSqrt, NodeResultNumber) \
    /* Arithmetic operators call ToNumber on their operands. */\
    macro(ValueToNumber, NodeResultNumber | NodeMustGenerate) \
    \
    /* A variant of ValueToNumber, which a hint that the parents will always use this as a double. */\
    macro(ValueToDouble, NodeResultNumber | NodeMustGenerate) \
    \
    /* Add of values may either be arithmetic, or result in string concatenation. */\
    macro(ValueAdd, NodeResultJS | NodeMustGenerate | NodeMightClobber) \
    \
    /* Property access. */\
    /* PutByValAlias indicates a 'put' aliases a prior write to the same property. */\
    /* Since a put to 'length' may invalidate optimizations here, */\
    /* this must be the directly subsequent property put. */\
    macro(GetByVal, NodeResultJS | NodeMustGenerate | NodeMightClobber) \
    macro(PutByVal, NodeMustGenerate | NodeClobbersWorld) \
    macro(PutByValAlias, NodeMustGenerate | NodeClobbersWorld) \
    macro(GetById, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
    macro(GetByIdFlush, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
    macro(PutById, NodeMustGenerate | NodeClobbersWorld) \
    macro(PutByIdDirect, NodeMustGenerate | NodeClobbersWorld) \
    macro(CheckStructure, NodeMustGenerate) \
    macro(PutStructure, NodeMustGenerate | NodeClobbersWorld) \
    macro(GetPropertyStorage, NodeResultStorage) \
    macro(GetIndexedPropertyStorage, NodeMustGenerate | NodeResultStorage) \
    macro(GetByOffset, NodeResultJS) \
    macro(PutByOffset, NodeMustGenerate | NodeClobbersWorld) \
    macro(GetArrayLength, NodeResultInt32) \
    macro(GetStringLength, NodeResultInt32) \
    macro(GetByteArrayLength, NodeResultInt32) \
    macro(GetInt8ArrayLength, NodeResultInt32) \
    macro(GetInt16ArrayLength, NodeResultInt32) \
    macro(GetInt32ArrayLength, NodeResultInt32) \
    macro(GetUint8ArrayLength, NodeResultInt32) \
    macro(GetUint16ArrayLength, NodeResultInt32) \
    macro(GetUint32ArrayLength, NodeResultInt32) \
    macro(GetFloat32ArrayLength, NodeResultInt32) \
    macro(GetFloat64ArrayLength, NodeResultInt32) \
    macro(GetScopeChain, NodeResultJS) \
    macro(GetScopedVar, NodeResultJS | NodeMustGenerate) \
    macro(PutScopedVar, NodeMustGenerate | NodeClobbersWorld) \
    macro(GetGlobalVar, NodeResultJS | NodeMustGenerate) \
    macro(PutGlobalVar, NodeMustGenerate | NodeClobbersWorld) \
    macro(CheckFunction, NodeMustGenerate) \
    \
    /* Optimizations for array mutation. */\
    macro(ArrayPush, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
    macro(ArrayPop, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
    \
    /* Optimizations for string access */ \
    macro(StringCharCodeAt, NodeResultInt32) \
    macro(StringCharAt, NodeResultJS) \
    \
    /* Nodes for comparison operations. */\
    macro(CompareLess, NodeResultBoolean | NodeMustGenerate | NodeMightClobber) \
    macro(CompareLessEq, NodeResultBoolean | NodeMustGenerate | NodeMightClobber) \
    macro(CompareGreater, NodeResultBoolean | NodeMustGenerate | NodeMightClobber) \
    macro(CompareGreaterEq, NodeResultBoolean | NodeMustGenerate | NodeMightClobber) \
    macro(CompareEq, NodeResultBoolean | NodeMustGenerate | NodeMightClobber) \
    macro(CompareStrictEq, NodeResultBoolean) \
    \
    /* Calls. */\
    macro(Call, NodeResultJS | NodeMustGenerate | NodeHasVarArgs | NodeClobbersWorld) \
    macro(Construct, NodeResultJS | NodeMustGenerate | NodeHasVarArgs | NodeClobbersWorld) \
    \
    /* Allocations. */\
    macro(NewObject, NodeResultJS) \
    macro(NewArray, NodeResultJS | NodeHasVarArgs) \
    macro(NewArrayBuffer, NodeResultJS) \
    macro(NewRegexp, NodeResultJS) \
    \
    /* Resolve nodes. */\
    macro(Resolve, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
    macro(ResolveBase, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
    macro(ResolveBaseStrictPut, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
    macro(ResolveGlobal, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
    \
    /* Nodes for misc operations. */\
    macro(Breakpoint, NodeMustGenerate | NodeClobbersWorld) \
    macro(CheckHasInstance, NodeMustGenerate) \
    macro(InstanceOf, NodeResultBoolean) \
    macro(LogicalNot, NodeResultBoolean | NodeMightClobber) \
    macro(ToPrimitive, NodeResultJS | NodeMustGenerate | NodeClobbersWorld) \
    macro(StrCat, NodeResultJS | NodeMustGenerate | NodeHasVarArgs | NodeClobbersWorld) \
    \
    /* Block terminals. */\
    macro(Jump, NodeMustGenerate | NodeIsTerminal | NodeIsJump) \
    macro(Branch, NodeMustGenerate | NodeIsTerminal | NodeIsBranch) \
    macro(Return, NodeMustGenerate | NodeIsTerminal) \
    macro(Throw, NodeMustGenerate | NodeIsTerminal) \
    macro(ThrowReferenceError, NodeMustGenerate | NodeIsTerminal) \
    \
    /* 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. */\
    macro(ForceOSRExit, NodeMustGenerate)

// This enum generates a monotonically increasing id for all Node types,
// and is used by the subsequent enum to fill out the id (as accessed via the NodeIdMask).
enum NodeId {
#define DFG_OP_ENUM(opcode, flags) opcode##_id,
    FOR_EACH_DFG_OP(DFG_OP_ENUM)
#undef DFG_OP_ENUM
    LastNodeId
};

// Entries in this enum describe all Node types.
// The enum value contains a monotonically increasing id, a result type, and additional flags.
enum NodeType {
#define DFG_OP_ENUM(opcode, flags) opcode = opcode##_id | (flags),
    FOR_EACH_DFG_OP(DFG_OP_ENUM)
#undef DFG_OP_ENUM
};

// This type used in passing an immediate argument to Node constructor;
// distinguishes an immediate value (typically an index into a CodeBlock data structure - 
// a constant index, argument, or identifier) from a NodeIndex.
struct OpInfo {
    explicit OpInfo(int32_t value) : m_value(static_cast<uintptr_t>(value)) { }
    explicit OpInfo(uint32_t value) : m_value(static_cast<uintptr_t>(value)) { }
#if OS(DARWIN) || USE(JSVALUE64)
    explicit OpInfo(size_t value) : m_value(static_cast<uintptr_t>(value)) { }
#endif
    explicit OpInfo(void* value) : m_value(reinterpret_cast<uintptr_t>(value)) { }
    uintptr_t m_value;
};

// === Node ===
//
// Node represents a single operation in the data flow graph.
struct Node {
    enum VarArgTag { VarArg };

    // Construct a node with up to 3 children, no immediate value.
    Node(NodeType op, CodeOrigin codeOrigin, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
        : op(op)
        , codeOrigin(codeOrigin)
        , m_virtualRegister(InvalidVirtualRegister)
        , m_refCount(0)
        , m_prediction(PredictNone)
    {
        ASSERT(!(op & NodeHasVarArgs));
        ASSERT(!hasArithNodeFlags());
        children.fixed.child1 = child1;
        children.fixed.child2 = child2;
        children.fixed.child3 = child3;
    }

    // Construct a node with up to 3 children and an immediate value.
    Node(NodeType op, CodeOrigin codeOrigin, OpInfo imm, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
        : op(op)
        , codeOrigin(codeOrigin)
        , m_virtualRegister(InvalidVirtualRegister)
        , m_refCount(0)
        , m_opInfo(imm.m_value)
        , m_prediction(PredictNone)
    {
        ASSERT(!(op & NodeHasVarArgs));
        children.fixed.child1 = child1;
        children.fixed.child2 = child2;
        children.fixed.child3 = child3;
    }

    // Construct a node with up to 3 children and two immediate values.
    Node(NodeType op, CodeOrigin codeOrigin, OpInfo imm1, OpInfo imm2, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
        : op(op)
        , codeOrigin(codeOrigin)
        , m_virtualRegister(InvalidVirtualRegister)
        , m_refCount(0)
        , m_opInfo(imm1.m_value)
        , m_opInfo2(safeCast<unsigned>(imm2.m_value))
        , m_prediction(PredictNone)
    {
        ASSERT(!(op & NodeHasVarArgs));
        children.fixed.child1 = child1;
        children.fixed.child2 = child2;
        children.fixed.child3 = child3;
    }
    
    // Construct a node with a variable number of children and two immediate values.
    Node(VarArgTag, NodeType op, CodeOrigin codeOrigin, OpInfo imm1, OpInfo imm2, unsigned firstChild, unsigned numChildren)
        : op(op)
        , codeOrigin(codeOrigin)
        , m_virtualRegister(InvalidVirtualRegister)
        , m_refCount(0)
        , m_opInfo(imm1.m_value)
        , m_opInfo2(safeCast<unsigned>(imm2.m_value))
        , m_prediction(PredictNone)
    {
        ASSERT(op & NodeHasVarArgs);
        children.variable.firstChild = firstChild;
        children.variable.numChildren = numChildren;
    }

    bool mustGenerate()
    {
        return op & NodeMustGenerate;
    }

    bool isConstant()
    {
        return op == JSConstant;
    }
    
    bool isWeakConstant()
    {
        return op == WeakJSConstant;
    }
    
    bool hasConstant()
    {
        return isConstant() || isWeakConstant();
    }

    unsigned constantNumber()
    {
        ASSERT(isConstant());
        return m_opInfo;
    }
    
    JSCell* weakConstant()
    {
        return bitwise_cast<JSCell*>(m_opInfo);
    }
    
    JSValue valueOfJSConstant(CodeBlock* codeBlock)
    {
        if (op == WeakJSConstant)
            return JSValue(weakConstant());
        return codeBlock->constantRegister(FirstConstantRegisterIndex + constantNumber()).get();
    }

    bool isInt32Constant(CodeBlock* codeBlock)
    {
        return isConstant() && valueOfJSConstant(codeBlock).isInt32();
    }
    
    bool isDoubleConstant(CodeBlock* codeBlock)
    {
        bool result = isConstant() && valueOfJSConstant(codeBlock).isDouble();
        if (result)
            ASSERT(!isInt32Constant(codeBlock));
        return result;
    }
    
    bool isNumberConstant(CodeBlock* codeBlock)
    {
        bool result = isConstant() && valueOfJSConstant(codeBlock).isNumber();
        ASSERT(result == (isInt32Constant(codeBlock) || isDoubleConstant(codeBlock)));
        return result;
    }
    
    bool isBooleanConstant(CodeBlock* codeBlock)
    {
        return isConstant() && valueOfJSConstant(codeBlock).isBoolean();
    }
    
    bool hasVariableAccessData()
    {
        switch (op) {
        case GetLocal:
        case SetLocal:
        case Phi:
        case SetArgument:
        case Flush:
            return true;
        default:
            return false;
        }
    }
    
    bool hasLocal()
    {
        return hasVariableAccessData();
    }
    
    VariableAccessData* variableAccessData()
    {
        ASSERT(hasVariableAccessData());
        return reinterpret_cast<VariableAccessData*>(m_opInfo)->find();
    }
    
    VirtualRegister local()
    {
        return variableAccessData()->local();
    }

#ifndef NDEBUG
    bool hasIdentifier()
    {
        switch (op) {
        case GetById:
        case GetByIdFlush:
        case PutById:
        case PutByIdDirect:
        case Resolve:
        case ResolveBase:
        case ResolveBaseStrictPut:
            return true;
        default:
            return false;
        }
    }
#endif

    unsigned identifierNumber()
    {
        ASSERT(hasIdentifier());
        return m_opInfo;
    }
    
    unsigned resolveGlobalDataIndex()
    {
        ASSERT(op == ResolveGlobal);
        return m_opInfo;
    }

    bool hasArithNodeFlags()
    {
        switch (op) {
        case ValueToNumber:
        case ValueToDouble:
        case UInt32ToNumber:
        case ArithAdd:
        case ArithSub:
        case ArithMul:
        case ArithAbs:
        case ArithMin:
        case ArithMax:
        case ArithMod:
        case ArithDiv:
        case ValueAdd:
            return true;
        default:
            return false;
        }
    }
    
    ArithNodeFlags rawArithNodeFlags()
    {
        ASSERT(hasArithNodeFlags());
        return m_opInfo;
    }
    
    // This corrects the arithmetic node flags, so that irrelevant bits are
    // ignored. In particular, anything other than ArithMul does not need
    // to know if it can speculate on negative zero.
    ArithNodeFlags arithNodeFlags()
    {
        ArithNodeFlags result = rawArithNodeFlags();
        if (op == ArithMul)
            return result;
        return result & ~NodeNeedsNegZero;
    }
    
    ArithNodeFlags arithNodeFlagsForCompare()
    {
        if (hasArithNodeFlags())
            return arithNodeFlags();
        return 0;
    }
    
    void setArithNodeFlag(ArithNodeFlags flags)
    {
        ASSERT(hasArithNodeFlags());
        m_opInfo = flags;
    }
    
    bool mergeArithNodeFlags(ArithNodeFlags flags)
    {
        if (!hasArithNodeFlags())
            return false;
        ArithNodeFlags newFlags = m_opInfo | flags;
        if (newFlags == m_opInfo)
            return false;
        m_opInfo = newFlags;
        return true;
    }
    
    bool hasConstantBuffer()
    {
        return op == NewArrayBuffer;
    }
    
    unsigned startConstant()
    {
        ASSERT(hasConstantBuffer());
        return m_opInfo;
    }
    
    unsigned numConstants()
    {
        ASSERT(hasConstantBuffer());
        return m_opInfo2;
    }
    
    bool hasRegexpIndex()
    {
        return op == NewRegexp;
    }
    
    unsigned regexpIndex()
    {
        ASSERT(hasRegexpIndex());
        return m_opInfo;
    }
    
    bool hasVarNumber()
    {
        return op == GetGlobalVar || op == PutGlobalVar || op == GetScopedVar || op == PutScopedVar;
    }

    unsigned varNumber()
    {
        ASSERT(hasVarNumber());
        return m_opInfo;
    }

    bool hasScopeChainDepth()
    {
        return op == GetScopeChain;
    }
    
    unsigned scopeChainDepth()
    {
        ASSERT(hasScopeChainDepth());
        return m_opInfo;
    }

    bool hasResult()
    {
        return op & NodeResultMask;
    }

    bool hasInt32Result()
    {
        return (op & NodeResultMask) == NodeResultInt32;
    }
    
    bool hasNumberResult()
    {
        return (op & NodeResultMask) == NodeResultNumber;
    }
    
    bool hasJSResult()
    {
        return (op & NodeResultMask) == NodeResultJS;
    }
    
    bool hasBooleanResult()
    {
        return (op & NodeResultMask) == NodeResultBoolean;
    }

    bool isJump()
    {
        return op & NodeIsJump;
    }

    bool isBranch()
    {
        return op & NodeIsBranch;
    }

    bool isTerminal()
    {
        return op & NodeIsTerminal;
    }

    unsigned takenBytecodeOffsetDuringParsing()
    {
        ASSERT(isBranch() || isJump());
        return m_opInfo;
    }

    unsigned notTakenBytecodeOffsetDuringParsing()
    {
        ASSERT(isBranch());
        return m_opInfo2;
    }
    
    void setTakenBlockIndex(BlockIndex blockIndex)
    {
        ASSERT(isBranch() || isJump());
        m_opInfo = blockIndex;
    }
    
    void setNotTakenBlockIndex(BlockIndex blockIndex)
    {
        ASSERT(isBranch());
        m_opInfo2 = blockIndex;
    }
    
    BlockIndex takenBlockIndex()
    {
        ASSERT(isBranch() || isJump());
        return m_opInfo;
    }
    
    BlockIndex notTakenBlockIndex()
    {
        ASSERT(isBranch());
        return m_opInfo2;
    }
    
    bool hasHeapPrediction()
    {
        switch (op) {
        case GetById:
        case GetByIdFlush:
        case GetByVal:
        case Call:
        case Construct:
        case GetByOffset:
        case GetScopedVar:
        case Resolve:
        case ResolveBase:
        case ResolveBaseStrictPut:
        case ResolveGlobal:
        case ArrayPop:
        case ArrayPush:
            return true;
        default:
            return false;
        }
    }
    
    PredictedType getHeapPrediction()
    {
        ASSERT(hasHeapPrediction());
        return static_cast<PredictedType>(m_opInfo2);
    }
    
    bool predictHeap(PredictedType prediction)
    {
        ASSERT(hasHeapPrediction());
        
        return mergePrediction(m_opInfo2, prediction);
    }
    
    bool hasFunctionCheckData()
    {
        return op == CheckFunction;
    }

    JSFunction* function()
    {
        ASSERT(hasFunctionCheckData());
        return reinterpret_cast<JSFunction*>(m_opInfo);
    }

    bool hasStructureTransitionData()
    {
        return op == PutStructure;
    }
    
    StructureTransitionData& structureTransitionData()
    {
        ASSERT(hasStructureTransitionData());
        return *reinterpret_cast<StructureTransitionData*>(m_opInfo);
    }
    
    bool hasStructureSet()
    {
        return op == CheckStructure;
    }
    
    StructureSet& structureSet()
    {
        ASSERT(hasStructureSet());
        return *reinterpret_cast<StructureSet*>(m_opInfo);
    }
    
    bool hasStorageAccessData()
    {
        return op == GetByOffset || op == PutByOffset;
    }
    
    unsigned storageAccessDataIndex()
    {
        return m_opInfo;
    }
    
    bool hasVirtualRegister()
    {
        return m_virtualRegister != InvalidVirtualRegister;
    }
    
    VirtualRegister virtualRegister()
    {
        ASSERT(hasResult());
        ASSERT(m_virtualRegister != InvalidVirtualRegister);
        return m_virtualRegister;
    }

    void setVirtualRegister(VirtualRegister virtualRegister)
    {
        ASSERT(hasResult());
        ASSERT(m_virtualRegister == InvalidVirtualRegister);
        m_virtualRegister = virtualRegister;
    }

    bool shouldGenerate()
    {
        return m_refCount && op != Phi && op != Flush;
    }

    unsigned refCount()
    {
        return m_refCount;
    }

    // returns true when ref count passes from 0 to 1.
    bool ref()
    {
        return !m_refCount++;
    }

    unsigned adjustedRefCount()
    {
        return mustGenerate() ? m_refCount - 1 : m_refCount;
    }
    
    void setRefCount(unsigned refCount)
    {
        m_refCount = refCount;
    }
    
    // Derefs the node and returns true if the ref count reached zero.
    // In general you don't want to use this directly; use Graph::deref
    // instead.
    bool deref()
    {
        ASSERT(m_refCount);
        return !--m_refCount;
    }
    
    NodeIndex child1()
    {
        ASSERT(!(op & NodeHasVarArgs));
        return children.fixed.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.
    NodeIndex child1Unchecked()
    {
        return children.fixed.child1;
    }

    NodeIndex child2()
    {
        ASSERT(!(op & NodeHasVarArgs));
        return children.fixed.child2;
    }

    NodeIndex child3()
    {
        ASSERT(!(op & NodeHasVarArgs));
        return children.fixed.child3;
    }
    
    unsigned firstChild()
    {
        ASSERT(op & NodeHasVarArgs);
        return children.variable.firstChild;
    }
    
    unsigned numChildren()
    {
        ASSERT(op & NodeHasVarArgs);
        return children.variable.numChildren;
    }
    
    PredictedType prediction()
    {
        return m_prediction;
    }
    
    bool predict(PredictedType prediction)
    {
        return mergePrediction(m_prediction, prediction);
    }
    
    bool shouldSpeculateInteger()
    {
        return isInt32Prediction(prediction());
    }
    
    bool shouldSpeculateDouble()
    {
        return isDoublePrediction(prediction());
    }
    
    bool shouldSpeculateNumber()
    {
        return isNumberPrediction(prediction()) || prediction() == PredictNone;
    }
    
    bool shouldNotSpeculateInteger()
    {
        return !!(prediction() & PredictDouble);
    }
    
    bool shouldSpeculateFinalObject()
    {
        return isFinalObjectPrediction(prediction());
    }
    
    bool shouldSpeculateFinalObjectOrOther()
    {
        return isFinalObjectOrOtherPrediction(prediction());
    }
    
    bool shouldSpeculateArray()
    {
        return isArrayPrediction(prediction());
    }
    
    bool shouldSpeculateByteArray()
    {
        return !!(prediction() & PredictByteArray);
    }
    
    bool shouldSpeculateInt8Array()
    {
        return prediction() == PredictInt8Array;
    }
    
    bool shouldSpeculateInt16Array()
    {
        return prediction() == PredictInt16Array;
    }
    
    bool shouldSpeculateInt32Array()
    {
        return prediction() == PredictInt32Array;
    }
    
    bool shouldSpeculateUint8Array()
    {
        return prediction() == PredictUint8Array;
    }
    
    bool shouldSpeculateUint16Array()
    {
        return prediction() == PredictUint16Array;
    }
    
    bool shouldSpeculateUint32Array()
    {
        return prediction() == PredictUint32Array;
    }
    
    bool shouldSpeculateFloat32Array()
    {
#if CPU(X86) || CPU(X86_64)
        return !!(prediction() & PredictFloat32Array);
#else
        return false;
#endif
    }
    
    bool shouldSpeculateFloat64Array()
    {
        return prediction() == PredictFloat64Array;
    }
    
    bool shouldSpeculateArrayOrOther()
    {
        return isArrayOrOtherPrediction(prediction());
    }
    
    bool shouldSpeculateObject()
    {
        return isObjectPrediction(prediction());
    }
    
    bool shouldSpeculateCell()
    {
        return isCellPrediction(prediction());
    }
    
    static bool shouldSpeculateInteger(Node& op1, Node& op2)
    {
        return op1.shouldSpeculateInteger() && op2.shouldSpeculateInteger();
    }
    
    static bool shouldSpeculateNumber(Node& op1, Node& op2)
    {
        return op1.shouldSpeculateNumber() && op2.shouldSpeculateNumber();
    }
    
    static bool shouldSpeculateFinalObject(Node& op1, Node& op2)
    {
        return (op1.shouldSpeculateFinalObject() && op2.shouldSpeculateObject())
            || (op1.shouldSpeculateObject() && op2.shouldSpeculateFinalObject());
    }

    static bool shouldSpeculateArray(Node& op1, Node& op2)
    {
        return (op1.shouldSpeculateArray() && op2.shouldSpeculateObject())
            || (op1.shouldSpeculateObject() && op2.shouldSpeculateArray());
    }
    
    bool canSpeculateInteger()
    {
        return nodeCanSpeculateInteger(arithNodeFlags());
    }
    
#ifndef NDEBUG
    void dumpChildren(FILE* out)
    {
        if (child1() == NoNode)
            return;
        fprintf(out, "@%u", child1());
        if (child2() == NoNode)
            return;
        fprintf(out, ", @%u", child2());
        if (child3() == NoNode)
            return;
        fprintf(out, ", @%u", child3());
    }
#endif
    
    // This enum value describes the type of the node.
    NodeType op;
    // Used to look up exception handling information (currently implemented as a bytecode index).
    CodeOrigin codeOrigin;
    // References to up to 3 children (0 for no child).
    union {
        struct {
            NodeIndex child1, child2, child3;
        } fixed;
        struct {
            unsigned firstChild;
            unsigned numChildren;
        } variable;
    } children;

private:
    // 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;
    // Immediate values, accesses type-checked via accessors above. The first one is
    // big enough to store a pointer.
    uintptr_t m_opInfo;
    unsigned m_opInfo2;
    // The prediction ascribed to this node after propagation.
    PredictedType m_prediction;
};

} } // namespace JSC::DFG

#endif
#endif