Include register selection heuristics in "Allocating Registers" table (#52513)
authorKunal Pathak <Kunal.Pathak@microsoft.com>
Mon, 10 May 2021 21:46:11 +0000 (03:16 +0530)
committerGitHub <noreply@github.com>
Mon, 10 May 2021 21:46:11 +0000 (14:46 -0700)
* Print Heuristic used to allocate register in table

* some more fixes to heuristics

* Fixed all problems

* Better formatting

* cleanup

* Rename RegSel_ to STAT_

src/coreclr/jit/lsra.cpp
src/coreclr/jit/lsra.h
src/coreclr/jit/lsra_score.h [new file with mode: 0644]
src/coreclr/jit/lsra_stats.h [new file with mode: 0644]
src/coreclr/jit/lsrastats.h [deleted file]

index 5eeef1c..779e241 100644 (file)
@@ -2753,9 +2753,13 @@ bool LinearScan::isMatchingConstant(RegRecord* physRegRecord, RefPosition* refPo
 //        no such ref position, no register will be allocated.
 //
 
-regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPosition)
+regNumber LinearScan::allocateReg(Interval*    currentInterval,
+                                  RefPosition* refPosition DEBUG_ARG(RegisterScore* registerScore))
 {
     regNumber foundReg = REG_NA;
+#ifdef DEBUG
+    *registerScore = NONE;
+#endif
 
     RegisterType regType = getRegisterType(currentInterval, refPosition);
 
@@ -2958,39 +2962,6 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos
 
     RegRecord* availablePhysRegRecord = nullptr;
 
-    // Each register will receive a score which takes into account the scoring criteria below.
-    // These were selected on the assumption that they will have an impact on the "goodness"
-    // of a register selection, and have been tuned to a certain extent by observing the impact
-    // of the ordering on asmDiffs.  However, there is much more room for tuning,
-    // and perhaps additional criteria.
-    //
-    enum RegisterScore
-    {
-        FREE = 0x10000, // It is not currently assigned to an *active* interval
-
-        // These are the original criteria for comparing registers that are free.
-        CONST_AVAILABLE = 0x8000, // It is a constant value that is already in an acceptable register.
-        THIS_ASSIGNED   = 0x4000, // It is in the interval's preference set and it is already assigned to this interval.
-        COVERS          = 0x2000, // It is in the interval's preference set and it covers the current range.
-        OWN_PREFERENCE  = 0x1000, // It is in the preference set of this interval.
-        COVERS_RELATED  = 0x0800, // It is in the preference set of the related interval and covers its entire lifetime.
-        RELATED_PREFERENCE = 0x0400, // It is in the preference set of the related interval.
-        CALLER_CALLEE      = 0x0200, // It is in the right "set" for the interval (caller or callee-save).
-        UNASSIGNED         = 0x0100, // It is not currently assigned to any (active or inactive) interval
-        COVERS_FULL        = 0x0080, // It covers the full range of the interval from current position to the end.
-        BEST_FIT           = 0x0040, // The available range is the closest match to the full range of the interval.
-        IS_PREV_REG        = 0x0020, // This register was previously assigned to the interval.
-        REG_ORDER          = 0x0010, // Tie-breaker
-
-        // These are the original criteria for comparing registers that are in use.
-        SPILL_COST   = 0x0008, // It has the lowest cost of all the candidates.
-        FAR_NEXT_REF = 0x0004, // It has a farther next reference than the best candidate thus far.
-        PREV_REG_OPT = 0x0002, // The previous RefPosition of its current assigned interval is RegOptional.
-
-        // TODO-CQ: Consider using REG_ORDER as a tie-breaker even for busy registers.
-        REG_NUM = 0x0001, // It has a lower register number.
-    };
-
     // These are used in the post-selection updates, and must be set for any selection.
     regMaskTP freeCandidates    = RBM_NONE;
     regMaskTP matchingConstants = RBM_NONE;
@@ -3054,6 +3025,9 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos
         {
             candidates = prevRegBit;
             found      = true;
+#ifdef DEBUG
+            *registerScore = THIS_ASSIGNED;
+#endif
         }
     }
 
@@ -3095,8 +3069,8 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos
     }
     else if (!found)
     {
-        found = selector.applySelection(FREE, freeCandidates);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_FREE, refPosition->bbNum));
+        found = selector.applySelection(FREE, freeCandidates DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_FREE, refPosition->bbNum));
     }
 
     // Apply the CONST_AVAILABLE (matching constant) heuristic. Only applies if we have freeCandidates.
@@ -3106,16 +3080,17 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos
         if (currentInterval->isConstant && RefTypeIsDef(refPosition->refType))
         {
             matchingConstants = getMatchingConstants(selector.candidates, currentInterval, refPosition);
-            found             = selector.applySelection(CONST_AVAILABLE, matchingConstants);
-            INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_CONST_AVAILABLE, refPosition->bbNum));
+            found             = selector.applySelection(CONST_AVAILABLE, matchingConstants DEBUG_ARG(registerScore));
+            INTRACK_STATS_IF(found, updateLsraStat(STAT_CONST_AVAILABLE, refPosition->bbNum));
         }
     }
 
     // Apply the THIS_ASSIGNED heuristic. Only applies if we have freeCandidates.
     if (!found && (prevRegRec != nullptr) && (freeCandidates != RBM_NONE))
     {
-        found = selector.applySelection(THIS_ASSIGNED, freeCandidates & preferences & prevRegBit);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_THIS_ASSIGNED, refPosition->bbNum));
+        found =
+            selector.applySelection(THIS_ASSIGNED, freeCandidates & preferences & prevRegBit DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_THIS_ASSIGNED, refPosition->bbNum));
     }
 
     // Compute the sets for COVERS, OWN_PREFERENCE, COVERS_RELATED, COVERS_FULL and UNASSIGNED together,
@@ -3192,8 +3167,8 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos
     // Apply the COVERS heuristic.
     if (!found)
     {
-        found = selector.applySelection(COVERS, coversSet & preferenceSet);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_COVERS, refPosition->bbNum));
+        found = selector.applySelection(COVERS, coversSet & preferenceSet DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_COVERS, refPosition->bbNum));
     }
 
     // Apply the OWN_PREFERENCE heuristic.
@@ -3201,45 +3176,46 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos
     if (!found)
     {
         assert((preferenceSet & freeCandidates) == preferenceSet);
-        found = selector.applySelection(OWN_PREFERENCE, preferenceSet);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_OWN_PREFERENCE, refPosition->bbNum));
+        found = selector.applySelection(OWN_PREFERENCE, preferenceSet DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_OWN_PREFERENCE, refPosition->bbNum));
     }
 
     // Apply the COVERS_RELATED heuristic.
     if (!found)
     {
         assert((coversRelatedSet & freeCandidates) == coversRelatedSet);
-        found = selector.applySelection(COVERS_RELATED, coversRelatedSet);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_COVERS_RELATED, refPosition->bbNum));
+        found = selector.applySelection(COVERS_RELATED, coversRelatedSet DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_COVERS_RELATED, refPosition->bbNum));
     }
 
     // Apply the RELATED_PREFERENCE heuristic.
     if (!found)
     {
-        found = selector.applySelection(RELATED_PREFERENCE, relatedPreferences & freeCandidates);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_RELATED_PREFERENCE, refPosition->bbNum));
+        found =
+            selector.applySelection(RELATED_PREFERENCE, relatedPreferences & freeCandidates DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_RELATED_PREFERENCE, refPosition->bbNum));
     }
 
     // Apply the CALLER_CALLEE heuristic.
     if (!found)
     {
-        found = selector.applySelection(CALLER_CALLEE, callerCalleePrefs & freeCandidates);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_CALLER_CALLEE, refPosition->bbNum));
+        found = selector.applySelection(CALLER_CALLEE, callerCalleePrefs & freeCandidates DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_CALLER_CALLEE, refPosition->bbNum));
     }
 
     // Apply the UNASSIGNED heuristic.
     if (!found)
     {
-        found = selector.applySelection(UNASSIGNED, unassignedSet);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_UNASSIGNED, refPosition->bbNum));
+        found = selector.applySelection(UNASSIGNED, unassignedSet DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_UNASSIGNED, refPosition->bbNum));
     }
 
     // Apply the COVERS_FULL heuristic.
     if (!found)
     {
         assert((coversFullSet & freeCandidates) == coversFullSet);
-        found = selector.applySelection(COVERS_FULL, coversFullSet);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_COVERS_FULL, refPosition->bbNum));
+        found = selector.applySelection(COVERS_FULL, coversFullSet DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_COVERS_FULL, refPosition->bbNum));
     }
 
     // Apply the BEST_FIT heuristic. Only applies if we have freeCandidates.
@@ -3304,16 +3280,16 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos
             }
         }
         assert(bestFitSet != RBM_NONE);
-        found = selector.applySelection(BEST_FIT, bestFitSet);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_BEST_FIT, refPosition->bbNum));
+        found = selector.applySelection(BEST_FIT, bestFitSet DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_BEST_FIT, refPosition->bbNum));
     }
 
     // Apply the IS_PREV_REG heuristic. TODO: Check if Only applies if we have freeCandidates.
     // Oddly, the previous heuristics only considered this if it covered the range.
     if ((prevRegRec != nullptr) && ((selector.score & COVERS_FULL) != 0))
     {
-        found = selector.applySingleRegSelection(IS_PREV_REG, prevRegBit);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_IS_PREV_REG, refPosition->bbNum));
+        found = selector.applySingleRegSelection(IS_PREV_REG, prevRegBit DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_IS_PREV_REG, refPosition->bbNum));
     }
 
     // Apply the REG_ORDER heuristic. Only applies if we have freeCandidates.
@@ -3337,8 +3313,8 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos
             }
         }
         assert(lowestRegOrderBit != RBM_NONE);
-        found = selector.applySingleRegSelection(REG_ORDER, lowestRegOrderBit);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_REG_ORDER, refPosition->bbNum));
+        found = selector.applySingleRegSelection(REG_ORDER, lowestRegOrderBit DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_REG_ORDER, refPosition->bbNum));
     }
 
     // The set of registers with the lowest spill weight.
@@ -3401,8 +3377,8 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos
     {
         // We must have at least one with the lowest spill cost.
         assert(lowestCostSpillSet != RBM_NONE);
-        found = selector.applySelection(SPILL_COST, lowestCostSpillSet);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_SPILL_COST, refPosition->bbNum));
+        found = selector.applySelection(SPILL_COST, lowestCostSpillSet DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_SPILL_COST, refPosition->bbNum));
     }
 
     // Apply the FAR_NEXT_REF heuristic.
@@ -3432,8 +3408,8 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos
         }
         // We must have at least one with the lowest spill cost.
         assert(farthestSet != RBM_NONE);
-        found = selector.applySelection(FAR_NEXT_REF, farthestSet);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_FAR_NEXT_REF, refPosition->bbNum));
+        found = selector.applySelection(FAR_NEXT_REF, farthestSet DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_FAR_NEXT_REF, refPosition->bbNum));
     }
 
     // Apply the PREV_REG_OPT heuristic.
@@ -3511,15 +3487,16 @@ regNumber LinearScan::allocateReg(Interval* currentInterval, RefPosition* refPos
             }
 #endif
         }
-        found = selector.applySelection(PREV_REG_OPT, prevRegOptSet);
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_PREV_REG_OPT, refPosition->bbNum));
+        found = selector.applySelection(PREV_REG_OPT, prevRegOptSet DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_PREV_REG_OPT, refPosition->bbNum));
     }
 
     // Apply the REG_NUM heuristic.
     if (!found)
     {
-        found = selector.applySingleRegSelection(REG_NUM, genFindLowestBit(selector.candidates));
-        INTRACK_STATS_IF(found, updateLsraStat(LsraStat::REGSEL_REG_NUM, refPosition->bbNum));
+        found =
+            selector.applySingleRegSelection(REG_NUM, genFindLowestBit(selector.candidates) DEBUG_ARG(registerScore));
+        INTRACK_STATS_IF(found, updateLsraStat(STAT_REG_NUM, refPosition->bbNum));
     }
 
     assert(found && isSingleRegister(selector.candidates));
@@ -3812,7 +3789,11 @@ regNumber LinearScan::assignCopyReg(RefPosition* refPosition)
     // refPosition->RegOptional() will return false.
     refPosition->copyReg = true;
 
-    regNumber allocatedReg = allocateReg(currentInterval, refPosition);
+    RegisterScore registerScore = NONE;
+    regNumber allocatedReg      = allocateReg(currentInterval, refPosition DEBUG_ARG(&registerScore));
+    assert(allocatedReg != REG_NA);
+
+    INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_COPY_REG, currentInterval, allocatedReg, nullptr, registerScore));
 
     // Now restore the old info
     currentInterval->relatedInterval = savedRelatedInterval;
@@ -5983,9 +5964,7 @@ void LinearScan::allocateRegisters()
                 // It's already in a register, but not one we need.
                 if (!RefTypeIsDef(currentRefPosition->refType))
                 {
-                    regNumber copyReg = assignCopyReg(currentRefPosition);
-                    assert(copyReg != REG_NA);
-                    INDEBUG(dumpLsraAllocationEvent(LSRA_EVENT_COPY_REG, currentInterval, copyReg));
+                    regNumber copyReg        = assignCopyReg(currentRefPosition);
                     lastAllocatedRefPosition = currentRefPosition;
                     bool unassign            = false;
                     if (currentInterval->isWriteThru)
@@ -6095,6 +6074,7 @@ void LinearScan::allocateRegisters()
 #endif
             }
 
+            RegisterScore registerScore = NONE;
             if (allocate)
             {
                 // Allocate a register, if we must, or if it is profitable to do so.
@@ -6107,7 +6087,7 @@ void LinearScan::allocateRegisters()
                 {
                     unassignPhysReg(currentInterval->assignedReg, nullptr);
                 }
-                assignedRegister = allocateReg(currentInterval, currentRefPosition);
+                assignedRegister = allocateReg(currentInterval, currentRefPosition DEBUG_ARG(&registerScore));
             }
 
             // If no register was found, this RefPosition must not require a register.
@@ -6128,11 +6108,13 @@ void LinearScan::allocateRegisters()
                     if (currentInterval->isConstant && (currentRefPosition->treeNode != nullptr) &&
                         currentRefPosition->treeNode->IsReuseRegVal())
                     {
-                        dumpLsraAllocationEvent(LSRA_EVENT_REUSE_REG, currentInterval, assignedRegister, currentBlock);
+                        dumpLsraAllocationEvent(LSRA_EVENT_REUSE_REG, currentInterval, assignedRegister, currentBlock,
+                                                registerScore);
                     }
                     else
                     {
-                        dumpLsraAllocationEvent(LSRA_EVENT_ALLOC_REG, currentInterval, assignedRegister, currentBlock);
+                        dumpLsraAllocationEvent(LSRA_EVENT_ALLOC_REG, currentInterval, assignedRegister, currentBlock,
+                                                registerScore);
                     }
                 }
             }
@@ -9306,20 +9288,25 @@ void LinearScan::resolveEdge(BasicBlock*      fromBlock,
 
 #if TRACK_LSRA_STATS
 
+#if TRACK_LSRA_STATS
 const char* LinearScan::getStatName(unsigned stat)
 {
     LsraStat lsraStat = (LsraStat)stat;
-    assert(lsraStat != COUNT);
+    assert(lsraStat != LsraStat::COUNT);
 
     static const char* const lsraStatNames[] = {
 #define LSRA_STAT_DEF(stat, name) name,
-#include "lsrastats.h"
-    };
+#include "lsra_stats.h"
 #undef LSRA_STAT_DEF
+#define REG_SEL_DEF(stat, value, shortname) #stat,
+#include "lsra_score.h"
+#undef REG_SEL_DEF
+    };
 
     assert(stat < ArrLen(lsraStatNames));
     return lsraStatNames[lsraStat];
 }
+#endif // TRACK_LSRA_STATS
 
 // ----------------------------------------------------------
 // updateLsraStat: Increment LSRA stat counter.
@@ -9453,7 +9440,7 @@ void LinearScan::dumpLsraStats(FILE* file)
     }
 
     fprintf(file, "..........\n");
-    for (int regSelectI = 0; regSelectI < COUNT; regSelectI++)
+    for (int regSelectI = 0; regSelectI < LsraStat::COUNT; regSelectI++)
     {
         if (regSelectI == firstRegSelStat)
         {
@@ -9582,6 +9569,22 @@ static const char* getRefTypeShortName(RefType refType)
     }
 }
 
+//------------------------------------------------------------------------
+// getScoreName: Returns the texual name of register score
+const char* LinearScan::getScoreName(RegisterScore score)
+{
+    switch (score)
+    {
+#define REG_SEL_DEF(stat, value, shortname)                                                                            \
+    case stat:                                                                                                         \
+        return shortname;
+#include "lsra_score.h"
+#undef REG_SEL_DEF
+        default:
+            return "  -  ";
+    }
+}
+
 void RefPosition::dump()
 {
     printf("<RefPosition #%-3u @%-3u", rpNum, nodeLocation);
@@ -10280,10 +10283,8 @@ void LinearScan::TupleStyleDump(LsraTupleDumpMode mode)
     printf("\n\n");
 }
 
-void LinearScan::dumpLsraAllocationEvent(LsraDumpEvent event,
-                                         Interval*     interval,
-                                         regNumber     reg,
-                                         BasicBlock*   currentBlock)
+void LinearScan::dumpLsraAllocationEvent(
+    LsraDumpEvent event, Interval* interval, regNumber reg, BasicBlock* currentBlock, RegisterScore registerScore)
 {
     if (!(VERBOSE))
     {
@@ -10342,7 +10343,7 @@ void LinearScan::dumpLsraAllocationEvent(LsraDumpEvent event,
         case LSRA_EVENT_SPILL:
             dumpRefPositionShort(activeRefPosition, currentBlock);
             assert(interval != nullptr && interval->assignedReg != nullptr);
-            printf("Spill %-4s ", getRegName(interval->assignedReg->regNum));
+            printf("Spill    %-4s ", getRegName(interval->assignedReg->regNum));
             dumpRegRecords();
             break;
 
@@ -10358,7 +10359,8 @@ void LinearScan::dumpLsraAllocationEvent(LsraDumpEvent event,
             {
                 dumpRefPositionShort(activeRefPosition, currentBlock);
             }
-            printf((event == LSRA_EVENT_RESTORE_PREVIOUS_INTERVAL) ? "Restr %-4s " : "SRstr %-4s ", getRegName(reg));
+            printf((event == LSRA_EVENT_RESTORE_PREVIOUS_INTERVAL) ? "Restr    %-4s " : "SRstr    %-4s ",
+                   getRegName(reg));
             dumpRegRecords();
             break;
 
@@ -10407,63 +10409,84 @@ void LinearScan::dumpLsraAllocationEvent(LsraDumpEvent event,
         case LSRA_EVENT_EXP_USE:
         case LSRA_EVENT_KEPT_ALLOCATION:
             dumpRefPositionShort(activeRefPosition, currentBlock);
-            printf("Keep  %-4s ", getRegName(reg));
+            printf("Keep     %-4s ", getRegName(reg));
             break;
 
         case LSRA_EVENT_COPY_REG:
             assert(interval != nullptr && interval->recentRefPosition != nullptr);
             dumpRefPositionShort(activeRefPosition, currentBlock);
-            printf("Copy  %-4s ", getRegName(reg));
+            if (allocationPassComplete || (registerScore == 0))
+            {
+                printf("Copy     %-4s ", getRegName(reg));
+            }
+            else
+            {
+                printf("%-5s(C) %-4s ", getScoreName(registerScore), getRegName(reg));
+            }
             break;
 
         case LSRA_EVENT_MOVE_REG:
             assert(interval != nullptr && interval->recentRefPosition != nullptr);
             dumpRefPositionShort(activeRefPosition, currentBlock);
-            printf("Move  %-4s ", getRegName(reg));
+            printf("Move     %-4s ", getRegName(reg));
             dumpRegRecords();
             break;
 
         case LSRA_EVENT_ALLOC_REG:
             dumpRefPositionShort(activeRefPosition, currentBlock);
-            printf("Alloc %-4s ", getRegName(reg));
+            if (allocationPassComplete || (registerScore == 0))
+            {
+                printf("Alloc    %-4s ", getRegName(reg));
+            }
+            else
+            {
+                printf("%-5s(A) %-4s ", getScoreName(registerScore), getRegName(reg));
+            }
+
             break;
 
         case LSRA_EVENT_REUSE_REG:
             dumpRefPositionShort(activeRefPosition, currentBlock);
-            printf("Reuse %-4s ", getRegName(reg));
+            if (allocationPassComplete || (registerScore == 0))
+            {
+                printf("Reuse     %-4s ", getRegName(reg));
+            }
+            else
+            {
+                printf("%-5s(A) %-4s ", getScoreName(registerScore), getRegName(reg));
+            }
             break;
 
         case LSRA_EVENT_NO_ENTRY_REG_ALLOCATED:
             assert(interval != nullptr && interval->isLocalVar);
             dumpRefPositionShort(activeRefPosition, currentBlock);
-            printf("LoRef      ");
+            printf("LoRef         ");
             break;
 
         case LSRA_EVENT_NO_REG_ALLOCATED:
             dumpRefPositionShort(activeRefPosition, currentBlock);
-            printf("NoReg      ");
+            printf("NoReg         ");
             break;
 
         case LSRA_EVENT_RELOAD:
             dumpRefPositionShort(activeRefPosition, currentBlock);
-            printf("ReLod %-4s ", getRegName(reg));
+            printf("ReLod    %-4s ", getRegName(reg));
             dumpRegRecords();
             break;
 
         case LSRA_EVENT_SPECIAL_PUTARG:
             dumpRefPositionShort(activeRefPosition, currentBlock);
-            printf("PtArg %-4s ", getRegName(reg));
+            printf("PtArg    %-4s ", getRegName(reg));
             break;
 
         case LSRA_EVENT_UPPER_VECTOR_SAVE:
             dumpRefPositionShort(activeRefPosition, currentBlock);
-            printf("UVSav %-4s ", getRegName(reg));
+            printf("UVSav    %-4s ", getRegName(reg));
             break;
 
         case LSRA_EVENT_UPPER_VECTOR_RESTORE:
             dumpRefPositionShort(activeRefPosition, currentBlock);
-            printf("UVRes %-4s ", getRegName(reg));
-            dumpRegRecords();
+            printf("UVRes    %-4s ", getRegName(reg));
             break;
 
         // We currently don't dump anything for these events.
@@ -10477,7 +10500,7 @@ void LinearScan::dumpLsraAllocationEvent(LsraDumpEvent event,
             break;
 
         default:
-            printf("????? %-4s ", getRegName(reg));
+            printf("?????    %-4s ", getRegName(reg));
             dumpRegRecords();
             break;
     }
@@ -10553,11 +10576,11 @@ void LinearScan::dumpRegRecordHeader()
     sprintf_s(emptyRefPositionFormat, MAX_FORMAT_CHARS, "%%-%ds", shortRefPositionDumpWidth);
 
     // The width of the "allocation info"
-    //  - a 5-character allocation decision
+    //  - a 8-character allocation decision
     //  - a space
     //  - a 4-character register
     //  - a space
-    int allocationInfoWidth = 5 + 1 + 4 + 1;
+    int allocationInfoWidth = 8 + 1 + 4 + 1;
 
     // Next, determine the width of the legend for each row.  This includes:
     //  - a short RefPosition dump (shortRefPositionDumpWidth), which includes a space
@@ -10648,7 +10671,7 @@ void LinearScan::dumpRegRecordTitle()
     dumpRegRecordTitleLines();
 
     // Print out the legend for the RefPosition info
-    printf(legendFormat, "Loc ", "RP# ", "Name ", "Type  Action Reg  ");
+    printf(legendFormat, "Loc ", "RP# ", "Name ", "Type  Action    Reg  ");
 
     // Print out the register name column headers
     char columnFormatArray[MAX_FORMAT_CHARS];
@@ -10764,7 +10787,7 @@ void LinearScan::dumpNewBlock(BasicBlock* currentBlock, LsraLocation location)
     if (currentBlock == nullptr)
     {
         printf(regNameFormat, "END");
-        printf("              ");
+        printf("                 ");
         printf(regNameFormat, "");
     }
     else
@@ -11166,7 +11189,7 @@ void LinearScan::verifyFinalAllocation()
                     if (VERBOSE)
                     {
                         dumpEmptyRefPosition();
-                        printf("Move  %-4s ", getRegName(regRecord->regNum));
+                        printf("Move     %-4s ", getRegName(regRecord->regNum));
                     }
                 }
                 else
@@ -11196,11 +11219,11 @@ void LinearScan::verifyFinalAllocation()
                             dumpEmptyRefPosition();
                             if (currentRefPosition->writeThru)
                             {
-                                printf("WThru %-4s ", getRegName(spillReg));
+                                printf("WThru    %-4s ", getRegName(spillReg));
                             }
                             else
                             {
-                                printf("Spill %-4s ", getRegName(spillReg));
+                                printf("Spill    %-4s ", getRegName(spillReg));
                             }
                         }
                     }
@@ -11247,7 +11270,7 @@ void LinearScan::verifyFinalAllocation()
             case RefTypeDummyDef:
                 // Do nothing; these will be handled by the RefTypeBB.
                 DBEXEC(VERBOSE, dumpRefPositionShort(currentRefPosition, currentBlock));
-                DBEXEC(VERBOSE, printf("           "));
+                DBEXEC(VERBOSE, printf("              "));
                 break;
 
             case RefTypeInvalid:
@@ -11493,7 +11516,7 @@ void LinearScan::verifyResolutionMove(GenTree* resolutionMove, LsraLocation curr
     {
         printf(shortRefPositionFormat, currentLocation, 0);
         dumpIntervalName(interval);
-        printf("  Move   ");
+        printf("  Move      ");
         printf("      %-4s ", getRegName(dstRegNum));
         dumpRegRecords();
     }
index e6f4aea..1cf89d4 100644 (file)
@@ -379,8 +379,12 @@ public:
 enum LsraStat
 {
 #define LSRA_STAT_DEF(enum_name, enum_str) enum_name,
-#include "lsrastats.h"
+#include "lsra_stats.h"
 #undef LSRA_STAT_DEF
+#define REG_SEL_DEF(enum_name, value, short_str) STAT_##enum_name,
+#include "lsra_score.h"
+#undef REG_SEL_DEF
+    COUNT
 };
 #endif // TRACK_LSRA_STATS
 
@@ -402,6 +406,14 @@ struct LsraBlockInfo
 #endif // TRACK_LSRA_STATS
 };
 
+enum RegisterScore
+{
+#define REG_SEL_DEF(enum_name, value, short_str) enum_name = value,
+#include "lsra_score.h"
+#undef REG_SEL_DEF
+    NONE = 0
+};
+
 // This is sort of a bit mask
 // The low order 2 bits will be 1 for defs, and 2 for uses
 enum RefType : unsigned char
@@ -1123,7 +1135,10 @@ private:
      ****************************************************************************/
     RegisterType getRegisterType(Interval* currentInterval, RefPosition* refPosition);
 
-    regNumber allocateReg(Interval* current, RefPosition* refPosition);
+#ifdef DEBUG
+    const char* getScoreName(RegisterScore score);
+#endif
+    regNumber allocateReg(Interval* current, RefPosition* refPosition DEBUG_ARG(RegisterScore* registerScore));
     regNumber assignCopyReg(RefPosition* refPosition);
 
     bool isMatchingConstant(RegRecord* physRegRecord, RefPosition* refPosition);
@@ -1177,21 +1192,29 @@ private:
 #endif // TARGET_ARM
 
         // Apply a simple mask-based selection heuristic, and return 'true' if we now have a single candidate.
-        bool applySelection(int selectionScore, regMaskTP selectionCandidates)
+        bool applySelection(int selectionScore, regMaskTP selectionCandidates DEBUG_ARG(RegisterScore* registerScore))
         {
             regMaskTP newCandidates = candidates & selectionCandidates;
             if (newCandidates != RBM_NONE)
             {
                 score += selectionScore;
                 candidates = newCandidates;
-                return isSingleRegister(candidates);
+                bool found = isSingleRegister(candidates);
+#ifdef DEBUG
+                if (found)
+                {
+                    *registerScore = (RegisterScore)selectionScore;
+                }
+#endif
+                return found;
             }
             return false;
         }
 
         // Select a single register, if it is in the candidate set.
         // Return true if so.
-        bool applySingleRegSelection(int selectionScore, regMaskTP selectionCandidate)
+        bool applySingleRegSelection(int       selectionScore,
+                                     regMaskTP selectionCandidate DEBUG_ARG(RegisterScore* registerScore))
         {
             assert(isSingleRegister(selectionCandidate));
             regMaskTP newCandidates = candidates & selectionCandidate;
@@ -1199,6 +1222,9 @@ private:
             {
                 score += selectionScore;
                 candidates = newCandidates;
+#ifdef DEBUG
+                *registerScore = (RegisterScore)selectionScore;
+#endif
                 return true;
             }
             return false;
@@ -1348,9 +1374,10 @@ private:
         LSRA_EVENT_NO_REG_ALLOCATED, LSRA_EVENT_RELOAD, LSRA_EVENT_SPECIAL_PUTARG, LSRA_EVENT_REUSE_REG,
     };
     void dumpLsraAllocationEvent(LsraDumpEvent event,
-                                 Interval*     interval     = nullptr,
-                                 regNumber     reg          = REG_NA,
-                                 BasicBlock*   currentBlock = nullptr);
+                                 Interval*     interval      = nullptr,
+                                 regNumber     reg           = REG_NA,
+                                 BasicBlock*   currentBlock  = nullptr,
+                                 RegisterScore registerScore = NONE);
 
     void validateIntervals();
 #endif // DEBUG
@@ -1359,7 +1386,7 @@ private:
     unsigned regCandidateVarCount;
     void updateLsraStat(LsraStat stat, unsigned currentBBNum);
     void dumpLsraStats(FILE* file);
-    LsraStat firstRegSelStat = LsraStat::REGSEL_FREE;
+    LsraStat firstRegSelStat = STAT_FREE;
 
 public:
     virtual void dumpLsraStatsCsv(FILE* file);
diff --git a/src/coreclr/jit/lsra_score.h b/src/coreclr/jit/lsra_score.h
new file mode 100644 (file)
index 0000000..6cc6670
--- /dev/null
@@ -0,0 +1,45 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// clang-format off
+
+/*****************************************************************************/
+/*****************************************************************************/
+
+#ifndef REG_SEL_DEF
+#error  Must define REG_SEL_DEF macro before including this file
+#endif
+
+// Register selection stats
+// Each register will receive a score which takes into account the scoring criteria below.
+// These were selected on the assumption that they will have an impact on the "goodness"
+// of a register selection, and have been tuned to a certain extent by observing the impact
+// of the ordering on asmDiffs.  However, there is much more room for tuning,
+// and perhaps additional criteria.
+//
+//          name                score       short_name
+REG_SEL_DEF(FREE,               0x10000,    "FREE ")    // It is not currently assigned to an *active* interval
+
+// These are the original criteria for comparing registers that are free.
+REG_SEL_DEF(CONST_AVAILABLE,    0x08000,    "CONST")   // It is a constant value that is already in an acceptable register.
+REG_SEL_DEF(THIS_ASSIGNED,      0x04000,    "THISA")   // It is in the interval's preference set and it is already assigned to this interval.
+REG_SEL_DEF(COVERS,             0x02000,    "COVRS")   // It is in the interval's preference set and it covers the current range.
+REG_SEL_DEF(OWN_PREFERENCE,     0x01000,    "OWNPR")   // It is in the preference set of this interval.
+REG_SEL_DEF(COVERS_RELATED,     0x00800,    "COREL")   // It is in the preference set of the related interval and covers its entire lifetime.
+REG_SEL_DEF(RELATED_PREFERENCE, 0x00400,    "RELPR")   // It is in the preference set of the related interval.
+REG_SEL_DEF(CALLER_CALLEE,      0x00200,    "CRCE ")   // It is in the right "set" for the interval (caller or callee-save).
+REG_SEL_DEF(UNASSIGNED,         0x00100,    "UNASG")   // It is not currently assigned to any (active or inactive) interval
+REG_SEL_DEF(COVERS_FULL,        0x00080,    "COFUL")   // It covers the full range of the interval from current position to the end.
+REG_SEL_DEF(BEST_FIT,           0x00040,    "BSFIT")   // The available range is the closest match to the full range of the interval.
+REG_SEL_DEF(IS_PREV_REG,        0x00020,    "PRVRG")   // This register was previously assigned to the interval.
+REG_SEL_DEF(REG_ORDER,          0x00010,    "ORDER")   // Tie-breaker
+
+// These are the original criteria for comparing registers that are in use.
+REG_SEL_DEF(SPILL_COST,         0x00008,    "SPILL")   // It has the lowest cost of all the candidates.
+REG_SEL_DEF(FAR_NEXT_REF,       0x00004,    "FNREF")   // It has a farther next reference than the best candidate thus far.
+REG_SEL_DEF(PREV_REG_OPT,       0x00002,    "PRGOP")   // The previous RefPosition of its current assigned interval is RegOptional.
+
+// TODO-CQ: Consider using REG_ORDER as a tie-breaker even for busy registers.
+REG_SEL_DEF(REG_NUM,            0x00001,    "RGNUM")   // It has a lower register number.
+
+// clang-format on
diff --git a/src/coreclr/jit/lsra_stats.h b/src/coreclr/jit/lsra_stats.h
new file mode 100644 (file)
index 0000000..1df27cf
--- /dev/null
@@ -0,0 +1,31 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+// clang-format off
+
+/*****************************************************************************/
+/*****************************************************************************/
+#ifndef LSRA_STAT_DEF
+#error  Must define LSRA_STAT_DEF macro before including this file
+#endif
+
+#if TRACK_LSRA_STATS
+
+// Number of spills of local vars or tree temps in this basic block.
+LSRA_STAT_DEF(STAT_SPILL,               "SpillCount")
+
+// Number of GT_COPY nodes inserted in this basic block while allocating regs.
+// Note that GT_COPY nodes are also inserted as part of basic block boundary
+// resolution, which are accounted against resolutionMovCount but not
+// against copyRegCount.
+LSRA_STAT_DEF(STAT_COPY_REG,             "CopyReg")
+
+// Number of resolution moves inserted in this basic block.
+LSRA_STAT_DEF(STAT_RESOLUTION_MOV,       "ResolutionMovs")
+
+// Number of critical edges from this block that are split.
+LSRA_STAT_DEF(STAT_SPLIT_EDGE,           "SplitEdges")
+
+#endif // TRACK_LSRA_STATS
+
+// clang-format on
diff --git a/src/coreclr/jit/lsrastats.h b/src/coreclr/jit/lsrastats.h
deleted file mode 100644 (file)
index 847dc67..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-// clang-format off
-
-/*****************************************************************************/
-/*****************************************************************************/
-#ifndef LSRA_STAT_DEF
-#error  Must define LSRA_STAT_DEF macro before including this file
-#endif
-
-#if TRACK_LSRA_STATS
-
-// Number of spills of local vars or tree temps in this basic block.
-LSRA_STAT_DEF(STAT_SPILL,                   "SpillCount")
-
-// Number of GT_COPY nodes inserted in this basic block while allocating regs.
-// Note that GT_COPY nodes are also inserted as part of basic block boundary
-// resolution, which are accounted against resolutionMovCount but not
-// against copyRegCount.
-LSRA_STAT_DEF(STAT_COPY_REG,                "CopyReg")
-
-// Number of resolution moves inserted in this basic block.
-LSRA_STAT_DEF(STAT_RESOLUTION_MOV,          "ResolutionMovs")
-
-// Number of critical edges from this block that are split.
-LSRA_STAT_DEF(STAT_SPLIT_EDGE,              "SplitEdges")
-
-// Register selection stats
-
-LSRA_STAT_DEF(REGSEL_FREE,                  "FREE")
-LSRA_STAT_DEF(REGSEL_CONST_AVAILABLE,       "CONST_AVAILABLE")
-LSRA_STAT_DEF(REGSEL_THIS_ASSIGNED,         "THIS_ASSIGNED")
-LSRA_STAT_DEF(REGSEL_COVERS,                "COVERS")
-LSRA_STAT_DEF(REGSEL_OWN_PREFERENCE,        "OWN_PREFERENCE")
-LSRA_STAT_DEF(REGSEL_COVERS_RELATED,        "COVERS_RELATED")
-LSRA_STAT_DEF(REGSEL_RELATED_PREFERENCE,    "RELATED_PREFERENCE")
-LSRA_STAT_DEF(REGSEL_CALLER_CALLEE,         "CALLER_CALLEE")
-LSRA_STAT_DEF(REGSEL_UNASSIGNED,            "UNASSIGNED")
-LSRA_STAT_DEF(REGSEL_COVERS_FULL,           "COVERS_FULL")
-LSRA_STAT_DEF(REGSEL_BEST_FIT,              "BEST_FIT")
-LSRA_STAT_DEF(REGSEL_IS_PREV_REG,           "IS_PREV_REG")
-LSRA_STAT_DEF(REGSEL_REG_ORDER,             "REG_ORDER")
-LSRA_STAT_DEF(REGSEL_SPILL_COST,            "SPILL_COST")
-LSRA_STAT_DEF(REGSEL_FAR_NEXT_REF,          "FAR_NEXT_REF")
-LSRA_STAT_DEF(REGSEL_PREV_REG_OPT,          "PREV_REG_OPT")
-LSRA_STAT_DEF(REGSEL_REG_NUM,               "REG_NUM")
-
-LSRA_STAT_DEF(COUNT,                        "COUNT")
-
-#endif // TRACK_LSRA_STATS
-
-// clang-format on