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.h188
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;