dumpTerse = (JitConfig.JitDumpTerseLsra() != 0);
#endif // DEBUG
- availableIntRegs = (RBM_ALLINT & ~compiler->codeGen->regSet.rsMaskResvd);
+ enregisterLocalVars = ((compiler->opts.compFlags & CLFLG_REGVAR) != 0) && compiler->lvaTrackedCount > 0;
+ availableIntRegs = (RBM_ALLINT & ~compiler->codeGen->regSet.rsMaskResvd);
#if ETW_EBP_FRAMED
availableIntRegs &= ~RBM_FPBASE;
void LinearScan::recordVarLocationsAtStartOfBB(BasicBlock* bb)
{
+ if (!enregisterLocalVars)
+ {
+ return;
+ }
JITDUMP("Recording Var Locations at start of BB%02u\n", bb->bbNum);
VarToRegMap map = getInVarToRegMap(bb->bbNum);
unsigned count = 0;
- VARSET_ITER_INIT(compiler, iter, bb->bbLiveIn, varIndex);
+ VarSetOps::AssignNoCopy(compiler, currentLiveVars,
+ VarSetOps::Intersection(compiler, registerCandidateVars, bb->bbLiveIn));
+ VARSET_ITER_INIT(compiler, iter, currentLiveVars, varIndex);
while (iter.NextElem(&varIndex))
{
unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
bool LinearScan::isRegCandidate(LclVarDsc* varDsc)
{
- // Check to see if opt settings permit register variables
- if ((compiler->opts.compFlags & CLFLG_REGVAR) == 0)
+ // We shouldn't be called if opt settings do not permit register variables.
+ assert((compiler->opts.compFlags & CLFLG_REGVAR) != 0);
+
+ if (!varDsc->lvTracked)
{
return false;
}
- // If we have JMP, reg args must be put on the stack
-
- if (compiler->compJmpOpUsed && varDsc->lvIsRegArg)
+#if !defined(_TARGET_64BIT_)
+ if (varDsc->lvType == TYP_LONG)
{
+ // Long variables should not be register candidates.
+ // Lowering will have split any candidate lclVars into lo/hi vars.
return false;
}
+#endif // !defined(_TARGET_64BIT)
- if (!varDsc->lvTracked)
+ // If we have JMP, reg args must be put on the stack
+
+ if (compiler->compJmpOpUsed && varDsc->lvIsRegArg)
{
return false;
}
void LinearScan::identifyCandidates()
{
+ if (enregisterLocalVars)
+ {
+ // Initialize the set of lclVars that are candidates for register allocation.
+ VarSetOps::AssignNoCopy(compiler, registerCandidateVars, VarSetOps::MakeEmpty(compiler));
- // Initialize the sets of lclVars that are used to determine whether, and for which lclVars,
- // we need to perform resolution across basic blocks.
- // Note that we can't do this in the constructor because the number of tracked lclVars may
- // change between the constructor and the actual allocation.
- VarSetOps::AssignNoCopy(compiler, resolutionCandidateVars, VarSetOps::MakeEmpty(compiler));
- VarSetOps::AssignNoCopy(compiler, splitOrSpilledVars, VarSetOps::MakeEmpty(compiler));
+ // Initialize the sets of lclVars that are used to determine whether, and for which lclVars,
+ // we need to perform resolution across basic blocks.
+ // Note that we can't do this in the constructor because the number of tracked lclVars may
+ // change between the constructor and the actual allocation.
+ VarSetOps::AssignNoCopy(compiler, resolutionCandidateVars, VarSetOps::MakeEmpty(compiler));
+ VarSetOps::AssignNoCopy(compiler, splitOrSpilledVars, VarSetOps::MakeEmpty(compiler));
- if (compiler->lvaCount == 0)
+ // We set enregisterLocalVars to true only if there are tracked lclVars
+ assert(compiler->lvaCount != 0);
+ }
+ else if (compiler->lvaCount == 0)
{
+ // Nothing to do. Note that even if enregisterLocalVars is false, we still need to set the
+ // lvLRACandidate field on all the lclVars to false if we have any.
return;
}
// This is defined as thresholdLargeVectorRefCntWtd, as we are likely to use the same mechanism
// for vectors on Arm64, though the actual value may differ.
- VarSetOps::AssignNoCopy(compiler, fpCalleeSaveCandidateVars, VarSetOps::MakeEmpty(compiler));
- VARSET_TP fpMaybeCandidateVars(VarSetOps::MakeEmpty(compiler));
unsigned int floatVarCount = 0;
unsigned int thresholdFPRefCntWtd = 4 * BB_UNITY_WEIGHT;
unsigned int maybeFPRefCntWtd = 2 * BB_UNITY_WEIGHT;
+ VARSET_TP fpMaybeCandidateVars(VarSetOps::UninitVal());
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
- VarSetOps::AssignNoCopy(compiler, largeVectorVars, VarSetOps::MakeEmpty(compiler));
- VarSetOps::AssignNoCopy(compiler, largeVectorCalleeSaveCandidateVars, VarSetOps::MakeEmpty(compiler));
unsigned int largeVectorVarCount = 0;
unsigned int thresholdLargeVectorRefCntWtd = 4 * BB_UNITY_WEIGHT;
#endif // FEATURE_PARTIAL_SIMD_CALLEE_SAVE
+ if (enregisterLocalVars)
+ {
+ VarSetOps::AssignNoCopy(compiler, fpCalleeSaveCandidateVars, VarSetOps::MakeEmpty(compiler));
+ VarSetOps::AssignNoCopy(compiler, fpMaybeCandidateVars, VarSetOps::MakeEmpty(compiler));
+#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
+ VarSetOps::AssignNoCopy(compiler, largeVectorVars, VarSetOps::MakeEmpty(compiler));
+ VarSetOps::AssignNoCopy(compiler, largeVectorCalleeSaveCandidateVars, VarSetOps::MakeEmpty(compiler));
+#endif // FEATURE_PARTIAL_SIMD_CALLEE_SAVE
+ }
#if DOUBLE_ALIGN
unsigned refCntStk = 0;
unsigned refCntReg = 0;
}
#endif // DOUBLE_ALIGN
- // initialize mapping from tracked local to interval
- if (compiler->lvaTrackedCount > 0)
+ // Check whether register variables are permitted.
+ if (!enregisterLocalVars)
+ {
+ localVarIntervals = nullptr;
+ }
+ else if (compiler->lvaTrackedCount > 0)
{
+ // initialize mapping from tracked local to interval
localVarIntervals = new (compiler, CMK_LSRA) Interval*[compiler->lvaTrackedCount];
}
+ INTRACK_STATS(regCandidateVarCount = 0);
for (lclNum = 0, varDsc = compiler->lvaTable; lclNum < compiler->lvaCount; lclNum++, varDsc++)
{
- Interval* newInt = nullptr; // Initialize to prevent VC++ "C4701: potentially uninitialized" warning.
+ // Initialize all variables to REG_STK
+ varDsc->lvRegNum = REG_STK;
+#ifndef _TARGET_64BIT_
+ varDsc->lvOtherReg = REG_STK;
+#endif // _TARGET_64BIT_
- if (varDsc->lvTracked)
+ if (!enregisterLocalVars)
{
- // Create an interval for all tracked variables, even if the tracked variable
- // is not a register candidate. This is probably just done for consistency
- // (e.g., code that simply walks over all tracked variables don't need to check if
- // they are register candidates, or if the interval exists). It might be possible to
- // only allocate intervals for register candidate tracked variables, if all the code
- // that depends on every tracked variable having a non-null Interval was fixed.
-
- newInt = newInterval((var_types)varDsc->lvType);
- newInt->setLocalNumber(compiler, lclNum, this);
+ varDsc->lvLRACandidate = false;
+ continue;
}
#if DOUBLE_ALIGN
}
#endif // DOUBLE_ALIGN
- // Initialize all variables to REG_STK
- varDsc->lvRegNum = REG_STK;
-#ifndef _TARGET_64BIT_
- varDsc->lvOtherReg = REG_STK;
-#endif // _TARGET_64BIT_
-
-#if !defined(_TARGET_64BIT_)
- if (varDsc->lvType == TYP_LONG)
- {
- // Long variables should not be register candidates.
- // Lowering will have split any candidate lclVars into lo/hi vars.
- varDsc->lvLRACandidate = 0;
- continue;
- }
-#endif // !defined(_TARGET_64BIT)
-
/* Track all locals that can be enregistered */
if (!isRegCandidate(varDsc))
{
varDsc->lvLRACandidate = 0;
+ if (varDsc->lvTracked)
+ {
+ localVarIntervals[varDsc->lvVarIndex] = nullptr;
+ }
continue;
}
varDsc->lvLRACandidate = 1;
- if (varDsc->lvIsStructField)
- {
- newInt->isStructField = true;
- }
-
// Start with lvRegister as false - set it true only if the variable gets
// the same register assignment throughout
varDsc->lvRegister = false;
if (compiler->opts.MinOpts() && compiler->compHndBBtabCount > 0)
{
compiler->lvaSetVarDoNotEnregister(lclNum DEBUGARG(Compiler::DNER_LiveInOutOfHandler));
- varDsc->lvLRACandidate = 0;
- continue;
}
if (varDsc->lvDoNotEnregister)
{
- varDsc->lvLRACandidate = 0;
+ varDsc->lvLRACandidate = 0;
+ localVarIntervals[varDsc->lvVarIndex] = nullptr;
continue;
}
varDsc->lvLRACandidate = 0;
}
- // we will set this later when we have determined liveness
if (varDsc->lvLRACandidate)
{
+ Interval* newInt = newInterval(type);
+ newInt->setLocalNumber(compiler, lclNum, this);
+ VarSetOps::AddElemD(compiler, registerCandidateVars, varDsc->lvVarIndex);
+
+ // we will set this later when we have determined liveness
varDsc->lvMustInit = false;
- }
- // We maintain two sets of FP vars - those that meet the first threshold of weighted ref Count,
- // and those that meet the second (see the definitions of thresholdFPRefCntWtd and maybeFPRefCntWtd
- // above).
- CLANG_FORMAT_COMMENT_ANCHOR;
+ if (varDsc->lvIsStructField)
+ {
+ newInt->isStructField = true;
+ }
+
+ INTRACK_STATS(regCandidateVarCount++);
+
+ // We maintain two sets of FP vars - those that meet the first threshold of weighted ref Count,
+ // and those that meet the second (see the definitions of thresholdFPRefCntWtd and maybeFPRefCntWtd
+ // above).
+ CLANG_FORMAT_COMMENT_ANCHOR;
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
- // Additionally, when we are generating AVX on non-UNIX amd64, we keep a separate set of the LargeVectorType
- // vars.
- if (varDsc->lvType == LargeVectorType)
- {
- largeVectorVarCount++;
- VarSetOps::AddElemD(compiler, largeVectorVars, varDsc->lvVarIndex);
- unsigned refCntWtd = varDsc->lvRefCntWtd;
- if (refCntWtd >= thresholdLargeVectorRefCntWtd)
+ // Additionally, when we are generating AVX on non-UNIX amd64, we keep a separate set of the LargeVectorType
+ // vars.
+ if (varDsc->lvType == LargeVectorType)
{
- VarSetOps::AddElemD(compiler, largeVectorCalleeSaveCandidateVars, varDsc->lvVarIndex);
+ largeVectorVarCount++;
+ VarSetOps::AddElemD(compiler, largeVectorVars, varDsc->lvVarIndex);
+ unsigned refCntWtd = varDsc->lvRefCntWtd;
+ if (refCntWtd >= thresholdLargeVectorRefCntWtd)
+ {
+ VarSetOps::AddElemD(compiler, largeVectorCalleeSaveCandidateVars, varDsc->lvVarIndex);
+ }
}
- }
- else
+ else
#endif // FEATURE_PARTIAL_SIMD_CALLEE_SAVE
- if (regType(newInt->registerType) == FloatRegisterType)
- {
- floatVarCount++;
- unsigned refCntWtd = varDsc->lvRefCntWtd;
- if (varDsc->lvIsRegArg)
- {
- // Don't count the initial reference for register params. In those cases,
- // using a callee-save causes an extra copy.
- refCntWtd -= BB_UNITY_WEIGHT;
- }
- if (refCntWtd >= thresholdFPRefCntWtd)
- {
- VarSetOps::AddElemD(compiler, fpCalleeSaveCandidateVars, varDsc->lvVarIndex);
- }
- else if (refCntWtd >= maybeFPRefCntWtd)
+ if (regType(type) == FloatRegisterType)
{
- VarSetOps::AddElemD(compiler, fpMaybeCandidateVars, varDsc->lvVarIndex);
+ floatVarCount++;
+ unsigned refCntWtd = varDsc->lvRefCntWtd;
+ if (varDsc->lvIsRegArg)
+ {
+ // Don't count the initial reference for register params. In those cases,
+ // using a callee-save causes an extra copy.
+ refCntWtd -= BB_UNITY_WEIGHT;
+ }
+ if (refCntWtd >= thresholdFPRefCntWtd)
+ {
+ VarSetOps::AddElemD(compiler, fpCalleeSaveCandidateVars, varDsc->lvVarIndex);
+ }
+ else if (refCntWtd >= maybeFPRefCntWtd)
+ {
+ VarSetOps::AddElemD(compiler, fpMaybeCandidateVars, varDsc->lvVarIndex);
+ }
}
}
+ else
+ {
+ localVarIntervals[varDsc->lvVarIndex] = nullptr;
+ }
}
#if DOUBLE_ALIGN
if (floatVarCount > 6 && compiler->fgHasLoops &&
(compiler->fgReturnBlocks == nullptr || compiler->fgReturnBlocks->next == nullptr))
{
+ assert(enregisterLocalVars);
#ifdef DEBUG
if (VERBOSE)
{
// TODO-Throughput: This mapping can surely be more efficiently done
void LinearScan::initVarRegMaps()
{
+ if (!enregisterLocalVars)
+ {
+ inVarToRegMaps = nullptr;
+ outVarToRegMaps = nullptr;
+ return;
+ }
assert(compiler->lvaTrackedFixed); // We should have already set this to prevent us from adding any new tracked
// variables.
// So, if we want to index by bbNum we have to know the maximum value.
unsigned int bbCount = compiler->fgBBNumMax + 1;
- inVarToRegMaps = new (compiler, CMK_LSRA) regNumber*[bbCount];
- outVarToRegMaps = new (compiler, CMK_LSRA) regNumber*[bbCount];
+ inVarToRegMaps = new (compiler, CMK_LSRA) regNumberSmall*[bbCount];
+ outVarToRegMaps = new (compiler, CMK_LSRA) regNumberSmall*[bbCount];
if (varCount > 0)
{
// This VarToRegMap is used during the resolution of critical edges.
- sharedCriticalVarToRegMap = new (compiler, CMK_LSRA) regNumber[regMapCount];
+ sharedCriticalVarToRegMap = new (compiler, CMK_LSRA) regNumberSmall[regMapCount];
for (unsigned int i = 0; i < bbCount; i++)
{
- regNumber* inVarToRegMap = new (compiler, CMK_LSRA) regNumber[regMapCount];
- regNumber* outVarToRegMap = new (compiler, CMK_LSRA) regNumber[regMapCount];
+ VarToRegMap inVarToRegMap = new (compiler, CMK_LSRA) regNumberSmall[regMapCount];
+ VarToRegMap outVarToRegMap = new (compiler, CMK_LSRA) regNumberSmall[regMapCount];
for (unsigned int j = 0; j < regMapCount; j++)
{
void LinearScan::setInVarRegForBB(unsigned int bbNum, unsigned int varNum, regNumber reg)
{
+ assert(enregisterLocalVars);
assert(reg < UCHAR_MAX && varNum < compiler->lvaCount);
- inVarToRegMaps[bbNum][compiler->lvaTable[varNum].lvVarIndex] = reg;
+ inVarToRegMaps[bbNum][compiler->lvaTable[varNum].lvVarIndex] = (regNumberSmall)reg;
}
void LinearScan::setOutVarRegForBB(unsigned int bbNum, unsigned int varNum, regNumber reg)
{
+ assert(enregisterLocalVars);
assert(reg < UCHAR_MAX && varNum < compiler->lvaCount);
- outVarToRegMaps[bbNum][compiler->lvaTable[varNum].lvVarIndex] = reg;
+ outVarToRegMaps[bbNum][compiler->lvaTable[varNum].lvVarIndex] = (regNumberSmall)reg;
}
LinearScan::SplitEdgeInfo LinearScan::getSplitEdgeInfo(unsigned int bbNum)
{
+ assert(enregisterLocalVars);
SplitEdgeInfo splitEdgeInfo;
assert(bbNum <= compiler->fgBBNumMax);
assert(bbNum > bbNumMaxBeforeResolution);
VarToRegMap LinearScan::getInVarToRegMap(unsigned int bbNum)
{
+ assert(enregisterLocalVars);
assert(bbNum <= compiler->fgBBNumMax);
// For the blocks inserted to split critical edges, the inVarToRegMap is
// equal to the outVarToRegMap at the "from" block.
VarToRegMap LinearScan::getOutVarToRegMap(unsigned int bbNum)
{
+ assert(enregisterLocalVars);
assert(bbNum <= compiler->fgBBNumMax);
// For the blocks inserted to split critical edges, the outVarToRegMap is
// equal to the inVarToRegMap at the target.
return outVarToRegMaps[bbNum];
}
+//------------------------------------------------------------------------
+// setVarReg: Set the register associated with a variable in the given 'bbVarToRegMap'.
+//
+// Arguments:
+// bbVarToRegMap - the map of interest
+// trackedVarIndex - the lvVarIndex for the variable
+// reg - the register to which it is being mapped
+//
+// Return Value:
+// None
+//
+void LinearScan::setVarReg(VarToRegMap bbVarToRegMap, unsigned int trackedVarIndex, regNumber reg)
+{
+ assert(trackedVarIndex < compiler->lvaTrackedCount);
+ regNumberSmall regSmall = (regNumberSmall)reg;
+ assert((regNumber)regSmall == reg);
+ bbVarToRegMap[trackedVarIndex] = regSmall;
+}
+
+//------------------------------------------------------------------------
+// getVarReg: Get the register associated with a variable in the given 'bbVarToRegMap'.
+//
+// Arguments:
+// bbVarToRegMap - the map of interest
+// trackedVarIndex - the lvVarIndex for the variable
+//
+// Return Value:
+// The register to which 'trackedVarIndex' is mapped
+//
regNumber LinearScan::getVarReg(VarToRegMap bbVarToRegMap, unsigned int trackedVarIndex)
{
+ assert(enregisterLocalVars);
assert(trackedVarIndex < compiler->lvaTrackedCount);
- return bbVarToRegMap[trackedVarIndex];
+ return (regNumber)bbVarToRegMap[trackedVarIndex];
}
// Initialize the incoming VarToRegMap to the given map values (generally a predecessor of
// the block)
VarToRegMap LinearScan::setInVarToRegMap(unsigned int bbNum, VarToRegMap srcVarToRegMap)
{
+ assert(enregisterLocalVars);
VarToRegMap inVarToRegMap = inVarToRegMaps[bbNum];
memcpy(inVarToRegMap, srcVarToRegMap, (regMapCount * sizeof(regNumber)));
return inVarToRegMap;
// not in that set, then it's a last use.
VARSET_TP temp(VarSetOps::MakeCopy(compiler, block->bbLiveOut));
+ VarSetOps::IntersectionD(compiler, temp, registerCandidateVars);
bool foundDiff = false;
auto currentRefPosition = refPositions.rbegin();
}
VARSET_TP temp2(VarSetOps::MakeCopy(compiler, block->bbLiveIn));
+ VarSetOps::IntersectionD(compiler, temp2, registerCandidateVars);
VarSetOps::DiffD(compiler, temp2, temp);
VarSetOps::DiffD(compiler, temp, block->bbLiveIn);
// but are not really because the frequent path becomes smaller. Validating these diffs will need
// to be done before making this change.
// if (!blockSequence[curBBSeqNum]->isRunRarely())
+ if (enregisterLocalVars)
{
VARSET_ITER_INIT(compiler, iter, currentLiveVars, varIndex);
VARSET_VALRET_TP
LinearScan::buildUpperVectorSaveRefPositions(GenTree* tree, LsraLocation currentLoc)
{
+ assert(enregisterLocalVars);
VARSET_TP liveLargeVectors(VarSetOps::MakeEmpty(compiler));
regMaskTP fpCalleeKillSet = RBM_NONE;
if (!VarSetOps::IsEmpty(compiler, largeVectorVars))
LsraLocation currentLoc,
VARSET_VALARG_TP liveLargeVectors)
{
+ assert(enregisterLocalVars);
if (!VarSetOps::IsEmpty(compiler, liveLargeVectors))
{
VARSET_ITER_INIT(compiler, iter, liveLargeVectors, varIndex);
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
VARSET_TP liveLargeVectors(VarSetOps::UninitVal());
- if (RBM_FLT_CALLEE_SAVED != RBM_NONE)
+ if (enregisterLocalVars && (RBM_FLT_CALLEE_SAVED != RBM_NONE))
{
// Build RefPositions for saving any live large vectors.
// This must be done after the kills, so that we know which large vectors are still live.
#if FEATURE_PARTIAL_SIMD_CALLEE_SAVE
// SaveDef position must be at the same location as Def position of call node.
- buildUpperVectorRestoreRefPositions(tree, defLocation, liveLargeVectors);
+ if (enregisterLocalVars)
+ {
+ buildUpperVectorRestoreRefPositions(tree, defLocation, liveLargeVectors);
+ }
#endif // FEATURE_PARTIAL_SIMD_CALLEE_SAVE
if (!locationInfoList.IsEmpty())
// insertZeroInitRefPositions: Handle lclVars that are live-in to the first block
//
// Notes:
-// For each lclVar that is live-in to the first block:
+// Prior to calling this method, 'currentLiveVars' must be set to the set of register
+// candidate variables that are liveIn to the first block.
+// For each register candidate that is live-in to the first block:
// - If it is a GC ref, or if compInitMem is set, a ZeroInit RefPosition will be created.
// - Otherwise, it will be marked as spilled, since it will not be assigned a register
// on entry and will be loaded from memory on the undefined path.
//
void LinearScan::insertZeroInitRefPositions()
{
+ assert(enregisterLocalVars);
+#ifdef DEBUG
+ VARSET_TP expectedLiveVars(VarSetOps::Intersection(compiler, registerCandidateVars, compiler->fgFirstBB->bbLiveIn));
+ assert(VarSetOps::Equal(compiler, currentLiveVars, expectedLiveVars));
+#endif // DEBUG
+
// insert defs for this, then a block boundary
- VARSET_ITER_INIT(compiler, iter, compiler->fgFirstBB->bbLiveIn, varIndex);
+ VARSET_ITER_INIT(compiler, iter, currentLiveVars, varIndex);
while (iter.NextElem(&varIndex))
{
unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
bool predBlockIsAllocated = false;
predBlock = findPredBlockForLiveIn(block, prevBlock DEBUGARG(&predBlockIsAllocated));
-
- if (block == compiler->fgFirstBB)
- {
- insertZeroInitRefPositions();
- }
-
- // Any lclVars live-in to a block are resolution candidates.
- VarSetOps::UnionD(compiler, resolutionCandidateVars, block->bbLiveIn);
-
- // Determine if we need any DummyDefs.
- // We need DummyDefs for cases where "predBlock" isn't really a predecessor.
- // Note that it's possible to have uses of unitialized variables, in which case even the first
- // block may require DummyDefs, which we are not currently adding - this means that these variables
- // will always be considered to be in memory on entry (and reloaded when the use is encountered).
- // TODO-CQ: Consider how best to tune this. Currently, if we create DummyDefs for uninitialized
- // variables (which may actually be initialized along the dynamically executed paths, but not
- // on all static paths), we wind up with excessive liveranges for some of these variables.
- VARSET_TP newLiveIn(VarSetOps::MakeCopy(compiler, block->bbLiveIn));
if (predBlock)
{
JITDUMP("\n\nSetting BB%02u as the predecessor for determining incoming variable registers of BB%02u\n",
- block->bbNum, predBlock->bbNum);
+ block->bbNum, predBlock->bbNum);
assert(predBlock->bbNum <= bbNumMaxBeforeResolution);
blockInfo[block->bbNum].predBBNum = predBlock->bbNum;
- // Compute set difference: newLiveIn = block->bbLiveIn - predBlock->bbLiveOut
- VarSetOps::DiffD(compiler, newLiveIn, predBlock->bbLiveOut);
}
- bool needsDummyDefs = (!VarSetOps::IsEmpty(compiler, newLiveIn) && block != compiler->fgFirstBB);
- // Create dummy def RefPositions
-
- if (needsDummyDefs)
+ if (enregisterLocalVars)
{
- // If we are using locations from a predecessor, we should never require DummyDefs.
- assert(!predBlockIsAllocated);
+ VarSetOps::AssignNoCopy(compiler, currentLiveVars,
+ VarSetOps::Intersection(compiler, registerCandidateVars, block->bbLiveIn));
- JITDUMP("Creating dummy definitions\n");
- VARSET_ITER_INIT(compiler, iter, newLiveIn, varIndex);
- while (iter.NextElem(&varIndex))
+ if (block == compiler->fgFirstBB)
{
- unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
- LclVarDsc* varDsc = compiler->lvaTable + varNum;
- // Add a dummyDef for any candidate vars that are in the "newLiveIn" set.
- // If this is the entry block, don't add any incoming parameters (they're handled with ParamDefs).
- if (isCandidateVar(varDsc) && (predBlock != nullptr || !varDsc->lvIsParam))
+ insertZeroInitRefPositions();
+ }
+
+ // Any lclVars live-in to a block are resolution candidates.
+ VarSetOps::UnionD(compiler, resolutionCandidateVars, currentLiveVars);
+
+ // Determine if we need any DummyDefs.
+ // We need DummyDefs for cases where "predBlock" isn't really a predecessor.
+ // Note that it's possible to have uses of unitialized variables, in which case even the first
+ // block may require DummyDefs, which we are not currently adding - this means that these variables
+ // will always be considered to be in memory on entry (and reloaded when the use is encountered).
+ // TODO-CQ: Consider how best to tune this. Currently, if we create DummyDefs for uninitialized
+ // variables (which may actually be initialized along the dynamically executed paths, but not
+ // on all static paths), we wind up with excessive liveranges for some of these variables.
+ VARSET_TP newLiveIn(VarSetOps::MakeCopy(compiler, currentLiveVars));
+ if (predBlock)
+ {
+ // Compute set difference: newLiveIn = currentLiveVars - predBlock->bbLiveOut
+ VarSetOps::DiffD(compiler, newLiveIn, predBlock->bbLiveOut);
+ }
+ bool needsDummyDefs = (!VarSetOps::IsEmpty(compiler, newLiveIn) && block != compiler->fgFirstBB);
+
+ // Create dummy def RefPositions
+
+ if (needsDummyDefs)
+ {
+ // If we are using locations from a predecessor, we should never require DummyDefs.
+ assert(!predBlockIsAllocated);
+
+ JITDUMP("Creating dummy definitions\n");
+ VARSET_ITER_INIT(compiler, iter, newLiveIn, varIndex);
+ while (iter.NextElem(&varIndex))
{
- Interval* interval = getIntervalForLocalVar(varIndex);
- RefPosition* pos =
- newRefPosition(interval, currentLoc, RefTypeDummyDef, nullptr, allRegs(interval->registerType));
+ unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
+ LclVarDsc* varDsc = compiler->lvaTable + varNum;
+ // Add a dummyDef for any candidate vars that are in the "newLiveIn" set.
+ // If this is the entry block, don't add any incoming parameters (they're handled with ParamDefs).
+ if (isCandidateVar(varDsc) && (predBlock != nullptr || !varDsc->lvIsParam))
+ {
+ Interval* interval = getIntervalForLocalVar(varIndex);
+ RefPosition* pos =
+ newRefPosition(interval, currentLoc, RefTypeDummyDef, nullptr, allRegs(interval->registerType));
+ }
}
+ JITDUMP("Finished creating dummy definitions\n\n");
}
- JITDUMP("Finished creating dummy definitions\n\n");
}
// Add a dummy RefPosition to mark the block boundary.
RefPosition* pos = newRefPosition((Interval*)nullptr, currentLoc, RefTypeBB, nullptr, RBM_NONE);
- VarSetOps::Assign(compiler, currentLiveVars, block->bbLiveIn);
-
LIR::Range& blockRange = LIR::AsRange(block);
for (GenTree* node : blockRange.NonPhiNodes())
{
// Note: the visited set is cleared in LinearScan::doLinearScan()
markBlockVisited(block);
- // Insert exposed uses for a lclVar that is live-out of 'block' but not live-in to the
- // next block, or any unvisited successors.
- // This will address lclVars that are live on a backedge, as well as those that are kept
- // live at a GT_JMP.
- //
- // Blocks ending with "jmp method" are marked as BBJ_HAS_JMP,
- // and jmp call is represented using GT_JMP node which is a leaf node.
- // Liveness phase keeps all the arguments of the method live till the end of
- // block by adding them to liveout set of the block containing GT_JMP.
- //
- // The target of a GT_JMP implicitly uses all the current method arguments, however
- // there are no actual references to them. This can cause LSRA to assert, because
- // the variables are live but it sees no references. In order to correctly model the
- // liveness of these arguments, we add dummy exposed uses, in the same manner as for
- // backward branches. This will happen automatically via expUseSet.
- //
- // Note that a block ending with GT_JMP has no successors and hence the variables
- // for which dummy use ref positions are added are arguments of the method.
-
- VARSET_TP expUseSet(VarSetOps::MakeCopy(compiler, block->bbLiveOut));
- BasicBlock* nextBlock = getNextBlock();
- if (nextBlock != nullptr)
- {
- VarSetOps::DiffD(compiler, expUseSet, nextBlock->bbLiveIn);
- }
- for (BasicBlock* succ : block->GetAllSuccs(compiler))
+ if (enregisterLocalVars)
{
- if (VarSetOps::IsEmpty(compiler, expUseSet))
+ // Insert exposed uses for a lclVar that is live-out of 'block' but not live-in to the
+ // next block, or any unvisited successors.
+ // This will address lclVars that are live on a backedge, as well as those that are kept
+ // live at a GT_JMP.
+ //
+ // Blocks ending with "jmp method" are marked as BBJ_HAS_JMP,
+ // and jmp call is represented using GT_JMP node which is a leaf node.
+ // Liveness phase keeps all the arguments of the method live till the end of
+ // block by adding them to liveout set of the block containing GT_JMP.
+ //
+ // The target of a GT_JMP implicitly uses all the current method arguments, however
+ // there are no actual references to them. This can cause LSRA to assert, because
+ // the variables are live but it sees no references. In order to correctly model the
+ // liveness of these arguments, we add dummy exposed uses, in the same manner as for
+ // backward branches. This will happen automatically via expUseSet.
+ //
+ // Note that a block ending with GT_JMP has no successors and hence the variables
+ // for which dummy use ref positions are added are arguments of the method.
+
+ VARSET_TP expUseSet(VarSetOps::MakeCopy(compiler, block->bbLiveOut));
+ VarSetOps::IntersectionD(compiler, expUseSet, registerCandidateVars);
+ BasicBlock* nextBlock = getNextBlock();
+ if (nextBlock != nullptr)
{
- break;
+ VarSetOps::DiffD(compiler, expUseSet, nextBlock->bbLiveIn);
}
-
- if (isBlockVisited(succ))
+ for (BasicBlock* succ : block->GetAllSuccs(compiler))
{
- continue;
+ if (VarSetOps::IsEmpty(compiler, expUseSet))
+ {
+ break;
+ }
+
+ if (isBlockVisited(succ))
+ {
+ continue;
+ }
+ VarSetOps::DiffD(compiler, expUseSet, succ->bbLiveIn);
}
- VarSetOps::DiffD(compiler, expUseSet, succ->bbLiveIn);
- }
- if (!VarSetOps::IsEmpty(compiler, expUseSet))
- {
- JITDUMP("Exposed uses:");
- VARSET_ITER_INIT(compiler, iter, expUseSet, varIndex);
- while (iter.NextElem(&varIndex))
+ if (!VarSetOps::IsEmpty(compiler, expUseSet))
{
- unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
- LclVarDsc* varDsc = compiler->lvaTable + varNum;
- if (isCandidateVar(varDsc))
+ JITDUMP("Exposed uses:");
+ VARSET_ITER_INIT(compiler, iter, expUseSet, varIndex);
+ while (iter.NextElem(&varIndex))
{
+ unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
+ LclVarDsc* varDsc = compiler->lvaTable + varNum;
+ assert(isCandidateVar(varDsc));
Interval* interval = getIntervalForLocalVar(varIndex);
RefPosition* pos =
newRefPosition(interval, currentLoc, RefTypeExpUse, nullptr, allRegs(interval->registerType));
JITDUMP(" V%02u", varNum);
}
+ JITDUMP("\n");
}
- JITDUMP("\n");
- }
- // Clear the "last use" flag on any vars that are live-out from this block.
- {
- VARSET_ITER_INIT(compiler, iter, block->bbLiveOut, varIndex);
- while (iter.NextElem(&varIndex))
+ // Clear the "last use" flag on any vars that are live-out from this block.
{
- unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
- LclVarDsc* const varDsc = &compiler->lvaTable[varNum];
- if (isCandidateVar(varDsc))
+ VARSET_ITER_INIT(compiler, iter, block->bbLiveOut, varIndex);
+ while (iter.NextElem(&varIndex))
{
- RefPosition* const lastRP = getIntervalForLocalVar(varIndex)->lastRefPosition;
- if ((lastRP != nullptr) && (lastRP->bbNum == block->bbNum))
+ unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
+ LclVarDsc* const varDsc = &compiler->lvaTable[varNum];
+ if (isCandidateVar(varDsc))
{
- lastRP->lastUse = false;
+ RefPosition* const lastRP = getIntervalForLocalVar(varIndex)->lastRefPosition;
+ if ((lastRP != nullptr) && (lastRP->bbNum == block->bbNum))
+ {
+ lastRP->lastUse = false;
+ }
}
}
}
- }
#ifdef DEBUG
- checkLastUses(block);
+ checkLastUses(block);
- if (VERBOSE)
- {
- printf("use: ");
- dumpConvertedVarSet(compiler, block->bbVarUse);
- printf("\ndef: ");
- dumpConvertedVarSet(compiler, block->bbVarDef);
- printf("\n");
- }
+ if (VERBOSE)
+ {
+ printf("use: ");
+ dumpConvertedVarSet(compiler, block->bbVarUse);
+ printf("\ndef: ");
+ dumpConvertedVarSet(compiler, block->bbVarDef);
+ printf("\n");
+ }
#endif // DEBUG
+ }
prevBlock = block;
}
- // If we need to KeepAliveAndReportThis, add a dummy exposed use of it at the end
- if (compiler->lvaKeepAliveAndReportThis())
+ if (enregisterLocalVars)
{
- unsigned keepAliveVarNum = compiler->info.compThisArg;
- assert(compiler->info.compIsStatic == false);
- LclVarDsc* varDsc = compiler->lvaTable + keepAliveVarNum;
- if (isCandidateVar(varDsc))
+ if (compiler->lvaKeepAliveAndReportThis())
{
- JITDUMP("Adding exposed use of this, for lvaKeepAliveAndReportThis\n");
- Interval* interval = getIntervalForLocalVar(varDsc->lvVarIndex);
- RefPosition* pos =
- newRefPosition(interval, currentLoc, RefTypeExpUse, nullptr, allRegs(interval->registerType));
+ // If we need to KeepAliveAndReportThis, add a dummy exposed use of it at the end
+ unsigned keepAliveVarNum = compiler->info.compThisArg;
+ assert(compiler->info.compIsStatic == false);
+ LclVarDsc* varDsc = compiler->lvaTable + keepAliveVarNum;
+ if (isCandidateVar(varDsc))
+ {
+ JITDUMP("Adding exposed use of this, for lvaKeepAliveAndReportThis\n");
+ Interval* interval = getIntervalForLocalVar(varDsc->lvVarIndex);
+ RefPosition* pos =
+ newRefPosition(interval, currentLoc, RefTypeExpUse, nullptr, allRegs(interval->registerType));
+ }
}
- }
#ifdef DEBUG
- if (getLsraExtendLifeTimes())
- {
- LclVarDsc* varDsc;
- for (lclNum = 0, varDsc = compiler->lvaTable; lclNum < compiler->lvaCount; lclNum++, varDsc++)
+ if (getLsraExtendLifeTimes())
{
- if (varDsc->lvLRACandidate)
+ LclVarDsc* varDsc;
+ for (lclNum = 0, varDsc = compiler->lvaTable; lclNum < compiler->lvaCount; lclNum++, varDsc++)
{
- JITDUMP("Adding exposed use of V%02u for LsraExtendLifetimes\n", lclNum);
- Interval* interval = getIntervalForLocalVar(varDsc->lvVarIndex);
- RefPosition* pos =
- newRefPosition(interval, currentLoc, RefTypeExpUse, nullptr, allRegs(interval->registerType));
+ if (varDsc->lvLRACandidate)
+ {
+ JITDUMP("Adding exposed use of V%02u for LsraExtendLifetimes\n", lclNum);
+ Interval* interval = getIntervalForLocalVar(varDsc->lvVarIndex);
+ RefPosition* pos =
+ newRefPosition(interval, currentLoc, RefTypeExpUse, nullptr, allRegs(interval->registerType));
+ }
}
}
- }
#endif // DEBUG
+ }
// If the last block has successors, create a RefTypeBB to record
// what's live
#ifdef DEBUG
void LinearScan::dumpVarRefPositions(const char* title)
{
- printf("\nVAR REFPOSITIONS %s\n", title);
-
- for (unsigned i = 0; i < compiler->lvaCount; i++)
+ if (enregisterLocalVars)
{
- printf("--- V%02u\n", i);
+ printf("\nVAR REFPOSITIONS %s\n", title);
- LclVarDsc* varDsc = compiler->lvaTable + i;
- if (varDsc->lvTracked)
+ for (unsigned i = 0; i < compiler->lvaCount; i++)
{
- Interval* interval = getIntervalForLocalVar(varDsc->lvVarIndex);
- for (RefPosition* ref = interval->firstRefPosition; ref != nullptr; ref = ref->nextRefPosition)
+ printf("--- V%02u\n", i);
+
+ LclVarDsc* varDsc = compiler->lvaTable + i;
+ if (varDsc->lvIsRegCandidate())
{
- ref->dump();
+ Interval* interval = getIntervalForLocalVar(varDsc->lvVarIndex);
+ for (RefPosition* ref = interval->firstRefPosition; ref != nullptr; ref = ref->nextRefPosition)
+ {
+ ref->dump();
+ }
}
}
+ printf("\n");
}
-
- printf("\n");
}
void LinearScan::validateIntervals()
{
- for (unsigned i = 0; i < compiler->lvaTrackedCount; i++)
+ if (enregisterLocalVars)
{
- Interval* interval = getIntervalForLocalVar(i);
-
- bool defined = false;
- printf("-----------------\n");
- for (RefPosition* ref = interval->firstRefPosition; ref != nullptr; ref = ref->nextRefPosition)
+ for (unsigned i = 0; i < compiler->lvaTrackedCount; i++)
{
- ref->dump();
- RefType refType = ref->refType;
- if (!defined && RefTypeIsUse(refType))
+ if (!compiler->lvaTable[compiler->lvaTrackedToVarNum[i]].lvLRACandidate)
{
- if (compiler->info.compMethodName != nullptr)
- {
- printf("%s: ", compiler->info.compMethodName);
- }
- printf("LocalVar V%02u: undefined use at %u\n", interval->varNum, ref->nodeLocation);
- }
- // Note that there can be multiple last uses if they are on disjoint paths,
- // so we can't really check the lastUse flag
- if (ref->lastUse)
- {
- defined = false;
+ continue;
}
- if (RefTypeIsDef(refType))
+ Interval* interval = getIntervalForLocalVar(i);
+
+ bool defined = false;
+ printf("-----------------\n");
+ for (RefPosition* ref = interval->firstRefPosition; ref != nullptr; ref = ref->nextRefPosition)
{
- defined = true;
+ ref->dump();
+ RefType refType = ref->refType;
+ if (!defined && RefTypeIsUse(refType))
+ {
+ if (compiler->info.compMethodName != nullptr)
+ {
+ printf("%s: ", compiler->info.compMethodName);
+ }
+ printf("LocalVar V%02u: undefined use at %u\n", interval->varNum, ref->nodeLocation);
+ }
+ // Note that there can be multiple last uses if they are on disjoint paths,
+ // so we can't really check the lastUse flag
+ if (ref->lastUse)
+ {
+ defined = false;
+ }
+ if (RefTypeIsDef(refType))
+ {
+ defined = true;
+ }
}
}
}
// None
//
// Notes:
-// Calls processBlockEndLocation() to set the outVarToRegMap, then gets the next block,
+// Calls processBlockEndLocations() to set the outVarToRegMap, then gets the next block,
// and sets the inVarToRegMap appropriately.
void LinearScan::processBlockEndAllocation(BasicBlock* currentBlock)
{
assert(currentBlock != nullptr);
- processBlockEndLocations(currentBlock);
+ if (enregisterLocalVars)
+ {
+ processBlockEndLocations(currentBlock);
+ }
markBlockVisited(currentBlock);
// Get the next block to allocate.
}
//------------------------------------------------------------------------
-// processBlockStartLocations: Update var locations on entry to 'currentBlock'
+// processBlockStartLocations: Update var locations on entry to 'currentBlock' and clear constant
+// registers.
//
// Arguments:
-// currentBlock - the BasicBlock we have just finished allocating registers for
+// currentBlock - the BasicBlock we are about to allocate registers for
// allocationPass - true if we are currently allocating registers (versus writing them back)
//
// Return Value:
// a lclVar was spilled after the block had been completed.
void LinearScan::processBlockStartLocations(BasicBlock* currentBlock, bool allocationPass)
{
+ // If we have no register candidates we should only call this method during allocation.
+
+ assert(enregisterLocalVars || allocationPass);
+
+ if (!enregisterLocalVars)
+ {
+ // Just clear any constant registers and return.
+ for (regNumber reg = REG_FIRST; reg < ACTUAL_REG_COUNT; reg = REG_NEXT(reg))
+ {
+ RegRecord* physRegRecord = getRegisterRecord(reg);
+ Interval* assignedInterval = physRegRecord->assignedInterval;
+
+ if (assignedInterval != nullptr)
+ {
+ assert(assignedInterval->isConstant);
+ physRegRecord->assignedInterval = nullptr;
+ }
+ }
+ INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_START_BB, nullptr, REG_NA, currentBlock));
+ return;
+ }
+
unsigned predBBNum = blockInfo[currentBlock->bbNum].predBBNum;
VarToRegMap predVarToRegMap = getOutVarToRegMap(predBBNum);
VarToRegMap inVarToRegMap = getInVarToRegMap(currentBlock->bbNum);
bool hasCriticalInEdge = blockInfo[currentBlock->bbNum].hasCriticalInEdge;
- const VARSET_TP* liveIn = ¤tBlock->bbLiveIn;
+ VarSetOps::AssignNoCopy(compiler, currentLiveVars,
+ VarSetOps::Intersection(compiler, registerCandidateVars, currentBlock->bbLiveIn));
#ifdef DEBUG
if (getLsraExtendLifeTimes())
{
- liveIn = &compiler->lvaTrackedVars;
+ VarSetOps::AssignNoCopy(compiler, currentLiveVars, registerCandidateVars);
}
// If we are rotating register assignments at block boundaries, we want to make the
// inactive registers available for the rotation.
regMaskTP inactiveRegs = RBM_NONE;
#endif // DEBUG
regMaskTP liveRegs = RBM_NONE;
- VARSET_ITER_INIT(compiler, iter, *liveIn, varIndex);
+ VARSET_ITER_INIT(compiler, iter, currentLiveVars, varIndex);
while (iter.NextElem(&varIndex))
{
unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
if (allocationPass)
{
- targetReg = predVarToRegMap[varIndex];
+ targetReg = getVarReg(predVarToRegMap, varIndex);
#ifdef DEBUG
regNumber newTargetReg = rotateBlockStartLocation(interval, targetReg, (~liveRegs | inactiveRegs));
if (newTargetReg != targetReg)
setIntervalAsSplit(interval);
}
#endif // DEBUG
- inVarToRegMap[varIndex] = targetReg;
+ setVarReg(inVarToRegMap, varIndex, targetReg);
}
else // !allocationPass (i.e. resolution/write-back pass)
{
- targetReg = inVarToRegMap[varIndex];
+ targetReg = getVarReg(inVarToRegMap, varIndex);
// There are four cases that we need to consider during the resolution pass:
// 1. This variable had a register allocated initially, and it was not spilled in the RefPosition
// that feeds this block. In this case, both targetReg and predVarToRegMap[varIndex] will be targetReg.
// is not REG_STK. We retain the REG_STK value in the inVarToRegMap.
if (targetReg != REG_STK)
{
- if (predVarToRegMap[varIndex] != REG_STK)
+ if (getVarReg(predVarToRegMap, varIndex) != REG_STK)
{
// Case #1 above.
- assert(predVarToRegMap[varIndex] == targetReg ||
+ assert(getVarReg(predVarToRegMap, varIndex) == targetReg ||
getLsraBlockBoundaryLocations() == LSRA_BLOCK_BOUNDARY_ROTATE);
}
else if (!nextRefPosition->copyReg)
{
// case #2 above.
- inVarToRegMap[varIndex] = REG_STK;
- targetReg = REG_STK;
+ setVarReg(inVarToRegMap, varIndex, REG_STK);
+ targetReg = REG_STK;
}
// Else case 2a. - retain targetReg.
}
interval->isActive = true;
liveRegs |= genRegMask(interval->physReg);
INDEBUG(inactiveRegs |= genRegMask(interval->physReg));
- inVarToRegMap[varIndex] = interval->physReg;
+ setVarReg(inVarToRegMap, varIndex, interval->physReg);
}
else
{
assert(currentBlock != nullptr && currentBlock->bbNum == curBBNum);
VarToRegMap outVarToRegMap = getOutVarToRegMap(curBBNum);
- const VARSET_TP* liveOut = ¤tBlock->bbLiveOut;
+ VarSetOps::AssignNoCopy(compiler, currentLiveVars,
+ VarSetOps::Intersection(compiler, registerCandidateVars, currentBlock->bbLiveOut));
#ifdef DEBUG
if (getLsraExtendLifeTimes())
{
- liveOut = &compiler->lvaTrackedVars;
+ VarSetOps::Assign(compiler, currentLiveVars, registerCandidateVars);
}
#endif // DEBUG
regMaskTP liveRegs = RBM_NONE;
- VARSET_ITER_INIT(compiler, iter, *liveOut, varIndex);
+ VARSET_ITER_INIT(compiler, iter, currentLiveVars, varIndex);
while (iter.NextElem(&varIndex))
{
Interval* interval = getIntervalForLocalVar(varIndex);
if (interval->isActive)
{
assert(interval->physReg != REG_NA && interval->physReg != REG_STK);
- outVarToRegMap[varIndex] = interval->physReg;
+ setVarReg(outVarToRegMap, varIndex, interval->physReg);
}
else
{
// was available for use so we kept the association.
if (RefTypeIsUse(refType))
{
+ assert(enregisterLocalVars);
assert(inVarToRegMaps[curBBNum][currentInterval->getVarIndex(compiler)] == REG_STK &&
previousRefPosition->nodeLocation <= curBBStartLocation);
isInRegister = false;
void LinearScan::resolveLocalRef(BasicBlock* block, GenTreePtr treeNode, RefPosition* currentRefPosition)
{
assert((block == nullptr) == (treeNode == nullptr));
+ assert(enregisterLocalVars);
// Is this a tracked local? Or just a register allocated for loading
// a non-tracked one?
// Clear register assignments - these will be reestablished as lclVar defs (including RefTypeParamDefs)
// are encountered.
- for (regNumber reg = REG_FIRST; reg < ACTUAL_REG_COUNT; reg = REG_NEXT(reg))
+ if (enregisterLocalVars)
{
- RegRecord* physRegRecord = getRegisterRecord(reg);
- Interval* assignedInterval = physRegRecord->assignedInterval;
- if (assignedInterval != nullptr)
+ for (regNumber reg = REG_FIRST; reg < ACTUAL_REG_COUNT; reg = REG_NEXT(reg))
{
- assignedInterval->assignedReg = nullptr;
- assignedInterval->physReg = REG_NA;
+ RegRecord* physRegRecord = getRegisterRecord(reg);
+ Interval* assignedInterval = physRegRecord->assignedInterval;
+ if (assignedInterval != nullptr)
+ {
+ assignedInterval->assignedReg = nullptr;
+ assignedInterval->physReg = REG_NA;
+ }
+ physRegRecord->assignedInterval = nullptr;
+ physRegRecord->recentRefPosition = nullptr;
}
- physRegRecord->assignedInterval = nullptr;
- physRegRecord->recentRefPosition = nullptr;
- }
- // Clear "recentRefPosition" for lclVar intervals
- for (unsigned varIndex = 0; varIndex < compiler->lvaTrackedCount; varIndex++)
- {
- localVarIntervals[varIndex]->recentRefPosition = nullptr;
- localVarIntervals[varIndex]->isActive = false;
+ // Clear "recentRefPosition" for lclVar intervals
+ for (unsigned varIndex = 0; varIndex < compiler->lvaTrackedCount; varIndex++)
+ {
+ if (localVarIntervals[varIndex] != nullptr)
+ {
+ localVarIntervals[varIndex]->recentRefPosition = nullptr;
+ localVarIntervals[varIndex]->isActive = false;
+ }
+ else
+ {
+ assert(compiler->lvaTable[compiler->lvaTrackedToVarNum[varIndex]].lvLRACandidate == false);
+ }
+ }
}
// handle incoming arguments and special temps
auto currentRefPosition = refPositions.begin();
- VarToRegMap entryVarToRegMap = inVarToRegMaps[compiler->fgFirstBB->bbNum];
- while (currentRefPosition != refPositions.end() &&
- (currentRefPosition->refType == RefTypeParamDef || currentRefPosition->refType == RefTypeZeroInit))
+ if (enregisterLocalVars)
{
- Interval* interval = currentRefPosition->getInterval();
- assert(interval != nullptr && interval->isLocalVar);
- resolveLocalRef(nullptr, nullptr, currentRefPosition);
- regNumber reg = REG_STK;
- int varIndex = interval->getVarIndex(compiler);
-
- if (!currentRefPosition->spillAfter && currentRefPosition->registerAssignment != RBM_NONE)
+ VarToRegMap entryVarToRegMap = inVarToRegMaps[compiler->fgFirstBB->bbNum];
+ while (currentRefPosition != refPositions.end() &&
+ (currentRefPosition->refType == RefTypeParamDef || currentRefPosition->refType == RefTypeZeroInit))
{
- reg = currentRefPosition->assignedReg();
- }
- else
- {
- reg = REG_STK;
- interval->isActive = false;
+ Interval* interval = currentRefPosition->getInterval();
+ assert(interval != nullptr && interval->isLocalVar);
+ resolveLocalRef(nullptr, nullptr, currentRefPosition);
+ regNumber reg = REG_STK;
+ int varIndex = interval->getVarIndex(compiler);
+
+ if (!currentRefPosition->spillAfter && currentRefPosition->registerAssignment != RBM_NONE)
+ {
+ reg = currentRefPosition->assignedReg();
+ }
+ else
+ {
+ reg = REG_STK;
+ interval->isActive = false;
+ }
+ setVarReg(entryVarToRegMap, varIndex, reg);
+ ++currentRefPosition;
}
- entryVarToRegMap[varIndex] = reg;
- ++currentRefPosition;
+ }
+ else
+ {
+ assert(currentRefPosition == refPositions.end() ||
+ (currentRefPosition->refType != RefTypeParamDef && currentRefPosition->refType != RefTypeZeroInit));
}
JITDUMP("------------------------\n");
}
#endif // DEBUG
- // Record the var locations at the start of this block.
- // (If it's fgFirstBB, we've already done that above, see entryVarToRegMap)
-
- curBBStartLocation = currentRefPosition->nodeLocation;
- if (block != compiler->fgFirstBB)
+ if (enregisterLocalVars)
{
- processBlockStartLocations(block, false);
- }
+ // Record the var locations at the start of this block.
+ // (If it's fgFirstBB, we've already done that above, see entryVarToRegMap)
- // Handle the DummyDefs, updating the incoming var location.
- for (; currentRefPosition != refPositions.end() && currentRefPosition->refType == RefTypeDummyDef;
- ++currentRefPosition)
- {
- assert(currentRefPosition->isIntervalRef());
- // Don't mark dummy defs as reload
- currentRefPosition->reload = false;
- resolveLocalRef(nullptr, nullptr, currentRefPosition);
- regNumber reg;
- if (currentRefPosition->registerAssignment != RBM_NONE)
+ curBBStartLocation = currentRefPosition->nodeLocation;
+ if (block != compiler->fgFirstBB)
{
- reg = currentRefPosition->assignedReg();
+ processBlockStartLocations(block, false);
}
- else
+
+ // Handle the DummyDefs, updating the incoming var location.
+ for (; currentRefPosition != refPositions.end() && currentRefPosition->refType == RefTypeDummyDef;
+ ++currentRefPosition)
{
- reg = REG_STK;
- currentRefPosition->getInterval()->isActive = false;
+ assert(currentRefPosition->isIntervalRef());
+ // Don't mark dummy defs as reload
+ currentRefPosition->reload = false;
+ resolveLocalRef(nullptr, nullptr, currentRefPosition);
+ regNumber reg;
+ if (currentRefPosition->registerAssignment != RBM_NONE)
+ {
+ reg = currentRefPosition->assignedReg();
+ }
+ else
+ {
+ reg = REG_STK;
+ currentRefPosition->getInterval()->isActive = false;
+ }
+ setInVarRegForBB(curBBNum, currentRefPosition->getInterval()->varNum, reg);
}
- setInVarRegForBB(curBBNum, currentRefPosition->getInterval()->varNum, reg);
}
// The next RefPosition should be for the block. Move past it.
JITDUMP("\n");
}
- processBlockEndLocations(block);
+ if (enregisterLocalVars)
+ {
+ processBlockEndLocations(block);
+ }
}
-#ifdef DEBUG
- if (VERBOSE)
+ if (enregisterLocalVars)
{
- printf("-----------------------\n");
- printf("RESOLVING BB BOUNDARIES\n");
- printf("-----------------------\n");
-
- printf("Resolution Candidates: ");
- dumpConvertedVarSet(compiler, resolutionCandidateVars);
- printf("\n");
- printf("Has %sCritical Edges\n\n", hasCriticalEdges ? "" : "No");
-
- printf("Prior to Resolution\n");
- foreach_block(compiler, block)
+#ifdef DEBUG
+ if (VERBOSE)
{
- printf("\nBB%02u use def in out\n", block->bbNum);
- dumpConvertedVarSet(compiler, block->bbVarUse);
- printf("\n");
- dumpConvertedVarSet(compiler, block->bbVarDef);
- printf("\n");
- dumpConvertedVarSet(compiler, block->bbLiveIn);
- printf("\n");
- dumpConvertedVarSet(compiler, block->bbLiveOut);
- printf("\n");
+ printf("-----------------------\n");
+ printf("RESOLVING BB BOUNDARIES\n");
+ printf("-----------------------\n");
- dumpInVarToRegMap(block);
- dumpOutVarToRegMap(block);
- }
+ printf("Resolution Candidates: ");
+ dumpConvertedVarSet(compiler, resolutionCandidateVars);
+ printf("\n");
+ printf("Has %sCritical Edges\n\n", hasCriticalEdges ? "" : "No");
- printf("\n\n");
- }
-#endif // DEBUG
+ printf("Prior to Resolution\n");
+ foreach_block(compiler, block)
+ {
+ printf("\nBB%02u use def in out\n", block->bbNum);
+ dumpConvertedVarSet(compiler, block->bbVarUse);
+ printf("\n");
+ dumpConvertedVarSet(compiler, block->bbVarDef);
+ printf("\n");
+ dumpConvertedVarSet(compiler, block->bbLiveIn);
+ printf("\n");
+ dumpConvertedVarSet(compiler, block->bbLiveOut);
+ printf("\n");
- resolveEdges();
+ dumpInVarToRegMap(block);
+ dumpOutVarToRegMap(block);
+ }
- // Verify register assignments on variables
- unsigned lclNum;
- LclVarDsc* varDsc;
- for (lclNum = 0, varDsc = compiler->lvaTable; lclNum < compiler->lvaCount; lclNum++, varDsc++)
- {
- if (!isCandidateVar(varDsc))
- {
- varDsc->lvRegNum = REG_STK;
+ printf("\n\n");
}
- else
- {
- Interval* interval = getIntervalForLocalVar(varDsc->lvVarIndex);
+#endif // DEBUG
- // Determine initial position for parameters
+ resolveEdges();
- if (varDsc->lvIsParam)
+ // Verify register assignments on variables
+ unsigned lclNum;
+ LclVarDsc* varDsc;
+ for (lclNum = 0, varDsc = compiler->lvaTable; lclNum < compiler->lvaCount; lclNum++, varDsc++)
+ {
+ if (!isCandidateVar(varDsc))
{
- regMaskTP initialRegMask = interval->firstRefPosition->registerAssignment;
- regNumber initialReg = (initialRegMask == RBM_NONE || interval->firstRefPosition->spillAfter)
- ? REG_STK
- : genRegNumFromMask(initialRegMask);
- regNumber sourceReg = (varDsc->lvIsRegArg) ? varDsc->lvArgReg : REG_STK;
-
-#ifdef _TARGET_ARM_
- if (varTypeIsMultiReg(varDsc))
- {
- // TODO-ARM-NYI: Map the hi/lo intervals back to lvRegNum and lvOtherReg (these should NYI before
- // this)
- assert(!"Multi-reg types not yet supported");
- }
- else
-#endif // _TARGET_ARM_
- {
- varDsc->lvArgInitReg = initialReg;
- JITDUMP(" Set V%02u argument initial register to %s\n", lclNum, getRegName(initialReg));
- }
-
- // Stack args that are part of dependently-promoted structs should never be register candidates (see
- // LinearScan::isRegCandidate).
- assert(varDsc->lvIsRegArg || !compiler->lvaIsFieldOfDependentlyPromotedStruct(varDsc));
+ varDsc->lvRegNum = REG_STK;
}
-
- // If lvRegNum is REG_STK, that means that either no register
- // was assigned, or (more likely) that the same register was not
- // used for all references. In that case, codegen gets the register
- // from the tree node.
- if (varDsc->lvRegNum == REG_STK || interval->isSpilled || interval->isSplit)
+ else
{
- // For codegen purposes, we'll set lvRegNum to whatever register
- // it's currently in as we go.
- // However, we never mark an interval as lvRegister if it has either been spilled
- // or split.
- varDsc->lvRegister = false;
+ Interval* interval = getIntervalForLocalVar(varDsc->lvVarIndex);
- // Skip any dead defs or exposed uses
- // (first use exposed will only occur when there is no explicit initialization)
- RefPosition* firstRefPosition = interval->firstRefPosition;
- while ((firstRefPosition != nullptr) && (firstRefPosition->refType == RefTypeExpUse))
- {
- firstRefPosition = firstRefPosition->nextRefPosition;
- }
- if (firstRefPosition == nullptr)
+ // Determine initial position for parameters
+
+ if (varDsc->lvIsParam)
{
- // Dead interval
- varDsc->lvLRACandidate = false;
- if (varDsc->lvRefCnt == 0)
+ regMaskTP initialRegMask = interval->firstRefPosition->registerAssignment;
+ regNumber initialReg = (initialRegMask == RBM_NONE || interval->firstRefPosition->spillAfter)
+ ? REG_STK
+ : genRegNumFromMask(initialRegMask);
+ regNumber sourceReg = (varDsc->lvIsRegArg) ? varDsc->lvArgReg : REG_STK;
+
+#ifdef _TARGET_ARM_
+ if (varTypeIsMultiReg(varDsc))
{
- varDsc->lvOnFrame = false;
+ // TODO-ARM-NYI: Map the hi/lo intervals back to lvRegNum and lvOtherReg (these should NYI
+ // before this)
+ assert(!"Multi-reg types not yet supported");
}
else
+#endif // _TARGET_ARM_
{
- // We may encounter cases where a lclVar actually has no references, but
- // a non-zero refCnt. For safety (in case this is some "hidden" lclVar that we're
- // not correctly recognizing), we'll mark those as needing a stack location.
- // TODO-Cleanup: Make this an assert if/when we correct the refCnt
- // updating.
- varDsc->lvOnFrame = true;
+ varDsc->lvArgInitReg = initialReg;
+ JITDUMP(" Set V%02u argument initial register to %s\n", lclNum, getRegName(initialReg));
}
+
+ // Stack args that are part of dependently-promoted structs should never be register candidates (see
+ // LinearScan::isRegCandidate).
+ assert(varDsc->lvIsRegArg || !compiler->lvaIsFieldOfDependentlyPromotedStruct(varDsc));
}
- else
+
+ // If lvRegNum is REG_STK, that means that either no register
+ // was assigned, or (more likely) that the same register was not
+ // used for all references. In that case, codegen gets the register
+ // from the tree node.
+ if (varDsc->lvRegNum == REG_STK || interval->isSpilled || interval->isSplit)
{
- // If the interval was not spilled, it doesn't need a stack location.
- if (!interval->isSpilled)
+ // For codegen purposes, we'll set lvRegNum to whatever register
+ // it's currently in as we go.
+ // However, we never mark an interval as lvRegister if it has either been spilled
+ // or split.
+ varDsc->lvRegister = false;
+
+ // Skip any dead defs or exposed uses
+ // (first use exposed will only occur when there is no explicit initialization)
+ RefPosition* firstRefPosition = interval->firstRefPosition;
+ while ((firstRefPosition != nullptr) && (firstRefPosition->refType == RefTypeExpUse))
{
- varDsc->lvOnFrame = false;
+ firstRefPosition = firstRefPosition->nextRefPosition;
}
- if (firstRefPosition->registerAssignment == RBM_NONE || firstRefPosition->spillAfter)
+ if (firstRefPosition == nullptr)
{
- // Either this RefPosition is spilled, or regOptional or it is not a "real" def or use
- assert(firstRefPosition->spillAfter || firstRefPosition->AllocateIfProfitable() ||
- (firstRefPosition->refType != RefTypeDef && firstRefPosition->refType != RefTypeUse));
- varDsc->lvRegNum = REG_STK;
+ // Dead interval
+ varDsc->lvLRACandidate = false;
+ if (varDsc->lvRefCnt == 0)
+ {
+ varDsc->lvOnFrame = false;
+ }
+ else
+ {
+ // We may encounter cases where a lclVar actually has no references, but
+ // a non-zero refCnt. For safety (in case this is some "hidden" lclVar that we're
+ // not correctly recognizing), we'll mark those as needing a stack location.
+ // TODO-Cleanup: Make this an assert if/when we correct the refCnt
+ // updating.
+ varDsc->lvOnFrame = true;
+ }
}
else
{
- varDsc->lvRegNum = firstRefPosition->assignedReg();
+ // If the interval was not spilled, it doesn't need a stack location.
+ if (!interval->isSpilled)
+ {
+ varDsc->lvOnFrame = false;
+ }
+ if (firstRefPosition->registerAssignment == RBM_NONE || firstRefPosition->spillAfter)
+ {
+ // Either this RefPosition is spilled, or regOptional or it is not a "real" def or use
+ assert(
+ firstRefPosition->spillAfter || firstRefPosition->AllocateIfProfitable() ||
+ (firstRefPosition->refType != RefTypeDef && firstRefPosition->refType != RefTypeUse));
+ varDsc->lvRegNum = REG_STK;
+ }
+ else
+ {
+ varDsc->lvRegNum = firstRefPosition->assignedReg();
+ }
}
}
- }
- else
- {
+ else
{
- varDsc->lvRegister = true;
- varDsc->lvOnFrame = false;
- }
+ {
+ varDsc->lvRegister = true;
+ varDsc->lvOnFrame = false;
+ }
#ifdef DEBUG
- regMaskTP registerAssignment = genRegMask(varDsc->lvRegNum);
- assert(!interval->isSpilled && !interval->isSplit);
- RefPosition* refPosition = interval->firstRefPosition;
- assert(refPosition != nullptr);
+ regMaskTP registerAssignment = genRegMask(varDsc->lvRegNum);
+ assert(!interval->isSpilled && !interval->isSplit);
+ RefPosition* refPosition = interval->firstRefPosition;
+ assert(refPosition != nullptr);
- while (refPosition != nullptr)
- {
- // All RefPositions must match, except for dead definitions,
- // copyReg/moveReg and RefTypeExpUse positions
- if (refPosition->registerAssignment != RBM_NONE && !refPosition->copyReg && !refPosition->moveReg &&
- refPosition->refType != RefTypeExpUse)
+ while (refPosition != nullptr)
{
- assert(refPosition->registerAssignment == registerAssignment);
+ // All RefPositions must match, except for dead definitions,
+ // copyReg/moveReg and RefTypeExpUse positions
+ if (refPosition->registerAssignment != RBM_NONE && !refPosition->copyReg &&
+ !refPosition->moveReg && refPosition->refType != RefTypeExpUse)
+ {
+ assert(refPosition->registerAssignment == registerAssignment);
+ }
+ refPosition = refPosition->nextRefPosition;
}
- refPosition = refPosition->nextRefPosition;
- }
#endif // DEBUG
+ }
}
}
}
VARSET_ITER_INIT(compiler, iter, toBlock->bbLiveIn, varIndex);
while (iter.NextElem(&varIndex) && freeRegs != RBM_NONE)
{
- regNumber fromReg = fromVarToRegMap[varIndex];
- regNumber toReg = toVarToRegMap[varIndex];
+ regNumber fromReg = getVarReg(fromVarToRegMap, varIndex);
+ regNumber toReg = getVarReg(toVarToRegMap, varIndex);
assert(fromReg != REG_NA && toReg != REG_NA);
if (fromReg != REG_STK)
{
else if (sameToReg != fromReg)
{
VarSetOps::AddElemD(compiler, sameResolutionSet, varIndex);
- sameVarToRegMap[varIndex] = sameToReg;
+ setVarReg(sameVarToRegMap, varIndex, sameToReg);
if (sameToReg != REG_STK)
{
sameWriteRegs |= genRegMask(sameToReg);
// since we'll add a new block to do the move.
if (resolveType == ResolveSplit)
{
- toVarToRegMap[varIndex] = fromReg;
+ setVarReg(toVarToRegMap, varIndex, fromReg);
}
else if (resolveType == ResolveJoin || resolveType == ResolveSharedCritical)
{
- fromVarToRegMap[varIndex] = toReg;
+ setVarReg(fromVarToRegMap, varIndex, toReg);
}
assert(fromReg < UCHAR_MAX && toReg < UCHAR_MAX);
wtdResolutionMovCount += (UINT64)resolutionMovCount * block->bbWeight;
}
+ fprintf(file, "Total Tracked Vars: %d\n", compiler->lvaTrackedCount);
+ fprintf(file, "Total Reg Cand Vars: %d\n", regCandidateVarCount);
+ fprintf(file, "Total number of Intervals: %d\n", static_cast<unsigned>(intervals.size() - 1));
+ fprintf(file, "Total number of RefPositions: %d\n", static_cast<unsigned>(refPositions.size() - 1));
fprintf(file, "Total Spill Count: %d Weighted: %I64u\n", sumSpillCount, wtdSpillCount);
fprintf(file, "Total CopyReg Count: %d Weighted: %I64u\n", sumCopyRegCount, wtdCopyRegCount);
fprintf(file, "Total ResolutionMov Count: %d Weighted: %I64u\n", sumResolutionMovCount, wtdResolutionMovCount);
block->dspBlockHeader(compiler);
printf("=====\n");
}
- if (mode == LSRA_DUMP_POST && block != compiler->fgFirstBB && block->bbNum <= bbNumMaxBeforeResolution)
+ if (enregisterLocalVars && mode == LSRA_DUMP_POST && block != compiler->fgFirstBB &&
+ block->bbNum <= bbNumMaxBeforeResolution)
{
printf("Predecessor for variable locations: BB%02u\n", blockInfo[block->bbNum].predBBNum);
dumpInVarToRegMap(block);
printf("\n");
}
}
- if (mode == LSRA_DUMP_POST)
+ if (enregisterLocalVars && mode == LSRA_DUMP_POST)
{
dumpOutVarToRegMap(block);
}
// Verify the resolution moves at the end of the previous block.
for (GenTree* node = firstBlockEndResolutionNode; node != nullptr; node = node->gtNext)
{
+ assert(enregisterLocalVars);
// Only verify nodes that are actually moves; don't bother with the nodes that are
// operands to moves.
if (IsResolutionMove(node))
}
// Validate the locations at the end of the previous block.
- VarToRegMap outVarToRegMap = outVarToRegMaps[currentBlock->bbNum];
- VARSET_ITER_INIT(compiler, iter, currentBlock->bbLiveOut, varIndex);
- while (iter.NextElem(&varIndex))
+ if (enregisterLocalVars)
{
- regNumber regNum = getVarReg(outVarToRegMap, varIndex);
- interval = getIntervalForLocalVar(varIndex);
- assert(interval->physReg == regNum || (interval->physReg == REG_NA && regNum == REG_STK));
- interval->physReg = REG_NA;
- interval->assignedReg = nullptr;
- interval->isActive = false;
+ VarToRegMap outVarToRegMap = outVarToRegMaps[currentBlock->bbNum];
+ VARSET_ITER_INIT(compiler, iter, currentBlock->bbLiveOut, varIndex);
+ while (iter.NextElem(&varIndex))
+ {
+ if (localVarIntervals[varIndex] == nullptr)
+ {
+ assert(!compiler->lvaTable[compiler->lvaTrackedToVarNum[varIndex]].lvLRACandidate);
+ continue;
+ }
+ regNumber regNum = getVarReg(outVarToRegMap, varIndex);
+ interval = getIntervalForLocalVar(varIndex);
+ assert(interval->physReg == regNum || (interval->physReg == REG_NA && regNum == REG_STK));
+ interval->physReg = REG_NA;
+ interval->assignedReg = nullptr;
+ interval->isActive = false;
+ }
}
// Clear register assignments.
if (currentBlock != nullptr)
{
- VarToRegMap inVarToRegMap = inVarToRegMaps[currentBlock->bbNum];
- VARSET_ITER_INIT(compiler, iter, currentBlock->bbLiveIn, varIndex);
- while (iter.NextElem(&varIndex))
+ if (enregisterLocalVars)
{
- regNumber regNum = getVarReg(inVarToRegMap, varIndex);
- interval = getIntervalForLocalVar(varIndex);
- interval->physReg = regNum;
- interval->assignedReg = &(physRegs[regNum]);
- interval->isActive = true;
- physRegs[regNum].assignedInterval = interval;
+ VarToRegMap inVarToRegMap = inVarToRegMaps[currentBlock->bbNum];
+ VARSET_ITER_INIT(compiler, iter, currentBlock->bbLiveIn, varIndex);
+ while (iter.NextElem(&varIndex))
+ {
+ if (localVarIntervals[varIndex] == nullptr)
+ {
+ assert(!compiler->lvaTable[compiler->lvaTrackedToVarNum[varIndex]].lvLRACandidate);
+ continue;
+ }
+ regNumber regNum = getVarReg(inVarToRegMap, varIndex);
+ interval = getIntervalForLocalVar(varIndex);
+ interval->physReg = regNum;
+ interval->assignedReg = &(physRegs[regNum]);
+ interval->isActive = true;
+ physRegs[regNum].assignedInterval = interval;
+ }
}
if (VERBOSE)
{
if (IsResolutionNode(currentBlockRange, node))
{
+ assert(enregisterLocalVars);
if (foundNonResolutionNode)
{
firstBlockEndResolutionNode = node;
{
if (currentBlock->bbNum > bbNumMaxBeforeResolution)
{
+ // If we haven't enregistered an lclVars, we have no resolution blocks.
+ assert(enregisterLocalVars);
+
if (VERBOSE)
{
dumpRegRecordTitle();
VARSET_ITER_INIT(compiler, iter, currentBlock->bbLiveIn, varIndex);
while (iter.NextElem(&varIndex))
{
+ if (localVarIntervals[varIndex] == nullptr)
+ {
+ assert(!compiler->lvaTable[compiler->lvaTrackedToVarNum[varIndex]].lvLRACandidate);
+ continue;
+ }
regNumber regNum = getVarReg(inVarToRegMap, varIndex);
Interval* interval = getIntervalForLocalVar(varIndex);
interval->physReg = regNum;
VARSET_ITER_INIT(compiler, iter, currentBlock->bbLiveOut, varIndex);
while (iter.NextElem(&varIndex))
{
+ if (localVarIntervals[varIndex] == nullptr)
+ {
+ assert(!compiler->lvaTable[compiler->lvaTrackedToVarNum[varIndex]].lvLRACandidate);
+ continue;
+ }
regNumber regNum = getVarReg(outVarToRegMap, varIndex);
Interval* interval = getIntervalForLocalVar(varIndex);
assert(interval->physReg == regNum || (interval->physReg == REG_NA && regNum == REG_STK));