Register promotion support for 64-bit targets

Not sufficiently tested for 64-bit targets, but should be
fairly close.

A significant amount of refactoring could stil be done, (in
later CLs).

With this change we are not making any changes to the vmap
scheme.  As a result, it is a requirement that if a vreg
is promoted to both a 32-bit view and the low half of a
64-bit view it must share the same physical register.  We
may change this restriction later on to allow for more flexibility
for 32-bit Arm.

For example, if v4, v5, v4/v5 and v5/v6 are all hot enough to
promote, we'd end up with something like:

v4 (as an int)    -> r10
v4/v5 (as a long) -> r10
v5 (as an int)    -> r11
v5/v6 (as a long) -> r11

Fix a couple of ARM64 bugs on the way...

Change-Id: I6a152b9c164d9f1a053622266e165428045362f3
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 132ac3e..d5405fb 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -159,11 +159,22 @@
       uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
       uintptr_t ptr_val;
       bool success = false;
+      bool target64 = (kRuntimeISA == kArm64) || (kRuntimeISA == kX86_64);
       if (is_float) {
         success = GetFPR(reg, &ptr_val);
       } else {
         success = GetGPR(reg, &ptr_val);
       }
+      if (success && target64) {
+        bool wide_lo = (kind == kLongLoVReg) || (kind == kDoubleLoVReg);
+        bool wide_hi = (kind == kLongHiVReg) || (kind == kDoubleHiVReg);
+        int64_t value_long = static_cast<int64_t>(ptr_val);
+        if (wide_lo) {
+          ptr_val = static_cast<uintptr_t>(value_long & 0xFFFFFFFF);
+        } else if (wide_hi) {
+          ptr_val = static_cast<uintptr_t>(value_long >> 32);
+        }
+      }
       *val = ptr_val;
       return success;
     } else {
@@ -194,6 +205,28 @@
       bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
       uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
       const uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
+      bool target64 = (kRuntimeISA == kArm64) || (kRuntimeISA == kX86_64);
+      // Deal with 32 or 64-bit wide registers in a way that builds on all targets.
+      if (target64) {
+        bool wide_lo = (kind == kLongLoVReg) || (kind == kDoubleLoVReg);
+        bool wide_hi = (kind == kLongHiVReg) || (kind == kDoubleHiVReg);
+        if (wide_lo || wide_hi) {
+          uintptr_t old_reg_val;
+          bool success = is_float ? GetFPR(reg, &old_reg_val) : GetGPR(reg, &old_reg_val);
+          if (!success) {
+            return false;
+          }
+          uint64_t new_vreg_portion = static_cast<uint64_t>(new_value);
+          uint64_t old_reg_val_as_wide = static_cast<uint64_t>(old_reg_val);
+          uint64_t mask = 0xffffffff;
+          if (wide_lo) {
+            mask = mask << 32;
+          } else {
+            new_vreg_portion = new_vreg_portion << 32;
+          }
+          new_value = static_cast<uintptr_t>((old_reg_val_as_wide & mask) | new_vreg_portion);
+        }
+      }
       if (is_float) {
         return SetFPR(reg, new_value);
       } else {
diff --git a/runtime/vmap_table.h b/runtime/vmap_table.h
index 9821753..df5cd80 100644
--- a/runtime/vmap_table.h
+++ b/runtime/vmap_table.h
@@ -64,6 +64,12 @@
     const uint8_t* table = table_;
     uint16_t adjusted_vreg = vreg + kEntryAdjustment;
     size_t end = DecodeUnsignedLeb128(&table);
+    bool high_reg = (kind == kLongHiVReg) || (kind == kDoubleHiVReg);
+    bool target64 = (kRuntimeISA == kArm64) || (kRuntimeISA == kX86_64);
+    if (target64 && high_reg) {
+      // Wide promoted registers are associated with the sreg of the low portion.
+      adjusted_vreg--;
+    }
     for (size_t i = 0; i < end; ++i) {
       // Stop if we find what we are are looking for.
       uint16_t adjusted_entry = DecodeUnsignedLeb128(&table);