Further improve lowering constants for binop/lit8 and binop/lit16
* Don't explicitly number the instructions
* Cache negative path checks between basic blocks
This brings the compilation time of the supplied repro down to 29s (13s
seconds for d8). These numbers are from a P920, and the user time is
4m40s and 2m40s respectively.
On the supplied repro the d8 size saving is 342 bytes of dex when the
binop/2addr check is included (compressed it actually grown with 1837 bytes)
Bug: 145052564
Change-Id: I4c6dcd47474e17effb733c5eecaf9cd0109cb732
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 b2273e9..d71b495 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
@@ -559,30 +559,13 @@
}
public int numberInstructions(int nextInstructionNumber) {
- return numberInstructions(nextInstructionNumber, INSTRUCTION_NUMBER_DELTA);
- }
-
- public int numberInstructions(int nextInstructionNumber, int increment) {
for (Instruction instruction : instructions) {
instruction.setNumber(nextInstructionNumber);
- nextInstructionNumber += increment;
+ nextInstructionNumber += INSTRUCTION_NUMBER_DELTA;
}
return nextInstructionNumber;
}
- public void clearInstructionNumbers() {
- for (Instruction instruction : instructions) {
- instruction.clearNumber();
- }
- }
-
- public boolean hasNoInstructionNumbers() {
- for (Instruction instruction : instructions) {
- assert instruction.getNumber() == -1;
- }
- return true;
- }
-
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 6a130a1..1a34adf 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
@@ -991,25 +991,6 @@
return blocks;
}
- public void numberInstructionsPerBlock() {
- for (BasicBlock block : blocks) {
- block.numberInstructions(0, 1);
- }
- }
-
- public void clearInstructionNumbers() {
- for (BasicBlock block : blocks) {
- block.clearInstructionNumbers();
- }
- }
-
- public boolean hasNoInstructionNumbers() {
- for (BasicBlock block : blocks) {
- assert block.hasNoInstructionNumbers();
- }
- return true;
- }
-
public int numberRemainingInstructions() {
for (Instruction instruction : instructions()) {
if (instruction.getNumber() == -1) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 6893464..8deda3f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -268,6 +268,7 @@
/**
* Returns the basic block containing this instruction.
*/
+ @Override
public BasicBlock getBlock() {
assert block != null;
return block;
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java b/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java
index e9a5347..144bbdb 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionOrPhi.java
@@ -21,4 +21,6 @@
default Phi asPhi() {
return null;
}
+
+ BasicBlock getBlock();
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/Phi.java b/src/main/java/com/android/tools/r8/ir/code/Phi.java
index 3252d67..2f0911d 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Phi.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Phi.java
@@ -67,6 +67,7 @@
return this;
}
+ @Override
public BasicBlock getBlock() {
return block;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 326c17b..3a78425 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -51,6 +51,7 @@
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.InstructionOrPhi;
import com.android.tools.r8.ir.code.IntSwitch;
import com.android.tools.r8.ir.code.Invoke;
import com.android.tools.r8.ir.code.InvokeDirect;
@@ -1646,8 +1647,6 @@
return;
}
- code.numberInstructionsPerBlock();
-
for (BasicBlock block : code.blocks) {
InstructionListIterator instructionIterator = block.listIterator(code);
// Collect all the non constant in values for binop/lit8 or binop/lit16 instructions.
@@ -1669,26 +1668,32 @@
Reference2IntMap<Value> lastUseOfBinopsWithLit8OrLit16NonConstantValues =
new Reference2IntOpenHashMap<>();
lastUseOfBinopsWithLit8OrLit16NonConstantValues.defaultReturnValue(-1);
+ int currentInstructionNumber = block.getInstructions().size();
while (instructionIterator.hasPrevious()) {
Instruction currentInstruction = instructionIterator.previous();
- for (Value value : Iterables.concat(currentInstruction.inValues(), currentInstruction.getDebugValues())) {
+ currentInstructionNumber--;
+ for (Value value :
+ Iterables.concat(currentInstruction.inValues(), currentInstruction.getDebugValues())) {
if (!binopsWithLit8OrLit16NonConstantValues.contains(value)) {
continue;
}
if (!lastUseOfBinopsWithLit8OrLit16NonConstantValues.containsKey(value)) {
- lastUseOfBinopsWithLit8OrLit16NonConstantValues.put(
- value, currentInstruction.getNumber());
+ lastUseOfBinopsWithLit8OrLit16NonConstantValues.put(value, currentInstructionNumber);
}
}
}
// Do the transformation except if the binop can use the binop/2addr format.
+ currentInstructionNumber--;
+ assert currentInstructionNumber == -1;
while (instructionIterator.hasNext()) {
Instruction currentInstruction = instructionIterator.next();
+ currentInstructionNumber++;
if (!isBinopWithLit8OrLit16(currentInstruction)) {
continue;
}
Binop binop = currentInstruction.asBinop();
- if (!canBe2AddrInstruction(binop, lastUseOfBinopsWithLit8OrLit16NonConstantValues)) {
+ if (!canBe2AddrInstruction(
+ binop, currentInstructionNumber, lastUseOfBinopsWithLit8OrLit16NonConstantValues)) {
Value constValue = binopWithLit8OrLit16Constant(currentInstruction);
if (constValue.numberOfAllUsers() > 1) {
// No need to do the transformation if the const value is already used only one time.
@@ -1706,9 +1711,6 @@
}
}
- code.clearInstructionNumbers();
- assert code.hasNoInstructionNumbers();
-
assert code.isConsistentSSA();
}
@@ -1751,55 +1753,46 @@
}
/**
- * Estimate if a binary operation can be a 2addr form or not. It can be a 2addr form when an
+ * Estimate if a binary operation can be a binop/2addr form or not. It can be a 2addr form when an
* argument is no longer needed after the binary operation and can be overwritten. That is
* definitely the case if there is no path between the binary operation and all other usages.
*/
private static boolean canBe2AddrInstruction(
- Binop binop, Reference2IntMap<Value> lastUseOfRelevantValue) {
+ Binop binop, int binopInstructionNumber, Reference2IntMap<Value> lastUseOfRelevantValue) {
Value value = binopWithLit8OrLit16NonConstant(binop);
assert value != null;
- int number = lastUseOfRelevantValue.getInt(value);
- if (number > 0 && number > binop.getNumber()) {
+ int lastUseInstructionNumber = lastUseOfRelevantValue.getInt(value);
+ // The binop instruction is a user, so there is always a last use in the block.
+ assert lastUseInstructionNumber != -1;
+ if (lastUseInstructionNumber > binopInstructionNumber) {
return false;
}
- Iterable<Instruction> users =
+
+ Set<BasicBlock> noPathTo = Sets.newIdentityHashSet();
+ BasicBlock binopBlock = binop.getBlock();
+ Iterable<InstructionOrPhi> users =
value.debugUsers() != null
- ? Iterables.concat(value.uniqueUsers(), value.debugUsers())
- : value.uniqueUsers();
-
- for (Instruction user : users) {
- if (hasPath(binop, user)) {
+ ? Iterables.concat(value.uniqueUsers(), value.debugUsers(), value.uniquePhiUsers())
+ : Iterables.concat(value.uniqueUsers(), value.uniquePhiUsers());
+ for (InstructionOrPhi user : users) {
+ BasicBlock userBlock = user.getBlock();
+ if (userBlock == binopBlock) {
+ // All users in the current block are either before the binop instruction or the
+ // binop instruction itself.
+ continue;
+ }
+ if (noPathTo.contains(userBlock)) {
+ continue;
+ }
+ if (binopBlock.hasPathTo(userBlock)) {
return false;
}
- }
-
- for (Phi user : value.uniquePhiUsers()) {
- if (binop.getBlock().hasPathTo(user.getBlock())) {
- return false;
- }
+ noPathTo.add(userBlock);
}
return true;
}
- /**
- * Return true if there is a path between {@code source} instruction and {@code target}
- * instruction.
- */
- private static boolean hasPath(Instruction source, Instruction target) {
- BasicBlock sourceBlock = source.getBlock();
- BasicBlock targetBlock = target.getBlock();
- if (sourceBlock == targetBlock) {
- // Instructions must be numbered when getting here.
- assert source.getNumber() != -1;
- assert target.getNumber() != -1;
- return source.getNumber() < target.getNumber();
- }
-
- return source.getBlock().hasPathTo(targetBlock);
- }
-
public void shortenLiveRanges(IRCode code) {
// Currently, we are only shortening the live range of ConstNumbers in the entry block
// and ConstStrings with one user.