Allow using move exception register for values not live to catch
This records on each live intervals whether the given live interval is live at entry to a block that starts with a move-exception instruction.
All values that are not live at entry to a move-exception block do not need to check for overlap with move exception live intervals.
This improves build speed but also size, since this means that the move-exception split live intervals will be able to reuse the dedicated move-exception register. In other words, this fixes the issue that the compiler would almost always insert a (often) redundant move immediately after each move-exception instruction.
Change-Id: I22e0c6b040dd49c115680034adc148bc35ca72bd
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 31fe227..bb5742b 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
@@ -1139,9 +1139,8 @@
}
// Split their live ranges which will force another register if used.
for (LiveIntervals intervals : moveExceptionIntervals) {
- if (intervals.getUses().size() > 1) {
- LiveIntervals split =
- intervals.splitBefore(intervals.getFirstUse() + INSTRUCTION_NUMBER_DELTA);
+ if (intervals.getValue().hasAnyUsers()) {
+ LiveIntervals split = intervals.splitAfter(intervals.getValue().getDefinition());
unhandled.add(split);
}
}
@@ -1482,7 +1481,7 @@
if (hasDedicatedMoveExceptionRegister()) {
boolean canUseMoveExceptionRegisterForLinkedIntervals =
isDedicatedMoveExceptionRegisterInFirstLocalRegister()
- && !overlapsMoveExceptionInterval(start);
+ && (!start.isLiveAtMoveExceptionEntry() || !overlapsMoveExceptionInterval(start));
if (!canUseMoveExceptionRegisterForLinkedIntervals) {
freeRegisters.remove(getMoveExceptionRegister());
}
@@ -1659,7 +1658,8 @@
// Check for overlap with the move exception interval.
boolean overlapsMoveExceptionInterval =
- hasDedicatedMoveExceptionRegister()
+ intervals.isLiveAtMoveExceptionEntry()
+ && hasDedicatedMoveExceptionRegister()
&& (register == getMoveExceptionRegister()
|| (intervals.getType().isWide() && register + 1 == getMoveExceptionRegister()))
&& overlapsMoveExceptionInterval(intervals);
@@ -2069,10 +2069,12 @@
// place to put a spill move (because the move exception instruction has to be the
// first instruction in the handler block).
if (hasDedicatedMoveExceptionRegister()) {
- if (unhandledInterval.getRegisterLimit() == Constants.U4BIT_MAX
+ if (!mode.is4Bit()
+ && unhandledInterval.getRegisterLimit() == Constants.U4BIT_MAX
&& isDedicatedMoveExceptionRegisterInLastLocalRegister()) {
freePositions.setBlocked(getMoveExceptionRegister());
- } else if (overlapsMoveExceptionInterval(unhandledInterval)) {
+ } else if (unhandledInterval.isLiveAtMoveExceptionEntry()
+ && overlapsMoveExceptionInterval(unhandledInterval)) {
int moveExceptionRegister = getMoveExceptionRegister();
if (moveExceptionRegister <= registerConstraint) {
freePositions.setBlocked(moveExceptionRegister);
@@ -2568,7 +2570,8 @@
// Disallow reuse of the move exception register if we have reserved one.
if (hasDedicatedMoveExceptionRegister()) {
- if (unhandledInterval.getRegisterLimit() == Constants.U4BIT_MAX
+ if (!mode.is4Bit()
+ && unhandledInterval.getRegisterLimit() == Constants.U4BIT_MAX
&& isDedicatedMoveExceptionRegisterInLastLocalRegister()) {
usePositions.setBlocked(getMoveExceptionRegister());
} else if (overlapsMoveExceptionInterval(unhandledInterval)) {
@@ -3105,6 +3108,13 @@
private void computeLiveRanges() {
computeLiveRanges(appView, code, liveAtEntrySets, liveIntervals);
+ boolean hasMoveException = false;
+ for (BasicBlock block : code.blocks(block -> block.entry().isMoveException())) {
+ for (Value value : liveAtEntrySets.get(block).liveValues) {
+ value.getLiveIntervals().setIsLiveAtMoveExceptionEntry();
+ }
+ hasMoveException = true;
+ }
// Art VMs before Android M assume that the register for the receiver never changes its value.
// This assumption is used during verification. Allowing the receiver register to be
// overwritten can therefore lead to verification errors. If we could be targeting one of these
@@ -3117,6 +3127,9 @@
for (LiveAtEntrySets values : liveAtEntrySets.values()) {
values.liveValues.add(firstArgumentValue);
}
+ if (hasMoveException) {
+ thisIntervals.setIsLiveAtMoveExceptionEntry();
+ }
}
}
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 3a8f340..64d42b0 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
@@ -43,6 +43,7 @@
private boolean spilled = false;
private boolean isInvokeRangeIntervals = false;
private boolean usedInMonitorOperations = false;
+ private boolean liveAtMoveExceptionEntry = false;
// Only registers up to and including the registerLimit are allowed for this interval.
private int registerLimit = U16BIT_MAX;
@@ -305,9 +306,19 @@
}
public void unsetIsInvokeRangeIntervals() {
+ assert isSplitParent();
isInvokeRangeIntervals = false;
}
+ public boolean isLiveAtMoveExceptionEntry() {
+ return splitParent.liveAtMoveExceptionEntry;
+ }
+
+ public void setIsLiveAtMoveExceptionEntry() {
+ assert isSplitParent();
+ liveAtMoveExceptionEntry = true;
+ }
+
private int computeMaxNonSpilledRegister() {
assert splitParent == this;
assert maxNonSpilledRegister == NO_REGISTER;