Minor cleanup of move scheduler
Bug: b/374702812
Change-Id: Id84958fe0b4c14030f4a6a42b14d5acbf75dab7e
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 be68587..963bc1f 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
@@ -3,9 +3,13 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.regalloc;
+import static com.android.tools.r8.ir.regalloc.LiveIntervals.NO_REGISTER;
+
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Instruction;
-import java.util.Map;
+import com.android.tools.r8.utils.ObjectUtils;
+import it.unimi.dsi.fastutil.ints.Int2IntMap;
+import java.util.Objects;
import java.util.Set;
// Register moves used by the spilling register allocator. These are used both for spill and
@@ -27,51 +31,56 @@
public RegisterMove(int dst, TypeElement type, Instruction definition) {
assert definition.isOutConstant();
this.dst = dst;
- this.src = LiveIntervals.NO_REGISTER;
+ this.src = NO_REGISTER;
this.definition = definition;
this.type = type;
}
- private boolean writes(int register) {
- if (type.isWidePrimitive() && (dst + 1) == register) {
+ public boolean writes(int register, boolean otherIsWide) {
+ if (dst == register) {
return true;
}
- return dst == register;
+ if (isWide() && dst + 1 == register) {
+ return true;
+ }
+ if (otherIsWide && dst == register + 1) {
+ return true;
+ }
+ return false;
}
- @SuppressWarnings("ReferenceEquality")
- public boolean isBlocked(Set<RegisterMove> moveSet, Map<Integer, Integer> valueMap) {
+ public boolean isBlocked(Set<RegisterMove> moveSet, Int2IntMap valueMap) {
for (RegisterMove move : moveSet) {
- if (move.src == LiveIntervals.NO_REGISTER) {
+ if (isIdentical(move) || move.src == NO_REGISTER) {
continue;
}
- if (move != this) {
- if (writes(valueMap.get(move.src))) {
- return true;
- }
- if (move.type.isWidePrimitive()) {
- if (writes(valueMap.get(move.src) + 1)) {
- return true;
- }
- }
+ if (writes(valueMap.get(move.src), move.isWide())) {
+ return true;
}
}
return false;
}
- @Override
- public int hashCode() {
- return src + dst * 3 + type.hashCode() * 5 + (definition == null ? 0 : definition.hashCode());
+ public boolean isIdentical(RegisterMove move) {
+ return ObjectUtils.identical(this, move);
+ }
+
+ public boolean isWide() {
+ return type.isWidePrimitive();
}
@Override
- @SuppressWarnings("ReferenceEquality")
+ public int hashCode() {
+ return src + dst * 3 + type.hashCode() * 5 + Objects.hashCode(definition);
+ }
+
+ @Override
public boolean equals(Object other) {
if (!(other instanceof RegisterMove)) {
return false;
}
RegisterMove o = (RegisterMove) other;
- return o.src == src && o.dst == dst && o.type == type && o.definition == definition;
+ return o.src == src && o.dst == dst && type.equals(o.type) && o.definition == definition;
}
@Override
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 5251d17..4c60ed6 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
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.regalloc;
+import static com.android.tools.r8.ir.regalloc.LiveIntervals.NO_REGISTER;
+
import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Argument;
@@ -14,15 +16,15 @@
import com.android.tools.r8.ir.code.Move;
import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.ints.Int2IntMap;
+import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArraySet;
import it.unimi.dsi.fastutil.ints.IntSet;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
-import java.util.HashMap;
import java.util.Iterator;
-import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
@@ -31,7 +33,7 @@
private final Set<RegisterMove> moveSet = new TreeSet<>();
// Mapping to keep track of which values currently corresponds to each other.
// This is initially an identity map but changes as we insert moves.
- private final Map<Integer, Integer> valueMap = new HashMap<>();
+ private final Int2IntMap valueMap = new Int2IntOpenHashMap();
// Number of temp registers used to schedule the moves.
private int usedTempRegisters = 0;
// Location at which to insert the scheduled moves.
@@ -46,6 +48,7 @@
this.insertAt = insertAt;
this.tempRegister = tempRegister;
this.position = position;
+ this.valueMap.defaultReturnValue(NO_REGISTER);
}
public RegisterMoveScheduler(InstructionListIterator insertAt, int tempRegister) {
@@ -54,19 +57,17 @@
public void addMove(RegisterMove move) {
moveSet.add(move);
- if (move.src != LiveIntervals.NO_REGISTER) {
+ if (move.src != NO_REGISTER) {
valueMap.put(move.src, move.src);
}
valueMap.put(move.dst, move.dst);
}
- // TODO(b/270398965): Replace LinkedList.
- @SuppressWarnings("JdkObsolete")
public void schedule() {
assert everyDestinationOnlyWrittenOnce();
// Worklist of moves that are ready to be inserted.
- Deque<RegisterMove> worklist = new LinkedList<>();
+ Deque<RegisterMove> worklist = new ArrayDeque<>();
// Initialize worklist with the moves that do not interfere with other moves.
Iterator<RegisterMove> iterator = moveSet.iterator();
@@ -85,10 +86,10 @@
RegisterMove move = worklist.removeFirst();
assert !move.isBlocked(moveSet, valueMap);
// Insert the move.
- Integer generatedDest = createMove(move);
+ int generatedDest = createMove(move);
// Update the value map with the information that dest can be used instead of
// src starting now.
- if (move.src != LiveIntervals.NO_REGISTER) {
+ if (move.src != NO_REGISTER) {
valueMap.put(move.src, generatedDest);
}
// Iterate and find the moves that were blocked because they need to write to
@@ -107,6 +108,9 @@
// temporary registers for its destination value(s).
RegisterMove move = pickMoveToUnblock();
createMoveDestToTemp(move);
+ // TODO(b/375147902): After emitting the newly unblocked move, try to prioritize the moves
+ // that blocked it, so that we free up the temp register, rather than getting overlapping
+ // temporary registers.
worklist.addLast(move);
}
}
@@ -118,9 +122,9 @@
private List<RegisterMove> findMovesWithSrc(int src, TypeElement type) {
List<RegisterMove> result = new ArrayList<>();
- assert src != LiveIntervals.NO_REGISTER;
+ assert src != NO_REGISTER;
for (RegisterMove move : moveSet) {
- if (move.src == LiveIntervals.NO_REGISTER) {
+ if (move.src == NO_REGISTER) {
continue;
}
int moveSrc = valueMap.get(move.src);
@@ -135,7 +139,7 @@
return result;
}
- private Integer createMove(RegisterMove move) {
+ private int createMove(RegisterMove move) {
Instruction instruction;
if (move.definition != null) {
if (move.definition.isArgument()) {
@@ -171,18 +175,19 @@
List<RegisterMove> movesWithSrc = findMovesWithSrc(move.dst, move.type);
assert movesWithSrc.size() > 0;
for (RegisterMove moveWithSrc : movesWithSrc) {
- // TODO(ager): For now we always use a new temporary register whenever we have to unblock
- // a move. The move scheduler can have multiple unblocking temps live at the same time
- // and therefore we cannot have just one tempRegister (pair). However, we could check here
- // if the previously used tempRegisters is still needed by any of the moves in the move set
- // (taking the value map into account). If not, we can reuse the temp register instead
- // of generating a new one.
- Value to = new FixedRegisterValue(moveWithSrc.type, tempRegister + usedTempRegisters);
+ // TODO(b/375147902): For now we always use a new temporary register whenever we have to
+ // unblock a move. The move scheduler can have multiple unblocking temps live at the same
+ // time and therefore we cannot have just one tempRegister (pair). However, we could check
+ // here if the previously used tempRegisters is still needed by any of the moves in the move
+ // set (taking the value map into account). If not, we can reuse the temp register instead
+ // of generating a new one.
+ int register = tempRegister + usedTempRegisters;
+ Value to = new FixedRegisterValue(moveWithSrc.type, register);
Value from = new FixedRegisterValue(moveWithSrc.type, valueMap.get(moveWithSrc.src));
Move instruction = new Move(to, from);
instruction.setPosition(position);
insertAt.add(instruction);
- valueMap.put(moveWithSrc.src, tempRegister + usedTempRegisters);
+ valueMap.put(moveWithSrc.src, register);
usedTempRegisters += moveWithSrc.type.requiredRegisters();
}
}