Streamline LSRA for non-enregistered lclVars
authorCarol Eidt <carol.eidt@microsoft.com>
Tue, 16 May 2017 23:56:05 +0000 (16:56 -0700)
committerCarol Eidt <carol.eidt@microsoft.com>
Wed, 7 Jun 2017 00:28:47 +0000 (17:28 -0700)
- Don't create any lclVar intervals if no lclVars are ever enregistered.
- Don't create intervals for tracked lclVars that are not register candidates.
- Don't create any `RegToVarMap`s when no lclVars are ever enregistered.
- Don't do edge resolution if no lclVars are enregistered.
- Eliminate non-candidate lclVars from the sets used by LSRA.

Also:
- Change VarToRegMap to use regNumberSmall.
- Enhance `COMPLus_JitLsraStats` and `COMPlus_JitTimeLogCsv` dumping.

src/jit/compiler.cpp
src/jit/compphases.h
src/jit/lsra.cpp
src/jit/lsra.h

index 3845292aebebbb597e6f2bc9f97ed2d3986a0aaf..4a13372abe7cf0d6b6528899b5d83ceda27b5d0f 100644 (file)
@@ -7986,16 +7986,16 @@ void JitTimer::PrintCsvHeader()
         if (ftell(fp) == 0)
         {
             fprintf(fp, "\"Method Name\",");
-            fprintf(fp, "\"Method Index\",");
+            fprintf(fp, "\"Assembly or SPMI Index\",");
             fprintf(fp, "\"IL Bytes\",");
             fprintf(fp, "\"Basic Blocks\",");
-            fprintf(fp, "\"Opt Level\",");
+            fprintf(fp, "\"Min Opts\",");
             fprintf(fp, "\"Loops Cloned\",");
 
             for (int i = 0; i < PHASE_NUMBER_OF; i++)
             {
                 fprintf(fp, "\"%s\",", PhaseNames[i]);
-                if (PhaseReportsIRSize[i])
+                if ((JitConfig.JitMeasureIR() != 0) && PhaseReportsIRSize[i])
                 {
                     fprintf(fp, "\"Node Count After %s\",", PhaseNames[i]);
                 }
@@ -8039,7 +8039,16 @@ void JitTimer::PrintCsvMethodStats(Compiler* comp)
 
     FILE* fp = _wfopen(jitTimeLogCsv, W("a"));
     fprintf(fp, "\"%s\",", methName);
-    fprintf(fp, "%d,", index);
+    if (index != 0)
+    {
+        fprintf(fp, "%d,", index);
+    }
+    else
+    {
+        const char* methodAssemblyName = comp->info.compCompHnd->getAssemblyName(
+            comp->info.compCompHnd->getModuleAssembly(comp->info.compCompHnd->getClassModule(comp->info.compClassHnd)));
+        fprintf(fp, "\"%s\",", methodAssemblyName);
+    }
     fprintf(fp, "%u,", comp->info.compILCodeSize);
     fprintf(fp, "%u,", comp->fgBBcount);
     fprintf(fp, "%u,", comp->opts.MinOpts());
@@ -8053,7 +8062,7 @@ void JitTimer::PrintCsvMethodStats(Compiler* comp)
         }
         fprintf(fp, "%I64u,", m_info.m_cyclesByPhase[i]);
 
-        if (PhaseReportsIRSize[i])
+        if ((JitConfig.JitMeasureIR() != 0) && PhaseReportsIRSize[i])
         {
             fprintf(fp, "%u,", m_info.m_nodeCountAfterPhase[i]);
         }
index 2e8ccebef1b7eee08e07690b072d8b551f5b4aca..fe19d919702f5c52ce46020895d5f0458fa38fb7 100644 (file)
@@ -22,6 +22,8 @@
 //     true.
 
 // clang-format off
+//                 enumName                      stringName                        shortName hasChildren measureIR
+//                                                                                                   parent
 CompPhaseNameMacro(PHASE_PRE_IMPORT,             "Pre-import",                     "PRE-IMP",  false, -1, false)
 CompPhaseNameMacro(PHASE_IMPORTATION,            "Importation",                    "IMPORT",   false, -1, true)
 CompPhaseNameMacro(PHASE_POST_IMPORT,            "Post-import",                    "POST-IMP", false, -1, false)
index 72cb78c28d4e50cb8463ee4708a59cddc5960ce2..bea484a442f6827d8b3894bd568babc44924140e 100644 (file)
@@ -1148,7 +1148,8 @@ LinearScan::LinearScan(Compiler* theCompiler)
     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;
@@ -1809,11 +1810,17 @@ void LinearScan::doLinearScan()
 
 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];
@@ -1930,20 +1937,26 @@ void LinearScan::identifyCandidatesExceptionDataflow()
 
 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;
     }
@@ -1962,16 +1975,25 @@ bool LinearScan::isRegCandidate(LclVarDsc* varDsc)
 
 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;
     }
 
@@ -2002,17 +2024,23 @@ void LinearScan::identifyCandidates()
     // 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;
@@ -2045,27 +2073,30 @@ void LinearScan::identifyCandidates()
     }
 #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
@@ -2093,27 +2124,15 @@ void LinearScan::identifyCandidates()
         }
 #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;
         }
 
@@ -2121,11 +2140,6 @@ void LinearScan::identifyCandidates()
 
         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;
@@ -2171,13 +2185,12 @@ void LinearScan::identifyCandidates()
         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;
         }
 
@@ -2232,51 +2245,66 @@ void LinearScan::identifyCandidates()
                 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
@@ -2326,6 +2354,7 @@ void LinearScan::identifyCandidates()
     if (floatVarCount > 6 && compiler->fgHasLoops &&
         (compiler->fgReturnBlocks == nullptr || compiler->fgReturnBlocks->next == nullptr))
     {
+        assert(enregisterLocalVars);
 #ifdef DEBUG
         if (VERBOSE)
         {
@@ -2359,6 +2388,12 @@ void LinearScan::identifyCandidates()
 // 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.
 
@@ -2371,18 +2406,18 @@ void LinearScan::initVarRegMaps()
     // 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++)
             {
@@ -2406,18 +2441,21 @@ void LinearScan::initVarRegMaps()
 
 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);
@@ -2430,6 +2468,7 @@ LinearScan::SplitEdgeInfo LinearScan::getSplitEdgeInfo(unsigned int bbNum)
 
 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.
@@ -2453,6 +2492,7 @@ VarToRegMap LinearScan::getInVarToRegMap(unsigned int bbNum)
 
 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.
@@ -2475,16 +2515,47 @@ VarToRegMap LinearScan::getOutVarToRegMap(unsigned int bbNum)
     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;
@@ -2550,6 +2621,7 @@ void LinearScan::checkLastUses(BasicBlock* block)
     // 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();
@@ -2616,6 +2688,7 @@ void LinearScan::checkLastUses(BasicBlock* block)
     }
 
     VARSET_TP temp2(VarSetOps::MakeCopy(compiler, block->bbLiveIn));
+    VarSetOps::IntersectionD(compiler, temp2, registerCandidateVars);
     VarSetOps::DiffD(compiler, temp2, temp);
     VarSetOps::DiffD(compiler, temp, block->bbLiveIn);
 
@@ -2890,6 +2963,7 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo
         // 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);
@@ -3323,6 +3397,7 @@ public:
 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))
@@ -3359,6 +3434,7 @@ void LinearScan::buildUpperVectorRestoreRefPositions(GenTree*         tree,
                                                      LsraLocation     currentLoc,
                                                      VARSET_VALARG_TP liveLargeVectors)
 {
+    assert(enregisterLocalVars);
     if (!VarSetOps::IsEmpty(compiler, liveLargeVectors))
     {
         VARSET_ITER_INIT(compiler, iter, liveLargeVectors, varIndex);
@@ -4036,7 +4112,7 @@ void LinearScan::buildRefPositionsForNode(GenTree*                  tree,
 
 #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.
@@ -4125,7 +4201,10 @@ void LinearScan::buildRefPositionsForNode(GenTree*                  tree,
 
 #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())
@@ -4169,7 +4248,9 @@ BasicBlock* getNonEmptyBlock(BasicBlock* block)
 // 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.
@@ -4183,9 +4264,15 @@ BasicBlock* getNonEmptyBlock(BasicBlock* block)
 //
 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];
@@ -4607,58 +4694,67 @@ void LinearScan::buildIntervals()
 
         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.
@@ -4668,8 +4764,6 @@ void LinearScan::buildIntervals()
 
         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())
         {
@@ -4694,129 +4788,134 @@ void LinearScan::buildIntervals()
         // 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
@@ -4845,55 +4944,64 @@ void LinearScan::buildIntervals()
 #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;
+                }
             }
         }
     }
@@ -6536,13 +6644,16 @@ void LinearScan::spillGCRefs(RefPosition* killRefPosition)
 //    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.
@@ -6712,10 +6823,11 @@ bool LinearScan::canRestorePreviousInterval(RegRecord* regRec, Interval* assigne
 }
 
 //------------------------------------------------------------------------
-// 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:
@@ -6728,23 +6840,46 @@ bool LinearScan::canRestorePreviousInterval(RegRecord* regRec, Interval* assigne
 //    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 = &currentBlock->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];
@@ -6759,7 +6894,7 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock, bool alloc
 
         if (allocationPass)
         {
-            targetReg = predVarToRegMap[varIndex];
+            targetReg = getVarReg(predVarToRegMap, varIndex);
 #ifdef DEBUG
             regNumber newTargetReg = rotateBlockStartLocation(interval, targetReg, (~liveRegs | inactiveRegs));
             if (newTargetReg != targetReg)
@@ -6768,11 +6903,11 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock, bool alloc
                 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.
@@ -6792,17 +6927,17 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock, bool alloc
             //    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.
             }
@@ -6846,7 +6981,7 @@ void LinearScan::processBlockStartLocations(BasicBlock* currentBlock, bool alloc
                 interval->isActive = true;
                 liveRegs |= genRegMask(interval->physReg);
                 INDEBUG(inactiveRegs |= genRegMask(interval->physReg));
-                inVarToRegMap[varIndex] = interval->physReg;
+                setVarReg(inVarToRegMap, varIndex, interval->physReg);
             }
             else
             {
@@ -6958,22 +7093,23 @@ void LinearScan::processBlockEndLocations(BasicBlock* currentBlock)
     assert(currentBlock != nullptr && currentBlock->bbNum == curBBNum);
     VarToRegMap outVarToRegMap = getOutVarToRegMap(curBBNum);
 
-    const VARSET_TP* liveOut = &currentBlock->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
         {
@@ -7453,6 +7589,7 @@ void LinearScan::allocateRegisters()
                 // 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;
@@ -7916,6 +8053,7 @@ void LinearScan::allocateRegisters()
 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?
@@ -8571,50 +8709,68 @@ void LinearScan::resolveRegisters()
 
     // 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");
@@ -8637,34 +8793,37 @@ void LinearScan::resolveRegisters()
         }
 #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.
@@ -8897,167 +9056,174 @@ void LinearScan::resolveRegisters()
             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
+                }
             }
         }
     }
@@ -9298,8 +9464,8 @@ regNumber LinearScan::getTempRegForResolution(BasicBlock* fromBlock, BasicBlock*
     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)
         {
@@ -9537,7 +9703,7 @@ void LinearScan::handleOutgoingCriticalEdges(BasicBlock* block)
         else if (sameToReg != fromReg)
         {
             VarSetOps::AddElemD(compiler, sameResolutionSet, varIndex);
-            sameVarToRegMap[varIndex] = sameToReg;
+            setVarReg(sameVarToRegMap, varIndex, sameToReg);
             if (sameToReg != REG_STK)
             {
                 sameWriteRegs |= genRegMask(sameToReg);
@@ -9951,11 +10117,11 @@ void LinearScan::resolveEdge(BasicBlock*      fromBlock,
         // 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);
@@ -10339,6 +10505,10 @@ void LinearScan::dumpLsraStats(FILE* file)
         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);
@@ -10936,7 +11106,8 @@ void LinearScan::TupleStyleDump(LsraTupleDumpMode mode)
             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);
@@ -11144,7 +11315,7 @@ void LinearScan::TupleStyleDump(LsraTupleDumpMode mode)
                 printf("\n");
             }
         }
-        if (mode == LSRA_DUMP_POST)
+        if (enregisterLocalVars && mode == LSRA_DUMP_POST)
         {
             dumpOutVarToRegMap(block);
         }
@@ -11991,6 +12162,7 @@ void LinearScan::verifyFinalAllocation()
                     // 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))
@@ -12000,16 +12172,24 @@ void LinearScan::verifyFinalAllocation()
                     }
 
                     // 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.
@@ -12025,16 +12205,24 @@ void LinearScan::verifyFinalAllocation()
 
                 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)
@@ -12052,6 +12240,7 @@ void LinearScan::verifyFinalAllocation()
                     {
                         if (IsResolutionNode(currentBlockRange, node))
                         {
+                            assert(enregisterLocalVars);
                             if (foundNonResolutionNode)
                             {
                                 firstBlockEndResolutionNode = node;
@@ -12249,6 +12438,9 @@ void LinearScan::verifyFinalAllocation()
     {
         if (currentBlock->bbNum > bbNumMaxBeforeResolution)
         {
+            // If we haven't enregistered an lclVars, we have no resolution blocks.
+            assert(enregisterLocalVars);
+
             if (VERBOSE)
             {
                 dumpRegRecordTitle();
@@ -12270,6 +12462,11 @@ void LinearScan::verifyFinalAllocation()
             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;
@@ -12297,6 +12494,11 @@ void LinearScan::verifyFinalAllocation()
                 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));
index b660048ce4c95502473ad5574be0118ce6549807..b59c808ddecac236a8451a802351c799c0f7280d 100644 (file)
@@ -121,7 +121,7 @@ inline bool RefTypeIsDef(RefType refType)
     return ((refType & RefTypeDef) == RefTypeDef);
 }
 
-typedef regNumber* VarToRegMap;
+typedef regNumberSmall* VarToRegMap;
 
 template <typename ElementType, CompMemKind MemKind>
 class ListElementAllocator
@@ -854,6 +854,7 @@ private:
     Interval* getIntervalForLocalVar(unsigned varIndex)
     {
         assert(varIndex < compiler->lvaTrackedCount);
+        assert(localVarIntervals[varIndex] != nullptr);
         return localVarIntervals[varIndex];
     }
 
@@ -958,6 +959,7 @@ private:
     void setOutVarRegForBB(unsigned int bbNum, unsigned int varNum, regNumber reg);
     VarToRegMap getInVarToRegMap(unsigned int bbNum);
     VarToRegMap getOutVarToRegMap(unsigned int bbNum);
+    void setVarReg(VarToRegMap map, unsigned int trackedVarIndex, regNumber reg);
     regNumber getVarReg(VarToRegMap map, unsigned int trackedVarIndex);
     // Initialize the incoming VarToRegMap to the given map values (generally a predecessor of
     // the block)
@@ -1073,6 +1075,7 @@ private:
         LSRA_STAT_SPILL, LSRA_STAT_COPY_REG, LSRA_STAT_RESOLUTION_MOV, LSRA_STAT_SPLIT_EDGE,
     };
 
+    unsigned regCandidateVarCount;
     void updateLsraStat(LsraStat stat, unsigned currentBBNum);
 
     void dumpLsraStats(FILE* file);
@@ -1162,6 +1165,9 @@ private:
     // True if the method contains any critical edges.
     bool hasCriticalEdges;
 
+    // True if there are any register candidate lclVars available for allocation.
+    bool enregisterLocalVars;
+
     // Ordered list of RefPositions
     RefPositionList refPositions;
 
@@ -1177,8 +1183,10 @@ private:
     PhasedVar<regMaskTP> availableFloatRegs;
     PhasedVar<regMaskTP> availableDoubleRegs;
 
-    // Current set of live tracked vars, used during building of RefPositions to determine whether
-    // to preference to callee-save
+    // The set of all register candidates. Note that this may be a subset of tracked vars.
+    VARSET_TP registerCandidateVars;
+    // Current set of live register candidate vars, used during building of RefPositions to determine
+    // whether to preference to callee-save.
     VARSET_TP currentLiveVars;
     // Set of variables that may require resolution across an edge.
     // This is first constructed during interval building, to contain all the lclVars that are live at BB edges.