Remove argument sentinel values in register allocator
Change-Id: I8999a6d5a0260d4f90fa8db55e2e3829d532de46
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 c6477f3..dbce627 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
@@ -75,7 +75,6 @@
public static final int REGISTER_CANDIDATE_NOT_FOUND = -1;
public static final int MIN_CONSTANT_FREE_FOR_POSITIONS = 5;
- private static final int RESERVED_MOVE_EXCEPTION_REGISTER = 0;
private enum ArgumentReuseMode {
ALLOW_ARGUMENT_REUSE,
@@ -113,8 +112,6 @@
// The max register number that will fit in any dex instruction encoding.
private static final int MAX_SMALL_REGISTER = Constants.U4BIT_MAX;
- // We put one sentinel in front of the argument chain and one after the argument chain.
- private static final int NUMBER_OF_SENTINEL_REGISTERS = 2;
// The code for which to allocate registers.
private final IRCode code;
@@ -125,8 +122,8 @@
// Mapping from basic blocks to the set of values live at entry to that basic block.
private Map<BasicBlock, Set<Value>> liveAtEntrySets;
- // The sentinel value starting the chain of linked argument values.
- private Value preArgumentSentinelValue = null;
+ // The value of the first argument, or null if the method has no arguments.
+ private Value firstArgumentValue;
// The set of registers that are free for allocation.
private TreeSet<Integer> freeRegisters = new TreeSet<>();
@@ -157,9 +154,10 @@
// register.
private boolean hasDedicatedMoveExceptionRegister = false;
+ // We allocate a dedicated move exception register right after the arguments.
private int getMoveExceptionRegister() {
- return numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS +
- RESERVED_MOVE_EXCEPTION_REGISTER;
+ assert hasDedicatedMoveExceptionRegister;
+ return numberOfArgumentRegisters;
}
private int getNextUnusedRegisterNumber() {
@@ -515,12 +513,9 @@
// Compute the set of registers that is used based on all live intervals.
Set<Integer> usedRegisters = new HashSet<>();
for (LiveIntervals intervals : liveIntervals) {
- // Argument sentinel values do not occupy any registers.
- if (!isArgumentSentinelIntervals(intervals)) {
- addRegisterIfUsed(usedRegisters, intervals);
- for (LiveIntervals childIntervals : intervals.getSplitChildren()) {
- addRegisterIfUsed(usedRegisters, childIntervals);
- }
+ addRegisterIfUsed(usedRegisters, intervals);
+ for (LiveIntervals childIntervals : intervals.getSplitChildren()) {
+ addRegisterIfUsed(usedRegisters, childIntervals);
}
}
// Additionally, we have used temporary registers for parallel move scheduling, those
@@ -540,11 +535,6 @@
unusedRegisters = computed;
}
- private boolean isArgumentSentinelIntervals(LiveIntervals intervals) {
- return intervals.isArgumentInterval() &&
- (intervals.getPreviousConsecutive() == null || intervals.getNextConsecutive() == null);
- }
-
private void addRegisterIfUsed(Set<Integer> used, LiveIntervals intervals) {
boolean unused = intervals.isSpilledAndRematerializable(this);
if (!unused) {
@@ -567,7 +557,7 @@
@Override
public int registersUsed() {
- int numberOfRegister = maxRegisterNumber + 1 - NUMBER_OF_SENTINEL_REGISTERS;
+ int numberOfRegister = maxRegisterNumber + 1;
if (unusedRegisters != null) {
return numberOfRegister - unusedRegisters[unusedRegisters.length - 1];
}
@@ -627,7 +617,7 @@
// for all uses and thereby avoid moving argument values around.
private boolean unsplitArguments() {
boolean argumentRegisterUnsplit = false;
- Value current = preArgumentSentinelValue;
+ Value current = firstArgumentValue;
while (current != null) {
LiveIntervals intervals = current.getLiveIntervals();
assert intervals.getRegisterLimit() == Constants.U16BIT_MAX;
@@ -701,15 +691,12 @@
assert allocated != NO_REGISTER;
assert allocated >= 0;
int register;
- if (allocated < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS) {
+ if (allocated < numberOfArgumentRegisters) {
// For the |numberOfArguments| first registers map to the correct argument register.
- // Due to the sentinel register in front of the arguments, the register number returned is
- // the argument number starting from one.
- register = maxRegisterNumber - (numberOfArgumentRegisters - allocated)
- - NUMBER_OF_SENTINEL_REGISTERS;
+ register = maxRegisterNumber - (numberOfArgumentRegisters - allocated - 1);
} else {
// For everything else use the lower numbers.
- register = allocated - numberOfArgumentRegisters - NUMBER_OF_SENTINEL_REGISTERS;
+ register = allocated - numberOfArgumentRegisters;
}
return register;
}
@@ -730,8 +717,10 @@
private boolean performLinearScan(ArgumentReuseMode mode) {
unhandled.addAll(liveIntervals);
- LiveIntervals argumentInterval = preArgumentSentinelValue.getLiveIntervals();
- while (argumentInterval != null) {
+
+ Value argumentValue = firstArgumentValue;
+ while (argumentValue != null) {
+ LiveIntervals argumentInterval = argumentValue.getLiveIntervals();
assert argumentInterval.getRegister() != NO_REGISTER;
unhandled.remove(argumentInterval);
if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
@@ -758,7 +747,7 @@
}
}
}
- argumentInterval = argumentInterval.getNextConsecutive();
+ argumentValue = argumentValue.getNextConsecutive();
}
// We have to be careful when it comes to the register allocated for a move exception
@@ -783,7 +772,6 @@
unhandled.remove(intervals);
moveExceptionIntervals.add(intervals);
if (moveExceptionRegister == NO_REGISTER) {
- assert RESERVED_MOVE_EXCEPTION_REGISTER == 0;
moveExceptionRegister = getFreeConsecutiveRegisters(1);
assert moveExceptionRegister == getMoveExceptionRegister();
assert !freeRegisters.contains(moveExceptionRegister);
@@ -957,6 +945,7 @@
allocateLinkedIntervals(destIntervals);
// Restore the register allocation state.
freeRegisters = savedFreeRegisters;
+ // In case maxRegisterNumber has changed, update freeRegisters.
for (int i = savedUnusedRegisterNumber, j = getNextUnusedRegisterNumber(); i < j; i++) {
freeRegisters.add(i);
}
@@ -993,7 +982,6 @@
// Exclude move exception register if the first interval overlaps a move exception interval.
if (overlapsMoveExceptionInterval(current) &&
freeRegisters.remove(getMoveExceptionRegister())) {
- assert RESERVED_MOVE_EXCEPTION_REGISTER == 0;
excludedRegisters.add(getMoveExceptionRegister());
}
// Select registers.
@@ -1260,13 +1248,9 @@
}
if (registerConstraint < Constants.U16BIT_MAX) {
- // We always have argument sentinels that will not actually occupy registers. Therefore, we
- // allow the use of NUMBER_OF_SENTINEL_REGISTERS more than the limit.
- registerConstraint += NUMBER_OF_SENTINEL_REGISTERS;
if (mode == ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE) {
// We know that none of the argument registers will be reused. Therefore, we allow the
- // use of number of arguments more registers. (We will never use number of arguments +
- // number of sentinel registers of them.)
+ // use of number of arguments more registers.
registerConstraint += numberOfArgumentRegisters;
}
}
@@ -1275,26 +1259,18 @@
RegisterPositions freePositions = new RegisterPositions(registerConstraint + 1);
if (mode == ArgumentReuseMode.ALLOW_ARGUMENT_REUSE) {
- // The sentinel registers cannot be used and we block them.
- freePositions.set(0, 0);
if (options.debug && !code.method.accessFlags.isStatic()) {
// If we are generating debug information, we pin the this value register since the
// debugger expects to always be able to find it in the input register.
assert numberOfArgumentRegisters > 0;
- assert preArgumentSentinelValue.getNextConsecutive().requiredRegisters() == 1;
- freePositions.set(1, 0);
- }
- int lastSentinelRegister = numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS - 1;
- if (lastSentinelRegister <= registerConstraint) {
- freePositions.set(lastSentinelRegister, 0);
+ assert firstArgumentValue != null && firstArgumentValue.requiredRegisters() == 1;
+ freePositions.set(0, 0);
}
} else {
// Argument reuse is not allowed and we block all the argument registers so that
// arguments are never free.
- for (int i = 0; i < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS; i++) {
- if (i <= registerConstraint) {
- freePositions.set(i, 0);
- }
+ for (int i = 0; i < numberOfArgumentRegisters && i <= registerConstraint; i++) {
+ freePositions.set(i, 0);
}
}
@@ -1541,8 +1517,11 @@
active.add(unhandledInterval);
}
- private int getLargestCandidate(int registerConstraint, RegisterPositions freePositions,
- boolean needsRegisterPair, RegisterPositions.Type type) {
+ private int getLargestCandidate(
+ int registerConstraint,
+ RegisterPositions freePositions,
+ boolean needsRegisterPair,
+ RegisterPositions.Type type) {
int candidate = REGISTER_CANDIDATE_NOT_FOUND;
int largest = -1;
@@ -1552,6 +1531,10 @@
}
int freePosition = 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|).
+ continue;
+ }
if (i >= registerConstraint) {
break;
}
@@ -1615,11 +1598,8 @@
private void allocateBlockedRegister(LiveIntervals unhandledInterval) {
int registerConstraint = unhandledInterval.getRegisterLimit();
if (registerConstraint < Constants.U16BIT_MAX) {
- // We always have argument sentinels that will not actually occupy registers. Therefore, we
- // allow the use of NUMBER_OF_SENTINEL_REGISTERS more than the limit. Additionally, we never
- // reuse argument registers and therefore allow the use of numberOfArgumentRegisters as well.
+ // We never reuse argument registers and therefore allow the use of numberOfArgumentRegisters.
registerConstraint += numberOfArgumentRegisters;
- registerConstraint += NUMBER_OF_SENTINEL_REGISTERS;
}
// Initialize all candidate registers to Integer.MAX_VALUE.
@@ -1657,7 +1637,7 @@
// Disallow the reuse of argument registers by always treating them as being used
// at instruction number 0.
- for (int i = 0; i < numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS; i++) {
+ for (int i = 0; i < numberOfArgumentRegisters; i++) {
usePositions.set(i, 0);
}
@@ -1949,8 +1929,7 @@
}
private void insertMoves() {
- SpillMoveSet spillMoves =
- new SpillMoveSet(this, code, numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS);
+ SpillMoveSet spillMoves = new SpillMoveSet(this, code, numberOfArgumentRegisters);
for (LiveIntervals intervals : liveIntervals) {
if (intervals.hasSplits()) {
LiveIntervals current = intervals;
@@ -2261,53 +2240,36 @@
return true;
}
- private Value createSentinelRegisterValue() {
- return createValue(ValueType.OBJECT);
- }
-
- private LiveIntervals createSentinelLiveInterval(Value sentinelValue) {
- LiveIntervals sentinelInterval = new LiveIntervals(sentinelValue);
- sentinelInterval.addRange(LiveRange.INFINITE);
- liveIntervals.add(sentinelInterval);
- return sentinelInterval;
- }
-
- private void linkArgumentValues() {
- Value current = preArgumentSentinelValue = createSentinelRegisterValue();
- for (Value argument : code.collectArguments()) {
- current.linkTo(argument);
- current = argument;
- }
- Value endSentinel = createSentinelRegisterValue();
- current.linkTo(endSentinel);
- }
-
- private void setupArgumentLiveIntervals() {
- Value current = preArgumentSentinelValue;
- LiveIntervals currentIntervals = createSentinelLiveInterval(current);
- current = current.getNextConsecutive();
+ private void createArgumentLiveIntervals(List<Value> arguments) {
int index = 0;
- while (current.getNextConsecutive() != null) {
+ for (Value argument : arguments) {
// Add a live range to this value from the beginning of the block up to the argument
// instruction to avoid dead arguments without a range. This may create an actually empty
// range like [0,0[ but that works, too.
- LiveIntervals argumentInterval = new LiveIntervals(current);
- argumentInterval.addRange(new LiveRange(0, index * INSTRUCTION_NUMBER_DELTA));
- index++;
+ LiveIntervals argumentInterval = new LiveIntervals(argument);
+ argumentInterval.addRange(new LiveRange(0, index));
liveIntervals.add(argumentInterval);
- currentIntervals.link(argumentInterval);
- currentIntervals = argumentInterval;
- current = current.getNextConsecutive();
+ index += INSTRUCTION_NUMBER_DELTA;
}
- currentIntervals.link(createSentinelLiveInterval(current));
+ }
+
+ private void linkArgumentValuesAndIntervals(List<Value> arguments) {
+ if (!arguments.isEmpty()) {
+ Value last = firstArgumentValue = arguments.get(0);
+ for (int i = 1; i < arguments.size(); ++i) {
+ Value next = arguments.get(i);
+ last.linkTo(next);
+ last.getLiveIntervals().link(next.getLiveIntervals());
+ last = next;
+ }
+ }
}
private void insertArgumentMoves() {
// Record the constraint that incoming arguments are in consecutive registers.
- // We also insert sentinel registers before and after the arguments at this
- // point.
- linkArgumentValues();
- setupArgumentLiveIntervals();
+ List<Value> arguments = code.collectArguments();
+ createArgumentLiveIntervals(arguments);
+ linkArgumentValuesAndIntervals(arguments);
for (BasicBlock block : code.blocks) {
InstructionListIterator it = block.listIterator();
while (it.hasNext()) {
@@ -2336,30 +2298,32 @@
private void pinArgumentRegisters() {
// Special handling for arguments. Pin their register.
- int register = 0;
- Value current = preArgumentSentinelValue;
- while (current != null) {
- LiveIntervals argumentLiveInterval = current.getLiveIntervals();
- // Pin the argument register. We use getFreeConsecutiveRegisters to make sure that we update
- // the max register number.
- register = getFreeConsecutiveRegisters(argumentLiveInterval.requiredRegisters());
- assignRegister(argumentLiveInterval, register);
- current = current.getNextConsecutive();
+ if (firstArgumentValue != null) {
+ int register = 0;
+ Value current = firstArgumentValue;
+ while (current != null) {
+ LiveIntervals argumentLiveInterval = current.getLiveIntervals();
+ // Pin the argument register. We use getFreeConsecutiveRegisters to make sure that we update
+ // the max register number.
+ register = getFreeConsecutiveRegisters(argumentLiveInterval.requiredRegisters());
+ assignRegister(argumentLiveInterval, register);
+ current = current.getNextConsecutive();
+ }
+ // If the last argument is wide, then its register will be numberOfArgumentRegisters - 2.
+ assert register == numberOfArgumentRegisters - 1 || register == numberOfArgumentRegisters - 2;
}
- assert register == numberOfArgumentRegisters + NUMBER_OF_SENTINEL_REGISTERS - 1;
}
- private int getFreeConsecutiveRegisters(int numberOfRegister) {
- List<Integer> unused = new ArrayList<>();
- int first = getNextFreeRegister();
+ private int getFreeConsecutiveRegisters(int numberOfRegisters) {
+ Iterator<Integer> freeRegistersIterator = freeRegisters.iterator();
+ int first = getNextFreeRegister(freeRegistersIterator);
int current = first;
- while ((current - first + 1) != numberOfRegister) {
- for (int i = 0; i < numberOfRegister - 1; i++) {
- int next = getNextFreeRegister();
- if (next != current + 1) {
- for (int j = first; j <= current; j++) {
- unused.add(j);
- }
+ while (current - first + 1 != numberOfRegisters) {
+ for (int i = 0; i < numberOfRegisters - 1; i++) {
+ int next = getNextFreeRegister(freeRegistersIterator);
+ // We cannot allow that some are argument registers and some or not, because they will no
+ // longer be consecutive if we later decide to increment maxRegisterNumber.
+ if (next != current + 1 || next == numberOfArgumentRegisters) {
first = next;
current = first;
break;
@@ -2367,13 +2331,21 @@
current++;
}
}
- freeRegisters.addAll(unused);
+ for (int register = first; register < first + numberOfRegisters; ++register) {
+ freeRegisters.remove(register);
+ }
+ // Either all the consecutive registers are from the argument registers, or all are from the
+ // non-argument registers.
+ assert (first < numberOfArgumentRegisters
+ && first + numberOfRegisters - 1 < numberOfArgumentRegisters)
+ || (first >= numberOfArgumentRegisters
+ && first + numberOfRegisters - 1 >= numberOfArgumentRegisters);
return first;
}
- private int getNextFreeRegister() {
- if (freeRegisters.size() > 0) {
- return freeRegisters.pollFirst();
+ private int getNextFreeRegister(Iterator<Integer> freeRegistersIterator) {
+ if (freeRegistersIterator.hasNext()) {
+ return freeRegistersIterator.next();
}
maxRegisterNumber = getNextUnusedRegisterNumber();
return maxRegisterNumber;