Improve register allocation in presence of DebugLocalWrite
Fixes: b/382217790
Change-Id: Ib1e4c03ee0ebfa79e7f55857ffefae488dd81652
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 f85e068..d351ec2 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
@@ -22,6 +22,7 @@
import com.android.tools.r8.ir.code.ArithmeticBinop;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.DebugLocalWrite;
import com.android.tools.r8.ir.code.DebugLocalsChange;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.IRCode.LiveAtEntrySets;
@@ -1087,13 +1088,12 @@
intervals.setSpilled(false);
}
intervals.clearRegisterAssignment();
+ intervals.unsetHandled();
intervals.unsetIsInvokeRangeIntervals();
}
}
- /**
- * Get the register allocated to a given set of live intervals.
- */
+ /** Get the register allocated to a given set of live intervals. */
private int getRegisterForIntervals(LiveIntervals intervals) {
int intervalsRegister = intervals.getRegister();
return realRegisterNumberFromAllocated(intervalsRegister);
@@ -1145,12 +1145,6 @@
allocateRegistersForMoveExceptionIntervals(hasInvokeRangeLiveIntervals);
timing.end();
- timing.begin("Argument linked");
- for (LiveIntervals argumentLiveIntervals : getArgumentLiveIntervals()) {
- allocateRegistersForInvokeRangeSplits(argumentLiveIntervals);
- }
- timing.end();
-
// Go through each unhandled live interval and find a register for it.
timing.begin("Process all unhandled");
while (!unhandled.isEmpty()) {
@@ -1474,22 +1468,24 @@
*/
@SuppressWarnings("JdkObsolete")
private void allocateRegistersForInvokeRangeSplits(LiveIntervals unhandledIntervals) {
- if (!unhandledIntervals.isSplitParent()) {
+ LiveIntervals splitParent = unhandledIntervals.getSplitParent();
+ if (splitParent.isInvokeRangeIntervalsProcessed()) {
return;
}
timing.begin("Extract splits");
List<LiveIntervals> invokeRangeIntervals =
ListUtils.filter(
- unhandledIntervals.getSplitChildren(),
+ splitParent.getSplitChildren(),
split -> split.isInvokeRangeIntervals() && !split.hasRegister());
timing.end();
timing.begin("Process splits");
- if (unhandledIntervals.isSplitParent() && unhandledIntervals.isInvokeRangeIntervals()) {
+ if (splitParent.isInvokeRangeIntervals() && !splitParent.hasRegister()) {
allocateRegistersForInvokeRangeSplit(unhandledIntervals);
}
for (LiveIntervals split : invokeRangeIntervals) {
allocateRegistersForInvokeRangeSplit(split);
}
+ splitParent.setInvokeRangeIntervalsProcessed();
timing.end();
}
@@ -2284,34 +2280,38 @@
// phi could have a hint from an argument moves which it seems more important to honor in
// practice.
IntSet triedHints = new IntArraySet();
- if (unhandledInterval.hasHint()
- && triedHints.add(unhandledInterval.getHint())
- && tryHint(
- unhandledInterval,
- registerConstraint,
- freePositions,
- unhandledInterval.getHint())) {
+ Multiset<Integer> hints = HashMultiset.create();
+ for (LiveIntervals sibling : unhandledInterval.getSplitParent().getSplitChildren()) {
+ if (sibling.hasRegister()) {
+ hints.add(sibling.getRegister());
+ }
+ }
+ if (options().debug) {
+ for (Instruction user :
+ unhandledInterval.getValue().uniqueUsers(Instruction::isDebugLocalWrite)) {
+ for (LiveIntervals aliasSibling : user.outValue().getLiveIntervals().getSplitChildren()) {
+ if (aliasSibling.hasRegister()) {
+ hints.add(aliasSibling.getRegister());
+ }
+ }
+ }
+ }
+ if (tryHints(unhandledInterval, registerConstraint, freePositions, hints, triedHints)) {
return true;
}
- LiveIntervals previousSplit = unhandledInterval.getPreviousSplit();
- if (previousSplit != null
- && triedHints.add(previousSplit.getRegister())
+ if (unhandledInterval.hasHint()
&& tryHint(
unhandledInterval,
registerConstraint,
freePositions,
- previousSplit.getRegister())) {
+ unhandledInterval.getHint(),
+ triedHints)) {
return true;
}
LiveIntervals nextSplit = unhandledInterval.getNextSplit();
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())) {
@@ -2323,31 +2323,53 @@
// the registers assigned to the operand intervals. We determine all the registers used
// for operands and try them one by one based on frequency.
Value value = unhandledInterval.getValue();
- if (value.isPhi()) {
+ if (value.isDefinedByInstructionSatisfying(Instruction::isDebugLocalWrite)) {
+ DebugLocalWrite debugLocalWrite = value.getDefinition().asDebugLocalWrite();
+ LiveIntervals theIntervals =
+ debugLocalWrite.src().getLiveIntervals().getSplitCovering(debugLocalWrite);
+ assert theIntervals != null;
+ if (tryHint(unhandledInterval, registerConstraint, freePositions, theIntervals, triedHints)) {
+ return true;
+ }
+ } else if (value.isPhi()) {
Phi phi = value.asPhi();
- Multiset<Integer> map = HashMultiset.create();
- List<Value> operands = phi.getOperands();
- for (int i = 0; i < operands.size(); i++) {
- LiveIntervals intervals = operands.get(i).getLiveIntervals();
+ hints.clear();
+ for (int i = 0; i < phi.getOperands().size(); i++) {
+ LiveIntervals intervals = phi.getOperand(i).getLiveIntervals();
if (intervals.hasSplits()) {
BasicBlock pred = phi.getBlock().getPredecessor(i);
intervals = intervals.getSplitCovering(pred.exit().getNumber());
}
if (intervals.hasRegister()) {
- map.add(intervals.getRegister());
+ hints.add(intervals.getRegister());
}
}
- for (Multiset.Entry<Integer> entry : Multisets.copyHighestCountFirst(map).entrySet()) {
- int register = entry.getElement();
- if (tryHint(unhandledInterval, registerConstraint, freePositions, register)) {
- return true;
- }
- }
+ return tryHints(unhandledInterval, registerConstraint, freePositions, hints, triedHints);
}
-
return false;
}
+ private boolean tryHint(
+ LiveIntervals unhandledInterval,
+ int registerConstraint,
+ RegisterPositions freePositions,
+ LiveIntervals hint,
+ IntSet triedHints) {
+ return hint.hasRegister()
+ && tryHint(
+ unhandledInterval, registerConstraint, freePositions, hint.getRegister(), triedHints);
+ }
+
+ private boolean tryHint(
+ LiveIntervals unhandledInterval,
+ int registerConstraint,
+ RegisterPositions freePositions,
+ int hint,
+ IntSet triedHints) {
+ return triedHints.add(hint)
+ && tryHint(unhandledInterval, registerConstraint, freePositions, hint);
+ }
+
// Attempt to allocate the hint register to the unhandled intervals.
private boolean tryHint(
LiveIntervals unhandledInterval,
@@ -2380,6 +2402,21 @@
return true;
}
+ private boolean tryHints(
+ LiveIntervals unhandledInterval,
+ int registerConstraint,
+ RegisterPositions freePositions,
+ Multiset<Integer> hints,
+ IntSet triedHints) {
+ for (Multiset.Entry<Integer> entry : Multisets.copyHighestCountFirst(hints).entrySet()) {
+ int hint = entry.getElement();
+ if (tryHint(unhandledInterval, registerConstraint, freePositions, hint, triedHints)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
private boolean tryAllocateBlockedHint(
LiveIntervals unhandledInterval, int registerConstraint, int candidate) {
int registerEnd = candidate + unhandledInterval.requiredRegisters() - 1;
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 a1576b0..816d263 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 int hint = NO_REGISTER;
private boolean spilled = false;
private Invoke isInvokeRangeIntervals = null;
+ private boolean isInvokeRangeIntervalsProcessed = false;
private boolean usedInMonitorOperations = false;
private boolean liveAtMoveExceptionEntry = false;
private boolean handled = false;
@@ -125,6 +126,10 @@
handled = true;
}
+ public void unsetHandled() {
+ handled = false;
+ }
+
public void setSpilled(boolean value) {
// Check that we always spill arguments to their original register.
assert getRegister() != NO_REGISTER;
@@ -323,6 +328,15 @@
public void unsetIsInvokeRangeIntervals() {
assert isSplitParent();
isInvokeRangeIntervals = null;
+ isInvokeRangeIntervalsProcessed = false;
+ }
+
+ public boolean isInvokeRangeIntervalsProcessed() {
+ return isInvokeRangeIntervalsProcessed;
+ }
+
+ public void setInvokeRangeIntervalsProcessed() {
+ isInvokeRangeIntervalsProcessed = true;
}
public boolean isLiveAtMoveExceptionEntry() {