Add DupDupDupPeephole for replacing 3 x dup with dup + dup2
Change-Id: I432efe2c293456f279cd1f9d750640bb0e2a26de
diff --git a/src/main/java/com/android/tools/r8/ir/code/Dup2.java b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
new file mode 100644
index 0000000..decbaec
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/Dup2.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.code;
+
+import com.android.tools.r8.cf.LoadStoreHelper;
+import com.android.tools.r8.cf.code.CfStackInstruction;
+import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
+import com.android.tools.r8.errors.Unreachable;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.conversion.CfBuilder;
+import com.android.tools.r8.ir.conversion.DexBuilder;
+import com.android.tools.r8.ir.optimize.Inliner.ConstraintWithTarget;
+import com.android.tools.r8.ir.optimize.InliningConstraints;
+import com.google.common.collect.ImmutableList;
+
+public class Dup2 extends Instruction {
+
+ public Dup2(StackValues dest, StackValue src1, StackValue src2) {
+ super(dest, ImmutableList.of(src1, src2));
+ assert !src1.getTypeLattice().isWide();
+ assert !src2.getTypeLattice().isWide();
+ assert dest.getStackValues().size() == 4;
+ }
+
+ @Override
+ public void buildDex(DexBuilder builder) {
+ throw new Unreachable("This classfile-specific IR should not be inserted in the Dex backend.");
+ }
+
+ @Override
+ public void buildCf(CfBuilder builder) {
+ builder.add(new CfStackInstruction(Opcode.Dup2));
+ }
+
+ @Override
+ public boolean identicalNonValueNonPositionParts(Instruction other) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int compareNonValueParts(Instruction other) {
+ throw new Unreachable();
+ }
+
+ @Override
+ public int maxInValueRegister() {
+ return 0;
+ }
+
+ @Override
+ public int maxOutValueRegister() {
+ throw new Unreachable();
+ }
+
+ @Override
+ public ConstraintWithTarget inliningConstraint(
+ InliningConstraints inliningConstraints, DexType invocationContext) {
+ return inliningConstraints.forDup2();
+ }
+
+ @Override
+ public void insertLoadAndStores(InstructionListIterator it, LoadStoreHelper helper) {
+ // Intentionally empty. Dup2 is a stack operation.
+ }
+
+ @Override
+ public boolean hasInvariantOutType() {
+ return false;
+ }
+
+ @Override
+ public boolean isDup2() {
+ return true;
+ }
+
+ @Override
+ public Dup2 asDup2() {
+ return this;
+ }
+}
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 90e8a6a..d4229bd 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
@@ -629,6 +629,14 @@
return null;
}
+ public boolean isDup2() {
+ return false;
+ }
+
+ public Dup2 asDup2() {
+ return null;
+ }
+
public boolean isJumpInstruction() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
index cc85e39..4e3f9be 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/InliningConstraints.java
@@ -98,6 +98,10 @@
return ConstraintWithTarget.ALWAYS;
}
+ public ConstraintWithTarget forDup2() {
+ return ConstraintWithTarget.ALWAYS;
+ }
+
public ConstraintWithTarget forInstanceGet(DexField field, DexType invocationContext) {
DexField lookup = graphLense.lookupField(field);
return forFieldInstruction(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
index 6df0447..25c46f8 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/BasicBlockMuncher.java
@@ -19,7 +19,10 @@
// other peepholes to allow for more matches.
private final List<BasicBlockPeephole> destructivePeepholes =
ImmutableList.of(
- new StoreSequenceLoadPeephole(), new StoreLoadPeephole(), new LoadLoadDupPeephole());
+ new StoreSequenceLoadPeephole(),
+ new StoreLoadPeephole(),
+ new LoadLoadDupPeephole(),
+ new DupDupDupPeephole());
private final List<List<BasicBlockPeephole>> allPeepholes =
ImmutableList.of(nonDestructivePeepholes, destructivePeepholes);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/DupDupDupPeephole.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/DupDupDupPeephole.java
new file mode 100644
index 0000000..9b86873
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/DupDupDupPeephole.java
@@ -0,0 +1,91 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.ir.optimize.peepholes;
+
+import com.android.tools.r8.ir.code.Dup;
+import com.android.tools.r8.ir.code.Dup2;
+import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.StackValue;
+import com.android.tools.r8.ir.code.StackValues;
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Peephole that looks for the following pattern:
+ *
+ * <pre>
+ * Dup
+ * Dup
+ * Dup
+ * </pre>
+ *
+ * and replaces with
+ *
+ * <pre>
+ * Dup
+ * Dup2
+ * </pre>
+ */
+public class DupDupDupPeephole implements BasicBlockPeephole {
+
+ private final Point dup1Exp =
+ new Point((i) -> i.isDup() && !i.inValues().get(0).getTypeLattice().isWide());
+ private final Point dup2Exp =
+ new Point((i) -> i.isDup() && !i.inValues().get(0).getTypeLattice().isWide());
+ private final Point dup3Exp =
+ new Point((i) -> i.isDup() && !i.inValues().get(0).getTypeLattice().isWide());
+
+ private final PeepholeLayout layout = PeepholeLayout.lookBackward(dup1Exp, dup2Exp, dup3Exp);
+
+ @Override
+ public boolean match(InstructionListIterator it) {
+ Match match = layout.test(it);
+ if (match == null) {
+ return false;
+ }
+
+ Dup dupTop = dup3Exp.get(match).asDup();
+ Dup dupMiddle = dup2Exp.get(match).asDup();
+ Dup dupBottom = dup1Exp.get(match).asDup();
+
+ StackValue src = (StackValue) dupTop.inValues().get(0);
+ StackValue srcMiddle = (StackValue) dupMiddle.inValues().get(0);
+ StackValue srcBottom = (StackValue) dupBottom.inValues().get(0);
+
+ StackValues tv = (StackValues) dupTop.outValue();
+ StackValues mv = (StackValues) dupMiddle.outValue();
+ StackValues bv = (StackValues) dupBottom.outValue();
+
+ // The stack looks like:
+ // ..., tv0, mv0, bv0, bv1,.. -->
+ // because tv1 was used by dupMiddle and mv1 was used by dupBottom.
+
+ StackValue tv0Dup2 = tv.getStackValues().get(0).duplicate(src.getHeight());
+ StackValue mv0Dup2 = mv.getStackValues().get(0).duplicate(src.getHeight() + 1);
+ StackValue bv0Dup2 = bv.getStackValues().get(0).duplicate(src.getHeight() + 2);
+ StackValue bv1Dup2 = bv.getStackValues().get(1).duplicate(src.getHeight() + 3);
+
+ // Remove tv1 use.
+ srcMiddle.removeUser(dupMiddle);
+ // Remove mv1 use.
+ srcBottom.removeUser(dupBottom);
+ // Replace other uses.
+ tv.getStackValues().get(0).replaceUsers(tv0Dup2);
+ mv.getStackValues().get(0).replaceUsers(mv0Dup2);
+ bv.getStackValues().get(0).replaceUsers(bv0Dup2);
+ bv.getStackValues().get(1).replaceUsers(bv1Dup2);
+
+ StackValues dest = new StackValues(ImmutableList.of(tv0Dup2, mv0Dup2, bv0Dup2, bv1Dup2));
+
+ Dup2 dup2 = new Dup2(dest, tv.getStackValues().get(0), tv.getStackValues().get(1));
+
+ it.removeOrReplaceByDebugLocalRead();
+ it.previous();
+ it.replaceCurrentInstruction(dup2);
+
+ // Reset the pointer
+ PeepholeHelper.resetPrevious(it, 1);
+ return true;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/LoadLoadDupPeephole.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/LoadLoadDupPeephole.java
index 922b56f..eff527c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/LoadLoadDupPeephole.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/LoadLoadDupPeephole.java
@@ -31,12 +31,12 @@
*/
public class LoadLoadDupPeephole implements BasicBlockPeephole {
- private final Point bottomLoadExp =
+ private final Point lastLoadExp =
new Point(PeepholeHelper.withoutLocalInfo(Instruction::isLoad));
- private final Point topLoadExp = new Point(PeepholeHelper.withoutLocalInfo(Instruction::isLoad));
+ private final Point firstLoadExp = new Point(PeepholeHelper.withoutLocalInfo(Instruction::isLoad));
// This searches backwards thus the pattern is built from the bottom.
- private final PeepholeLayout layout = PeepholeLayout.lookBackward(bottomLoadExp, topLoadExp);
+ private final PeepholeLayout layout = PeepholeLayout.lookBackward(lastLoadExp, firstLoadExp);
@Override
public boolean match(InstructionListIterator it) {
@@ -44,24 +44,26 @@
if (match == null) {
return false;
}
- Load bottomLoad = bottomLoadExp.get(match).asLoad();
- Load topLoad = topLoadExp.get(match).asLoad();
- if (topLoad.src() != bottomLoad.src()) {
+ Load lastLoad = lastLoadExp.get(match).asLoad();
+ Load firstLoad = firstLoadExp.get(match).asLoad();
+ if (firstLoad.src() != lastLoad.src()) {
return false;
}
- assert !topLoad.src().hasLocalInfo();
- assert !bottomLoad.src().hasLocalInfo();
+ assert !firstLoad.src().hasLocalInfo();
+ assert !lastLoad.src().hasLocalInfo();
- StackValue src = (StackValue) topLoad.outValue();
- src.removeUser(bottomLoad);
+ StackValue src = (StackValue) firstLoad.outValue();
+ src.removeUser(lastLoad);
int height = src.getHeight();
- StackValue newSrc = src.duplicate(height);
- StackValue newTop = src.duplicate(height + 1);
- StackValues dest = new StackValues(ImmutableList.of(newSrc, newTop));
+ StackValue newFirstLoadOut = src.duplicate(height);
+ StackValue newLastLoadOut = src.duplicate(height + 1);
+ StackValues dest = new StackValues(ImmutableList.of(newFirstLoadOut, newLastLoadOut));
- bottomLoad.outValue().replaceUsers(newSrc);
+ firstLoad.outValue().replaceUsers(newFirstLoadOut);
+ lastLoad.outValue().replaceUsers(newLastLoadOut);
+
it.replaceCurrentInstruction(new Dup(dest, src));
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeHelper.java b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeHelper.java
index 72b9ee6..255e4bb 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/peepholes/PeepholeHelper.java
@@ -14,7 +14,10 @@
public class PeepholeHelper {
public static Predicate<Instruction> withoutLocalInfo(Predicate<Instruction> predicate) {
- return t -> !t.hasInValueWithLocalInfo() && predicate.test(t);
+ return t ->
+ predicate.test(t)
+ && !t.hasInValueWithLocalInfo()
+ && (t.outValue() == null || !t.outValue().hasLocalInfo());
}
public static void swapNextTwoInstructions(InstructionListIterator it) {