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());