Merge "Add convinience argument for installing sample app"
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
index d1fe04c..031d161 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/RegisterMoveScheduler.java
@@ -14,6 +14,8 @@
 import com.android.tools.r8.ir.code.Move;
 import com.android.tools.r8.ir.code.Position;
 import com.android.tools.r8.ir.code.Value;
+import it.unimi.dsi.fastutil.ints.IntArraySet;
+import it.unimi.dsi.fastutil.ints.IntSet;
 import java.util.ArrayList;
 import java.util.Deque;
 import java.util.HashMap;
@@ -59,6 +61,8 @@
   }
 
   public void schedule() {
+    assert everyDestinationOnlyWrittenOnce();
+
     // Worklist of moves that are ready to be inserted.
     Deque<RegisterMove> worklist = new LinkedList<>();
 
@@ -196,4 +200,13 @@
     iterator.remove();
     return move;
   }
+
+  private boolean everyDestinationOnlyWrittenOnce() {
+    IntSet destinations = new IntArraySet(moveSet.size());
+    for (RegisterMove move : moveSet) {
+      boolean changed = destinations.add(move.dst);
+      assert changed;
+    }
+    return true;
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
index 1ea788d..cebcc8c 100644
--- a/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
+++ b/src/main/java/com/android/tools/r8/ir/regalloc/SpillMoveSet.java
@@ -61,7 +61,46 @@
     assert to.getSplitParent() == from.getSplitParent();
     BasicBlock atEntryToBlock = blockStartMap.get(i + 1);
     if (atEntryToBlock == null) {
-      addInMove(i, to, from);
+      Value value = from.getValue();
+      if (value.definition != null
+          && value.definition.isMoveException()
+          && to.getStart() == value.definition.asMoveException().getNumber() + 1) {
+        // Consider the following IR code.
+        //   40: ...
+        //   42: v0 <- move-exception
+        //   44: ...
+        //   ...
+        //   50: ... v0 ... // 4-bit constrained use of v0
+        //
+        // Initially, the liveness interval of v0 is [42; 50[. If the method has overlapping move-
+        // exception intervals (and the register allocator needs more than 16 registers), then the
+        // intervals of v0 will be split immediately after its definition. Therefore, v0 will have
+        // two liveness intervals: I1=[42; 43[ and I2=[43;50[.
+        //
+        // When allocating a register for the interval I2, we may need to spill an existing value v1
+        // that is currently active. As a result, we will split the liveness interval of v1 before
+        // the start of I2 (i.e., at position 43). The live intervals of v1 after the split
+        // therefore becomes J1=[x, y[, J2=[41, 43[, J3=[43, z[.
+        //
+        // If the registers assigned to J1 and J2 are different, we will create an in-move at
+        // position 43 in resolveControlFlow() (not position 41, since the first instruction of the
+        // target block is a move-exception instruction). If the the registers assigned to I1 and I2
+        // are also different, then an in-move will also be created at position 43 by the call to
+        // addSpillOrRestoreMove() in insertMoves().
+        //
+        // If the registers of I2 and J2 are the same (which is a valid assignment), then we will
+        // do parallel move scheduling for the following two in-moves:
+        //   move X, reg(I2)
+        //   move X, reg(J2)
+        //
+        // Therefore, there is a risk that we end up with the value that has been spilled instead of
+        // the exception object in register X at position 44. To avoid this situation, we schedule
+        // the move of the exception object (in this case, "move X, reg(I2)") as an out-move, such
+        // that it always gets inserted *after* the resolution moves of the current block.
+        addOutMove(i, to, from);
+      } else {
+        addInMove(i, to, from);
+      }
     }
   }