In preparation for computing the TreeNodeInfo on the fly, move all the TreeNodeInfoInit and related methods to the LinearScan class.
}
//------------------------------------------------------------------------
-// IsContainableMemoryOp: Checks whether this is a memory op that can be contained.
-//
-// Arguments:
-// node - the node of interest.
-//
-// Return value:
-// True if this will definitely be a memory reference that could be contained.
-//
-// Notes:
-// This differs from the isMemoryOp() method on GenTree because it checks for
-// the case of doNotEnregister local. This won't include locals that
-// for some other reason do not become register candidates, nor those that get
-// spilled.
-// Also, because we usually call this before we redo dataflow, any new lclVars
-// introduced after the last dataflow analysis will not yet be marked lvTracked,
-// so we don't use that.
-//
-bool Lowering::IsContainableMemoryOp(GenTree* node)
-{
-#ifdef _TARGET_XARCH_
- if (node->isMemoryOp())
- {
- return true;
- }
- if (node->IsLocal())
- {
- if (!m_lsra->enregisterLocalVars)
- {
- return true;
- }
- LclVarDsc* varDsc = &comp->lvaTable[node->AsLclVar()->gtLclNum];
- return varDsc->lvDoNotEnregister;
- }
-#endif // _TARGET_XARCH_
- return false;
-}
-
-//------------------------------------------------------------------------
// This is the main entry point for Lowering.
GenTree* Lowering::LowerNode(GenTree* node)
GenTreeIntCon* op2 = cmp->gtGetOp2()->AsIntCon();
ssize_t op2Value = op2->IconValue();
- if (IsContainableMemoryOp(op1) && varTypeIsSmall(op1Type) && genTypeCanRepresentValue(op1Type, op2Value))
+ if (m_lsra->isContainableMemoryOp(op1) && varTypeIsSmall(op1Type) && genTypeCanRepresentValue(op1Type, op2Value))
{
//
// If op1's type is small then try to narrow op2 so it has the same type as op1.
// the result of bool returning calls.
//
- if (castOp->OperIs(GT_CALL, GT_LCL_VAR) || castOp->OperIsLogical() || IsContainableMemoryOp(castOp))
+ if (castOp->OperIs(GT_CALL, GT_LCL_VAR) || castOp->OperIsLogical() || m_lsra->isContainableMemoryOp(castOp))
{
assert(!castOp->gtOverflowEx()); // Must not be an overflow checking operation
andOp1->ClearContained();
andOp2->ClearContained();
- if (IsContainableMemoryOp(andOp1) && andOp2->IsIntegralConst())
+ if (m_lsra->isContainableMemoryOp(andOp1) && andOp2->IsIntegralConst())
{
//
// For "test" we only care about the bits that are set in the second operand (mask).
}
#endif
- // The initialization code for the TreeNodeInfo map was initially part of a single full IR
- // traversal and it has been split because the order of traversal performed by fgWalkTreePost
// does not necessarily lower nodes in execution order and also, it could potentially
// add new BasicBlocks on the fly as part of the Lowering pass so the traversal won't be complete.
//
currentLoc += 2;
- TreeNodeInfoInit(node);
+ m_lsra->TreeNodeInfoInit(node);
// Only nodes that produce values should have a non-zero dstCount.
assert((node->gtLsraInfo.dstCount == 0) || node->IsValue());
}
//------------------------------------------------------------------------
-// GetIndirSourceCount: Get the source registers for an indirection that might be contained.
-//
-// Arguments:
-// node - The node of interest
-//
-// Return Value:
-// The number of source registers used by the *parent* of this node.
-//
-int Lowering::GetIndirSourceCount(GenTreeIndir* indirTree)
-{
- GenTree* const addr = indirTree->gtOp1;
- if (!addr->isContained())
- {
- return 1;
- }
- if (!addr->OperIs(GT_LEA))
- {
- return 0;
- }
-
- GenTreeAddrMode* const addrMode = addr->AsAddrMode();
-
- unsigned srcCount = 0;
- if ((addrMode->Base() != nullptr) && !addrMode->Base()->isContained())
- {
- srcCount++;
- }
- if (addrMode->Index() != nullptr)
- {
- // We never have a contained index.
- assert(!addrMode->Index()->isContained());
- srcCount++;
- }
- return srcCount;
-}
-
-//------------------------------------------------------------------------
// ContainCheckDivOrMod: determine which operands of a div/mod should be contained.
//
// Arguments:
// everything is made explicit by adding casts.
assert(dividend->TypeGet() == divisor->TypeGet());
- if (IsContainableMemoryOp(divisor) || divisor->IsCnsNonZeroFltOrDbl())
+ if (m_lsra->isContainableMemoryOp(divisor) || divisor->IsCnsNonZeroFltOrDbl())
{
MakeSrcContained(node, divisor);
}
#endif
// divisor can be an r/m, but the memory indirection must be of the same size as the divide
- if (IsContainableMemoryOp(divisor) && (divisor->TypeGet() == node->TypeGet()))
+ if (m_lsra->isContainableMemoryOp(divisor) && (divisor->TypeGet() == node->TypeGet()))
{
MakeSrcContained(node, divisor);
}
return oldUseNode->AsLclVarCommon()->gtLclNum;
}
- // returns true if the tree can use the read-modify-write memory instruction form
- bool isRMWRegOper(GenTreePtr tree);
-
// return true if this call target is within range of a pc-rel call on the machine
bool IsCallTargetInRange(void* addr);
-#ifdef _TARGET_X86_
- bool ExcludeNonByteableRegisters(GenTree* tree);
-#endif
-
- void TreeNodeInfoInit(GenTree* stmt);
-
- void TreeNodeInfoInitCheckByteable(GenTree* tree);
-
- void SetDelayFree(GenTree* delayUseSrc);
-
#if defined(_TARGET_XARCH_)
- void TreeNodeInfoInitSimple(GenTree* tree);
-
//----------------------------------------------------------------------
// SetRegOptional - sets a bit to indicate to LSRA that register
// for a given tree node is optional for codegen purpose. If no
}
#endif // defined(_TARGET_XARCH_)
- // TreeNodeInfoInit methods
-
- int GetOperandSourceCount(GenTree* node);
- int GetIndirSourceCount(GenTreeIndir* indirTree);
- void HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs);
-
- void TreeNodeInfoInitStoreLoc(GenTree* tree);
- void TreeNodeInfoInitReturn(GenTree* tree);
- void TreeNodeInfoInitShiftRotate(GenTree* tree);
- void TreeNodeInfoInitPutArgReg(GenTreeUnOp* node);
- void TreeNodeInfoInitCall(GenTreeCall* call);
- void TreeNodeInfoInitCmp(GenTreePtr tree);
- void TreeNodeInfoInitStructArg(GenTreePtr structArg);
- void TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode);
- void TreeNodeInfoInitModDiv(GenTree* tree);
- void TreeNodeInfoInitIntrinsic(GenTree* tree);
- void TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* tree);
- void TreeNodeInfoInitIndir(GenTreeIndir* indirTree);
- void TreeNodeInfoInitGCWriteBarrier(GenTree* tree);
- void TreeNodeInfoInitCast(GenTree* tree);
-
-#if defined(_TARGET_XARCH_)
- void TreeNodeInfoInitMul(GenTreePtr tree);
- void SetContainsAVXFlags(bool isFloatingPointType = true, unsigned sizeOfSIMDVector = 0);
-#endif // defined(_TARGET_XARCH_)
-
-#ifdef FEATURE_SIMD
- void TreeNodeInfoInitSIMD(GenTreeSIMD* tree);
-#endif // FEATURE_SIMD
-
- void TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode);
+ // Per tree node member functions
+ void LowerStoreIndir(GenTreeIndir* node);
+ GenTree* LowerAdd(GenTree* node);
+ bool LowerUnsignedDivOrMod(GenTreeOp* divMod);
+ GenTree* LowerConstIntDivOrMod(GenTree* node);
+ GenTree* LowerSignedDivOrMod(GenTree* node);
+ void LowerBlockStore(GenTreeBlk* blkNode);
#ifdef _TARGET_ARM64_
void LowerPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info);
#endif // _TARGET_ARM64_
void LowerPutArgStk(GenTreePutArgStk* argNode, fgArgTabEntryPtr info);
#endif // _TARGET_ARM64_
void LowerPutArgStk(GenTreePutArgStk* tree);
-#ifdef _TARGET_ARM_
- void TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* tree);
-#endif
- void TreeNodeInfoInitLclHeap(GenTree* tree);
-
void DumpNodeInfoMap();
- // Per tree node member functions
- void LowerStoreIndir(GenTreeIndir* node);
- GenTree* LowerAdd(GenTree* node);
- bool LowerUnsignedDivOrMod(GenTreeOp* divMod);
- GenTree* LowerConstIntDivOrMod(GenTree* node);
- GenTree* LowerSignedDivOrMod(GenTree* node);
- void LowerBlockStore(GenTreeBlk* blkNode);
-
GenTree* TryCreateAddrMode(LIR::Use&& use, bool isIndir);
void AddrModeCleanupHelper(GenTreeAddrMode* addrMode, GenTree* node);
// for example small enough and non-relocatable
bool IsContainableImmed(GenTree* parentNode, GenTree* childNode);
- // Return true if 'node' is a containable memory op.
- bool IsContainableMemoryOp(GenTree* node);
-
// Makes 'childNode' contained in the 'parentNode'
void MakeSrcContained(GenTreePtr parentNode, GenTreePtr childNode);
#include "sideeffects.h"
#include "lower.h"
-//------------------------------------------------------------------------
-// isRMWRegOper: Can use the read-mofify-write memory instruction form?
-//
-// Return Value:
-// True if the tree can use the read-modify-write memory instruction form
-//
-bool Lowering::isRMWRegOper(GenTreePtr tree)
-{
- NYI_ARM("isRMWRegOper() is never used and tested for ARM");
- return false;
-}
-
#endif // _TARGET_ARM_
#endif // !LEGACY_BACKEND
#include "sideeffects.h"
#include "lower.h"
-// returns true if the tree can use the read-modify-write memory instruction form
-bool Lowering::isRMWRegOper(GenTreePtr tree)
-{
- return false;
-}
-
#endif // _TARGET_ARM64_
#endif // !LEGACY_BACKEND
return true;
}
-//------------------------------------------------------------------------------
-// isRMWRegOper: Can this binary tree node be used in a Read-Modify-Write format
-//
-// Arguments:
-// tree - a binary tree node
-//
-// Return Value:
-// Returns true if we can use the read-modify-write instruction form
-//
-// Notes:
-// This is used to determine whether to preference the source to the destination register.
-//
-bool Lowering::isRMWRegOper(GenTreePtr tree)
-{
- // TODO-XArch-CQ: Make this more accurate.
- // For now, We assume that most binary operators are of the RMW form.
- assert(tree->OperIsBinary());
-
- if (tree->OperIsCompare() || tree->OperIs(GT_CMP))
- {
- return false;
- }
-
- switch (tree->OperGet())
- {
- // These Opers either support a three op form (i.e. GT_LEA), or do not read/write their first operand
- case GT_LEA:
- case GT_STOREIND:
- case GT_ARR_INDEX:
- case GT_STORE_BLK:
- case GT_STORE_OBJ:
- return false;
-
- // x86/x64 does support a three op multiply when op2|op1 is a contained immediate
- case GT_MUL:
- return (!IsContainableImmed(tree, tree->gtOp.gtOp2) && !IsContainableImmed(tree, tree->gtOp.gtOp1));
-
- default:
- return true;
- }
-}
-
// anything is in range for AMD64
bool Lowering::IsCallTargetInRange(void* addr)
{
{
assert(node->OperGet() == GT_MUL);
- if (IsContainableMemoryOp(op2) || op2->IsCnsNonZeroFltOrDbl())
+ if (m_lsra->isContainableMemoryOp(op2) || op2->IsCnsNonZeroFltOrDbl())
{
MakeSrcContained(node, op2);
}
- else if (op1->IsCnsNonZeroFltOrDbl() || (IsContainableMemoryOp(op1) && IsSafeToContainMem(node, op1)))
+ else if (op1->IsCnsNonZeroFltOrDbl() || (m_lsra->isContainableMemoryOp(op1) && IsSafeToContainMem(node, op1)))
{
// Since GT_MUL is commutative, we will try to re-order operands if it is safe to
// generate more efficient code sequence for the case of GT_MUL(op1=memOp, op2=non-memOp)
}
MakeSrcContained(node, imm); // The imm is always contained
- if (IsContainableMemoryOp(other))
+ if (m_lsra->isContainableMemoryOp(other))
{
memOp = other; // memOp may be contained below
}
//
if (memOp == nullptr)
{
- if (IsContainableMemoryOp(op2) && (op2->TypeGet() == node->TypeGet()) && IsSafeToContainMem(node, op2))
+ if (m_lsra->isContainableMemoryOp(op2) && (op2->TypeGet() == node->TypeGet()) && IsSafeToContainMem(node, op2))
{
memOp = op2;
}
- else if (IsContainableMemoryOp(op1) && (op1->TypeGet() == node->TypeGet()) && IsSafeToContainMem(node, op1))
+ else if (m_lsra->isContainableMemoryOp(op1) && (op1->TypeGet() == node->TypeGet()) && IsSafeToContainMem(node, op1))
{
memOp = op1;
}
// U8 -> R8 conversion requires that the operand be in a register.
if (srcType != TYP_ULONG)
{
- if (IsContainableMemoryOp(castOp) || castOp->IsCnsNonZeroFltOrDbl())
+ if (m_lsra->isContainableMemoryOp(castOp) || castOp->IsCnsNonZeroFltOrDbl())
{
MakeSrcContained(node, castOp);
}
{
MakeSrcContained(cmp, otherOp);
}
- else if (IsContainableMemoryOp(otherOp) && ((otherOp == op2) || IsSafeToContainMem(cmp, otherOp)))
+ else if (m_lsra->isContainableMemoryOp(otherOp) && ((otherOp == op2) || IsSafeToContainMem(cmp, otherOp)))
{
MakeSrcContained(cmp, otherOp);
}
// we can treat the MemoryOp as contained.
if (op1Type == op2Type)
{
- if (IsContainableMemoryOp(op1))
+ if (m_lsra->isContainableMemoryOp(op1))
{
MakeSrcContained(cmp, op1);
}
// Note that TEST does not have a r,rm encoding like CMP has but we can still
// contain the second operand because the emitter maps both r,rm and rm,r to
// the same instruction code. This avoids the need to special case TEST here.
- if (IsContainableMemoryOp(op2))
+ if (m_lsra->isContainableMemoryOp(op2))
{
MakeSrcContained(cmp, op2);
}
- else if (IsContainableMemoryOp(op1) && IsSafeToContainMem(cmp, op1))
+ else if (m_lsra->isContainableMemoryOp(op1) && IsSafeToContainMem(cmp, op1))
{
MakeSrcContained(cmp, op1);
}
// On Xarch RMW operations require the source to be an immediate or in a register.
// Therefore, if we have previously marked the indirOpSource as contained while lowering
// the binary node, we need to reset that now.
- if (IsContainableMemoryOp(indirOpSource))
+ if (m_lsra->isContainableMemoryOp(indirOpSource))
{
indirOpSource->ClearContained();
}
if (!binOpInRMW)
{
const unsigned operatorSize = genTypeSize(node->TypeGet());
- if (IsContainableMemoryOp(op2) && (genTypeSize(op2->TypeGet()) == operatorSize))
+ if (m_lsra->isContainableMemoryOp(op2) && (genTypeSize(op2->TypeGet()) == operatorSize))
{
directlyEncodable = true;
operand = op2;
else if (node->OperIsCommutative())
{
if (IsContainableImmed(node, op1) ||
- (IsContainableMemoryOp(op1) && (genTypeSize(op1->TypeGet()) == operatorSize) &&
+ (m_lsra->isContainableMemoryOp(op1) && (genTypeSize(op1->TypeGet()) == operatorSize) &&
IsSafeToContainMem(node, op1)))
{
// If it is safe, we can reverse the order of operands of commutative operations for efficient
{
other = node->gtIndex;
}
- else if (IsContainableMemoryOp(node->gtIndex))
+ else if (m_lsra->isContainableMemoryOp(node->gtIndex))
{
other = node->gtIndex;
}
if (node->gtIndex->TypeGet() == node->gtArrLen->TypeGet())
{
- if (IsContainableMemoryOp(other))
+ if (m_lsra->isContainableMemoryOp(other))
{
MakeSrcContained(node, other);
}
if (node->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Sqrt)
{
GenTree* op1 = node->gtGetOp1();
- if (IsContainableMemoryOp(op1) || op1->IsCnsNonZeroFltOrDbl())
+ if (m_lsra->isContainableMemoryOp(op1) || op1->IsCnsNonZeroFltOrDbl())
{
MakeSrcContained(node, op1);
}
// If the index is a constant, mark it as contained.
CheckImmedAndMakeContained(simdNode, op2);
- if (IsContainableMemoryOp(op1))
+ if (m_lsra->isContainableMemoryOp(op1))
{
MakeSrcContained(simdNode, op1);
if (op1->OperGet() == GT_IND)
// everything is made explicit by adding casts.
assert(op1->TypeGet() == op2->TypeGet());
- if (IsContainableMemoryOp(op2) || op2->IsCnsNonZeroFltOrDbl())
+ if (m_lsra->isContainableMemoryOp(op2) || op2->IsCnsNonZeroFltOrDbl())
{
MakeSrcContained(node, op2);
}
else if (node->OperIsCommutative() &&
- (op1->IsCnsNonZeroFltOrDbl() || (IsContainableMemoryOp(op1) && IsSafeToContainMem(node, op1))))
+ (op1->IsCnsNonZeroFltOrDbl() || (m_lsra->isContainableMemoryOp(op1) && IsSafeToContainMem(node, op1))))
{
// Though we have GT_ADD(op1=memOp, op2=non-memOp, we try to reorder the operands
// as long as it is safe so that the following efficient code sequence is generated:
}
}
+//------------------------------------------------------------------------
+// IsContainableMemoryOp: Checks whether this is a memory op that can be contained.
+//
+// Arguments:
+// node - the node of interest.
+//
+// Return value:
+// True if this will definitely be a memory reference that could be contained.
+//
+// Notes:
+// This differs from the isMemoryOp() method on GenTree because it checks for
+// the case of doNotEnregister local. This won't include locals that
+// for some other reason do not become register candidates, nor those that get
+// spilled.
+// Also, because we usually call this before we redo dataflow, any new lclVars
+// introduced after the last dataflow analysis will not yet be marked lvTracked,
+// so we don't use that.
+//
+bool LinearScan::isContainableMemoryOp(GenTree* node)
+{
+#ifdef _TARGET_XARCH_
+ if (node->isMemoryOp())
+ {
+ return true;
+ }
+ if (node->IsLocal())
+ {
+ if (!enregisterLocalVars)
+ {
+ return true;
+ }
+ LclVarDsc* varDsc = &compiler->lvaTable[node->AsLclVar()->gtLclNum];
+ return varDsc->lvDoNotEnregister;
+ }
+#endif // _TARGET_XARCH_
+ return false;
+}
+
bool LinearScan::isRegCandidate(LclVarDsc* varDsc)
{
// We shouldn't be called if opt settings do not permit register variables.
{
BasicBlock* block;
- // start numbering at 1; 0 is the entry
- LsraLocation currentLoc = 1;
-
JITDUMP("\nbuildIntervals ========\n");
// Now build (empty) records for all of the physical registers
// second part:
JITDUMP("\nbuildIntervals second part ========\n");
- currentLoc = 0;
+ LsraLocation currentLoc = 0;
// Next, create ParamDef RefPositions for all the tracked parameters,
// in order of their varIndex
}
}
+//------------------------------------------------------------------------
+// GetIndirSourceCount: Get the source registers for an indirection that might be contained.
+//
+// Arguments:
+// node - The node of interest
+//
+// Return Value:
+// The number of source registers used by the *parent* of this node.
+//
+int LinearScan::GetIndirSourceCount(GenTreeIndir* indirTree)
+{
+ GenTree* const addr = indirTree->gtOp1;
+ if (!addr->isContained())
+ {
+ return 1;
+ }
+ if (!addr->OperIs(GT_LEA))
+ {
+ return 0;
+ }
+
+ GenTreeAddrMode* const addrMode = addr->AsAddrMode();
+
+ unsigned srcCount = 0;
+ if ((addrMode->Base() != nullptr) && !addrMode->Base()->isContained())
+ {
+ srcCount++;
+ }
+ if (addrMode->Index() != nullptr)
+ {
+ // We never have a contained index.
+ assert(!addrMode->Index()->isContained());
+ srcCount++;
+ }
+ return srcCount;
+}
+
void TreeNodeInfo::Initialize(LinearScan* lsra, GenTree* node, LsraLocation location)
{
regMaskTP dstCandidates;
// Used by Lowering when considering whether to split Longs, as well as by identifyCandidates().
bool isRegCandidate(LclVarDsc* varDsc);
+ bool isContainableMemoryOp(GenTree* node);
+
private:
// Determine which locals are candidates for allocation
void identifyCandidates();
// Set of large vector (TYP_SIMD32 on AVX) variables to consider for callee-save registers.
VARSET_TP largeVectorCalleeSaveCandidateVars;
#endif // FEATURE_PARTIAL_SIMD_CALLEE_SAVE
+
+ //-----------------------------------------------------------------------
+ // TreeNodeInfo methods
+ //-----------------------------------------------------------------------
+
+ void TreeNodeInfoInit(GenTree* stmt);
+
+ void TreeNodeInfoInitCheckByteable(GenTree* tree);
+
+ void SetDelayFree(GenTree* delayUseSrc);
+
+ void TreeNodeInfoInitSimple(GenTree* tree);
+ int GetOperandSourceCount(GenTree* node);
+ int GetIndirSourceCount(GenTreeIndir* indirTree);
+ void HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs);
+
+ void TreeNodeInfoInitStoreLoc(GenTree* tree);
+ void TreeNodeInfoInitReturn(GenTree* tree);
+ void TreeNodeInfoInitShiftRotate(GenTree* tree);
+ void TreeNodeInfoInitPutArgReg(GenTreeUnOp* node);
+ void TreeNodeInfoInitCall(GenTreeCall* call);
+ void TreeNodeInfoInitCmp(GenTreePtr tree);
+ void TreeNodeInfoInitStructArg(GenTreePtr structArg);
+ void TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode);
+ void TreeNodeInfoInitModDiv(GenTree* tree);
+ void TreeNodeInfoInitIntrinsic(GenTree* tree);
+ void TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* tree);
+ void TreeNodeInfoInitIndir(GenTreeIndir* indirTree);
+ void TreeNodeInfoInitGCWriteBarrier(GenTree* tree);
+ void TreeNodeInfoInitCast(GenTree* tree);
+
+#ifdef _TARGET_X86_
+ bool ExcludeNonByteableRegisters(GenTree* tree);
+#endif
+
+#if defined(_TARGET_XARCH_)
+ // returns true if the tree can use the read-modify-write memory instruction form
+ bool isRMWRegOper(GenTreePtr tree);
+ void TreeNodeInfoInitMul(GenTreePtr tree);
+ void SetContainsAVXFlags(bool isFloatingPointType = true, unsigned sizeOfSIMDVector = 0);
+#endif // defined(_TARGET_XARCH_)
+
+#ifdef FEATURE_SIMD
+ void TreeNodeInfoInitSIMD(GenTreeSIMD* tree);
+#endif // FEATURE_SIMD
+
+ void TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode);
+#ifdef _TARGET_ARM_
+ void TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* tree);
+#endif
+ void TreeNodeInfoInitLclHeap(GenTree* tree);
};
/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
+void LinearScan::TreeNodeInfoInitReturn(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
GenTree* op1 = tree->gtGetOp1();
assert(info->dstCount == 0);
GenTree* loVal = op1->gtGetOp1();
GenTree* hiVal = op1->gtGetOp2();
info->srcCount = 2;
- loVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_LO);
- hiVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_HI);
+ loVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_LO);
+ hiVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_HI);
}
else
{
if (useCandidates != RBM_NONE)
{
- tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, useCandidates);
+ tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, useCandidates);
}
}
}
-void Lowering::TreeNodeInfoInitLclHeap(GenTree* tree)
+void LinearScan::TreeNodeInfoInitLclHeap(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
assert(info->dstCount == 1);
// requirements needed by LSRA to build the Interval Table (source,
// destination and internal [temp] register counts).
//
-void Lowering::TreeNodeInfoInit(GenTree* tree)
+void LinearScan::TreeNodeInfoInit(GenTree* tree)
{
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
-
unsigned kind = tree->OperKind();
TreeNodeInfo* info = &(tree->gtLsraInfo);
RegisterType registerType = TypeGet(tree);
assert(info->dstCount == 1);
break;
default:
- NYI_ARM("Lowering::TreeNodeInfoInit for GT_INTRINSIC");
+ NYI_ARM("LinearScan::TreeNodeInfoInit for GT_INTRINSIC");
break;
}
}
// FloatToIntCast needs a temporary register
if (varTypeIsFloating(castOpType) && varTypeIsIntOrI(tree))
{
- info->setInternalCandidates(m_lsra, RBM_ALLFLOAT);
+ info->setInternalCandidates(this, RBM_ALLFLOAT);
info->internalFloatCount = 1;
info->isInternalRegDelayFree = true;
}
- CastInfo castInfo;
+ Lowering::CastInfo castInfo;
// Get information about the cast.
- getCastDescription(tree, &castInfo);
+ Lowering::getCastDescription(tree, &castInfo);
if (castInfo.requiresOverflowCheck)
{
assert(tree->TypeGet() == TYP_INT);
info->srcCount = 1;
- info->setSrcCandidates(l, RBM_INTRET);
- tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, RBM_INTRET);
+ info->setSrcCandidates(this, RBM_INTRET);
+ tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, RBM_INTRET);
}
break;
case GT_STORE_BLK:
case GT_STORE_OBJ:
case GT_STORE_DYN_BLK:
- LowerBlockStore(tree->AsBlk());
TreeNodeInfoInitBlockStore(tree->AsBlk());
break;
case GT_CATCH_ARG:
info->srcCount = 0;
assert(info->dstCount == 1);
- info->setDstCandidates(l, RBM_EXCEPTION_OBJECT);
+ info->setDstCandidates(this, RBM_EXCEPTION_OBJECT);
break;
case GT_CLS_VAR:
// requirements needed by LSRA to build the Interval Table (source,
// destination and internal [temp] register counts).
//
-void Lowering::TreeNodeInfoInit(GenTree* tree)
+void LinearScan::TreeNodeInfoInit(GenTree* tree)
{
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
-
unsigned kind = tree->OperKind();
TreeNodeInfo* info = &(tree->gtLsraInfo);
RegisterType registerType = TypeGet(tree);
info->srcCount = 1;
assert(info->dstCount == 0);
- info->setSrcCandidates(l, RBM_INTRET);
- tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, RBM_INTRET);
+ info->setSrcCandidates(this, RBM_INTRET);
+ tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, RBM_INTRET);
}
break;
#endif // DEBUG
// Some overflow checks need a temp reg
- CastInfo castInfo;
-
+ Lowering::CastInfo castInfo;
// Get information about the cast.
- getCastDescription(tree, &castInfo);
+ Lowering::getCastDescription(tree, &castInfo);
if (castInfo.requiresOverflowCheck)
{
{
// For a GT_ADDR, the child node should not be evaluated into a register
GenTreePtr child = tree->gtOp.gtOp1;
- assert(!l->isCandidateLocalRef(child));
- MakeSrcContained(tree, child);
- info->srcCount = 0;
+ assert(!isCandidateLocalRef(child));
+ assert(child->isContained());
assert(info->dstCount == 1);
+ info->srcCount = 0;
}
break;
case GT_STORE_BLK:
case GT_STORE_OBJ:
case GT_STORE_DYN_BLK:
- LowerBlockStore(tree->AsBlk());
TreeNodeInfoInitBlockStore(tree->AsBlk());
break;
case GT_CATCH_ARG:
info->srcCount = 0;
assert(info->dstCount == 1);
- info->setDstCandidates(l, RBM_EXCEPTION_OBJECT);
+ info->setDstCandidates(this, RBM_EXCEPTION_OBJECT);
break;
case GT_CLS_VAR:
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
+void LinearScan::TreeNodeInfoInitReturn(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
GenTree* op1 = tree->gtGetOp1();
regMaskTP useCandidates = RBM_NONE;
if (useCandidates != RBM_NONE)
{
- tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, useCandidates);
+ tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, useCandidates);
}
}
// - Setting the appropriate candidates for a store of a multi-reg call return value.
// - Handling of contained immediates.
//
-void Lowering::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
+void LinearScan::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
{
TreeNodeInfo* info = &(storeLoc->gtLsraInfo);
GenTree* op1 = storeLoc->gtGetOp1();
info->srcCount = retTypeDesc->GetReturnRegCount();
// Call node srcCandidates = Bitwise-OR(allregs(GetReturnRegType(i))) for all i=0..RetRegCount-1
- regMaskTP srcCandidates = m_lsra->allMultiRegCallNodeRegs(call);
- op1->gtLsraInfo.setSrcCandidates(m_lsra, srcCandidates);
+ regMaskTP srcCandidates = allMultiRegCallNodeRegs(call);
+ op1->gtLsraInfo.setSrcCandidates(this, srcCandidates);
}
else
{
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCmp(GenTreePtr tree)
+void LinearScan::TreeNodeInfoInitCmp(GenTreePtr tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
}
}
-void Lowering::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
+void LinearScan::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
{
GenTreePtr dst = tree;
GenTreePtr addr = tree->gtOp.gtOp1;
// the 'addr' goes into x14 (REG_WRITE_BARRIER_DST_BYREF)
// the 'src' goes into x15 (REG_WRITE_BARRIER)
//
- addr->gtLsraInfo.setSrcCandidates(m_lsra, RBM_WRITE_BARRIER_DST_BYREF);
- src->gtLsraInfo.setSrcCandidates(m_lsra, RBM_WRITE_BARRIER);
+ addr->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_DST_BYREF);
+ src->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER);
#else
// For the standard JIT Helper calls
// op1 goes into REG_ARG_0 and
// op2 goes into REG_ARG_1
//
- addr->gtLsraInfo.setSrcCandidates(m_lsra, RBM_ARG_0);
- src->gtLsraInfo.setSrcCandidates(m_lsra, RBM_ARG_1);
+ addr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
+ src->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
#endif // NOGC_WRITE_BARRIERS
// Both src and dst must reside in a register, which they should since we haven't set
// Arguments:
// indirTree - GT_IND, GT_STOREIND, block node or GT_NULLCHECK gentree node
//
-void Lowering::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
+void LinearScan::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
{
// If this is the rhs of a block copy (i.e. non-enregisterable struct),
// it has no register requirements.
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitShiftRotate(GenTree* tree)
+void LinearScan::TreeNodeInfoInitShiftRotate(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
GenTreePtr shiftBy = tree->gtOp.gtOp2;
info->srcCount = shiftBy->isContained() ? 1 : 2;
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitPutArgReg(GenTreeUnOp* node)
+void LinearScan::TreeNodeInfoInitPutArgReg(GenTreeUnOp* node)
{
assert(node != nullptr);
assert(node->OperIsPutArgReg());
argMask |= genRegMask(REG_NEXT(argReg));
}
#endif // ARM_SOFTFP
- node->gtLsraInfo.setDstCandidates(m_lsra, argMask);
- node->gtLsraInfo.setSrcCandidates(m_lsra, argMask);
+ node->gtLsraInfo.setDstCandidates(this, argMask);
+ node->gtLsraInfo.setSrcCandidates(this, argMask);
// To avoid redundant moves, have the argument operand computed in the
// register in which the argument is passed to the call.
- node->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(m_lsra, m_lsra->getUseCandidates(node));
+ node->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, getUseCandidates(node));
}
//------------------------------------------------------------------------
// Since the integer register is not associated with the arg node, we will reserve it as
// an internal register on the call so that it is not used during the evaluation of the call node
// (e.g. for the target).
-void Lowering::HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs)
+void LinearScan::HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs)
{
#if FEATURE_VARARG
if (call->IsVarargs() && varTypeIsFloating(argNode))
*callHasFloatRegArgs = true;
regNumber argReg = argNode->gtRegNum;
- regNumber targetReg = comp->getCallArgIntRegister(argReg);
+ regNumber targetReg = compiler->getCallArgIntRegister(argReg);
call->gtLsraInfo.setInternalIntCount(call->gtLsraInfo.internalIntCount + 1);
- call->gtLsraInfo.addInternalCandidates(m_lsra, genRegMask(targetReg));
+ call->gtLsraInfo.addInternalCandidates(this, genRegMask(targetReg));
}
#endif // FEATURE_VARARG
}
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
+void LinearScan::TreeNodeInfoInitCall(GenTreeCall* call)
{
TreeNodeInfo* info = &(call->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
bool hasMultiRegRetVal = false;
ReturnTypeDesc* retTypeDesc = nullptr;
#ifdef _TARGET_ARM64_
// Fast tail call - make sure that call target is always computed in IP0
// so that epilog sequence can generate "br xip0" to achieve fast tail call.
- ctrlExpr->gtLsraInfo.setSrcCandidates(l, genRegMask(REG_IP0));
+ ctrlExpr->gtLsraInfo.setSrcCandidates(this, genRegMask(REG_IP0));
#endif // _TARGET_ARM64_
}
}
{
// The ARM CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
// TCB in REG_PINVOKE_TCB. fgMorphCall() sets the correct argument registers.
- info->setDstCandidates(l, RBM_PINVOKE_TCB);
+ info->setDstCandidates(this, RBM_PINVOKE_TCB);
}
else
#endif // _TARGET_ARM_
if (hasMultiRegRetVal)
{
assert(retTypeDesc != nullptr);
- info->setDstCandidates(l, retTypeDesc->GetABIReturnRegs());
+ info->setDstCandidates(this, retTypeDesc->GetABIReturnRegs());
}
else if (varTypeIsFloating(registerType))
{
- info->setDstCandidates(l, RBM_FLOATRET);
+ info->setDstCandidates(this, RBM_FLOATRET);
}
else if (registerType == TYP_LONG)
{
- info->setDstCandidates(l, RBM_LNGRET);
+ info->setDstCandidates(this, RBM_LNGRET);
}
else
{
- info->setDstCandidates(l, RBM_INTRET);
+ info->setDstCandidates(this, RBM_INTRET);
}
// First, count reg args
// Don't assign the call target to any of the argument registers because
// we will use them to also pass floating point arguments as required
// by Arm64 ABI.
- ctrlExpr->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~(RBM_ARG_REGS));
+ ctrlExpr->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~(RBM_ARG_REGS));
}
#ifdef _TARGET_ARM_
// Notes:
// Set the child node(s) to be contained when we have a multireg arg
//
-void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode)
+void LinearScan::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* argNode)
{
assert(argNode->gtOper == GT_PUTARG_STK);
// Notes:
// Set the child node(s) to be contained
//
-void Lowering::TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* argNode)
+void LinearScan::TreeNodeInfoInitPutArgSplit(GenTreePutArgSplit* argNode)
{
assert(argNode->gtOper == GT_PUTARG_SPLIT);
{
argMask |= genRegMask((regNumber)((unsigned)argReg + i));
}
- argNode->gtLsraInfo.setDstCandidates(m_lsra, argMask);
- argNode->gtLsraInfo.setSrcCandidates(m_lsra, argMask);
+ argNode->gtLsraInfo.setDstCandidates(this, argMask);
+ argNode->gtLsraInfo.setSrcCandidates(this, argMask);
if (putArgChild->OperGet() == GT_FIELD_LIST)
{
if (idx < argNode->gtNumRegs)
{
GenTreePtr node = fieldListPtr->gtGetOp1();
- node->gtLsraInfo.setSrcCandidates(m_lsra, genRegMask((regNumber)((unsigned)argReg + idx)));
+ node->gtLsraInfo.setSrcCandidates(this, genRegMask((regNumber)((unsigned)argReg + idx)));
}
else
{
// We can use a ldr/str sequence so we need an internal register
argNode->gtLsraInfo.internalIntCount = 1;
regMaskTP internalMask = RBM_ALLINT & ~argMask;
- argNode->gtLsraInfo.setInternalCandidates(m_lsra, internalMask);
+ argNode->gtLsraInfo.setInternalCandidates(this, internalMask);
GenTreePtr objChild = putArgChild->gtOp.gtOp1;
if (objChild->OperGet() == GT_LCL_VAR_ADDR)
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
+void LinearScan::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
{
GenTree* dstAddr = blkNode->Addr();
unsigned size = blkNode->gtBlkSize;
GenTree* source = blkNode->Data();
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
// Sources are dest address and initVal or source.
// We may require an additional source or temp register for the size.
{
assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
// The helper follows the regular ABI.
- dstAddr->gtLsraInfo.setSrcCandidates(l, RBM_ARG_0);
+ dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
assert(!initVal->isContained());
blkNode->gtLsraInfo.srcCount++;
- initVal->gtLsraInfo.setSrcCandidates(l, RBM_ARG_1);
+ initVal->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
if (size != 0)
{
// Reserve a temp register for the block size argument.
- blkNode->gtLsraInfo.setInternalCandidates(l, RBM_ARG_2);
+ blkNode->gtLsraInfo.setInternalCandidates(this, RBM_ARG_2);
blkNode->gtLsraInfo.internalIntCount = 1;
}
else
noway_assert(blkNode->gtOper == GT_STORE_DYN_BLK);
blkNode->gtLsraInfo.setSrcCount(3);
GenTree* sizeNode = blkNode->AsDynBlk()->gtDynamicSize;
- sizeNode->gtLsraInfo.setSrcCandidates(l, RBM_ARG_2);
+ sizeNode->gtLsraInfo.setSrcCandidates(this, RBM_ARG_2);
}
}
}
// We can't use the special Write Barrier registers, so exclude them from the mask
regMaskTP internalIntCandidates = RBM_ALLINT & ~(RBM_WRITE_BARRIER_DST_BYREF | RBM_WRITE_BARRIER_SRC_BYREF);
- blkNode->gtLsraInfo.setInternalCandidates(l, internalIntCandidates);
+ blkNode->gtLsraInfo.setInternalCandidates(this, internalIntCandidates);
// If we have a dest address we want it in RBM_WRITE_BARRIER_DST_BYREF.
- dstAddr->gtLsraInfo.setSrcCandidates(l, RBM_WRITE_BARRIER_DST_BYREF);
+ dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_DST_BYREF);
// If we have a source address we want it in REG_WRITE_BARRIER_SRC_BYREF.
// Otherwise, if it is a local, codegen will put its address in REG_WRITE_BARRIER_SRC_BYREF,
// which is killed by a StoreObj (and thus needn't be reserved).
if (srcAddrOrFill != nullptr)
{
- srcAddrOrFill->gtLsraInfo.setSrcCandidates(l, RBM_WRITE_BARRIER_SRC_BYREF);
+ srcAddrOrFill->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_SRC_BYREF);
}
}
else
else
{
assert(blkNode->gtBlkOpKind == GenTreeBlk::BlkOpKindHelper);
- dstAddr->gtLsraInfo.setSrcCandidates(l, RBM_ARG_0);
+ dstAddr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
// The srcAddr goes in arg1.
if (srcAddrOrFill != nullptr)
{
- srcAddrOrFill->gtLsraInfo.setSrcCandidates(l, RBM_ARG_1);
+ srcAddrOrFill->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
}
if (size != 0)
{
assert(blkNode->gtOper == GT_STORE_DYN_BLK);
blkNode->gtLsraInfo.srcCount++;
GenTree* blockSize = blkNode->AsDynBlk()->gtDynamicSize;
- blockSize->gtLsraInfo.setSrcCandidates(l, RBM_ARG_2);
+ blockSize->gtLsraInfo.setSrcCandidates(this, RBM_ARG_2);
}
}
if (internalIntCount != 0)
{
blkNode->gtLsraInfo.internalIntCount = internalIntCount;
- blkNode->gtLsraInfo.setInternalCandidates(l, internalIntCandidates);
+ blkNode->gtLsraInfo.setInternalCandidates(this, internalIntCandidates);
}
}
blkNode->gtLsraInfo.srcCount += GetOperandSourceCount(source);
// Return Value:
// The number of source registers used by the *parent* of this node.
//
-int Lowering::GetOperandSourceCount(GenTree* node)
+int LinearScan::GetOperandSourceCount(GenTree* node)
{
if (!node->isContained())
{
// - Setting the appropriate candidates for a store of a multi-reg call return value.
// - Requesting an internal register for SIMD12 stores.
//
-void Lowering::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
+void LinearScan::TreeNodeInfoInitStoreLoc(GenTreeLclVarCommon* storeLoc)
{
TreeNodeInfo* info = &(storeLoc->gtLsraInfo);
assert(info->dstCount == 0);
info->srcCount = retTypeDesc->GetReturnRegCount();
// Call node srcCandidates = Bitwise-OR(allregs(GetReturnRegType(i))) for all i=0..RetRegCount-1
- regMaskTP srcCandidates = m_lsra->allMultiRegCallNodeRegs(call);
- op1->gtLsraInfo.setSrcCandidates(m_lsra, srcCandidates);
+ regMaskTP srcCandidates = allMultiRegCallNodeRegs(call);
+ op1->gtLsraInfo.setSrcCandidates(this, srcCandidates);
return;
}
else
{
// Need an additional register to extract upper 4 bytes of Vector3.
info->internalFloatCount = 1;
- info->setInternalCandidates(m_lsra, m_lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
return;
}
// requirements needed by LSRA to build the Interval Table (source,
// destination and internal [temp] register counts).
//
-void Lowering::TreeNodeInfoInit(GenTree* tree)
+void LinearScan::TreeNodeInfoInit(GenTree* tree)
{
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
-
TreeNodeInfo* info = &(tree->gtLsraInfo);
if (tree->isContained())
// because both targetReg and internal reg will be in use at the same time.
info->internalFloatCount = 1;
info->isInternalRegDelayFree = true;
- info->setInternalCandidates(m_lsra, m_lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
#endif
break;
info->srcCount = 1;
- info->setSrcCandidates(l, RBM_INTRET);
- tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(l, RBM_INTRET);
+ info->setSrcCandidates(this, RBM_INTRET);
+ tree->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, RBM_INTRET);
}
break;
info->srcCount = 0;
assert(info->dstCount == 1);
#ifdef _TARGET_X86_
- info->setDstCandidates(m_lsra, RBM_BYTE_REGS);
+ info->setDstCandidates(this, RBM_BYTE_REGS);
#endif // _TARGET_X86_
break;
info->srcCount = tree->gtOp.gtOp1->isContained() ? 0 : 1;
assert(info->dstCount == 0);
info->internalIntCount = 1;
- info->setInternalCandidates(l, l->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allRegs(TYP_INT));
break;
case GT_MOD:
if (varTypeIsFloating(tree))
{
info->internalFloatCount = 1;
- info->setInternalCandidates(l, l->internalFloatRegCandidates());
+ info->setInternalCandidates(this, internalFloatRegCandidates());
}
break;
// comparand is preferenced to RAX.
// Remaining two operands can be in any reg other than RAX.
- tree->gtCmpXchg.gtOpComparand->gtLsraInfo.setSrcCandidates(l, RBM_RAX);
- tree->gtCmpXchg.gtOpLocation->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~RBM_RAX);
- tree->gtCmpXchg.gtOpValue->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~RBM_RAX);
- tree->gtLsraInfo.setDstCandidates(l, RBM_RAX);
+ tree->gtCmpXchg.gtOpComparand->gtLsraInfo.setSrcCandidates(this, RBM_RAX);
+ tree->gtCmpXchg.gtOpLocation->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~RBM_RAX);
+ tree->gtCmpXchg.gtOpValue->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~RBM_RAX);
+ tree->gtLsraInfo.setDstCandidates(this, RBM_RAX);
break;
case GT_LOCKADD:
{
// For a GT_ADDR, the child node should not be evaluated into a register
GenTreePtr child = tree->gtOp.gtOp1;
- assert(!l->isCandidateLocalRef(child));
+ assert(!isCandidateLocalRef(child));
assert(child->isContained());
assert(info->dstCount == 1);
info->srcCount = 0;
case GT_CATCH_ARG:
info->srcCount = 0;
assert(info->dstCount == 1);
- info->setDstCandidates(l, RBM_EXCEPTION_OBJECT);
+ info->setDstCandidates(this, RBM_EXCEPTION_OBJECT);
break;
#if !FEATURE_EH_FUNCLETS
delayUseSrc = op1;
}
else if ((op2 != nullptr) &&
- (!tree->OperIsCommutative() || (IsContainableMemoryOp(op2) && (op2->gtLsraInfo.srcCount == 0))))
+ (!tree->OperIsCommutative() || (isContainableMemoryOp(op2) && (op2->gtLsraInfo.srcCount == 0))))
{
delayUseSrc = op2;
}
assert((info->dstCount < 2) || (tree->IsMultiRegCall() && info->dstCount == MAX_RET_REG_COUNT));
}
-void Lowering::SetDelayFree(GenTree* delayUseSrc)
+void LinearScan::SetDelayFree(GenTree* delayUseSrc)
{
// If delayUseSrc is an indirection and it doesn't produce a result, then we need to set "delayFree'
// on the base & index, if any.
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCheckByteable(GenTree* tree)
+void LinearScan::TreeNodeInfoInitCheckByteable(GenTree* tree)
{
#ifdef _TARGET_X86_
- LinearScan* l = m_lsra;
TreeNodeInfo* info = &(tree->gtLsraInfo);
// Exclude RBM_NON_BYTE_REGS from dst candidates of tree node and src candidates of operands
regMaskTP regMask;
if (info->dstCount > 0)
{
- regMask = info->getDstCandidates(l);
+ regMask = info->getDstCandidates(this);
assert(regMask != RBM_NONE);
- info->setDstCandidates(l, regMask & ~RBM_NON_BYTE_REGS);
+ info->setDstCandidates(this, regMask & ~RBM_NON_BYTE_REGS);
}
if (tree->OperIsSimple())
// No need to set src candidates on a contained child operand.
if (!op->isContained())
{
- regMask = op->gtLsraInfo.getSrcCandidates(l);
+ regMask = op->gtLsraInfo.getSrcCandidates(this);
assert(regMask != RBM_NONE);
- op->gtLsraInfo.setSrcCandidates(l, regMask & ~RBM_NON_BYTE_REGS);
+ op->gtLsraInfo.setSrcCandidates(this, regMask & ~RBM_NON_BYTE_REGS);
}
}
op = tree->gtOp.gtOp2;
if (!op->isContained())
{
- regMask = op->gtLsraInfo.getSrcCandidates(l);
+ regMask = op->gtLsraInfo.getSrcCandidates(this);
assert(regMask != RBM_NONE);
- op->gtLsraInfo.setSrcCandidates(l, regMask & ~RBM_NON_BYTE_REGS);
+ op->gtLsraInfo.setSrcCandidates(this, regMask & ~RBM_NON_BYTE_REGS);
}
}
}
#endif //_TARGET_X86_
}
+//------------------------------------------------------------------------------
+// isRMWRegOper: Can this binary tree node be used in a Read-Modify-Write format
+//
+// Arguments:
+// tree - a binary tree node
+//
+// Return Value:
+// Returns true if we can use the read-modify-write instruction form
+//
+// Notes:
+// This is used to determine whether to preference the source to the destination register.
+//
+bool LinearScan::isRMWRegOper(GenTreePtr tree)
+{
+ // TODO-XArch-CQ: Make this more accurate.
+ // For now, We assume that most binary operators are of the RMW form.
+ assert(tree->OperIsBinary());
+
+ if (tree->OperIsCompare() || tree->OperIs(GT_CMP))
+ {
+ return false;
+ }
+
+ switch (tree->OperGet())
+ {
+ // These Opers either support a three op form (i.e. GT_LEA), or do not read/write their first operand
+ case GT_LEA:
+ case GT_STOREIND:
+ case GT_ARR_INDEX:
+ case GT_STORE_BLK:
+ case GT_STORE_OBJ:
+ return false;
+
+ // x86/x64 does support a three op multiply when op2|op1 is a contained immediate
+ case GT_MUL:
+ return (!tree->gtOp.gtOp2->isContainedIntOrIImmed() && !tree->gtOp.gtOp1->isContainedIntOrIImmed());
+
+ default:
+ return true;
+ }
+}
+
//------------------------------------------------------------------------
// TreeNodeInfoInitSimple: Sets the srcCount for all the trees
// without special handling based on the tree node type.
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitSimple(GenTree* tree)
+void LinearScan::TreeNodeInfoInitSimple(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
if (tree->isContained())
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitReturn(GenTree* tree)
+void LinearScan::TreeNodeInfoInitReturn(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
GenTree* op1 = tree->gtGetOp1();
#if !defined(_TARGET_64BIT_)
GenTree* loVal = op1->gtGetOp1();
GenTree* hiVal = op1->gtGetOp2();
info->srcCount = 2;
- loVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_LO);
- hiVal->gtLsraInfo.setSrcCandidates(l, RBM_LNGRET_HI);
+ loVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_LO);
+ hiVal->gtLsraInfo.setSrcCandidates(this, RBM_LNGRET_HI);
assert(info->dstCount == 0);
}
else
if (useCandidates != RBM_NONE)
{
- op1->gtLsraInfo.setSrcCandidates(l, useCandidates);
+ op1->gtLsraInfo.setSrcCandidates(this, useCandidates);
}
}
}
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitShiftRotate(GenTree* tree)
+void LinearScan::TreeNodeInfoInitShiftRotate(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
// For shift operations, we need that the number
// of bits moved gets stored in CL in case
// We will allow whatever can be encoded - hope you know what you are doing.
if (!shiftBy->isContained())
{
- source->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~RBM_RCX);
- shiftBy->gtLsraInfo.setSrcCandidates(l, RBM_RCX);
- info->setDstCandidates(l, l->allRegs(TYP_INT) & ~RBM_RCX);
+ source->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~RBM_RCX);
+ shiftBy->gtLsraInfo.setSrcCandidates(this, RBM_RCX);
+ info->setDstCandidates(this, allRegs(TYP_INT) & ~RBM_RCX);
if (!tree->isContained())
{
info->srcCount = 2;
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitPutArgReg(GenTreeUnOp* node)
+void LinearScan::TreeNodeInfoInitPutArgReg(GenTreeUnOp* node)
{
assert(node != nullptr);
assert(node->OperIsPutArgReg());
// Set the register requirements for the node.
const regMaskTP argMask = genRegMask(argReg);
- node->gtLsraInfo.setDstCandidates(m_lsra, argMask);
- node->gtLsraInfo.setSrcCandidates(m_lsra, argMask);
+ node->gtLsraInfo.setDstCandidates(this, argMask);
+ node->gtLsraInfo.setSrcCandidates(this, argMask);
// To avoid redundant moves, have the argument operand computed in the
// register in which the argument is passed to the call.
- node->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(m_lsra, m_lsra->getUseCandidates(node));
+ node->gtOp.gtOp1->gtLsraInfo.setSrcCandidates(this, getUseCandidates(node));
}
//------------------------------------------------------------------------
// Since the integer register is not associated with the arg node, we will reserve it as
// an internal register on the call so that it is not used during the evaluation of the call node
// (e.g. for the target).
-void Lowering::HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs)
+void LinearScan::HandleFloatVarArgs(GenTreeCall* call, GenTree* argNode, bool* callHasFloatRegArgs)
{
#if FEATURE_VARARG
if (call->IsVarargs() && varTypeIsFloating(argNode))
*callHasFloatRegArgs = true;
regNumber argReg = argNode->gtRegNum;
- regNumber targetReg = comp->getCallArgIntRegister(argReg);
+ regNumber targetReg = compiler->getCallArgIntRegister(argReg);
call->gtLsraInfo.setInternalIntCount(call->gtLsraInfo.internalIntCount + 1);
- call->gtLsraInfo.addInternalCandidates(m_lsra, genRegMask(targetReg));
+ call->gtLsraInfo.addInternalCandidates(this, genRegMask(targetReg));
}
#endif // FEATURE_VARARG
}
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCall(GenTreeCall* call)
+void LinearScan::TreeNodeInfoInitCall(GenTreeCall* call)
{
TreeNodeInfo* info = &(call->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
bool hasMultiRegRetVal = false;
ReturnTypeDesc* retTypeDesc = nullptr;
{
// Fast tail call - make sure that call target is always computed in RAX
// so that epilog sequence can generate "jmp rax" to achieve fast tail call.
- ctrlExpr->gtLsraInfo.setSrcCandidates(l, RBM_RAX);
+ ctrlExpr->gtLsraInfo.setSrcCandidates(this, RBM_RAX);
}
}
#ifdef _TARGET_X86_
if (call->IsVirtualStub() && (call->gtCallType == CT_INDIRECT))
{
assert(ctrlExpr->isIndir() && ctrlExpr->isContained());
- ctrlExpr->gtGetOp1()->gtLsraInfo.setSrcCandidates(l, RBM_VIRTUAL_STUB_TARGET);
+ ctrlExpr->gtGetOp1()->gtLsraInfo.setSrcCandidates(this, RBM_VIRTUAL_STUB_TARGET);
}
}
#endif // _TARGET_X86_
// the individual specific registers will have no effect.
if (call->IsVarargs())
{
- info->setInternalCandidates(l, RBM_NONE);
+ info->setInternalCandidates(this, RBM_NONE);
}
RegisterType registerType = call->TypeGet();
// The x86 CORINFO_HELP_INIT_PINVOKE_FRAME helper uses a custom calling convention that returns with
// TCB in REG_PINVOKE_TCB. AMD64/ARM64 use the standard calling convention. fgMorphCall() sets the
// correct argument registers.
- info->setDstCandidates(l, RBM_PINVOKE_TCB);
+ info->setDstCandidates(this, RBM_PINVOKE_TCB);
}
else
#endif // _TARGET_X86_
if (hasMultiRegRetVal)
{
assert(retTypeDesc != nullptr);
- info->setDstCandidates(l, retTypeDesc->GetABIReturnRegs());
+ info->setDstCandidates(this, retTypeDesc->GetABIReturnRegs());
}
else if (varTypeIsFloating(registerType))
{
#ifdef _TARGET_X86_
// The return value will be on the X87 stack, and we will need to move it.
- info->setDstCandidates(l, l->allRegs(registerType));
+ info->setDstCandidates(this, allRegs(registerType));
#else // !_TARGET_X86_
- info->setDstCandidates(l, RBM_FLOATRET);
+ info->setDstCandidates(this, RBM_FLOATRET);
#endif // !_TARGET_X86_
}
else if (registerType == TYP_LONG)
{
- info->setDstCandidates(l, RBM_LNGRET);
+ info->setDstCandidates(this, RBM_LNGRET);
}
else
{
- info->setDstCandidates(l, RBM_INTRET);
+ info->setDstCandidates(this, RBM_INTRET);
}
// number of args to a call =
// - a field list
// - a put arg
//
- // Note that this property is statically checked by Lowering::CheckBlock.
+ // Note that this property is statically checked by LinearScan::CheckBlock.
GenTreePtr argNode = list->Current();
// Each register argument corresponds to one source.
// Don't assign the call target to any of the argument registers because
// we will use them to also pass floating point arguments as required
// by Amd64 ABI.
- ctrlExpr->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~(RBM_ARG_REGS));
+ ctrlExpr->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~(RBM_ARG_REGS));
}
#endif // !FEATURE_VARARG
}
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
+void LinearScan::TreeNodeInfoInitBlockStore(GenTreeBlk* blkNode)
{
GenTree* dstAddr = blkNode->Addr();
unsigned size = blkNode->gtBlkSize;
GenTree* source = blkNode->Data();
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
// Sources are dest address, initVal or source.
// We may require an additional source or temp register for the size.
blkNode->gtLsraInfo.srcCount = GetOperandSourceCount(dstAddr);
assert(blkNode->gtLsraInfo.dstCount == 0);
- blkNode->gtLsraInfo.setInternalCandidates(l, RBM_NONE);
+ blkNode->gtLsraInfo.setInternalCandidates(this, RBM_NONE);
GenTreePtr srcAddrOrFill = nullptr;
bool isInitBlk = blkNode->OperIsInitBlkOp();
{
// Reserve an XMM register to fill it with a pack of 16 init value constants.
blkNode->gtLsraInfo.internalFloatCount = 1;
- blkNode->gtLsraInfo.setInternalCandidates(l, l->internalFloatRegCandidates());
+ blkNode->gtLsraInfo.setInternalCandidates(this, internalFloatRegCandidates());
// use XMM register to fill with constants, it's AVX instruction and set the flag
SetContainsAVXFlags();
}
if ((size & (XMM_REGSIZE_BYTES - 1)) != 0)
{
blkNode->gtLsraInfo.internalIntCount++;
- regMaskTP regMask = l->allRegs(TYP_INT);
+ regMaskTP regMask = allRegs(TYP_INT);
#ifdef _TARGET_X86_
if ((size & 1) != 0)
regMask &= ~RBM_NON_BYTE_REGS;
}
#endif
- blkNode->gtLsraInfo.setInternalCandidates(l, regMask);
+ blkNode->gtLsraInfo.setInternalCandidates(this, regMask);
}
if (size >= XMM_REGSIZE_BYTES)
// reserve an XMM register to use it for a
// series of 16-byte loads and stores.
blkNode->gtLsraInfo.internalFloatCount = 1;
- blkNode->gtLsraInfo.addInternalCandidates(l, l->internalFloatRegCandidates());
+ blkNode->gtLsraInfo.addInternalCandidates(this, internalFloatRegCandidates());
// Uses XMM reg for load and store and hence check to see whether AVX instructions
// are used for codegen, set ContainsAVX flag
SetContainsAVXFlags();
if (dstAddrRegMask != RBM_NONE)
{
- dstAddr->gtLsraInfo.setSrcCandidates(l, dstAddrRegMask);
+ dstAddr->gtLsraInfo.setSrcCandidates(this, dstAddrRegMask);
}
if (sourceRegMask != RBM_NONE)
{
if (srcAddrOrFill != nullptr)
{
- srcAddrOrFill->gtLsraInfo.setSrcCandidates(l, sourceRegMask);
+ srcAddrOrFill->gtLsraInfo.setSrcCandidates(this, sourceRegMask);
}
else
{
// This is a local source; we'll use a temp register for its address.
- blkNode->gtLsraInfo.addInternalCandidates(l, sourceRegMask);
+ blkNode->gtLsraInfo.addInternalCandidates(this, sourceRegMask);
blkNode->gtLsraInfo.internalIntCount++;
}
}
if (size != 0)
{
// Reserve a temp register for the block size argument.
- blkNode->gtLsraInfo.addInternalCandidates(l, blkSizeRegMask);
+ blkNode->gtLsraInfo.addInternalCandidates(this, blkSizeRegMask);
blkNode->gtLsraInfo.internalIntCount++;
}
else
assert(blkNode->gtOper == GT_STORE_DYN_BLK);
blkNode->gtLsraInfo.setSrcCount(3);
GenTree* blockSize = blkNode->AsDynBlk()->gtDynamicSize;
- blockSize->gtLsraInfo.setSrcCandidates(l, blkSizeRegMask);
+ blockSize->gtLsraInfo.setSrcCandidates(this, blkSizeRegMask);
}
}
}
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
+void LinearScan::TreeNodeInfoInitPutArgStk(GenTreePutArgStk* putArgStk)
{
TreeNodeInfo* info = &(putArgStk->gtLsraInfo);
- LinearScan* l = m_lsra;
info->srcCount = 0;
assert(info->dstCount == 0);
// If any of the fields cannot be stored with an actual push, we may need a temporary
// register to load the value before storing it to the stack location.
info->internalIntCount = 1;
- regMaskTP regMask = l->allRegs(TYP_INT);
+ regMaskTP regMask = allRegs(TYP_INT);
if (needsByteTemp)
{
regMask &= ~RBM_NON_BYTE_REGS;
}
- info->setInternalCandidates(l, regMask);
+ info->setInternalCandidates(this, regMask);
}
#if defined(FEATURE_SIMD)
info->srcCount = putArgStk->gtOp1->gtLsraInfo.dstCount;
assert(info->dstCount == 0);
info->internalFloatCount += 1;
- info->addInternalCandidates(l, l->allSIMDRegs());
+ info->addInternalCandidates(this, allSIMDRegs());
}
#endif // defined(FEATURE_SIMD)
{
info->srcCount = putArgStk->gtOp1->gtLsraInfo.dstCount;
info->internalFloatCount = 1;
- info->setInternalCandidates(l, l->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
return;
}
#endif // defined(FEATURE_SIMD) && defined(_TARGET_X86_)
if ((putArgStk->gtNumberReferenceSlots == 0) && (size & (XMM_REGSIZE_BYTES - 1)) != 0)
{
info->internalIntCount++;
- regMaskTP regMask = l->allRegs(TYP_INT);
+ regMaskTP regMask = allRegs(TYP_INT);
#ifdef _TARGET_X86_
if ((size % 2) != 0)
regMask &= ~RBM_NON_BYTE_REGS;
}
#endif
- info->setInternalCandidates(l, regMask);
+ info->setInternalCandidates(this, regMask);
}
#ifdef _TARGET_X86_
// or larger than or equal to 8 bytes on x86, reserve an XMM register to use it for a
// series of 16-byte loads and stores.
info->internalFloatCount = 1;
- info->addInternalCandidates(l, l->internalFloatRegCandidates());
+ info->addInternalCandidates(this, internalFloatRegCandidates());
SetContainsAVXFlags();
}
break;
case GenTreePutArgStk::Kind::RepInstr:
info->internalIntCount += 3;
- info->setInternalCandidates(l, (RBM_RDI | RBM_RCX | RBM_RSI));
+ info->setInternalCandidates(this, (RBM_RDI | RBM_RCX | RBM_RSI));
break;
default:
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitLclHeap(GenTree* tree)
+void LinearScan::TreeNodeInfoInitLclHeap(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
- Compiler* compiler = comp;
info->srcCount = 1;
assert(info->dstCount == 1);
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitModDiv(GenTree* tree)
+void LinearScan::TreeNodeInfoInitModDiv(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
GenTree* op1 = tree->gtGetOp1();
GenTree* op2 = tree->gtGetOp2();
{
// We are interested in just the remainder.
// RAX is used as a trashable register during computation of remainder.
- info->setDstCandidates(l, RBM_RDX);
+ info->setDstCandidates(this, RBM_RDX);
}
else
{
// We are interested in just the quotient.
// RDX gets used as trashable register during computation of quotient
- info->setDstCandidates(l, RBM_RAX);
+ info->setDstCandidates(this, RBM_RAX);
}
#ifdef _TARGET_X86_
// This situation also requires an internal register.
info->internalIntCount = 1;
- info->setInternalCandidates(l, l->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allRegs(TYP_INT));
- loVal->gtLsraInfo.setSrcCandidates(l, RBM_EAX);
- hiVal->gtLsraInfo.setSrcCandidates(l, RBM_EDX);
+ loVal->gtLsraInfo.setSrcCandidates(this, RBM_EAX);
+ hiVal->gtLsraInfo.setSrcCandidates(this, RBM_EDX);
}
else
#endif
{
// If possible would like to have op1 in RAX to avoid a register move
- op1->gtLsraInfo.setSrcCandidates(l, RBM_RAX);
+ op1->gtLsraInfo.setSrcCandidates(this, RBM_RAX);
}
if (!op2->isContained())
{
- op2->gtLsraInfo.setSrcCandidates(l, l->allRegs(TYP_INT) & ~(RBM_RAX | RBM_RDX));
+ op2->gtLsraInfo.setSrcCandidates(this, allRegs(TYP_INT) & ~(RBM_RAX | RBM_RDX));
}
}
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitIntrinsic(GenTree* tree)
+void LinearScan::TreeNodeInfoInitIntrinsic(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
- LinearScan* l = m_lsra;
// Both operand and its result must be of floating point type.
GenTree* op1 = tree->gtGetOp1();
if (tree->gtIntrinsic.gtIntrinsicId == CORINFO_INTRINSIC_Abs)
{
info->internalFloatCount = 1;
- info->setInternalCandidates(l, l->internalFloatRegCandidates());
+ info->setInternalCandidates(this, internalFloatRegCandidates());
}
break;
// Return Value:
// None.
-void Lowering::TreeNodeInfoInitSIMD(GenTreeSIMD* simdTree)
+void LinearScan::TreeNodeInfoInitSIMD(GenTreeSIMD* simdTree)
{
TreeNodeInfo* info = &(simdTree->gtLsraInfo);
- LinearScan* lsra = m_lsra;
+
// Only SIMDIntrinsicInit can be contained. Other than that,
// only SIMDIntrinsicOpEquality and SIMDIntrinsicOpInEquality can have 0 dstCount.
if (simdTree->isContained())
{
// need a temp
info->internalFloatCount = 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
info->isInternalRegDelayFree = true;
info->srcCount = 2;
}
// Need an internal register to stitch together all the values into a single vector in a SIMD reg.
info->internalFloatCount = 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
break;
// Must be a Vector<int> or Vector<short> Vector<sbyte>
assert(simdTree->gtSIMDBaseType == TYP_INT || simdTree->gtSIMDBaseType == TYP_SHORT ||
simdTree->gtSIMDBaseType == TYP_BYTE);
- assert(comp->getSIMDInstructionSet() >= InstructionSet_SSE3_4);
+ assert(compiler->getSIMDInstructionSet() >= InstructionSet_SSE3_4);
info->srcCount = 1;
break;
// SSE2 32-bit integer multiplication requires two temp regs
if (simdTree->gtSIMDIntrinsicID == SIMDIntrinsicMul && simdTree->gtSIMDBaseType == TYP_INT &&
- comp->getSIMDInstructionSet() == InstructionSet_SSE2)
+ compiler->getSIMDInstructionSet() == InstructionSet_SSE2)
{
info->internalFloatCount = 2;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
break;
// registers reserved are guaranteed to be different from target
// integer register without explicitly specifying.
info->internalFloatCount = 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
if (info->isNoRegCompare)
{
// A target reg is not needed on AVX when comparing against Vector Zero.
// In all other cases we need to reserve an int type internal register if we
// don't have a target register on the compare.
- if (!comp->canUseAVX() || !simdTree->gtGetOp2()->IsIntegralConstVector(0))
+ if (!compiler->canUseAVX() || !simdTree->gtGetOp2()->IsIntegralConstVector(0))
{
info->internalIntCount = 1;
- info->addInternalCandidates(lsra, lsra->allRegs(TYP_INT));
+ info->addInternalCandidates(this, allRegs(TYP_INT));
}
}
break;
// and the need for scratch registers.
if (varTypeIsFloating(simdTree->gtSIMDBaseType))
{
- if ((comp->getSIMDInstructionSet() == InstructionSet_SSE2) ||
+ if ((compiler->getSIMDInstructionSet() == InstructionSet_SSE2) ||
(simdTree->gtOp.gtOp1->TypeGet() == TYP_SIMD32))
{
info->internalFloatCount = 1;
info->isInternalRegDelayFree = true;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
// else don't need scratch reg(s).
}
else
{
- assert(simdTree->gtSIMDBaseType == TYP_INT && comp->getSIMDInstructionSet() >= InstructionSet_SSE3_4);
+ assert(simdTree->gtSIMDBaseType == TYP_INT && compiler->getSIMDInstructionSet() >= InstructionSet_SSE3_4);
// No need to set isInternalRegDelayFree since targetReg is a
// an int type reg and guaranteed to be different from xmm/ymm
// regs.
- info->internalFloatCount = comp->canUseAVX() ? 2 : 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->internalFloatCount = compiler->canUseAVX() ? 2 : 1;
+ info->setInternalCandidates(this, allSIMDRegs());
}
info->srcCount = 2;
break;
info->srcCount++;
if (!op2->IsCnsIntOrI())
{
- (void)comp->getSIMDInitTempVarNum();
+ (void)compiler->getSIMDInitTempVarNum();
}
else if (!varTypeIsFloating(simdTree->gtSIMDBaseType))
{
bool needFloatTemp;
if (varTypeIsSmallInt(simdTree->gtSIMDBaseType) &&
- (comp->getSIMDInstructionSet() == InstructionSet_AVX))
+ (compiler->getSIMDInstructionSet() == InstructionSet_AVX))
{
int byteShiftCnt = (int)op2->AsIntCon()->gtIconVal * genTypeSize(simdTree->gtSIMDBaseType);
needFloatTemp = (byteShiftCnt >= 16);
if (needFloatTemp)
{
info->internalFloatCount = 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
}
}
info->srcCount = 2;
// We need an internal integer register for SSE2 codegen
- if (comp->getSIMDInstructionSet() == InstructionSet_SSE2)
+ if (compiler->getSIMDInstructionSet() == InstructionSet_SSE2)
{
info->internalIntCount = 1;
- info->setInternalCandidates(lsra, lsra->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allRegs(TYP_INT));
}
break;
info->isInternalRegDelayFree = true;
info->internalIntCount = 1;
info->internalFloatCount = 2;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs() | lsra->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allSIMDRegs() | allRegs(TYP_INT));
}
break;
// We need an internal register different from targetReg.
info->isInternalRegDelayFree = true;
info->internalFloatCount = 1;
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
}
break;
info->isInternalRegDelayFree = true;
info->srcCount = 1;
info->internalIntCount = 1;
- if (comp->getSIMDInstructionSet() == InstructionSet_AVX)
+ if (compiler->getSIMDInstructionSet() == InstructionSet_AVX)
{
info->internalFloatCount = 2;
}
{
info->internalFloatCount = 1;
}
- info->setInternalCandidates(lsra, lsra->allSIMDRegs() | lsra->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allSIMDRegs() | allRegs(TYP_INT));
break;
case SIMDIntrinsicConvertToDouble:
}
else
#endif
- if ((comp->getSIMDInstructionSet() == InstructionSet_AVX) || (simdTree->gtSIMDBaseType == TYP_ULONG))
+ if ((compiler->getSIMDInstructionSet() == InstructionSet_AVX) || (simdTree->gtSIMDBaseType == TYP_ULONG))
{
info->internalFloatCount = 2;
}
{
info->internalFloatCount = 1;
}
- info->setInternalCandidates(lsra, lsra->allSIMDRegs() | lsra->allRegs(TYP_INT));
+ info->setInternalCandidates(this, allSIMDRegs() | allRegs(TYP_INT));
break;
case SIMDIntrinsicNarrow:
// We need an internal register different from targetReg.
info->isInternalRegDelayFree = true;
info->srcCount = 2;
- if ((comp->getSIMDInstructionSet() == InstructionSet_AVX) && (simdTree->gtSIMDBaseType != TYP_DOUBLE))
+ if ((compiler->getSIMDInstructionSet() == InstructionSet_AVX) && (simdTree->gtSIMDBaseType != TYP_DOUBLE))
{
info->internalFloatCount = 2;
}
{
info->internalFloatCount = 1;
}
- info->setInternalCandidates(lsra, lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
break;
case SIMDIntrinsicShuffleSSE2:
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCast(GenTree* tree)
+void LinearScan::TreeNodeInfoInitCast(GenTree* tree)
{
TreeNodeInfo* info = &(tree->gtLsraInfo);
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
+void LinearScan::TreeNodeInfoInitGCWriteBarrier(GenTree* tree)
{
assert(tree->OperGet() == GT_STOREIND);
useOptimizedWriteBarrierHelper = true; // On x86, use the optimized write barriers by default.
#ifdef DEBUG
- GCInfo::WriteBarrierForm wbf = comp->codeGen->gcInfo.gcIsWriteBarrierCandidate(tree, src);
+ GCInfo::WriteBarrierForm wbf = compiler->codeGen->gcInfo.gcIsWriteBarrierCandidate(tree, src);
if (wbf == GCInfo::WBF_NoBarrier_CheckNotHeapInDebug) // This one is always a call to a C++ method.
{
useOptimizedWriteBarrierHelper = false;
// Special write barrier:
// op1 (addr) goes into REG_WRITE_BARRIER (rdx) and
// op2 (src) goes into any int register.
- addr->gtLsraInfo.setSrcCandidates(m_lsra, RBM_WRITE_BARRIER);
- src->gtLsraInfo.setSrcCandidates(m_lsra, RBM_WRITE_BARRIER_SRC);
+ addr->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER);
+ src->gtLsraInfo.setSrcCandidates(this, RBM_WRITE_BARRIER_SRC);
}
#else // !defined(_TARGET_X86_)
// For the standard JIT Helper calls:
// op1 (addr) goes into REG_ARG_0 and
// op2 (src) goes into REG_ARG_1
- addr->gtLsraInfo.setSrcCandidates(m_lsra, RBM_ARG_0);
- src->gtLsraInfo.setSrcCandidates(m_lsra, RBM_ARG_1);
+ addr->gtLsraInfo.setSrcCandidates(this, RBM_ARG_0);
+ src->gtLsraInfo.setSrcCandidates(this, RBM_ARG_1);
}
// Both src and dst must reside in a register, which they should since we haven't set
// Arguments:
// indirTree - GT_IND or GT_STOREIND gentree node
//
-void Lowering::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
+void LinearScan::TreeNodeInfoInitIndir(GenTreeIndir* indirTree)
{
// If this is the rhs of a block copy (i.e. non-enregisterable struct),
// it has no register requirements.
if (varTypeIsByte(indirTree) && !nonMemSource->isContained())
{
// If storeInd is of TYP_BYTE, set source to byteable registers.
- regMaskTP regMask = nonMemSource->gtLsraInfo.getSrcCandidates(m_lsra);
+ regMaskTP regMask = nonMemSource->gtLsraInfo.getSrcCandidates(this);
regMask &= ~RBM_NON_BYTE_REGS;
assert(regMask != RBM_NONE);
- nonMemSource->gtLsraInfo.setSrcCandidates(m_lsra, regMask);
+ nonMemSource->gtLsraInfo.setSrcCandidates(this, regMask);
}
#endif
}
if (varTypeIsByte(indirTree) && !source->isContained())
{
// If storeInd is of TYP_BYTE, set source to byteable registers.
- regMaskTP regMask = source->gtLsraInfo.getSrcCandidates(m_lsra);
+ regMaskTP regMask = source->gtLsraInfo.getSrcCandidates(this);
regMask &= ~RBM_NON_BYTE_REGS;
assert(regMask != RBM_NONE);
- source->gtLsraInfo.setSrcCandidates(m_lsra, regMask);
+ source->gtLsraInfo.setSrcCandidates(this, regMask);
}
#endif
}
info->isInternalRegDelayFree = true;
}
- info->setInternalCandidates(m_lsra, m_lsra->allSIMDRegs());
+ info->setInternalCandidates(this, allSIMDRegs());
return;
}
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitCmp(GenTreePtr tree)
+void LinearScan::TreeNodeInfoInitCmp(GenTreePtr tree)
{
assert(tree->OperIsCompare() || tree->OperIs(GT_CMP));
// We always set the dst candidates, though, because if this is compare is consumed by a jump, they
// won't be used. We might be able to use GTF_RELOP_JMP_USED to determine this case, but it's not clear
// that flag is maintained until this location (especially for decomposed long compares).
- info->setDstCandidates(m_lsra, RBM_BYTE_REGS);
+ info->setDstCandidates(this, RBM_BYTE_REGS);
#endif // _TARGET_X86_
GenTreePtr op1 = tree->gtOp.gtOp1;
// Return Value:
// None.
//
-void Lowering::TreeNodeInfoInitMul(GenTreePtr tree)
+void LinearScan::TreeNodeInfoInitMul(GenTreePtr tree)
{
#if defined(_TARGET_X86_)
assert(tree->OperIs(GT_MUL, GT_MULHI, GT_MUL_LONG));
// Here we set RAX as the only destination candidate
// In LSRA we set the kill set for this operation to RBM_RAX|RBM_RDX
//
- info->setDstCandidates(m_lsra, RBM_RAX);
+ info->setDstCandidates(this, RBM_RAX);
}
else if (tree->OperGet() == GT_MULHI)
{
// Have to use the encoding:RDX:RAX = RAX * rm. Since we only care about the
// upper 32 bits of the result set the destination candidate to REG_RDX.
- info->setDstCandidates(m_lsra, RBM_RDX);
+ info->setDstCandidates(this, RBM_RDX);
}
#if defined(_TARGET_X86_)
else if (tree->OperGet() == GT_MUL_LONG)
{
// have to use the encoding:RDX:RAX = RAX * rm
- info->setDstCandidates(m_lsra, RBM_RAX);
+ info->setDstCandidates(this, RBM_RAX);
}
#endif
GenTree* containedMemOp = nullptr;
// isFloatingPointType - true if it is floating point type
// sizeOfSIMDVector - SIMD Vector size
//
-void Lowering::SetContainsAVXFlags(bool isFloatingPointType /* = true */, unsigned sizeOfSIMDVector /* = 0*/)
+void LinearScan::SetContainsAVXFlags(bool isFloatingPointType /* = true */, unsigned sizeOfSIMDVector /* = 0*/)
{
#ifdef FEATURE_AVX_SUPPORT
if (isFloatingPointType)
{
- if (comp->getFloatingPointInstructionSet() == InstructionSet_AVX)
+ if (compiler->getFloatingPointInstructionSet() == InstructionSet_AVX)
{
- comp->getEmitter()->SetContainsAVX(true);
+ compiler->getEmitter()->SetContainsAVX(true);
}
- if (sizeOfSIMDVector == 32 && comp->getSIMDInstructionSet() == InstructionSet_AVX)
+ if (sizeOfSIMDVector == 32 && compiler->getSIMDInstructionSet() == InstructionSet_AVX)
{
- comp->getEmitter()->SetContains256bitAVX(true);
+ compiler->getEmitter()->SetContains256bitAVX(true);
}
}
#endif
// Return Value:
// If we need to exclude non-byteable registers
//
-bool Lowering::ExcludeNonByteableRegisters(GenTree* tree)
+bool LinearScan::ExcludeNonByteableRegisters(GenTree* tree)
{
// Example1: GT_STOREIND(byte, addr, op2) - storeind of byte sized value from op2 into mem 'addr'
// Storeind itself will not produce any value and hence dstCount=0. But op2 could be TYP_INT
GenTree* op1 = simdNode->gtGetOp1();
GenTree* op2 = simdNode->gtGetOp2();
var_types baseType = simdNode->gtSIMDBaseType;
- if (!IsContainableMemoryOp(op1) && op2->IsCnsIntOrI() && varTypeIsSmallInt(baseType))
+ if (!isContainableMemoryOp(op1) && op2->IsCnsIntOrI() && varTypeIsSmallInt(baseType))
{
bool ZeroOrSignExtnReqd = true;
unsigned baseSize = genTypeSize(baseType);
// Return Value:
// The number of source registers used by the *parent* of this node.
//
-int Lowering::GetOperandSourceCount(GenTree* node)
+int LinearScan::GetOperandSourceCount(GenTree* node)
{
if (!node->isContained())
{