diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index 4e0e708be326c6..dd3f0907af8e60 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -5599,11 +5599,6 @@ Compiler::fgWalkResult Compiler::optVNConstantPropCurStmt(BasicBlock* block, Sta case GT_INTRINSIC: break; - case GT_INC_SATURATE: - case GT_MULHI: - assert(false && "Unexpected GT_INC_SATURATE/GT_MULHI node encountered before lowering"); - break; - case GT_JTRUE: break; diff --git a/src/coreclr/jit/fgstmt.cpp b/src/coreclr/jit/fgstmt.cpp index f5f07399a1d371..0730540d175e67 100644 --- a/src/coreclr/jit/fgstmt.cpp +++ b/src/coreclr/jit/fgstmt.cpp @@ -511,9 +511,9 @@ void Compiler::fgRemoveStmt(BasicBlock* block, Statement* stmt DEBUGARG(bool isU } /******************************************************************************/ -// Returns true if the operator is involved in control-flow -// TODO-Cleanup: Move this into genTreeKinds in genTree.h - +// Returns true if the operator is involved in control-flow. +// TODO-Cleanup: Make this a GenTreeOperKind. +// inline bool OperIsControlFlow(genTreeOps oper) { switch (oper) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index 1b99f5171cef08..ad05a1a45d774e 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -21,10 +21,17 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /*****************************************************************************/ const unsigned char GenTree::gtOperKindTable[] = { -#define GTNODE(en, st, cm, ok) (ok) + GTK_COMMUTE *cm, +#define GTNODE(en, st, cm, ok) ((ok)>K_MASK) + GTK_COMMUTE *cm, #include "gtlist.h" }; +#ifdef DEBUG +const GenTreeDebugOperKind GenTree::gtDebugOperKindTable[] = { +#define GTNODE(en, st, cm, ok) static_cast((ok)&DBK_MASK), +#include "gtlist.h" +}; +#endif // DEBUG + /***************************************************************************** * * The types of different GenTree nodes @@ -15592,29 +15599,32 @@ unsigned GenTree::IsLclVarUpdateTree(GenTree** pOtherTree, genTreeOps* pOper) return lclNum; } +#ifdef DEBUG //------------------------------------------------------------------------ // canBeContained: check whether this tree node may be a subcomponent of its parent for purposes // of code generation. // -// Return value: returns true if it is possible to contain this node and false otherwise. +// Return Value: +// True if it is possible to contain this node and false otherwise. +// bool GenTree::canBeContained() const { - assert(IsLIR()); + assert(OperIsLIR()); if (gtHasReg()) { return false; } - // It is not possible for nodes that do not produce values or that are not containable values - // to be contained. - if (((OperKind() & (GTK_NOVALUE | GTK_NOCONTAIN)) != 0) || (OperIsHWIntrinsic() && !isContainableHWIntrinsic())) + // It is not possible for nodes that do not produce values or that are not containable values to be contained. + if (!IsValue() || ((DebugOperKind() & DBK_NOCONTAIN) != 0) || (OperIsHWIntrinsic() && !isContainableHWIntrinsic())) { return false; } return true; } +#endif // DEBUG //------------------------------------------------------------------------ // isContained: check whether this tree node is a subcomponent of its parent for codegen purposes @@ -15631,7 +15641,7 @@ bool GenTree::canBeContained() const // bool GenTree::isContained() const { - assert(IsLIR()); + assert(OperIsLIR()); const bool isMarkedContained = ((gtFlags & GTF_CONTAINED) != 0); #ifdef DEBUG diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index b023c2bb8c52d4..46909dbd87194b 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -85,7 +85,7 @@ enum genTreeOps : BYTE // The following enum defines a set of bit flags that can be used // to classify expression tree nodes. // -enum genTreeKinds +enum GenTreeOperKind { GTK_SPECIAL = 0x00, // special operator GTK_LEAF = 0x01, // leaf operator @@ -95,12 +95,28 @@ enum genTreeKinds GTK_KINDMASK = (GTK_SPECIAL | GTK_LEAF | GTK_UNOP | GTK_BINOP), // operator kind mask GTK_SMPOP = (GTK_UNOP | GTK_BINOP), - GTK_COMMUTE = 0x08, // commutative operator - GTK_EXOP = 0x10, // Indicates that an oper for a node type that extends GenTreeOp (or GenTreeUnOp) - // by adding non-node fields to unary or binary operator. - GTK_NOVALUE = 0x20, // node does not produce a value - GTK_NOTLIR = 0x40, // node is not allowed in LIR - GTK_NOCONTAIN = 0x80, // this node is a value, but may not be contained + GTK_COMMUTE = 0x08, // commutative operator + GTK_EXOP = 0x10, // Indicates that an oper for a node type that extends GenTreeOp (or GenTreeUnOp) + // by adding non-node fields to unary or binary operator. + GTK_NOVALUE = 0x20, // node does not produce a value + + GTK_MASK = 0xFF +}; + +// The following enum defines a set of bit flags that describe opers for the purposes +// of DEBUG-only checks. This is separate from the above "GenTreeOperKind"s to avoid +// making the table for those larger in Release builds. However, it resides in the same +// "namespace" and so all values here must be distinct from those in "GenTreeOperKind". +// +enum GenTreeDebugOperKind +{ + DBK_FIRST_FLAG = GTK_MASK + 1, + + DBK_NOTHIR = DBK_FIRST_FLAG, // This oper is not supported in HIR (before rationalization). + DBK_NOTLIR = DBK_FIRST_FLAG << 1, // This oper is not supported in LIR (after rationalization). + DBK_NOCONTAIN = DBK_FIRST_FLAG << 2, // This oper produces a value, but may not be contained. + + DBK_MASK = ~GTK_MASK }; /*****************************************************************************/ @@ -878,8 +894,11 @@ struct GenTree public: // The register number is stored in a small format (8 bits), but the getters return and the setters take // a full-size (unsigned) format, to localize the casts here. + CLANG_FORMAT_COMMENT_ANCHOR; +#ifdef DEBUG bool canBeContained() const; +#endif // for codegen purposes, is this node a subnode of its parent bool isContained() const; @@ -1073,34 +1092,6 @@ struct GenTree return true; } - bool IsLIR() const - { - if ((OperKind(gtOper) & GTK_NOTLIR) != 0) - { - return false; - } - - switch (gtOper) - { - case GT_NOP: - // NOPs may only be present in LIR if they do not produce a value. - return IsNothingNode(); - - case GT_ADDR: - { - // ADDR ndoes may only be present in LIR if the location they refer to is not a - // local, class variable, or IND node. - GenTree* location = gtGetOp1(); - genTreeOps locationOp = location->OperGet(); - return !location->IsLocal() && (locationOp != GT_CLS_VAR) && (locationOp != GT_IND); - } - - default: - // All other nodes are assumed to be correct. - return true; - } - } - // LIR flags // These helper methods, along with the flag values they manipulate, are defined in lir.h // @@ -1645,6 +1636,20 @@ struct GenTree } #ifdef DEBUG + static const GenTreeDebugOperKind gtDebugOperKindTable[]; + + static GenTreeDebugOperKind DebugOperKind(genTreeOps oper) + { + assert(oper < GT_COUNT); + + return gtDebugOperKindTable[oper]; + } + + GenTreeDebugOperKind DebugOperKind() const + { + return DebugOperKind(OperGet()); + } + bool NullOp1Legal() const { assert(OperIsSimple()); @@ -1683,6 +1688,17 @@ struct GenTree } } + bool OperIsLIR() const + { + if (OperIs(GT_NOP)) + { + // NOPs may only be present in LIR if they do not produce a value. + return IsNothingNode(); + } + + return (DebugOperKind() & DBK_NOTLIR) == 0; + } + bool OperSupportsReverseOps() const; static bool RequiresNonNullOp2(genTreeOps oper); bool IsValidCallArgument(); diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 9b40861fef0fd0..1d0be8bd167664 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -11,7 +11,7 @@ // Node enum // , GenTree struct flavor // ,commutative -// ,operKind +// ,oper kind | DEBUG oper kind GTNODE(NONE , char ,0,GTK_SPECIAL) @@ -19,23 +19,27 @@ GTNODE(NONE , char ,0,GTK_SPECIAL) // Nodes related to locals: //----------------------------------------------------------------------------- -GTNODE(PHI , GenTreePhi ,0,GTK_SPECIAL) // phi node for ssa. -GTNODE(PHI_ARG , GenTreePhiArg ,0,GTK_LEAF) // phi(phiarg, phiarg, phiarg) -GTNODE(LCL_VAR , GenTreeLclVar ,0,GTK_LEAF) // local variable -GTNODE(LCL_FLD , GenTreeLclFld ,0,GTK_LEAF) // field in a non-primitive variable -GTNODE(STORE_LCL_VAR , GenTreeLclVar ,0,(GTK_UNOP|GTK_NOVALUE)) // store to local variable -GTNODE(STORE_LCL_FLD , GenTreeLclFld ,0,(GTK_UNOP|GTK_NOVALUE)) // store to a part of the variable -GTNODE(LCL_VAR_ADDR , GenTreeLclVar ,0,GTK_LEAF) // address of local variable -GTNODE(LCL_FLD_ADDR , GenTreeLclFld ,0,GTK_LEAF) // address of field in a non-primitive variable +GTNODE(PHI , GenTreePhi ,0,GTK_SPECIAL) // phi node for ssa. +GTNODE(PHI_ARG , GenTreePhiArg ,0,GTK_LEAF) // phi(phiarg, phiarg, phiarg) +GTNODE(LCL_VAR , GenTreeLclVar ,0,GTK_LEAF) // local variable +GTNODE(LCL_FLD , GenTreeLclFld ,0,GTK_LEAF) // field in a non-primitive variable +GTNODE(STORE_LCL_VAR , GenTreeLclVar ,0,GTK_UNOP|GTK_NOVALUE) // store to local variable +GTNODE(STORE_LCL_FLD , GenTreeLclFld ,0,GTK_UNOP|GTK_NOVALUE) // store to a part of the variable +GTNODE(LCL_VAR_ADDR , GenTreeLclVar ,0,GTK_LEAF) // address of local variable +GTNODE(LCL_FLD_ADDR , GenTreeLclFld ,0,GTK_LEAF) // address of field in a non-primitive variable //----------------------------------------------------------------------------- // Leaf nodes (i.e. these nodes have no sub-operands): //----------------------------------------------------------------------------- -GTNODE(CATCH_ARG , GenTree ,0,GTK_LEAF) // Exception object in a catch block -GTNODE(LABEL , GenTree ,0,GTK_LEAF) // Jump-target -GTNODE(FTN_ADDR , GenTreeFptrVal ,0,GTK_LEAF) // Address of a function -GTNODE(RET_EXPR , GenTreeRetExpr ,0,GTK_LEAF|GTK_NOTLIR) // Place holder for the return expression from an inline candidate +GTNODE(CATCH_ARG , GenTree ,0,GTK_LEAF) // Exception object in a catch block +GTNODE(LABEL , GenTree ,0,GTK_LEAF) // Jump-target +GTNODE(JMP , GenTreeVal ,0,GTK_LEAF|GTK_NOVALUE) // Jump to another function +GTNODE(FTN_ADDR , GenTreeFptrVal ,0,GTK_LEAF) // Address of a function +GTNODE(RET_EXPR , GenTreeRetExpr ,0,GTK_LEAF|DBK_NOTLIR) // Place holder for the return expression from an inline candidate +GTNODE(CLS_VAR , GenTreeClsVar ,0,GTK_LEAF) // Static data member + +GTNODE(ARGPLACE , GenTreeArgPlace ,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTLIR) // Placeholder for a "late arg" in the original arg list. //----------------------------------------------------------------------------- // Constant nodes: @@ -51,54 +55,52 @@ GTNODE(CNS_STR , GenTreeStrCon ,0,GTK_LEAF) //----------------------------------------------------------------------------- GTNODE(NOT , GenTreeOp ,0,GTK_UNOP) -GTNODE(NOP , GenTree ,0,(GTK_UNOP|GTK_NOCONTAIN)) +GTNODE(NOP , GenTree ,0,GTK_UNOP|DBK_NOCONTAIN) GTNODE(NEG , GenTreeOp ,0,GTK_UNOP) -GTNODE(COPY , GenTreeCopyOrReload,0,GTK_UNOP) // Copies a variable from its current location to a register that satisfies - // code generation constraints. The child is the actual lclVar node. -GTNODE(RELOAD , GenTreeCopyOrReload,0,GTK_UNOP) -GTNODE(ARR_LENGTH , GenTreeArrLen ,0,(GTK_UNOP|GTK_EXOP)) // array-length -GTNODE(INTRINSIC , GenTreeIntrinsic ,0,(GTK_BINOP|GTK_EXOP)) // intrinsics -GTNODE(LOCKADD , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) +GTNODE(INTRINSIC , GenTreeIntrinsic ,0,GTK_BINOP|GTK_EXOP) + +GTNODE(LOCKADD , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR) GTNODE(XAND , GenTreeOp ,0,GTK_BINOP) GTNODE(XORR , GenTreeOp ,0,GTK_BINOP) GTNODE(XADD , GenTreeOp ,0,GTK_BINOP) GTNODE(XCHG , GenTreeOp ,0,GTK_BINOP) GTNODE(CMPXCHG , GenTreeCmpXchg ,0,GTK_SPECIAL) -GTNODE(MEMORYBARRIER , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) +GTNODE(MEMORYBARRIER , GenTree ,0,GTK_LEAF|GTK_NOVALUE) -GTNODE(KEEPALIVE , GenTree ,0,(GTK_UNOP|GTK_NOVALUE)) // keep operand alive, generate no code, produce no result +GTNODE(KEEPALIVE , GenTree ,0,GTK_UNOP|GTK_NOVALUE) // keep operand alive, generate no code, produce no result -GTNODE(CAST , GenTreeCast ,0,(GTK_UNOP|GTK_EXOP)) // conversion to another type +GTNODE(CAST , GenTreeCast ,0,GTK_UNOP|GTK_EXOP) // conversion to another type #if defined(TARGET_ARM) GTNODE(BITCAST , GenTreeMultiRegOp ,0,GTK_UNOP) // reinterpretation of bits as another type #else GTNODE(BITCAST , GenTreeOp ,0,GTK_UNOP) // reinterpretation of bits as another type #endif -GTNODE(CKFINITE , GenTreeOp ,0,(GTK_UNOP|GTK_NOCONTAIN)) // Check for NaN -GTNODE(LCLHEAP , GenTreeOp ,0,(GTK_UNOP|GTK_NOCONTAIN)) // alloca() -GTNODE(JMP , GenTreeVal ,0,(GTK_LEAF|GTK_NOVALUE)) // Jump to another function - -GTNODE(ADDR , GenTreeOp ,0,GTK_UNOP) // address of +GTNODE(CKFINITE , GenTreeOp ,0,GTK_UNOP|DBK_NOCONTAIN) // Check for NaN +GTNODE(LCLHEAP , GenTreeOp ,0,GTK_UNOP|DBK_NOCONTAIN) // alloca() -GTNODE(IND , GenTreeIndir ,0,GTK_UNOP) // load indirection -GTNODE(STOREIND , GenTreeStoreInd ,0,(GTK_BINOP|GTK_NOVALUE)) // store indirection +GTNODE(ADDR , GenTreeOp ,0,GTK_UNOP|DBK_NOTLIR) // address of -GTNODE(BOUNDS_CHECK , GenTreeBoundsChk ,0,(GTK_BINOP|GTK_EXOP|GTK_NOVALUE)) // a bounds check - for arrays/spans/SIMDs/HWINTRINSICs +GTNODE(BOUNDS_CHECK , GenTreeBoundsChk ,0,GTK_BINOP|GTK_EXOP|GTK_NOVALUE) // a bounds check - for arrays/spans/SIMDs/HWINTRINSICs -GTNODE(OBJ , GenTreeObj ,0,(GTK_UNOP|GTK_EXOP)) // Object that MAY have gc pointers, and thus includes the relevant gc layout info. -GTNODE(STORE_OBJ , GenTreeObj ,0,(GTK_BINOP|GTK_EXOP|GTK_NOVALUE)) // Object that MAY have gc pointers, and thus includes the relevant gc layout info. -GTNODE(BLK , GenTreeBlk ,0,(GTK_UNOP|GTK_EXOP)) // Block/object with no gc pointers, and with a known size (e.g. a struct with no gc fields) -GTNODE(STORE_BLK , GenTreeBlk ,0,(GTK_BINOP|GTK_EXOP|GTK_NOVALUE)) // Block/object with no gc pointers, and with a known size (e.g. a struct with no gc fields) -GTNODE(STORE_DYN_BLK , GenTreeStoreDynBlk ,0,(GTK_SPECIAL|GTK_NOVALUE)) // Dynamically sized block store +GTNODE(IND , GenTreeIndir ,0,GTK_UNOP) // Load indirection +GTNODE(STOREIND , GenTreeStoreInd ,0,GTK_BINOP|GTK_NOVALUE) // Store indirection +GTNODE(OBJ , GenTreeObj ,0,GTK_UNOP|GTK_EXOP) // Object that MAY have gc pointers, and thus includes the relevant gc layout info. +GTNODE(STORE_OBJ , GenTreeObj ,0,GTK_BINOP|GTK_EXOP|GTK_NOVALUE) // Object that MAY have gc pointers, and thus includes the relevant gc layout info. +GTNODE(BLK , GenTreeBlk ,0,GTK_UNOP|GTK_EXOP) // Block/object with no gc pointers, and with a known size (e.g. a struct with no gc fields) +GTNODE(STORE_BLK , GenTreeBlk ,0,GTK_BINOP|GTK_EXOP|GTK_NOVALUE) // Block/object with no gc pointers, and with a known size (e.g. a struct with no gc fields) +GTNODE(STORE_DYN_BLK , GenTreeStoreDynBlk ,0,GTK_SPECIAL|GTK_NOVALUE) // Dynamically sized block store +GTNODE(NULLCHECK , GenTreeIndir ,0,GTK_UNOP|GTK_NOVALUE) // Null checks the source -GTNODE(BOX , GenTreeBox ,0,(GTK_UNOP|GTK_EXOP|GTK_NOTLIR)) -GTNODE(FIELD , GenTreeField ,0,(GTK_UNOP|GTK_EXOP)) // Member-field -GTNODE(ALLOCOBJ , GenTreeAllocObj ,0,(GTK_UNOP|GTK_EXOP)) // object allocator +GTNODE(ARR_LENGTH , GenTreeArrLen ,0,GTK_UNOP|GTK_EXOP) +GTNODE(FIELD , GenTreeField ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // Member-field +GTNODE(ALLOCOBJ , GenTreeAllocObj ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // object allocator -GTNODE(INIT_VAL , GenTreeOp ,0,GTK_UNOP) // Initialization value for an initBlk +GTNODE(INIT_VAL , GenTreeOp ,0,GTK_UNOP) // Initialization value for an initBlk -GTNODE(RUNTIMELOOKUP , GenTreeRuntimeLookup, 0,(GTK_UNOP|GTK_EXOP)) // Runtime handle lookup +GTNODE(BOX , GenTreeBox ,0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // Marks its first operands (a local) as being a box +GTNODE(PUTARG_TYPE , GenTreeOp ,0,GTK_UNOP|DBK_NOTLIR) // Saves argument type between importation and morph +GTNODE(RUNTIMELOOKUP , GenTreeRuntimeLookup, 0,GTK_UNOP|GTK_EXOP|DBK_NOTLIR) // Runtime handle lookup GTNODE(BSWAP , GenTreeOp ,0,GTK_UNOP) // Byte swap (32-bit or 64-bit) GTNODE(BSWAP16 , GenTreeOp ,0,GTK_UNOP) // Byte swap (16-bit) @@ -126,7 +128,7 @@ GTNODE(RSZ , GenTreeOp ,0,GTK_BINOP) GTNODE(ROL , GenTreeOp ,0,GTK_BINOP) GTNODE(ROR , GenTreeOp ,0,GTK_BINOP) -GTNODE(ASG , GenTreeOp ,0,(GTK_BINOP|GTK_NOTLIR)) +GTNODE(ASG , GenTreeOp ,0,GTK_BINOP|DBK_NOTLIR) GTNODE(EQ , GenTreeOp ,0,GTK_BINOP) GTNODE(NE , GenTreeOp ,0,GTK_BINOP) GTNODE(LT , GenTreeOp ,0,GTK_BINOP) @@ -141,38 +143,35 @@ GTNODE(GT , GenTreeOp ,0,GTK_BINOP) // codegen which emits a "test reg, reg" instruction, that would be more difficult to do // during lowering because the source operand is used twice so it has to be a lclvar. // Because of this there is no need to also add GT_TEST_LT/LE/GE/GT opers. -GTNODE(TEST_EQ , GenTreeOp ,0,GTK_BINOP) -GTNODE(TEST_NE , GenTreeOp ,0,GTK_BINOP) - -GTNODE(COMMA , GenTreeOp ,0,(GTK_BINOP|GTK_NOTLIR)) - -GTNODE(QMARK , GenTreeQmark ,0,(GTK_BINOP|GTK_EXOP|GTK_NOTLIR)) -GTNODE(COLON , GenTreeColon ,0,(GTK_BINOP|GTK_NOTLIR)) +GTNODE(TEST_EQ , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) +GTNODE(TEST_NE , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) -GTNODE(INDEX , GenTreeIndex ,0,(GTK_BINOP|GTK_EXOP|GTK_NOTLIR)) // SZ-array-element -GTNODE(INDEX_ADDR , GenTreeIndexAddr ,0,(GTK_BINOP|GTK_EXOP)) // addr of SZ-array-element; - // used when aiming to minimize compile times. +GTNODE(COMMA , GenTreeOp ,0,GTK_BINOP|DBK_NOTLIR) +GTNODE(QMARK , GenTreeQmark ,0,GTK_BINOP|GTK_EXOP|DBK_NOTLIR) +GTNODE(COLON , GenTreeColon ,0,GTK_BINOP|DBK_NOTLIR) -GTNODE(MKREFANY , GenTreeOp ,0,GTK_BINOP|GTK_NOTLIR) +GTNODE(INDEX , GenTreeIndex ,0,GTK_BINOP|GTK_EXOP|DBK_NOTLIR) // SZ-array-element. +GTNODE(INDEX_ADDR , GenTreeIndexAddr ,0,GTK_BINOP|GTK_EXOP) // Addr of SZ-array-element; used when aiming to minimize compile times. -GTNODE(LEA , GenTreeAddrMode ,0,(GTK_BINOP|GTK_EXOP)) +GTNODE(MKREFANY , GenTreeOp ,0,GTK_BINOP|DBK_NOTLIR) +GTNODE(LEA , GenTreeAddrMode ,0,GTK_BINOP|GTK_EXOP) #if !defined(TARGET_64BIT) // A GT_LONG node simply represents the long value produced by the concatenation // of its two (lower and upper half) operands. Some GT_LONG nodes are transient, // during the decomposing of longs; others are handled by codegen as operands of // nodes such as calls, returns and stores of long lclVars. -GTNODE(LONG , GenTreeOp ,0,GTK_BINOP) +GTNODE(LONG , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) // The following are nodes representing x86/arm32 specific long operators, including // high operators of a 64-bit operations that requires a carry/borrow, which are // named GT_XXX_HI for consistency, low operators of 64-bit operations that need // to not be modified in phases post-decompose, and operators that return 64-bit // results in one instruction. -GTNODE(ADD_LO , GenTreeOp ,1,GTK_BINOP) -GTNODE(ADD_HI , GenTreeOp ,1,GTK_BINOP) -GTNODE(SUB_LO , GenTreeOp ,0,GTK_BINOP) -GTNODE(SUB_HI , GenTreeOp ,0,GTK_BINOP) +GTNODE(ADD_LO , GenTreeOp ,1,GTK_BINOP|DBK_NOTHIR) +GTNODE(ADD_HI , GenTreeOp ,1,GTK_BINOP|DBK_NOTHIR) +GTNODE(SUB_LO , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) +GTNODE(SUB_HI , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) // The following are nodes that specify shifts that take a GT_LONG op1. The GT_LONG // contains the hi and lo parts of three operand shift form where one op will be @@ -181,8 +180,8 @@ GTNODE(SUB_HI , GenTreeOp ,0,GTK_BINOP) // will shift the lo bits of the high operand into the lo operand). LSH_HI // represents the high operation of a 64-bit left shift by a constant int, and // RSH_LO represents the lo operation of a 64-bit right shift by a constant int. -GTNODE(LSH_HI , GenTreeOp ,0,GTK_BINOP) -GTNODE(RSH_LO , GenTreeOp ,0,GTK_BINOP) +GTNODE(LSH_HI , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) +GTNODE(RSH_LO , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) #endif // !defined(TARGET_64BIT) #ifdef FEATURE_SIMD @@ -197,13 +196,14 @@ GTNODE(HWINTRINSIC , GenTreeHWIntrinsic ,0,GTK_SPECIAL) // ha // Backend-specific arithmetic nodes: //----------------------------------------------------------------------------- -GTNODE(INC_SATURATE , GenTreeOp ,0,GTK_UNOP) // saturating increment, used in division by a constant (LowerUnsignedDivOrMod) +// Saturating increment, used in division by a constant (LowerUnsignedDivOrMod). +GTNODE(INC_SATURATE , GenTreeOp ,0,GTK_UNOP|DBK_NOTHIR) // Returns high bits (top N bits of the 2N bit result of an NxN multiply) // GT_MULHI is used in division by a constant (LowerUnsignedDivOrMod). We turn // the div into a MULHI + some adjustments. In codegen, we only use the // results of the high register, and we drop the low results. -GTNODE(MULHI , GenTreeOp ,1,GTK_BINOP) +GTNODE(MULHI , GenTreeOp ,1,GTK_BINOP|DBK_NOTHIR) // A mul that returns the 2N bit result of an NxN multiply. This op is used for // multiplies that take two ints and return a long result. For 32 bit targets, @@ -212,100 +212,96 @@ GTNODE(MULHI , GenTreeOp ,1,GTK_BINOP) // part of the result, whereas GT_MUL_LONG keeps both parts of the result. // MUL_LONG is also used on ARM64, where 64 bit multiplication is more expensive. #if !defined(TARGET_64BIT) -GTNODE(MUL_LONG , GenTreeMultiRegOp ,1,GTK_BINOP) +GTNODE(MUL_LONG , GenTreeMultiRegOp ,1,GTK_BINOP|DBK_NOTHIR) #elif defined(TARGET_ARM64) -GTNODE(MUL_LONG , GenTreeOp ,1,GTK_BINOP) +GTNODE(MUL_LONG , GenTreeOp ,1,GTK_BINOP|DBK_NOTHIR) #endif // AndNot - emitted on ARM/ARM64 as the BIC instruction. Also used for creating AndNot HWINTRINSIC vector nodes in a cross-ISA manner. -GTNODE(AND_NOT , GenTreeOp ,0,GTK_BINOP) +GTNODE(AND_NOT , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) + +#ifdef TARGET_ARM64 +GTNODE(MADD , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) // Generates the Multiply-Add instruction (madd/msub) In the future, we might consider + // enabling it for both armarch and xarch for floating-point MADD "unsafe" math. +GTNODE(ADDEX, GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) // Add with sign/zero extension. +GTNODE(BFIZ , GenTreeOp ,0,GTK_BINOP|DBK_NOTHIR) // Bitfield Insert in Zero. +#endif + //----------------------------------------------------------------------------- // LIR specific compare and conditional branch/set nodes: //----------------------------------------------------------------------------- -GTNODE(CMP , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) // Sets the condition flags according to the compare result. - // N.B. Not a relop, it does not produce a value and it cannot be reversed. -GTNODE(JCMP , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) // Makes a comparison and jump if the condition specified. Does not set flags -GTNODE(JCC , GenTreeCC ,0,(GTK_LEAF|GTK_NOVALUE)) // Checks the condition flags and branch if the condition specified - // by GenTreeCC::gtCondition is true. -GTNODE(SETCC , GenTreeCC ,0,GTK_LEAF) // Checks the condition flags and produces 1 if the condition specified - // by GenTreeCC::gtCondition is true and 0 otherwise. +// Sets the condition flags according to the compare result. N.B. Not a relop, it does not produce a value and it cannot be reversed. +GTNODE(CMP , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR) +// Makes a comparison and jump if the condition specified. Does not set flags. +GTNODE(JCMP , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR) +// Checks the condition flags and branch if the condition specified by GenTreeCC::gtCondition is true. +GTNODE(JCC , GenTreeCC ,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) +// Checks the condition flags and produces 1 if the condition specified by GenTreeCC::gtCondition is true and 0 otherwise. +GTNODE(SETCC , GenTreeCC ,0,GTK_LEAF|DBK_NOTHIR) #ifdef TARGET_XARCH -GTNODE(BT , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE)) // The XARCH BT instruction. Like CMP, this sets the condition flags (CF - // to be precise) and does not produce a value. +// The XARCH BT instruction. Like CMP, this sets the condition flags (CF to be precise) and does not produce a value. +GTNODE(BT , GenTreeOp ,0,(GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR)) #endif + //----------------------------------------------------------------------------- // Other nodes that look like unary/binary operators: //----------------------------------------------------------------------------- -GTNODE(JTRUE , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) +GTNODE(JTRUE , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) //----------------------------------------------------------------------------- // Other nodes that have special structure: //----------------------------------------------------------------------------- GTNODE(ARR_ELEM , GenTreeArrElem ,0,GTK_SPECIAL) // Multi-dimensional array-element address -GTNODE(ARR_INDEX , GenTreeArrIndex ,0,(GTK_BINOP|GTK_EXOP)) // Effective, bounds-checked index for one dimension of a multi-dimensional array element +GTNODE(ARR_INDEX , GenTreeArrIndex ,0,GTK_BINOP|GTK_EXOP) // Effective, bounds-checked index for one dimension of a multi-dimensional array element GTNODE(ARR_OFFSET , GenTreeArrOffs ,0,GTK_SPECIAL) // Flattened offset of multi-dimensional array element -GTNODE(CALL , GenTreeCall ,0,(GTK_SPECIAL|GTK_NOCONTAIN)) +GTNODE(CALL , GenTreeCall ,0,GTK_SPECIAL|DBK_NOCONTAIN) GTNODE(FIELD_LIST , GenTreeFieldList ,0,GTK_SPECIAL) // List of fields of a struct, when passed as an argument -GTNODE(RETURN , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) // return from current function -GTNODE(SWITCH , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) // switch - -GTNODE(NO_OP , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // nop! - -GTNODE(START_NONGC , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // starts a new instruction group that will be non-gc interruptible - -GTNODE(START_PREEMPTGC , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // starts a new instruction group where preemptive GC is enabled +GTNODE(RETURN , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) +GTNODE(SWITCH , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) +GTNODE(NO_OP , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // A NOP that cannot be deleted. -GTNODE(PROF_HOOK , GenTree ,0,(GTK_LEAF|GTK_NOVALUE)) // profiler Enter/Leave/TailCall hook +GTNODE(START_NONGC , GenTree ,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // Starts a new instruction group that will be non-gc interruptible. +GTNODE(START_PREEMPTGC , GenTree ,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // Starts a new instruction group where preemptive GC is enabled. +GTNODE(PROF_HOOK , GenTree ,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // Profiler Enter/Leave/TailCall hook. -GTNODE(RETFILT , GenTreeOp ,0,(GTK_UNOP|GTK_NOVALUE)) // end filter with TYP_I_IMPL return value +GTNODE(RETFILT , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) // End filter with TYP_I_IMPL return value. #if !defined(FEATURE_EH_FUNCLETS) -GTNODE(END_LFIN , GenTreeVal ,0,(GTK_LEAF|GTK_NOVALUE)) // end locally-invoked finally +GTNODE(END_LFIN , GenTreeVal ,0,GTK_LEAF|GTK_NOVALUE) // End locally-invoked finally. #endif // !FEATURE_EH_FUNCLETS //----------------------------------------------------------------------------- // Nodes used by Lower to generate a closer CPU representation of other nodes //----------------------------------------------------------------------------- -#ifdef TARGET_ARM64 -GTNODE(MADD , GenTreeOp ,0, GTK_BINOP) // Generates the Multiply-Add instruction (madd/msub) - // In future, we might consider enabling it for both armarch and xarch - // for floating-point MADD "unsafe" math -#endif -GTNODE(JMPTABLE , GenTree ,0, (GTK_LEAF|GTK_NOCONTAIN)) // Generates the jump table for switches -GTNODE(SWITCH_TABLE , GenTreeOp ,0, (GTK_BINOP|GTK_NOVALUE)) // Jump Table based switch construct -#ifdef TARGET_ARM64 -GTNODE(ADDEX, GenTreeOp ,0, GTK_BINOP) // Add with sign/zero extension -GTNODE(BFIZ , GenTreeOp ,0, GTK_BINOP) // Bitfield Insert in Zero -#endif +GTNODE(JMPTABLE , GenTree ,0,GTK_LEAF|DBK_NOCONTAIN|DBK_NOTHIR) // Generates the jump table for switches +GTNODE(SWITCH_TABLE , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR) // Jump Table based switch construct //----------------------------------------------------------------------------- // Nodes used only within the code generator: //----------------------------------------------------------------------------- -GTNODE(CLS_VAR , GenTreeClsVar ,0,GTK_LEAF) // static data member -GTNODE(CLS_VAR_ADDR , GenTreeClsVar ,0,GTK_LEAF) // static data member address -GTNODE(ARGPLACE , GenTreeArgPlace ,0,GTK_LEAF|GTK_NOVALUE|GTK_NOTLIR) // placeholder for a register arg -GTNODE(NULLCHECK , GenTreeIndir ,0,GTK_UNOP|GTK_NOVALUE) // null checks the source -GTNODE(PHYSREG , GenTreePhysReg ,0,GTK_LEAF) // read from a physical register -GTNODE(EMITNOP , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // emitter-placed nop -GTNODE(PINVOKE_PROLOG , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // pinvoke prolog seq -GTNODE(PINVOKE_EPILOG , GenTree ,0,GTK_LEAF|GTK_NOVALUE) // pinvoke epilog seq +GTNODE(CLS_VAR_ADDR , GenTreeClsVar ,0,GTK_LEAF|DBK_NOTHIR) // static data member address +GTNODE(PHYSREG , GenTreePhysReg ,0,GTK_LEAF|DBK_NOTHIR) // read from a physical register +GTNODE(EMITNOP , GenTree ,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // emitter-placed nop +GTNODE(PINVOKE_PROLOG , GenTree ,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // pinvoke prolog seq +GTNODE(PINVOKE_EPILOG , GenTree ,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // pinvoke epilog seq +GTNODE(RETURNTRAP , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE|DBK_NOTHIR) // a conditional call to wait on gc #if defined(TARGET_ARM) -GTNODE(PUTARG_REG , GenTreeMultiRegOp ,0,GTK_UNOP) // operator that places outgoing arg in register +GTNODE(PUTARG_REG , GenTreeMultiRegOp ,0,GTK_UNOP|DBK_NOTHIR) // operator that places outgoing arg in register #else -GTNODE(PUTARG_REG , GenTreeOp ,0,GTK_UNOP) // operator that places outgoing arg in register +GTNODE(PUTARG_REG , GenTreeOp ,0,GTK_UNOP|DBK_NOTHIR) // operator that places outgoing arg in register #endif -GTNODE(PUTARG_TYPE , GenTreeOp ,0,GTK_UNOP|GTK_NOTLIR) // operator that places saves argument type between importation and morph -GTNODE(PUTARG_STK , GenTreePutArgStk ,0,GTK_UNOP|GTK_NOVALUE) // operator that places outgoing arg in stack +GTNODE(PUTARG_STK , GenTreePutArgStk ,0,GTK_UNOP|GTK_NOVALUE|DBK_NOTHIR) // operator that places outgoing arg in stack #if FEATURE_ARG_SPLIT -GTNODE(PUTARG_SPLIT , GenTreePutArgSplit ,0,GTK_UNOP) // operator that places outgoing arg in registers with stack (split struct in ARM32) +GTNODE(PUTARG_SPLIT , GenTreePutArgSplit ,0,GTK_UNOP|DBK_NOTHIR) // operator that places outgoing arg in registers with stack (split struct in ARM32) #endif // FEATURE_ARG_SPLIT -GTNODE(RETURNTRAP , GenTreeOp ,0,GTK_UNOP|GTK_NOVALUE) // a conditional call to wait on gc -GTNODE(SWAP , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE) // op1 and op2 swap (registers) -GTNODE(IL_OFFSET , Statement ,0,GTK_LEAF|GTK_NOVALUE) // marks an IL offset for debugging purposes +GTNODE(SWAP , GenTreeOp ,0,GTK_BINOP|GTK_NOVALUE|DBK_NOTHIR) // op1 and op2 swap (registers) +GTNODE(COPY , GenTreeCopyOrReload,0,GTK_UNOP|DBK_NOTHIR) // Copies a variable from its current location to a register that satisfies +GTNODE(RELOAD , GenTreeCopyOrReload,0,GTK_UNOP|DBK_NOTHIR) // code generation constraints. The operand is the actual lclVar node. +GTNODE(IL_OFFSET , GenTreeILOffset ,0,GTK_LEAF|GTK_NOVALUE|DBK_NOTHIR) // marks an IL offset for debugging purposes /*****************************************************************************/ #undef GTNODE diff --git a/src/coreclr/jit/lir.cpp b/src/coreclr/jit/lir.cpp index 2f5b762214ef17..93344854c5b1a4 100644 --- a/src/coreclr/jit/lir.cpp +++ b/src/coreclr/jit/lir.cpp @@ -1415,12 +1415,12 @@ class CheckLclVarSemanticsHelper { for (GenTree* operand : node->Operands()) { - if (!operand->IsLIR()) + // ARGPLACE nodes are not represented in the LIR sequence. Ignore them. + if (operand->OperIs(GT_ARGPLACE)) { - // ARGPLACE nodes are not represented in the LIR sequence. Ignore them. - assert(operand->OperIs(GT_ARGPLACE)); continue; } + if (operand->isContained()) { UseNodeOperands(operand); @@ -1524,7 +1524,7 @@ bool LIR::Range::CheckLIR(Compiler* compiler, bool checkUnusedValues) const for (Iterator node = begin(), end = this->end(); node != end; prev = *node, ++node) { // Verify that the node is allowed in LIR. - assert(node->IsLIR()); + assert(node->OperIsLIR()); // Some nodes should never be marked unused, as they must be contained in the backend. // These may be marked as unused during dead code elimination traversal, but they *must* be subsequently diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index 88f137ab65764d..73fd12e4690de2 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -9400,7 +9400,7 @@ void LinearScan::DumpOperandDefs( { assert(operand != nullptr); assert(operandString != nullptr); - if (!operand->IsLIR()) + if (operand->OperIs(GT_ARGPLACE)) { return; } diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index d58f1996091fae..5d020d9e3dc533 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -1619,12 +1619,12 @@ void LinearScan::buildUpperVectorRestoreRefPosition(Interval* lclVarInterval, Ls int LinearScan::ComputeOperandDstCount(GenTree* operand) { // GT_ARGPLACE is the only non-LIR node that is currently in the trees at this stage, though - // note that it is not in the linear order. It seems best to check for !IsLIR() rather than - // GT_ARGPLACE directly, since it's that characteristic that makes it irrelevant for this method. - if (!operand->IsLIR()) + // note that it is not in the linear order. + if (operand->OperIs(GT_ARGPLACE)) { return 0; } + if (operand->isContained()) { int dstCount = 0; diff --git a/src/coreclr/jit/rationalize.cpp b/src/coreclr/jit/rationalize.cpp index 72c1591c6d4251..5eacf657ad8b7b 100644 --- a/src/coreclr/jit/rationalize.cpp +++ b/src/coreclr/jit/rationalize.cpp @@ -803,8 +803,8 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, Compiler::Ge #endif // FEATURE_HW_INTRINSICS default: - // These nodes should not be present in HIR. - assert(!node->OperIs(GT_CMP, GT_SETCC, GT_JCC, GT_JCMP, GT_LOCKADD)); + // Check that we don't have nodes not allowed in HIR here. + assert((node->DebugOperKind() & DBK_NOTHIR) == 0); break; } diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index 4be6e1a93a4cdb..8243764c11b394 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -9062,10 +9062,6 @@ void Compiler::fgValueNumberTree(GenTree* tree) } break; - case GT_LOCKADD: // Binop - noway_assert("LOCKADD should not appear before lowering"); - break; - case GT_XORR: // Binop case GT_XAND: // Binop case GT_XADD: // Binop