Merge "Add a class merging test for use of -keepparameternames"
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);
+      }
     }
   }
 
diff --git a/tools/build_sample_apk.py b/tools/build_sample_apk.py
index fca92ca..b31ade4 100755
--- a/tools/build_sample_apk.py
+++ b/tools/build_sample_apk.py
@@ -43,6 +43,9 @@
   result.add_option('--split',
                     help='Split the app using the split.spec file',
                     default=False, action='store_true')
+  result.add_option('--install',
+                    help='Install the app (including featuresplit)',
+                    default=False, action='store_true')
   result.add_option('--app',
                     help='Which app to build',
                     default='simple',
@@ -140,6 +143,20 @@
   utils.PrintCmd(command)
   subprocess.check_call(command)
 
+def run_adb(args):
+  command = ['adb']
+  command.extend(args)
+  utils.PrintCmd(command)
+  subprocess.check_call(command)
+
+def adb_install(apks):
+  args = [
+      'install-multiple' if len(apks) > 1 else 'install',
+      '-r',
+      '-d']
+  args.extend(apks)
+  run_adb(args)
+
 def create_temp_apk(app, prefix):
   temp_apk_path = os.path.join(get_bin_path(app), '%s.ap_' % app)
   shutil.copyfile(os.path.join(get_bin_path(app), '%sresources.ap_' % prefix),
@@ -154,6 +171,7 @@
 
 def Main():
   (options, args) = parse_options()
+  apks = []
   is_split = options.split
   run_aapt_pack(options.aapt, options.api, options.app)
   if is_split:
@@ -170,17 +188,20 @@
   aapt_add_dex(options.aapt, dex_path, temp_apk_path)
   apk_path = os.path.join(get_bin_path(options.app), '%s.apk' % options.app)
   apk_utils.sign(temp_apk_path, apk_path,  options.keystore)
-  print('Apk available at: %s' % apk_path)
+  apks.append(apk_path)
 
-  if split:
+  if is_split:
     split_temp_apk_path = create_temp_apk(options.app, 'split_')
     aapt_add_dex(options.aapt,
                  get_split_path(options.app, 'split'),
                  temp_apk_path)
     split_apk_path = os.path.join(get_bin_path(options.app), 'featuresplit.apk')
     apk_utils.sign(temp_apk_path, split_apk_path,  options.keystore)
-    print('Feature split available at: %s' % split_apk_path)
+    apks.append(split_apk_path)
 
+  print('Generated apks available at: %s' % ' '.join(apks))
+  if options.install:
+    adb_install(apks)
 
 
 if __name__ == '__main__':