Avoid using non-argument registers for arguments when we can.

This saves around 4kB when dexing gmscore.

Redoing the move insertion is a little heavy weight but with the current
parallel move scheduling I cannot reliably put a bound on the amount
of temporary registers it will need (even though I haven't seen more
than 4 ever). Therefore, I have to postpone the determination until the
very end and redo insertion.

R=sgjesse@google.com

Change-Id: I440ec35460033b5c6eb25f161ff1740c91eee2d9
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 f2d039d..f621c67 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
@@ -429,9 +429,48 @@
   private boolean performAllocation(ArgumentReuseMode mode) {
     boolean result = performAllocationWithoutMoveInsertion(mode);
     insertMoves();
+    if (mode == ArgumentReuseMode.DISALLOW_ARGUMENT_REUSE) {
+      // Now that we know the max register number we can compute whether it is safe to use
+      // argument registers in place. If it is, we redo move insertion to get rid of the moves
+      // caused by splitting of the argument registers.
+      if (unsplitArguments()) {
+        removeSpillAndPhiMoves();
+        insertMoves();
+      }
+    }
     return result;
   }
 
+  // When argument register reuse is disallowed, we split argument values to make sure that
+  // we can get the argument into low enough registers at uses that require low numbers. After
+  // register allocation we can check if it is safe to just use the argument register itself
+  // for all uses and thereby avoid moving argument values around.
+  private boolean unsplitArguments() {
+    boolean argumentRegisterUnsplit = false;
+    Value current = preArgumentSentinelValue;
+    while (current != null) {
+      LiveIntervals intervals = current.getLiveIntervals();
+      assert intervals.getRegisterLimit() == Constants.U16BIT_MAX;
+      boolean canUseArgumentRegister = true;
+      for (LiveIntervals child : intervals.getSplitChildren()) {
+        if (child.getRegisterLimit() < registersUsed()) {
+          canUseArgumentRegister = false;
+          break;
+        }
+      }
+      if (canUseArgumentRegister) {
+        argumentRegisterUnsplit = true;
+        for (LiveIntervals child : intervals.getSplitChildren()) {
+          child.clearRegisterAssignment();
+          child.setRegister(intervals.getRegister());
+          child.setSpilled(false);
+        }
+      }
+      current = current.getNextConsecutive();
+    }
+    return argumentRegisterUnsplit;
+  }
+
   private void removeSpillAndPhiMoves() {
     for (BasicBlock block : code.blocks) {
       InstructionListIterator it = block.listIterator();