Fix wrong assumptions about ParallelMove.

Registers involved in single and double operations can
drag stack locations as well, so it is possible to update
a single stack location with a slot from a double stack location.

bug:19999189

Change-Id: Ibeec7d6f1b3126c4ae226fca56e84dccf798d367
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index 7d0641e..9df8f56 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+#include <iostream>
 
 #include "parallel_move_resolver.h"
 #include "nodes.h"
@@ -63,39 +64,42 @@
   }
 }
 
+Location LowOf(Location location) {
+  if (location.IsRegisterPair()) {
+    return Location::RegisterLocation(location.low());
+  } else if (location.IsFpuRegisterPair()) {
+    return Location::FpuRegisterLocation(location.low());
+  } else if (location.IsDoubleStackSlot()) {
+    return Location::StackSlot(location.GetStackIndex());
+  } else {
+    return Location::NoLocation();
+  }
+}
+
+Location HighOf(Location location) {
+  if (location.IsRegisterPair()) {
+    return Location::RegisterLocation(location.high());
+  } else if (location.IsFpuRegisterPair()) {
+    return Location::FpuRegisterLocation(location.high());
+  } else if (location.IsDoubleStackSlot()) {
+    return Location::StackSlot(location.GetHighStackIndex(4));
+  } else {
+    return Location::NoLocation();
+  }
+}
+
 // Update the source of `move`, knowing that `updated_location` has been swapped
 // with `new_source`. Note that `updated_location` can be a pair, therefore if
 // `move` is non-pair, we need to extract which register to use.
 static void UpdateSourceOf(MoveOperands* move, Location updated_location, Location new_source) {
   Location source = move->GetSource();
-  if (new_source.GetKind() == source.GetKind()) {
-    DCHECK(updated_location.Equals(source));
+  if (LowOf(updated_location).Equals(source)) {
+    move->SetSource(LowOf(new_source));
+  } else if (HighOf(updated_location).Equals(source)) {
+    move->SetSource(HighOf(new_source));
+  } else {
+    DCHECK(updated_location.Equals(source)) << updated_location << " " << source;
     move->SetSource(new_source);
-  } else if (new_source.IsStackSlot()
-             || new_source.IsDoubleStackSlot()
-             || source.IsStackSlot()
-             || source.IsDoubleStackSlot()) {
-    // Stack slots never take part of a pair/non-pair swap.
-    DCHECK(updated_location.Equals(source));
-    move->SetSource(new_source);
-  } else if (source.IsRegister()) {
-    DCHECK(new_source.IsRegisterPair()) << new_source;
-    DCHECK(updated_location.IsRegisterPair()) << updated_location;
-    if (updated_location.low() == source.reg()) {
-      move->SetSource(Location::RegisterLocation(new_source.low()));
-    } else {
-      DCHECK_EQ(updated_location.high(), source.reg());
-      move->SetSource(Location::RegisterLocation(new_source.high()));
-    }
-  } else if (source.IsFpuRegister()) {
-    DCHECK(new_source.IsFpuRegisterPair()) << new_source;
-    DCHECK(updated_location.IsFpuRegisterPair()) << updated_location;
-    if (updated_location.low() == source.reg()) {
-      move->SetSource(Location::FpuRegisterLocation(new_source.low()));
-    } else {
-      DCHECK_EQ(updated_location.high(), source.reg());
-      move->SetSource(Location::FpuRegisterLocation(new_source.high()));
-    }
   }
 }
 
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 817a44b..5c502f7 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -31,8 +31,13 @@
       message_ << "C";
     } else if (location.IsPair()) {
       message_ << location.low() << "," << location.high();
-    } else {
+    } else if (location.IsRegister()) {
       message_ << location.reg();
+    } else if (location.IsStackSlot()) {
+      message_ << location.GetStackIndex() << "(sp)";
+    } else {
+      message_ << "2x" << location.GetStackIndex() << "(sp)";
+      DCHECK(location.IsDoubleStackSlot()) << location;
     }
   }
 
@@ -279,6 +284,26 @@
     resolver.EmitNativeCode(moves);
     ASSERT_STREQ("(0,1 <-> 2,3)", resolver.GetMessage().c_str());
   }
+
+  {
+    // Test involving registers used in single context and pair context.
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterLocation(10),
+        Location::RegisterLocation(5),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterPairLocation(4, 5),
+        Location::DoubleStackSlot(32),
+        nullptr);
+    moves->AddMove(
+        Location::DoubleStackSlot(32),
+        Location::RegisterPairLocation(10, 11),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(2x32(sp) <-> 10,11) (4,5 <-> 2x32(sp)) (4 -> 5)", resolver.GetMessage().c_str());
+  }
 }
 
 }  // namespace art