Allow treating DebugLocalChange instructions as identical if they are.
This enables common suffix sharing for spill moves inserted in catch
blocks. Previously, a DebugLocalChange event at the end of the moves
would prevent us from sharing any of the moves.
This saves around 30kB on the dexed framework.
R=zerny@google.com
Change-Id: I06ae0bbe1542b11bcec0b8bb6b560c1a2fe64754
diff --git a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
index 4d7d120..bf39f6f 100644
--- a/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/DebugLocalInfo.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+
public class DebugLocalInfo {
public final DexString name;
public final DexType type;
@@ -14,6 +16,25 @@
this.signature = signature;
}
+ public static boolean localsInfoMapsEqual(
+ Int2ReferenceMap<DebugLocalInfo> set0, Int2ReferenceMap<DebugLocalInfo> set1) {
+ if (set0 == null) {
+ return set1 == null;
+ }
+ if (set1 == null) {
+ return false;
+ }
+ if (set0.keySet().size() != set1.keySet().size()) {
+ return false;
+ }
+ for (int i : set0.keySet()) {
+ if (!set0.get(i).equals(set1.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
@Override
public boolean equals(Object other) {
if (this == other) {
diff --git a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
index 55bb03d..f579fd6 100644
--- a/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalsChange.java
@@ -52,7 +52,9 @@
@Override
public boolean identicalNonValueParts(Instruction other) {
assert other.isDebugLocalsChange();
- return false;
+ DebugLocalsChange o = (DebugLocalsChange) other;
+ return DebugLocalInfo.localsInfoMapsEqual(ending, o.ending)
+ && DebugLocalInfo.localsInfoMapsEqual(starting, o.starting);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
index 9a70864..4ad05cc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/PeepholeOptimizer.java
@@ -3,8 +3,10 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.optimize;
+import com.android.tools.r8.graph.DebugLocalInfo;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.ConstNumber;
+import com.android.tools.r8.ir.code.DebugLocalsChange;
import com.android.tools.r8.ir.code.Goto;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
@@ -15,6 +17,9 @@
import com.android.tools.r8.ir.regalloc.LiveIntervals;
import com.android.tools.r8.ir.regalloc.RegisterAllocator;
import com.google.common.base.Equivalence.Wrapper;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap.Entry;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
@@ -99,16 +104,30 @@
}
private static BasicBlock createAndInsertBlockForSuffix(
- int blockNumber, int suffixSize, List<BasicBlock> preds, BasicBlock block) {
+ int blockNumber, int suffixSize, List<BasicBlock> preds, BasicBlock successorBlock) {
BasicBlock newBlock = BasicBlock.createGotoBlock(blockNumber);
BasicBlock first = preds.get(0);
InstructionListIterator from = first.listIterator(first.getInstructions().size() - 1);
+ Int2ReferenceMap<DebugLocalInfo> newBlockEntryLocals = successorBlock.getLocalsAtEntry() == null
+ ? null
+ : new Int2ReferenceOpenHashMap<>(successorBlock.getLocalsAtEntry());
boolean movedThrowingInstruction = false;
for (int i = 0; i < suffixSize; i++) {
Instruction instruction = from.previous();
movedThrowingInstruction = movedThrowingInstruction || instruction.instructionTypeCanThrow();
newBlock.getInstructions().addFirst(instruction);
instruction.setBlock(newBlock);
+ if (instruction.isDebugLocalsChange()) {
+ // Replay the debug local changes backwards to compute the entry state.
+ assert newBlockEntryLocals != null;
+ DebugLocalsChange change = instruction.asDebugLocalsChange();
+ for (int starting : change.getStarting().keySet()) {
+ newBlockEntryLocals.remove(starting);
+ }
+ for (Entry<DebugLocalInfo> ending : change.getEnding().int2ReferenceEntrySet()) {
+ newBlockEntryLocals.put(ending.getIntKey(), ending.getValue());
+ }
+ }
}
if (movedThrowingInstruction && first.hasCatchHandlers()) {
newBlock.transferCatchHandlers(first);
@@ -121,13 +140,14 @@
}
instructions.add(exit);
newBlock.getPredecessors().add(pred);
- pred.replaceSuccessor(block, newBlock);
- block.getPredecessors().remove(pred);
+ pred.replaceSuccessor(successorBlock, newBlock);
+ successorBlock.getPredecessors().remove(pred);
if (movedThrowingInstruction) {
pred.clearCatchHandlers();
}
}
- newBlock.link(block);
+ newBlock.setLocalsAtEntry(newBlockEntryLocals);
+ newBlock.link(successorBlock);
return newBlock;
}