Eliminate debug-moves if their preceding phi-move is sufficient.

This reapplies 4c93257 after the recent debug def-use-chain changes.

Bug: 64831882

Change-Id: I4205d5738ea3b49bc311db6e428de3aed886a16d
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 8a2de9b..5089ca5 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
@@ -154,7 +154,7 @@
     for (Value value : current.inValues()) {
       value.removeUser(current);
     }
-    if (current.outValue() != null) {
+    if (current.outValue() != null && current.outValue().isUsed()) {
       assert newInstruction.outValue() != null;
       current.outValue().replaceUsers(newInstruction.outValue());
     }
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 ef49910..d8ea70b 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,8 +60,8 @@
   }
 
   /**
-   * Safe removal function that will insert a Nop to take over the debug values if any are assocated
-   * with the current instruction.
+   * Safe removal function that will insert a Nop to take over the debug values if any are
+   * associated with the current instruction.
    */
   void removeOrReplaceByNop();
 
diff --git a/src/main/java/com/android/tools/r8/ir/code/Value.java b/src/main/java/com/android/tools/r8/ir/code/Value.java
index 0af8f63..ffe3181 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Value.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Value.java
@@ -86,7 +86,7 @@
   private boolean isArgument = false;
   private boolean knownToBeBoolean = false;
   private LongInterval valueRange;
-  private final DebugData debugData;
+  private DebugData debugData;
 
   public Value(int number, MoveType type, DebugLocalInfo local) {
     this.number = number;
@@ -114,6 +114,11 @@
     return debugData == null ? null : debugData.local;
   }
 
+  public void setLocalInfo(DebugLocalInfo local) {
+    assert debugData == null;
+    debugData = new DebugData(local);
+  }
+
   public List<Instruction> getDebugLocalStarts() {
     if (debugData == null) {
       return Collections.emptyList();
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index 5b5d382..0ddd43a 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -442,6 +442,10 @@
     printC1VisualizerHeader(method);
     printMethod(code, "Initial IR (SSA)");
 
+    if (options.debug) {
+      codeRewriter.simplifyDebugLocals(code);
+    }
+
     if (!method.isProcessed()) {
       if (protoLiteRewriter != null && protoLiteRewriter.appliesTo(method)) {
         protoLiteRewriter.rewriteProtoLiteSpecialMethod(code, method);
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 52c9eb4..20f272a 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
@@ -38,6 +38,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.DebugLocalWrite;
 import com.android.tools.r8.ir.code.DominatorTree;
 import com.android.tools.r8.ir.code.Goto;
 import com.android.tools.r8.ir.code.IRCode;
@@ -1432,6 +1433,40 @@
     }
   }
 
+  public void simplifyDebugLocals(IRCode code) {
+    for (BasicBlock block : code.blocks) {
+      for (Phi phi : block.getPhis()) {
+        if (phi.getLocalInfo() == null && phi.numberOfUsers() == 1 && phi.numberOfAllUsers() == 1) {
+          Instruction instruction = phi.uniqueUsers().iterator().next();
+          if (instruction.isDebugLocalWrite()) {
+            removeDebugWriteOfPhi(phi, instruction.asDebugLocalWrite());
+          }
+        }
+      }
+    }
+  }
+
+  private void removeDebugWriteOfPhi(Phi phi, DebugLocalWrite write) {
+    assert write.src() == phi;
+    for (InstructionListIterator iterator = phi.getBlock().listIterator(); iterator.hasNext(); ) {
+      Instruction next = iterator.next();
+      if (!next.isDebugLocalWrite()) {
+        // If the debug write is not in the block header bail out.
+        return;
+      }
+      if (next == write) {
+        // Associate the phi with the local.
+        phi.setLocalInfo(write.getLocalInfo());
+        // Replace uses of the write with the phi.
+        write.outValue().replaceUsers(phi);
+        // Safely remove the write.
+        // TODO(zerny): Once phis become instructions, move debug values there instead of a nop.
+        iterator.removeOrReplaceByNop();
+        return;
+      }
+    }
+  }
+
   private static class ExpressionEquivalence extends Equivalence<Instruction> {
 
     @Override