Remove DebugLocalRead instructions in the presence of ReachabilitySensitive annotations.

Bug: 120255051
Change-Id: I17f37a58ab2b307da1fa78d3cb8e6cbab45981e6
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
index 40d31d2..be3c93b 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/LinearScanRegisterAllocator.java
@@ -21,6 +21,7 @@
 import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.IRCode.LiveAtEntrySets;
 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.Invoke;
 import com.android.tools.r8.ir.code.Move;
@@ -222,6 +223,15 @@
     // and we do not actually want locals information in the output.
     if (options.debug) {
       computeDebugInfo(blocks);
+    } else if (code.method.getOptimizationInfo().isReachabilitySensitive()) {
+      InstructionIterator it = code.instructionIterator();
+      while (it.hasNext()) {
+        Instruction instruction = it.next();
+        if (instruction.isDebugLocalRead()) {
+          instruction.clearDebugValues();
+          it.remove();
+        }
+      }
     }
     clearUserInfo();
     clearState();
@@ -372,11 +382,11 @@
           }
           // Remove the end markers now that local liveness is computed.
           instruction.clearDebugValues();
-          if (instruction.isDebugLocalRead()) {
-            Instruction prev = instructionIterator.previous();
-            assert prev == instruction;
-            instructionIterator.remove();
-          }
+        }
+        if (instruction.isDebugLocalRead()) {
+          Instruction prev = instructionIterator.previous();
+          assert prev == instruction;
+          instructionIterator.remove();
         }
 
         Instruction nextInstruction = instructionIterator.peekNext();
diff --git a/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveAndDebugLocalReads.java b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveAndDebugLocalReads.java
new file mode 100644
index 0000000..ff125be
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/reachabilitysensitive/ReachabilitySensitiveAndDebugLocalReads.java
@@ -0,0 +1,180 @@
+// 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.reachabilitysensitive;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.objectweb.asm.AnnotationVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+
+public class ReachabilitySensitiveAndDebugLocalReads extends TestBase {
+
+  @Test
+  public void test() throws Exception {
+    byte[] classdata = Dump.dump();
+    String mainClass = "test.Test";
+    String expected = StringUtils.lines("5");
+    testForJvm()
+        .addProgramClassFileData(classdata)
+        .run(mainClass)
+        .assertSuccessWithOutput(expected);
+
+    testForD8()
+        .release()
+        .addProgramClassFileData(classdata)
+        .run(mainClass)
+        .assertSuccessWithOutput(expected);
+  }
+}
+
+/* ASM Dump of ReachabilitySensitive/TestClassWithAnnotatedMethod + main method:
+<pre>
+ package test;
+ public class Test {
+
+  @ReachabilitySensitive
+  public void unrelatedAnnotatedMethod() {}
+
+  public void method() {
+    int i = 2;
+    int j = i + 1;
+    int k = j + 2;
+    System.out.println(k);
+  }
+
+  public static void main(String[] args) {
+    new Test().method();
+  }
+}
+</pre>
+ */
+class Dump implements Opcodes {
+
+  public static byte[] dump() {
+
+    ClassWriter classWriter = new ClassWriter(0);
+    MethodVisitor methodVisitor;
+    AnnotationVisitor annotationVisitor0;
+
+    classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "test/Test", null, "java/lang/Object", null);
+
+    classWriter.visitSource("Test.java", null);
+
+    {
+      methodVisitor = classWriter.visitMethod(0, "<init>", "()V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(53, label0);
+      methodVisitor.visitVarInsn(ALOAD, 0);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
+      methodVisitor.visitInsn(RETURN);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLocalVariable("this", "Ltest/Test;", null, label0, label1, 0);
+      methodVisitor.visitMaxs(1, 1);
+      methodVisitor.visitEnd();
+    }
+
+    {
+      methodVisitor =
+          classWriter.visitMethod(ACC_PUBLIC, "unrelatedAnnotatedMethod", "()V", null, null);
+      {
+        annotationVisitor0 =
+            methodVisitor.visitAnnotation(
+                "Ldalvik/annotation/optimization/ReachabilitySensitive;", true);
+        annotationVisitor0.visitEnd();
+      }
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(56, label0);
+      methodVisitor.visitInsn(RETURN);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLocalVariable("this", "Ltest/Test;", null, label0, label1, 0);
+      methodVisitor.visitMaxs(0, 1);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "method", "()V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(59, label0);
+      methodVisitor.visitInsn(ICONST_2);
+      methodVisitor.visitVarInsn(ISTORE, 1);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(60, label1);
+      methodVisitor.visitVarInsn(ILOAD, 1);
+      methodVisitor.visitInsn(ICONST_1);
+      methodVisitor.visitInsn(IADD);
+      methodVisitor.visitVarInsn(ISTORE, 2);
+      Label label2 = new Label();
+      methodVisitor.visitLabel(label2);
+      methodVisitor.visitLineNumber(61, label2);
+      methodVisitor.visitVarInsn(ILOAD, 2);
+      methodVisitor.visitInsn(ICONST_2);
+      methodVisitor.visitInsn(IADD);
+      methodVisitor.visitVarInsn(ISTORE, 3);
+      Label label3 = new Label();
+      methodVisitor.visitLabel(label3);
+      methodVisitor.visitLineNumber(62, label3);
+      methodVisitor.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
+      methodVisitor.visitVarInsn(ILOAD, 3);
+      methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(I)V", false);
+      Label label4 = new Label();
+      methodVisitor.visitLabel(label4);
+      methodVisitor.visitLineNumber(63, label4);
+      // Insert some unneeded code which we know will be removed.
+      methodVisitor.visitLdcInsn(1);
+      methodVisitor.visitLdcInsn(2);
+      methodVisitor.visitInsn(IADD);
+      // Insert an label that will end some local variables so removing will create local reads.
+      Label labelForEndingLocals = new Label();
+      methodVisitor.visitLabel(labelForEndingLocals);
+      methodVisitor.visitInsn(POP);
+      // Pop the unneeded value and continue as usual.
+      methodVisitor.visitInsn(RETURN);
+      Label label5 = new Label();
+      methodVisitor.visitLabel(label5);
+      methodVisitor.visitLocalVariable("this", "Ltest/Test;", null, label0, label5, 0);
+      methodVisitor.visitLocalVariable("i", "I", null, label1, labelForEndingLocals, 1);
+      methodVisitor.visitLocalVariable("j", "I", null, label2, labelForEndingLocals, 2);
+      methodVisitor.visitLocalVariable("k", "I", null, label3, labelForEndingLocals, 3);
+      methodVisitor.visitMaxs(2, 4);
+      methodVisitor.visitEnd();
+    }
+    {
+      methodVisitor =
+          classWriter.visitMethod(
+              ACC_PUBLIC | ACC_STATIC, "main", "([Ljava/lang/String;)V", null, null);
+      methodVisitor.visitCode();
+      Label label0 = new Label();
+      methodVisitor.visitLabel(label0);
+      methodVisitor.visitLineNumber(66, label0);
+      methodVisitor.visitTypeInsn(NEW, "test/Test");
+      methodVisitor.visitInsn(DUP);
+      methodVisitor.visitMethodInsn(INVOKESPECIAL, "test/Test", "<init>", "()V", false);
+      methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "test/Test", "method", "()V", false);
+      Label label1 = new Label();
+      methodVisitor.visitLabel(label1);
+      methodVisitor.visitLineNumber(67, label1);
+      methodVisitor.visitInsn(RETURN);
+      Label label2 = new Label();
+      methodVisitor.visitLabel(label2);
+      methodVisitor.visitLocalVariable("args", "[Ljava/lang/String;", null, label0, label2, 0);
+      methodVisitor.visitMaxs(2, 1);
+      methodVisitor.visitEnd();
+    }
+    classWriter.visitEnd();
+
+    return classWriter.toByteArray();
+  }
+}