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/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc
index 2149490..d6cd665 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.