Update insertion of argument moves
This CL changes the way we insert moves for invoke-range instructions with at least 17 argument registers. With this change, the move for an argument of an invoke-range instruction will be inserted immediately after its definition (if the argument is defined in the same block as the invoke), rather than right before the invoke-range instruction.
With the current implementation, there is a high risk that we will need to spill the arguments before they get moved to the correct register right before the invoke.
Change-Id: I1db77d50b3d2bff909ea2a31283a047e74d21ec5
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
index 241e9ef..24e6eb5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlock.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
+import static com.android.tools.r8.ir.code.IRCode.INSTRUCTION_NUMBER_DELTA;
+
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.DebugLocalInfo;
@@ -409,6 +411,14 @@
this.number = number;
}
+ public int numberInstructions(int nextInstructionNumber) {
+ for (Instruction instruction : instructions) {
+ instruction.setNumber(nextInstructionNumber);
+ nextInstructionNumber += INSTRUCTION_NUMBER_DELTA;
+ }
+ return nextInstructionNumber;
+ }
+
public LinkedList<Instruction> getInstructions() {
return instructions;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index d437da3..beaa62b 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -556,10 +556,7 @@
public ImmutableList<BasicBlock> numberInstructions() {
ImmutableList<BasicBlock> blocks = topologicallySortedBlocks();
for (BasicBlock block : blocks) {
- for (Instruction instruction : block.getInstructions()) {
- instruction.setNumber(nextInstructionNumber);
- nextInstructionNumber += INSTRUCTION_NUMBER_DELTA;
- }
+ nextInstructionNumber = block.numberInstructions(nextInstructionNumber);
}
return blocks;
}
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 aca1f85..f0f5b36 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
@@ -2351,6 +2351,23 @@
if (invoke.requiredArgumentRegisters() > 5 && !argumentsAreAlreadyLinked(invoke)) {
List<Value> arguments = invoke.arguments();
Value previous = null;
+
+ PriorityQueue<Move> insertAtDefinition = null;
+ if (invoke.requiredArgumentRegisters() > 16) {
+ insertAtDefinition =
+ new PriorityQueue<>(
+ (x, y) -> x.src().definition.getNumber() - y.src().definition.getNumber());
+
+ // Number the instructions in this basic block such that we can order the moves according
+ // to the positions of the instructions that define the srcs of the moves. Note that this
+ // is a local numbering of the instructions. These instruction numbers will be recomputed
+ // just before the liveness analysis.
+ BasicBlock block = invoke.getBlock();
+ if (block.entry().getNumber() == -1) {
+ block.numberInstructions(0);
+ }
+ }
+
for (int i = 0; i < arguments.size(); i++) {
Value argument = arguments.get(i);
Value newArgument = argument;
@@ -2371,15 +2388,65 @@
newArgument = createValue(argument.outType());
Move move = new Move(newArgument, argument);
move.setBlock(invoke.getBlock());
- move.setPosition(invoke.getPosition());
replaceArgument(invoke, i, newArgument);
- insertAt.add(move);
+
+ boolean argumentIsDefinedInSameBlock =
+ argument.definition != null && argument.definition.getBlock() == invoke.getBlock();
+ if (invoke.requiredArgumentRegisters() > 16 && argumentIsDefinedInSameBlock) {
+ // Heuristic: Insert the move immediately after the argument. This increases the
+ // likelyhood that we will be able to move the argument directly into the register it
+ // needs to be in for the ranged invoke.
+ //
+ // If we instead were to insert the moves immediately before the ranged invoke when
+ // there are many arguments, then there is a high risk that we will need to spill the
+ // arguments before they get moved to the correct register right before the invoke.
+ assert move.src().definition.getNumber() >= 0;
+ insertAtDefinition.add(move);
+ move.setPosition(argument.definition.getPosition());
+ } else {
+ insertAt.add(move);
+ move.setPosition(invoke.getPosition());
+ }
}
if (previous != null) {
previous.linkTo(newArgument);
}
previous = newArgument;
}
+
+ if (insertAtDefinition != null && !insertAtDefinition.isEmpty()) {
+ generateArgumentMovesAtDefinitions(invoke, insertAtDefinition, insertAt);
+ }
+ }
+ }
+
+ private void generateArgumentMovesAtDefinitions(
+ Invoke invoke, PriorityQueue<Move> insertAtDefinition, InstructionListIterator insertAt) {
+ Move move = insertAtDefinition.poll();
+ // Rewind instruction iterator to the position where the first move needs to be inserted.
+ Instruction previousDefinition = move.src().definition;
+ while (insertAt.peekPrevious() != previousDefinition) {
+ insertAt.previous();
+ }
+ // Insert the instructions one by one after their definition.
+ insertAt.add(move);
+ while (!insertAtDefinition.isEmpty()) {
+ move = insertAtDefinition.poll();
+ Instruction currentDefinition = move.src().definition;
+ assert currentDefinition.getNumber() >= previousDefinition.getNumber();
+ if (currentDefinition.getNumber() > previousDefinition.getNumber()) {
+ // Move the instruction iterator forward to where this move needs to be inserted.
+ while (insertAt.peekPrevious() != currentDefinition) {
+ insertAt.next();
+ }
+ }
+ insertAt.add(move);
+ // Update state.
+ previousDefinition = currentDefinition;
+ }
+ // Move the instruction iterator forward to its old position.
+ while (insertAt.peekNext() != invoke) {
+ insertAt.next();
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java
index 8724d43..d52b84a 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMove.java
@@ -26,7 +26,7 @@
this.dst = dst;
this.src = LiveIntervals.NO_REGISTER;
this.type = type;
- assert definition.isConstInstruction() || definition.isArgument();
+ assert definition.isOutConstant() || definition.isArgument();
this.definition = definition;
}
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
index afec916..b1e5e4f 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.code.MoveType;
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.code.Argument;
+import com.android.tools.r8.ir.code.ConstInstruction;
import com.android.tools.r8.ir.code.ConstNumber;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.FixedRegisterValue;
@@ -132,15 +133,16 @@
private Integer createMove(RegisterMove move) {
Instruction instruction;
if (move.definition != null) {
- Instruction definition = move.definition;
- if (definition.isArgument()) {
- Argument argument = definition.asArgument();
+ if (move.definition.isArgument()) {
+ Argument argument = move.definition.asArgument();
int argumentRegister = argument.outValue().getLiveIntervals().getRegister();
Value to = new FixedRegisterValue(argument.outType(), move.dst);
Value from = new FixedRegisterValue(argument.outType(), argumentRegister);
instruction = new Move(to, from);
} else {
- Value to = new FixedRegisterValue(definition.outType(), move.dst);
+ assert move.definition.isOutConstant();
+ Value to = new FixedRegisterValue(move.definition.outType(), move.dst);
+ ConstInstruction definition = move.definition.getOutConstantConstInstruction();
if (definition.isConstNumber()) {
instruction = new ConstNumber(to, definition.asConstNumber().getRawValue());
} else if (definition.isConstString()) {