Stack support for Optimizing compiler

Allows to read/write DEX registers from physical register or stack
location when the method is compiled with the Optimizing compiler.

Required fixing arm and arm64 JNI compiler by saving floating
point registers.

Bug: 18547544
Change-Id: I401579f251d1c0a130f6cf4a93a960cdcd7518f5
diff --git a/compiler/jni/quick/arm/calling_convention_arm.cc b/compiler/jni/quick/arm/calling_convention_arm.cc
index 669c3bb..d3690b2 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.cc
+++ b/compiler/jni/quick/arm/calling_convention_arm.cc
@@ -31,6 +31,10 @@
   S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15
 };
 
+static const SRegister kHFSCalleeSaveRegisters[] = {
+  S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31
+};
+
 static const DRegister kHFDArgumentRegisters[] = {
   D0, D1, D2, D3, D4, D5, D6, D7
 };
@@ -226,6 +230,10 @@
   callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R8));
   callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R10));
   callee_save_regs_.push_back(ArmManagedRegister::FromCoreRegister(R11));
+
+  for (size_t i = 0; i < arraysize(kHFSCalleeSaveRegisters); ++i) {
+    callee_save_regs_.push_back(ArmManagedRegister::FromSRegister(kHFSCalleeSaveRegisters[i]));
+  }
 }
 
 uint32_t ArmJniCallingConvention::CoreSpillMask() const {
@@ -235,6 +243,14 @@
   return result;
 }
 
+uint32_t ArmJniCallingConvention::FpSpillMask() const {
+  uint32_t result = 0;
+  for (size_t i = 0; i < arraysize(kHFSCalleeSaveRegisters); ++i) {
+    result |= (1 << kHFSCalleeSaveRegisters[i]);
+  }
+  return result;
+}
+
 ManagedRegister ArmJniCallingConvention::ReturnScratchRegister() const {
   return ArmManagedRegister::FromCoreRegister(R2);
 }
diff --git a/compiler/jni/quick/arm/calling_convention_arm.h b/compiler/jni/quick/arm/calling_convention_arm.h
index 604ce1c..dbecb8e 100644
--- a/compiler/jni/quick/arm/calling_convention_arm.h
+++ b/compiler/jni/quick/arm/calling_convention_arm.h
@@ -63,9 +63,7 @@
   }
   ManagedRegister ReturnScratchRegister() const OVERRIDE;
   uint32_t CoreSpillMask() const OVERRIDE;
-  uint32_t FpSpillMask() const OVERRIDE {
-    return 0;  // Floats aren't spilled in JNI down call
-  }
+  uint32_t FpSpillMask() const OVERRIDE;
   bool IsCurrentParamInRegister() OVERRIDE;
   bool IsCurrentParamOnStack() OVERRIDE;
   ManagedRegister CurrentParamRegister() OVERRIDE;
diff --git a/compiler/jni/quick/arm64/calling_convention_arm64.cc b/compiler/jni/quick/arm64/calling_convention_arm64.cc
index b9c8178..05eb80a 100644
--- a/compiler/jni/quick/arm64/calling_convention_arm64.cc
+++ b/compiler/jni/quick/arm64/calling_convention_arm64.cc
@@ -38,6 +38,10 @@
   S0, S1, S2, S3, S4, S5, S6, S7
 };
 
+static const DRegister kDCalleeSaveRegisters[] = {
+  D8, D9, D10, D11, D12, D13, D14, D15
+};
+
 // Calling convention
 ManagedRegister Arm64ManagedRuntimeCallingConvention::InterproceduralScratchRegister() {
   return Arm64ManagedRegister::FromXRegister(X20);  // saved on entry restored on exit
@@ -166,6 +170,10 @@
   callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X28));
   callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X29));
   callee_save_regs_.push_back(Arm64ManagedRegister::FromXRegister(X30));
+
+  for (size_t i = 0; i < arraysize(kDCalleeSaveRegisters); ++i) {
+    callee_save_regs_.push_back(Arm64ManagedRegister::FromDRegister(kDCalleeSaveRegisters[i]));
+  }
 }
 
 uint32_t Arm64JniCallingConvention::CoreSpillMask() const {
@@ -184,10 +192,11 @@
 }
 
 uint32_t Arm64JniCallingConvention::FpSpillMask() const {
-  // Compute spill mask to agree with callee saves initialized in the constructor
-  // Note: All callee-save fp registers will be preserved by aapcs64. And they are not used
-  // in the jni method.
-  return 0;
+  uint32_t result = 0;
+  for (size_t i = 0; i < arraysize(kDCalleeSaveRegisters); ++i) {
+    result |= (1 << kDCalleeSaveRegisters[i]);
+  }
+  return result;
 }
 
 ManagedRegister Arm64JniCallingConvention::ReturnScratchRegister() const {
diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc
index a52e6eb..a02191b 100644
--- a/compiler/utils/arm/assembler_arm.cc
+++ b/compiler/utils/arm/assembler_arm.cc
@@ -385,12 +385,24 @@
   // Push callee saves and link register.
   RegList push_list = 1 << LR;
   size_t pushed_values = 1;
+  int32_t min_s = kNumberOfSRegisters;
+  int32_t max_s = -1;
   for (size_t i = 0; i < callee_save_regs.size(); i++) {
-    Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister();
-    push_list |= 1 << reg;
-    pushed_values++;
+    if (callee_save_regs.at(i).AsArm().IsCoreRegister()) {
+      Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister();
+      push_list |= 1 << reg;
+      pushed_values++;
+    } else {
+      CHECK(callee_save_regs.at(i).AsArm().IsSRegister());
+      min_s = std::min(static_cast<int>(callee_save_regs.at(i).AsArm().AsSRegister()), min_s);
+      max_s = std::max(static_cast<int>(callee_save_regs.at(i).AsArm().AsSRegister()), max_s);
+    }
   }
   PushList(push_list);
+  if (max_s != -1) {
+    pushed_values += 1 + max_s - min_s;
+    vpushs(static_cast<SRegister>(min_s), 1 + max_s - min_s);
+  }
 
   // Increase frame to required size.
   CHECK_GT(frame_size, pushed_values * kFramePointerSize);  // Must at least have space for Method*.
@@ -427,10 +439,22 @@
   // Compute callee saves to pop and PC.
   RegList pop_list = 1 << PC;
   size_t pop_values = 1;
+  int32_t min_s = kNumberOfSRegisters;
+  int32_t max_s = -1;
   for (size_t i = 0; i < callee_save_regs.size(); i++) {
-    Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister();
-    pop_list |= 1 << reg;
-    pop_values++;
+    if (callee_save_regs.at(i).AsArm().IsCoreRegister()) {
+      Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister();
+      pop_list |= 1 << reg;
+      pop_values++;
+    } else {
+      CHECK(callee_save_regs.at(i).AsArm().IsSRegister());
+      min_s = std::min(static_cast<int>(callee_save_regs.at(i).AsArm().AsSRegister()), min_s);
+      max_s = std::max(static_cast<int>(callee_save_regs.at(i).AsArm().AsSRegister()), max_s);
+    }
+  }
+
+  if (max_s != -1) {
+    pop_values += 1 + max_s - min_s;
   }
 
   // Decrease frame to start of callee saves.
@@ -438,6 +462,10 @@
   size_t adjust = frame_size - (pop_values * kFramePointerSize);
   DecreaseFrameSize(adjust);
 
+  if (max_s != -1) {
+    vpops(static_cast<SRegister>(min_s), 1 + max_s - min_s);
+  }
+
   // Pop callee saves and PC.
   PopList(pop_list);
 }
diff --git a/compiler/utils/arm64/assembler_arm64.cc b/compiler/utils/arm64/assembler_arm64.cc
index 21014c8..58c7367 100644
--- a/compiler/utils/arm64/assembler_arm64.cc
+++ b/compiler/utils/arm64/assembler_arm64.cc
@@ -639,6 +639,7 @@
 }
 
 constexpr size_t kFramePointerSize = 8;
+constexpr unsigned int kJniRefSpillRegsSize = 11 + 8;
 
 void Arm64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
                         const std::vector<ManagedRegister>& callee_save_regs,
@@ -648,7 +649,7 @@
 
   // TODO: *create APCS FP - end of FP chain;
   //       *add support for saving a different set of callee regs.
-  // For now we check that the size of callee regs vector is 11.
+  // For now we check that the size of callee regs vector is 11 core registers and 8 fp registers.
   CHECK_EQ(callee_save_regs.size(), kJniRefSpillRegsSize);
   // Increase frame to required size - must be at least space to push StackReference<Method>.
   CHECK_GT(frame_size, kJniRefSpillRegsSize * kFramePointerSize);
@@ -682,6 +683,23 @@
   reg_offset -= 8;
   StoreToOffset(X20, SP, reg_offset);
 
+  reg_offset -= 8;
+  StoreDToOffset(D15, SP, reg_offset);
+  reg_offset -= 8;
+  StoreDToOffset(D14, SP, reg_offset);
+  reg_offset -= 8;
+  StoreDToOffset(D13, SP, reg_offset);
+  reg_offset -= 8;
+  StoreDToOffset(D12, SP, reg_offset);
+  reg_offset -= 8;
+  StoreDToOffset(D11, SP, reg_offset);
+  reg_offset -= 8;
+  StoreDToOffset(D10, SP, reg_offset);
+  reg_offset -= 8;
+  StoreDToOffset(D9, SP, reg_offset);
+  reg_offset -= 8;
+  StoreDToOffset(D8, SP, reg_offset);
+
   // Move TR(Caller saved) to ETR(Callee saved). The original (ETR)X21 has been saved on stack.
   // This way we make sure that TR is not trashed by native code.
   ___ Mov(reg_x(ETR), reg_x(TR));
@@ -753,6 +771,23 @@
   reg_offset -= 8;
   LoadFromOffset(X20, SP, reg_offset);
 
+  reg_offset -= 8;
+  LoadDFromOffset(D15, SP, reg_offset);
+  reg_offset -= 8;
+  LoadDFromOffset(D14, SP, reg_offset);
+  reg_offset -= 8;
+  LoadDFromOffset(D13, SP, reg_offset);
+  reg_offset -= 8;
+  LoadDFromOffset(D12, SP, reg_offset);
+  reg_offset -= 8;
+  LoadDFromOffset(D11, SP, reg_offset);
+  reg_offset -= 8;
+  LoadDFromOffset(D10, SP, reg_offset);
+  reg_offset -= 8;
+  LoadDFromOffset(D9, SP, reg_offset);
+  reg_offset -= 8;
+  LoadDFromOffset(D8, SP, reg_offset);
+
   // Decrease frame size to start of callee saved regs.
   DecreaseFrameSize(frame_size);
 
diff --git a/compiler/utils/arm64/constants_arm64.h b/compiler/utils/arm64/constants_arm64.h
index ffb54d3..01e8be9 100644
--- a/compiler/utils/arm64/constants_arm64.h
+++ b/compiler/utils/arm64/constants_arm64.h
@@ -29,8 +29,6 @@
 namespace art {
 namespace arm64 {
 
-constexpr unsigned int kJniRefSpillRegsSize = 11;
-
 constexpr size_t kArm64BaseBufferSize = 4096;
 
 }  // namespace arm64
diff --git a/runtime/stack.cc b/runtime/stack.cc
index b39aebf..d570880 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -134,8 +134,7 @@
     } else {
       return cur_shadow_frame_->GetVRegReference(0);
     }
-  } else if (m->IsOptimized(GetInstructionSetPointerSize(
-      Runtime::Current()->GetInstructionSet()))) {
+  } else if (m->IsOptimized(sizeof(void*))) {
     // TODO: Implement, currently only used for exceptions when jdwp is enabled.
     UNIMPLEMENTED(WARNING)
         << "StackVisitor::GetThisObject is unimplemented with the optimizing compiler";
@@ -163,42 +162,10 @@
   if (cur_quick_frame_ != nullptr) {
     DCHECK(context_ != nullptr);  // You can't reliably read registers without a context.
     DCHECK(m == GetMethod());
-    const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
-    DCHECK(code_pointer != nullptr);
-    const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
-    QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
-    uint32_t vmap_offset;
-    // TODO: IsInContext stops before spotting floating point registers.
-    if (vmap_table.IsInContext(vreg, kind, &vmap_offset)) {
-      bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
-      uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
-      uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
-      if (!IsAccessibleRegister(reg, is_float)) {
-        return false;
-      }
-      uintptr_t ptr_val = GetRegister(reg, is_float);
-      bool target64 = Is64BitInstructionSet(kRuntimeISA);
-      if (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 true;
+    if (m->IsOptimized(sizeof(void*))) {
+      return GetVRegFromOptimizedCode(m, vreg, kind, val);
     } else {
-      const DexFile::CodeItem* code_item = m->GetCodeItem();
-      DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
-                                                        // its instructions?
-      uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
-                                   frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
-      DCHECK(addr != nullptr);
-      *val = *addr;
-      return true;
+      return GetVRegFromQuickCode(m, vreg, kind, val);
     }
   } else {
     DCHECK(cur_shadow_frame_ != nullptr);
@@ -207,6 +174,86 @@
   }
 }
 
+bool StackVisitor::GetVRegFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind,
+                                        uint32_t* val) const {
+  const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
+  DCHECK(code_pointer != nullptr);
+  const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
+  QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
+  uint32_t vmap_offset;
+  // TODO: IsInContext stops before spotting floating point registers.
+  if (vmap_table.IsInContext(vreg, kind, &vmap_offset)) {
+    bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+    uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
+    uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
+    return GetRegisterIfAccessible(reg, kind, val);
+  } else {
+    const DexFile::CodeItem* code_item = m->GetCodeItem();
+    DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
+                                                      // its instructions?
+    *val = *GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
+                        frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+    return true;
+  }
+}
+
+bool StackVisitor::GetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind,
+                                            uint32_t* val) const {
+  const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
+  DCHECK(code_pointer != nullptr);
+  uint32_t native_pc_offset = m->NativeQuickPcOffset(cur_quick_frame_pc_);
+  CodeInfo code_info = m->GetOptimizedCodeInfo();
+  StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+  const DexFile::CodeItem* code_item = m->GetCodeItem();
+  DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
+                                                    // its instructions?
+  DCHECK_LT(vreg, code_item->registers_size_);
+  DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map,
+                                                                  code_item->registers_size_);
+  DexRegisterMap::LocationKind location_kind = dex_register_map.GetLocationKind(vreg);
+  switch (location_kind) {
+    case DexRegisterMap::kInStack: {
+      const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg);
+      const uint8_t* addr = reinterpret_cast<const uint8_t*>(cur_quick_frame_) + offset;
+      *val = *reinterpret_cast<const uint32_t*>(addr);
+      return true;
+    }
+    case DexRegisterMap::kInRegister:
+    case DexRegisterMap::kInFpuRegister: {
+      uint32_t reg = dex_register_map.GetMachineRegister(vreg);
+      return GetRegisterIfAccessible(reg, kind, val);
+    }
+    case DexRegisterMap::kConstant:
+      *val = dex_register_map.GetConstant(vreg);
+      return true;
+    case DexRegisterMap::kNone:
+      return false;
+  }
+  UNREACHABLE();
+  return false;
+}
+
+bool StackVisitor::GetRegisterIfAccessible(uint32_t reg, VRegKind kind, uint32_t* val) const {
+  const bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+  if (!IsAccessibleRegister(reg, is_float)) {
+    return false;
+  }
+  uintptr_t ptr_val = GetRegister(reg, is_float);
+  const bool target64 = Is64BitInstructionSet(kRuntimeISA);
+  if (target64) {
+    const bool wide_lo = (kind == kLongLoVReg) || (kind == kDoubleLoVReg);
+    const 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>(Low32Bits(value_long));
+    } else if (wide_hi) {
+      ptr_val = static_cast<uintptr_t>(High32Bits(value_long));
+    }
+  }
+  *val = ptr_val;
+  return true;
+}
+
 bool StackVisitor::GetVRegPair(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind_lo,
                                VRegKind kind_hi, uint64_t* val) const {
   if (kind_lo == kLongLoVReg) {
@@ -215,45 +262,15 @@
     DCHECK_EQ(kind_hi, kDoubleHiVReg);
   } else {
     LOG(FATAL) << "Expected long or double: kind_lo=" << kind_lo << ", kind_hi=" << kind_hi;
+    UNREACHABLE();
   }
   if (cur_quick_frame_ != nullptr) {
     DCHECK(context_ != nullptr);  // You can't reliably read registers without a context.
     DCHECK(m == GetMethod());
-    const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
-    DCHECK(code_pointer != nullptr);
-    const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
-    QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
-    uint32_t vmap_offset_lo, vmap_offset_hi;
-    // TODO: IsInContext stops before spotting floating point registers.
-    if (vmap_table.IsInContext(vreg, kind_lo, &vmap_offset_lo) &&
-        vmap_table.IsInContext(vreg + 1, kind_hi, &vmap_offset_hi)) {
-      bool is_float = (kind_lo == kDoubleLoVReg);
-      uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
-      uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo);
-      uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi);
-      if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) {
-        return false;
-      }
-      uintptr_t ptr_val_lo = GetRegister(reg_lo, is_float);
-      uintptr_t ptr_val_hi = GetRegister(reg_hi, is_float);
-      bool target64 = Is64BitInstructionSet(kRuntimeISA);
-      if (target64) {
-        int64_t value_long_lo = static_cast<int64_t>(ptr_val_lo);
-        int64_t value_long_hi = static_cast<int64_t>(ptr_val_hi);
-        ptr_val_lo = static_cast<uintptr_t>(value_long_lo & 0xFFFFFFFF);
-        ptr_val_hi = static_cast<uintptr_t>(value_long_hi >> 32);
-      }
-      *val = (static_cast<uint64_t>(ptr_val_hi) << 32) | static_cast<uint32_t>(ptr_val_lo);
-      return true;
+    if (m->IsOptimized(sizeof(void*))) {
+      return GetVRegPairFromOptimizedCode(m, vreg, kind_lo, kind_hi, val);
     } else {
-      const DexFile::CodeItem* code_item = m->GetCodeItem();
-      DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
-                                                        // its instructions?
-      uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
-                                   frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
-      DCHECK(addr != nullptr);
-      *val = *reinterpret_cast<uint64_t*>(addr);
-      return true;
+      return GetVRegPairFromQuickCode(m, vreg, kind_lo, kind_hi, val);
     }
   } else {
     DCHECK(cur_shadow_frame_ != nullptr);
@@ -262,61 +279,185 @@
   }
 }
 
+bool StackVisitor::GetVRegPairFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind_lo,
+                                            VRegKind kind_hi, uint64_t* val) const {
+  const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
+  DCHECK(code_pointer != nullptr);
+  const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
+  QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
+  uint32_t vmap_offset_lo, vmap_offset_hi;
+  // TODO: IsInContext stops before spotting floating point registers.
+  if (vmap_table.IsInContext(vreg, kind_lo, &vmap_offset_lo) &&
+      vmap_table.IsInContext(vreg + 1, kind_hi, &vmap_offset_hi)) {
+    bool is_float = (kind_lo == kDoubleLoVReg);
+    uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
+    uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo);
+    uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi);
+    return GetRegisterPairIfAccessible(reg_lo, reg_hi, kind_lo, val);
+  } else {
+    const DexFile::CodeItem* code_item = m->GetCodeItem();
+    DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
+                                                      // its instructions?
+    uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
+                                 frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+    *val = *reinterpret_cast<uint64_t*>(addr);
+    return true;
+  }
+}
+
+bool StackVisitor::GetVRegPairFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg,
+                                                VRegKind kind_lo, VRegKind kind_hi,
+                                                uint64_t* val) const {
+  uint32_t low_32bits;
+  uint32_t high_32bits;
+  bool success = GetVRegFromOptimizedCode(m, vreg, kind_lo, &low_32bits);
+  success &= GetVRegFromOptimizedCode(m, vreg + 1, kind_hi, &high_32bits);
+  if (success) {
+    *val = (static_cast<uint64_t>(high_32bits) << 32) | static_cast<uint64_t>(low_32bits);
+  }
+  return success;
+}
+
+bool StackVisitor::GetRegisterPairIfAccessible(uint32_t reg_lo, uint32_t reg_hi,
+                                               VRegKind kind_lo, uint64_t* val) const {
+  const bool is_float = (kind_lo == kDoubleLoVReg);
+  if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) {
+    return false;
+  }
+  uintptr_t ptr_val_lo = GetRegister(reg_lo, is_float);
+  uintptr_t ptr_val_hi = GetRegister(reg_hi, is_float);
+  bool target64 = Is64BitInstructionSet(kRuntimeISA);
+  if (target64) {
+    int64_t value_long_lo = static_cast<int64_t>(ptr_val_lo);
+    int64_t value_long_hi = static_cast<int64_t>(ptr_val_hi);
+    ptr_val_lo = static_cast<uintptr_t>(Low32Bits(value_long_lo));
+    ptr_val_hi = static_cast<uintptr_t>(High32Bits(value_long_hi));
+  }
+  *val = (static_cast<uint64_t>(ptr_val_hi) << 32) | static_cast<uint32_t>(ptr_val_lo);
+  return true;
+}
+
 bool StackVisitor::SetVReg(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_value,
                            VRegKind kind) {
   if (cur_quick_frame_ != nullptr) {
-    DCHECK(context_ != nullptr);  // You can't reliably write registers without a context.
-    DCHECK(m == GetMethod());
-    const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
-    DCHECK(code_pointer != nullptr);
-    const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
-    QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
-    uint32_t vmap_offset;
-    // TODO: IsInContext stops before spotting floating point registers.
-    if (vmap_table.IsInContext(vreg, kind, &vmap_offset)) {
-      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);
-      if (!IsAccessibleRegister(reg, is_float)) {
-        return false;
+      DCHECK(context_ != nullptr);  // You can't reliably write registers without a context.
+      DCHECK(m == GetMethod());
+      if (m->IsOptimized(sizeof(void*))) {
+        return SetVRegFromOptimizedCode(m, vreg, new_value, kind);
+      } else {
+        return SetVRegFromQuickCode(m, vreg, new_value, kind);
       }
-      bool target64 = Is64BitInstructionSet(kRuntimeISA);
-      // 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 = GetRegister(reg, is_float);
-          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);
-        }
-      }
-      SetRegister(reg, new_value, is_float);
-      return true;
     } else {
-      const DexFile::CodeItem* code_item = m->GetCodeItem();
-      DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
-                                                        // its instructions?
-      uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
-                                   frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
-      DCHECK(addr != nullptr);
-      *addr = new_value;
+      cur_shadow_frame_->SetVReg(vreg, new_value);
       return true;
     }
+}
+
+bool StackVisitor::SetVRegFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_value,
+                                        VRegKind kind) {
+  DCHECK(context_ != nullptr);  // You can't reliably write registers without a context.
+  DCHECK(m == GetMethod());
+  const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
+  DCHECK(code_pointer != nullptr);
+  const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
+  QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
+  uint32_t vmap_offset;
+  // TODO: IsInContext stops before spotting floating point registers.
+  if (vmap_table.IsInContext(vreg, kind, &vmap_offset)) {
+    bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+    uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
+    uint32_t reg = vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
+    return SetRegisterIfAccessible(reg, new_value, kind);
   } else {
-    DCHECK(cur_shadow_frame_ != nullptr);
-    cur_shadow_frame_->SetVReg(vreg, new_value);
+    const DexFile::CodeItem* code_item = m->GetCodeItem();
+    DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
+                                                      // its instructions?
+    uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
+                                 frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+    *addr = new_value;
     return true;
   }
 }
 
+bool StackVisitor::SetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_value,
+                                            VRegKind kind) {
+  const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
+  DCHECK(code_pointer != nullptr);
+  uint32_t native_pc_offset = m->NativeQuickPcOffset(cur_quick_frame_pc_);
+  CodeInfo code_info = m->GetOptimizedCodeInfo();
+  StackMap stack_map = code_info.GetStackMapForNativePcOffset(native_pc_offset);
+  const DexFile::CodeItem* code_item = m->GetCodeItem();
+  DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
+                                                    // its instructions?
+  DCHECK_LT(vreg, code_item->registers_size_);
+  DexRegisterMap dex_register_map = code_info.GetDexRegisterMapOf(stack_map,
+                                                                  code_item->registers_size_);
+  DexRegisterMap::LocationKind location_kind = dex_register_map.GetLocationKind(vreg);
+  uint32_t dex_pc = m->ToDexPc(cur_quick_frame_pc_, false);
+  switch (location_kind) {
+    case DexRegisterMap::kInStack: {
+      const int32_t offset = dex_register_map.GetStackOffsetInBytes(vreg);
+      uint8_t* addr = reinterpret_cast<uint8_t*>(cur_quick_frame_) + offset;
+      *reinterpret_cast<uint32_t*>(addr) = new_value;
+      return true;
+    }
+    case DexRegisterMap::kInRegister:
+    case DexRegisterMap::kInFpuRegister: {
+      uint32_t reg = dex_register_map.GetMachineRegister(vreg);
+      return SetRegisterIfAccessible(reg, new_value, kind);
+    }
+    case DexRegisterMap::kConstant:
+      LOG(ERROR) << StringPrintf("Cannot change value of DEX register v%u used as a constant at "
+                                 "DEX pc 0x%x (native pc 0x%x) of method %s",
+                                 vreg, dex_pc, native_pc_offset,
+                                 PrettyMethod(cur_quick_frame_->AsMirrorPtr()).c_str());
+      return false;
+    case DexRegisterMap::kNone:
+      LOG(ERROR) << StringPrintf("No location for DEX register v%u at DEX pc 0x%x "
+                                 "(native pc 0x%x) of method %s",
+                                 vreg, dex_pc, native_pc_offset,
+                                 PrettyMethod(cur_quick_frame_->AsMirrorPtr()).c_str());
+      return false;
+    default:
+      LOG(FATAL) << StringPrintf("Unknown location for DEX register v%u at DEX pc 0x%x "
+                                 "(native pc 0x%x) of method %s",
+                                 vreg, dex_pc, native_pc_offset,
+                                 PrettyMethod(cur_quick_frame_->AsMirrorPtr()).c_str());
+      UNREACHABLE();
+  }
+}
+
+bool StackVisitor::SetRegisterIfAccessible(uint32_t reg, uint32_t new_value, VRegKind kind) {
+  const bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+  if (!IsAccessibleRegister(reg, is_float)) {
+    return false;
+  }
+  const bool target64 = Is64BitInstructionSet(kRuntimeISA);
+
+  // Create a new value that can hold both low 32 and high 32 bits, in
+  // case we are running 64 bits.
+  uintptr_t full_new_value = new_value;
+  // 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 = GetRegister(reg, is_float);
+      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;
+      }
+      full_new_value = static_cast<uintptr_t>((old_reg_val_as_wide & mask) | new_vreg_portion);
+    }
+  }
+  SetRegister(reg, full_new_value, is_float);
+  return true;
+}
+
 bool StackVisitor::SetVRegPair(mirror::ArtMethod* m, uint16_t vreg, uint64_t new_value,
                                VRegKind kind_lo, VRegKind kind_hi) {
   if (kind_lo == kLongLoVReg) {
@@ -329,49 +470,10 @@
   if (cur_quick_frame_ != nullptr) {
     DCHECK(context_ != nullptr);  // You can't reliably write registers without a context.
     DCHECK(m == GetMethod());
-    const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
-    DCHECK(code_pointer != nullptr);
-    const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
-    QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
-    uint32_t vmap_offset_lo, vmap_offset_hi;
-    // TODO: IsInContext stops before spotting floating point registers.
-    if (vmap_table.IsInContext(vreg, kind_lo, &vmap_offset_lo) &&
-        vmap_table.IsInContext(vreg + 1, kind_hi, &vmap_offset_hi)) {
-      bool is_float = (kind_lo == kDoubleLoVReg);
-      uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
-      uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo);
-      uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi);
-      if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) {
-        return false;
-      }
-      uintptr_t new_value_lo = static_cast<uintptr_t>(new_value & 0xFFFFFFFF);
-      uintptr_t new_value_hi = static_cast<uintptr_t>(new_value >> 32);
-      bool target64 = Is64BitInstructionSet(kRuntimeISA);
-      // Deal with 32 or 64-bit wide registers in a way that builds on all targets.
-      if (target64) {
-        uintptr_t old_reg_val_lo = GetRegister(reg_lo, is_float);
-        uintptr_t old_reg_val_hi = GetRegister(reg_hi, is_float);
-        uint64_t new_vreg_portion_lo = static_cast<uint64_t>(new_value_lo);
-        uint64_t new_vreg_portion_hi = static_cast<uint64_t>(new_value_hi) << 32;
-        uint64_t old_reg_val_lo_as_wide = static_cast<uint64_t>(old_reg_val_lo);
-        uint64_t old_reg_val_hi_as_wide = static_cast<uint64_t>(old_reg_val_hi);
-        uint64_t mask_lo = static_cast<uint64_t>(0xffffffff) << 32;
-        uint64_t mask_hi = 0xffffffff;
-        new_value_lo = static_cast<uintptr_t>((old_reg_val_lo_as_wide & mask_lo) | new_vreg_portion_lo);
-        new_value_hi = static_cast<uintptr_t>((old_reg_val_hi_as_wide & mask_hi) | new_vreg_portion_hi);
-      }
-      SetRegister(reg_lo, new_value_lo, is_float);
-      SetRegister(reg_hi, new_value_hi, is_float);
-      return true;
+    if (m->IsOptimized(sizeof(void*))) {
+      return SetVRegPairFromOptimizedCode(m, vreg, new_value, kind_lo, kind_hi);
     } else {
-      const DexFile::CodeItem* code_item = m->GetCodeItem();
-      DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
-                                                        // its instructions?
-      uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
-                                   frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
-      DCHECK(addr != nullptr);
-      *reinterpret_cast<uint64_t*>(addr) = new_value;
-      return true;
+      return SetVRegPairFromQuickCode(m, vreg, new_value, kind_lo, kind_hi);
     }
   } else {
     DCHECK(cur_shadow_frame_ != nullptr);
@@ -380,6 +482,60 @@
   }
 }
 
+bool StackVisitor::SetVRegPairFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, uint64_t new_value,
+                                            VRegKind kind_lo, VRegKind kind_hi) {
+  const void* code_pointer = m->GetQuickOatCodePointer(sizeof(void*));
+  DCHECK(code_pointer != nullptr);
+  const VmapTable vmap_table(m->GetVmapTable(code_pointer, sizeof(void*)));
+  QuickMethodFrameInfo frame_info = m->GetQuickFrameInfo(code_pointer);
+  uint32_t vmap_offset_lo, vmap_offset_hi;
+  // TODO: IsInContext stops before spotting floating point registers.
+  if (vmap_table.IsInContext(vreg, kind_lo, &vmap_offset_lo) &&
+      vmap_table.IsInContext(vreg + 1, kind_hi, &vmap_offset_hi)) {
+    bool is_float = (kind_lo == kDoubleLoVReg);
+    uint32_t spill_mask = is_float ? frame_info.FpSpillMask() : frame_info.CoreSpillMask();
+    uint32_t reg_lo = vmap_table.ComputeRegister(spill_mask, vmap_offset_lo, kind_lo);
+    uint32_t reg_hi = vmap_table.ComputeRegister(spill_mask, vmap_offset_hi, kind_hi);
+    return SetRegisterPairIfAccessible(reg_lo, reg_hi, new_value, is_float);
+  } else {
+    const DexFile::CodeItem* code_item = m->GetCodeItem();
+    DCHECK(code_item != nullptr) << PrettyMethod(m);  // Can't be NULL or how would we compile
+                                                      // its instructions?
+    uint32_t* addr = GetVRegAddr(cur_quick_frame_, code_item, frame_info.CoreSpillMask(),
+                                 frame_info.FpSpillMask(), frame_info.FrameSizeInBytes(), vreg);
+    *reinterpret_cast<uint64_t*>(addr) = new_value;
+    return true;
+  }
+}
+
+bool StackVisitor::SetVRegPairFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, uint64_t new_value,
+                                                VRegKind kind_lo, VRegKind kind_hi) {
+  uint32_t low_32bits = Low32Bits(new_value);
+  uint32_t high_32bits = High32Bits(new_value);
+  bool success = SetVRegFromOptimizedCode(m, vreg, low_32bits, kind_lo);
+  success &= SetVRegFromOptimizedCode(m, vreg + 1, high_32bits, kind_hi);
+  return success;
+}
+
+bool StackVisitor::SetRegisterPairIfAccessible(uint32_t reg_lo, uint32_t reg_hi,
+                                               uint64_t new_value, bool is_float) {
+  if (!IsAccessibleRegister(reg_lo, is_float) || !IsAccessibleRegister(reg_hi, is_float)) {
+    return false;
+  }
+  uintptr_t new_value_lo = static_cast<uintptr_t>(new_value & 0xFFFFFFFF);
+  uintptr_t new_value_hi = static_cast<uintptr_t>(new_value >> 32);
+  bool target64 = Is64BitInstructionSet(kRuntimeISA);
+  // Deal with 32 or 64-bit wide registers in a way that builds on all targets.
+  if (target64) {
+    DCHECK_EQ(reg_lo, reg_hi);
+    SetRegister(reg_lo, new_value, is_float);
+  } else {
+    SetRegister(reg_lo, new_value_lo, is_float);
+    SetRegister(reg_hi, new_value_hi, is_float);
+  }
+  return true;
+}
+
 bool StackVisitor::IsAccessibleGPR(uint32_t reg) const {
   DCHECK(context_ != nullptr);
   return context_->IsAccessibleGPR(reg);
diff --git a/runtime/stack.h b/runtime/stack.h
index 5a86ca1..b495f03 100644
--- a/runtime/stack.h
+++ b/runtime/stack.h
@@ -492,7 +492,8 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     uint32_t val;
     bool success = GetVReg(m, vreg, kind, &val);
-    CHECK(success) << "Failed to read vreg " << vreg << " of kind " << kind;
+    CHECK(success) << "Failed to read v" << vreg << " of kind " << kind << " in method "
+                   << PrettyMethod(m);
     return val;
   }
 
@@ -505,7 +506,8 @@
     uint64_t val;
     bool success = GetVRegPair(m, vreg, kind_lo, kind_hi, &val);
     CHECK(success) << "Failed to read vreg pair " << vreg
-                   << " of kind [" << kind_lo << "," << kind_hi << "]";
+                   << " of kind [" << kind_lo << "," << kind_hi << "] in method "
+                   << PrettyMethod(m);
     return val;
   }
 
@@ -673,6 +675,45 @@
   uintptr_t GetFPR(uint32_t reg) const;
   void SetFPR(uint32_t reg, uintptr_t value);
 
+  bool GetVRegFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind,
+                            uint32_t* val) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool GetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind,
+                                uint32_t* val) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool GetRegisterIfAccessible(uint32_t reg, VRegKind kind, uint32_t* val) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  bool GetVRegPairFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, VRegKind kind_lo,
+                                VRegKind kind_hi, uint64_t* val) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool GetVRegPairFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg,
+                                    VRegKind kind_lo, VRegKind kind_hi,
+                                    uint64_t* val) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool GetRegisterPairIfAccessible(uint32_t reg_lo, uint32_t reg_hi, VRegKind kind_lo,
+                                   uint64_t* val) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  bool SetVRegFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_value,
+                            VRegKind kind)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool SetVRegFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, uint32_t new_value,
+                                VRegKind kind)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool SetRegisterIfAccessible(uint32_t reg, uint32_t new_value, VRegKind kind)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  bool SetVRegPairFromQuickCode(mirror::ArtMethod* m, uint16_t vreg, uint64_t new_value,
+                                VRegKind kind_lo, VRegKind kind_hi)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool SetVRegPairFromOptimizedCode(mirror::ArtMethod* m, uint16_t vreg, uint64_t new_value,
+                                    VRegKind kind_lo, VRegKind kind_hi)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  bool SetRegisterPairIfAccessible(uint32_t reg_lo, uint32_t reg_hi, uint64_t new_value,
+                                   bool is_float)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   void SanityCheckFrame() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   Thread* const thread_;
diff --git a/runtime/stack_map.h b/runtime/stack_map.h
index fd22361..ec37699 100644
--- a/runtime/stack_map.h
+++ b/runtime/stack_map.h
@@ -104,10 +104,9 @@
         return "in fpu register";
       case kConstant:
         return "as constant";
-      default:
-        LOG(FATAL) << "Invalid location kind " << static_cast<int>(kind);
-        return nullptr;
     }
+    UNREACHABLE();
+    return nullptr;
   }
 
   LocationKind GetLocationKind(uint16_t register_index) const {
@@ -126,6 +125,23 @@
         kFixedSize + sizeof(LocationKind) + register_index * SingleEntrySize());
   }
 
+  int32_t GetStackOffsetInBytes(uint16_t register_index) const {
+    DCHECK(GetLocationKind(register_index) == kInStack);
+    // We currently encode the offset in bytes.
+    return GetValue(register_index);
+  }
+
+  int32_t GetConstant(uint16_t register_index) const {
+    DCHECK(GetLocationKind(register_index) == kConstant);
+    return GetValue(register_index);
+  }
+
+  int32_t GetMachineRegister(uint16_t register_index) const {
+    DCHECK(GetLocationKind(register_index) == kInRegister
+        || GetLocationKind(register_index) == kInFpuRegister);
+    return GetValue(register_index);
+  }
+
   static size_t SingleEntrySize() {
     return sizeof(LocationKind) + sizeof(int32_t);
   }
diff --git a/test/454-get-vreg/expected.txt b/test/454-get-vreg/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/454-get-vreg/expected.txt
diff --git a/test/454-get-vreg/get_vreg_jni.cc b/test/454-get-vreg/get_vreg_jni.cc
new file mode 100644
index 0000000..937d2fe
--- /dev/null
+++ b/test/454-get-vreg/get_vreg_jni.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "arch/context.h"
+#include "jni.h"
+#include "mirror/art_method-inl.h"
+#include "scoped_thread_state_change.h"
+#include "stack.h"
+#include "thread.h"
+
+namespace art {
+
+namespace {
+
+class TestVisitor : public StackVisitor {
+ public:
+  TestVisitor(Thread* thread, Context* context, mirror::Object* this_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : StackVisitor(thread, context), this_value_(this_value), found_method_index_(0) {}
+
+  bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::ArtMethod* m = GetMethod();
+    std::string m_name(m->GetName());
+
+    if (m_name.compare("testSimpleVReg") == 0) {
+      found_method_index_ = 1;
+      uint32_t value = 0;
+
+      CHECK(GetVReg(m, 0, kIntVReg, &value));
+      CHECK_EQ(value, 42u);
+
+      bool success = GetVReg(m, 1, kIntVReg, &value);
+      if (m->IsOptimized(sizeof(void*))) CHECK(!success);
+
+      success = GetVReg(m, 2, kIntVReg, &value);
+      if (m->IsOptimized(sizeof(void*))) CHECK(!success);
+
+      CHECK(GetVReg(m, 3, kReferenceVReg, &value));
+      CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value_);
+
+      CHECK(GetVReg(m, 4, kIntVReg, &value));
+      CHECK_EQ(value, 1u);
+
+      CHECK(GetVReg(m, 5, kFloatVReg, &value));
+      uint32_t cast = bit_cast<float, uint32_t>(1.0f);
+      CHECK_EQ(value, cast);
+
+      CHECK(GetVReg(m, 6, kIntVReg, &value));
+      CHECK_EQ(value, 2u);
+
+      CHECK(GetVReg(m, 7, kIntVReg, &value));
+      CHECK_EQ(value, true);
+
+      CHECK(GetVReg(m, 8, kIntVReg, &value));
+      CHECK_EQ(value, 3u);
+
+      CHECK(GetVReg(m, 9, kIntVReg, &value));
+      CHECK_EQ(value, static_cast<uint32_t>('c'));
+    } else if (m_name.compare("testPairVReg") == 0) {
+      found_method_index_ = 2;
+      uint64_t value = 0;
+      CHECK(GetVRegPair(m, 0, kLongLoVReg, kLongHiVReg, &value));
+      CHECK_EQ(value, 42u);
+
+      bool success = GetVRegPair(m, 2, kLongLoVReg, kLongHiVReg, &value);
+      if (m->IsOptimized(sizeof(void*))) CHECK(!success);
+
+      success = GetVRegPair(m, 4, kLongLoVReg, kLongHiVReg, &value);
+      if (m->IsOptimized(sizeof(void*))) CHECK(!success);
+
+      uint32_t value32 = 0;
+      CHECK(GetVReg(m, 6, kReferenceVReg, &value32));
+      CHECK_EQ(reinterpret_cast<mirror::Object*>(value32), this_value_);
+
+      CHECK(GetVRegPair(m, 7, kLongLoVReg, kLongHiVReg, &value));
+      CHECK_EQ(static_cast<int64_t>(value), std::numeric_limits<int64_t>::min());
+
+      CHECK(GetVRegPair(m, 9, kLongLoVReg, kLongHiVReg, &value));
+      CHECK_EQ(static_cast<int64_t>(value), std::numeric_limits<int64_t>::max());
+
+      CHECK(GetVRegPair(m, 11, kLongLoVReg, kLongHiVReg, &value));
+      CHECK_EQ(value, 0u);
+
+      CHECK(GetVRegPair(m, 13, kDoubleLoVReg, kDoubleHiVReg, &value));
+      uint64_t cast = bit_cast<double, uint64_t>(2.0);
+      CHECK_EQ(value, cast);
+    }
+
+    return true;
+  }
+
+  mirror::Object* this_value_;
+
+  // Value returned to Java to ensure the methods testSimpleVReg and testPairVReg
+  // have been found and tested.
+  jint found_method_index_;
+};
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_doNativeCall(JNIEnv*, jobject value) {
+  ScopedObjectAccess soa(Thread::Current());
+  std::unique_ptr<Context> context(Context::Create());
+  TestVisitor visitor(soa.Self(), context.get(), soa.Decode<mirror::Object*>(value));
+  visitor.WalkStack();
+  return visitor.found_method_index_;
+}
+
+}  // namespace
+
+}  // namespace art
diff --git a/test/454-get-vreg/info.txt b/test/454-get-vreg/info.txt
new file mode 100644
index 0000000..20df0b5
--- /dev/null
+++ b/test/454-get-vreg/info.txt
@@ -0,0 +1 @@
+Tests for inspecting DEX registers in a Java method.
diff --git a/test/454-get-vreg/src/Main.java b/test/454-get-vreg/src/Main.java
new file mode 100644
index 0000000..df07d44
--- /dev/null
+++ b/test/454-get-vreg/src/Main.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public Main() {
+  }
+
+  int testSimpleVReg(int a, float f, short s, boolean z, byte b, char c) {
+    int e = doCall();
+    int g = doNativeCall();
+    return e + g;
+  }
+
+  long testPairVReg(long a, long b, long c, double e) {
+    long f = doCall();
+    long g = doNativeCall();
+    return f + g;
+  }
+
+  native int doNativeCall();
+
+  int doCall() {
+    return 42;
+  }
+
+  static {
+    System.loadLibrary("arttest");
+  }
+
+  public static void main(String[] args) {
+    Main rm = new Main();
+    if (rm.testSimpleVReg(1, 1.0f, (short)2, true, (byte)3, 'c') != 43) {
+      throw new Error("Expected 43");
+    }
+
+    if (rm.testPairVReg(Long.MIN_VALUE, Long.MAX_VALUE, 0, 2.0) != 44) {
+      throw new Error("Expected 44");
+    }
+  }
+}
diff --git a/test/455-set-vreg/expected.txt b/test/455-set-vreg/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/455-set-vreg/expected.txt
diff --git a/test/455-set-vreg/info.txt b/test/455-set-vreg/info.txt
new file mode 100644
index 0000000..e8c57b5
--- /dev/null
+++ b/test/455-set-vreg/info.txt
@@ -0,0 +1 @@
+Tests for setting DEX registers in a Java method.
diff --git a/test/455-set-vreg/set_vreg_jni.cc b/test/455-set-vreg/set_vreg_jni.cc
new file mode 100644
index 0000000..24d7832
--- /dev/null
+++ b/test/455-set-vreg/set_vreg_jni.cc
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "arch/context.h"
+#include "jni.h"
+#include "mirror/art_method-inl.h"
+#include "scoped_thread_state_change.h"
+#include "stack.h"
+#include "thread.h"
+
+namespace art {
+
+namespace {
+
+class TestVisitor : public StackVisitor {
+ public:
+  TestVisitor(Thread* thread, Context* context, mirror::Object* this_value)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
+      : StackVisitor(thread, context), this_value_(this_value) {}
+
+  bool VisitFrame() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    mirror::ArtMethod* m = GetMethod();
+    std::string m_name(m->GetName());
+
+    if (m_name.compare("testIntVReg") == 0) {
+      uint32_t value = 0;
+      CHECK(GetVReg(m, 1, kReferenceVReg, &value));
+      CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value_);
+
+      CHECK(SetVReg(m, 2, 5, kIntVReg));
+      CHECK(SetVReg(m, 3, 4, kIntVReg));
+      CHECK(SetVReg(m, 4, 3, kIntVReg));
+      CHECK(SetVReg(m, 5, 2, kIntVReg));
+      CHECK(SetVReg(m, 6, 1, kIntVReg));
+    } else if (m_name.compare("testLongVReg") == 0) {
+      uint32_t value = 0;
+      CHECK(GetVReg(m, 3, kReferenceVReg, &value));
+      CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value_);
+
+      CHECK(SetVRegPair(m, 4, std::numeric_limits<int64_t>::max(), kLongLoVReg, kLongHiVReg));
+      CHECK(SetVRegPair(m, 6, 4, kLongLoVReg, kLongHiVReg));
+      CHECK(SetVRegPair(m, 8, 3, kLongLoVReg, kLongHiVReg));
+      CHECK(SetVRegPair(m, 10, 2, kLongLoVReg, kLongHiVReg));
+      CHECK(SetVRegPair(m, 12, 1, kLongLoVReg, kLongHiVReg));
+    } else if (m_name.compare("testFloatVReg") == 0) {
+      uint32_t value = 0;
+      CHECK(GetVReg(m, 1, kReferenceVReg, &value));
+      CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value_);
+
+      CHECK(SetVReg(m, 2, bit_cast<float, uint32_t>(5.0f), kFloatVReg));
+      CHECK(SetVReg(m, 3, bit_cast<float, uint32_t>(4.0f), kFloatVReg));
+      CHECK(SetVReg(m, 4, bit_cast<float, uint32_t>(3.0f), kFloatVReg));
+      CHECK(SetVReg(m, 5, bit_cast<float, uint32_t>(2.0f), kFloatVReg));
+      CHECK(SetVReg(m, 6, bit_cast<float, uint32_t>(1.0f), kFloatVReg));
+    } else if (m_name.compare("testDoubleVReg") == 0) {
+      uint32_t value = 0;
+      CHECK(GetVReg(m, 3, kReferenceVReg, &value));
+      CHECK_EQ(reinterpret_cast<mirror::Object*>(value), this_value_);
+
+      CHECK(SetVRegPair(m, 4, bit_cast<double, uint64_t>(5.0), kDoubleLoVReg, kDoubleHiVReg));
+      CHECK(SetVRegPair(m, 6, bit_cast<double, uint64_t>(4.0), kDoubleLoVReg, kDoubleHiVReg));
+      CHECK(SetVRegPair(m, 8, bit_cast<double, uint64_t>(3.0), kDoubleLoVReg, kDoubleHiVReg));
+      CHECK(SetVRegPair(m, 10, bit_cast<double, uint64_t>(2.0), kDoubleLoVReg, kDoubleHiVReg));
+      CHECK(SetVRegPair(m, 12, bit_cast<double, uint64_t>(1.0), kDoubleLoVReg, kDoubleHiVReg));
+    }
+
+    return true;
+  }
+
+  mirror::Object* this_value_;
+};
+
+extern "C" JNIEXPORT void JNICALL Java_Main_doNativeCallSetVReg(JNIEnv*, jobject value) {
+  ScopedObjectAccess soa(Thread::Current());
+  std::unique_ptr<Context> context(Context::Create());
+  TestVisitor visitor(soa.Self(), context.get(), soa.Decode<mirror::Object*>(value));
+  visitor.WalkStack();
+}
+
+}  // namespace
+
+}  // namespace art
diff --git a/test/455-set-vreg/src/Main.java b/test/455-set-vreg/src/Main.java
new file mode 100644
index 0000000..2172d92
--- /dev/null
+++ b/test/455-set-vreg/src/Main.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+  public Main() {
+  }
+
+  int testIntVReg(int a, int b, int c, int d, int e) {
+    doNativeCallSetVReg();
+    return a - b - c - d - e;
+  }
+
+  long testLongVReg(long a, long b, long c, long d, long e) {
+    doNativeCallSetVReg();
+    return a - b - c - d - e;
+  }
+
+  float testFloatVReg(float a, float b, float c, float d, float e) {
+    doNativeCallSetVReg();
+    return a - b - c - d - e;
+  }
+
+  double testDoubleVReg(double a, double b, double c, double d, double e) {
+    doNativeCallSetVReg();
+    return a - b - c - d - e;
+  }
+
+  native void doNativeCallSetVReg();
+
+  static {
+    System.loadLibrary("arttest");
+  }
+
+  public static void main(String[] args) {
+    Main rm = new Main();
+    int intExpected = 5 - 4 - 3 - 2 - 1;
+    int intResult = rm.testIntVReg(0, 0, 0, 0, 0);
+    if (intResult != intExpected) {
+      throw new Error("Expected " + intExpected + ", got " + intResult);
+    }
+
+    long longExpected = Long.MAX_VALUE - 4 - 3 - 2 - 1;
+    long longResult = rm.testLongVReg(0, 0, 0, 0, 0);
+    if (longResult != longExpected) {
+      throw new Error("Expected " + longExpected + ", got " + longResult);
+    }
+
+    float floatExpected = 5.0f - 4.0f - 3.0f - 2.0f - 1.0f;
+    float floatResult = rm.testFloatVReg(0.0f, 0.0f, 0.0f, 0.0f, 0.0f);
+    if (floatResult != floatExpected) {
+      throw new Error("Expected " + floatExpected + ", got " + floatResult);
+    }
+
+    double doubleExpected = 5.0 - 4.0 - 3.0 - 2.0 - 1.0;
+    double doubleResult = rm.testDoubleVReg(0.0, 0.0, 0.0, 0.0, 0.0);
+    if (doubleResult != doubleExpected) {
+      throw new Error("Expected " + doubleExpected + ", got " + doubleResult);
+    }
+  }
+}
diff --git a/test/Android.libarttest.mk b/test/Android.libarttest.mk
index e64df5c..75c5d72 100644
--- a/test/Android.libarttest.mk
+++ b/test/Android.libarttest.mk
@@ -27,7 +27,9 @@
   051-thread/thread_test.cc \
   116-nodex2oat/nodex2oat.cc \
   117-nopatchoat/nopatchoat.cc \
-  118-noimage-dex2oat/noimage-dex2oat.cc
+  118-noimage-dex2oat/noimage-dex2oat.cc \
+  454-get-vreg/get_vreg_jni.cc \
+  455-set-vreg/set_vreg_jni.cc
 
 ART_TARGET_LIBARTTEST_$(ART_PHONY_TEST_TARGET_SUFFIX) += $(ART_TARGET_TEST_OUT)/$(TARGET_ARCH)/libarttest.so
 ifdef TARGET_2ND_ARCH
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index ee40ee8..7a2ad1c 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -361,8 +361,8 @@
              export ANDROID_DATA=$DEX_LOCATION && \
              export DEX_LOCATION=$DEX_LOCATION && \
              export ANDROID_ROOT=$ANDROID_ROOT && \
-             export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
              $mkdir_cmdline && \
+             export LD_LIBRARY_PATH=$LD_LIBRARY_PATH && \
              $dex2oat_cmdline && \
              $dalvikvm_cmdline"