Merge "Minor extension of method usesRegister"
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 8dd9162..d945dea 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -231,7 +231,7 @@
store.outValue().removeUser(load);
// Remove the store.
it.previous();
- it.remove();
+ it.removeOrReplaceByDebugLocalRead();
// Remove the load.
it.next();
it.remove();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
index 2152364..7befc69 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/ClassProcessor.java
@@ -108,6 +108,8 @@
// default method, including bridge flag.
DexMethod newMethod = rewriter.factory.createMethod(clazz.type, method.proto, method.name);
MethodAccessFlags newFlags = defaultMethod.accessFlags.copy();
+ // Some debuggers (like IntelliJ) automatically skip synthetic methods on single step.
+ newFlags.setSynthetic();
return new DexEncodedMethod(newMethod, newFlags,
defaultMethod.annotations, defaultMethod.parameterAnnotations,
new SynthesizedCode(new ForwardMethodSourceCode(
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
index 9b8b8e2..8add960 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/InterfaceMethodRewriter.java
@@ -65,8 +65,8 @@
public final class InterfaceMethodRewriter {
// Public for testing.
- public static final String COMPANION_CLASS_NAME_SUFFIX = "-CC";
- private static final String DEFAULT_METHOD_PREFIX = "$default$";
+ public static final String COMPANION_CLASS_NAME_SUFFIX = "$-CC";
+ public static final String DEFAULT_METHOD_PREFIX = "$default$";
private static final String PRIVATE_METHOD_PREFIX = "$private$";
private final IRConverter converter;
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 bed4df3..a54208d 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
@@ -1932,43 +1932,65 @@
return ch0.equals(ch1);
}
- public void commonSubexpressionElimination(IRCode code) {
- final ListMultimap<Wrapper<Instruction>, Value> instructionToValue = ArrayListMultimap.create();
- final DominatorTree dominatorTree = new DominatorTree(code);
- final CSEExpressionEquivalence equivalence = new CSEExpressionEquivalence(code);
+ private boolean isCSEInstructionCandidate(Instruction instruction) {
+ return (instruction.isBinop()
+ || instruction.isUnop()
+ || instruction.isInstanceOf()
+ || instruction.isCheckCast())
+ && instruction.getLocalInfo() == null
+ && !instruction.hasInValueWithLocalInfo();
+ }
- for (int i = 0; i < dominatorTree.getSortedBlocks().length; i++) {
- BasicBlock block = dominatorTree.getSortedBlocks()[i];
+ private boolean hasCSECandidate(IRCode code, int noCandidate) {
+ for (BasicBlock block : code.blocks) {
InstructionListIterator iterator = block.listIterator();
while (iterator.hasNext()) {
- Instruction instruction = iterator.next();
- if (instruction.isBinop()
- || instruction.isUnop()
- || instruction.isInstanceOf()
- || instruction.isCheckCast()) {
- if (instruction.getLocalInfo() != null || instruction.hasInValueWithLocalInfo()) {
- // If the instruction has input or output values then it is not safe to share it.
- continue;
- }
- List<Value> candidates = instructionToValue.get(equivalence.wrap(instruction));
- boolean eliminated = false;
- if (candidates.size() > 0) {
- for (Value candidate : candidates) {
- if (dominatorTree.dominatedBy(block, candidate.definition.getBlock()) &&
- shareCatchHandlers(instruction, candidate.definition)) {
- instruction.outValue().replaceUsers(candidate);
- eliminated = true;
- iterator.removeOrReplaceByDebugLocalRead();
- break; // Don't try any more candidates.
+ if (isCSEInstructionCandidate(iterator.next())) {
+ return true;
+ }
+ }
+ block.mark(noCandidate);
+ }
+ return false;
+ }
+
+ public void commonSubexpressionElimination(IRCode code) {
+ int noCandidate = code.reserveMarkingColor();
+ if (hasCSECandidate(code, noCandidate)) {
+ final ListMultimap<Wrapper<Instruction>, Value> instructionToValue =
+ ArrayListMultimap.create();
+ final CSEExpressionEquivalence equivalence = new CSEExpressionEquivalence(code);
+ final DominatorTree dominatorTree = new DominatorTree(code);
+ for (int i = 0; i < dominatorTree.getSortedBlocks().length; i++) {
+ BasicBlock block = dominatorTree.getSortedBlocks()[i];
+ if (block.isMarked(noCandidate)) {
+ continue;
+ }
+ InstructionListIterator iterator = block.listIterator();
+ while (iterator.hasNext()) {
+ Instruction instruction = iterator.next();
+ if (isCSEInstructionCandidate(instruction)) {
+ List<Value> candidates = instructionToValue.get(equivalence.wrap(instruction));
+ boolean eliminated = false;
+ if (candidates.size() > 0) {
+ for (Value candidate : candidates) {
+ if (dominatorTree.dominatedBy(block, candidate.definition.getBlock())
+ && shareCatchHandlers(instruction, candidate.definition)) {
+ instruction.outValue().replaceUsers(candidate);
+ eliminated = true;
+ iterator.removeOrReplaceByDebugLocalRead();
+ break; // Don't try any more candidates.
+ }
}
}
- }
- if (!eliminated) {
- instructionToValue.put(equivalence.wrap(instruction), instruction.outValue());
+ if (!eliminated) {
+ instructionToValue.put(equivalence.wrap(instruction), instruction.outValue());
+ }
}
}
}
}
+ code.returnMarkingColor(noCandidate);
assert code.isConsistentSSA();
}
diff --git a/src/test/debugTestResourcesJava8/DebugInterfaceMethod.java b/src/test/debugTestResourcesJava8/DebugInterfaceMethod.java
index b407698..f951357 100644
--- a/src/test/debugTestResourcesJava8/DebugInterfaceMethod.java
+++ b/src/test/debugTestResourcesJava8/DebugInterfaceMethod.java
@@ -4,21 +4,10 @@
public class DebugInterfaceMethod {
- interface I {
- default void doSomething(String msg) {
- String name = getClass().getName();
- System.out.println(name + ": " + msg);
- }
-
- static void printString(String msg) {
- System.out.println(msg);
- }
+ static class DefaultImpl implements InterfaceWithDefaultAndStaticMethods {
}
- static class DefaultImpl implements I {
- }
-
- static class OverrideImpl implements I {
+ static class OverrideImpl implements InterfaceWithDefaultAndStaticMethods {
@Override
public void doSomething(String msg) {
@@ -27,12 +16,12 @@
}
}
- private static void testDefaultMethod(I i) {
+ private static void testDefaultMethod(InterfaceWithDefaultAndStaticMethods i) {
i.doSomething("Test");
}
private static void testStaticMethod() {
- I.printString("I'm a static method in interface");
+ InterfaceWithDefaultAndStaticMethods.printString("I'm a static method in interface");
}
public static void main(String[] args) {
diff --git a/src/test/debugTestResourcesJava8/InterfaceWithDefaultAndStaticMethods.java b/src/test/debugTestResourcesJava8/InterfaceWithDefaultAndStaticMethods.java
new file mode 100644
index 0000000..05f48ab
--- /dev/null
+++ b/src/test/debugTestResourcesJava8/InterfaceWithDefaultAndStaticMethods.java
@@ -0,0 +1,14 @@
+// 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.
+
+interface InterfaceWithDefaultAndStaticMethods {
+ default void doSomething(String msg) {
+ String name = getClass().getName();
+ System.out.println(name + ": " + msg);
+ }
+
+ static void printString(String msg) {
+ System.out.println(msg);
+ }
+}
diff --git a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
index 8a1119a..587d947 100644
--- a/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
+++ b/src/test/examplesAndroidO/lambdadesugaringnplus/LambdasWithStaticAndDefaultMethods.java
@@ -290,7 +290,7 @@
private static Class getCompanionClassOrInterface() {
try {
return Class.forName("lambdadesugaringnplus."
- + "LambdasWithStaticAndDefaultMethods$B38302860$AnnotatedInterface-CC");
+ + "LambdasWithStaticAndDefaultMethods$B38302860$AnnotatedInterface$-CC");
} catch (Exception e) {
return AnnotatedInterface.class;
}
diff --git a/src/test/java/com/android/tools/r8/cf/UnneededLoadStoreDebugInfoTest.java b/src/test/java/com/android/tools/r8/cf/UnneededLoadStoreDebugInfoTest.java
new file mode 100644
index 0000000..63f131e
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/UnneededLoadStoreDebugInfoTest.java
@@ -0,0 +1,75 @@
+// 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.cf;
+
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.ClassFileConsumer.ArchiveConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.ToolHelper.ProcessResult;
+import java.nio.file.Path;
+import org.junit.Test;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class UnneededLoadStoreDebugInfoTest extends TestBase {
+
+ private static final String CLASS_NAME = "UnneededLoadStoreDebugInfoTest";
+ private static final String DESCRIPTOR = "L" + CLASS_NAME + ";";
+
+ public static class Dump implements Opcodes {
+
+ public static byte[] dump() {
+ ClassWriter cw = new ClassWriter(0);
+ MethodVisitor mv;
+
+ cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, CLASS_NAME, null, "java/lang/Object", null);
+
+ {
+ mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+ mv.visitCode();
+ Label argsStart = new Label();
+ mv.visitLabel(argsStart);
+ mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+ Label argsEnd = new Label();
+ mv.visitLabel(argsEnd);
+ mv.visitTypeInsn(CHECKCAST, "java/io/PrintStream");
+ mv.visitInsn(POP);
+ mv.visitInsn(RETURN);
+ mv.visitLocalVariable("args", "[Ljava/lang/String;", null, argsStart, argsEnd, 0);
+ mv.visitMaxs(1, 1);
+ mv.visitEnd();
+ }
+ cw.visitEnd();
+
+ return cw.toByteArray();
+ }
+ }
+
+ @Test
+ public void test() throws Exception {
+ Path inputJar = temp.getRoot().toPath().resolve("input.jar");
+ ArchiveConsumer consumer = new ArchiveConsumer(inputJar);
+ consumer.accept(Dump.dump(), DESCRIPTOR, null);
+ consumer.finished(null);
+ ProcessResult runInput = ToolHelper.runJava(inputJar, CLASS_NAME);
+ assertEquals(0, runInput.exitCode);
+ Path outputJar = temp.getRoot().toPath().resolve("output.jar");
+ R8.run(
+ R8Command.builder()
+ .setMode(CompilationMode.DEBUG)
+ .addProgramFiles(inputJar)
+ .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+ .setProgramConsumer(new ArchiveConsumer(outputJar))
+ .build());
+ ProcessResult runOutput = ToolHelper.runJava(outputJar, CLASS_NAME);
+ assertEquals(runInput.toString(), runOutput.toString());
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
index 26d3d5c..9bd3c18 100644
--- a/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/debug/InterfaceMethodTest.java
@@ -4,7 +4,10 @@
package com.android.tools.r8.debug;
+import static org.junit.Assert.assertEquals;
+
import com.android.tools.r8.debug.DebugTestBase.JUnit3Wrapper.Command;
+import com.android.tools.r8.ir.desugar.InterfaceMethodRewriter;
import com.google.common.collect.ImmutableList;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -19,7 +22,8 @@
public class InterfaceMethodTest extends DebugTestBase {
private static final Path JAR = DebugTestBase.DEBUGGEE_JAVA8_JAR;
- private static final String SOURCE_FILE = "DebugInterfaceMethod.java";
+ private static final String TEST_SOURCE_FILE = "DebugInterfaceMethod.java";
+ private static final String INTERFACE_SOURCE_FILE = "InterfaceWithDefaultAndStaticMethods.java";
@Parameters(name = "{0}")
public static Collection configs() {
@@ -43,29 +47,42 @@
String parameterName = "msg";
String localVariableName = "name";
+ final String defaultMethodContainerClass;
+ final String defaultMethodName;
+ final String defaultMethodThisName;
+ if (supportsDefaultMethod(config)) {
+ defaultMethodContainerClass = "InterfaceWithDefaultAndStaticMethods";
+ defaultMethodName = "doSomething";
+ defaultMethodThisName = "this";
+ } else {
+ defaultMethodContainerClass = "InterfaceWithDefaultAndStaticMethods"
+ + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+ // IntelliJ's debugger does not know about the companion class. The only way to match it with
+ // the source file or the desguared interface is to make it an inner class.
+ assertEquals('$', InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX.charAt(0));
+ defaultMethodName = InterfaceMethodRewriter.DEFAULT_METHOD_PREFIX + "doSomething";
+ defaultMethodThisName = "-this";
+ }
+
+
List<Command> commands = new ArrayList<>();
commands.add(breakpoint(debuggeeClass, "testDefaultMethod"));
commands.add(run());
commands.add(checkMethod(debuggeeClass, "testDefaultMethod"));
- commands.add(checkLine(SOURCE_FILE, 31));
- if (!supportsDefaultMethod(config)) {
- // We desugared default method. This means we're going to step through an extra (forward)
- // method first.
- commands.add(stepInto(INTELLIJ_FILTER));
- }
+ commands.add(checkLine(TEST_SOURCE_FILE, 20));
+
+ // Step into the default method.
commands.add(stepInto(INTELLIJ_FILTER));
- commands.add(checkLine(SOURCE_FILE, 9));
- // TODO(shertz) we should see the local variable this even when desugaring.
- if (supportsDefaultMethod(config)) {
- commands.add(checkLocal("this"));
- } else {
- commands.add(checkLocal("-this"));
- }
+ commands.add(checkLine(INTERFACE_SOURCE_FILE, 7));
+ commands.add(checkMethod(defaultMethodContainerClass, defaultMethodName));
+ commands.add(checkLocal(defaultMethodThisName));
commands.add(checkLocal(parameterName));
commands.add(stepOver(INTELLIJ_FILTER));
+ commands.add(checkLine(INTERFACE_SOURCE_FILE, 8));
+ commands.add(checkMethod(defaultMethodContainerClass, defaultMethodName));
+ commands.add(checkLocal(defaultMethodThisName));
commands.add(checkLocal(parameterName));
commands.add(checkLocal(localVariableName));
- // TODO(shertz) check current method name ?
commands.add(run());
commands.add(run() /* resume after 2nd breakpoint */);
@@ -83,7 +100,7 @@
commands.add(run());
commands.add(run() /* resume after 1st breakpoint */);
commands.add(checkMethod(debuggeeClass, "testDefaultMethod"));
- commands.add(checkLine(SOURCE_FILE, 31));
+ commands.add(checkLine(TEST_SOURCE_FILE, 20));
commands.add(stepInto());
commands.add(checkMethod("DebugInterfaceMethod$OverrideImpl", "doSomething"));
commands.add(checkLocal("this"));
@@ -102,14 +119,30 @@
String debuggeeClass = "DebugInterfaceMethod";
String parameterName = "msg";
+ final String staticMethodContainerClass;
+ final String staticMethodName = "printString";
+ if (supportsDefaultMethod(config)) {
+ staticMethodContainerClass = "InterfaceWithDefaultAndStaticMethods";
+ } else {
+ staticMethodContainerClass = "InterfaceWithDefaultAndStaticMethods"
+ + InterfaceMethodRewriter.COMPANION_CLASS_NAME_SUFFIX;
+ }
+
List<Command> commands = new ArrayList<>();
commands.add(breakpoint(debuggeeClass, "testStaticMethod"));
commands.add(run());
commands.add(checkMethod(debuggeeClass, "testStaticMethod"));
- commands.add(checkLine(SOURCE_FILE, 35));
+ commands.add(checkLine(TEST_SOURCE_FILE, 24));
+
+ // Step into static method.
commands.add(stepInto());
+ commands.add(checkLine(INTERFACE_SOURCE_FILE, 12));
+ commands.add(checkMethod(staticMethodContainerClass, staticMethodName));
+ commands.add(checkNoLocal("this"));
+ commands.add(checkNoLocal("-this"));
commands.add(checkLocal(parameterName));
commands.add(stepOver());
+ commands.add(checkLine(INTERFACE_SOURCE_FILE, 13));
commands.add(checkLocal(parameterName));
commands.add(run());