| /* |
| * 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 |