Add "kind" argument to Get/SetVReg.

In order to determine where a register is promoted its necessary to know
the kind of use of the register.
Extend notion of precise-ness to numeric verifier register types.
Dump verifier output in oatdump.
Dump vregs with their location or constant value.
Introduce indenting ostream utility.

Change-Id: Ia3d29497877976bc24465484743bca08236e1768
diff --git a/build/Android.common.mk b/build/Android.common.mk
index d200874..d86a785 100644
--- a/build/Android.common.mk
+++ b/build/Android.common.mk
@@ -366,6 +366,7 @@
 	src/gtest_test.cc \
 	src/heap_test.cc \
 	src/image_test.cc \
+	src/indenter_test.cc \
 	src/indirect_reference_table_test.cc \
 	src/intern_table_test.cc \
 	src/jni_internal_test.cc \
diff --git a/src/class_linker.cc b/src/class_linker.cc
index 689b928..9a354e1 100644
--- a/src/class_linker.cc
+++ b/src/class_linker.cc
@@ -683,6 +683,11 @@
 
 void ClassLinker::RegisterOatFileLocked(const OatFile& oat_file) {
   dex_lock_.AssertHeld(Thread::Current());
+#ifndef NDEBUG
+  for (size_t i = 0; i < oat_files_.size(); ++i) {
+    CHECK_NE(&oat_file, oat_files_[i]) << oat_file.GetLocation();
+  }
+#endif
   oat_files_.push_back(&oat_file);
 }
 
@@ -955,7 +960,6 @@
   if (oat_file == NULL) {
     return NULL;
   }
-  CHECK(oat_file != NULL) << oat_location;
   return oat_file;
 }
 
@@ -1840,13 +1844,27 @@
 
 DexCache* ClassLinker::FindDexCache(const DexFile& dex_file) const {
   MutexLock mu(Thread::Current(), dex_lock_);
+  // Search assuming unique-ness of dex file.
   for (size_t i = 0; i != dex_caches_.size(); ++i) {
     DexCache* dex_cache = dex_caches_[i];
     if (dex_cache->GetDexFile() == &dex_file) {
       return dex_cache;
     }
   }
-  LOG(FATAL) << "Failed to find DexCache for DexFile " << dex_file.GetLocation();
+  // Search matching by location name.
+  std::string location(dex_file.GetLocation());
+  for (size_t i = 0; i != dex_caches_.size(); ++i) {
+    DexCache* dex_cache = dex_caches_[i];
+    if (dex_cache->GetDexFile()->GetLocation() == location) {
+      return dex_cache;
+    }
+  }
+  // Failure, dump diagnostic and abort.
+  for (size_t i = 0; i != dex_caches_.size(); ++i) {
+    DexCache* dex_cache = dex_caches_[i];
+    LOG(ERROR) << "Registered dex file " << i << " = " << dex_cache->GetDexFile()->GetLocation();
+  }
+  LOG(FATAL) << "Failed to find DexCache for DexFile " << location;
   return NULL;
 }
 
diff --git a/src/debugger.cc b/src/debugger.cc
index 1d8b997..42c0c7f 100644
--- a/src/debugger.cc
+++ b/src/debugger.cc
@@ -25,9 +25,7 @@
 #include "dex_instruction.h"
 #include "gc/large_object_space.h"
 #include "gc/space.h"
-#if !defined(ART_USE_LLVM_COMPILER)
-#include "oat/runtime/context.h"  // For VmapTable
-#endif
+#include "oat/runtime/context.h"
 #include "object_utils.h"
 #include "safe_map.h"
 #include "ScopedLocalRef.h"
@@ -1691,7 +1689,7 @@
       this_object = NULL;
     } else {
       uint16_t reg = DemangleSlot(0, m);
-      this_object = reinterpret_cast<Object*>(GetVReg(m, reg));
+      this_object = reinterpret_cast<Object*>(GetVReg(m, reg, kReferenceVReg));
     }
     return false;
   }
@@ -1759,7 +1757,7 @@
       case JDWP::JT_BOOLEAN:
         {
           CHECK_EQ(width_, 1U);
-          uint32_t intVal = GetVReg(m, reg);
+          uint32_t intVal = GetVReg(m, reg, kIntVReg);
           VLOG(jdwp) << "get boolean local " << reg << " = " << intVal;
           JDWP::Set1(buf_+1, intVal != 0);
         }
@@ -1767,7 +1765,7 @@
       case JDWP::JT_BYTE:
         {
           CHECK_EQ(width_, 1U);
-          uint32_t intVal = GetVReg(m, reg);
+          uint32_t intVal = GetVReg(m, reg, kIntVReg);
           VLOG(jdwp) << "get byte local " << reg << " = " << intVal;
           JDWP::Set1(buf_+1, intVal);
         }
@@ -1776,16 +1774,23 @@
       case JDWP::JT_CHAR:
         {
           CHECK_EQ(width_, 2U);
-          uint32_t intVal = GetVReg(m, reg);
+          uint32_t intVal = GetVReg(m, reg, kIntVReg);
           VLOG(jdwp) << "get short/char local " << reg << " = " << intVal;
           JDWP::Set2BE(buf_+1, intVal);
         }
         break;
       case JDWP::JT_INT:
+        {
+          CHECK_EQ(width_, 4U);
+          uint32_t intVal = GetVReg(m, reg, kIntVReg);
+          VLOG(jdwp) << "get int local " << reg << " = " << intVal;
+          JDWP::Set4BE(buf_+1, intVal);
+        }
+        break;
       case JDWP::JT_FLOAT:
         {
           CHECK_EQ(width_, 4U);
-          uint32_t intVal = GetVReg(m, reg);
+          uint32_t intVal = GetVReg(m, reg, kFloatVReg);
           VLOG(jdwp) << "get int/float local " << reg << " = " << intVal;
           JDWP::Set4BE(buf_+1, intVal);
         }
@@ -1793,7 +1798,7 @@
       case JDWP::JT_ARRAY:
         {
           CHECK_EQ(width_, sizeof(JDWP::ObjectId));
-          Object* o = reinterpret_cast<Object*>(GetVReg(m, reg));
+          Object* o = reinterpret_cast<Object*>(GetVReg(m, reg, kReferenceVReg));
           VLOG(jdwp) << "get array local " << reg << " = " << o;
           if (!Runtime::Current()->GetHeap()->IsHeapAddress(o)) {
             LOG(FATAL) << "Register " << reg << " expected to hold array: " << o;
@@ -1809,7 +1814,7 @@
       case JDWP::JT_THREAD_GROUP:
         {
           CHECK_EQ(width_, sizeof(JDWP::ObjectId));
-          Object* o = reinterpret_cast<Object*>(GetVReg(m, reg));
+          Object* o = reinterpret_cast<Object*>(GetVReg(m, reg, kReferenceVReg));
           VLOG(jdwp) << "get object local " << reg << " = " << o;
           if (!Runtime::Current()->GetHeap()->IsHeapAddress(o)) {
             LOG(FATAL) << "Register " << reg << " expected to hold object: " << o;
@@ -1819,11 +1824,20 @@
         }
         break;
       case JDWP::JT_DOUBLE:
+        {
+          CHECK_EQ(width_, 8U);
+          uint32_t lo = GetVReg(m, reg, kDoubleLoVReg);
+          uint64_t hi = GetVReg(m, reg + 1, kDoubleHiVReg);
+          uint64_t longVal = (hi << 32) | lo;
+          VLOG(jdwp) << "get double/long local " << hi << ":" << lo << " = " << longVal;
+          JDWP::Set8BE(buf_+1, longVal);
+        }
+        break;
       case JDWP::JT_LONG:
         {
           CHECK_EQ(width_, 8U);
-          uint32_t lo = GetVReg(m, reg);
-          uint64_t hi = GetVReg(m, reg + 1);
+          uint32_t lo = GetVReg(m, reg, kLongLoVReg);
+          uint64_t hi = GetVReg(m, reg + 1, kLongHiVReg);
           uint64_t longVal = (hi << 32) | lo;
           VLOG(jdwp) << "get double/long local " << hi << ":" << lo << " = " << longVal;
           JDWP::Set8BE(buf_+1, longVal);
@@ -1878,17 +1892,20 @@
         case JDWP::JT_BOOLEAN:
         case JDWP::JT_BYTE:
           CHECK_EQ(width_, 1U);
-          SetVReg(m, reg, static_cast<uint32_t>(value_));
+          SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg);
           break;
         case JDWP::JT_SHORT:
         case JDWP::JT_CHAR:
           CHECK_EQ(width_, 2U);
-          SetVReg(m, reg, static_cast<uint32_t>(value_));
+          SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg);
           break;
         case JDWP::JT_INT:
+          CHECK_EQ(width_, 4U);
+          SetVReg(m, reg, static_cast<uint32_t>(value_), kIntVReg);
+          break;
         case JDWP::JT_FLOAT:
           CHECK_EQ(width_, 4U);
-          SetVReg(m, reg, static_cast<uint32_t>(value_));
+          SetVReg(m, reg, static_cast<uint32_t>(value_), kFloatVReg);
           break;
         case JDWP::JT_ARRAY:
         case JDWP::JT_OBJECT:
@@ -1899,14 +1916,18 @@
           if (o == kInvalidObject) {
             UNIMPLEMENTED(FATAL) << "return an error code when given an invalid object to store";
           }
-          SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)));
+          SetVReg(m, reg, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(o)), kReferenceVReg);
         }
         break;
         case JDWP::JT_DOUBLE:
+          CHECK_EQ(width_, 8U);
+          SetVReg(m, reg, static_cast<uint32_t>(value_), kDoubleLoVReg);
+          SetVReg(m, reg + 1, static_cast<uint32_t>(value_ >> 32), kDoubleHiVReg);
+          break;
         case JDWP::JT_LONG:
           CHECK_EQ(width_, 8U);
-          SetVReg(m, reg, static_cast<uint32_t>(value_));
-          SetVReg(m, reg + 1, static_cast<uint32_t>(value_ >> 32));
+          SetVReg(m, reg, static_cast<uint32_t>(value_), kLongLoVReg);
+          SetVReg(m, reg + 1, static_cast<uint32_t>(value_ >> 32), kLongHiVReg);
           break;
         default:
           LOG(FATAL) << "Unknown tag " << tag_;
diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc
index 201a8e6..d3aa238 100644
--- a/src/dex_instruction.cc
+++ b/src/dex_instruction.cc
@@ -289,8 +289,8 @@
       switch (insn.opcode) {
         case CONST_STRING:
           if (file != NULL) {
-            os << StringPrintf("const-string v%d, \"%s\" // string@%d", insn.vA,
-                               file->StringDataByIdx(insn.vB), insn.vB);
+            os << StringPrintf("const-string v%d, %s // string@%d", insn.vA,
+                               PrintableString(file->StringDataByIdx(insn.vB)).c_str(), insn.vB);
             break;
           }  // else fall-through
         case CHECK_CAST:
diff --git a/src/disassembler_arm.cc b/src/disassembler_arm.cc
index d047f0e..15480b9 100644
--- a/src/disassembler_arm.cc
+++ b/src/disassembler_arm.cc
@@ -275,7 +275,7 @@
     opcode += kConditionCodeNames[cond];
     opcode += suffixes;
     // TODO: a more complete ARM disassembler could generate wider opcodes.
-    os << StringPrintf("\t\t\t%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
+    os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
 }
 
 size_t DisassemblerArm::DumpThumb32(std::ostream& os, const uint8_t* instr_ptr) {
@@ -854,7 +854,7 @@
     it_conditions_.pop_back();
   }
 
-  os << StringPrintf("\t\t\t%p: %08x\t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
+  os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
   return 4;
 }
 
@@ -1157,7 +1157,7 @@
       it_conditions_.pop_back();
     }
 
-    os << StringPrintf("\t\t\t%p: %04x    \t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
+    os << StringPrintf("%p: %04x    \t%-7s ", instr_ptr, instr, opcode.str().c_str()) << args.str() << '\n';
   }
   return 2;
 }
diff --git a/src/disassembler_mips.cc b/src/disassembler_mips.cc
index 86a661c..0efcb0b 100644
--- a/src/disassembler_mips.cc
+++ b/src/disassembler_mips.cc
@@ -254,7 +254,7 @@
     }
   }
 
-  os << StringPrintf("\t\t\t%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
+  os << StringPrintf("%p: %08x\t%-7s ", instr_ptr, instruction, opcode.c_str()) << args.str() << '\n';
 }
 
 DisassemblerMips::DisassemblerMips() {
diff --git a/src/disassembler_x86.cc b/src/disassembler_x86.cc
index 35593a3..bb187e1 100644
--- a/src/disassembler_x86.cc
+++ b/src/disassembler_x86.cc
@@ -731,7 +731,7 @@
   for (size_t i = 0; begin_instr + i < instr; ++i) {
     hex << StringPrintf("%02X", begin_instr[i]);
   }
-  os << StringPrintf("\t\t\t%p: %22s    \t%-7s ", begin_instr, hex.str().c_str(), opcode.str().c_str()) << args.str() << '\n';
+  os << StringPrintf("%p: %22s    \t%-7s ", begin_instr, hex.str().c_str(), opcode.str().c_str()) << args.str() << '\n';
   return instr - begin_instr;
 }
 
diff --git a/src/gc/mark_sweep.cc b/src/gc/mark_sweep.cc
index d6c44db..1ccceaa 100644
--- a/src/gc/mark_sweep.cc
+++ b/src/gc/mark_sweep.cc
@@ -272,8 +272,7 @@
       LOG(ERROR) << "Found invalid root: " << root;
       LOG(ERROR) << "VReg: " << vreg;
       if (method != NULL) {
-        LOG(ERROR) << "In method " << PrettyMethod(method, true) << "\nVerifier output:\n";
-        verifier::MethodVerifier::VerifyMethodAndDump(const_cast<AbstractMethod*>(method));
+        LOG(ERROR) << "In method " << PrettyMethod(method, true);
       }
     }
   }
diff --git a/src/indenter.h b/src/indenter.h
new file mode 100644
index 0000000..f66df08
--- /dev/null
+++ b/src/indenter.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+#ifndef ART_SRC_INDENTER_H_
+#define ART_SRC_INDENTER_H_
+
+#include "macros.h"
+#include <streambuf>
+
+const char kIndentChar =' ';
+const size_t kIndentBy1Count = 2;
+
+class Indenter : public std::streambuf {
+ public:
+  Indenter(std::streambuf* out, char text, size_t count)
+      : indent_next_(true), out_sbuf_(out), text_(text), count_(count) {}
+
+ private:
+  int_type overflow(int_type c) {
+    if (c != std::char_traits<char>::eof()) {
+      if (indent_next_) {
+        for (size_t i = 0; i < count_; ++i) {
+          out_sbuf_->sputc(text_);
+        }
+      }
+      out_sbuf_->sputc(c);
+      indent_next_ = (c == '\n');
+    }
+    return std::char_traits<char>::not_eof(c);
+  }
+
+  int sync() {
+    return out_sbuf_->pubsync();
+  }
+
+  bool indent_next_;
+
+  // Buffer to write output to.
+  std::streambuf* const out_sbuf_;
+
+  // Text output as indent.
+  const char text_;
+
+  // Number of times text is output.
+  const size_t count_;
+
+  DISALLOW_COPY_AND_ASSIGN(Indenter);
+};
+
+#endif // ART_SRC_INDENTER_H_
diff --git a/src/indenter_test.cc b/src/indenter_test.cc
new file mode 100644
index 0000000..1919e3d
--- /dev/null
+++ b/src/indenter_test.cc
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2011 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 "gtest/gtest.h"
+#include "indenter.h"
+
+TEST(IndenterTest, MultiLineTest) {
+  std::ostringstream output;
+  Indenter indent_filter(output.rdbuf(), '\t', 2);
+  std::ostream input(&indent_filter);
+
+  EXPECT_EQ(output.str(), "");
+
+  input << "hello";
+  EXPECT_EQ(output.str(), "\t\thello");
+
+  input << "\nhello again";
+  EXPECT_EQ(output.str(), "\t\thello\n\t\thello again");
+
+  input << "\n";
+  EXPECT_EQ(output.str(), "\t\thello\n\t\thello again\n");
+}
diff --git a/src/monitor.cc b/src/monitor.cc
index 7c433f7..1fbda87 100644
--- a/src/monitor.cc
+++ b/src/monitor.cc
@@ -932,7 +932,8 @@
       }
 
       uint16_t monitor_register = ((monitor_enter_instruction >> 8) & 0xff);
-      Object* o = reinterpret_cast<Object*>(stack_visitor->GetVReg(m, monitor_register));
+      Object* o = reinterpret_cast<Object*>(stack_visitor->GetVReg(m, monitor_register,
+                                                                   kReferenceVReg));
       DumpLockedObject(os, o);
     }
   }
diff --git a/src/oat/runtime/context.h b/src/oat/runtime/context.h
index 7002f6a..317030f 100644
--- a/src/oat/runtime/context.h
+++ b/src/oat/runtime/context.h
@@ -57,54 +57,6 @@
   };
 };
 
-class VmapTable {
- public:
-  explicit VmapTable(const uint16_t* table) : table_(table) {
-  }
-
-  uint16_t operator[](size_t i) const {
-    return table_[i + 1];
-  }
-
-  size_t size() const {
-    return table_[0];
-  }
-
-  /*
-   * WARNING: This code should be changed or renamed.  The "reg"
-   * argument is a Dalvik virtual register number, but the way
-   * the vmap and register promotion works a Dalvik vReg can have
-   * neither, one or both of core register and floating point register
-   * identities. The "INVALID_VREG" marker of 0xffff below separates the
-   * core promoted registers from the floating point promoted registers,
-   * and thus terminates the search before reaching the fp section.
-   * This is likely the desired behavior for GC, as references won't
-   * ever be promoted to float registers - but we'll probably want to
-   * rework this shared code to make it useful for the debugger as well.
-   */
-  // Is register 'reg' in the context or on the stack?
-  bool IsInContext(size_t reg, uint32_t& vmap_offset) const {
-    vmap_offset = 0xEBAD0FF5;
-    // TODO: take advantage of the registers being ordered
-    for (size_t i = 0; i < size(); ++i) {
-      // Stop if we find what we are are looking for...
-      if (table_[i + 1] == reg) {
-        vmap_offset = i;
-        return true;
-      }
-      // ...or the INVALID_VREG that marks lr.
-      // TODO: x86?
-      if (table_[i + 1] == 0xffff) {
-        break;
-      }
-    }
-    return false;
-  }
-
- private:
-  const uint16_t* table_;
-};
-
 }  // namespace art
 
 #endif  // ART_SRC_OAT_RUNTIME_CONTEXT_H_
diff --git a/src/oatdump.cc b/src/oatdump.cc
index a371d28..45d936a 100644
--- a/src/oatdump.cc
+++ b/src/oatdump.cc
@@ -26,17 +26,18 @@
 #include "dex_instruction.h"
 #include "disassembler.h"
 #include "file.h"
+#include "gc_map.h"
 #include "gc/large_object_space.h"
 #include "gc/space.h"
 #include "image.h"
-#include "oat/runtime/context.h"  // For VmapTable
+#include "indenter.h"
 #include "object_utils.h"
 #include "os.h"
 #include "runtime.h"
 #include "safe_map.h"
 #include "scoped_thread_state_change.h"
 #include "stringpiece.h"
-#include "gc_map.h"
+#include "verifier/method_verifier.h"
 
 namespace art {
 
@@ -251,7 +252,9 @@
          // TODO: JACK CLASS ACCESS (HACK TO BE REMOVED)
          << ( (class_def.access_flags_ & kAccClassJack) == kAccClassJack ? " (Jack)" : "" )
          << "\n";
-      DumpOatClass(os, *oat_class.get(), *(dex_file.get()), class_def);
+      Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+      std::ostream indented_os(&indent_filter);
+      DumpOatClass(indented_os, *oat_class.get(), *(dex_file.get()), class_def);
     }
 
     os << std::flush;
@@ -274,73 +277,105 @@
     }
     ClassDataItemIterator it(dex_file, class_data);
     SkipAllFields(it);
-
-    uint32_t class_method_index = 0;
+    uint32_t class_def_idx = dex_file.GetIndexForClassDef(class_def);
+    uint32_t class_method_idx = 0;
     while (it.HasNextDirectMethod()) {
-      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
-      DumpOatMethod(os, class_method_index, oat_method, dex_file,
-                    it.GetMemberIndex(), it.GetMethodCodeItem());
-      class_method_index++;
+      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+      DumpOatMethod(os, class_def_idx, class_method_idx, oat_method, dex_file,
+                    it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetMemberAccessFlags());
+      class_method_idx++;
       it.Next();
     }
     while (it.HasNextVirtualMethod()) {
-      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_index);
-      DumpOatMethod(os, class_method_index, oat_method, dex_file,
-                    it.GetMemberIndex(), it.GetMethodCodeItem());
-      class_method_index++;
+      const OatFile::OatMethod oat_method = oat_class.GetOatMethod(class_method_idx);
+      DumpOatMethod(os, class_def_idx, class_method_idx, oat_method, dex_file,
+                    it.GetMemberIndex(), it.GetMethodCodeItem(), it.GetMemberAccessFlags());
+      class_method_idx++;
       it.Next();
     }
     DCHECK(!it.HasNext());
     os << std::flush;
   }
 
-  void DumpOatMethod(std::ostream& os, uint32_t class_method_index,
+  void DumpOatMethod(std::ostream& os, uint32_t class_def_idx, uint32_t class_method_index,
                      const OatFile::OatMethod& oat_method, const DexFile& dex_file,
-                     uint32_t dex_method_idx, const DexFile::CodeItem* code_item) {
-    os << StringPrintf("\t%d: %s (dex_method_idx=%d)\n",
+                     uint32_t dex_method_idx, const DexFile::CodeItem* code_item,
+                     uint32_t method_access_flags) {
+    os << StringPrintf("%d: %s (dex_method_idx=%d)\n",
                        class_method_index, PrettyMethod(dex_method_idx, dex_file, true).c_str(),
                        dex_method_idx);
-    os << StringPrintf("\t\tframe_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes());
-    os << StringPrintf("\t\tcore_spill_mask: 0x%08x\n", oat_method.GetCoreSpillMask());
-    DumpSpillMask(os, oat_method.GetCoreSpillMask(), false);
-    os << StringPrintf("\n\t\tfp_spill_mask: 0x%08x\n", oat_method.GetFpSpillMask());
-    DumpSpillMask(os, oat_method.GetFpSpillMask(), true);
-    os << StringPrintf("\t\tvmap_table: %p (offset=0x%08x)\n",
-                       oat_method.GetVmapTable(), oat_method.GetVmapTableOffset());
-    DumpVmap(os, oat_method);
-    const bool kDumpRawMappingTable = false;
-    if (kDumpRawMappingTable) {
-      os << StringPrintf("\t\tmapping_table: %p (offset=0x%08x)\n",
-                         oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
-      DumpMappingTable(os, oat_method);
+    Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+    std::ostream indent1_os(&indent1_filter);
+    {
+      indent1_os << "DEX CODE:\n";
+      Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+      std::ostream indent2_os(&indent2_filter);
+      DumpDexCode(indent2_os, dex_file, code_item);
     }
-    const bool kDumpRawGcMap = false;
-    if (kDumpRawGcMap) {
-      os << StringPrintf("\t\tgc_map: %p (offset=0x%08x)\n",
-                         oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset());
-      DumpGcMap(os, oat_method, code_item);
+    if (Runtime::Current() != NULL) {
+      indent1_os << "VERIFIER TYPE ANALYSIS:\n";
+      Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+      std::ostream indent2_os(&indent2_filter);
+      DumpVerifier(indent2_os, dex_method_idx, &dex_file, class_def_idx, code_item, method_access_flags);
     }
-    os << "\t\tDEX CODE:\n";
-    DumpDexCode(os, dex_file, code_item);
-    os << StringPrintf("\t\tCODE: %p (offset=0x%08x size=%d)%s\n",
-                       oat_method.GetCode(),
-                       oat_method.GetCodeOffset(),
-                       oat_method.GetCodeSize(),
-                       oat_method.GetCode() != NULL ? "..." : "");
-    DumpCode(os, oat_method, code_item);
-    os << StringPrintf("\t\tINVOKE STUB: %p (offset=0x%08x size=%d)%s\n",
-                       oat_method.GetInvokeStub(),
-                       oat_method.GetInvokeStubOffset(),
-                       oat_method.GetInvokeStubSize(),
-                       oat_method.GetInvokeStub() != NULL ? "..." : "");
-    DumpInvokeStub(os, oat_method);
+    {
+      indent1_os << "OAT DATA:\n";
+      Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+      std::ostream indent2_os(&indent2_filter);
+
+      indent2_os << StringPrintf("frame_size_in_bytes: %zd\n", oat_method.GetFrameSizeInBytes());
+      indent2_os << StringPrintf("core_spill_mask: 0x%08x ", oat_method.GetCoreSpillMask());
+      DumpSpillMask(indent2_os, oat_method.GetCoreSpillMask(), false);
+      indent2_os << StringPrintf("\nfp_spill_mask: 0x%08x ", oat_method.GetFpSpillMask());
+      DumpSpillMask(indent2_os, oat_method.GetFpSpillMask(), true);
+      indent2_os << StringPrintf("\nvmap_table: %p (offset=0x%08x)\n",
+                                 oat_method.GetVmapTable(), oat_method.GetVmapTableOffset());
+      DumpVmap(indent2_os, oat_method);
+      indent2_os << StringPrintf("mapping_table: %p (offset=0x%08x)\n",
+                                 oat_method.GetMappingTable(), oat_method.GetMappingTableOffset());
+      const bool kDumpRawMappingTable = false;
+      if (kDumpRawMappingTable) {
+        Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
+        std::ostream indent3_os(&indent3_filter);
+        DumpMappingTable(indent3_os, oat_method);
+      }
+      indent2_os << StringPrintf("gc_map: %p (offset=0x%08x)\n",
+                                 oat_method.GetNativeGcMap(), oat_method.GetNativeGcMapOffset());
+      const bool kDumpRawGcMap = false;
+      if (kDumpRawGcMap) {
+        Indenter indent3_filter(indent2_os.rdbuf(), kIndentChar, kIndentBy1Count);
+        std::ostream indent3_os(&indent3_filter);
+        DumpGcMap(indent3_os, oat_method, code_item);
+      }
+    }
+    {
+      indent1_os << StringPrintf("CODE: %p (offset=0x%08x size=%d)%s\n",
+                                 oat_method.GetCode(),
+                                 oat_method.GetCodeOffset(),
+                                 oat_method.GetCodeSize(),
+                                 oat_method.GetCode() != NULL ? "..." : "");
+      Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+      std::ostream indent2_os(&indent2_filter);
+      DumpCode(indent2_os, oat_method, dex_method_idx, &dex_file, class_def_idx, code_item,
+               method_access_flags);
+    }
+    {
+      indent1_os << StringPrintf("INVOKE STUB: %p (offset=0x%08x size=%d)%s\n",
+                                 oat_method.GetInvokeStub(),
+                                 oat_method.GetInvokeStubOffset(),
+                                 oat_method.GetInvokeStubSize(),
+                                 oat_method.GetInvokeStub() != NULL ? "..." : "");
+      Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+      std::ostream indent2_os(&indent2_filter);
+      DumpInvokeStub(indent2_os, oat_method);
+    }
   }
 
   void DumpSpillMask(std::ostream& os, uint32_t spill_mask, bool is_float) {
     if (spill_mask == 0) {
       return;
     }
-    os << " (";
+    os << "(";
     for (size_t i = 0; i < 32; i++) {
       if ((spill_mask & (1 << i)) != 0) {
         if (is_float) {
@@ -366,57 +401,38 @@
     }
     const VmapTable vmap_table(raw_table);
     bool first = true;
-    os << "\t\t\t";
+    bool processing_fp = false;
+    uint32_t spill_mask = oat_method.GetCoreSpillMask();
     for (size_t i = 0; i < vmap_table.size(); i++) {
       uint16_t dex_reg = vmap_table[i];
-      size_t matches = 0;
-      size_t spill_shifts = 0;
-      uint32_t spill_mask = oat_method.GetCoreSpillMask();
-      bool processing_fp = false;
-      while (matches != (i + 1)) {
-        if (spill_mask == 0) {
-          CHECK(!processing_fp);
-          spill_mask = oat_method.GetFpSpillMask();
-          processing_fp = true;
-        }
-        matches += spill_mask & 1;  // Add 1 if the low bit is set
-        spill_mask >>= 1;
-        spill_shifts++;
-      }
-      size_t arm_reg = spill_shifts - 1;  // wind back one as we want the last match
+      uint32_t cpu_reg = vmap_table.ComputeRegister(spill_mask, i,
+                                                    processing_fp ? kFloatVReg : kIntVReg);
       os << (first ? "v" : ", v")  << dex_reg;
-      if (arm_reg < 16) {
-        os << "/r" << arm_reg;
+      if (!processing_fp) {
+        os << "/r" << cpu_reg;
       } else {
-        os << "/fr" << (arm_reg - 16);
+        os << "/fr" << cpu_reg;
       }
-      if (first) {
-        first = false;
+      first = false;
+      if (!processing_fp && dex_reg == 0xFFFF) {
+        processing_fp = true;
+        spill_mask = oat_method.GetFpSpillMask();
       }
     }
     os << "\n";
   }
 
   void DescribeVReg(std::ostream& os, const OatFile::OatMethod& oat_method,
-                    const DexFile::CodeItem* code_item, size_t reg) {
+                    const DexFile::CodeItem* code_item, size_t reg, VRegKind kind) {
     const uint16_t* raw_table = oat_method.GetVmapTable();
     if (raw_table != NULL) {
       const VmapTable vmap_table(raw_table);
       uint32_t vmap_offset;
-      if (vmap_table.IsInContext(reg, vmap_offset)) {
-        // Compute the register we need to load from the context
-        uint32_t spill_mask = oat_method.GetCoreSpillMask();
-        CHECK_LT(vmap_offset, static_cast<uint32_t>(__builtin_popcount(spill_mask)));
-        uint32_t matches = 0;
-        uint32_t spill_shifts = 0;
-        while (matches != (vmap_offset + 1)) {
-          DCHECK_NE(spill_mask, 0u);
-          matches += spill_mask & 1;  // Add 1 if the low bit is set
-          spill_mask >>= 1;
-          spill_shifts++;
-        }
-        spill_shifts--;  // wind back one as we want the last match
-        os << "r" << spill_shifts;
+      if (vmap_table.IsInContext(reg, vmap_offset, kind)) {
+        bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+        uint32_t spill_mask = is_float ? oat_method.GetFpSpillMask()
+                                       : oat_method.GetCoreSpillMask();
+        os << (is_float ? "fr" : "r") << vmap_table.ComputeRegister(spill_mask, vmap_offset, kind);
       } else {
         uint32_t offset = StackVisitor::GetVRegOffset(code_item, oat_method.GetCoreSpillMask(),
                                                       oat_method.GetFpSpillMask(),
@@ -437,7 +453,7 @@
     for (size_t entry = 0; entry < map.NumEntries(); entry++) {
       const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) +
                                  map.GetNativePcOffset(entry);
-      os << StringPrintf("\t\t\t%p", native_pc);
+      os << StringPrintf("%p", native_pc);
       size_t num_regs = map.RegWidth() * 8;
       const uint8_t* reg_bitmap = map.GetBitMap(entry);
       bool first = true;
@@ -445,12 +461,12 @@
         if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) {
           if (first) {
             os << "  v" << reg << " (";
-            DescribeVReg(os, oat_method, code_item, reg);
+            DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
             os << ")";
             first = false;
           } else {
             os << ", v" << reg << " (";
-            DescribeVReg(os, oat_method, code_item, reg);
+            DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
             os << ")";
           }
         }
@@ -469,26 +485,33 @@
     ++raw_table;
     uint32_t length = *raw_table;
     ++raw_table;
+    if (length == 0) {
+      return;
+    }
     uint32_t pc_to_dex_entries = *raw_table;
     ++raw_table;
-
-    os << "\t\tsuspend point mappings {";
+    if (pc_to_dex_entries != 0) {
+      os << "suspend point mappings {\n";
+    } else {
+      os << "catch entry mappings {\n";
+    }
+    Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+    std::ostream indent_os(&indent_filter);
     for (size_t i = 0; i < length; i += 2) {
       const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code) + raw_table[i];
       uint32_t dex_pc = raw_table[i + 1];
-      os << StringPrintf("%p -> 0x%04x", native_pc, dex_pc);
-      if (i + 2 == pc_to_dex_entries) {
+      indent_os << StringPrintf("%p -> 0x%04x\n", native_pc, dex_pc);
+      if (i + 2 == pc_to_dex_entries && pc_to_dex_entries != length) {
         // Separate the pc -> dex from dex -> pc sections
-        os << "}\n\t\tcatch entry mappings {";
-      } else if (i + 2 < length) {
-        os << ", ";
+        indent_os << std::flush;
+        os << "}\ncatch entry mappings {\n";
       }
     }
     os << "}\n";
   }
 
-  void DumpMappingAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method, size_t offset,
-                           bool suspend_point_mapping) {
+  uint32_t DumpMappingAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method, size_t offset,
+                               bool suspend_point_mapping) {
     const uint32_t* raw_table = oat_method.GetMappingTable();
     if (raw_table != NULL) {
       ++raw_table;
@@ -506,37 +529,39 @@
       }
       for (size_t i = start; i < end; i += 2) {
         if (offset == raw_table[i]) {
+          uint32_t dex_pc = raw_table[i + 1];
           if (suspend_point_mapping) {
-            os << "\t\t\tsuspend point dex PC: 0x";
+            os << "suspend point dex PC: 0x";
           } else {
-            os << "\t\t\tcatch entry dex PC: 0x";
+            os << "catch entry dex PC: 0x";
           }
-          os << std::hex << raw_table[i + 1] << std::dec << "\n";
-          return;
+          os << std::hex << dex_pc << std::dec << "\n";
+          return dex_pc;
         }
       }
     }
+    return DexFile::kDexNoIndex;
   }
 
-  void DumpGcMapAtOffset(std::ostream& os, const OatFile::OatMethod& oat_method,
-                         const DexFile::CodeItem* code_item, size_t offset) {
+  void DumpGcMapAtNativePcOffset(std::ostream& os, const OatFile::OatMethod& oat_method,
+                                 const DexFile::CodeItem* code_item, size_t native_pc_offset) {
     const uint8_t* gc_map_raw = oat_method.GetNativeGcMap();
     if (gc_map_raw != NULL) {
       NativePcOffsetToReferenceMap map(gc_map_raw);
-      if (map.HasEntry(offset)) {
+      if (map.HasEntry(native_pc_offset)) {
         size_t num_regs = map.RegWidth() * 8;
-        const uint8_t* reg_bitmap = map.FindBitMap(offset);
+        const uint8_t* reg_bitmap = map.FindBitMap(native_pc_offset);
         bool first = true;
         for (size_t reg = 0; reg < num_regs; reg++) {
           if (((reg_bitmap[reg / 8] >> (reg % 8)) & 0x01) != 0) {
             if (first) {
-              os << "\t\t\tGC map objects:  v" << reg << " (";
-              DescribeVReg(os, oat_method, code_item, reg);
+              os << "GC map objects:  v" << reg << " (";
+              DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
               os << ")";
               first = false;
             } else {
               os << ", v" << reg << " (";
-              DescribeVReg(os, oat_method, code_item, reg);
+              DescribeVReg(os, oat_method, code_item, reg, kReferenceVReg);
               os << ")";
             }
           }
@@ -548,32 +573,97 @@
     }
   }
 
+  void DumpVRegsAtDexPc(std::ostream& os,  const OatFile::OatMethod& oat_method,
+                        uint32_t dex_method_idx, const DexFile* dex_file,
+                        uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+                        uint32_t method_access_flags, uint32_t dex_pc) {
+    bool first = true;
+    ScopedObjectAccess soa(Thread::Current());
+    DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file);
+    ClassLoader* class_loader = NULL;
+    std::vector<int32_t> kinds =
+        verifier::MethodVerifier::DescribeVRegs(dex_method_idx, dex_file, dex_cache,
+                                                class_loader, class_def_idx, code_item, NULL,
+                                                method_access_flags, dex_pc);
+    for (size_t reg = 0; reg < code_item->registers_size_; reg++) {
+      VRegKind kind = static_cast<VRegKind>(kinds.at(reg * 2));
+      if (kind != kUndefined) {
+        if (first) {
+          os << "VRegs:  v";
+          first = false;
+        } else {
+          os << ", v";
+        }
+        os << reg << " (";
+        switch (kind) {
+          case kImpreciseConstant:
+            os << "Imprecise Constant: " << kinds.at((reg * 2) + 1) << ", ";
+            DescribeVReg(os, oat_method, code_item, reg, kind);
+            break;
+          case kConstant:
+            os << "Constant: " << kinds.at((reg * 2) + 1);
+            break;
+          default:
+            DescribeVReg(os, oat_method, code_item, reg, kind);
+            break;
+        }
+        os << ")";
+      }
+    }
+    if (!first) {
+      os << "\n";
+    }
+  }
+
+
   void DumpDexCode(std::ostream& os, const DexFile& dex_file, const DexFile::CodeItem* code_item) {
     if (code_item != NULL) {
       size_t i = 0;
       while (i < code_item->insns_size_in_code_units_) {
         const Instruction* instruction = Instruction::At(&code_item->insns_[i]);
-        os << StringPrintf("\t\t\t0x%04zx: %s\n", i, instruction->DumpString(&dex_file).c_str());
+        os << StringPrintf("0x%04zx: %s\n", i, instruction->DumpString(&dex_file).c_str());
         i += instruction->SizeInCodeUnits();
       }
     }
   }
 
-  void DumpCode(std::ostream& os, const OatFile::OatMethod& oat_method,
-                const DexFile::CodeItem* code_item) {
+  void DumpVerifier(std::ostream& os, uint32_t dex_method_idx, const DexFile* dex_file,
+                    uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+                    uint32_t method_access_flags) {
+    if ((method_access_flags & kAccNative) == 0) {
+      ScopedObjectAccess soa(Thread::Current());
+      DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache(*dex_file);
+      ClassLoader* class_loader = NULL;
+      verifier::MethodVerifier::VerifyMethodAndDump(os, dex_method_idx, dex_file, dex_cache,
+                                                    class_loader, class_def_idx, code_item, NULL,
+                                                    method_access_flags);
+    }
+  }
+
+  void DumpCode(std::ostream& os,  const OatFile::OatMethod& oat_method,
+                uint32_t dex_method_idx, const DexFile* dex_file,
+                uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+                uint32_t method_access_flags) {
     const void* code = oat_method.GetCode();
     size_t code_size = oat_method.GetCodeSize();
     if (code == NULL || code_size == 0) {
-      os << "\t\t\tNO CODE!\n";
+      os << "NO CODE!\n";
       return;
     }
     const uint8_t* native_pc = reinterpret_cast<const uint8_t*>(code);
     size_t offset = 0;
+    const bool kDumpVRegs = (Runtime::Current() != NULL);
     while (offset < code_size) {
       DumpMappingAtOffset(os, oat_method, offset, false);
       offset += disassembler_->Dump(os, native_pc + offset);
-      DumpMappingAtOffset(os, oat_method, offset, true);
-      DumpGcMapAtOffset(os, oat_method, code_item, offset);
+      uint32_t dex_pc = DumpMappingAtOffset(os, oat_method, offset, true);
+      if (dex_pc != DexFile::kDexNoIndex) {
+        DumpGcMapAtNativePcOffset(os, oat_method, code_item, offset);
+        if (kDumpVRegs) {
+          DumpVRegsAtDexPc(os, oat_method, dex_method_idx, dex_file, class_def_idx, code_item,
+                           method_access_flags, dex_pc);
+        }
+      }
     }
   }
 
@@ -592,72 +682,70 @@
 
 class ImageDumper {
  public:
-  explicit ImageDumper(std::ostream& os, const std::string& image_filename,
+  explicit ImageDumper(std::ostream* os, const std::string& image_filename,
                        const std::string& host_prefix, Space& image_space,
                        const ImageHeader& image_header)
       : os_(os), image_filename_(image_filename), host_prefix_(host_prefix),
         image_space_(image_space), image_header_(image_header) {}
 
   void Dump() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    os_ << "MAGIC:\n";
-    os_ << image_header_.GetMagic() << "\n\n";
+    std::ostream& os = *os_;
+    os << "MAGIC: " << image_header_.GetMagic() << "\n\n";
 
-    os_ << "IMAGE BEGIN:\n";
-    os_ << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
+    os << "IMAGE BEGIN: " << reinterpret_cast<void*>(image_header_.GetImageBegin()) << "\n\n";
 
-    os_ << "OAT CHECKSUM:\n";
-    os_ << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum());
+    os << "OAT CHECKSUM: " << StringPrintf("0x%08x\n\n", image_header_.GetOatChecksum());
 
-    os_ << "OAT BEGIN:\n";
-    os_ << reinterpret_cast<void*>(image_header_.GetOatBegin()) << "\n\n";
+    os << "OAT BEGIN:" << reinterpret_cast<void*>(image_header_.GetOatBegin()) << "\n\n";
 
-    os_ << "OAT END:\n";
-    os_ << reinterpret_cast<void*>(image_header_.GetOatEnd()) << "\n\n";
+    os << "OAT END:" << reinterpret_cast<void*>(image_header_.GetOatEnd()) << "\n\n";
 
-    os_ << "ROOTS:\n";
-    os_ << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
-    CHECK_EQ(arraysize(image_roots_descriptions_), size_t(ImageHeader::kImageRootsMax));
-    for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
-      ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
-      const char* image_root_description = image_roots_descriptions_[i];
-      Object* image_root_object = image_header_.GetImageRoot(image_root);
-      os_ << StringPrintf("%s: %p\n", image_root_description, image_root_object);
-      if (image_root_object->IsObjectArray()) {
-        // TODO: replace down_cast with AsObjectArray (g++ currently has a problem with this)
-        ObjectArray<Object>* image_root_object_array
-            = down_cast<ObjectArray<Object>*>(image_root_object);
-        //  = image_root_object->AsObjectArray<Object>();
-        for (int i = 0; i < image_root_object_array->GetLength(); i++) {
-          Object* value = image_root_object_array->Get(i);
-          if (value != NULL) {
-            os_ << "\t" << i << ": ";
-            std::string summary;
-            PrettyObjectValue(summary, value->GetClass(), value);
-            os_ << summary;
-          } else {
-            os_ << StringPrintf("\t%d: null\n", i);
+    {
+      os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
+      Indenter indent1_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+      std::ostream indent1_os(&indent1_filter);
+      CHECK_EQ(arraysize(image_roots_descriptions_), size_t(ImageHeader::kImageRootsMax));
+      for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
+        ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
+        const char* image_root_description = image_roots_descriptions_[i];
+        Object* image_root_object = image_header_.GetImageRoot(image_root);
+        indent1_os << StringPrintf("%s: %p\n", image_root_description, image_root_object);
+        if (image_root_object->IsObjectArray()) {
+          Indenter indent2_filter(indent1_os.rdbuf(), kIndentChar, kIndentBy1Count);
+          std::ostream indent2_os(&indent2_filter);
+          // TODO: replace down_cast with AsObjectArray (g++ currently has a problem with this)
+          ObjectArray<Object>* image_root_object_array
+              = down_cast<ObjectArray<Object>*>(image_root_object);
+          //  = image_root_object->AsObjectArray<Object>();
+          for (int i = 0; i < image_root_object_array->GetLength(); i++) {
+            Object* value = image_root_object_array->Get(i);
+            if (value != NULL) {
+              indent2_os << i << ": ";
+              PrettyObjectValue(indent2_os, value->GetClass(), value);
+            } else {
+              indent2_os << i << ": null\n";
+            }
           }
         }
       }
     }
-    os_ << "\n";
+    os << "\n";
 
-    os_ << "OAT LOCATION:\n" << std::flush;
     ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
     Object* oat_location_object = image_header_.GetImageRoot(ImageHeader::kOatLocation);
     std::string oat_location(oat_location_object->AsString()->ToModifiedUtf8());
-    os_ << oat_location;
+    os << "OAT LOCATION: " << oat_location;
     if (!host_prefix_.empty()) {
       oat_location = host_prefix_ + oat_location;
-      os_ << " (" << oat_location << ")";
+      os << " (" << oat_location << ")";
     }
-    os_ << "\n";
+    os << "\n";
     const OatFile* oat_file = class_linker->FindOatFileFromOatLocation(oat_location);
     if (oat_file == NULL) {
-      os_ << "NOT FOUND\n";
+      os << "NOT FOUND\n";
       return;
     }
-    os_ << "\n";
+    os << "\n";
 
     stats_.oat_file_bytes = oat_file->Size();
 
@@ -672,7 +760,7 @@
       stats_.oat_dex_file_sizes.push_back(entry);
     }
 
-    os_ << "OBJECTS:\n" << std::flush;
+    os << "OBJECTS:\n" << std::flush;
 
     // Loop through all the image spaces and dump their objects.
     Heap* heap = Runtime::Current()->GetHeap();
@@ -682,98 +770,105 @@
       WriterMutexLock mu(self, *Locks::heap_bitmap_lock_);
       heap->FlushAllocStack();
     }
-    ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
-    // TODO: C++0x auto
-    for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) {
-      Space* space = *it;
-      if (space->IsImageSpace()) {
-        ImageSpace* image_space = space->AsImageSpace();
-        image_space->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
-        os_ << "\n";
+    {
+      std::ostream* saved_os = os_;
+      Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+      std::ostream indent_os(&indent_filter);
+      os_ = &indent_os;
+      ReaderMutexLock mu(self, *Locks::heap_bitmap_lock_);
+      // TODO: C++0x auto
+      for (Spaces::const_iterator it = spaces.begin(); it != spaces.end(); ++it) {
+        Space* space = *it;
+        if (space->IsImageSpace()) {
+          ImageSpace* image_space = space->AsImageSpace();
+          image_space->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
+          indent_os << "\n";
+        }
       }
+      // Dump the large objects separately.
+      heap->GetLargeObjectsSpace()->GetLiveObjects()->Walk(ImageDumper::Callback, this);
+      indent_os << "\n";
+      os_ = saved_os;
     }
-    // Dump the large objects separately.
-    heap->GetLargeObjectsSpace()->GetLiveObjects()->Walk(ImageDumper::Callback, this);
-    os_ << "\n";
-
-    os_ << "STATS:\n" << std::flush;
+    os << "STATS:\n" << std::flush;
     UniquePtr<File> file(OS::OpenFile(image_filename_.c_str(), false));
     stats_.file_bytes = file->Length();
     size_t header_bytes = sizeof(ImageHeader);
     stats_.header_bytes = header_bytes;
     size_t alignment_bytes = RoundUp(header_bytes, kObjectAlignment) - header_bytes;
     stats_.alignment_bytes += alignment_bytes;
-    stats_.Dump(os_);
-    os_ << "\n";
+    stats_.Dump(os);
+    os << "\n";
 
-    os_ << std::flush;
+    os << std::flush;
 
-    oat_dumper_->Dump(os_);
+    oat_dumper_->Dump(os);
   }
 
  private:
-  static void PrettyObjectValue(std::string& summary, Class* type, Object* value)
+  static void PrettyObjectValue(std::ostream& os, Class* type, Object* value)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     CHECK(type != NULL);
     if (value == NULL) {
-      StringAppendF(&summary, "null   %s\n", PrettyDescriptor(type).c_str());
+      os << StringPrintf("null   %s\n", PrettyDescriptor(type).c_str());
     } else if (type->IsStringClass()) {
       String* string = value->AsString();
-      StringAppendF(&summary, "%p   String: \"%s\"\n", string, string->ToModifiedUtf8().c_str());
+      os << StringPrintf("%p   String: %s\n", string,
+                         PrintableString(string->ToModifiedUtf8()).c_str());
     } else if (type->IsClassClass()) {
       Class* klass = value->AsClass();
-      StringAppendF(&summary, "%p   Class: %s\n", klass, PrettyDescriptor(klass).c_str());
+      os << StringPrintf("%p   Class: %s\n", klass, PrettyDescriptor(klass).c_str());
     } else if (type->IsFieldClass()) {
       Field* field = value->AsField();
-      StringAppendF(&summary, "%p   Field: %s\n", field, PrettyField(field).c_str());
+      os << StringPrintf("%p   Field: %s\n", field, PrettyField(field).c_str());
     } else if (type->IsMethodClass()) {
       AbstractMethod* method = value->AsMethod();
-      StringAppendF(&summary, "%p   Method: %s\n", method, PrettyMethod(method).c_str());
+      os << StringPrintf("%p   Method: %s\n", method, PrettyMethod(method).c_str());
     } else {
-      StringAppendF(&summary, "%p   %s\n", value, PrettyDescriptor(type).c_str());
+      os << StringPrintf("%p   %s\n", value, PrettyDescriptor(type).c_str());
     }
   }
 
-  static void PrintField(std::string& summary, Field* field, Object* obj)
+  static void PrintField(std::ostream& os, Field* field, Object* obj)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     FieldHelper fh(field);
     const char* descriptor = fh.GetTypeDescriptor();
-    StringAppendF(&summary, "\t%s: ", fh.GetName());
+    os << StringPrintf("%s: ", fh.GetName());
     if (descriptor[0] != 'L' && descriptor[0] != '[') {
       Class* type = fh.GetType();
       if (type->IsPrimitiveLong()) {
-        StringAppendF(&summary, "%lld (0x%llx)\n", field->Get64(obj), field->Get64(obj));
+        os << StringPrintf("%lld (0x%llx)\n", field->Get64(obj), field->Get64(obj));
       } else if (type->IsPrimitiveDouble()) {
-        StringAppendF(&summary, "%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj));
+        os << StringPrintf("%f (%a)\n", field->GetDouble(obj), field->GetDouble(obj));
       } else if (type->IsPrimitiveFloat()) {
-        StringAppendF(&summary, "%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj));
+        os << StringPrintf("%f (%a)\n", field->GetFloat(obj), field->GetFloat(obj));
       } else {
         DCHECK(type->IsPrimitive());
-        StringAppendF(&summary, "%d (0x%x)\n", field->Get32(obj), field->Get32(obj));
+        os << StringPrintf("%d (0x%x)\n", field->Get32(obj), field->Get32(obj));
       }
     } else {
       // Get the value, don't compute the type unless it is non-null as we don't want
       // to cause class loading.
       Object* value = field->GetObj(obj);
       if (value == NULL) {
-        StringAppendF(&summary, "null   %s\n", PrettyDescriptor(descriptor).c_str());
+        os << StringPrintf("null   %s\n", PrettyDescriptor(descriptor).c_str());
       } else {
-        PrettyObjectValue(summary, fh.GetType(), value);
+        PrettyObjectValue(os, fh.GetType(), value);
       }
     }
   }
 
-  static void DumpFields(std::string& summary, Object* obj, Class* klass)
+  static void DumpFields(std::ostream& os, Object* obj, Class* klass)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     Class* super = klass->GetSuperClass();
     if (super != NULL) {
-      DumpFields(summary, obj, super);
+      DumpFields(os, obj, super);
     }
     ObjectArray<Field>* fields = klass->GetIFields();
     if (fields != NULL) {
       for (int32_t i = 0; i < fields->GetLength(); i++) {
         Field* field = fields->Get(i);
-        PrintField(summary, field, obj);
+        PrintField(os, field, obj);
       }
     }
   }
@@ -827,31 +922,30 @@
     state->stats_.object_bytes += object_bytes;
     state->stats_.alignment_bytes += alignment_bytes;
 
-    std::string summary;
+    std::ostream& os = *state->os_;
     Class* obj_class = obj->GetClass();
     if (obj_class->IsArrayClass()) {
-      StringAppendF(&summary, "%p: %s length:%d\n", obj, PrettyDescriptor(obj_class).c_str(),
-                    obj->AsArray()->GetLength());
+      os << StringPrintf("%p: %s length:%d\n", obj, PrettyDescriptor(obj_class).c_str(),
+                         obj->AsArray()->GetLength());
     } else if (obj->IsClass()) {
       Class* klass = obj->AsClass();
-      StringAppendF(&summary, "%p: java.lang.Class \"%s\" (", obj,
-                    PrettyDescriptor(klass).c_str());
-      std::ostringstream ss;
-      ss << klass->GetStatus() << ")\n";
-      summary += ss.str();
+      os << StringPrintf("%p: java.lang.Class \"%s\" (", obj, PrettyDescriptor(klass).c_str())
+         << klass->GetStatus() << ")\n";
     } else if (obj->IsField()) {
-      StringAppendF(&summary, "%p: java.lang.reflect.Field %s\n", obj,
-                    PrettyField(obj->AsField()).c_str());
+      os << StringPrintf("%p: java.lang.reflect.Field %s\n", obj,
+                         PrettyField(obj->AsField()).c_str());
     } else if (obj->IsMethod()) {
-      StringAppendF(&summary, "%p: java.lang.reflect.Method %s\n", obj,
-                    PrettyMethod(obj->AsMethod()).c_str());
+      os << StringPrintf("%p: java.lang.reflect.Method %s\n", obj,
+                         PrettyMethod(obj->AsMethod()).c_str());
     } else if (obj_class->IsStringClass()) {
-      StringAppendF(&summary, "%p: java.lang.String %s\n", obj,
-                    PrintableString(obj->AsString()->ToModifiedUtf8()).c_str());
+      os << StringPrintf("%p: java.lang.String %s\n", obj,
+                         PrintableString(obj->AsString()->ToModifiedUtf8()).c_str());
     } else {
-      StringAppendF(&summary, "%p: %s\n", obj, PrettyDescriptor(obj_class).c_str());
+      os << StringPrintf("%p: %s\n", obj, PrettyDescriptor(obj_class).c_str());
     }
-    DumpFields(summary, obj, obj_class);
+    Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+    std::ostream indent_os(&indent_filter);
+    DumpFields(indent_os, obj, obj_class);
     if (obj->IsObjectArray()) {
       ObjectArray<Object>* obj_array = obj->AsObjectArray<Object>();
       int32_t length = obj_array->GetLength();
@@ -866,21 +960,23 @@
           }
         }
         if (run == 0) {
-          StringAppendF(&summary, "\t%d: ", i);
+          indent_os << StringPrintf("%d: ", i);
         } else {
-          StringAppendF(&summary, "\t%d to %zd: ", i, i + run);
+          indent_os << StringPrintf("%d to %zd: ", i, i + run);
           i = i + run;
         }
         Class* value_class = value == NULL ? obj_class->GetComponentType() : value->GetClass();
-        PrettyObjectValue(summary, value_class, value);
+        PrettyObjectValue(indent_os, value_class, value);
       }
     } else if (obj->IsClass()) {
       ObjectArray<Field>* sfields = obj->AsClass()->GetSFields();
       if (sfields != NULL) {
-        summary += "\t\tSTATICS:\n";
+        indent_os << "STATICS:\n";
+        Indenter indent2_filter(indent_os.rdbuf(), kIndentChar, kIndentBy1Count);
+        std::ostream indent2_os(&indent2_filter);
         for (int32_t i = 0; i < sfields->GetLength(); i++) {
           Field* field = sfields->Get(i);
-          PrintField(summary, field, field->GetDeclaringClass());
+          PrintField(indent2_os, field, field->GetDeclaringClass());
         }
       }
     } else if (obj->IsMethod()) {
@@ -901,7 +997,7 @@
           state->stats_.native_to_managed_code_bytes += oat_code_size;
         }
         if (oat_code != method->GetCode()) {
-          StringAppendF(&summary, "\t\tOAT CODE: %p\n", oat_code);
+          indent_os << StringPrintf("OAT CODE: %p\n", oat_code);
         }
       } else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
           method->IsResolutionMethod()) {
@@ -958,9 +1054,9 @@
         }
         state->stats_.managed_code_bytes_ignoring_deduplication += oat_code_size;
 
-        StringAppendF(&summary, "\t\tOAT CODE: %p-%p\n", oat_code_begin, oat_code_end);
-        StringAppendF(&summary, "\t\tSIZE: Dex Instructions=%zd GC=%zd Mapping=%zd\n",
-                      dex_instruction_bytes, gc_map_bytes, pc_mapping_table_bytes);
+        indent_os << StringPrintf("OAT CODE: %p-%p\n", oat_code_begin, oat_code_end);
+        indent_os << StringPrintf("SIZE: Dex Instructions=%zd GC=%zd Mapping=%zd\n",
+                                  dex_instruction_bytes, gc_map_bytes, pc_mapping_table_bytes);
 
         size_t total_size = dex_instruction_bytes + gc_map_bytes + pc_mapping_table_bytes +
             vmap_table_bytes + invoke_stub_size + oat_code_size + object_bytes;
@@ -971,8 +1067,6 @@
       }
     }
     state->stats_.Update(ClassHelper(obj_class).GetDescriptor(), object_bytes);
-
-    state->os_ << summary << std::flush;
   }
 
   std::set<const void*> already_seen_;
@@ -1115,7 +1209,7 @@
                   os << "\nBig methods (size > " << i << " standard deviations the norm):\n";
                   first = false;
                 }
-                os << "\t" << PrettyMethod(method_outlier[j]) << " requires storage of "
+                os << PrettyMethod(method_outlier[j]) << " requires storage of "
                     << PrettySize(cur_size) << "\n";
                 method_outlier_size[j] = 0;  // don't consider this method again
                 dumped_values++;
@@ -1125,7 +1219,7 @@
         }
       }
       if (skipped_values > 0) {
-        os << "\t... skipped " << skipped_values
+        os << "... skipped " << skipped_values
            << " methods with size > 1 standard deviation from the norm\n";
       }
       os << std::flush;
@@ -1155,7 +1249,7 @@
                       << " standard deviations the norm):\n";
                   first = false;
                 }
-                os << "\t" << PrettyMethod(method_outlier[j]) << " expanded code by "
+                os << PrettyMethod(method_outlier[j]) << " expanded code by "
                    << cur_expansion << "\n";
                 method_outlier_expansion[j] = 0.0;  // don't consider this method again
                 dumped_values++;
@@ -1165,33 +1259,36 @@
         }
       }
       if (skipped_values > 0) {
-        os << "\t... skipped " << skipped_values
+        os << "... skipped " << skipped_values
            << " methods with expansion > 1 standard deviation from the norm\n";
       }
       os << "\n" << std::flush;
     }
 
     void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-      os << "\tart_file_bytes = " << PrettySize(file_bytes) << "\n\n"
-         << "\tart_file_bytes = header_bytes + object_bytes + alignment_bytes\n"
-         << StringPrintf("\theader_bytes    =  %8zd (%2.0f%% of art file bytes)\n"
-                         "\tobject_bytes    =  %8zd (%2.0f%% of art file bytes)\n"
-                         "\talignment_bytes =  %8zd (%2.0f%% of art file bytes)\n\n",
-                         header_bytes, PercentOfFileBytes(header_bytes),
-                         object_bytes, PercentOfFileBytes(object_bytes),
-                         alignment_bytes, PercentOfFileBytes(alignment_bytes))
-         << std::flush;
+      {
+        os << "art_file_bytes = " << PrettySize(file_bytes) << "\n\n"
+           << "art_file_bytes = header_bytes + object_bytes + alignment_bytes\n";
+        Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+        std::ostream indent_os(&indent_filter);
+        indent_os << StringPrintf("header_bytes    =  %8zd (%2.0f%% of art file bytes)\n"
+                                  "object_bytes    =  %8zd (%2.0f%% of art file bytes)\n"
+                                  "alignment_bytes =  %8zd (%2.0f%% of art file bytes)\n\n",
+                                  header_bytes, PercentOfFileBytes(header_bytes),
+                                  object_bytes, PercentOfFileBytes(object_bytes),
+                                  alignment_bytes, PercentOfFileBytes(alignment_bytes))
+            << std::flush;
+        CHECK_EQ(file_bytes, header_bytes + object_bytes + alignment_bytes);
+      }
 
-      CHECK_EQ(file_bytes, header_bytes + object_bytes + alignment_bytes);
-
-      os << "\tobject_bytes breakdown:\n";
+      os << "object_bytes breakdown:\n";
       size_t object_bytes_total = 0;
       typedef SizeAndCountTable::const_iterator It;  // TODO: C++0x auto
       for (It it = sizes_and_counts.begin(), end = sizes_and_counts.end(); it != end; ++it) {
         const std::string& descriptor(it->first);
         double average = static_cast<double>(it->second.bytes) / static_cast<double>(it->second.count);
         double percent = PercentOfObjectBytes(it->second.bytes);
-        os << StringPrintf("\t%32s %8zd bytes %6zd instances "
+        os << StringPrintf("%32s %8zd bytes %6zd instances "
                            "(%4.0f bytes/instance) %2.0f%% of object_bytes\n",
                            descriptor.c_str(), it->second.bytes, it->second.count,
                            average, percent);
@@ -1200,13 +1297,13 @@
       os << "\n" << std::flush;
       CHECK_EQ(object_bytes, object_bytes_total);
 
-      os << StringPrintf("\toat_file_bytes               = %8zd\n"
-                         "\tmanaged_code_bytes           = %8zd (%2.0f%% of oat file bytes)\n"
-                         "\tmanaged_to_native_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
-                         "\tnative_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n"
-                         "\tclass_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
-                         "\tlarge_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
-                         "\tlarge_method_code_bytes      = %8zd (%2.0f%% of oat file bytes)\n\n",
+      os << StringPrintf("oat_file_bytes               = %8zd\n"
+                         "managed_code_bytes           = %8zd (%2.0f%% of oat file bytes)\n"
+                         "managed_to_native_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
+                         "native_to_managed_code_bytes = %8zd (%2.0f%% of oat file bytes)\n\n"
+                         "class_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
+                         "large_initializer_code_bytes = %8zd (%2.0f%% of oat file bytes)\n"
+                         "large_method_code_bytes      = %8zd (%2.0f%% of oat file bytes)\n\n",
                          oat_file_bytes,
                          managed_code_bytes, PercentOfOatBytes(managed_code_bytes),
                          managed_to_native_code_bytes, PercentOfOatBytes(managed_to_native_code_bytes),
@@ -1214,27 +1311,24 @@
                          class_initializer_code_bytes, PercentOfOatBytes(class_initializer_code_bytes),
                          large_initializer_code_bytes, PercentOfOatBytes(large_initializer_code_bytes),
                          large_method_code_bytes, PercentOfOatBytes(large_method_code_bytes))
-         << std::flush;
-
-      os << "\tDexFile sizes:\n";
+            << "DexFile sizes:\n";
       typedef std::vector<std::pair<std::string, size_t> >::const_iterator It2;
       for (It2 it = oat_dex_file_sizes.begin(); it != oat_dex_file_sizes.end();
           ++it) {
-        os << StringPrintf("\t%s = %zd (%2.0f%% of oat file bytes)\n",
-                           it->first.c_str(), it->second,
-                           PercentOfOatBytes(it->second));
+        os << StringPrintf("%s = %zd (%2.0f%% of oat file bytes)\n",
+                           it->first.c_str(), it->second, PercentOfOatBytes(it->second));
       }
 
-      os << "\n" << StringPrintf("\tgc_map_bytes           = %7zd (%2.0f%% of oat file bytes)\n"
-                                 "\tpc_mapping_table_bytes = %7zd (%2.0f%% of oat file bytes)\n"
-                                 "\tvmap_table_bytes       = %7zd (%2.0f%% of oat file bytes)\n\n",
+      os << "\n" << StringPrintf("gc_map_bytes           = %7zd (%2.0f%% of oat file bytes)\n"
+                                 "pc_mapping_table_bytes = %7zd (%2.0f%% of oat file bytes)\n"
+                                 "vmap_table_bytes       = %7zd (%2.0f%% of oat file bytes)\n\n",
                                  gc_map_bytes, PercentOfOatBytes(gc_map_bytes),
                                  pc_mapping_table_bytes, PercentOfOatBytes(pc_mapping_table_bytes),
                                  vmap_table_bytes, PercentOfOatBytes(vmap_table_bytes))
          << std::flush;
 
-      os << StringPrintf("\tdex_instruction_bytes = %zd\n", dex_instruction_bytes)
-         << StringPrintf("\tmanaged_code_bytes expansion = %.2f (ignoring deduplication %.2f)\n\n",
+      os << StringPrintf("dex_instruction_bytes = %zd\n", dex_instruction_bytes)
+         << StringPrintf("managed_code_bytes expansion = %.2f (ignoring deduplication %.2f)\n\n",
                          static_cast<double>(managed_code_bytes) / static_cast<double>(dex_instruction_bytes),
                          static_cast<double>(managed_code_bytes_ignoring_deduplication) /
                              static_cast<double>(dex_instruction_bytes))
@@ -1254,7 +1348,7 @@
     kLargeMethodDexBytes = 16000
   };
   UniquePtr<OatDumper> oat_dumper_;
-  std::ostream& os_;
+  std::ostream* os_;
   const std::string image_filename_;
   const std::string host_prefix_;
   Space& image_space_;
@@ -1376,7 +1470,7 @@
     fprintf(stderr, "Invalid image header %s\n", image_filename);
     return EXIT_FAILURE;
   }
-  ImageDumper image_dumper(*os, image_filename, *host_prefix.get(), *image_space, image_header);
+  ImageDumper image_dumper(os, image_filename, *host_prefix.get(), *image_space, image_header);
   image_dumper.Dump();
   return EXIT_SUCCESS;
 }
diff --git a/src/stack.cc b/src/stack.cc
index 8a741c6..f712652 100644
--- a/src/stack.cc
+++ b/src/stack.cc
@@ -64,49 +64,38 @@
   return GetMethod()->NativePcOffset(cur_quick_frame_pc_);
 }
 
-
-uint32_t StackVisitor::GetVReg(AbstractMethod* m, int vreg) const {
-  DCHECK(m == GetMethod());
+uint32_t StackVisitor::GetVReg(AbstractMethod* m, uint16_t vreg, VRegKind kind) const {
   if (cur_quick_frame_ != NULL) {
     DCHECK(context_ != NULL); // You can't reliably read registers without a context.
-    uint32_t core_spills = m->GetCoreSpillMask();
+    DCHECK(m == GetMethod());
     const VmapTable vmap_table(m->GetVmapTableRaw());
     uint32_t vmap_offset;
     // TODO: IsInContext stops before spotting floating point registers.
-    if (vmap_table.IsInContext(vreg, vmap_offset)) {
-      // Compute the register we need to load from the context.
-      uint32_t spill_mask = core_spills;
-      CHECK_LT(vmap_offset, static_cast<uint32_t>(__builtin_popcount(spill_mask)));
-      uint32_t matches = 0;
-      uint32_t spill_shifts = 0;
-      while (matches != (vmap_offset + 1)) {
-        DCHECK_NE(spill_mask, 0u);
-        matches += spill_mask & 1;  // Add 1 if the low bit is set.
-        spill_mask >>= 1;
-        spill_shifts++;
-      }
-      spill_shifts--;  // Wind back one as we want the last match.
-      return GetGPR(spill_shifts);
+    if (vmap_table.IsInContext(vreg, vmap_offset, kind)) {
+      bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+      uint32_t spill_mask = is_float ? m->GetFpSpillMask()
+                                     : m->GetCoreSpillMask();
+      return GetGPR(vmap_table.ComputeRegister(spill_mask, vmap_offset, kind));
     } else {
       const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
       DCHECK(code_item != NULL) << PrettyMethod(m); // Can't be NULL or how would we compile its instructions?
-      uint32_t fp_spills = m->GetFpSpillMask();
       size_t frame_size = m->GetFrameSizeInBytes();
-      return GetVReg(cur_quick_frame_, code_item, core_spills, fp_spills, frame_size, vreg);
+      return GetVReg(cur_quick_frame_, code_item, m->GetCoreSpillMask(), m->GetFpSpillMask(),
+                     frame_size, vreg);
     }
   } else {
     return cur_shadow_frame_->GetVReg(vreg);
   }
 }
 
-void StackVisitor::SetVReg(AbstractMethod* m, int vreg, uint32_t new_value) {
+void StackVisitor::SetVReg(AbstractMethod* m, uint16_t vreg, uint32_t new_value, VRegKind kind) {
   if (cur_quick_frame_ != NULL) {
     DCHECK(context_ != NULL); // You can't reliably write registers without a context.
     DCHECK(m == GetMethod());
     const VmapTable vmap_table(m->GetVmapTableRaw());
     uint32_t vmap_offset;
     // TODO: IsInContext stops before spotting floating point registers.
-    if (vmap_table.IsInContext(vreg, vmap_offset)) {
+    if (vmap_table.IsInContext(vreg, vmap_offset, kind)) {
       UNIMPLEMENTED(FATAL);
     }
     const DexFile::CodeItem* code_item = MethodHelper(m).GetCodeItem();
@@ -118,7 +107,7 @@
     byte* vreg_addr = reinterpret_cast<byte*>(GetCurrentQuickFrame()) + offset;
     *reinterpret_cast<uint32_t*>(vreg_addr) = new_value;
   } else {
-    LOG(FATAL) << "Unimplemented - shadow frame SetVReg";
+    return cur_shadow_frame_->SetVReg(vreg, new_value);
   }
 }
 
@@ -129,7 +118,7 @@
 
 uintptr_t StackVisitor::GetReturnPc() const {
   AbstractMethod** sp = GetCurrentQuickFrame();
-  CHECK(sp != NULL);
+  DCHECK(sp != NULL);
   byte* pc_addr = reinterpret_cast<byte*>(sp) + GetMethod()->GetReturnPcOffsetInBytes();
   return *reinterpret_cast<uintptr_t*>(pc_addr);
 }
diff --git a/src/stack.h b/src/stack.h
index fd2fd15..5b5609c 100644
--- a/src/stack.h
+++ b/src/stack.h
@@ -29,12 +29,27 @@
 namespace art {
 
 class AbstractMethod;
+class Context;
 class Object;
 class ShadowFrame;
 class StackIndirectReferenceTable;
 class ScopedObjectAccess;
 class Thread;
 
+// The kind of vreg being accessed in calls to Set/GetVReg.
+enum VRegKind {
+  kReferenceVReg,
+  kIntVReg,
+  kFloatVReg,
+  kLongLoVReg,
+  kLongHiVReg,
+  kDoubleLoVReg,
+  kDoubleHiVReg,
+  kConstant,
+  kImpreciseConstant,
+  kUndefined,
+};
+
 class ShadowFrame {
  public:
   static ShadowFrame* Create(uint16_t num_refs, uint16_t num_vregs, ShadowFrame* link,
@@ -366,15 +381,17 @@
     return num_frames_;
   }
 
-  uint32_t GetVReg(AbstractMethod* m, int vreg) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  uint32_t GetVReg(AbstractMethod* m, uint16_t vreg, VRegKind kind) const
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  void SetVReg(AbstractMethod* m, int vreg, uint32_t new_value)
+  void SetVReg(AbstractMethod* m, uint16_t vreg, uint32_t new_value, VRegKind kind)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   uintptr_t GetGPR(uint32_t reg) const;
 
   uint32_t GetVReg(AbstractMethod** cur_quick_frame, const DexFile::CodeItem* code_item,
-                   uint32_t core_spills, uint32_t fp_spills, size_t frame_size, int vreg) const {
+                   uint32_t core_spills, uint32_t fp_spills, size_t frame_size,
+                   uint16_t vreg) const {
     int offset = GetVRegOffset(code_item, core_spills, fp_spills, frame_size, vreg);
     DCHECK_EQ(cur_quick_frame, GetCurrentQuickFrame());
     byte* vreg_addr = reinterpret_cast<byte*>(cur_quick_frame) + offset;
@@ -482,6 +499,77 @@
   Context* const context_;
 };
 
+class VmapTable {
+ public:
+  explicit VmapTable(const uint16_t* table) : table_(table) {
+  }
+
+  uint16_t operator[](size_t i) const {
+    return table_[i + 1];
+  }
+
+  size_t size() const {
+    return table_[0];
+  }
+
+  // Is the dex register 'vreg' in the context or on the stack? Should not be called when the
+  // 'kind' is unknown or constant.
+  bool IsInContext(size_t vreg, uint32_t& vmap_offset, VRegKind kind) const {
+    DCHECK(kind == kReferenceVReg || kind == kIntVReg || kind == kFloatVReg ||
+           kind == kLongLoVReg || kind == kLongHiVReg || kind == kDoubleLoVReg ||
+           kind == kDoubleHiVReg || kind == kImpreciseConstant);
+    vmap_offset = 0xEBAD0FF5;
+    // TODO: take advantage of the registers being ordered
+    // TODO: we treat kImpreciseConstant as an integer below, need to ensure that such values
+    //       are never promoted to floating point registers.
+    bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+    bool in_floats = false;
+    for (size_t i = 0; i < size(); ++i) {
+      // Stop if we find what we are are looking for.
+      if ((table_[i + 1] == vreg) && (in_floats == is_float)) {
+        vmap_offset = i;
+        return true;
+      }
+      // 0xffff is the marker for LR (return PC on x86), following it are spilled float registers.
+      if (table_[i + 1] == 0xffff) {
+        in_floats = true;
+      }
+    }
+    return false;
+  }
+
+  // Compute the register number that corresponds to the entry in the vmap (vmap_offset, computed
+  // by IsInContext above). If the kind is floating point then the result will be a floating point
+  // register number, otherwise it will be an integer register number.
+  uint32_t ComputeRegister(uint32_t spill_mask, uint32_t vmap_offset, VRegKind kind) const {
+    // Compute the register we need to load from the context.
+    DCHECK(kind == kReferenceVReg || kind == kIntVReg || kind == kFloatVReg ||
+           kind == kLongLoVReg || kind == kLongHiVReg || kind == kDoubleLoVReg ||
+           kind == kDoubleHiVReg || kind == kImpreciseConstant);
+    // TODO: we treat kImpreciseConstant as an integer below, need to ensure that such values
+    //       are never promoted to floating point registers.
+    bool is_float = (kind == kFloatVReg) || (kind == kDoubleLoVReg) || (kind == kDoubleHiVReg);
+    uint32_t matches = 0;
+    if (is_float) {
+      while (table_[matches] != 0xffff) {
+        matches++;
+      }
+    }
+    CHECK_LT(vmap_offset - matches, static_cast<uint32_t>(__builtin_popcount(spill_mask)));
+    uint32_t spill_shifts = 0;
+    while (matches != (vmap_offset + 1)) {
+      DCHECK_NE(spill_mask, 0u);
+      matches += spill_mask & 1;  // Add 1 if the low bit is set
+      spill_mask >>= 1;
+      spill_shifts++;
+    }
+    spill_shifts--;  // wind back one as we want the last match
+    return spill_shifts;
+  }
+ private:
+  const uint16_t* table_;
+};
+
 }  // namespace art
 
 #endif  // ART_SRC_STACK_H_
diff --git a/src/thread.cc b/src/thread.cc
index 0d06e42..943fdcb 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -1883,20 +1883,10 @@
             if (TestBitmap(reg, reg_bitmap)) {
               uint32_t vmap_offset;
               Object* ref;
-              if (vmap_table.IsInContext(reg, vmap_offset)) {
-                // Compute the register we need to load from the context
-                uint32_t spill_mask = core_spills;
-                CHECK_LT(vmap_offset, static_cast<uint32_t>(__builtin_popcount(spill_mask)));
-                uint32_t matches = 0;
-                uint32_t spill_shifts = 0;
-                while (matches != (vmap_offset + 1)) {
-                  DCHECK_NE(spill_mask, 0u);
-                  matches += spill_mask & 1;  // Add 1 if the low bit is set
-                  spill_mask >>= 1;
-                  spill_shifts++;
-                }
-                spill_shifts--;  // wind back one as we want the last match
-                ref = reinterpret_cast<Object*>(GetGPR(spill_shifts));
+              if (vmap_table.IsInContext(reg, vmap_offset, kReferenceVReg)) {
+                uintptr_t val = GetGPR(vmap_table.ComputeRegister(core_spills, vmap_offset,
+                                                                  kReferenceVReg));
+                ref = reinterpret_cast<Object*>(val);
               } else {
                 ref = reinterpret_cast<Object*>(GetVReg(cur_quick_frame, code_item, core_spills,
                                                         fp_spills, frame_size, reg));
diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 2149490..d6cd665dd 100644
--- a/src/verifier/method_verifier.cc
+++ b/src/verifier/method_verifier.cc
@@ -25,6 +25,7 @@
 #include "dex_instruction.h"
 #include "dex_instruction_visitor.h"
 #include "verifier/dex_gc_map.h"
+#include "indenter.h"
 #include "intern_table.h"
 #include "leb128.h"
 #include "logging.h"
@@ -324,22 +325,37 @@
   return result;
 }
 
-void MethodVerifier::VerifyMethodAndDump(AbstractMethod* method) {
-  CHECK(method != NULL);
-  MethodHelper mh(method);
-  MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
-                          mh.GetClassDefIndex(), mh.GetCodeItem(), method->GetDexMethodIndex(),
-                          method, method->GetAccessFlags());
+void MethodVerifier::VerifyMethodAndDump(std::ostream& os, uint32_t dex_method_idx,
+                                         const DexFile* dex_file, DexCache* dex_cache,
+                                         ClassLoader* class_loader, uint32_t class_def_idx,
+                                         const DexFile::CodeItem* code_item, AbstractMethod* method,
+                                         uint32_t method_access_flags) {
+  MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item,
+                          dex_method_idx, method, method_access_flags);
   verifier.Verify();
-  verifier.DumpFailures(LOG(INFO) << "Dump of method " << PrettyMethod(method) << "\n")
-      << verifier.info_messages_.str() << MutatorLockedDumpable<MethodVerifier>(verifier);
+  verifier.DumpFailures(os);
+  os << verifier.info_messages_.str();
+  verifier.Dump(os);
+}
+
+std::vector<int32_t> MethodVerifier::DescribeVRegs(uint32_t dex_method_idx,
+                                                   const DexFile* dex_file, DexCache* dex_cache,
+                                                   ClassLoader* class_loader,
+                                                   uint32_t class_def_idx,
+                                                   const DexFile::CodeItem* code_item,
+                                                   AbstractMethod* method,
+                                                   uint32_t method_access_flags, uint32_t dex_pc) {
+  MethodVerifier verifier(dex_file, dex_cache, class_loader, class_def_idx, code_item,
+                          dex_method_idx, method, method_access_flags);
+  verifier.Verify();
+  return verifier.DescribeVRegs(dex_pc);
 }
 
 MethodVerifier::MethodVerifier(const DexFile* dex_file, DexCache* dex_cache,
     ClassLoader* class_loader, uint32_t class_def_idx, const DexFile::CodeItem* code_item,
-    uint32_t method_idx, AbstractMethod* method, uint32_t method_access_flags)
+    uint32_t dex_method_idx, AbstractMethod* method, uint32_t method_access_flags)
     : work_insn_idx_(-1),
-      method_idx_(method_idx),
+      dex_method_idx_(dex_method_idx),
       foo_method_(method),
       method_access_flags_(method_access_flags),
       dex_file_(dex_file),
@@ -355,7 +371,8 @@
       monitor_enter_count_(0) {
 }
 
-void MethodVerifier::FindLocksAtDexPc(AbstractMethod* m, uint32_t dex_pc, std::vector<uint32_t>& monitor_enter_dex_pcs) {
+void MethodVerifier::FindLocksAtDexPc(AbstractMethod* m, uint32_t dex_pc,
+                                      std::vector<uint32_t>& monitor_enter_dex_pcs) {
   MethodHelper mh(m);
   MethodVerifier verifier(&mh.GetDexFile(), mh.GetDexCache(), mh.GetClassLoader(),
                           mh.GetClassDefIndex(), mh.GetCodeItem(), m->GetDexMethodIndex(),
@@ -446,7 +463,7 @@
     }
   }
   failures_.push_back(error);
-  std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(method_idx_, *dex_file_).c_str(),
+  std::string location(StringPrintf("%s: [0x%X]", PrettyMethod(dex_method_idx_, *dex_file_).c_str(),
                                     work_insn_idx_));
   std::ostringstream* failure_message = new std::ostringstream(location);
   failure_messages_.push_back(failure_message);
@@ -1000,7 +1017,7 @@
   if (!SetTypesFromSignature()) {
     DCHECK_NE(failures_.size(), 0U);
     std::string prepend("Bad signature in ");
-    prepend += PrettyMethod(method_idx_, *dex_file_);
+    prepend += PrettyMethod(dex_method_idx_, *dex_file_);
     PrependToLastFailMessage(prepend);
     return false;
   }
@@ -1010,7 +1027,7 @@
     return false;
   }
 
-  Compiler::MethodReference ref(dex_file_, method_idx_);
+  Compiler::MethodReference ref(dex_file_, dex_method_idx_);
 
 #if !defined(ART_USE_LLVM_COMPILER)
 
@@ -1054,17 +1071,28 @@
     os << "Native method\n";
     return;
   }
-  reg_types_.Dump(os);
+  {
+    os << "Register Types:\n";
+    Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+    std::ostream indent_os(&indent_filter);
+    reg_types_.Dump(indent_os);
+  }
   os << "Dumping instructions and register lines:\n";
+  Indenter indent_filter(os.rdbuf(), kIndentChar, kIndentBy1Count);
+  std::ostream indent_os(&indent_filter);
   const Instruction* inst = Instruction::At(code_item_->insns_);
   for (size_t dex_pc = 0; dex_pc < code_item_->insns_size_in_code_units_;
       dex_pc += insn_flags_[dex_pc].GetLengthInCodeUnits()) {
-    os << StringPrintf("0x%04zx", dex_pc) << ": " << insn_flags_[dex_pc].Dump()
-       << " " << inst->DumpHex(5) << " " << inst->DumpString(dex_file_) << "\n";
     RegisterLine* reg_line = reg_table_.GetLine(dex_pc);
     if (reg_line != NULL) {
-      os << reg_line->Dump() << "\n";
+      indent_os << reg_line->Dump() << "\n";
     }
+    indent_os << StringPrintf("0x%04zx", dex_pc) << ": " << insn_flags_[dex_pc].Dump() << " ";
+    const bool kDumpHexOfInstruction = false;
+    if (kDumpHexOfInstruction) {
+      indent_os << inst->DumpHex(5) << " ";
+    }
+    indent_os << inst->DumpString(dex_file_) << "\n";
     inst = inst->Next();
   }
 }
@@ -1108,7 +1136,7 @@
   }
 
   const DexFile::ProtoId& proto_id =
-      dex_file_->GetMethodPrototype(dex_file_->GetMethodId(method_idx_));
+      dex_file_->GetMethodPrototype(dex_file_->GetMethodId(dex_method_idx_));
   DexFileParameterIterator iterator(*dex_file_, proto_id);
 
   for (; iterator.HasNext(); iterator.Next()) {
@@ -1153,8 +1181,9 @@
         break;
       case 'J':
       case 'D': {
-        const RegType& low_half = descriptor[0] == 'J' ? reg_types_.Long() : reg_types_.Double();
-        reg_line->SetRegisterType(arg_start + cur_arg, low_half);  // implicitly sets high-register
+        const RegType& lo_half = descriptor[0] == 'J' ? reg_types_.LongLo() : reg_types_.DoubleLo();
+        const RegType& hi_half = descriptor[0] == 'J' ? reg_types_.LongHi() : reg_types_.DoubleHi();
+        reg_line->SetRegisterTypeWide(arg_start + cur_arg, lo_half, hi_half);
         cur_arg++;
         break;
       }
@@ -1250,7 +1279,7 @@
         if (work_line_->CompareLine(register_line) != 0) {
           Dump(std::cout);
           std::cout << info_messages_.str();
-          LOG(FATAL) << "work_line diverged in " << PrettyMethod(method_idx_, *dex_file_)
+          LOG(FATAL) << "work_line diverged in " << PrettyMethod(dex_method_idx_, *dex_file_)
                      << "@" << reinterpret_cast<void*>(work_insn_idx_) << "\n"
                      << " work_line=" << *work_line_ << "\n"
                      << "  expected=" << *register_line;
@@ -1259,7 +1288,7 @@
 #endif
     }
     if (!CodeFlowVerifyInstruction(&start_guess)) {
-      std::string prepend(PrettyMethod(method_idx_, *dex_file_));
+      std::string prepend(PrettyMethod(dex_method_idx_, *dex_file_));
       prepend += " failed to verify: ";
       PrependToLastFailMessage(prepend);
       return false;
@@ -1500,32 +1529,51 @@
       }
       break;
 
-    case Instruction::CONST_4:
       /* could be boolean, int, float, or a null reference */
+    case Instruction::CONST_4:
       work_line_->SetRegisterType(dec_insn.vA,
-                                  reg_types_.FromCat1Const((dec_insn.vB << 28) >> 28));
+                                  reg_types_.FromCat1Const(static_cast<int32_t>(dec_insn.vB << 28) >> 28, true));
       break;
     case Instruction::CONST_16:
-      /* could be boolean, int, float, or a null reference */
       work_line_->SetRegisterType(dec_insn.vA,
-                                  reg_types_.FromCat1Const(static_cast<int16_t>(dec_insn.vB)));
+                                  reg_types_.FromCat1Const(static_cast<int16_t>(dec_insn.vB), true));
       break;
     case Instruction::CONST:
-      /* could be boolean, int, float, or a null reference */
-      work_line_->SetRegisterType(dec_insn.vA, reg_types_.FromCat1Const(dec_insn.vB));
+      work_line_->SetRegisterType(dec_insn.vA, reg_types_.FromCat1Const(dec_insn.vB, true));
       break;
     case Instruction::CONST_HIGH16:
-      /* could be boolean, int, float, or a null reference */
       work_line_->SetRegisterType(dec_insn.vA,
-                                  reg_types_.FromCat1Const(dec_insn.vB << 16));
+                                  reg_types_.FromCat1Const(dec_insn.vB << 16, true));
       break;
-    case Instruction::CONST_WIDE_16:
-    case Instruction::CONST_WIDE_32:
-    case Instruction::CONST_WIDE:
-    case Instruction::CONST_WIDE_HIGH16:
       /* could be long or double; resolved upon use */
-      work_line_->SetRegisterType(dec_insn.vA, reg_types_.ConstLo());
+    case Instruction::CONST_WIDE_16: {
+      int64_t val = static_cast<int16_t>(dec_insn.vB);
+      const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+      const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+      work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi);
       break;
+    }
+    case Instruction::CONST_WIDE_32: {
+      int64_t val = static_cast<int32_t>(dec_insn.vB);
+      const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+      const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+      work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi);
+      break;
+    }
+    case Instruction::CONST_WIDE: {
+      int64_t val = dec_insn.vB_wide;
+      const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+      const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+      work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi);
+      break;
+    }
+    case Instruction::CONST_WIDE_HIGH16: {
+      int64_t val = static_cast<uint64_t>(dec_insn.vB) << 48;
+      const RegType& lo = reg_types_.FromCat2ConstLo(static_cast<int32_t>(val), true);
+      const RegType& hi = reg_types_.FromCat2ConstHi(static_cast<int32_t>(val >> 32), true);
+      work_line_->SetRegisterTypeWide(dec_insn.vA, lo, hi);
+      break;
+    }
     case Instruction::CONST_STRING:
     case Instruction::CONST_STRING_JUMBO:
       work_line_->SetRegisterType(dec_insn.vA, reg_types_.JavaLangString());
@@ -1658,19 +1706,23 @@
       break;
     case Instruction::CMPL_DOUBLE:
     case Instruction::CMPG_DOUBLE:
-      if (!work_line_->VerifyRegisterType(dec_insn.vB, reg_types_.Double())) {
+      if (!work_line_->VerifyRegisterTypeWide(dec_insn.vB, reg_types_.DoubleLo(),
+                                              reg_types_.DoubleHi())) {
         break;
       }
-      if (!work_line_->VerifyRegisterType(dec_insn.vC, reg_types_.Double())) {
+      if (!work_line_->VerifyRegisterTypeWide(dec_insn.vC, reg_types_.DoubleLo(),
+                                              reg_types_.DoubleHi())) {
         break;
       }
       work_line_->SetRegisterType(dec_insn.vA, reg_types_.Integer());
       break;
     case Instruction::CMP_LONG:
-      if (!work_line_->VerifyRegisterType(dec_insn.vB, reg_types_.Long())) {
+      if (!work_line_->VerifyRegisterTypeWide(dec_insn.vB, reg_types_.LongLo(),
+                                              reg_types_.LongHi())) {
         break;
       }
-      if (!work_line_->VerifyRegisterType(dec_insn.vC, reg_types_.Long())) {
+      if (!work_line_->VerifyRegisterTypeWide(dec_insn.vC, reg_types_.LongLo(),
+                                              reg_types_.LongHi())) {
         break;
       }
       work_line_->SetRegisterType(dec_insn.vA, reg_types_.Integer());
@@ -1792,7 +1844,7 @@
       VerifyAGet(dec_insn, reg_types_.Integer(), true);
       break;
     case Instruction::AGET_WIDE:
-      VerifyAGet(dec_insn, reg_types_.Long(), true);
+      VerifyAGet(dec_insn, reg_types_.LongLo(), true);
       break;
     case Instruction::AGET_OBJECT:
       VerifyAGet(dec_insn, reg_types_.JavaLangObject(false), false);
@@ -1814,7 +1866,7 @@
       VerifyAPut(dec_insn, reg_types_.Integer(), true);
       break;
     case Instruction::APUT_WIDE:
-      VerifyAPut(dec_insn, reg_types_.Long(), true);
+      VerifyAPut(dec_insn, reg_types_.LongLo(), true);
       break;
     case Instruction::APUT_OBJECT:
       VerifyAPut(dec_insn, reg_types_.JavaLangObject(false), false);
@@ -1836,7 +1888,7 @@
       VerifyISGet(dec_insn, reg_types_.Integer(), true, false);
       break;
     case Instruction::IGET_WIDE:
-      VerifyISGet(dec_insn, reg_types_.Long(), true, false);
+      VerifyISGet(dec_insn, reg_types_.LongLo(), true, false);
       break;
     case Instruction::IGET_OBJECT:
       VerifyISGet(dec_insn, reg_types_.JavaLangObject(false), false, false);
@@ -1858,7 +1910,7 @@
       VerifyISPut(dec_insn, reg_types_.Integer(), true, false);
       break;
     case Instruction::IPUT_WIDE:
-      VerifyISPut(dec_insn, reg_types_.Long(), true, false);
+      VerifyISPut(dec_insn, reg_types_.LongLo(), true, false);
       break;
     case Instruction::IPUT_OBJECT:
       VerifyISPut(dec_insn, reg_types_.JavaLangObject(false), false, false);
@@ -1880,7 +1932,7 @@
       VerifyISGet(dec_insn, reg_types_.Integer(), true, true);
       break;
     case Instruction::SGET_WIDE:
-      VerifyISGet(dec_insn, reg_types_.Long(), true, true);
+      VerifyISGet(dec_insn, reg_types_.LongLo(), true, true);
       break;
     case Instruction::SGET_OBJECT:
       VerifyISGet(dec_insn, reg_types_.JavaLangObject(false), false, true);
@@ -1902,7 +1954,7 @@
       VerifyISPut(dec_insn, reg_types_.Integer(), true, true);
       break;
     case Instruction::SPUT_WIDE:
-      VerifyISPut(dec_insn, reg_types_.Long(), true, true);
+      VerifyISPut(dec_insn, reg_types_.LongLo(), true, true);
       break;
     case Instruction::SPUT_OBJECT:
       VerifyISPut(dec_insn, reg_types_.JavaLangObject(false), false, true);
@@ -1927,7 +1979,11 @@
         descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
       }
       const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
-      work_line_->SetResultRegisterType(return_type);
+      if (!return_type.IsLowHalf()) {
+        work_line_->SetResultRegisterType(return_type);
+      } else {
+        work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
+      }
       just_set_result = true;
       break;
     }
@@ -1989,7 +2045,11 @@
       }
       const RegType& return_type = reg_types_.FromDescriptor(class_loader_, return_type_descriptor,
                                                              false);
-      work_line_->SetResultRegisterType(return_type);
+      if (!return_type.IsLowHalf()) {
+        work_line_->SetResultRegisterType(return_type);
+      } else {
+        work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
+      }
       just_set_result = true;
       break;
     }
@@ -2007,7 +2067,11 @@
           descriptor = MethodHelper(called_method).GetReturnTypeDescriptor();
         }
         const RegType& return_type =  reg_types_.FromDescriptor(class_loader_, descriptor, false);
-        work_line_->SetResultRegisterType(return_type);
+        if (!return_type.IsLowHalf()) {
+          work_line_->SetResultRegisterType(return_type);
+        } else {
+          work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
+        }
         just_set_result = true;
       }
       break;
@@ -2057,8 +2121,11 @@
         descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor();
       }
       const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false);
-      work_line_->SetResultRegisterType(return_type);
-      work_line_->SetResultRegisterType(return_type);
+      if (!return_type.IsLowHalf()) {
+        work_line_->SetResultRegisterType(return_type);
+      } else {
+        work_line_->SetResultRegisterTypeWide(return_type, return_type.HighHalf(&reg_types_));
+      }
       just_set_result = true;
       break;
     }
@@ -2068,49 +2135,61 @@
       break;
     case Instruction::NEG_LONG:
     case Instruction::NOT_LONG:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Long(), reg_types_.Long());
+      work_line_->CheckUnaryOpWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+                                   reg_types_.LongLo(), reg_types_.LongHi());
       break;
     case Instruction::NEG_FLOAT:
       work_line_->CheckUnaryOp(dec_insn, reg_types_.Float(), reg_types_.Float());
       break;
     case Instruction::NEG_DOUBLE:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Double(), reg_types_.Double());
+      work_line_->CheckUnaryOpWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+                                   reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
     case Instruction::INT_TO_LONG:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Long(), reg_types_.Integer());
+      work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+                                     reg_types_.Integer());
       break;
     case Instruction::INT_TO_FLOAT:
       work_line_->CheckUnaryOp(dec_insn, reg_types_.Float(), reg_types_.Integer());
       break;
     case Instruction::INT_TO_DOUBLE:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Double(), reg_types_.Integer());
+      work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+                                     reg_types_.Integer());
       break;
     case Instruction::LONG_TO_INT:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Integer(), reg_types_.Long());
+      work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Integer(),
+                                       reg_types_.LongLo(), reg_types_.LongHi());
       break;
     case Instruction::LONG_TO_FLOAT:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Float(), reg_types_.Long());
+      work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Float(),
+                                       reg_types_.LongLo(), reg_types_.LongHi());
       break;
     case Instruction::LONG_TO_DOUBLE:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Double(), reg_types_.Long());
+      work_line_->CheckUnaryOpWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+                                   reg_types_.LongLo(), reg_types_.LongHi());
       break;
     case Instruction::FLOAT_TO_INT:
       work_line_->CheckUnaryOp(dec_insn, reg_types_.Integer(), reg_types_.Float());
       break;
     case Instruction::FLOAT_TO_LONG:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Long(), reg_types_.Float());
+      work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+                                     reg_types_.Float());
       break;
     case Instruction::FLOAT_TO_DOUBLE:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Double(), reg_types_.Float());
+      work_line_->CheckUnaryOpToWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+                                     reg_types_.Float());
       break;
     case Instruction::DOUBLE_TO_INT:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Integer(), reg_types_.Double());
+      work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Integer(),
+                                       reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
     case Instruction::DOUBLE_TO_LONG:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Long(), reg_types_.Double());
+      work_line_->CheckUnaryOpWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+                                   reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
     case Instruction::DOUBLE_TO_FLOAT:
-      work_line_->CheckUnaryOp(dec_insn, reg_types_.Float(), reg_types_.Double());
+      work_line_->CheckUnaryOpFromWide(dec_insn, reg_types_.Float(),
+                                       reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
     case Instruction::INT_TO_BYTE:
       work_line_->CheckUnaryOp(dec_insn, reg_types_.Byte(), reg_types_.Integer());
@@ -2130,12 +2209,14 @@
     case Instruction::SHL_INT:
     case Instruction::SHR_INT:
     case Instruction::USHR_INT:
-      work_line_->CheckBinaryOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), false);
+      work_line_->CheckBinaryOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(),
+                                reg_types_.Integer(), false);
       break;
     case Instruction::AND_INT:
     case Instruction::OR_INT:
     case Instruction::XOR_INT:
-      work_line_->CheckBinaryOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(), reg_types_.Integer(), true);
+      work_line_->CheckBinaryOp(dec_insn, reg_types_.Integer(), reg_types_.Integer(),
+                                reg_types_.Integer(), true);
       break;
     case Instruction::ADD_LONG:
     case Instruction::SUB_LONG:
@@ -2145,13 +2226,16 @@
     case Instruction::AND_LONG:
     case Instruction::OR_LONG:
     case Instruction::XOR_LONG:
-      work_line_->CheckBinaryOp(dec_insn, reg_types_.Long(), reg_types_.Long(), reg_types_.Long(), false);
+      work_line_->CheckBinaryOpWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+                                    reg_types_.LongLo(), reg_types_.LongHi(),
+                                    reg_types_.LongLo(), reg_types_.LongHi());
       break;
     case Instruction::SHL_LONG:
     case Instruction::SHR_LONG:
     case Instruction::USHR_LONG:
       /* shift distance is Int, making these different from other binary operations */
-      work_line_->CheckBinaryOp(dec_insn, reg_types_.Long(), reg_types_.Long(), reg_types_.Integer(), false);
+      work_line_->CheckBinaryOpWideShift(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+                                         reg_types_.Integer());
       break;
     case Instruction::ADD_FLOAT:
     case Instruction::SUB_FLOAT:
@@ -2165,7 +2249,9 @@
     case Instruction::MUL_DOUBLE:
     case Instruction::DIV_DOUBLE:
     case Instruction::REM_DOUBLE:
-      work_line_->CheckBinaryOp(dec_insn, reg_types_.Double(), reg_types_.Double(), reg_types_.Double(), false);
+      work_line_->CheckBinaryOpWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+                                    reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+                                    reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
     case Instruction::ADD_INT_2ADDR:
     case Instruction::SUB_INT_2ADDR:
@@ -2192,12 +2278,15 @@
     case Instruction::AND_LONG_2ADDR:
     case Instruction::OR_LONG_2ADDR:
     case Instruction::XOR_LONG_2ADDR:
-      work_line_->CheckBinaryOp2addr(dec_insn, reg_types_.Long(), reg_types_.Long(), reg_types_.Long(), false);
+      work_line_->CheckBinaryOp2addrWide(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+                                         reg_types_.LongLo(), reg_types_.LongHi(),
+                                         reg_types_.LongLo(), reg_types_.LongHi());
       break;
     case Instruction::SHL_LONG_2ADDR:
     case Instruction::SHR_LONG_2ADDR:
     case Instruction::USHR_LONG_2ADDR:
-      work_line_->CheckBinaryOp2addr(dec_insn, reg_types_.Long(), reg_types_.Long(), reg_types_.Integer(), false);
+      work_line_->CheckBinaryOp2addrWideShift(dec_insn, reg_types_.LongLo(), reg_types_.LongHi(),
+                                              reg_types_.Integer());
       break;
     case Instruction::ADD_FLOAT_2ADDR:
     case Instruction::SUB_FLOAT_2ADDR:
@@ -2211,7 +2300,9 @@
     case Instruction::MUL_DOUBLE_2ADDR:
     case Instruction::DIV_DOUBLE_2ADDR:
     case Instruction::REM_DOUBLE_2ADDR:
-      work_line_->CheckBinaryOp2addr(dec_insn, reg_types_.Double(), reg_types_.Double(),  reg_types_.Double(), false);
+      work_line_->CheckBinaryOp2addrWide(dec_insn, reg_types_.DoubleLo(), reg_types_.DoubleHi(),
+                                         reg_types_.DoubleLo(),  reg_types_.DoubleHi(),
+                                         reg_types_.DoubleLo(), reg_types_.DoubleHi());
       break;
     case Instruction::ADD_INT_LIT16:
     case Instruction::RSUB_INT:
@@ -2290,7 +2381,7 @@
   }  // end - switch (dec_insn.opcode)
 
   if (have_pending_hard_failure_) {
-    if (!Runtime::Current()->IsStarted()) {
+    if (Runtime::Current()->IsCompiler()) {
       /* When compiling, check that the last failure is a hard failure */
       CHECK_EQ(failures_[failures_.size() - 1], VERIFY_ERROR_BAD_CLASS_HARD);
     }
@@ -2645,7 +2736,7 @@
     const RegType& super = GetDeclaringClass().GetSuperClass(&reg_types_);
     if (super.IsUnresolvedTypes()) {
       Fail(VERIFY_ERROR_NO_METHOD) << "unknown super class in invoke-super from "
-                                   << PrettyMethod(method_idx_, *dex_file_)
+                                   << PrettyMethod(dex_method_idx_, *dex_file_)
                                    << " to super " << PrettyMethod(res_method);
       return NULL;
     }
@@ -2653,7 +2744,7 @@
     if (res_method->GetMethodIndex() >= super_klass->GetVTable()->GetLength()) {
       MethodHelper mh(res_method);
       Fail(VERIFY_ERROR_NO_METHOD) << "invalid invoke-super from "
-                                   << PrettyMethod(method_idx_, *dex_file_)
+                                   << PrettyMethod(dex_method_idx_, *dex_file_)
                                    << " to super " << super
                                    << "." << mh.GetName()
                                    << mh.GetSignature();
@@ -2768,7 +2859,7 @@
 }
 
 void MethodVerifier::VerifyAGet(const DecodedInstruction& dec_insn,
-                             const RegType& insn_type, bool is_primitive) {
+                                const RegType& insn_type, bool is_primitive) {
   const RegType& index_type = work_line_->GetRegisterType(dec_insn.vC);
   if (!index_type.IsArrayIndexTypes()) {
     Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Invalid reg type for array index (" << index_type << ")";
@@ -2782,7 +2873,8 @@
         work_line_->SetRegisterType(dec_insn.vA, reg_types_.Zero());
       } else {
         // Category 2
-        work_line_->SetRegisterType(dec_insn.vA, reg_types_.ConstLo());
+        work_line_->SetRegisterTypeWide(dec_insn.vA, reg_types_.FromCat2ConstLo(0, false),
+                                        reg_types_.FromCat2ConstHi(0, false));
       }
     } else if (!array_type.IsArrayTypes()) {
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "not array type " << array_type << " with aget";
@@ -2797,14 +2889,19 @@
             << " source for category 1 aget";
       } else if (is_primitive && !insn_type.Equals(component_type) &&
                  !((insn_type.IsInteger() && component_type.IsFloat()) ||
-                   (insn_type.IsLong() && component_type.IsDouble()))) {
-          Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array type " << array_type
-              << " incompatible with aget of type " << insn_type;
+                 (insn_type.IsLong() && component_type.IsDouble()))) {
+        Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "array type " << array_type
+            << " incompatible with aget of type " << insn_type;
       } else {
-          // Use knowledge of the field type which is stronger than the type inferred from the
-          // instruction, which can't differentiate object types and ints from floats, longs from
-          // doubles.
+        // Use knowledge of the field type which is stronger than the type inferred from the
+        // instruction, which can't differentiate object types and ints from floats, longs from
+        // doubles.
+        if (!component_type.IsLowHalf()) {
           work_line_->SetRegisterType(dec_insn.vA, component_type);
+        } else {
+          work_line_->SetRegisterTypeWide(dec_insn.vA, component_type,
+                                          component_type.HighHalf(&reg_types_));
+        }
       }
     }
   }
@@ -2925,7 +3022,7 @@
       // the field is declared in this class
       Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "cannot access instance field " << PrettyField(field)
                                         << " of a not fully initialized object within the context of "
-                                        << PrettyMethod(method_idx_, *dex_file_);
+                                        << PrettyMethod(dex_method_idx_, *dex_file_);
       return NULL;
     } else if (!field_klass.IsAssignableFrom(obj_type)) {
       // Trying to access C1.field1 using reference of type C2, which is neither C1 or a sub-class
@@ -2986,7 +3083,11 @@
       return;
     }
   }
-  work_line_->SetRegisterType(dec_insn.vA, field_type);
+  if (!field_type.IsLowHalf()) {
+    work_line_->SetRegisterType(dec_insn.vA, field_type);
+  } else {
+    work_line_->SetRegisterTypeWide(dec_insn.vA, field_type, field_type.HighHalf(&reg_types_));
+  }
 }
 
 void MethodVerifier::VerifyISPut(const DecodedInstruction& dec_insn,
@@ -3113,7 +3214,7 @@
 }
 
 const RegType& MethodVerifier::GetMethodReturnType() {
-  const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx_);
+  const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
   const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id);
   uint16_t return_type_idx = proto_id.return_type_idx_;
   const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx));
@@ -3125,7 +3226,7 @@
     Class* klass = foo_method_->GetDeclaringClass();
     return reg_types_.FromClass(klass, klass->IsFinal());
   } else {
-    const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx_);
+    const DexFile::MethodId& method_id = dex_file_->GetMethodId(dex_method_idx_);
     const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_));
     return reg_types_.FromDescriptor(class_loader_, descriptor, false);
   }
@@ -3265,6 +3366,50 @@
   return it->second;
 }
 
+std::vector<int32_t> MethodVerifier::DescribeVRegs(uint32_t dex_pc) {
+  RegisterLine* line = reg_table_.GetLine(dex_pc);
+  std::vector<int32_t> result;
+  for (size_t i = 0; i < line->NumRegs(); ++i) {
+    const RegType& type = line->GetRegisterType(i);
+    if (type.IsConstant()) {
+      result.push_back(type.IsPreciseConstant() ? kConstant : kImpreciseConstant);
+      result.push_back(type.ConstantValue());
+    } else if (type.IsConstantLo()) {
+      result.push_back(type.IsPreciseConstantLo() ? kConstant : kImpreciseConstant);
+      result.push_back(type.ConstantValueLo());
+    } else if (type.IsConstantHi()) {
+      result.push_back(type.IsPreciseConstantHi() ? kConstant : kImpreciseConstant);
+      result.push_back(type.ConstantValueHi());
+    } else if (type.IsIntegralTypes()) {
+      result.push_back(kIntVReg);
+      result.push_back(0);
+    } else if (type.IsFloat()) {
+      result.push_back(kFloatVReg);
+      result.push_back(0);
+    } else if (type.IsLong()) {
+      result.push_back(kLongLoVReg);
+      result.push_back(0);
+      result.push_back(kLongHiVReg);
+      result.push_back(0);
+      ++i;
+    } else if (type.IsDouble()) {
+      result.push_back(kDoubleLoVReg);
+      result.push_back(0);
+      result.push_back(kDoubleHiVReg);
+      result.push_back(0);
+      ++i;
+    } else if (type.IsUndefined() || type.IsConflict() || type.IsHighHalf()) {
+      result.push_back(kUndefined);
+      result.push_back(0);
+    } else {
+      CHECK(type.IsNonZeroReferenceTypes()) << type;
+      result.push_back(kReferenceVReg);
+      result.push_back(0);
+    }
+  }
+  return result;
+}
+
 Mutex* MethodVerifier::dex_gc_maps_lock_ = NULL;
 MethodVerifier::DexGcMapTable* MethodVerifier::dex_gc_maps_ = NULL;
 
diff --git a/src/verifier/method_verifier.h b/src/verifier/method_verifier.h
index 42283a2..95d7905 100644
--- a/src/verifier/method_verifier.h
+++ b/src/verifier/method_verifier.h
@@ -113,6 +113,8 @@
   kTrackRegsAll,
 };
 
+// A mapping from a dex pc to the register line statuses as they are immediately prior to the
+// execution of that instruction.
 class PcToRegisterLineTable {
  public:
   PcToRegisterLineTable() {}
@@ -137,7 +139,6 @@
 
  private:
   typedef SafeMap<int32_t, RegisterLine*> Table;
-  // Map from a dex pc to the register status associated with it
   Table pc_to_register_line_;
 };
 
@@ -162,7 +163,19 @@
                                  std::string& error)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  static void VerifyMethodAndDump(AbstractMethod* method)
+  static void VerifyMethodAndDump(std::ostream& os, uint32_t method_idx, const DexFile* dex_file,
+                                  DexCache* dex_cache, ClassLoader* class_loader,
+                                  uint32_t class_def_idx, const DexFile::CodeItem* code_item,
+                                  AbstractMethod* method, uint32_t method_access_flags)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  static std::vector<int32_t> DescribeVRegs(uint32_t dex_method_idx,
+                                            const DexFile* dex_file, DexCache* dex_cache,
+                                            ClassLoader* class_loader,
+                                            uint32_t class_def_idx,
+                                            const DexFile::CodeItem* code_item,
+                                            AbstractMethod* method,
+                                            uint32_t method_access_flags, uint32_t dex_pc)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   uint8_t EncodePcToReferenceMapData() const;
@@ -180,7 +193,7 @@
 
   // Log for verification information.
   std::ostream& LogVerifyInfo() {
-    return info_messages_ << "VFY: " << PrettyMethod(method_idx_, *dex_file_)
+    return info_messages_ << "VFY: " << PrettyMethod(dex_method_idx_, *dex_file_)
                           << '[' << reinterpret_cast<void*>(work_insn_idx_) << "] : ";
   }
 
@@ -576,6 +589,9 @@
   // Compute sizes for GC map data
   void ComputeGcMapSizes(size_t* gc_points, size_t* ref_bitmap_bits, size_t* log2_max_gc_pc);
 
+  // Describe VRegs at the given dex pc.
+  std::vector<int32_t> DescribeVRegs(uint32_t dex_pc);
+
   InsnFlags* CurrentInsnFlags();
 
   // All the GC maps that the verifier has created
@@ -617,7 +633,7 @@
   // Storage for the register status we're saving for later.
   UniquePtr<RegisterLine> saved_line_;
 
-  uint32_t method_idx_;  // The method we're working on.
+  uint32_t dex_method_idx_;  // The method we're working on.
   // Its object representation if known.
   AbstractMethod* foo_method_ GUARDED_BY(Locks::mutator_lock_);
   uint32_t method_access_flags_;  // Method's access flags.
diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc
index e02fbf4..dc41ceb 100644
--- a/src/verifier/reg_type.cc
+++ b/src/verifier/reg_type.cc
@@ -25,19 +25,22 @@
 static const char* type_strings[] = {
     "Undefined",
     "Conflict",
-    "Boolean",
-    "Byte",
-    "Short",
-    "Char",
-    "Integer",
-    "Float",
-    "Long (Low Half)",
-    "Long (High Half)",
-    "Double (Low Half)",
-    "Double (High Half)",
-    "64-bit Constant (Low Half)",
-    "64-bit Constant (High Half)",
-    "32-bit Constant",
+    "boolean",
+    "byte",
+    "short",
+    "char",
+    "int",
+    "float",
+    "long (Low Half)",
+    "long (High Half)",
+    "double (Low Half)",
+    "double (High Half)",
+    "Precise 32-bit Constant",
+    "Imprecise 32-bit Constant",
+    "Precise 64-bit Constant (Low Half)",
+    "Precise 64-bit Constant (High Half)",
+    "Imprecise 64-bit Constant (Low Half)",
+    "Imprecise 64-bit Constant (High Half)",
     "Unresolved Reference",
     "Uninitialized Reference",
     "Uninitialized This Reference",
@@ -81,14 +84,34 @@
   } else if (IsConstant()) {
     uint32_t val = ConstantValue();
     if (val == 0) {
-      result = "Zero";
+      CHECK(IsPreciseConstant());
+      result = "Zero/null";
     } else {
+      result = IsPreciseConstant() ? "Precise " : "Imprecise ";
       if (IsConstantShort()) {
-        result = StringPrintf("32-bit Constant: %d", val);
+        result += StringPrintf("Constant: %d", val);
       } else {
-        result = StringPrintf("32-bit Constant: 0x%x", val);
+        result += StringPrintf("Constant: 0x%x", val);
       }
     }
+  } else if (IsConstantLo()) {
+    int32_t val = ConstantValueLo();
+    result = IsPreciseConstantLo() ? "Precise " : "Imprecise ";
+    if (val >= std::numeric_limits<jshort>::min() &&
+        val <= std::numeric_limits<jshort>::max()) {
+      result += StringPrintf("Low-half Constant: %d", val);
+    } else {
+      result += StringPrintf("Low-half Constant: 0x%x", val);
+    }
+  } else if (IsConstantHi()) {
+    int32_t val = ConstantValueHi();
+    result = IsPreciseConstantHi() ? "Precise " : "Imprecise ";
+    if (val >= std::numeric_limits<jshort>::min() &&
+        val <= std::numeric_limits<jshort>::max()) {
+      result += StringPrintf("High-half Constant: %d", val);
+    } else {
+      result += StringPrintf("High-half Constant: 0x%x", val);
+    }
   } else {
     result = type_strings[type_];
     if (IsReferenceTypes()) {
@@ -104,13 +127,14 @@
 }
 
 const RegType& RegType::HighHalf(RegTypeCache* cache) const {
-  CHECK(IsLowHalf());
+  DCHECK(IsLowHalf());
   if (type_ == kRegTypeLongLo) {
     return cache->FromType(kRegTypeLongHi);
   } else if (type_ == kRegTypeDoubleLo) {
     return cache->FromType(kRegTypeDoubleHi);
   } else {
-    return cache->FromType(kRegTypeConstHi);
+    DCHECK_EQ(type_, kRegTypeImpreciseConstLo);
+    return cache->FromType(kRegTypeImpreciseConstHi);
   }
 }
 
@@ -244,16 +268,32 @@
     if (val1 >= 0 && val2 >= 0) {
       // +ve1 MERGE +ve2 => MAX(+ve1, +ve2)
       if (val1 >= val2) {
-        return *this;
+        if (!IsPreciseConstant()) {
+          return *this;
+        } else {
+          return reg_types->FromCat1Const(val1, false);
+        }
       } else {
-        return incoming_type;
+        if (!incoming_type.IsPreciseConstant()) {
+          return incoming_type;
+        } else {
+          return reg_types->FromCat1Const(val2, false);
+        }
       }
     } else if (val1 < 0 && val2 < 0) {
       // -ve1 MERGE -ve2 => MIN(-ve1, -ve2)
       if (val1 <= val2) {
-        return *this;
+        if (!IsPreciseConstant()) {
+          return *this;
+        } else {
+          return reg_types->FromCat1Const(val1, false);
+        }
       } else {
-        return incoming_type;
+        if (!incoming_type.IsPreciseConstant()) {
+          return incoming_type;
+        } else {
+          return reg_types->FromCat1Const(val2, false);
+        }
       }
     } else {
       // Values are +ve and -ve, choose smallest signed type in which they both fit
@@ -275,6 +315,14 @@
         return reg_types->IntConstant();
       }
     }
+  } else if (IsConstantLo() && incoming_type.IsConstantLo()) {
+    int32_t val1 = ConstantValueLo();
+    int32_t val2 = incoming_type.ConstantValueLo();
+    return reg_types->FromCat2ConstLo(val1 | val2, false);
+  } else if (IsConstantHi() && incoming_type.IsConstantHi()) {
+    int32_t val1 = ConstantValueHi();
+    int32_t val2 = incoming_type.ConstantValueHi();
+    return reg_types->FromCat2ConstHi(val1 | val2, false);
   } else if (IsIntegralTypes() && incoming_type.IsIntegralTypes()) {
     if (IsBooleanTypes() && incoming_type.IsBooleanTypes()) {
       return reg_types->Boolean();  // boolean MERGE boolean => boolean
diff --git a/src/verifier/reg_type.h b/src/verifier/reg_type.h
index 205867d..5daaf60 100644
--- a/src/verifier/reg_type.h
+++ b/src/verifier/reg_type.h
@@ -52,10 +52,13 @@
     kRegTypeLongHi,
     kRegTypeDoubleLo,       // D.
     kRegTypeDoubleHi,
-    kRegTypeConstLo,        // Const derived wide, lower half - could be long or double.
-    kRegTypeConstHi,        // Const derived wide, upper half - could be long or double.
-    kRegTypeLastFixedLocation = kRegTypeConstHi,
-    kRegTypeConst,          // 32-bit constant derived value - could be float or int.
+    kRegTypeLastFixedLocation = kRegTypeDoubleHi,
+    kRegTypePreciseConst,   // 32-bit constant - could be float or int.
+    kRegTypeImpreciseConst, // 32-bit constant derived value - could be float or int.
+    kRegTypePreciseConstLo, // Const wide, lower half - could be long or double.
+    kRegTypePreciseConstHi, // Const wide, upper half - could be long or double.
+    kRegTypeImpreciseConstLo, // Const derived wide, lower half - could be long or double.
+    kRegTypeImpreciseConstHi, // Const derived wide, upper half - could be long or double.
     kRegTypeUnresolvedReference,        // Reference type that couldn't be resolved.
     kRegTypeUninitializedReference,     // Freshly allocated reference type.
     kRegTypeUninitializedThisReference, // Freshly allocated reference passed as "this".
@@ -105,38 +108,85 @@
         IsUnresolvedAndUninitializedThisReference() || IsUnresolvedMergedReference() ||
         IsUnresolvedSuperClass();
   }
-  bool IsLowHalf() const { return type_ == kRegTypeLongLo ||
-                                  type_ == kRegTypeDoubleLo ||
-                                  type_ == kRegTypeConstLo; }
-  bool IsHighHalf() const { return type_ == kRegTypeLongHi ||
-                                   type_ == kRegTypeDoubleHi ||
-                                   type_ == kRegTypeConstHi; }
+  bool IsLowHalf() const {
+    return type_ == kRegTypeLongLo ||
+           type_ == kRegTypeDoubleLo ||
+           type_ == kRegTypePreciseConstLo ||
+           type_ == kRegTypeImpreciseConstLo;
+  }
+  bool IsHighHalf() const {
+    return type_ == kRegTypeLongHi ||
+           type_ == kRegTypeDoubleHi ||
+           type_ == kRegTypePreciseConstHi ||
+           type_ == kRegTypeImpreciseConstHi;
+  }
 
   bool IsLongOrDoubleTypes() const { return IsLowHalf(); }
 
   // Check this is the low half, and that type_h is its matching high-half
   bool CheckWidePair(const RegType& type_h) const {
-    return IsLowHalf() && (type_h.type_ == type_ + 1);
+    if (IsLowHalf()) {
+      return (type_h.type_ == type_ + 1) ||
+          (IsPreciseConstantLo() && !type_h.IsPreciseConstantHi()) ||
+          (!IsPreciseConstantLo() && type_h.IsPreciseConstantHi());
+    }
+    return false;
   }
 
   // The high half that corresponds to this low half
   const RegType& HighHalf(RegTypeCache* cache) const
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
-  bool IsConstant() const { return type_ == kRegTypeConst; }
-  bool IsLongConstant() const { return type_ == kRegTypeConstLo; }
-  bool IsLongConstantHigh() const { return type_ == kRegTypeConstHi; }
+  bool IsPreciseConstant() const {
+    return type_ == kRegTypePreciseConst;
+  }
+  bool IsPreciseConstantLo() const {
+    bool result = (type_ == kRegTypePreciseConstLo);
+    DCHECK(result || (type_ == kRegTypeImpreciseConstLo));
+    return result;
+  }
+  bool IsPreciseConstantHi() const {
+    bool result = (type_ == kRegTypePreciseConstHi);
+    DCHECK(result || (type_ == kRegTypeImpreciseConstHi));
+    return result;
+  }
+  bool IsConstant() const {
+    return type_ == kRegTypePreciseConst || type_ == kRegTypeImpreciseConst;
+  }
 
-  // If this is a 32-bit constant, what is the value? This value may just
-  // approximate to the actual constant value by virtue of merging.
+  bool IsConstantLo() const {
+    return type_ == kRegTypePreciseConstLo || type_ == kRegTypeImpreciseConstLo;
+  }
+  bool IsLongConstant() const {
+    return IsConstantLo();
+  }
+  bool IsConstantHi() const {
+    return type_ == kRegTypePreciseConstHi || type_ == kRegTypeImpreciseConstHi;
+  }
+  bool IsLongConstantHigh() const {
+    return IsConstantHi();
+  }
+
+  // If this is a 32-bit constant, what is the value? This value may be imprecise in which case
+  // the value represents part of the integer range of values that may be held in the register.
   int32_t ConstantValue() const {
     DCHECK(IsConstant());
     return allocation_pc_or_constant_or_merged_types_;
   }
+  int32_t ConstantValueLo() const {
+    DCHECK(IsConstantLo());
+    return allocation_pc_or_constant_or_merged_types_;
+  }
+  int32_t ConstantValueHi() const {
+    DCHECK(IsConstantHi());
+    return allocation_pc_or_constant_or_merged_types_;
+  }
 
-  bool IsZero()         const { return IsConstant() && ConstantValue() == 0; }
-  bool IsOne()          const { return IsConstant() && ConstantValue() == 1; }
-  bool IsConstantBoolean() const { return IsZero() || IsOne(); }
+  bool IsZero()         const { return IsPreciseConstant() && ConstantValue() == 0; }
+  bool IsOne()          const { return IsPreciseConstant() && ConstantValue() == 1; }
+  bool IsConstantBoolean() const {
+    return IsConstant() && ConstantValue() >= 0 && ConstantValue() <= 1;
+  }
   bool IsConstantByte() const {
     return IsConstant() &&
         ConstantValue() >= std::numeric_limits<jbyte>::min() &&
@@ -187,13 +237,17 @@
     return IsLong() || IsLongConstant();
   }
   bool IsLongHighTypes() const {
-    return type_ == kRegTypeLongHi || type_ == kRegTypeConstHi;
+    return type_ == kRegTypeLongHi ||
+           type_ == kRegTypePreciseConstHi ||
+           type_ == kRegTypeImpreciseConstHi;
   }
   bool IsDoubleTypes() const {
     return IsDouble() || IsLongConstant();
   }
   bool IsDoubleHighTypes() const {
-    return type_ == kRegTypeDoubleHi || type_ == kRegTypeConstHi;
+    return type_ == kRegTypeDoubleHi ||
+           type_ == kRegTypePreciseConstHi ||
+           type_ == kRegTypeImpreciseConstHi;
   }
 
   uint32_t GetAllocationPc() const {
@@ -351,8 +405,9 @@
       : type_(type), klass_or_descriptor_(klass_or_descriptor),
         allocation_pc_or_constant_or_merged_types_(allocation_pc_or_constant_or_merged_types),
         cache_id_(cache_id) {
-    DCHECK(IsConstant() || IsUninitializedTypes() || IsUnresolvedMergedReference() ||
-           IsUnresolvedSuperClass() || allocation_pc_or_constant_or_merged_types == 0);
+    DCHECK(IsConstant() || IsConstantLo() || IsConstantHi() ||
+           IsUninitializedTypes() || IsUnresolvedMergedReference() || IsUnresolvedSuperClass() ||
+           allocation_pc_or_constant_or_merged_types == 0);
     if (!IsConstant() && !IsLongConstant() && !IsLongConstantHigh() && !IsUndefined() &&
         !IsConflict() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) {
       DCHECK(klass_or_descriptor != NULL);
diff --git a/src/verifier/reg_type_cache.cc b/src/verifier/reg_type_cache.cc
index 847cde9..1b91321 100644
--- a/src/verifier/reg_type_cache.cc
+++ b/src/verifier/reg_type_cache.cc
@@ -312,14 +312,41 @@
   }
 }
 
-const RegType& RegTypeCache::FromCat1Const(int32_t value) {
+const RegType& RegTypeCache::FromCat1Const(int32_t value, bool precise) {
+  RegType::Type wanted_type = precise ? RegType::kRegTypePreciseConst : RegType::kRegTypeImpreciseConst;
   for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
     RegType* cur_entry = entries_[i];
-    if (cur_entry->IsConstant() && cur_entry->ConstantValue() == value) {
+    if (cur_entry->GetType() == wanted_type && cur_entry->ConstantValue() == value) {
       return *cur_entry;
     }
   }
-  RegType* entry = new RegType(RegType::kRegTypeConst, NULL, value, entries_.size());
+  RegType* entry = new RegType(wanted_type, NULL, value, entries_.size());
+  entries_.push_back(entry);
+  return *entry;
+}
+
+const RegType& RegTypeCache::FromCat2ConstLo(int32_t value, bool precise) {
+  RegType::Type wanted_type = precise ? RegType::kRegTypePreciseConstLo : RegType::kRegTypeImpreciseConstLo;
+  for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+    RegType* cur_entry = entries_[i];
+    if (cur_entry->GetType() == wanted_type && cur_entry->ConstantValueLo() == value) {
+      return *cur_entry;
+    }
+  }
+  RegType* entry = new RegType(wanted_type, NULL, value, entries_.size());
+  entries_.push_back(entry);
+  return *entry;
+}
+
+const RegType& RegTypeCache::FromCat2ConstHi(int32_t value, bool precise) {
+  RegType::Type wanted_type = precise ? RegType::kRegTypePreciseConstHi : RegType::kRegTypeImpreciseConstHi;
+  for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) {
+    RegType* cur_entry = entries_[i];
+    if (cur_entry->GetType() == wanted_type && cur_entry->ConstantValueHi() == value) {
+      return *cur_entry;
+    }
+  }
+  RegType* entry = new RegType(wanted_type, NULL, value, entries_.size());
   entries_.push_back(entry);
   return *entry;
 }
@@ -337,11 +364,10 @@
 }
 
 void RegTypeCache::Dump(std::ostream& os) {
-  os << "Register Types:\n";
   for (size_t i = 0; i < entries_.size(); i++) {
     RegType* cur_entry = entries_[i];
     if (cur_entry != NULL) {
-      os << "\t" << i << ": " << cur_entry->Dump() << "\n";
+      os << i << ": " << cur_entry->Dump() << "\n";
     }
   }
 }
diff --git a/src/verifier/reg_type_cache.h b/src/verifier/reg_type_cache.h
index 36fd2c7..bf0c5b1 100644
--- a/src/verifier/reg_type_cache.h
+++ b/src/verifier/reg_type_cache.h
@@ -44,7 +44,10 @@
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   const RegType& FromClass(Class* klass, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  const RegType& FromCat1Const(int32_t value);
+  const RegType& FromCat1Const(int32_t value, bool precise)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+  const RegType& FromCat2ConstLo(int32_t value, bool precise);
+  const RegType& FromCat2ConstHi(int32_t value, bool precise);
   const RegType& FromDescriptor(ClassLoader* loader, const char* descriptor, bool precise)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   const RegType& FromType(RegType::Type)
@@ -70,12 +73,18 @@
   const RegType& Float() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return FromType(RegType::kRegTypeFloat);
   }
-  const RegType& Long() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const RegType& LongLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return FromType(RegType::kRegTypeLongLo);
   }
-  const RegType& Double() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+  const RegType& LongHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return FromType(RegType::kRegTypeLongHi);
+  }
+  const RegType& DoubleLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return FromType(RegType::kRegTypeDoubleLo);
   }
+  const RegType& DoubleHi() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return FromType(RegType::kRegTypeDoubleHi);
+  }
 
   const RegType& JavaLangClass(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return From(precise ? RegType::kRegTypeReference
@@ -103,11 +112,8 @@
   const RegType& Conflict() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
     return FromType(RegType::kRegTypeConflict);
   }
-  const RegType& ConstLo() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return FromType(RegType::kRegTypeConstLo);
-  }
   const RegType& Zero() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
-    return FromCat1Const(0);
+    return FromCat1Const(0, true);
   }
 
   const RegType& Uninitialized(const RegType& type, uint32_t allocation_pc);
@@ -118,9 +124,15 @@
   // Representatives of various constant types. When merging constants we can't infer a type,
   // (an int may later be used as a float) so we select these representative values meaning future
   // merges won't know the exact constant value but have some notion of its size.
-  const RegType& ByteConstant() { return FromCat1Const(std::numeric_limits<jbyte>::min()); }
-  const RegType& ShortConstant() { return FromCat1Const(std::numeric_limits<jshort>::min()); }
-  const RegType& IntConstant() { return FromCat1Const(std::numeric_limits<jint>::max()); }
+  const RegType& ByteConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return FromCat1Const(std::numeric_limits<jbyte>::min(), false);
+  }
+  const RegType& ShortConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return FromCat1Const(std::numeric_limits<jshort>::min(), false);
+  }
+  const RegType& IntConstant() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    return FromCat1Const(std::numeric_limits<jint>::max(), false);
+  }
 
   const RegType& GetComponentType(const RegType& array, ClassLoader* loader)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
diff --git a/src/verifier/reg_type_test.cc b/src/verifier/reg_type_test.cc
index 6bdf886..18e9a65 100644
--- a/src/verifier/reg_type_test.cc
+++ b/src/verifier/reg_type_test.cc
@@ -184,7 +184,7 @@
   EXPECT_FALSE(int_reg_type.IsDoubleTypes());
   EXPECT_TRUE(int_reg_type.IsArrayIndexTypes());
 
-  const RegType& long_reg_type = cache.Long();
+  const RegType& long_reg_type = cache.LongLo();
   EXPECT_FALSE(long_reg_type.IsUndefined());
   EXPECT_FALSE(long_reg_type.IsConflict());
   EXPECT_FALSE(long_reg_type.IsZero());
@@ -246,7 +246,7 @@
   EXPECT_FALSE(float_reg_type.IsDoubleTypes());
   EXPECT_FALSE(float_reg_type.IsArrayIndexTypes());
 
-  const RegType& double_reg_type = cache.Double();
+  const RegType& double_reg_type = cache.DoubleLo();
   EXPECT_FALSE(double_reg_type.IsUndefined());
   EXPECT_FALSE(double_reg_type.IsConflict());
   EXPECT_FALSE(double_reg_type.IsZero());
diff --git a/src/verifier/register_line.cc b/src/verifier/register_line.cc
index 4882740..afd2eff 100644
--- a/src/verifier/register_line.cc
+++ b/src/verifier/register_line.cc
@@ -35,12 +35,9 @@
 
 bool RegisterLine::SetRegisterType(uint32_t vdst, const RegType& new_type) {
   DCHECK_LT(vdst, num_regs_);
-  if (new_type.IsLowHalf()) {
-    line_[vdst] = new_type.GetId();
-    line_[vdst + 1] = new_type.HighHalf(verifier_->GetRegTypeCache()).GetId();
-  } else if (new_type.IsHighHalf()) {
-    /* should never set these explicitly */
-    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Explicit set of high register type";
+  if (new_type.IsLowHalf() || new_type.IsHighHalf()) {
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Expected category1 register type not '"
+        << new_type << "'";
     return false;
   } else if (new_type.IsConflict()) {  // should only be set during a merge
     verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type;
@@ -58,13 +55,33 @@
   return true;
 }
 
+bool RegisterLine::SetRegisterTypeWide(uint32_t vdst, const RegType& new_type1,
+                                       const RegType& new_type2) {
+  DCHECK_LT(vdst, num_regs_);
+  if (!new_type1.CheckWidePair(new_type2)) {
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Invalid wide pair '"
+        << new_type1 << "' '" << new_type2 << "'";
+    return false;
+  } else {
+    line_[vdst] = new_type1.GetId();
+    line_[vdst + 1] = new_type2.GetId();
+  }
+  // Clear the monitor entry bits for this register.
+  ClearAllRegToLockDepths(vdst);
+  ClearAllRegToLockDepths(vdst + 1);
+  return true;
+}
+
 void RegisterLine::SetResultTypeToUnknown() {
   result_[0] = RegType::kRegTypeUndefined;
   result_[1] = RegType::kRegTypeUndefined;
 }
 
 void RegisterLine::SetResultRegisterType(const RegType& new_type) {
+  DCHECK(!new_type.IsLowHalf());
+  DCHECK(!new_type.IsHighHalf());
   result_[0] = new_type.GetId();
+  result_[1] = RegType::kRegTypeUndefined;
   if (new_type.IsLowHalf()) {
     DCHECK_EQ(new_type.HighHalf(verifier_->GetRegTypeCache()).GetId(), new_type.GetId() + 1);
     result_[1] = new_type.GetId() + 1;
@@ -73,6 +90,12 @@
   }
 }
 
+void RegisterLine::SetResultRegisterTypeWide(const RegType& new_type1, const RegType& new_type2) {
+  DCHECK(new_type1.CheckWidePair(new_type2));
+  result_[0] = new_type1.GetId();
+  result_[1] = new_type2.GetId();
+}
+
 const RegType& RegisterLine::GetRegisterType(uint32_t vsrc) const {
   // The register index was validated during the static pass, so we don't need to check it here.
   DCHECK_LT(vsrc, num_regs_);
@@ -121,6 +144,29 @@
   return true;
 }
 
+bool RegisterLine::VerifyRegisterTypeWide(uint32_t vsrc, const RegType& check_type1,
+                                          const RegType& check_type2) {
+  DCHECK(check_type1.CheckWidePair(check_type2));
+  // Verify the src register type against the check type refining the type of the register
+  const RegType& src_type = GetRegisterType(vsrc);
+  if (!check_type1.IsAssignableFrom(src_type)) {
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "register v" << vsrc << " has type " << src_type
+                               << " but expected " << check_type1;
+    return false;
+  }
+  const RegType& src_type_h = GetRegisterType(vsrc + 1);
+  if (!src_type.CheckWidePair(src_type_h)) {
+    verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "wide register v" << vsrc << " has type "
+        << src_type << "/" << src_type_h;
+    return false;
+  }
+  // The register at vsrc has a defined type, we know the lower-upper-bound, but this is less
+  // precise than the subtype in vsrc so leave it for reference types. For primitive types
+  // if they are a defined type then they are as precise as we can get, however, for constant
+  // types we may wish to refine them. Unfortunately constant propagation has rendered this useless.
+  return true;
+}
+
 void RegisterLine::MarkRefsAsInitialized(const RegType& uninit_type) {
   DCHECK(uninit_type.IsUninitializedTypes());
   const RegType& init_type = verifier_->GetRegTypeCache()->FromUninitialized(uninit_type);
@@ -180,7 +226,7 @@
     verifier_->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy2 v" << vdst << "<-v" << vsrc
                                                  << " type=" << type_l << "/" << type_h;
   } else {
-    SetRegisterType(vdst, type_l);  // implicitly sets the second half
+    SetRegisterTypeWide(vdst, type_l, type_h);
   }
 }
 
@@ -209,7 +255,7 @@
         << "copyRes2 v" << vdst << "<- result0"  << " type=" << type_l;
   } else {
     DCHECK(type_l.CheckWidePair(type_h));  // Set should never allow this case
-    SetRegisterType(vdst, type_l);  // also sets the high
+    SetRegisterTypeWide(vdst, type_l, type_h);  // also sets the high
     result_[0] = RegType::kRegTypeUndefined;
     result_[1] = RegType::kRegTypeUndefined;
   }
@@ -222,6 +268,30 @@
   }
 }
 
+void RegisterLine::CheckUnaryOpWide(const DecodedInstruction& dec_insn,
+                                    const RegType& dst_type1, const RegType& dst_type2,
+                                    const RegType& src_type1, const RegType& src_type2) {
+  if (VerifyRegisterTypeWide(dec_insn.vB, src_type1, src_type2)) {
+    SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2);
+  }
+}
+
+void RegisterLine::CheckUnaryOpToWide(const DecodedInstruction& dec_insn,
+                                      const RegType& dst_type1, const RegType& dst_type2,
+                                      const RegType& src_type) {
+  if (VerifyRegisterType(dec_insn.vB, src_type)) {
+    SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2);
+  }
+}
+
+void RegisterLine::CheckUnaryOpFromWide(const DecodedInstruction& dec_insn,
+                                        const RegType& dst_type,
+                                        const RegType& src_type1, const RegType& src_type2) {
+  if (VerifyRegisterTypeWide(dec_insn.vB, src_type1, src_type2)) {
+    SetRegisterType(dec_insn.vA, dst_type);
+  }
+}
+
 void RegisterLine::CheckBinaryOp(const DecodedInstruction& dec_insn,
                                  const RegType& dst_type,
                                  const RegType& src_type1, const RegType& src_type2,
@@ -240,6 +310,25 @@
   }
 }
 
+void RegisterLine::CheckBinaryOpWide(const DecodedInstruction& dec_insn,
+                                     const RegType& dst_type1, const RegType& dst_type2,
+                                     const RegType& src_type1_1, const RegType& src_type1_2,
+                                     const RegType& src_type2_1, const RegType& src_type2_2) {
+  if (VerifyRegisterTypeWide(dec_insn.vB, src_type1_1, src_type1_2) &&
+      VerifyRegisterTypeWide(dec_insn.vC, src_type2_1, src_type2_2)) {
+    SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2);
+  }
+}
+
+void RegisterLine::CheckBinaryOpWideShift(const DecodedInstruction& dec_insn,
+                                          const RegType& long_lo_type, const RegType& long_hi_type,
+                                          const RegType& int_type) {
+  if (VerifyRegisterTypeWide(dec_insn.vB, long_lo_type, long_hi_type) &&
+      VerifyRegisterType(dec_insn.vC, int_type)) {
+    SetRegisterTypeWide(dec_insn.vA, long_lo_type, long_hi_type);
+  }
+}
+
 void RegisterLine::CheckBinaryOp2addr(const DecodedInstruction& dec_insn,
                                       const RegType& dst_type, const RegType& src_type1,
                                       const RegType& src_type2, bool check_boolean_op) {
@@ -257,6 +346,25 @@
   }
 }
 
+void RegisterLine::CheckBinaryOp2addrWide(const DecodedInstruction& dec_insn,
+                                          const RegType& dst_type1, const RegType& dst_type2,
+                                          const RegType& src_type1_1, const RegType& src_type1_2,
+                                          const RegType& src_type2_1, const RegType& src_type2_2) {
+  if (VerifyRegisterTypeWide(dec_insn.vA, src_type1_1, src_type1_2) &&
+      VerifyRegisterTypeWide(dec_insn.vB, src_type2_1, src_type2_2)) {
+    SetRegisterTypeWide(dec_insn.vA, dst_type1, dst_type2);
+  }
+}
+
+void RegisterLine::CheckBinaryOp2addrWideShift(const DecodedInstruction& dec_insn,
+                                               const RegType& long_lo_type, const RegType& long_hi_type,
+                                               const RegType& int_type) {
+  if (VerifyRegisterTypeWide(dec_insn.vA, long_lo_type, long_hi_type) &&
+      VerifyRegisterType(dec_insn.vB, int_type)) {
+    SetRegisterTypeWide(dec_insn.vA, long_lo_type, long_hi_type);
+  }
+}
+
 void RegisterLine::CheckLiteralOp(const DecodedInstruction& dec_insn,
                                   const RegType& dst_type, const RegType& src_type,
                                   bool check_boolean_op) {
diff --git a/src/verifier/register_line.h b/src/verifier/register_line.h
index 9f0fcb0..c6c19f8 100644
--- a/src/verifier/register_line.h
+++ b/src/verifier/register_line.h
@@ -48,9 +48,6 @@
 // During verification, we associate one of these with every "interesting" instruction. We track
 // the status of all registers, and (if the method has any monitor-enter instructions) maintain a
 // stack of entered monitors (identified by code unit offset).
-// If live-precise register maps are enabled, the "liveRegs" vector will be populated. Unlike the
-// other lists of registers here, we do not track the liveness of the method result register
-// (which is not visible to the GC).
 class RegisterLine {
  public:
   RegisterLine(size_t num_regs, MethodVerifier* verifier)
@@ -88,16 +85,25 @@
   bool SetRegisterType(uint32_t vdst, const RegType& new_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  bool SetRegisterTypeWide(uint32_t vdst, const RegType& new_type1, const RegType& new_type2)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   /* Set the type of the "result" register. */
   void SetResultRegisterType(const RegType& new_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void SetResultRegisterTypeWide(const RegType& new_type1, const RegType& new_type2)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   // Get the type of register vsrc.
   const RegType& GetRegisterType(uint32_t vsrc) const;
 
   bool VerifyRegisterType(uint32_t vsrc, const RegType& check_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  bool VerifyRegisterTypeWide(uint32_t vsrc, const RegType& check_type1, const RegType& check_type2)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   void CopyFromLine(const RegisterLine* src) {
     DCHECK_EQ(num_regs_, src->num_regs_);
     memcpy(line_.get(), src->line_.get(), num_regs_ * sizeof(uint16_t));
@@ -171,6 +177,21 @@
                     const RegType& dst_type, const RegType& src_type)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void CheckUnaryOpWide(const DecodedInstruction& dec_insn,
+                        const RegType& dst_type1, const RegType& dst_type2,
+                        const RegType& src_type1, const RegType& src_type2)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void CheckUnaryOpToWide(const DecodedInstruction& dec_insn,
+                          const RegType& dst_type1, const RegType& dst_type2,
+                          const RegType& src_type)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void CheckUnaryOpFromWide(const DecodedInstruction& dec_insn,
+                            const RegType& dst_type,
+                            const RegType& src_type1, const RegType& src_type2)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   /*
    * Verify types for a simple three-register instruction (e.g. "add-int").
    * "dst_type" is stored into vA, and "src_type1"/"src_type2" are verified
@@ -181,6 +202,17 @@
                      bool check_boolean_op)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void CheckBinaryOpWide(const DecodedInstruction& dec_insn,
+                         const RegType& dst_type1, const RegType& dst_type2,
+                         const RegType& src_type1_1, const RegType& src_type1_2,
+                         const RegType& src_type2_1, const RegType& src_type2_2)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void CheckBinaryOpWideShift(const DecodedInstruction& dec_insn,
+                              const RegType& long_lo_type, const RegType& long_hi_type,
+                              const RegType& int_type)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   /*
    * Verify types for a binary "2addr" operation. "src_type1"/"src_type2"
    * are verified against vA/vB, then "dst_type" is stored into vA.
@@ -191,6 +223,17 @@
                           bool check_boolean_op)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  void CheckBinaryOp2addrWide(const DecodedInstruction& dec_insn,
+                              const RegType& dst_type1, const RegType& dst_type2,
+                              const RegType& src_type1_1, const RegType& src_type1_2,
+                              const RegType& src_type2_1, const RegType& src_type2_2)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+  void CheckBinaryOp2addrWideShift(const DecodedInstruction& dec_insn,
+                                   const RegType& long_lo_type, const RegType& long_hi_type,
+                                   const RegType& int_type)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
   /*
    * Verify types for A two-register instruction with a literal constant (e.g. "add-int/lit8").
    * "dst_type" is stored into vA, and "src_type" is verified against vB.
diff --git a/test/ReferenceMap/stack_walk_refmap_jni.cc b/test/ReferenceMap/stack_walk_refmap_jni.cc
index 31bd57f..e276897 100644
--- a/test/ReferenceMap/stack_walk_refmap_jni.cc
+++ b/test/ReferenceMap/stack_walk_refmap_jni.cc
@@ -62,11 +62,6 @@
       return true;
     }
 
-    // Enable this to dump reference map to LOG(INFO)
-    if (false) {
-      ScopedObjectAccess ts(Thread::Current());
-      art::verifier::MethodVerifier::VerifyMethodAndDump(m);
-    }
     const uint8_t* ref_bitmap = NULL;
     MethodHelper mh(m);
     std::string m_name(mh.GetName());