diff options
Diffstat (limited to 'src/jit/gentree.h')
-rw-r--r-- | src/jit/gentree.h | 188 |
1 files changed, 136 insertions, 52 deletions
diff --git a/src/jit/gentree.h b/src/jit/gentree.h index 4611d35465..0ea8321e77 100644 --- a/src/jit/gentree.h +++ b/src/jit/gentree.h @@ -566,7 +566,7 @@ public: bool isContainedIntOrIImmed() const { - return isContained() && IsCnsIntOrI() && !isContainedSpillTemp(); + return isContained() && IsCnsIntOrI() && !isUsedFromSpillTemp(); } bool isContainedFltOrDblImmed() const @@ -579,28 +579,34 @@ public: return OperGet() == GT_LCL_FLD || OperGet() == GT_STORE_LCL_FLD; } - bool isContainedLclField() const + bool isUsedFromSpillTemp() const; + + // Indicates whether it is a memory op. + // Right now it includes Indir and LclField ops. + bool isMemoryOp() const { - return isContained() && isLclField(); + return isIndir() || isLclField(); } - bool isContainedLclVar() const + bool isUsedFromMemory() const { - return isContained() && (OperGet() == GT_LCL_VAR); + return ((isContained() && (isMemoryOp() || (OperGet() == GT_LCL_VAR) || (OperGet() == GT_CNS_DBL))) || + isUsedFromSpillTemp()); } - bool isContainedSpillTemp() const; + bool isLclVarUsedFromMemory() const + { + return (OperGet() == GT_LCL_VAR) && (isContained() || isUsedFromSpillTemp()); + } - // Indicates whether it is a memory op. - // Right now it includes Indir and LclField ops. - bool isMemoryOp() const + bool isLclFldUsedFromMemory() const { - return isIndir() || isLclField(); + return isLclField() && (isContained() || isUsedFromSpillTemp()); } - bool isContainedMemoryOp() const + bool isUsedFromReg() const { - return (isContained() && isMemoryOp()) || isContainedLclVar() || isContainedSpillTemp(); + return !isContained() && !isUsedFromSpillTemp(); } regNumber GetRegNum() const @@ -903,8 +909,6 @@ public: #define GTF_RELOP_NAN_UN 0x80000000 // GT_<relop> -- Is branch taken if ops are NaN? #define GTF_RELOP_JMP_USED 0x40000000 // GT_<relop> -- result of compare used for jump or ?: #define GTF_RELOP_QMARK 0x20000000 // GT_<relop> -- the node is the condition for ?: -#define GTF_RELOP_SMALL 0x10000000 // GT_<relop> -- We should use a byte or short sized compare (op1->gtType - // is the small type) #define GTF_RELOP_ZTT 0x08000000 // GT_<relop> -- Loop test cloned for converting while-loops into do-while // with explicit "loop test" in the header block. @@ -1073,6 +1077,17 @@ public: } } + bool OperIs(genTreeOps oper) + { + return OperGet() == oper; + } + + template <typename... T> + bool OperIs(genTreeOps oper, T... rest) + { + return OperIs(oper) || OperIs(rest...); + } + static bool OperIsConst(genTreeOps gtOper) { return (OperKind(gtOper) & GTK_CONST) != 0; @@ -1588,8 +1603,14 @@ public: inline GenTreePtr gtGetOp1(); + // Directly return op2. Asserts the node is binary. Might return nullptr if the binary node allows + // a nullptr op2, such as GT_LIST. This is more efficient than gtGetOp2IfPresent() if you know what + // node type you have. inline GenTreePtr gtGetOp2(); + // The returned pointer might be nullptr if the node is not binary, or if non-null op2 is not required. + inline GenTreePtr gtGetOp2IfPresent(); + // Given a tree node, if this is a child of that node, return the pointer to the child node so that it // can be modified; otherwise, return null. GenTreePtr* gtGetChildPointer(GenTreePtr parent); @@ -3248,43 +3269,52 @@ struct GenTreeCall final : public GenTree #endif } -#define GTF_CALL_M_EXPLICIT_TAILCALL \ - 0x0001 // GT_CALL -- the call is "tail" prefixed and importer has performed tail call checks -#define GTF_CALL_M_TAILCALL 0x0002 // GT_CALL -- the call is a tailcall -#define GTF_CALL_M_VARARGS 0x0004 // GT_CALL -- the call uses varargs ABI -#define GTF_CALL_M_RETBUFFARG 0x0008 // GT_CALL -- first parameter is the return buffer argument -#define GTF_CALL_M_DELEGATE_INV 0x0010 // GT_CALL -- call to Delegate.Invoke -#define GTF_CALL_M_NOGCCHECK 0x0020 // GT_CALL -- not a call for computing full interruptability -#define GTF_CALL_M_SPECIAL_INTRINSIC 0x0040 // GT_CALL -- function that could be optimized as an intrinsic - // in special cases. Used to optimize fast way out in morphing -#define GTF_CALL_M_UNMGD_THISCALL \ - 0x0080 // "this" pointer (first argument) should be enregistered (only for GTF_CALL_UNMANAGED) -#define GTF_CALL_M_VIRTSTUB_REL_INDIRECT \ - 0x0080 // the virtstub is indirected through a relative address (only for GTF_CALL_VIRT_STUB) -#define GTF_CALL_M_NONVIRT_SAME_THIS \ - 0x0080 // callee "this" pointer is equal to caller this pointer (only for GTF_CALL_NONVIRT) -#define GTF_CALL_M_FRAME_VAR_DEATH 0x0100 // GT_CALL -- the compLvFrameListRoot variable dies here (last use) +// clang-format off + +#define GTF_CALL_M_EXPLICIT_TAILCALL 0x00000001 // GT_CALL -- the call is "tail" prefixed and + // importer has performed tail call checks +#define GTF_CALL_M_TAILCALL 0x00000002 // GT_CALL -- the call is a tailcall +#define GTF_CALL_M_VARARGS 0x00000004 // GT_CALL -- the call uses varargs ABI +#define GTF_CALL_M_RETBUFFARG 0x00000008 // GT_CALL -- first parameter is the return buffer argument +#define GTF_CALL_M_DELEGATE_INV 0x00000010 // GT_CALL -- call to Delegate.Invoke +#define GTF_CALL_M_NOGCCHECK 0x00000020 // GT_CALL -- not a call for computing full interruptability +#define GTF_CALL_M_SPECIAL_INTRINSIC 0x00000040 // GT_CALL -- function that could be optimized as an intrinsic + // in special cases. Used to optimize fast way out in morphing +#define GTF_CALL_M_UNMGD_THISCALL 0x00000080 // GT_CALL -- "this" pointer (first argument) + // should be enregistered (only for GTF_CALL_UNMANAGED) +#define GTF_CALL_M_VIRTSTUB_REL_INDIRECT 0x00000080 // the virtstub is indirected through + // a relative address (only for GTF_CALL_VIRT_STUB) +#define GTF_CALL_M_NONVIRT_SAME_THIS 0x00000080 // GT_CALL -- callee "this" pointer is + // equal to caller this pointer (only for GTF_CALL_NONVIRT) +#define GTF_CALL_M_FRAME_VAR_DEATH 0x00000100 // GT_CALL -- the compLvFrameListRoot variable dies here (last use) #ifndef LEGACY_BACKEND -#define GTF_CALL_M_TAILCALL_VIA_HELPER 0x0200 // GT_CALL -- call is a tail call dispatched via tail call JIT helper. -#endif // !LEGACY_BACKEND +#define GTF_CALL_M_TAILCALL_VIA_HELPER 0x00000200 // GT_CALL -- call is a tail call dispatched via tail call JIT helper. +#endif #if FEATURE_TAILCALL_OPT -#define GTF_CALL_M_IMPLICIT_TAILCALL \ - 0x0400 // GT_CALL -- call is an opportunistic tail call and importer has performed tail call checks -#define GTF_CALL_M_TAILCALL_TO_LOOP \ - 0x0800 // GT_CALL -- call is a fast recursive tail call that can be converted into a loop +#define GTF_CALL_M_IMPLICIT_TAILCALL 0x00000400 // GT_CALL -- call is an opportunistic + // tail call and importer has performed tail call checks +#define GTF_CALL_M_TAILCALL_TO_LOOP 0x00000800 // GT_CALL -- call is a fast recursive tail call + // that can be converted into a loop #endif -#define GTF_CALL_M_PINVOKE 0x1000 // GT_CALL -- call is a pinvoke. This mirrors VM flag CORINFO_FLG_PINVOKE. - // A call marked as Pinvoke is not necessarily a GT_CALL_UNMANAGED. For e.g. - // an IL Stub dynamically generated for a PInvoke declaration is flagged as - // a Pinvoke but not as an unmanaged call. See impCheckForPInvokeCall() to - // know when these flags are set. +#define GTF_CALL_M_PINVOKE 0x00001000 // GT_CALL -- call is a pinvoke. This mirrors VM flag CORINFO_FLG_PINVOKE. + // A call marked as Pinvoke is not necessarily a GT_CALL_UNMANAGED. For e.g. + // an IL Stub dynamically generated for a PInvoke declaration is flagged as + // a Pinvoke but not as an unmanaged call. See impCheckForPInvokeCall() to + // know when these flags are set. + +#define GTF_CALL_M_R2R_REL_INDIRECT 0x00002000 // GT_CALL -- ready to run call is indirected through a relative address +#define GTF_CALL_M_DOES_NOT_RETURN 0x00004000 // GT_CALL -- call does not return +#define GTF_CALL_M_SECURE_DELEGATE_INV 0x00008000 // GT_CALL -- call is in secure delegate +#define GTF_CALL_M_FAT_POINTER_CHECK 0x00010000 // GT_CALL -- CoreRT managed calli needs transformation, that checks + // special bit in calli address. If it is set, then it is necessary + // to restore real function address and load hidden argument + // as the first argument for calli. It is CoreRT replacement for instantiating + // stubs, because executable code cannot be generated at runtime. -#define GTF_CALL_M_R2R_REL_INDIRECT 0x2000 // GT_CALL -- ready to run call is indirected through a relative address -#define GTF_CALL_M_DOES_NOT_RETURN 0x4000 // GT_CALL -- call does not return -#define GTF_CALL_M_SECURE_DELEGATE_INV 0x8000 // GT_CALL -- call is in secure delegate + // clang-format on bool IsUnmanaged() const { @@ -3482,9 +3512,24 @@ struct GenTreeCall final : public GenTree return (gtCallMoreFlags & GTF_CALL_M_DOES_NOT_RETURN) != 0; } + bool IsFatPointerCandidate() const + { + return (gtCallMoreFlags & GTF_CALL_M_FAT_POINTER_CHECK) != 0; + } + bool IsPure(Compiler* compiler) const; - unsigned short gtCallMoreFlags; // in addition to gtFlags + void ClearFatPointerCandidate() + { + gtCallMoreFlags &= ~GTF_CALL_M_FAT_POINTER_CHECK; + } + + void SetFatPointerCandidate() + { + gtCallMoreFlags |= GTF_CALL_M_FAT_POINTER_CHECK; + } + + unsigned gtCallMoreFlags; // in addition to gtFlags unsigned char gtCallType : 3; // value from the gtCallTypes enumeration unsigned char gtReturnType : 5; // exact return type @@ -3764,8 +3809,8 @@ public: struct GenTreeBoundsChk : public GenTree { - GenTreePtr gtArrLen; // An expression for the length of the array being indexed. GenTreePtr gtIndex; // The index expression. + GenTreePtr gtArrLen; // An expression for the length of the array being indexed. GenTreePtr gtIndRngFailBB; // Label to jump to for array-index-out-of-range SpecialCodeKind gtThrowKind; // Kind of throw block to branch to on failure @@ -3775,10 +3820,10 @@ struct GenTreeBoundsChk : public GenTree optimizer has a chance of eliminating some of the rng checks */ unsigned gtStkDepth; - GenTreeBoundsChk(genTreeOps oper, var_types type, GenTreePtr arrLen, GenTreePtr index, SpecialCodeKind kind) + GenTreeBoundsChk(genTreeOps oper, var_types type, GenTreePtr index, GenTreePtr arrLen, SpecialCodeKind kind) : GenTree(oper, type) - , gtArrLen(arrLen) , gtIndex(index) + , gtArrLen(arrLen) , gtIndRngFailBB(nullptr) , gtThrowKind(kind) , gtStkDepth(0) @@ -4531,6 +4576,9 @@ struct GenTreePhiArg : public GenTreeLclVarCommon struct GenTreePutArgStk : public GenTreeUnOp { unsigned gtSlotNum; // Slot number of the argument to be passed on stack +#if defined(UNIX_X86_ABI) + unsigned gtPadAlign; // Number of padding slots for stack alignment +#endif #if FEATURE_FASTTAILCALL bool putInIncomingArgArea; // Whether this arg needs to be placed in incoming arg area. @@ -4546,6 +4594,9 @@ struct GenTreePutArgStk : public GenTreeUnOp DEBUGARG(bool largeNode = false)) : GenTreeUnOp(oper, type DEBUGARG(largeNode)) , gtSlotNum(slotNum) +#if defined(UNIX_X86_ABI) + , gtPadAlign(0) +#endif , putInIncomingArgArea(_putInIncomingArgArea) #ifdef FEATURE_PUT_STRUCT_ARG_STK , gtPutArgStkKind(Kind::Invalid) @@ -4567,6 +4618,9 @@ struct GenTreePutArgStk : public GenTreeUnOp DEBUGARG(bool largeNode = false)) : GenTreeUnOp(oper, type, op1 DEBUGARG(largeNode)) , gtSlotNum(slotNum) +#if defined(UNIX_X86_ABI) + , gtPadAlign(0) +#endif , putInIncomingArgArea(_putInIncomingArgArea) #ifdef FEATURE_PUT_STRUCT_ARG_STK , gtPutArgStkKind(Kind::Invalid) @@ -4588,6 +4642,9 @@ struct GenTreePutArgStk : public GenTreeUnOp DEBUGARG(GenTreePtr callNode = NULL) DEBUGARG(bool largeNode = false)) : GenTreeUnOp(oper, type DEBUGARG(largeNode)) , gtSlotNum(slotNum) +#if defined(UNIX_X86_ABI) + , gtPadAlign(0) +#endif #ifdef FEATURE_PUT_STRUCT_ARG_STK , gtPutArgStkKind(Kind::Invalid) , gtNumSlots(numSlots) @@ -4607,6 +4664,9 @@ struct GenTreePutArgStk : public GenTreeUnOp DEBUGARG(GenTreePtr callNode = NULL) DEBUGARG(bool largeNode = false)) : GenTreeUnOp(oper, type, op1 DEBUGARG(largeNode)) , gtSlotNum(slotNum) +#if defined(UNIX_X86_ABI) + , gtPadAlign(0) +#endif #ifdef FEATURE_PUT_STRUCT_ARG_STK , gtPutArgStkKind(Kind::Invalid) , gtNumSlots(numSlots) @@ -4625,6 +4685,18 @@ struct GenTreePutArgStk : public GenTreeUnOp return gtSlotNum * TARGET_POINTER_SIZE; } +#if defined(UNIX_X86_ABI) + unsigned getArgPadding() + { + return gtPadAlign; + } + + void setArgPadding(unsigned padAlign) + { + gtPadAlign = padAlign; + } +#endif + #ifdef FEATURE_PUT_STRUCT_ARG_STK unsigned getArgSize() { @@ -4968,7 +5040,7 @@ inline bool GenTree::IsIntegralConstVector(ssize_t constVal) if ((gtOper == GT_SIMD) && (gtSIMD.gtSIMDIntrinsicID == SIMDIntrinsicInit) && gtGetOp1()->IsIntegralConst(constVal)) { assert(varTypeIsIntegral(gtSIMD.gtSIMDBaseType)); - assert(gtGetOp2() == nullptr); + assert(gtGetOp2IfPresent() == nullptr); return true; } #endif @@ -5149,12 +5221,24 @@ inline bool GenTree::RequiresNonNullOp2(genTreeOps oper) inline GenTreePtr GenTree::gtGetOp2() { + assert(OperIsBinary()); + + GenTreePtr op2 = gtOp.gtOp2; + + // Only allow null op2 if the node type allows it, e.g. GT_LIST. + assert((op2 != nullptr) || !RequiresNonNullOp2(gtOper)); + + return op2; +} + +inline GenTreePtr GenTree::gtGetOp2IfPresent() +{ /* gtOp.gtOp2 is only valid for GTK_BINOP nodes. */ GenTreePtr op2 = OperIsBinary() ? gtOp.gtOp2 : nullptr; // This documents the genTreeOps for which gtOp.gtOp2 cannot be nullptr. - // This helps prefix in its analyis of code which calls gtGetOp2() + // This helps prefix in its analysis of code which calls gtGetOp2() assert((op2 != nullptr) || !RequiresNonNullOp2(gtOper)); @@ -5319,10 +5403,10 @@ inline bool GenTreeBlk::HasGCPtr() return false; } -inline bool GenTree::isContainedSpillTemp() const +inline bool GenTree::isUsedFromSpillTemp() const { #if !defined(LEGACY_BACKEND) - // If spilled and no reg at use, then it is treated as contained. + // If spilled and no reg at use, then it is used from the spill temp location rather than being reloaded. if (((gtFlags & GTF_SPILLED) != 0) && ((gtFlags & GTF_NOREG_AT_USE) != 0)) { return true; |