Attempt to allocate invoke/range register when blocked
Bug: b/302281605
Change-Id: I5dab4138e712318e853f0f798bf43aaf08fce827
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 9a88563..8c4a6af 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -2016,7 +2016,7 @@
assert freePositionsAreConsistentWithFreeRegisters(freePositions, registerConstraint);
// Attempt to use register hints.
- if (useRegisterHint(unhandledInterval, registerConstraint, freePositions, needsRegisterPair)) {
+ if (useRegisterHint(unhandledInterval, registerConstraint, freePositions)) {
return true;
}
@@ -2036,10 +2036,7 @@
// we need to spill a valid candidate. That path is triggered when largestFreePosition is 0.
int largestFreePosition = 0;
if (candidate != REGISTER_CANDIDATE_NOT_FOUND) {
- largestFreePosition = freePositions.get(candidate);
- if (needsRegisterPair) {
- largestFreePosition = Math.min(largestFreePosition, freePositions.get(candidate + 1));
- }
+ largestFreePosition = freePositions.get(candidate, unhandledInterval.isWide());
}
// Determine what to do based on how long the selected candidate is free.
@@ -2226,10 +2223,7 @@
// Attempt to use the register hint for the unhandled interval in order to avoid generating
// moves.
private boolean useRegisterHint(
- LiveIntervals unhandledInterval,
- int registerConstraint,
- RegisterPositions freePositions,
- boolean needsRegisterPair) {
+ LiveIntervals unhandledInterval, int registerConstraint, RegisterPositions freePositions) {
// If the unhandled interval has a hint we give it that register if it is available without
// spilling. For phis we also use the hint before looking at the operand registers. The
// phi could have a hint from an argument moves which it seems more important to honor in
@@ -2241,7 +2235,6 @@
unhandledInterval,
registerConstraint,
freePositions,
- needsRegisterPair,
unhandledInterval.getHint())) {
return true;
}
@@ -2253,22 +2246,22 @@
unhandledInterval,
registerConstraint,
freePositions,
- needsRegisterPair,
previousSplit.getRegister())) {
return true;
}
LiveIntervals nextSplit = unhandledInterval.getNextSplit();
- if (nextSplit != null
- && nextSplit.hasRegister()
- && triedHints.add(nextSplit.getRegister())
- && tryHint(
- unhandledInterval,
- registerConstraint,
- freePositions,
- needsRegisterPair,
- nextSplit.getRegister())) {
- return true;
+ if (nextSplit != null && nextSplit.hasRegister()) {
+ if (triedHints.add(nextSplit.getRegister())
+ && tryHint(
+ unhandledInterval, registerConstraint, freePositions, nextSplit.getRegister())) {
+ return true;
+ }
+ if (freePositions.isBlocked(nextSplit.getRegister(), unhandledInterval.isWide())
+ && tryAllocateBlockedHint(
+ unhandledInterval, registerConstraint, nextSplit.getRegister())) {
+ return true;
+ }
}
// If there is no hint or it cannot be applied we search for a good register for phis using
@@ -2291,8 +2284,7 @@
}
for (Multiset.Entry<Integer> entry : Multisets.copyHighestCountFirst(map).entrySet()) {
int register = entry.getElement();
- if (tryHint(unhandledInterval, registerConstraint, freePositions, needsRegisterPair,
- register)) {
+ if (tryHint(unhandledInterval, registerConstraint, freePositions, register)) {
return true;
}
}
@@ -2302,25 +2294,20 @@
}
// Attempt to allocate the hint register to the unhandled intervals.
- private boolean tryHint(LiveIntervals unhandledInterval, int registerConstraint,
- RegisterPositions freePositions, boolean needsRegisterPair, int register) {
- // At some point after the hint has been added, the register allocator can
- // decide to redo allocation for the hint interval. In that case, the hint will be
- // reset to NO_REGISTER and provides no hinting info.
- if (register == NO_REGISTER) {
- return false;
- }
- int registerEnd = register + (needsRegisterPair ? 1 : 0);
+ private boolean tryHint(
+ LiveIntervals unhandledInterval,
+ int registerConstraint,
+ RegisterPositions freePositions,
+ int register) {
+ assert register != NO_REGISTER;
+ int registerEnd = register + unhandledInterval.requiredRegisters() - 1;
if (registerEnd > registerConstraint) {
return false;
}
- if (freePositions.isBlocked(register, needsRegisterPair)) {
- return tryAllocateBlockedHint(unhandledInterval, register);
+ if (freePositions.isBlocked(register, unhandledInterval.isWide())) {
+ return false;
}
- int freePosition = freePositions.get(register);
- if (needsRegisterPair) {
- freePosition = Math.min(freePosition, freePositions.get(register + 1));
- }
+ int freePosition = freePositions.get(register, unhandledInterval.isWide());
if (freePosition < unhandledInterval.getEnd()) {
return false;
}
@@ -2338,73 +2325,64 @@
return true;
}
- private boolean tryAllocateBlockedHint(LiveIntervals unhandledInterval, int candidate) {
- if (!options().getTestingOptions().enableRegisterHintsForBlockedRegisters) {
- return false;
- }
- LiveIntervals nextSplit = unhandledInterval.getNextSplit();
- int alternativeHint = nextSplit != null ? nextSplit.getRegister() : NO_REGISTER;
- if (candidate != alternativeHint) {
+ private boolean tryAllocateBlockedHint(
+ LiveIntervals unhandledInterval, int registerConstraint, int candidate) {
+ int registerEnd = candidate + unhandledInterval.requiredRegisters() - 1;
+ if (registerEnd > registerConstraint) {
return false;
}
if (needsArrayGetWideWorkaround(unhandledInterval)
- || needsLongResultOverlappingLongOperandsWorkaround(unhandledInterval)) {
+ || needsLongResultOverlappingLongOperandsWorkaround(unhandledInterval)
+ || needsSingleResultOverlappingLongOperandsWorkaround(unhandledInterval)) {
+ return false;
+ }
+ if (unhandledInterval.isLiveAtMoveExceptionEntry()
+ && isDedicatedMoveExceptionRegister(candidate)) {
return false;
}
if (isArgumentRegister(candidate)) {
for (Value argument = firstArgumentValue;
argument != null;
argument = argument.getNextConsecutive()) {
- if (isPinnedArgument(argument)) {
+ if (isPinnedArgument(argument)
+ && argument.getLiveIntervals().usesRegister(candidate, unhandledInterval.isWide())) {
return false;
}
}
}
- if (isDedicatedMoveExceptionRegister(candidate)) {
- return false;
- }
+ // Check if the current live intervals is blocked by an inactive (overlapping live intervals).
if (!getLiveIntervalsWithRegister(
inactive, unhandledInterval, candidate, unhandledInterval::overlaps)
.isEmpty()) {
return false;
}
- // Find the value occupying the register of interest. Note that the current live intervals may
- // be blocked by an inactive (overlapping) live intervals.
+ // Find the value occupying the register of interest.
Collection<LiveIntervals> blockingIntervals =
getLiveIntervalsWithRegister(active, unhandledInterval, candidate);
assert !blockingIntervals.isEmpty();
- if (blockingIntervals.size() != 1) {
- // Validate that not finding any blocking live intervals means the current live intervals is
- // blocked by an inactive live intervals.
- return false;
- }
- LiveIntervals blockingInterval = blockingIntervals.iterator().next();
- if (unhandledInterval.getType().isWide()) {
- if (blockingInterval.getRegister() != candidate || !blockingInterval.getType().isWide()) {
- // Conservatively bail out. It could be that the low-half of the register pair is blocked by
- // an inactive live intervals.
+ for (LiveIntervals blockingInterval : blockingIntervals) {
+ if (toInstructionPosition(blockingInterval.getStart())
+ == toInstructionPosition(unhandledInterval.getStart())) {
+ // TODO(b/302281605): Look into allowing this when the blocking interval starts at the same
+ // offset.
+ return false;
+ }
+ if (hasConstrainedUseInRange(
+ blockingInterval, unhandledInterval.getStart(), unhandledInterval.getEnd())) {
return false;
}
}
- if (isArgumentRegister(candidate) && isPinnedArgumentRegister(blockingInterval)) {
- return false;
- }
- if (toInstructionPosition(blockingInterval.getStart())
- == toInstructionPosition(unhandledInterval.getStart())) {
- return false;
- }
- if (hasConstrainedUseInRange(
- blockingInterval, unhandledInterval.getStart(), unhandledInterval.getEnd())) {
- return false;
- }
+ // TODO(b/302281605): Look into allowing this even when expiredHere is non-empty.
if (!expiredHere.isEmpty()) {
return false;
}
- LiveIntervals split = blockingInterval.splitBefore(unhandledInterval.getStart(), mode);
- freeOccupiedRegistersForIntervals(blockingInterval);
- assignFreeRegisterToUnhandledInterval(unhandledInterval, blockingInterval.getRegister());
- active.remove(blockingInterval);
- unhandled.add(split);
+ for (LiveIntervals blockingInterval : blockingIntervals) {
+ LiveIntervals split = blockingInterval.splitBefore(unhandledInterval.getStart(), mode);
+ freeOccupiedRegistersForIntervals(blockingInterval);
+ active.remove(blockingInterval);
+ unhandled.add(split);
+ }
+ assignFreeRegisterToUnhandledInterval(unhandledInterval, candidate);
return true;
}
@@ -2509,7 +2487,6 @@
if (freePositions.isBlocked(i, needsRegisterPair) || !freePositions.hasType(i, type)) {
continue;
}
- int usePosition = freePositions.get(i);
if (needsRegisterPair) {
if (i == numberOfArgumentRegisters - 1) {
// The last register of the method is |i|, so we cannot use the pair (|i|, |i+1|).
@@ -2525,8 +2502,8 @@
if (i >= registerConstraint) {
break;
}
- usePosition = Math.min(usePosition, freePositions.get(i + 1));
}
+ int usePosition = freePositions.get(i, needsRegisterPair);
if (unhandledInterval.hasUses() && usePosition == unhandledInterval.getFirstUse()) {
// This register has a use at the same instruction as the value we are allocation a register
// for. Find another register.
@@ -2765,36 +2742,31 @@
if (blockedPosition > unhandledInterval.getEnd()) {
// Spilling can make a register available for the entire interval.
- assignRegisterAndSpill(unhandledInterval, candidate, needsRegisterPair);
+ assignRegisterAndSpill(unhandledInterval, candidate);
} else {
// Spilling only makes a register available for the first part of current.
LiveIntervals splitChild = unhandledInterval.splitBefore(blockedPosition, mode);
unhandled.add(splitChild);
- assignRegisterAndSpill(unhandledInterval, candidate, needsRegisterPair);
+ assignRegisterAndSpill(unhandledInterval, candidate);
}
}
}
private int getLargestPosition(
RegisterPositions positions, int register, boolean needsRegisterPair) {
- int position = positions.get(register);
- if (needsRegisterPair) {
- return Math.min(position, positions.get(register + 1));
- }
- return position;
+ return positions.get(register, needsRegisterPair);
}
- private void assignRegisterAndSpill(
- LiveIntervals unhandledInterval, int candidate, boolean candidateIsWide) {
+ private void assignRegisterAndSpill(LiveIntervals unhandledInterval, int candidate) {
// Split and spill intersecting active intervals for this register.
- spillOverlappingActiveIntervals(unhandledInterval, candidate, candidateIsWide);
+ spillOverlappingActiveIntervals(unhandledInterval, candidate, unhandledInterval.isWide());
// Now that that active intervals have been spilled, we are free to take the candidate.
assignRegister(unhandledInterval, candidate);
takeFreeRegistersForIntervals(unhandledInterval);
active.add(unhandledInterval);
// Split all overlapping inactive intervals for this register. They need to have a new
// register assigned at the next use.
- splitOverlappingInactiveIntervals(unhandledInterval, candidate, candidateIsWide);
+ splitOverlappingInactiveIntervals(unhandledInterval, candidate, unhandledInterval.isWide());
}
protected void splitOverlappingInactiveIntervals(
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
index 6eadee6..19fd3d3 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LiveIntervals.java
@@ -87,6 +87,10 @@
return getType().requiredRegisters();
}
+ public boolean isWide() {
+ return getType().isWide();
+ }
+
public void setHint(LiveIntervals intervals, PriorityQueue<LiveIntervals> unhandled) {
assert intervals.hasRegister();
// Do not set hints if they cannot be used anyway.
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterPositions.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterPositions.java
index 445b46f..064048e 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterPositions.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterPositions.java
@@ -24,6 +24,14 @@
public abstract int get(int index);
+ public final int get(int index, boolean isWide) {
+ int result = get(index);
+ if (isWide) {
+ return Math.min(result, get(index + 1));
+ }
+ return result;
+ }
+
public abstract int getLimit();
public abstract void setBlocked(int index);
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 2b5aa51..6709446 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -2487,7 +2487,6 @@
public boolean allowUnusedDontWarnRules = true;
public boolean alwaysUseExistingAccessInfoCollectionsInMemberRebinding = true;
public boolean alwaysUsePessimisticRegisterAllocation = false;
- public boolean enableRegisterHintsForBlockedRegisters = false;
// TODO(b/374715251): Look into enabling this.
public boolean enableUseLastLocalRegisterAsMoveExceptionRegister = false;
public boolean enableKeepInfoCanonicalizer = true;
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/InvokeRangeWithSameValueRepeatedTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/InvokeRangeWithSameValueRepeatedTest.java
index a6452b5..06735e3 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/InvokeRangeWithSameValueRepeatedTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/InvokeRangeWithSameValueRepeatedTest.java
@@ -27,8 +27,6 @@
public void testD8() throws Exception {
testForD8()
.addInnerClasses(getClass())
- .addOptionsModification(
- options -> options.getTestingOptions().enableRegisterHintsForBlockedRegisters = true)
.release()
.setMinApi(parameters)
.compile()
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RedundantSpillingBeforeInvokeRangeTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RedundantSpillingBeforeInvokeRangeTest.java
index f87a53d..0e6900a 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RedundantSpillingBeforeInvokeRangeTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RedundantSpillingBeforeInvokeRangeTest.java
@@ -33,8 +33,6 @@
public void testD8() throws Exception {
testForD8()
.addInnerClasses(getClass())
- .addOptionsModification(
- options -> options.getTestingOptions().enableRegisterHintsForBlockedRegisters = true)
.release()
.setMinApi(parameters)
.compile()
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/ValueUsedInMultipleInvokeRangeInstructionsTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/ValueUsedInMultipleInvokeRangeInstructionsTest.java
index b8bd25f..92132f1 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/ValueUsedInMultipleInvokeRangeInstructionsTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/ValueUsedInMultipleInvokeRangeInstructionsTest.java
@@ -27,8 +27,6 @@
public void testD8() throws Exception {
testForD8()
.addInnerClasses(getClass())
- .addOptionsModification(
- options -> options.getTestingOptions().enableRegisterHintsForBlockedRegisters = true)
.release()
.setMinApi(parameters)
.compile()