/* * 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 #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 #include 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 ""; static const int size = 64; static char description[size]; BoundsCheckedPointer 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(GetUint8ClampedArrayLength, 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(value)) { } explicit OpInfo(uint32_t value) : m_value(static_cast(value)) { } #if OS(DARWIN) || USE(JSVALUE64) explicit OpInfo(size_t value) : m_value(static_cast(value)) { } #endif explicit OpInfo(void* value) : m_value(reinterpret_cast(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(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(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(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(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(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(m_opInfo); } bool hasStructureTransitionData() { return op == PutStructure; } StructureTransitionData& structureTransitionData() { ASSERT(hasStructureTransitionData()); return *reinterpret_cast(m_opInfo); } bool hasStructureSet() { return op == CheckStructure; } StructureSet& structureSet() { ASSERT(hasStructureSet()); return *reinterpret_cast(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() { #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 shouldSpeculateUint8ClampedArray() { return isUint8ClampedArrayPrediction(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