summaryrefslogtreecommitdiff
path: root/src/jit/gentree.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/jit/gentree.h')
-rw-r--r--src/jit/gentree.h563
1 files changed, 404 insertions, 159 deletions
diff --git a/src/jit/gentree.h b/src/jit/gentree.h
index 4efeeae620..4611d35465 100644
--- a/src/jit/gentree.h
+++ b/src/jit/gentree.h
@@ -68,7 +68,7 @@ enum SpecialCodeKind
DECLARE_TYPED_ENUM(genTreeOps, BYTE)
{
-#define GTNODE(en, sn, cm, ok) GT_##en,
+#define GTNODE(en, sn, st, cm, ok) GT_##en,
#include "gtlist.h"
GT_COUNT,
@@ -429,13 +429,15 @@ struct GenTree
noway_assert(FitsIn<unsigned char>(level));
gtFPlvl = (unsigned char)level;
}
-#else // FEATURE_STACK_FP_X87
+#else // FEATURE_STACK_FP_X87
+
void gtCopyFPlvl(GenTree* other)
{
}
void gtSetFPlvl(unsigned level)
{
}
+
#endif // FEATURE_STACK_FP_X87
//
@@ -564,7 +566,7 @@ public:
bool isContainedIntOrIImmed() const
{
- return isContained() && IsCnsIntOrI();
+ return isContained() && IsCnsIntOrI() && !isContainedSpillTemp();
}
bool isContainedFltOrDblImmed() const
@@ -766,15 +768,15 @@ public:
#ifdef LEGACY_BACKEND
#define GTF_SPILLED_OPER 0x00000100 // op1 has been spilled
#define GTF_SPILLED_OP2 0x00000200 // op2 has been spilled
-#else
+#else // !LEGACY_BACKEND
#define GTF_NOREG_AT_USE 0x00000100 // tree node is in memory at the point of use
-#endif // LEGACY_BACKEND
+#endif // !LEGACY_BACKEND
#define GTF_ZSF_SET 0x00000400 // the zero(ZF) and sign(SF) flags set to the operand
-#if FEATURE_SET_FLAGS
+
#define GTF_SET_FLAGS 0x00000800 // Requires that codegen for this node set the flags
// Use gtSetFlags() to check this flags
-#endif
+
#define GTF_IND_NONFAULTING 0x00000800 // An indir that cannot fault. GTF_SET_FLAGS is not used on indirs
#define GTF_MAKE_CSE 0x00002000 // Hoisted Expression: try hard to make this into CSE (see optPerformHoistExpr)
@@ -865,12 +867,18 @@ public:
#define GTF_IND_TLS_REF 0x08000000 // GT_IND -- the target is accessed via TLS
#define GTF_IND_ASG_LHS 0x04000000 // GT_IND -- this GT_IND node is (the effective val) of the LHS of an
// assignment; don't evaluate it independently.
-#define GTF_IND_UNALIGNED 0x02000000 // GT_IND -- the load or store is unaligned (we assume worst case
- // alignment of 1 byte)
-#define GTF_IND_INVARIANT 0x01000000 // GT_IND -- the target is invariant (a prejit indirection)
-#define GTF_IND_ARR_LEN 0x80000000 // GT_IND -- the indirection represents an array length (of the REF
- // contribution to its argument).
-#define GTF_IND_ARR_INDEX 0x00800000 // GT_IND -- the indirection represents an (SZ) array index
+#define GTF_IND_REQ_ADDR_IN_REG GTF_IND_ASG_LHS // GT_IND -- requires its addr operand to be evaluated
+ // into a register. This flag is useful in cases where it
+ // is required to generate register indirect addressing mode.
+ // One such case is virtual stub calls on xarch. This is only
+ // valid in the backend, where GTF_IND_ASG_LHS is not necessary
+ // (all such indirections will be lowered to GT_STOREIND).
+#define GTF_IND_UNALIGNED 0x02000000 // GT_IND -- the load or store is unaligned (we assume worst case
+ // alignment of 1 byte)
+#define GTF_IND_INVARIANT 0x01000000 // GT_IND -- the target is invariant (a prejit indirection)
+#define GTF_IND_ARR_LEN 0x80000000 // GT_IND -- the indirection represents an array length (of the REF
+ // contribution to its argument).
+#define GTF_IND_ARR_INDEX 0x00800000 // GT_IND -- the indirection represents an (SZ) array index
#define GTF_IND_FLAGS \
(GTF_IND_VOLATILE | GTF_IND_REFARR_LAYOUT | GTF_IND_TGTANYWHERE | GTF_IND_NONFAULTING | GTF_IND_TLS_REF | \
@@ -925,11 +933,12 @@ public:
#define GTF_ICON_FIELD_OFF 0x08000000 // GT_CNS_INT -- constant is a field offset
+#define GTF_ICON_SIMD_COUNT 0x04000000 // GT_CNS_INT -- constant is Vector<T>.Count
+
#define GTF_BLK_VOLATILE 0x40000000 // GT_ASG, GT_STORE_BLK, GT_STORE_OBJ, GT_STORE_DYNBLK
// -- is a volatile block operation
#define GTF_BLK_UNALIGNED 0x02000000 // GT_ASG, GT_STORE_BLK, GT_STORE_OBJ, GT_STORE_DYNBLK
// -- is an unaligned block operation
-#define GTF_BLK_INIT 0x01000000 // GT_ASG, GT_STORE_BLK, GT_STORE_OBJ, GT_STORE_DYNBLK -- is an init block operation
#define GTF_OVERFLOW 0x10000000 // GT_ADD, GT_SUB, GT_MUL, - Need overflow check
// GT_ASG_ADD, GT_ASG_SUB,
@@ -942,10 +951,13 @@ public:
#define GTF_ARRLEN_ARR_IDX 0x80000000 // GT_ARR_LENGTH -- Length which feeds into an array index expression
-#define GTF_LIST_AGGREGATE 0x80000000 // GT_LIST -- Indicates that this list should be treated as an
- // anonymous aggregate value (e.g. a multi-value argument).
+#define GTF_FIELD_LIST_HEAD 0x80000000 // GT_FIELD_LIST -- Indicates that this is the first field in a list of
+ // struct fields constituting a single call argument.
//----------------------------------------------------------------
+#define GTF_SIMD12_OP 0x80000000 // GT_SIMD -- Indicates that the operands need to be handled as SIMD12
+ // even if they have been retyped as SIMD16.
+//----------------------------------------------------------------
#define GTF_STMT_CMPADD 0x80000000 // GT_STMT -- added by compiler
#define GTF_STMT_HAS_CSE 0x40000000 // GT_STMT -- CSE def or use was subsituted
@@ -958,8 +970,10 @@ public:
#define GTF_DEBUG_NODE_MORPHED 0x00000001 // the node has been morphed (in the global morphing phase)
#define GTF_DEBUG_NODE_SMALL 0x00000002
#define GTF_DEBUG_NODE_LARGE 0x00000004
+#define GTF_DEBUG_NODE_CG_PRODUCED 0x00000008 // genProduceReg has been called on this node
+#define GTF_DEBUG_NODE_CG_CONSUMED 0x00000010 // genConsumeReg has been called on this node
-#define GTF_DEBUG_NODE_MASK 0x00000007 // These flags are all node (rather than operation) properties.
+#define GTF_DEBUG_NODE_MASK 0x0000001F // These flags are all node (rather than operation) properties.
#define GTF_DEBUG_VAR_CSE_REF 0x00800000 // GT_LCL_VAR -- This is a CSE LCL_VAR node
#endif // defined(DEBUG)
@@ -970,6 +984,8 @@ public:
#ifdef DEBUG
unsigned gtTreeID;
unsigned gtSeqNum; // liveness traversal order within the current statement
+
+ int gtUseNum; // use-ordered traversal within the function
#endif
static const unsigned short gtOperKindTable[];
@@ -1011,9 +1027,9 @@ public:
return gtType != TYP_VOID;
}
- if (gtOper == GT_LIST)
+ if (gtOper == GT_FIELD_LIST)
{
- return (gtFlags & GTF_LIST_AGGREGATE) != 0;
+ return (gtFlags & GTF_FIELD_LIST_HEAD) != 0;
}
return true;
@@ -1033,14 +1049,14 @@ public:
return IsNothingNode();
case GT_ARGPLACE:
- // ARGPLACE nodes may not be present in a block's LIR sequence, but they may
+ case GT_LIST:
+ // ARGPLACE and LIST nodes may not be present in a block's LIR sequence, but they may
// be present as children of an LIR node.
return (gtNext == nullptr) && (gtPrev == nullptr);
- case GT_LIST:
- // LIST nodes may only be present in an LIR sequence if they represent aggregates.
- // They are always allowed, however, as children of an LIR node.
- return ((gtFlags & GTF_LIST_AGGREGATE) != 0) || ((gtNext == nullptr) && (gtPrev == nullptr));
+ case GT_FIELD_LIST:
+ // Only the head of the FIELD_LIST is present in the block's LIR sequence.
+ return (((gtFlags & GTF_FIELD_LIST_HEAD) != 0) || ((gtNext == nullptr) && (gtPrev == nullptr)));
case GT_ADDR:
{
@@ -1130,6 +1146,21 @@ public:
return (gtOper == GT_LEA);
}
+ static bool OperIsInitVal(genTreeOps gtOper)
+ {
+ return (gtOper == GT_INIT_VAL);
+ }
+
+ bool OperIsInitVal() const
+ {
+ return OperIsInitVal(OperGet());
+ }
+
+ bool IsConstInitVal()
+ {
+ return (gtOper == GT_CNS_INT) || (OperIsInitVal() && (gtGetOp1()->gtOper == GT_CNS_INT));
+ }
+
bool OperIsBlkOp();
bool OperIsCopyBlkOp();
bool OperIsInitBlkOp();
@@ -1146,6 +1177,16 @@ public:
return OperIsBlk(OperGet());
}
+ static bool OperIsDynBlk(genTreeOps gtOper)
+ {
+ return ((gtOper == GT_DYN_BLK) || (gtOper == GT_STORE_DYN_BLK));
+ }
+
+ bool OperIsDynBlk() const
+ {
+ return OperIsDynBlk(OperGet());
+ }
+
static bool OperIsStoreBlk(genTreeOps gtOper)
{
return ((gtOper == GT_STORE_BLK) || (gtOper == GT_STORE_OBJ) || (gtOper == GT_STORE_DYN_BLK));
@@ -1206,7 +1247,7 @@ public:
return OperIsLocalRead(OperGet());
}
- bool OperIsCompare()
+ bool OperIsCompare() const
{
return (OperKind(gtOper) & GTK_RELOP) != 0;
}
@@ -1270,7 +1311,6 @@ public:
{
case GT_ADD_HI:
case GT_SUB_HI:
- case GT_MUL_HI:
case GT_DIV_HI:
case GT_MOD_HI:
return true;
@@ -1396,8 +1436,7 @@ public:
static bool OperIsStore(genTreeOps gtOper)
{
return (gtOper == GT_STOREIND || gtOper == GT_STORE_LCL_VAR || gtOper == GT_STORE_LCL_FLD ||
- gtOper == GT_STORE_CLS_VAR || gtOper == GT_STORE_BLK || gtOper == GT_STORE_OBJ ||
- gtOper == GT_STORE_DYN_BLK);
+ gtOper == GT_STORE_BLK || gtOper == GT_STORE_OBJ || gtOper == GT_STORE_DYN_BLK);
}
static bool OperIsAtomicOp(genTreeOps gtOper)
@@ -1425,9 +1464,34 @@ public:
return OperIsSIMD(gtOper);
}
- bool OperIsAggregate()
+ bool OperIsFieldListHead()
+ {
+ return (gtOper == GT_FIELD_LIST) && ((gtFlags & GTF_FIELD_LIST_HEAD) != 0);
+ }
+
+ bool OperIsConditionalJump() const
+ {
+ return (gtOper == GT_JTRUE) || (gtOper == GT_JCC);
+ }
+
+ static bool OperIsBoundsCheck(genTreeOps op)
+ {
+ if (op == GT_ARR_BOUNDS_CHECK)
+ {
+ return true;
+ }
+#ifdef FEATURE_SIMD
+ if (op == GT_SIMD_CHK)
+ {
+ return true;
+ }
+#endif // FEATURE_SIMD
+ return false;
+ }
+
+ bool OperIsBoundsCheck() const
{
- return (gtOper == GT_LIST) && ((gtFlags & GTF_LIST_AGGREGATE) != 0);
+ return OperIsBoundsCheck(OperGet());
}
// Requires that "op" is an op= operator. Returns
@@ -1462,6 +1526,7 @@ public:
switch (gtOper)
{
case GT_LIST:
+ case GT_FIELD_LIST:
case GT_INTRINSIC:
case GT_LEA:
#ifdef FEATURE_SIMD
@@ -1474,19 +1539,47 @@ public:
}
static inline bool RequiresNonNullOp2(genTreeOps oper);
- bool IsListForMultiRegArg();
+ bool IsValidCallArgument();
#endif // DEBUG
inline bool IsFPZero();
inline bool IsIntegralConst(ssize_t constVal);
+ inline bool IsIntegralConstVector(ssize_t constVal);
inline bool IsBoxedValue();
- bool IsList() const
+ inline bool IsSIMDEqualityOrInequality() const;
+
+ static bool OperIsList(genTreeOps gtOper)
{
return gtOper == GT_LIST;
}
+ bool OperIsList() const
+ {
+ return OperIsList(gtOper);
+ }
+
+ static bool OperIsFieldList(genTreeOps gtOper)
+ {
+ return gtOper == GT_FIELD_LIST;
+ }
+
+ bool OperIsFieldList() const
+ {
+ return OperIsFieldList(gtOper);
+ }
+
+ static bool OperIsAnyList(genTreeOps gtOper)
+ {
+ return OperIsList(gtOper) || OperIsFieldList(gtOper);
+ }
+
+ bool OperIsAnyList() const
+ {
+ return OperIsAnyList(gtOper);
+ }
+
inline GenTreePtr MoveNext();
inline GenTreePtr Current();
@@ -1508,6 +1601,8 @@ public:
// Get the parent of this node, and optionally capture the pointer to the child so that it can be modified.
GenTreePtr gtGetParent(GenTreePtr** parentChildPtrPtr);
+ void ReplaceOperand(GenTree** useEdge, GenTree* replacement);
+
inline GenTreePtr gtEffectiveVal(bool commaOnly = false);
// Return the child of this node if it is a GT_RELOAD or GT_COPY; otherwise simply return the node itself
@@ -1536,7 +1631,13 @@ public:
public:
#if SMALL_TREE_NODES
static unsigned char s_gtNodeSizes[];
+#if NODEBASH_STATS || MEASURE_NODE_SIZE || COUNT_AST_OPERS
+ static unsigned char s_gtTrueSizes[];
+#endif
+#if COUNT_AST_OPERS
+ static LONG s_gtNodeCounts[];
#endif
+#endif // SMALL_TREE_NODES
static void InitNodeSize();
@@ -1555,15 +1656,19 @@ public:
static bool Compare(GenTreePtr op1, GenTreePtr op2, bool swapOK = false);
//---------------------------------------------------------------------
-#ifdef DEBUG
- //---------------------------------------------------------------------
+#if defined(DEBUG)
static const char* NodeName(genTreeOps op);
+#endif
+#if defined(DEBUG) || NODEBASH_STATS || MEASURE_NODE_SIZE || COUNT_AST_OPERS
static const char* OpName(genTreeOps op);
+#endif
-//---------------------------------------------------------------------
+#if MEASURE_NODE_SIZE && SMALL_TREE_NODES
+ static const char* OpStructName(genTreeOps op);
#endif
+
//---------------------------------------------------------------------
bool IsNothingNode() const;
@@ -1583,6 +1688,7 @@ public:
// set gtOper and only keep GTF_COMMON_MASK flags
void ChangeOper(genTreeOps oper, ValueNumberUpdate vnUpdate = CLEAR_VN);
void ChangeOperUnchecked(genTreeOps oper);
+ void SetOperRaw(genTreeOps oper);
void ChangeType(var_types newType)
{
@@ -1597,6 +1703,20 @@ public:
}
}
+#if SMALL_TREE_NODES
+#if NODEBASH_STATS
+ static void RecordOperBashing(genTreeOps operOld, genTreeOps operNew);
+ static void ReportOperBashing(FILE* fp);
+#else
+ static void RecordOperBashing(genTreeOps operOld, genTreeOps operNew)
+ { /* do nothing */
+ }
+ static void ReportOperBashing(FILE* fp)
+ { /* do nothing */
+ }
+#endif
+#endif
+
bool IsLocal() const
{
return OperIsLocal(OperGet());
@@ -1777,6 +1897,14 @@ public:
bool gtOverflowEx() const;
bool gtSetFlags() const;
bool gtRequestSetFlags();
+
+ // Returns true if the codegen of this tree node
+ // sets ZF and SF flags.
+ bool gtSetZSFlags() const
+ {
+ return (gtFlags & GTF_ZSF_SET) != 0;
+ }
+
#ifdef DEBUG
bool gtIsValid64RsltMul();
static int gtDispFlags(unsigned flags, unsigned debugFlags);
@@ -1827,10 +1955,10 @@ public:
// Returns an iterator that will produce the use edge to each operand of this node. Differs
// from the sequence of nodes produced by a loop over `GetChild` in its handling of call, phi,
// and block op nodes.
- GenTreeUseEdgeIterator GenTree::UseEdgesBegin();
- GenTreeUseEdgeIterator GenTree::UseEdgesEnd();
+ GenTreeUseEdgeIterator UseEdgesBegin();
+ GenTreeUseEdgeIterator UseEdgesEnd();
- IteratorPair<GenTreeUseEdgeIterator> GenTree::UseEdges();
+ IteratorPair<GenTreeUseEdgeIterator> UseEdges();
// Returns an iterator that will produce each operand of this node. Differs from the sequence
// of nodes produced by a loop over `GetChild` in its handling of call, phi, and block op
@@ -1866,6 +1994,10 @@ public:
gtFlags &= ~GTF_REUSE_REG_VAL;
}
+#if MEASURE_NODE_SIZE
+ static void DumpNodeSizes(FILE* fp);
+#endif
+
#ifdef DEBUG
private:
@@ -1931,7 +2063,7 @@ class GenTreeUseEdgeIterator final
#ifdef FEATURE_SIMD
void MoveToNextSIMDUseEdge();
#endif
- void MoveToNextAggregateUseEdge();
+ void MoveToNextFieldUseEdge();
public:
GenTreeUseEdgeIterator();
@@ -2128,7 +2260,7 @@ struct GenTreeIntConCommon : public GenTree
}
bool ImmedValNeedsReloc(Compiler* comp);
- bool GenTreeIntConCommon::ImmedValCanBeFolded(Compiler* comp, genTreeOps op);
+ bool ImmedValCanBeFolded(Compiler* comp, genTreeOps op);
#ifdef _TARGET_XARCH_
bool FitsInAddrBase(Compiler* comp);
@@ -2629,18 +2761,13 @@ struct GenTreeField : public GenTree
// method names for the arguments.
struct GenTreeArgList : public GenTreeOp
{
- bool IsAggregate() const
- {
- return (gtFlags & GTF_LIST_AGGREGATE) != 0;
- }
-
GenTreePtr& Current()
{
return gtOp1;
}
GenTreeArgList*& Rest()
{
- assert(gtOp2 == nullptr || gtOp2->OperGet() == GT_LIST);
+ assert(gtOp2 == nullptr || gtOp2->OperIsAnyList());
return *reinterpret_cast<GenTreeArgList**>(&gtOp2);
}
@@ -2654,20 +2781,68 @@ struct GenTreeArgList : public GenTreeOp
{
}
- GenTreeArgList(GenTreePtr arg, GenTreeArgList* rest) : GenTreeOp(GT_LIST, TYP_VOID, arg, rest)
+ GenTreeArgList(GenTreePtr arg, GenTreeArgList* rest) : GenTreeArgList(GT_LIST, arg, rest)
{
- // With structs passed in multiple args we could have an arg
- // GT_LIST containing a list of LCL_FLDs, see IsListForMultiRegArg()
- //
- assert((arg != nullptr) && ((!arg->IsList()) || (arg->IsListForMultiRegArg())));
+ }
+
+ GenTreeArgList(genTreeOps oper, GenTreePtr arg, GenTreeArgList* rest) : GenTreeOp(oper, TYP_VOID, arg, rest)
+ {
+ assert(OperIsAnyList(oper));
+ assert((arg != nullptr) && arg->IsValidCallArgument());
gtFlags |= arg->gtFlags & GTF_ALL_EFFECT;
if (rest != nullptr)
{
gtFlags |= rest->gtFlags & GTF_ALL_EFFECT;
}
}
+};
+
+// Represents a list of fields constituting a struct, when it is passed as an argument.
+// The first field of the struct is marked with the GTF_FIELD_LIST_HEAD flag, and
+// in LIR form it is the only member of the list that is threaded into the execution
+// order.
+// It differs from the GenTreeArgList in a couple of ways:
+// - The entire list represents a single argument.
+// - It contains additional fields to provide the offset and type of the field.
+//
+struct GenTreeFieldList : public GenTreeArgList
+{
+ unsigned gtFieldOffset;
+ var_types gtFieldType;
+
+ bool IsFieldListHead() const
+ {
+ return (gtFlags & GTF_FIELD_LIST_HEAD) != 0;
+ }
- GenTreeArgList* Prepend(Compiler* compiler, GenTree* element);
+#if DEBUGGABLE_GENTREE
+ GenTreeFieldList() : GenTreeArgList()
+ {
+ }
+#endif
+
+ GenTreeFieldList*& Rest()
+ {
+ assert(gtOp2 == nullptr || gtOp2->OperGet() == GT_FIELD_LIST);
+ return *reinterpret_cast<GenTreeFieldList**>(&gtOp2);
+ }
+
+ GenTreeFieldList(GenTreePtr arg, unsigned fieldOffset, var_types fieldType, GenTreeFieldList* prevList)
+ : GenTreeArgList(GT_FIELD_LIST, arg, nullptr)
+ {
+ // While GT_FIELD_LIST can be in a GT_LIST, GT_FIELD_LISTs cannot be nested or have GT_LISTs.
+ assert(!arg->OperIsAnyList());
+ gtFieldOffset = fieldOffset;
+ gtFieldType = fieldType;
+ if (prevList == nullptr)
+ {
+ gtFlags |= GTF_FIELD_LIST_HEAD;
+ }
+ else
+ {
+ prevList->gtOp2 = this;
+ }
+ }
};
// There was quite a bit of confusion in the code base about which of gtOp1 and gtOp2 was the
@@ -3360,8 +3535,13 @@ struct GenTreeCall final : public GenTree
bool IsHelperCall(Compiler* compiler, unsigned helper) const;
+ void ReplaceCallOperand(GenTree** operandUseEdge, GenTree* replacement);
+
+ bool AreArgsComplete() const;
+
GenTreeCall(var_types type) : GenTree(GT_CALL, type)
{
+ fgArgInfo = nullptr;
}
#if DEBUGGABLE_GENTREE
GenTreeCall() : GenTree()
@@ -4017,6 +4197,19 @@ struct GenTreeObj : public GenTreeBlk
// Let's assert it just to be safe.
noway_assert(roundUp(gtBlkSize, REGSIZE_BYTES) == gtBlkSize);
}
+ else
+ {
+ genTreeOps newOper = GT_BLK;
+ if (gtOper == GT_STORE_OBJ)
+ {
+ newOper = GT_STORE_BLK;
+ }
+ else
+ {
+ assert(gtOper == GT_OBJ);
+ }
+ SetOper(newOper);
+ }
}
void CopyGCInfo(GenTreeObj* srcObj)
@@ -4068,6 +4261,8 @@ public:
GenTreeDynBlk(GenTreePtr addr, GenTreePtr dynamicSize)
: GenTreeBlk(GT_DYN_BLK, TYP_STRUCT, addr, 0), gtDynamicSize(dynamicSize), gtEvalSizeFirst(false)
{
+ // Conservatively the 'addr' could be null or point into the global heap.
+ gtFlags |= GTF_EXCEPT | GTF_GLOB_REF;
gtFlags |= (dynamicSize->gtFlags & GTF_ALL_EFFECT);
}
@@ -4198,10 +4393,7 @@ struct GenTreeStmt : public GenTree
GenTreePtr gtStmtExpr; // root of the expression tree
GenTreePtr gtStmtList; // first node (for forward walks)
InlineContext* gtInlineContext; // The inline context for this statement.
-
-#if defined(DEBUGGING_SUPPORT) || defined(DEBUG)
- IL_OFFSETX gtStmtILoffsx; // instr offset (if available)
-#endif
+ IL_OFFSETX gtStmtILoffsx; // instr offset (if available)
#ifdef DEBUG
IL_OFFSET gtStmtLastILoffs; // instr offset at end of stmt
@@ -4240,9 +4432,7 @@ struct GenTreeStmt : public GenTree
, gtStmtExpr(expr)
, gtStmtList(nullptr)
, gtInlineContext(nullptr)
-#if defined(DEBUGGING_SUPPORT) || defined(DEBUG)
, gtStmtILoffsx(offset)
-#endif
#ifdef DEBUG
, gtStmtLastILoffs(BAD_IL_OFFSET)
#endif
@@ -4350,20 +4540,19 @@ struct GenTreePutArgStk : public GenTreeUnOp
GenTreePutArgStk(genTreeOps oper,
var_types type,
- unsigned slotNum FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(unsigned numSlots)
- FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(bool isStruct),
+ unsigned slotNum PUT_STRUCT_ARG_STK_ONLY_ARG(unsigned numSlots)
+ PUT_STRUCT_ARG_STK_ONLY_ARG(bool isStruct),
bool _putInIncomingArgArea = false DEBUGARG(GenTreePtr callNode = nullptr)
DEBUGARG(bool largeNode = false))
: GenTreeUnOp(oper, type DEBUGARG(largeNode))
, gtSlotNum(slotNum)
, putInIncomingArgArea(_putInIncomingArgArea)
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
- , gtPutArgStkKind(PutArgStkKindInvalid)
+#ifdef FEATURE_PUT_STRUCT_ARG_STK
+ , gtPutArgStkKind(Kind::Invalid)
, gtNumSlots(numSlots)
- , gtIsStruct(isStruct)
, gtNumberReferenceSlots(0)
, gtGcPtrs(nullptr)
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#endif // FEATURE_PUT_STRUCT_ARG_STK
{
#ifdef DEBUG
gtCall = callNode;
@@ -4373,20 +4562,18 @@ struct GenTreePutArgStk : public GenTreeUnOp
GenTreePutArgStk(genTreeOps oper,
var_types type,
GenTreePtr op1,
- unsigned slotNum FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(unsigned numSlots)
- FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(bool isStruct),
+ unsigned slotNum PUT_STRUCT_ARG_STK_ONLY_ARG(unsigned numSlots),
bool _putInIncomingArgArea = false DEBUGARG(GenTreePtr callNode = nullptr)
DEBUGARG(bool largeNode = false))
: GenTreeUnOp(oper, type, op1 DEBUGARG(largeNode))
, gtSlotNum(slotNum)
, putInIncomingArgArea(_putInIncomingArgArea)
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
- , gtPutArgStkKind(PutArgStkKindInvalid)
+#ifdef FEATURE_PUT_STRUCT_ARG_STK
+ , gtPutArgStkKind(Kind::Invalid)
, gtNumSlots(numSlots)
- , gtIsStruct(isStruct)
, gtNumberReferenceSlots(0)
, gtGcPtrs(nullptr)
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#endif // FEATURE_PUT_STRUCT_ARG_STK
{
#ifdef DEBUG
gtCall = callNode;
@@ -4397,18 +4584,16 @@ struct GenTreePutArgStk : public GenTreeUnOp
GenTreePutArgStk(genTreeOps oper,
var_types type,
- unsigned slotNum FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(unsigned numSlots)
- FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(bool isStruct) DEBUGARG(GenTreePtr callNode = NULL)
- DEBUGARG(bool largeNode = false))
+ unsigned slotNum PUT_STRUCT_ARG_STK_ONLY_ARG(unsigned numSlots)
+ DEBUGARG(GenTreePtr callNode = NULL) DEBUGARG(bool largeNode = false))
: GenTreeUnOp(oper, type DEBUGARG(largeNode))
, gtSlotNum(slotNum)
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
- , gtPutArgStkKind(PutArgStkKindInvalid)
+#ifdef FEATURE_PUT_STRUCT_ARG_STK
+ , gtPutArgStkKind(Kind::Invalid)
, gtNumSlots(numSlots)
- , gtIsStruct(isStruct)
, gtNumberReferenceSlots(0)
, gtGcPtrs(nullptr)
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#endif // FEATURE_PUT_STRUCT_ARG_STK
{
#ifdef DEBUG
gtCall = callNode;
@@ -4418,18 +4603,16 @@ struct GenTreePutArgStk : public GenTreeUnOp
GenTreePutArgStk(genTreeOps oper,
var_types type,
GenTreePtr op1,
- unsigned slotNum FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(unsigned numSlots)
- FEATURE_UNIX_AMD64_STRUCT_PASSING_ONLY_ARG(bool isStruct) DEBUGARG(GenTreePtr callNode = NULL)
- DEBUGARG(bool largeNode = false))
+ unsigned slotNum PUT_STRUCT_ARG_STK_ONLY_ARG(unsigned numSlots)
+ DEBUGARG(GenTreePtr callNode = NULL) DEBUGARG(bool largeNode = false))
: GenTreeUnOp(oper, type, op1 DEBUGARG(largeNode))
, gtSlotNum(slotNum)
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
- , gtPutArgStkKind(PutArgStkKindInvalid)
+#ifdef FEATURE_PUT_STRUCT_ARG_STK
+ , gtPutArgStkKind(Kind::Invalid)
, gtNumSlots(numSlots)
- , gtIsStruct(isStruct)
, gtNumberReferenceSlots(0)
, gtGcPtrs(nullptr)
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#endif // FEATURE_PUT_STRUCT_ARG_STK
{
#ifdef DEBUG
gtCall = callNode;
@@ -4442,14 +4625,14 @@ struct GenTreePutArgStk : public GenTreeUnOp
return gtSlotNum * TARGET_POINTER_SIZE;
}
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#ifdef FEATURE_PUT_STRUCT_ARG_STK
unsigned getArgSize()
{
return gtNumSlots * TARGET_POINTER_SIZE;
}
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#endif // FEATURE_PUT_STRUCT_ARG_STK
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#ifdef FEATURE_PUT_STRUCT_ARG_STK
//------------------------------------------------------------------------
// setGcPointers: Sets the number of references and the layout of the struct object returned by the VM.
//
@@ -4471,27 +4654,32 @@ struct GenTreePutArgStk : public GenTreeUnOp
gtNumberReferenceSlots = numPointers;
gtGcPtrs = pointers;
}
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#endif // FEATURE_PUT_STRUCT_ARG_STK
#ifdef DEBUG
GenTreePtr gtCall; // the call node to which this argument belongs
#endif
-#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
+#ifdef FEATURE_PUT_STRUCT_ARG_STK
// Instruction selection: during codegen time, what code sequence we will be using
// to encode this operation.
+ // TODO-Throughput: The following information should be obtained from the child
+ // block node.
- enum PutArgStkKind : __int8{
- PutArgStkKindInvalid, PutArgStkKindRepInstr, PutArgStkKindUnroll,
+ enum class Kind : __int8{
+ Invalid, RepInstr, Unroll, Push, PushAllSlots,
};
- PutArgStkKind gtPutArgStkKind;
+ Kind gtPutArgStkKind;
+ bool isPushKind()
+ {
+ return (gtPutArgStkKind == Kind::Push) || (gtPutArgStkKind == Kind::PushAllSlots);
+ }
unsigned gtNumSlots; // Number of slots for the argument to be passed on stack
- bool gtIsStruct; // This stack arg is a struct.
unsigned gtNumberReferenceSlots; // Number of reference slots.
BYTE* gtGcPtrs; // gcPointers
-#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
+#endif // FEATURE_PUT_STRUCT_ARG_STK
#if DEBUGGABLE_GENTREE
GenTreePutArgStk() : GenTreeUnOp()
@@ -4644,6 +4832,23 @@ struct GenTreeAllocObj final : public GenTreeUnOp
#endif
};
+struct GenTreeJumpCC final : public GenTree
+{
+ genTreeOps gtCondition; // any relop
+
+ GenTreeJumpCC(genTreeOps condition)
+ : GenTree(GT_JCC, TYP_VOID DEBUGARG(/*largeNode*/ FALSE)), gtCondition(condition)
+ {
+ assert(OperIsCompare(condition));
+ }
+
+#if DEBUGGABLE_GENTREE
+ GenTreeJumpCC() : GenTree()
+ {
+ }
+#endif // DEBUGGABLE_GENTREE
+};
+
//------------------------------------------------------------------------
// Deferred inline functions of GenTree -- these need the subtypes above to
// be defined already.
@@ -4673,34 +4878,31 @@ inline bool GenTree::OperIsDynBlkOp()
return false;
}
-inline bool GenTree::OperIsCopyBlkOp()
+inline bool GenTree::OperIsInitBlkOp()
{
- if (gtOper == GT_ASG)
+ if (!OperIsBlkOp())
{
- return (varTypeIsStruct(gtGetOp1()) && ((gtFlags & GTF_BLK_INIT) == 0));
+ return false;
}
#ifndef LEGACY_BACKEND
- else if (OperIsStoreBlk())
- {
- return ((gtFlags & GTF_BLK_INIT) == 0);
- }
-#endif
- return false;
-}
-
-inline bool GenTree::OperIsInitBlkOp()
-{
+ GenTree* src;
if (gtOper == GT_ASG)
{
- return (varTypeIsStruct(gtGetOp1()) && ((gtFlags & GTF_BLK_INIT) != 0));
+ src = gtGetOp2();
}
-#ifndef LEGACY_BACKEND
- else if (OperIsStoreBlk())
+ else
{
- return ((gtFlags & GTF_BLK_INIT) != 0);
+ src = AsBlk()->Data()->gtSkipReloadOrCopy();
}
-#endif
- return false;
+#else // LEGACY_BACKEND
+ GenTree* src = gtGetOp2();
+#endif // LEGACY_BACKEND
+ return src->OperIsInitVal() || src->OperIsConst();
+}
+
+inline bool GenTree::OperIsCopyBlkOp()
+{
+ return OperIsBlkOp() && !OperIsInitBlkOp();
}
//------------------------------------------------------------------------
@@ -4748,34 +4950,63 @@ inline bool GenTree::IsIntegralConst(ssize_t constVal)
return false;
}
+//-------------------------------------------------------------------
+// IsIntegralConstVector: returns true if this this is a SIMD vector
+// with all its elements equal to an integral constant.
+//
+// Arguments:
+// constVal - const value of vector element
+//
+// Returns:
+// True if this represents an integral const SIMD vector.
+//
+inline bool GenTree::IsIntegralConstVector(ssize_t constVal)
+{
+#ifdef FEATURE_SIMD
+ // SIMDIntrinsicInit intrinsic with a const value as initializer
+ // represents a const vector.
+ if ((gtOper == GT_SIMD) && (gtSIMD.gtSIMDIntrinsicID == SIMDIntrinsicInit) && gtGetOp1()->IsIntegralConst(constVal))
+ {
+ assert(varTypeIsIntegral(gtSIMD.gtSIMDBaseType));
+ assert(gtGetOp2() == nullptr);
+ return true;
+ }
+#endif
+
+ return false;
+}
+
inline bool GenTree::IsBoxedValue()
{
assert(gtOper != GT_BOX || gtBox.BoxOp() != nullptr);
return (gtOper == GT_BOX) && (gtFlags & GTF_BOX_VALUE);
}
+inline bool GenTree::IsSIMDEqualityOrInequality() const
+{
+#ifdef FEATURE_SIMD
+ if (gtOper == GT_SIMD)
+ {
+ // Has to cast away const-ness since AsSIMD() method is non-const.
+ GenTreeSIMD* simdNode = const_cast<GenTree*>(this)->AsSIMD();
+ return (simdNode->gtSIMDIntrinsicID == SIMDIntrinsicOpEquality ||
+ simdNode->gtSIMDIntrinsicID == SIMDIntrinsicOpInEquality);
+ }
+#endif
+
+ return false;
+}
+
inline GenTreePtr GenTree::MoveNext()
{
- assert(IsList());
+ assert(OperIsAnyList());
return gtOp.gtOp2;
}
#ifdef DEBUG
//------------------------------------------------------------------------
-// IsListForMultiRegArg: Given an GenTree node that represents an argument
-// enforce (or don't enforce) the following invariant.
-//
-// For LEGACY_BACKEND or architectures that don't support MultiReg args
-// we don't allow a GT_LIST at all.
-//
-// Currently for AMD64 UNIX we allow a limited case where a GT_LIST is
-// allowed but every element must be a GT_LCL_FLD.
-//
-// For the future targets that allow for Multireg args (and this includes
-// the current ARM64 target) we allow a GT_LIST of arbitrary nodes, these
-// would typically start out as GT_LCL_VARs or GT_LCL_FLDS or GT_INDs,
-// but could be changed into constants or GT_COMMA trees by the later
-// optimization phases.
+// IsValidCallArgument: Given an GenTree node that represents an argument
+// enforce (or don't enforce) the following invariant.
//
// Arguments:
// instance method for a GenTree node
@@ -4784,33 +5015,46 @@ inline GenTreePtr GenTree::MoveNext()
// true: the GenTree node is accepted as a valid argument
// false: the GenTree node is not accepted as a valid argumeny
//
-inline bool GenTree::IsListForMultiRegArg()
+// Notes:
+// For targets that don't support arguments as a list of fields, we do not support GT_FIELD_LIST.
+//
+// Currently for AMD64 UNIX we allow a limited case where a GT_FIELD_LIST is
+// allowed but every element must be a GT_LCL_FLD.
+//
+// For the future targets that allow for Multireg args (and this includes the current ARM64 target),
+// or that allow for passing promoted structs, we allow a GT_FIELD_LIST of arbitrary nodes.
+// These would typically start out as GT_LCL_VARs or GT_LCL_FLDS or GT_INDs,
+// but could be changed into constants or GT_COMMA trees by the later
+// optimization phases.
+
+inline bool GenTree::IsValidCallArgument()
{
- if (!IsList())
+ if (OperIsList())
{
- // We don't have a GT_LIST, so just return true.
- return true;
+ // GT_FIELD_LIST is the only list allowed.
+ return false;
}
- else // We do have a GT_LIST
+ if (OperIsFieldList())
{
-#if defined(LEGACY_BACKEND) || !FEATURE_MULTIREG_ARGS
-
- // Not allowed to have a GT_LIST for an argument
- // unless we have a RyuJIT backend and FEATURE_MULTIREG_ARGS
+#if defined(LEGACY_BACKEND) || (!FEATURE_MULTIREG_ARGS && !FEATURE_PUT_STRUCT_ARG_STK)
+ // Not allowed to have a GT_FIELD_LIST for an argument
+ // unless we have a RyuJIT backend and FEATURE_MULTIREG_ARGS or FEATURE_PUT_STRUCT_ARG_STK
return false;
-#else // we have RyuJIT backend and FEATURE_MULTIREG_ARGS
+#else // we have RyuJIT backend and FEATURE_MULTIREG_ARGS or FEATURE_PUT_STRUCT_ARG_STK
#ifdef FEATURE_UNIX_AMD64_STRUCT_PASSING
- // For UNIX ABI we currently only allow a GT_LIST of GT_LCL_FLDs nodes
+ // For UNIX ABI we currently only allow a GT_FIELD_LIST of GT_LCL_FLDs nodes
GenTree* gtListPtr = this;
while (gtListPtr != nullptr)
{
// ToDo: fix UNIX_AMD64 so that we do not generate this kind of a List
// Note the list as currently created is malformed, as the last entry is a nullptr
if (gtListPtr->Current() == nullptr)
+ {
break;
+ }
// Only a list of GT_LCL_FLDs is allowed
if (gtListPtr->Current()->OperGet() != GT_LCL_FLD)
@@ -4821,25 +5065,27 @@ inline bool GenTree::IsListForMultiRegArg()
}
#endif // FEATURE_UNIX_AMD64_STRUCT_PASSING
- // Note that for non-UNIX ABI the GT_LIST may contain any node
+ // Note that for non-UNIX ABI the GT_FIELD_LIST may contain any node
//
- // We allow this GT_LIST as an argument
+ // We allow this GT_FIELD_LIST as an argument
return true;
-#endif // RyuJIT backend and FEATURE_MULTIREG_ARGS
+#endif // FEATURE_MULTIREG_ARGS
}
+ // We don't have either kind of list, so it satisfies the invariant.
+ return true;
}
#endif // DEBUG
inline GenTreePtr GenTree::Current()
{
- assert(IsList());
+ assert(OperIsAnyList());
return gtOp.gtOp1;
}
inline GenTreePtr* GenTree::pCurrent()
{
- assert(IsList());
+ assert(OperIsAnyList());
return &(gtOp.gtOp1);
}
@@ -4917,23 +5163,22 @@ inline GenTreePtr GenTree::gtGetOp2()
inline GenTreePtr GenTree::gtEffectiveVal(bool commaOnly)
{
- switch (gtOper)
+ GenTree* effectiveVal = this;
+ for (;;)
{
- case GT_COMMA:
- return gtOp.gtOp2->gtEffectiveVal(commaOnly);
-
- case GT_NOP:
- if (!commaOnly && gtOp.gtOp1 != nullptr)
- {
- return gtOp.gtOp1->gtEffectiveVal();
- }
- break;
-
- default:
- break;
+ if (effectiveVal->gtOper == GT_COMMA)
+ {
+ effectiveVal = effectiveVal->gtOp.gtOp2;
+ }
+ else if (!commaOnly && (effectiveVal->gtOper == GT_NOP) && (effectiveVal->gtOp.gtOp1 != nullptr))
+ {
+ effectiveVal = effectiveVal->gtOp.gtOp1;
+ }
+ else
+ {
+ return effectiveVal;
+ }
}
-
- return this;
}
inline GenTree* GenTree::gtSkipReloadOrCopy()