blob: d72731cdd19d472bbcb6be4ae60568c6b329400e [file] [log] [blame]
/*
* 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
// Emit various logging information for debugging, including dumping the dataflow graphs.
#define DFG_DEBUG_VERBOSE 0
// Enable generation of dynamic checks into the instruction stream.
#define DFG_JIT_ASSERT 0
// Consistency check contents compiler data structures.
#define DFG_CONSISTENCY_CHECK 0
// Emit a breakpoint into the head of every generated function, to aid debugging in GDB.
#define DFG_JIT_BREAK_ON_EVERY_FUNCTION 0
// Emit a breakpoint into the head of every generated node, to aid debugging in GDB.
#define DFG_JIT_BREAK_ON_EVERY_BLOCK 0
// Emit a breakpoint into the head of every generated node, to aid debugging in GDB.
#define DFG_JIT_BREAK_ON_EVERY_NODE 0
// Emit a breakpoint into the speculation failure code.
#define DFG_JIT_BREAK_ON_SPECULATION_FAILURE 0
// Disable the DFG JIT without having to touch Platform.h!
#define DFG_DEBUG_LOCAL_DISBALE 0
// Disable the SpeculativeJIT without having to touch Platform.h!
#define DFG_DEBUG_LOCAL_DISBALE_SPECULATIVE 0
// Generate stats on how successful we were in making use of the DFG jit, and remaining on the hot path.
#define DFG_SUCCESS_STATS 0
#if ENABLE(DFG_JIT)
#include <wtf/Vector.h>
namespace JSC { namespace DFG {
// Type for a virtual register number (spill location).
// Using an enum to make this type-checked at compile time, to avert programmer errors.
enum VirtualRegister { InvalidVirtualRegister = -1 };
COMPILE_ASSERT(sizeof(VirtualRegister) == sizeof(int), VirtualRegister_is_32bit);
// Type for a reference to another node in the graph.
typedef uint32_t NodeIndex;
static const NodeIndex NoNode = UINT_MAX;
// Information used to map back from an exception to any handler/source information.
// (Presently implemented as a bytecode index).
typedef uint32_t ExceptionInfo;
// 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
// These values record the result type of the node (as checked by NodeResultMask, above), 0 for no result.
#define NodeResultJS 0x1000
#define NodeResultDouble 0x2000
#define NodeResultInt32 0x3000
// This macro defines a set of information about all known node types, used to populate NodeId, NodeType below.
#define FOR_EACH_DFG_OP(macro) \
/* Nodes for constants. */\
macro(JSConstant, NodeResultJS) \
macro(ConvertThis, NodeResultJS) \
\
/* Nodes for local variable access. */\
macro(GetLocal, NodeResultJS) \
macro(SetLocal, 0) \
macro(Phi, 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, NodeResultDouble) \
\
/* Nodes for arithmetic operations. */\
macro(ArithAdd, NodeResultDouble) \
macro(ArithSub, NodeResultDouble) \
macro(ArithMul, NodeResultDouble) \
macro(ArithDiv, NodeResultDouble) \
macro(ArithMod, NodeResultDouble) \
/* Arithmetic operators call ToNumber on their operands. */\
macro(ValueToNumber, NodeResultDouble | NodeMustGenerate) \
\
/* Add of values may either be arithmetic, or result in string concatenation. */\
macro(ValueAdd, NodeResultJS | NodeMustGenerate) \
\
/* 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) \
macro(PutByVal, NodeMustGenerate) \
macro(PutByValAlias, NodeMustGenerate) \
macro(GetById, NodeResultJS | NodeMustGenerate) \
macro(PutById, NodeMustGenerate) \
macro(PutByIdDirect, NodeMustGenerate) \
macro(GetMethod, NodeResultJS | NodeMustGenerate) \
macro(GetGlobalVar, NodeResultJS | NodeMustGenerate) \
macro(PutGlobalVar, NodeMustGenerate) \
\
/* Nodes for comparison operations. */\
macro(CompareLess, NodeResultJS | NodeMustGenerate) \
macro(CompareLessEq, NodeResultJS | NodeMustGenerate) \
macro(CompareGreater, NodeResultJS | NodeMustGenerate) \
macro(CompareGreaterEq, NodeResultJS | NodeMustGenerate) \
macro(CompareEq, NodeResultJS | NodeMustGenerate) \
macro(CompareStrictEq, NodeResultJS) \
\
/* Calls. */\
macro(Call, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \
macro(Construct, NodeResultJS | NodeMustGenerate | NodeHasVarArgs) \
\
/* Resolve nodes. */\
macro(Resolve, NodeResultJS | NodeMustGenerate) \
macro(ResolveBase, NodeResultJS | NodeMustGenerate) \
macro(ResolveBaseStrictPut, NodeResultJS | NodeMustGenerate) \
\
/* Nodes for misc operations. */\
macro(Breakpoint, NodeMustGenerate) \
macro(CheckHasInstance, NodeMustGenerate) \
macro(InstanceOf, NodeResultJS) \
macro(LogicalNot, NodeResultJS) \
\
/* Block terminals. */\
macro(Jump, NodeMustGenerate | NodeIsTerminal | NodeIsJump) \
macro(Branch, NodeMustGenerate | NodeIsTerminal | NodeIsBranch) \
macro(Return, NodeMustGenerate | NodeIsTerminal)
// 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
};
// 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(unsigned value) : m_value(value) {}
unsigned m_value;
};
typedef uint8_t PredictedType;
static const PredictedType PredictNone = 0;
static const PredictedType PredictCell = 0x01;
static const PredictedType PredictArray = 0x03;
static const PredictedType PredictInt32 = 0x04;
static const PredictedType PredictDouble = 0x08;
static const PredictedType PredictNumber = 0x0c;
inline bool isCellPrediction(PredictedType value)
{
return (value & PredictCell) == PredictCell && !(value & ~PredictArray);
}
inline bool isArrayPrediction(PredictedType value)
{
return value == PredictArray;
}
inline bool isInt32Prediction(PredictedType value)
{
return value == PredictInt32;
}
inline bool isDoublePrediction(PredictedType value)
{
return value == PredictDouble;
}
inline bool isNumberPrediction(PredictedType value)
{
return !!(value & PredictNumber) && !(value & ~PredictNumber);
}
#ifndef NDEBUG
inline const char* predictionToString(PredictedType value)
{
switch (value) {
case PredictNone:
return "p-bottom";
case PredictCell:
return "p-cell";
case PredictArray:
return "p-array";
case PredictInt32:
return "p-int32";
case PredictNumber:
return "p-number";
default:
return "p-top";
}
}
#endif
// === 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, ExceptionInfo exceptionInfo, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
: op(op)
, exceptionInfo(exceptionInfo)
, m_virtualRegister(InvalidVirtualRegister)
, m_refCount(0)
{
ASSERT(!(op & NodeHasVarArgs));
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, ExceptionInfo exceptionInfo, OpInfo imm, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
: op(op)
, exceptionInfo(exceptionInfo)
, m_virtualRegister(InvalidVirtualRegister)
, m_refCount(0)
, m_opInfo(imm.m_value)
{
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, ExceptionInfo exceptionInfo, OpInfo imm1, OpInfo imm2, NodeIndex child1 = NoNode, NodeIndex child2 = NoNode, NodeIndex child3 = NoNode)
: op(op)
, exceptionInfo(exceptionInfo)
, m_virtualRegister(InvalidVirtualRegister)
, m_refCount(0)
, m_opInfo(imm1.m_value)
, m_opInfo2(imm2.m_value)
{
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, ExceptionInfo exceptionInfo, OpInfo imm1, OpInfo imm2, unsigned firstChild, unsigned numChildren)
: op(op)
, exceptionInfo(exceptionInfo)
, m_virtualRegister(InvalidVirtualRegister)
, m_refCount(0)
, m_opInfo(imm1.m_value)
, m_opInfo2(imm2.m_value)
{
ASSERT(op & NodeHasVarArgs);
children.variable.firstChild = firstChild;
children.variable.numChildren = numChildren;
}
bool mustGenerate()
{
return op & NodeMustGenerate;
}
bool isConstant()
{
return op == JSConstant;
}
unsigned constantNumber()
{
ASSERT(isConstant());
return m_opInfo;
}
bool hasLocal()
{
return op == GetLocal || op == SetLocal;
}
VirtualRegister local()
{
ASSERT(hasLocal());
return (VirtualRegister)m_opInfo;
}
#if !ASSERT_DISABLED
// If we want to use this in production code, should make it faster -
// e.g. make hasIdentifier a flag in the bitfield.
bool hasIdentifier()
{
return op == GetById || op == PutById || op == PutByIdDirect || op == GetMethod
|| op == Resolve || op == ResolveBase || op == ResolveBaseStrictPut;
}
#endif
unsigned identifierNumber()
{
ASSERT(hasIdentifier());
return m_opInfo;
}
bool hasVarNumber()
{
return op == GetGlobalVar || op == PutGlobalVar;
}
unsigned varNumber()
{
ASSERT(hasVarNumber());
return m_opInfo;
}
bool hasResult()
{
return op & NodeResultMask;
}
bool hasInt32Result()
{
return (op & NodeResultMask) == NodeResultInt32;
}
bool hasDoubleResult()
{
return (op & NodeResultMask) == NodeResultDouble;
}
bool hasJSResult()
{
return (op & NodeResultMask) == NodeResultJS;
}
// Check for integers or doubles.
bool hasNumericResult()
{
// This check will need updating if more result types are added.
ASSERT((hasInt32Result() || hasDoubleResult()) == !hasJSResult());
return !hasJSResult();
}
bool isJump()
{
return op & NodeIsJump;
}
bool isBranch()
{
return op & NodeIsBranch;
}
bool isTerminal()
{
return op & NodeIsTerminal;
}
unsigned takenBytecodeOffset()
{
ASSERT(isBranch() || isJump());
return m_opInfo;
}
unsigned notTakenBytecodeOffset()
{
ASSERT(isBranch());
return m_opInfo2;
}
bool hasPrediction()
{
switch (op) {
case GetById:
case GetMethod:
case GetByVal:
case Call:
case Construct:
return true;
default:
return false;
}
}
PredictedType getPrediction()
{
ASSERT(hasPrediction());
return static_cast<PredictedType>(m_opInfo2);
}
void predict(PredictedType prediction)
{
ASSERT(hasPrediction());
m_opInfo2 |= prediction;
}
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;
}
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;
}
NodeIndex child1()
{
ASSERT(!(op & NodeHasVarArgs));
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;
}
// This enum value describes the type of the node.
NodeType op;
// Used to look up exception handling information (currently implemented as a bytecode index).
ExceptionInfo exceptionInfo;
// 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.
unsigned m_opInfo, m_opInfo2;
};
} } // namespace JSC::DFG
#endif
#endif