Fix IndexOutOfBoundsException in register allocation
Bug: b/545349635
Change-Id: I14809cac6950f6f9ba50b927abaf1a776a2412c7
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 6c8906a..347077c 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
@@ -2130,7 +2130,9 @@
assignFreeRegisterToUnhandledInterval(unhandledInterval, register);
}
} else {
- allocateBlockedRegister(unhandledInterval, registerConstraint);
+ if (!allocateBlockedRegister(unhandledInterval, registerConstraint)) {
+ return false;
+ }
}
} else {
// We will use the candidate register(s) for unhandledInterval, and therefore potentially
@@ -2717,7 +2719,7 @@
return candidate;
}
- private void allocateBlockedRegister(LiveIntervals unhandledInterval, int registerConstraint) {
+ private boolean allocateBlockedRegister(LiveIntervals unhandledInterval, int registerConstraint) {
// Initialize all candidate registers to Integer.MAX_VALUE.
RegisterPositions usePositions = new RegisterPositionsImpl(registerConstraint + 1);
RegisterPositions blockedPositions = new RegisterPositionsImpl(registerConstraint + 1);
@@ -2828,20 +2830,40 @@
RegisterType.MONITOR);
}
+ if (candidate == REGISTER_CANDIDATE_NOT_FOUND) {
+ if (toInstructionPosition(unhandledInterval.getFirstUse())
+ > toInstructionPosition(unhandledInterval.getStart())) {
+ // All active and inactive intervals are used before current. Therefore, it is best to spill
+ // current itself.
+ spillCurrentInterval(unhandledInterval);
+ return true;
+ }
+ LiveIntervalsUse firstUse = unhandledInterval.getUses().first();
+ LiveIntervalsUse nextStricterUse = null;
+ for (LiveIntervalsUse use : unhandledInterval.getUses()) {
+ if (use.getLimit() < firstUse.getLimit()) {
+ nextStricterUse = use;
+ break;
+ }
+ }
+ if (nextStricterUse != null
+ && toInstructionPosition(nextStricterUse.getPosition())
+ > toInstructionPosition(unhandledInterval.getStart())) {
+ LiveIntervals split = unhandledInterval.splitBefore(nextStricterUse.getPosition(), mode);
+ assert split != unhandledInterval;
+ unhandled.add(split);
+ return allocateSingleInterval(unhandledInterval);
+ }
+ return false;
+ }
+
int largestUsePosition = getLargestPosition(usePositions, candidate, needsRegisterPair);
int blockedPosition = getLargestPosition(blockedPositions, candidate, needsRegisterPair);
if (largestUsePosition < unhandledInterval.getFirstUse()) {
// All active and inactive intervals are used before current. Therefore, it is best to spill
// current itself.
- int splitPosition = unhandledInterval.getFirstUse();
- LiveIntervals split = unhandledInterval.splitBefore(splitPosition, mode);
- assert split != unhandledInterval;
- // Experiments show that it has a positive impact on code size to use a fresh register here.
- int registerNumber = getNewSpillRegister(unhandledInterval);
- assignFreeRegisterToUnhandledInterval(unhandledInterval, registerNumber);
- unhandledInterval.setSpilled(true);
- unhandled.add(split);
+ spillCurrentInterval(unhandledInterval);
} else {
// We will use the candidate register(s) for unhandledInterval, and therefore potentially
// need to adjust maxRegisterNumber.
@@ -2860,6 +2882,18 @@
assignRegisterAndSpill(unhandledInterval, candidate);
}
}
+ return true;
+ }
+
+ private void spillCurrentInterval(LiveIntervals unhandledInterval) {
+ int splitPosition = unhandledInterval.getFirstUse();
+ LiveIntervals split = unhandledInterval.splitBefore(splitPosition, mode);
+ assert split != unhandledInterval;
+ // Experiments show that it has a positive impact on code size to use a fresh register here.
+ int registerNumber = getNewSpillRegister(unhandledInterval);
+ assignFreeRegisterToUnhandledInterval(unhandledInterval, registerNumber);
+ unhandledInterval.setSpilled(true);
+ unhandled.add(split);
}
private int getLargestPosition(
diff --git a/src/test/java8/ir/com/android/tools/r8/ir/regalloc/SpillIntervalWithMultipleDifferentConstrainedUsesTest.java b/src/test/java8/ir/com/android/tools/r8/ir/regalloc/SpillIntervalWithMultipleDifferentConstrainedUsesTest.java
index 28762e7..061ad5c 100644
--- a/src/test/java8/ir/com/android/tools/r8/ir/regalloc/SpillIntervalWithMultipleDifferentConstrainedUsesTest.java
+++ b/src/test/java8/ir/com/android/tools/r8/ir/regalloc/SpillIntervalWithMultipleDifferentConstrainedUsesTest.java
@@ -3,12 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.regalloc;
-import static org.junit.Assert.assertTrue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
-import com.android.tools.r8.utils.codeinspector.AssertUtils;
import java.io.File;
import java.io.IOException;
import org.junit.Test;
@@ -17,6 +15,7 @@
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
+// Regression test for IndexOutOfBoundsException in debug mode: b/545349635.
@RunWith(Parameterized.class)
public class SpillIntervalWithMultipleDifferentConstrainedUsesTest extends TestBase {
@@ -30,9 +29,7 @@
@Test
public void testD8Debug() throws Exception {
- AssertUtils.assertFailsCompilation(
- () -> testForD8(parameters).addInnerClasses(getClass()).compile(),
- e -> assertTrue(e.getCause() instanceof IndexOutOfBoundsException));
+ testForD8(parameters).addInnerClasses(getClass()).compile();
}
@Test