-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Update handling of limited register during consecutive registers allocation #84588
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
d452147
1708964
c31cfbd
e684275
13e85ac
f2cda1e
c0c3f4a
01ec812
e5698cc
fe4f675
2a3d0f2
3f5dfd7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -496,13 +496,6 @@ regMaskTP LinearScan::stressLimitRegs(RefPosition* refPosition, regMaskTP mask) | |||||
| { | ||||||
| mask |= refPosition->registerAssignment; | ||||||
| } | ||||||
|
|
||||||
| #ifdef TARGET_ARM64 | ||||||
| if ((refPosition != nullptr) && refPosition->isFirstRefPositionOfConsecutiveRegisters()) | ||||||
| { | ||||||
| mask |= LsraLimitFPSetForConsecutive; | ||||||
| } | ||||||
| #endif | ||||||
| } | ||||||
|
|
||||||
| return mask; | ||||||
|
|
@@ -663,6 +656,8 @@ LinearScan::LinearScan(Compiler* theCompiler) | |||||
|
|
||||||
| #ifdef DEBUG | ||||||
| maxNodeLocation = 0; | ||||||
| consecutiveRegistersLocation = 0; | ||||||
|
|
||||||
| activeRefPosition = nullptr; | ||||||
| currBuildNode = nullptr; | ||||||
|
|
||||||
|
|
@@ -4901,6 +4896,24 @@ void LinearScan::allocateRegisters() | |||||
| } | ||||||
| } | ||||||
| prevLocation = currentLocation; | ||||||
| #ifdef TARGET_ARM64 | ||||||
|
|
||||||
| #ifdef DEBUG | ||||||
| if (hasConsecutiveRegister) | ||||||
| { | ||||||
| if (currentRefPosition.needsConsecutive) | ||||||
| { | ||||||
| // track all the refpositions around the location that is also | ||||||
| // allocating consecutive registers. | ||||||
| consecutiveRegistersLocation = currentLocation; | ||||||
| } | ||||||
| else if (consecutiveRegistersLocation < currentLocation) | ||||||
| { | ||||||
| consecutiveRegistersLocation = MinLocation; | ||||||
| } | ||||||
| } | ||||||
| #endif // DEBUG | ||||||
| #endif // TARGET_ARM64 | ||||||
|
|
||||||
| // get previous refposition, then current refpos is the new previous | ||||||
| if (currentReferent != nullptr) | ||||||
|
|
@@ -11686,46 +11699,50 @@ void LinearScan::RegisterSelection::try_SPILL_COST() | |||||
| // Can and should the interval in this register be spilled for this one, | ||||||
| // if we don't find a better alternative? | ||||||
|
|
||||||
| weight_t currentSpillWeight = 0; | ||||||
| #ifdef TARGET_ARM64 | ||||||
| if (assignedInterval == nullptr) | ||||||
| { | ||||||
| // Ideally we should not be seeing this candidate because it is not assigned to | ||||||
| // any interval. But based on that, we cannot determine if it is a good spill | ||||||
| // candidate or not. Skip processing it. | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| if ((recentRefPosition != nullptr) && linearScan->isRefPositionActive(recentRefPosition, thisLocation) && | ||||||
| (recentRefPosition->needsConsecutive)) | ||||||
| { | ||||||
| continue; | ||||||
| } | ||||||
| #endif // TARGET_ARM64 | ||||||
|
|
||||||
| if ((linearScan->getNextIntervalRef(spillCandidateRegNum, regType) == thisLocation) && | ||||||
| !assignedInterval->getNextRefPosition()->RegOptional()) | ||||||
| { | ||||||
| continue; | ||||||
| } | ||||||
| if (!linearScan->isSpillCandidate(currentInterval, refPosition, spillCandidateRegRecord)) | ||||||
| else if (assignedInterval != nullptr) | ||||||
| #endif | ||||||
| { | ||||||
| continue; | ||||||
| } | ||||||
| if ((linearScan->getNextIntervalRef(spillCandidateRegNum, regType) == thisLocation) && | ||||||
| !assignedInterval->getNextRefPosition()->RegOptional()) | ||||||
| { | ||||||
| continue; | ||||||
| } | ||||||
| if (!linearScan->isSpillCandidate(currentInterval, refPosition, spillCandidateRegRecord)) | ||||||
| { | ||||||
| continue; | ||||||
| } | ||||||
|
|
||||||
| weight_t currentSpillWeight = 0; | ||||||
| if ((recentRefPosition != nullptr) && | ||||||
| (recentRefPosition->RegOptional() && !(assignedInterval->isLocalVar && recentRefPosition->IsActualRef()))) | ||||||
| { | ||||||
| // We do not "spillAfter" if previous (recent) refPosition was regOptional or if it | ||||||
| // is not an actual ref. In those cases, we will reload in future (next) refPosition. | ||||||
| // For such cases, consider the spill cost of next refposition. | ||||||
| // See notes in "spillInterval()". | ||||||
| RefPosition* reloadRefPosition = assignedInterval->getNextRefPosition(); | ||||||
| if (reloadRefPosition != nullptr) | ||||||
| if ((recentRefPosition != nullptr) && (recentRefPosition->RegOptional() && | ||||||
| !(assignedInterval->isLocalVar && recentRefPosition->IsActualRef()))) | ||||||
| { | ||||||
| currentSpillWeight = linearScan->getWeight(reloadRefPosition); | ||||||
| // We do not "spillAfter" if previous (recent) refPosition was regOptional or if it | ||||||
| // is not an actual ref. In those cases, we will reload in future (next) refPosition. | ||||||
| // For such cases, consider the spill cost of next refposition. | ||||||
| // See notes in "spillInterval()". | ||||||
| RefPosition* reloadRefPosition = assignedInterval->getNextRefPosition(); | ||||||
| if (reloadRefPosition != nullptr) | ||||||
| { | ||||||
| currentSpillWeight = linearScan->getWeight(reloadRefPosition); | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| #ifdef TARGET_ARM64 | ||||||
| else | ||||||
| { | ||||||
| // Ideally we should not be seeing this candidate because it is not assigned to | ||||||
| // any interval. But it is possible for certain scenarios. One of them is that | ||||||
| // `refPosition` needs consecutive registers and we decided to pick a mix of free+busy | ||||||
| // registers. This candidate is part of that set and is free and hence is not assigned | ||||||
| // to any interval. | ||||||
| } | ||||||
| #endif // TARGET_ARM64 | ||||||
|
|
||||||
| // Only consider spillCost if we were not able to calculate weight of reloadRefPosition. | ||||||
| if (currentSpillWeight == 0) | ||||||
|
|
@@ -11875,7 +11892,16 @@ void LinearScan::RegisterSelection::try_PREV_REG_OPT() | |||||
| #ifdef DEBUG | ||||||
| // The assigned should be non-null, and should have a recentRefPosition, however since | ||||||
| // this is a heuristic, we don't want a fatal error, so we just assert (not noway_assert). | ||||||
| if (!hasAssignedInterval) | ||||||
| if (!hasAssignedInterval | ||||||
| #ifdef TARGET_ARM64 | ||||||
| // We could see a candidate that doesn't have assignedInterval because allocation is | ||||||
| // happening for `refPosition` that needs consecutive registers and we decided to pick | ||||||
| // a mix of free+busy registers. This candidate is part of that set and is free and hence | ||||||
| // is not assigned to any interval. | ||||||
|
|
||||||
| && !refPosition->needsConsecutive | ||||||
| #endif | ||||||
| ) | ||||||
| { | ||||||
| assert(!"Spill candidate has no assignedInterval recentRefPosition"); | ||||||
| } | ||||||
|
|
@@ -11988,6 +12014,10 @@ regMaskTP LinearScan::RegisterSelection::select(Interval* currentInterval, | |||||
| *registerScore = NONE; | ||||||
| #endif | ||||||
|
|
||||||
| #ifdef TARGET_ARM64 | ||||||
| assert(!needsConsecutiveRegisters || refPosition->needsConsecutive); | ||||||
| #endif | ||||||
|
|
||||||
| reset(currentInterval, refPosition); | ||||||
|
|
||||||
| // process data-structures | ||||||
|
|
@@ -12036,7 +12066,19 @@ regMaskTP LinearScan::RegisterSelection::select(Interval* currentInterval, | |||||
| } | ||||||
|
|
||||||
| #ifdef DEBUG | ||||||
| candidates = linearScan->stressLimitRegs(refPosition, candidates); | ||||||
| #ifdef TARGET_ARM64 | ||||||
| if (!refPosition->needsConsecutive && (linearScan->consecutiveRegistersLocation == refPosition->nodeLocation)) | ||||||
| { | ||||||
| // If a method has consecutive registers and we are assigning to refPositions that are not part | ||||||
| // of consecutive registers, but are live at same location, skip the limit stress for them, because | ||||||
| // there are high chances that many registers are busy for consecutive requirements and we don't | ||||||
| // have enough remaining for other refpositions (like operands). | ||||||
| } | ||||||
| else | ||||||
| #endif | ||||||
| { | ||||||
| candidates = linearScan->stressLimitRegs(refPosition, candidates); | ||||||
| } | ||||||
| #endif | ||||||
| assert(candidates != RBM_NONE); | ||||||
|
|
||||||
|
|
@@ -12186,6 +12228,10 @@ regMaskTP LinearScan::RegisterSelection::select(Interval* currentInterval, | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| #ifdef DEBUG | ||||||
| regMaskTP inUseOrBusyRegsMask = RBM_NONE; | ||||||
| #endif | ||||||
|
|
||||||
| // Eliminate candidates that are in-use or busy. | ||||||
| if (!found) | ||||||
| { | ||||||
|
|
@@ -12195,6 +12241,10 @@ regMaskTP LinearScan::RegisterSelection::select(Interval* currentInterval, | |||||
| regMaskTP busyRegs = linearScan->regsBusyUntilKill | linearScan->regsInUseThisLocation; | ||||||
| candidates &= ~busyRegs; | ||||||
|
|
||||||
| #ifdef DEBUG | ||||||
| inUseOrBusyRegsMask |= ~busyRegs; | ||||||
| #endif | ||||||
|
|
||||||
| // Also eliminate as busy any register with a conflicting fixed reference at this or | ||||||
| // the next location. | ||||||
| // Note that this will eliminate the fixedReg, if any, but we'll add it back below. | ||||||
|
|
@@ -12210,6 +12260,9 @@ regMaskTP LinearScan::RegisterSelection::select(Interval* currentInterval, | |||||
| (refPosition->delayRegFree && (checkConflictLocation == (refPosition->nodeLocation + 1)))) | ||||||
| { | ||||||
| candidates &= ~checkConflictBit; | ||||||
| #ifdef DEBUG | ||||||
| inUseOrBusyRegsMask |= ~checkConflictBit; | ||||||
|
||||||
| inUseOrBusyRegsMask |= ~checkConflictBit; | |
| inUseOrBusyRegsMask |= checkConflictBit; |
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Below, I am using this as registerAssignment & inUseOrBusyRegsMask.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But what if you | together two different conflict bits? Then all the bits will be set and registerAssignment & inUseOrBusyRegsMask will do nothing. Is that right? Don't you need to | together all the bits first and then registerAssignment & ~inUseOrBusyRegsMask? At the very least, inUseOrBusyRegsMask as a name doesn't make sense since in your usage it's (kind of) the opposite of that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this be:
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Below, I am using this as
registerAssignment & inUseOrBusyRegsMask.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should there be a separate one for just
inUseRegsMaskto help differentiate?It's confusing to see clearing all the busy regs here given the local name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have incorporated Bruce's feedback.