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(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 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 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;
      }
      
      void deref()
      {
          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()
      {
  #if CPU(X86) || CPU(X86_64)
          return isInt8ArrayPrediction(prediction());
  #else
          return false;
  #endif
      }
      
      bool shouldSpeculateInt16Array()
      {
  #if CPU(X86) || CPU(X86_64)
          return isInt16ArrayPrediction(prediction());
  #else
          return false;
  #endif
      }
      
      bool shouldSpeculateInt32Array()
      {
          return isInt32ArrayPrediction(prediction());
      }
      
      bool shouldSpeculateUint8Array()
      {
          return isUint8ArrayPrediction(prediction());
      }
      
      bool shouldSpeculateUint16Array()
      {
          return isUint16ArrayPrediction(prediction());
      }
      
      bool shouldSpeculateUint32Array()
      {
          return isUint32ArrayPrediction(prediction());
      }
      
      bool shouldSpeculateFloat32Array()
      {
  #if CPU(X86) || CPU(X86_64)
          return isFloat32ArrayPrediction(prediction());
  #else
          return false;
  #endif
      }
      
      bool shouldSpeculateFloat64Array()
      {
          return isFloat64ArrayPrediction(prediction());
      }
      
      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
  