Skip to content

Commit 7ebb61e

Browse files
committed
Check if interval interfers with the call
1 parent 9917145 commit 7ebb61e

File tree

4 files changed

+99
-6
lines changed

4 files changed

+99
-6
lines changed

src/coreclr/jit/clrjit.natvis

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ Documentation for VS debugger format specifiers: https://docs.microsoft.com/en-u
153153
</Type>
154154

155155
<Type Name="RefPosition">
156-
<DisplayString>[#{rpNum,d} - {refType,en}]</DisplayString>
156+
<DisplayString>[{nodeLocation,d}. #{rpNum,d} - {refType,en}]</DisplayString>
157157
<Expand>
158158
<Item Name="Referent" Condition="this->isPhysRegRef">(RegRecord*)this-&gt;referent</Item>
159159
<Item Name="Referent" Condition="!this->isPhysRegRef">(Interval*)this-&gt;referent</Item>

src/coreclr/jit/lsra.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,8 @@ LinearScan::LinearScan(Compiler* theCompiler)
624624
, intervals(theCompiler->getAllocator(CMK_LSRA_Interval))
625625
, allocationPassComplete(false)
626626
, refPositions(theCompiler->getAllocator(CMK_LSRA_RefPosition))
627+
, callRefPositionLocations(theCompiler->getAllocator(CMK_LSRA))
628+
, callKillMasks(theCompiler->getAllocator(CMK_LSRA))
627629
, listNodePool(theCompiler)
628630
{
629631
regSelector = new (theCompiler, CMK_LSRA) RegisterSelection(this);
@@ -703,6 +705,8 @@ LinearScan::LinearScan(Compiler* theCompiler)
703705
blockSequenceWorkList = nullptr;
704706
curBBSeqNum = 0;
705707
bbSeqCount = 0;
708+
callRefPositionCount = 0;
709+
recentRefPosition = nullptr;
706710

707711
// Information about each block, including predecessor blocks used for variable locations at block entry.
708712
blockInfo = nullptr;

src/coreclr/jit/lsra.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1582,6 +1582,15 @@ class LinearScan : public LinearScanInterface
15821582
// Ordered list of RefPositions
15831583
RefPositionList refPositions;
15841584

1585+
// Most recent refpostion assigned
1586+
RefPosition* recentRefPosition;
1587+
1588+
// Ordered list of locations where CALL happens
1589+
jitstd::list<LsraLocation> callRefPositionLocations;
1590+
// Equivalent list of kill masks for those calls
1591+
jitstd::list<regMaskTP> callKillMasks;
1592+
int callRefPositionCount;
1593+
15851594
// Per-block variable location mappings: an array indexed by block number that yields a
15861595
// pointer to an array of regNumber, one per variable.
15871596
VarToRegMap* inVarToRegMaps;

src/coreclr/jit/lsrabuild.cpp

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ RefPosition* LinearScan::newRefPositionRaw(LsraLocation nodeLocation, GenTree* t
186186
currBuildNode = nullptr;
187187
newRP->rpNum = static_cast<unsigned>(refPositions.size() - 1);
188188
#endif // DEBUG
189+
recentRefPosition = newRP;
189190
return newRP;
190191
}
191192

@@ -1141,8 +1142,9 @@ regMaskTP LinearScan::getKillSetForNode(GenTree* tree)
11411142
//
11421143
// Notes:
11431144
// The return value is needed because if we have any kills, we need to make sure that
1144-
// all defs are located AFTER the kills. On the other hand, if there aren't kills,
1145-
// the multiple defs for a regPair are in different locations.
1145+
// all defs are located AFTER the kills. TODO: This is always the case.
1146+
// On the other hand, if there aren't kills, the multiple defs for a regPair are in
1147+
// different locations.
11461148
// If we generate any kills, we will mark all currentLiveVars as being preferenced
11471149
// to avoid the killed registers. This is somewhat conservative.
11481150
//
@@ -1167,6 +1169,18 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo
11671169
// if (!blockSequence[curBBSeqNum]->isRunRarely())
11681170
if (enregisterLocalVars)
11691171
{
1172+
const bool isCallKill = ((killMask == RBM_INT_CALLEE_TRASH) || (killMask == RBM_CALLEE_TRASH));
1173+
if (isCallKill)
1174+
{
1175+
assert(recentRefPosition);
1176+
if (recentRefPosition->refType != RefTypeBB)
1177+
{
1178+
callRefPositionLocations.push_back(recentRefPosition->nodeLocation);
1179+
callKillMasks.push_back(killMask);
1180+
callRefPositionCount++;
1181+
}
1182+
}
1183+
11701184
VarSetOps::Iter iter(compiler, currentLiveVars);
11711185
unsigned varIndex = 0;
11721186
while (iter.NextElem(&varIndex))
@@ -1187,9 +1201,7 @@ bool LinearScan::buildKillPositionsForNode(GenTree* tree, LsraLocation currentLo
11871201
{
11881202
continue;
11891203
}
1190-
Interval* interval = getIntervalForLocalVar(varIndex);
1191-
const bool isCallKill = ((killMask == RBM_INT_CALLEE_TRASH) || (killMask == RBM_CALLEE_TRASH));
1192-
1204+
Interval* interval = getIntervalForLocalVar(varIndex);
11931205
if (isCallKill)
11941206
{
11951207
interval->preferCalleeSave = true;
@@ -2613,6 +2625,74 @@ void LinearScan::buildIntervals()
26132625
}
26142626
}
26152627

2628+
// Adjust preferCalleeSave
2629+
if (callRefPositionCount > 0)
2630+
{
2631+
LsraLocation firstCallLocation = callRefPositionLocations.front();
2632+
LsraLocation lastCallLocation = callRefPositionLocations.back();
2633+
2634+
JITDUMP("\n\nCHECKING if Intervals can use callee-save registers");
2635+
JITDUMP("\n==============================\n");
2636+
for (Interval& interval : intervals)
2637+
{
2638+
if (interval.isLocalVar)
2639+
{
2640+
// We already handle them in BuildKillPositionsForNode.
2641+
continue;
2642+
}
2643+
2644+
LsraLocation firstRef = interval.firstRefPosition->nodeLocation;
2645+
LsraLocation lastRef = interval.lastRefPosition->nodeLocation;
2646+
2647+
if ((firstRef > lastCallLocation) || (lastRef < firstCallLocation))
2648+
{
2649+
// Current interval doesn't interfer with any calls. Skip it.
2650+
continue;
2651+
}
2652+
2653+
jitstd::list<regMaskTP> callKillMasksLocal = callKillMasks;
2654+
for (LsraLocation& callLocation : callRefPositionLocations)
2655+
{
2656+
regMaskTP killMask = callKillMasksLocal.front();
2657+
callKillMasksLocal.pop_front();
2658+
2659+
if ((firstRef <= callLocation) && (callLocation < lastRef))
2660+
{
2661+
interval.preferCalleeSave = true;
2662+
if (!interval.isWriteThru)
2663+
{
2664+
// We are more conservative about allocating callee-saves registers to write-thru vars,
2665+
// since a call only requires reloading after (not spilling before). So we record (above)
2666+
// the fact that we'd prefer a callee-save register, but we don't update the preferences at
2667+
// this point. See the "heuristics for writeThru intervals" in 'buildIntervals()'.
2668+
regMaskTP newPreferences = allRegs(interval.registerType) & ~killMask;
2669+
2670+
if (newPreferences != RBM_NONE)
2671+
{
2672+
if (VERBOSE)
2673+
{
2674+
printf("Interval %2u: Update preferences (callee-save) from ",
2675+
interval.intervalIndex);
2676+
dumpRegMask(interval.registerPreferences);
2677+
printf(" to ");
2678+
dumpRegMask(newPreferences);
2679+
printf("\n");
2680+
}
2681+
interval.updateRegisterPreferences(newPreferences);
2682+
}
2683+
}
2684+
break;
2685+
}
2686+
2687+
if (lastRef < callLocation)
2688+
{
2689+
// We passed lastRef, no point in checking further callRefPositions.
2690+
break;
2691+
}
2692+
}
2693+
}
2694+
}
2695+
26162696
#ifdef DEBUG
26172697
if (getLsraExtendLifeTimes())
26182698
{

0 commit comments

Comments
 (0)