[CF] Share more code between the CF and DEX register allocators.
Change-Id: I69efec48b80e3d0013fcb5d4e1d947475165c4a1
diff --git a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
index adf82dd..1c7c6d9 100644
--- a/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/cf/CfRegisterAllocator.java
@@ -3,8 +3,6 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.cf;
-import static com.android.tools.r8.dex.Constants.U16BIT_MAX;
-import static com.android.tools.r8.ir.code.IRCode.INSTRUCTION_NUMBER_DELTA;
import static com.android.tools.r8.ir.regalloc.LiveIntervals.NO_REGISTER;
import com.android.tools.r8.errors.Unreachable;
@@ -12,12 +10,10 @@
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
-import com.android.tools.r8.ir.code.InstructionListIterator;
-import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.StackValue;
import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.ir.regalloc.LinearScanRegisterAllocator;
import com.android.tools.r8.ir.regalloc.LiveIntervals;
-import com.android.tools.r8.ir.regalloc.LiveIntervalsUse;
import com.android.tools.r8.ir.regalloc.LiveRange;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.android.tools.r8.utils.InternalOptions;
@@ -25,7 +21,6 @@
import it.unimi.dsi.fastutil.ints.IntList;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -128,117 +123,7 @@
private void computeLivenessInformation() {
code.numberInstructions();
liveAtEntrySets = code.computeLiveAtEntrySets();
- computeLiveRanges();
- }
-
- private void computeLiveRanges() {
- for (BasicBlock block : code.topologicallySortedBlocks()) {
- Set<Value> live = new HashSet<>();
- List<BasicBlock> successors = block.getSuccessors();
- Set<Value> phiOperands = new HashSet<>();
- for (BasicBlock successor : successors) {
- live.addAll(liveAtEntrySets.get(successor));
- int predIndex = successor.getPredecessors().indexOf(block);
- for (Phi phi : successor.getPhis()) {
- live.remove(phi);
- phiOperands.add(phi.getOperand(predIndex));
- assert phi.getDebugValues().stream().allMatch(Value::needsRegister);
- phiOperands.addAll(phi.getDebugValues());
- }
- }
- live.addAll(phiOperands);
- List<Instruction> instructions = block.getInstructions();
- for (Value value : live) {
- int end = block.entry().getNumber() + instructions.size() * INSTRUCTION_NUMBER_DELTA;
- // Make sure that phi operands do not overlap the phi live range. The phi operand is
- // not live until the next instruction, but only until the gap before the next instruction
- // where the phi value takes over.
- if (phiOperands.contains(value)) {
- end--;
- }
- addLiveRange(value, block, end);
- }
- InstructionListIterator it = block.listIterator(block.getInstructions().size());
- while (it.hasPrevious()) {
- Instruction instruction = it.previous();
- int number = instruction.getNumber();
- Value definition = instruction.outValue();
- if (definition != null) {
- // For instructions that define values which have no use create a live range covering
- // the instruction. This will typically be instructions that can have side effects even
- // if their output is not used.
- if (!definition.isUsed()) {
- addLiveRange(definition, block, number + INSTRUCTION_NUMBER_DELTA);
- assert instruction.isArgument()
- : "Arguments should be the only potentially unused local in CF";
- }
- live.remove(definition);
- }
- for (Value use : instruction.inValues()) {
- if (use.needsRegister()) {
- if (!live.contains(use)) {
- live.add(use);
- addLiveRange(use, block, number);
- }
- LiveIntervals useIntervals = use.getLiveIntervals();
- useIntervals.addUse(new LiveIntervalsUse(number, U16BIT_MAX /* unconstrained */));
- }
- }
- if (options.debug) {
- for (Value use : instruction.getDebugValues()) {
- assert use.needsRegister();
- if (!live.contains(use)) {
- live.add(use);
- addLiveRange(use, block, number);
- }
- }
- }
- }
- }
- }
-
- private void addLiveRange(Value value, BasicBlock block, int end) {
- int firstInstructionInBlock = block.entry().getNumber();
- int instructionsSize = block.getInstructions().size() * INSTRUCTION_NUMBER_DELTA;
- int lastInstructionInBlock =
- firstInstructionInBlock + instructionsSize - INSTRUCTION_NUMBER_DELTA;
- int instructionNumber;
- if (value.isPhi()) {
- instructionNumber = firstInstructionInBlock;
- } else {
- Instruction instruction = value.definition;
- instructionNumber = instruction.getNumber();
- }
- if (value.getLiveIntervals() == null) {
- Value current = value.getStartOfConsecutive();
- LiveIntervals intervals = new LiveIntervals(current);
- while (true) {
- liveIntervals.add(intervals);
- Value next = current.getNextConsecutive();
- if (next == null) {
- break;
- }
- LiveIntervals nextIntervals = new LiveIntervals(next);
- intervals.link(nextIntervals);
- current = next;
- intervals = nextIntervals;
- }
- }
- LiveIntervals intervals = value.getLiveIntervals();
- if (firstInstructionInBlock <= instructionNumber
- && instructionNumber <= lastInstructionInBlock) {
- if (value.isPhi()) {
- // Phis need to interfere with spill restore moves inserted before the instruction because
- // the phi value is defined on the inflowing edge.
- instructionNumber--;
- }
- intervals.addRange(new LiveRange(instructionNumber, end));
- if (!value.isPhi()) {
- intervals.addUse(new LiveIntervalsUse(instructionNumber, U16BIT_MAX /* unconstrained */));
- }
- } else {
- intervals.addRange(new LiveRange(firstInstructionInBlock - 1, end));
- }
+ LinearScanRegisterAllocator.computeLiveRanges(options, code, liveAtEntrySets, liveIntervals);
}
private void performLinearScan() {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Load.java b/src/main/java/com/android/tools/r8/ir/code/Load.java
index 74cf861..cc45086 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Load.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Load.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
import com.android.tools.r8.cf.code.CfLoad;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexType;
@@ -40,7 +41,7 @@
@Override
public int maxInValueRegister() {
- throw new Unreachable();
+ return Constants.U16BIT_MAX;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/Store.java b/src/main/java/com/android/tools/r8/ir/code/Store.java
index 05c7c79..6600137 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Store.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Store.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.cf.LoadStoreHelper;
import com.android.tools.r8.cf.TypeVerificationHelper;
import com.android.tools.r8.cf.code.CfStore;
+import com.android.tools.r8.dex.Constants;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AppInfoWithSubtyping;
import com.android.tools.r8.graph.DexType;
@@ -47,7 +48,7 @@
@Override
public int maxOutValueRegister() {
- throw new Unreachable();
+ return Constants.U16BIT_MAX;
}
@Override
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 b982b52..5de9f00 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
@@ -1814,20 +1814,25 @@
}
}
- private void addLiveRange(Value v, BasicBlock b, int end) {
- int firstInstructionInBlock = b.entry().getNumber();
- int instructionsSize = b.getInstructions().size() * INSTRUCTION_NUMBER_DELTA;
+ private static void addLiveRange(
+ Value value,
+ BasicBlock block,
+ int end,
+ List<LiveIntervals> liveIntervals,
+ InternalOptions options) {
+ int firstInstructionInBlock = block.entry().getNumber();
+ int instructionsSize = block.getInstructions().size() * INSTRUCTION_NUMBER_DELTA;
int lastInstructionInBlock =
firstInstructionInBlock + instructionsSize - INSTRUCTION_NUMBER_DELTA;
int instructionNumber;
- if (v.isPhi()) {
+ if (value.isPhi()) {
instructionNumber = firstInstructionInBlock;
} else {
- Instruction instruction = v.definition;
+ Instruction instruction = value.definition;
instructionNumber = instruction.getNumber();
}
- if (v.getLiveIntervals() == null) {
- Value current = v.getStartOfConsecutive();
+ if (value.getLiveIntervals() == null) {
+ Value current = value.getStartOfConsecutive();
LiveIntervals intervals = new LiveIntervals(current);
while (true) {
liveIntervals.add(intervals);
@@ -1841,17 +1846,18 @@
intervals = nextIntervals;
}
}
- LiveIntervals intervals = v.getLiveIntervals();
+ LiveIntervals intervals = value.getLiveIntervals();
if (firstInstructionInBlock <= instructionNumber &&
instructionNumber <= lastInstructionInBlock) {
- if (v.isPhi()) {
+ if (value.isPhi()) {
// Phis need to interfere with spill restore moves inserted before the instruction because
// the phi value is defined on the inflowing edge.
instructionNumber--;
}
intervals.addRange(new LiveRange(instructionNumber, end));
- if (!v.isPhi()) {
- int constraint = v.definition.maxOutValueRegister();
+ assert unconstrainedForCf(intervals.getRegisterLimit(), options);
+ if (!options.outputClassFiles && !value.isPhi()) {
+ int constraint = value.definition.maxOutValueRegister();
intervals.addUse(new LiveIntervalsUse(instructionNumber, constraint));
}
} else {
@@ -1859,10 +1865,18 @@
}
}
+ private void computeLiveRanges() {
+ computeLiveRanges(options, code, liveAtEntrySets, liveIntervals);
+ }
+
/**
* Compute live ranges based on liveAtEntry sets for all basic blocks.
*/
- private void computeLiveRanges() {
+ public static void computeLiveRanges(
+ InternalOptions options,
+ IRCode code,
+ Map<BasicBlock, Set<Value>> liveAtEntrySets,
+ List<LiveIntervals> liveIntervals) {
for (BasicBlock block : code.topologicallySortedBlocks()) {
Set<Value> live = new HashSet<>();
List<BasicBlock> successors = block.getSuccessors();
@@ -1886,7 +1900,7 @@
if (phiOperands.contains(value)) {
end--;
}
- addLiveRange(value, block, end);
+ addLiveRange(value, block, end, liveIntervals, options);
}
ListIterator<Instruction> iterator =
block.getInstructions().listIterator(block.getInstructions().size());
@@ -1898,19 +1912,29 @@
// the instruction. This will typically be instructions that can have side effects even
// if their output is not used.
if (!definition.isUsed()) {
- addLiveRange(definition, block, instruction.getNumber() + INSTRUCTION_NUMBER_DELTA);
+ addLiveRange(
+ definition,
+ block,
+ instruction.getNumber() + INSTRUCTION_NUMBER_DELTA,
+ liveIntervals,
+ options);
+ assert !options.outputClassFiles || instruction.isArgument()
+ : "Arguments should be the only potentially unused local in CF";
}
live.remove(definition);
}
for (Value use : instruction.inValues()) {
- if (!live.contains(use) && use.needsRegister()) {
- live.add(use);
- addLiveRange(use, block, instruction.getNumber());
- }
if (use.needsRegister()) {
- LiveIntervals useIntervals = use.getLiveIntervals();
- useIntervals.addUse(
- new LiveIntervalsUse(instruction.getNumber(), instruction.maxInValueRegister()));
+ assert unconstrainedForCf(instruction.maxInValueRegister(), options);
+ if (!live.contains(use)) {
+ live.add(use);
+ addLiveRange(use, block, instruction.getNumber(), liveIntervals, options);
+ }
+ if (!options.outputClassFiles) {
+ int inConstraint = instruction.maxInValueRegister();
+ LiveIntervals useIntervals = use.getLiveIntervals();
+ useIntervals.addUse(new LiveIntervalsUse(instruction.getNumber(), inConstraint));
+ }
}
}
if (options.debug) {
@@ -1919,7 +1943,7 @@
assert use.needsRegister();
if (!live.contains(use)) {
live.add(use);
- addLiveRange(use, block, number);
+ addLiveRange(use, block, number, liveIntervals, options);
}
}
}
@@ -1927,6 +1951,10 @@
}
}
+ private static boolean unconstrainedForCf(int constraint, InternalOptions options) {
+ return !options.outputClassFiles || constraint == Constants.U16BIT_MAX;
+ }
+
private void clearUserInfo() {
code.blocks.forEach(BasicBlock::clearUserInfo);
}