Rename IR Nop to DebugLocalReads and ensure it is never dead code.

R=ager

Bug:
Change-Id: Id9da9417dccf1220843e76f2e82620b6be791f11
diff --git a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
index 14e5288..a5a63e3 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDebugEventBuilder.java
@@ -86,7 +86,7 @@
       // If this is the end of the block clear out the pending state.
       pendingLocals = null;
       pendingLocalChanges = false;
-    } else if (pc != emittedPc && !instruction.isNop()) {
+    } else if (pc != emittedPc && !instruction.isDebugLocalRead()) {
       // For non-exit / pc-advancing instructions emit any pending changes once possible.
       emitLocalChanges(pc);
     }
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
index 5089ca5..aebb9ed 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockInstructionIterator.java
@@ -126,14 +126,14 @@
   }
 
   @Override
-  public void removeOrReplaceByNop() {
+  public void removeOrReplaceByDebugLocalRead() {
     if (current == null) {
       throw new IllegalStateException();
     }
     if (current.getDebugValues().isEmpty()) {
       remove();
     } else {
-      replaceCurrentInstruction(new Nop());
+      replaceCurrentInstruction(new DebugLocalRead());
     }
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Nop.java b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
similarity index 76%
rename from src/main/java/com/android/tools/r8/ir/code/Nop.java
rename to src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
index dc378de..f0eb4a5 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Nop.java
+++ b/src/main/java/com/android/tools/r8/ir/code/DebugLocalRead.java
@@ -10,25 +10,25 @@
 import com.android.tools.r8.ir.optimize.Inliner.Constraint;
 import com.android.tools.r8.utils.InternalOptions;
 
-public class Nop extends Instruction {
+public class DebugLocalRead extends Instruction {
 
-  public Nop() {
+  public DebugLocalRead() {
     super(null);
   }
 
   @Override
-  public boolean isNop() {
+  public boolean isDebugLocalRead() {
     return true;
   }
 
   @Override
-  public Nop asNop() {
+  public DebugLocalRead asDebugLocalRead() {
     return this;
   }
 
   @Override
   public void buildDex(DexBuilder builder) {
-    builder.addNop(this);
+    throw new Unreachable("Unexpected attempt to emit debug-local read.");
   }
 
   @Override
@@ -58,6 +58,8 @@
 
   @Override
   public boolean canBeDeadCode(IRCode code, InternalOptions options) {
-    return getDebugValues().isEmpty();
+    // Reads are never dead code.
+    // They should also have a non-empty set of debug values (see RegAlloc::computeDebugInfo)
+    return false;
   }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
index 1390620..1bedf26 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCodeInstructionsIterator.java
@@ -53,7 +53,7 @@
   }
 
   @Override
-  public void removeOrReplaceByNop() {
-    instructionIterator.removeOrReplaceByNop();
+  public void removeOrReplaceByDebugLocalRead() {
+    instructionIterator.removeOrReplaceByDebugLocalRead();
   }
 }
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 801c684..15291a5 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
@@ -849,11 +849,11 @@
     return null;
   }
 
-  public boolean isNop() {
+  public boolean isDebugLocalRead() {
     return false;
   }
 
-  public Nop asNop() {
+  public DebugLocalRead asDebugLocalRead() {
     return null;
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
index 56d81a2..2f2b3f1 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionIterator.java
@@ -33,8 +33,8 @@
   void add(Instruction instruction);
 
   /**
-   * Safe removal function that will insert a Nop to take over the debug values if any are
-   * associated with the current instruction.
+   * Safe removal function that will insert a DebugLocalRead to take over the debug values if any
+   * are associated with the current instruction.
    */
-  void removeOrReplaceByNop();
+  void removeOrReplaceByDebugLocalRead();
 }
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
index d8ea70b..92491c3 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstructionListIterator.java
@@ -60,10 +60,10 @@
   }
 
   /**
-   * Safe removal function that will insert a Nop to take over the debug values if any are
-   * associated with the current instruction.
+   * Safe removal function that will insert a DebugLocalRead to take over the debug values if any
+   * are associated with the current instruction.
    */
-  void removeOrReplaceByNop();
+  void removeOrReplaceByDebugLocalRead();
 
   /**
    * Remove the current instruction (aka the {@link Instruction} returned by the previous call to
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
index 3bdeefa..0d96df7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/DexBuilder.java
@@ -353,8 +353,7 @@
   }
 
   private static boolean isNopInstruction(com.android.tools.r8.ir.code.Instruction instruction) {
-    return instruction.isNop()
-        || instruction.isDebugLocalsChange()
+    return instruction.isDebugLocalsChange()
         || (instruction.isConstNumber() && !instruction.outValue().needsRegister());
   }
 
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index f2dfce4..fc516e4 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -35,6 +35,7 @@
 import com.android.tools.r8.ir.code.ConstNumber;
 import com.android.tools.r8.ir.code.ConstString;
 import com.android.tools.r8.ir.code.ConstType;
+import com.android.tools.r8.ir.code.DebugLocalRead;
 import com.android.tools.r8.ir.code.DebugLocalUninitialized;
 import com.android.tools.r8.ir.code.DebugLocalWrite;
 import com.android.tools.r8.ir.code.DebugPosition;
@@ -59,7 +60,6 @@
 import com.android.tools.r8.ir.code.NewArrayEmpty;
 import com.android.tools.r8.ir.code.NewArrayFilledData;
 import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Nop;
 import com.android.tools.r8.ir.code.Not;
 import com.android.tools.r8.ir.code.NumberConversion;
 import com.android.tools.r8.ir.code.NumericType;
@@ -608,7 +608,7 @@
     // 1. The block is empty (eg, instructions from block entry until now materialized to nothing).
     // 2. The block is non-empty (and the last instruction does not define the local to start).
     if (currentBlock.getInstructions().isEmpty()) {
-      addInstruction(new Nop());
+      addInstruction(new DebugLocalRead());
     }
     Instruction instruction = currentBlock.getInstructions().getLast();
     assert instruction.outValue() != value;
@@ -630,7 +630,7 @@
     // 2. The block has an instruction not defining the local being ended.
     // 3. The block has an instruction defining the local being ended.
     if (currentBlock.getInstructions().isEmpty()) {
-      addInstruction(new Nop());
+      addInstruction(new DebugLocalRead());
     }
     Instruction instruction = currentBlock.getInstructions().getLast();
     if (instruction.outValue() != value) {
@@ -645,7 +645,7 @@
     if (instruction.isDebugLocalWrite()) {
       DebugLocalWrite write = instruction.asDebugLocalWrite();
       currentBlock.replaceCurrentDefinitions(value, write.src());
-      currentBlock.listIterator(write).removeOrReplaceByNop();
+      currentBlock.listIterator(write).removeOrReplaceByDebugLocalRead();
     } else {
       instruction.outValue().clearLocalInfo();
     }
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
index 828ff10..f11af49 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/JarState.java
@@ -176,7 +176,7 @@
   // without conflating locals that are shared among different types. This issue arises because a
   // debugging range can be larger than the definite-assignment scope of a local (eg, a local
   // introduced in an unscoped switch case). To ensure that the SSA graph is valid we must introduce
-  // the local before inserting any DebugLocalReads (we do so in the method prelude, but that can
+  // the local before inserting any DebugLocalRead (we do so in the method prelude, but that can
   // potentially lead to phi functions merging locals of different move-types. Thus we allocate
   // registers from the three distinct spaces.
   private final int localsSize;
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 348fd02..fff55f0 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
@@ -1088,7 +1088,7 @@
             user -> user.isCheckCast()
                 && user.asCheckCast().getType().isSubtypeOf(checkCast.getType(), appInfo))) {
           checkCast.outValue().replaceUsers(checkCast.inValues().get(0));
-          it.removeOrReplaceByNop();
+          it.removeOrReplaceByDebugLocalRead();
         }
       }
     }
@@ -1506,7 +1506,7 @@
         write.outValue().replaceUsers(phi);
         // Safely remove the write.
         // TODO(zerny): Once phis become instructions, move debug values there instead of a nop.
-        iterator.removeOrReplaceByNop();
+        iterator.removeOrReplaceByDebugLocalRead();
         return;
       }
     }
@@ -1678,7 +1678,7 @@
                   shareCatchHandlers(instruction, candidate.definition)) {
                 instruction.outValue().replaceUsers(candidate);
                 eliminated = true;
-                iterator.removeOrReplaceByNop();
+                iterator.removeOrReplaceByDebugLocalRead();
                 break;  // Don't try any more candidates.
               }
             }
@@ -1927,13 +1927,13 @@
           DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
           if (matchesMethodOfThrowable(invokedMethod, throwableMethods.addSuppressed)) {
             // Remove Throwable::addSuppressed(Throwable) call.
-            iterator.removeOrReplaceByNop();
+            iterator.removeOrReplaceByDebugLocalRead();
           } else if (matchesMethodOfThrowable(invokedMethod, throwableMethods.getSuppressed)) {
             Value destValue = current.outValue();
             if (destValue == null) {
               // If the result of the call was not used we don't create
               // an empty array and just remove the call.
-              iterator.removeOrReplaceByNop();
+              iterator.removeOrReplaceByDebugLocalRead();
               continue;
             }
 
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
index ebf69b5..2790c03 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DeadCodeRemover.java
@@ -120,7 +120,7 @@
       // All users will be removed for this instruction. Eagerly clear them so further inspection
       // of this instruction during dead code elimination will terminate here.
       outValue.clearUsers();
-      iterator.removeOrReplaceByNop();
+      iterator.removeOrReplaceByDebugLocalRead();
     }
   }
 
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 a8b3972..7facea6 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
@@ -324,6 +324,12 @@
       int spillCount = 0;
       while (instructionIterator.hasNext()) {
         Instruction instruction = instructionIterator.next();
+        if (instruction.isDebugLocalRead()) {
+          // Remove debug local reads now that local liveness is computed.
+          assert !instruction.getDebugValues().isEmpty();
+          instruction.clearDebugValues();
+          instructionIterator.remove();
+        }
         int index = instruction.getNumber();
         if (index == -1) {
           spillCount++;
diff --git a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
index 8ab2de5..c8ea3bb 100644
--- a/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
+++ b/src/test/java/com/android/tools/r8/ir/regalloc/RegisterMoveSchedulerTest.java
@@ -74,7 +74,7 @@
     }
 
     @Override
-    public void removeOrReplaceByNop() {
+    public void removeOrReplaceByDebugLocalRead() {
       remove();
     }