Generate better code for ranged invokes in debug mode.

The register allocator inserts moves for arguments to ranged invokes
in order to give the register allocator complete freedom in selecting
registers for those arguments.

However, debug instructions are also moves and therefore, we often
don't have to generate extra argument moves in order to have enough
freedom in register selection.

This change avoids generating argument moves when the input is
already the result of a move to an unconstrained value that can
be linked into the current argument chain.

This saves around 40kB on GMSCore v10 deploy jar dexing.

R=mikaelpeltier@google.com, zerny@google.com

Bug: 62992398
Change-Id: I60f89d06e25c4347c3a688ed65fd99b75e477986
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 4136c6a..27b1e18 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
@@ -3,6 +3,7 @@
 // BSD-style license that can be found in the LICENSE file.
 package com.android.tools.r8.ir.code;
 
+import com.android.tools.r8.dex.Constants;
 import com.android.tools.r8.graph.DebugLocalInfo;
 import com.android.tools.r8.ir.regalloc.LiveIntervals;
 import com.android.tools.r8.utils.InternalOptions;
@@ -355,6 +356,15 @@
     return false;
   }
 
+  public boolean hasRegisterConstraint() {
+    for (Instruction instruction : uniqueUsers()) {
+      if (instruction.maxInValueRegister() != Constants.U16BIT_MAX) {
+        return true;
+      }
+    }
+    return false;
+  }
+
   @Override
   public int hashCode() {
     return number;
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 30d833b..5c12d95 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
@@ -647,6 +647,10 @@
       // consecutive arguments now and add hints to the move sources. This looks forward
       // and propagate hints backwards to avoid many moves in connection with ranged invokes.
       allocateArgumentIntervalsWithSrc(unhandledInterval);
+      if (unhandledInterval.getRegister() != NO_REGISTER) {
+        // The value itself could be in the chain that has now gotten registers allocated.
+        continue;
+      }
 
       int start = unhandledInterval.getStart();
       // Check for active intervals that expired or became inactive.
@@ -1829,11 +1833,27 @@
       Value previous = null;
       for (int i = 0; i < arguments.size(); i++) {
         Value argument = arguments.get(i);
-        Value newArgument = createValue(argument.outType());
-        Move move = new Move(newArgument, argument);
-        move.setBlock(invoke.getBlock());
-        replaceArgument(invoke, i, newArgument);
-        insertAt.add(move);
+        Value newArgument = argument;
+        // In debug mode, we have debug instructions that are also moves. Do not generate another
+        // move if there already is a move instruction that we can use. We generate moves if:
+        //
+        // 1. the argument is not defined by a move,
+        //
+        // 2. the argument is already linked or would cause a cycle if linked, or
+        //
+        // 3. the argument has a register constraint (the argument moves are there to make the
+        //    input value to a ranged invoke unconstrained.)
+        if (argument.definition == null ||
+            !argument.definition.isMove() ||
+            argument.isLinked() ||
+            argument == previous ||
+            argument.hasRegisterConstraint()) {
+          newArgument = createValue(argument.outType());
+          Move move = new Move(newArgument, argument);
+          move.setBlock(invoke.getBlock());
+          replaceArgument(invoke, i, newArgument);
+          insertAt.add(move);
+        }
         if (previous != null) {
           previous.linkTo(newArgument);
         }