diff options
Diffstat (limited to 'Source/JavaScriptCore/dfg/DFGNode.h')
-rw-r--r-- | Source/JavaScriptCore/dfg/DFGNode.h | 1076 |
1 files changed, 1076 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/dfg/DFGNode.h b/Source/JavaScriptCore/dfg/DFGNode.h new file mode 100644 index 000000000..cb5be691c --- /dev/null +++ b/Source/JavaScriptCore/dfg/DFGNode.h @@ -0,0 +1,1076 @@ +/* + * 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; + } + + // 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 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 |